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
|
||||
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.
|
||||
POPESCU LUCIAN IOAN 321CD
|
||||
-------------------------
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,21 +10,6 @@
|
|||
#include "memory.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() {
|
||||
|
||||
/*
|
||||
|
@ -77,30 +62,38 @@ int main() {
|
|||
memset(input_buffer, 0x00, MAX_COMMAND_SZ);
|
||||
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) {
|
||||
|
||||
int register_ret = op_register(&sockfd);
|
||||
operation_status = op_register(&sockfd);
|
||||
|
||||
/*
|
||||
* If the connection closed during the register operation then a new
|
||||
* socket must be opened for the communication with the server.
|
||||
*
|
||||
*/
|
||||
RESTORE_SOCKFD(register_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, LOGIN, sizeof(LOGIN) - 1) == 0) {
|
||||
|
||||
int login_ret = login(&sockfd, &cookie);
|
||||
RESTORE_SOCKFD(login_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = login(&sockfd, &cookie);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the login was successful then the client must delete the previous
|
||||
* @jwt_token.
|
||||
*
|
||||
*/
|
||||
if (login_ret == OPERATION_SUCCESSFUL) {
|
||||
if (operation_status == OPERATION_SUCCESSFUL) {
|
||||
FREE(jwt_token);
|
||||
}
|
||||
|
||||
|
@ -111,52 +104,64 @@ int main() {
|
|||
* delete it because a new enter_library will generate a new jwt_token.
|
||||
*
|
||||
*/
|
||||
int enter_library_ret = enter_library(&sockfd, cookie, &jwt_token);
|
||||
RESTORE_SOCKFD(enter_library_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = enter_library(&sockfd, cookie, &jwt_token);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, GET_BOOKS, sizeof(GET_BOOKS) - 1) == 0) {
|
||||
|
||||
int get_books_ret = get_books(&sockfd, jwt_token);
|
||||
RESTORE_SOCKFD(get_books_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = get_books(&sockfd, jwt_token);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, GET_BOOK, sizeof(GET_BOOK) - 1) == 0) {
|
||||
|
||||
int get_book_ret = get_book(&sockfd, jwt_token);
|
||||
RESTORE_SOCKFD(get_book_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = get_book(&sockfd, jwt_token);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, ADD_BOOK, sizeof(ADD_BOOK) - 1) == 0) {
|
||||
|
||||
int add_book_ret = add_book(&sockfd, jwt_token);
|
||||
RESTORE_SOCKFD(add_book_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = add_book(&sockfd, jwt_token);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, DELETE_BOOK, sizeof(DELETE_BOOK) - 1) == 0) {
|
||||
|
||||
int delete_book_ret = delete_book(&sockfd, jwt_token);
|
||||
RESTORE_SOCKFD(delete_book_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = delete_book(&sockfd, jwt_token);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, LOGOUT, sizeof(LOGOUT) - 1) == 0) {
|
||||
|
||||
int logout_ret = logout(&sockfd, cookie);
|
||||
RESTORE_SOCKFD(logout_ret == OPERATION_CONNECTION_CLOSED,
|
||||
sockfd, server_ip);
|
||||
operation_status = logout(&sockfd, cookie);
|
||||
|
||||
if (operation_status == OPERATION_CONNECTION_CLOSED) {
|
||||
restore_connection(&sockfd, server_ip);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(jwt_token);
|
||||
}
|
||||
|
||||
} else if (strncmp(input_buffer, EXIT, sizeof(EXIT) - 1) == 0) {
|
||||
|
||||
puts("Closing the connection with the server, goodbye.");
|
||||
puts(GOODBYE);
|
||||
close(sockfd);
|
||||
|
||||
FREE(cookie);
|
||||
|
|
|
@ -148,6 +148,19 @@ char *receive_from_server(int sockfd) {
|
|||
int send_and_receive(int *sockfd, char *request, char **response) {
|
||||
|
||||
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;
|
||||
|
||||
int return_status = SEND_RECV_FAIL;
|
||||
|
@ -200,3 +213,19 @@ int send_and_receive(int *sockfd, char *request, char **response) {
|
|||
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);
|
||||
|
||||
/*
|
||||
* Make a stabile connection with the server.
|
||||
*
|
||||
*/
|
||||
void restore_connection(int *sockfd, char *server_ip);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,8 +63,9 @@
|
|||
* Error messages for connection with the server.
|
||||
*
|
||||
*/
|
||||
#define SERVER_CONNECTION_CLOSED "Server closed the connection. Please retry the operation!"
|
||||
#define NO_INTERNET_CONNECTION "There is no internet connection!"
|
||||
#define SERVER_CONNECTION_CLOSED "Server closed the connection. Please retry the operation!"
|
||||
#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.
|
||||
|
@ -75,6 +76,12 @@
|
|||
#define JWT_TOKEN_ERROR "You must enter library before making this operation!"
|
||||
#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) \
|
||||
do { \
|
||||
if (assertion) { \
|
||||
|
|
Loading…
Reference in New Issue