Changed restore connection macro to a function, resolved an internet connection bug and wrote the README.

This commit is contained in:
lucic71 2020-05-09 19:01:14 +03:00
parent 9c7c1a7817
commit 42f1e1687b
6 changed files with 198 additions and 60 deletions

View File

@ -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.

View File

@ -61,8 +61,3 @@ void delete_auth_info(auth_info_t *auth_info) {
FREE(auth_info);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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) { \