Changed restore connection macro to a function, resolved an internet connection bug and wrote the README.
This commit is contained in:
parent
9c7c1a7817
commit
42f1e1687b
|
@ -1,7 +1,102 @@
|
||||||
# Tema3-Protocoale-de-Comuncatie
|
POPESCU LUCIAN IOAN 321CD
|
||||||
Implemented a web client that communicates witha a REST API. The server basically contains a library where the user
|
-------------------------
|
||||||
can store information about books. It supports operations like add_books, delete_books, etc.
|
|
||||||
|
TEMA 3 PROTOCOALE DE COMUNICATIE
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
I. File structure
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
include/
|
||||||
|
authentication.h Contains a auth_info_t object
|
||||||
|
book.h Contains a book_info_t object and a book_id_t object
|
||||||
|
buffer.h Functions used in connection.c
|
||||||
|
cJSON.h JSON parsing library
|
||||||
|
commands.h Prototypes of the implemented commands
|
||||||
|
connection.h Functions that deal with connection with the server
|
||||||
|
dns.h Functions that send DNS requests
|
||||||
|
error.h Macros used to print and deal with errors
|
||||||
|
http.h Functions that extract info from HTTP headers, etc
|
||||||
|
memory.h Macros used for safe memory management(CALLOC, MALLOC, FREE)
|
||||||
|
prompt.h Extract data from prompts (for example: username, password)
|
||||||
|
requests.h Functions that build GET, POST and DELETE requests
|
||||||
|
server.h Information about the server (URLS, NAME, etc)
|
||||||
|
./
|
||||||
|
authentication.c Implements specific functionality
|
||||||
|
book.c Implements specific functionality
|
||||||
|
buffer.c Implements specific functionality
|
||||||
|
cJSON.c JSON library implementation
|
||||||
|
client.c Main driver for client
|
||||||
|
commands.c Implements specific functionality
|
||||||
|
connection.c Implements specific functionality
|
||||||
|
dns.c Implements specific functionality
|
||||||
|
http.c Implements specific functionality
|
||||||
|
prompt.c Implements specific functionality
|
||||||
|
requests.c Implements specific functionality
|
||||||
|
Makefile Makefile to build the client
|
||||||
|
README This file
|
||||||
|
|
||||||
|
II. Design
|
||||||
|
----------
|
||||||
|
|
||||||
|
A. Client and operations(commands)
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The implementation of the client is pretty straight forward. The client reads
|
||||||
|
input from stdin and using a huge block of if..else statements, it decides which
|
||||||
|
operation to make.
|
||||||
|
|
||||||
|
Each operation has three return status codes: OPERATION_SUCCESSFUL,
|
||||||
|
OPERATION_CONNECTION_CLOSED and OPERATION_FAILED. The client checks after each
|
||||||
|
operation the return code to make sure that the connection with the server is
|
||||||
|
still ok, if not it retries to re-establish it. It also checks that the
|
||||||
|
operations were successful in order to manage the correct creation and deletion
|
||||||
|
of cookies and JWT tokens.
|
||||||
|
|
||||||
|
The operations are implemented using the same rules:
|
||||||
|
- fetch data from user if needed
|
||||||
|
- build the HTTP request
|
||||||
|
- send the request and receive the response
|
||||||
|
- work with the response if needed
|
||||||
|
|
||||||
|
Moreover each operation takes care of the errors which may occur during the
|
||||||
|
above mentioned steps. For this each variable is declared at the beginning
|
||||||
|
of each opearion and if an error occurs the memory will be free'd for this
|
||||||
|
variables using a goto statement and a label at the end of the current
|
||||||
|
operation.
|
||||||
|
|
||||||
|
B. JSON
|
||||||
|
-------
|
||||||
|
|
||||||
|
For parsing, converting and working with JSON objects I used the cJSON library.
|
||||||
|
It is a simple library containing only a header file and a source file, that
|
||||||
|
can easily be inserted in the project.
|
||||||
|
|
||||||
|
I chose this library because it has a basic interface containing functions and
|
||||||
|
objects that require a small amount of time to understand, as the authors say:
|
||||||
|
"cJSON aims to be the dumbest possible parser that you can get your job done
|
||||||
|
with".
|
||||||
|
|
||||||
|
More info and tutorials can be found on their github page:
|
||||||
|
"https://github.com/DaveGamble/cJSON".
|
||||||
|
|
||||||
|
C. Reading data from user
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
prompt.c is the file where this functionality is implemented. It pops up a
|
||||||
|
prompt for the required data and using a basic read function like fgets it
|
||||||
|
gets the job done. I also added password safety by turning off the echoing
|
||||||
|
when the password is inserted.
|
||||||
|
|
||||||
|
D. Other objects and functionality
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
For keeping all as simple as possible, I encapsulated the information coming
|
||||||
|
from the user (username, password, book info) in objects that have their own
|
||||||
|
metods for parsing to JSON or deletion. Their location can be found in
|
||||||
|
I. File structure.
|
||||||
|
|
||||||
|
The functionality for connecting with the server and creating requests is
|
||||||
|
mainly borrowed from lab10 HTTP with some minor changes.
|
||||||
|
|
||||||
It uses cookies to store a session and JWT tokens to restrict user access into the library.
|
|
||||||
The communication with the server is over the HTTP protocol, using GET POST and DELETE requests.
|
|
||||||
It uses cJSON library to work with JSON objects.
|
|
||||||
|
|
|
@ -61,8 +61,3 @@ void delete_auth_info(auth_info_t *auth_info) {
|
||||||
FREE(auth_info);
|
FREE(auth_info);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,21 +10,6 @@
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* This macro makes a stable connection with the server.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define RESTORE_SOCKFD(assertion, sockfd, server_ip) \
|
|
||||||
do { \
|
|
||||||
if (assertion) { \
|
|
||||||
ERROR(SERVER_CONNECTION_CLOSED); \
|
|
||||||
sockfd = -1; \
|
|
||||||
while (sockfd < 0) { \
|
|
||||||
sockfd = open_connection(server_ip, PORT, AF_INET, SOCK_STREAM, 0); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} while (sockfd < 0)
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,30 +62,38 @@ int main() {
|
||||||
memset(input_buffer, 0x00, MAX_COMMAND_SZ);
|
memset(input_buffer, 0x00, MAX_COMMAND_SZ);
|
||||||
fgets(input_buffer, MAX_COMMAND_SZ - 1, stdin);
|
fgets(input_buffer, MAX_COMMAND_SZ - 1, stdin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Possible values:
|
||||||
|
*
|
||||||
|
* OPERATION_SUCCESSFUL
|
||||||
|
* OPERATION_FAILED
|
||||||
|
* OPERATION_CONNECTION_CLOSED
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int operation_status;
|
||||||
|
|
||||||
if (strncmp(input_buffer, REGISTER, sizeof(REGISTER) - 1) == 0) {
|
if (strncmp(input_buffer, REGISTER, sizeof(REGISTER) - 1) == 0) {
|
||||||
|
|
||||||
int register_ret = op_register(&sockfd);
|
operation_status = op_register(&sockfd);
|
||||||
|
|
||||||
/*
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
* If the connection closed during the register operation then a new
|
restore_connection(&sockfd, server_ip);
|
||||||
* socket must be opened for the communication with the server.
|
}
|
||||||
*
|
|
||||||
*/
|
|
||||||
RESTORE_SOCKFD(register_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, LOGIN, sizeof(LOGIN) - 1) == 0) {
|
} else if (strncmp(input_buffer, LOGIN, sizeof(LOGIN) - 1) == 0) {
|
||||||
|
|
||||||
int login_ret = login(&sockfd, &cookie);
|
operation_status = login(&sockfd, &cookie);
|
||||||
RESTORE_SOCKFD(login_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the login was successful then the client must delete the previous
|
* If the login was successful then the client must delete the previous
|
||||||
* @jwt_token.
|
* @jwt_token.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
if (login_ret == OPERATION_SUCCESSFUL) {
|
if (operation_status == OPERATION_SUCCESSFUL) {
|
||||||
FREE(jwt_token);
|
FREE(jwt_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,52 +104,64 @@ int main() {
|
||||||
* delete it because a new enter_library will generate a new jwt_token.
|
* delete it because a new enter_library will generate a new jwt_token.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int enter_library_ret = enter_library(&sockfd, cookie, &jwt_token);
|
operation_status = enter_library(&sockfd, cookie, &jwt_token);
|
||||||
RESTORE_SOCKFD(enter_library_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, GET_BOOKS, sizeof(GET_BOOKS) - 1) == 0) {
|
} else if (strncmp(input_buffer, GET_BOOKS, sizeof(GET_BOOKS) - 1) == 0) {
|
||||||
|
|
||||||
int get_books_ret = get_books(&sockfd, jwt_token);
|
operation_status = get_books(&sockfd, jwt_token);
|
||||||
RESTORE_SOCKFD(get_books_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, GET_BOOK, sizeof(GET_BOOK) - 1) == 0) {
|
} else if (strncmp(input_buffer, GET_BOOK, sizeof(GET_BOOK) - 1) == 0) {
|
||||||
|
|
||||||
int get_book_ret = get_book(&sockfd, jwt_token);
|
operation_status = get_book(&sockfd, jwt_token);
|
||||||
RESTORE_SOCKFD(get_book_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, ADD_BOOK, sizeof(ADD_BOOK) - 1) == 0) {
|
} else if (strncmp(input_buffer, ADD_BOOK, sizeof(ADD_BOOK) - 1) == 0) {
|
||||||
|
|
||||||
int add_book_ret = add_book(&sockfd, jwt_token);
|
operation_status = add_book(&sockfd, jwt_token);
|
||||||
RESTORE_SOCKFD(add_book_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, DELETE_BOOK, sizeof(DELETE_BOOK) - 1) == 0) {
|
} else if (strncmp(input_buffer, DELETE_BOOK, sizeof(DELETE_BOOK) - 1) == 0) {
|
||||||
|
|
||||||
int delete_book_ret = delete_book(&sockfd, jwt_token);
|
operation_status = delete_book(&sockfd, jwt_token);
|
||||||
RESTORE_SOCKFD(delete_book_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, LOGOUT, sizeof(LOGOUT) - 1) == 0) {
|
} else if (strncmp(input_buffer, LOGOUT, sizeof(LOGOUT) - 1) == 0) {
|
||||||
|
|
||||||
int logout_ret = logout(&sockfd, cookie);
|
operation_status = logout(&sockfd, cookie);
|
||||||
RESTORE_SOCKFD(logout_ret == OPERATION_CONNECTION_CLOSED,
|
|
||||||
sockfd, server_ip);
|
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||||
|
restore_connection(&sockfd, server_ip);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete the cookie and the jwt_token if the operation was successful.
|
* Delete the cookie and the jwt_token if the operation was successful.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
if (logout_ret == OPERATION_SUCCESSFUL) {
|
if (operation_status == OPERATION_SUCCESSFUL) {
|
||||||
FREE(cookie);
|
FREE(cookie);
|
||||||
FREE(jwt_token);
|
FREE(jwt_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (strncmp(input_buffer, EXIT, sizeof(EXIT) - 1) == 0) {
|
} else if (strncmp(input_buffer, EXIT, sizeof(EXIT) - 1) == 0) {
|
||||||
|
|
||||||
puts("Closing the connection with the server, goodbye.");
|
puts(GOODBYE);
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
|
|
||||||
FREE(cookie);
|
FREE(cookie);
|
||||||
|
|
|
@ -148,6 +148,19 @@ char *receive_from_server(int sockfd) {
|
||||||
int send_and_receive(int *sockfd, char *request, char **response) {
|
int send_and_receive(int *sockfd, char *request, char **response) {
|
||||||
|
|
||||||
char *server_ip = get_ip(SERVER);
|
char *server_ip = get_ip(SERVER);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the internet connection dies while the client runs, get_ip will
|
||||||
|
* detect this because it makes a DNS request that will use the connection.
|
||||||
|
*
|
||||||
|
* So the client will die to.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (server_ip == NULL) {
|
||||||
|
ERROR(NO_INTERNET_CONNECTION);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
bool connection_failed = false;
|
bool connection_failed = false;
|
||||||
|
|
||||||
int return_status = SEND_RECV_FAIL;
|
int return_status = SEND_RECV_FAIL;
|
||||||
|
@ -200,3 +213,19 @@ int send_and_receive(int *sockfd, char *request, char **response) {
|
||||||
return return_status;
|
return return_status;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void restore_connection(int *sockfd, char *server_ip) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < SEND_RECV_TIMEOUT; i++) {
|
||||||
|
*sockfd = open_connection(server_ip, PORT, AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (*sockfd > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR(UNSTABLE_INTERNET_CONNECTION);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -64,4 +64,11 @@ char *receive_from_server(int sockfd);
|
||||||
|
|
||||||
int send_and_receive(int *sockfd, char *request, char **response);
|
int send_and_receive(int *sockfd, char *request, char **response);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a stabile connection with the server.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void restore_connection(int *sockfd, char *server_ip);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -63,8 +63,9 @@
|
||||||
* Error messages for connection with the server.
|
* Error messages for connection with the server.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define SERVER_CONNECTION_CLOSED "Server closed the connection. Please retry the operation!"
|
#define SERVER_CONNECTION_CLOSED "Server closed the connection. Please retry the operation!"
|
||||||
#define NO_INTERNET_CONNECTION "There is no internet connection!"
|
#define NO_INTERNET_CONNECTION "There is no internet connection!"
|
||||||
|
#define UNSTABLE_INTERNET_CONNECTION "Unstable internet connection! Please retry."
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error messages for invalid cookies or tokens.
|
* Error messages for invalid cookies or tokens.
|
||||||
|
@ -75,6 +76,12 @@
|
||||||
#define JWT_TOKEN_ERROR "You must enter library before making this operation!"
|
#define JWT_TOKEN_ERROR "You must enter library before making this operation!"
|
||||||
#define ENTER_LIBARY_ERROR "You are already in library!"
|
#define ENTER_LIBARY_ERROR "You are already in library!"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Status message for closing the client.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define GOODBYE "Closing the connection with the server, goodbye."
|
||||||
|
|
||||||
#define ERROR_HANDLER(assertion, return_value) \
|
#define ERROR_HANDLER(assertion, return_value) \
|
||||||
do { \
|
do { \
|
||||||
if (assertion) { \
|
if (assertion) { \
|
||||||
|
|
Loading…
Reference in New Issue