/* * File: bomberstudent_server.c * Author: Arthur Brandao * * Created on 14 novembre 2018 */ #include #include #include #include #include "arraylist.h" #include "bomberstudent_server.h" #include "error.h" #include "handler.h" /* --- Extern --- */ extern int serrno; /* --- Globale --- */ arraylist get; arraylist post; int error_code[] = { 520, 400, 403 }; char* error_message[] = { "Unknown error", "Bad Request", "Forbidden action" }; /* --- Fonctions privées --- */ /** * Thread de gestion d'un client * @param data * @return */ void* client_thread(void* data) { int* tmp, cliId, nbError = 0; Client* cli; //Recup la valeur de data tmp = (int*) data; cliId = *tmp; //Detache le thread if (pthread_detach(pthread_self()) != 0) { adderror("Impossible de détacher le thread Client"); return NULL; } //Recup le client cli = get_client(cliId); if (cli == NULL) { adderror("Le client n'existe pas"); return NULL; } printf("Connexion du client %d\n", cliId); //Attente requete du client while (true) { if (!receive_client(cli)) { adderror("Erreur lors de la reception d'une requete du client"); nbError++; } else { nbError = 0; } //Si trop d'erreur d'affilé if (nbError == NBERRORRESET) { //Fermeture du client et fin du thread printf("Connexion perdu avec le client %d\n", cliId); adderror("Client non accessible, fin de la connexion"); handler_client_end(cliId, NULL); break; } //Regarde si le client existe toujours if (get_client(cliId) == NULL) { break; } } return NULL; } /** * Thread de gestion des connexions UDP * @param data * @return */ void* udp_thread(void* data) { Server s; char buffer[BUFFER_SIZE]; //Cast data en serveur s = (Server) data; //Detache le thread if (pthread_detach(pthread_self()) != 0) { adderror("Impossible de détacher le thread UDP"); return NULL; } //Boucle attente while (true) { //Attente connexion if (s->server_receive(s, buffer, BUFFER_SIZE) == ERR) { addserror("Impossible de recevoir le demande de recherche"); continue; } //Si la phrase est correcte if (strncmp(buffer, SEARCH_SERV, strlen(buffer)) == 0) { if (!s->server_send(s, "i'm a bomberstudent server")) { addserror("Impossible de repondre à la recherche"); } } } return NULL; } /** * Thread de gestion des connexions TCP * @param data * @return */ void* tcp_thread(void* data) { Server* s; boolean res; int cliId; pthread_t client; //Cast data en serveur s = (Server*) data; //Detache le thread if (pthread_detach(pthread_self()) != 0) { adderror("Impossible de détacher le thread TCP"); return NULL; } //Boucle attente while (true) { res = s[0]->server_accept(s[0]); res = res && s[1]->server_accept(s[1]); if (!res) { addserror("Impossible d'etablir la connexion TCP"); continue; } //Creation d'un client et de son thread cliId = add_client(s[0], s[1]); if (pthread_create(&client, NULL, client_thread, &cliId) != 0) { adderror("Impossible de créer le thread Client"); /*ToDo Avertir Client du probleme*/ } } return NULL; } /* --- Fonctions publiques --- */ void ini_server() { arraylist_ini(&get); arraylist_ini(&post); } void add_handler(char* method, char* ressource, int(*handler)(int, JsonParser*)) { if (strncmp(method, "POST", 5) == 0) { arraylist_add(&post, ressource, handler); } else if (strncmp(method, "GET", 4) == 0) { arraylist_add(&get, ressource, handler); } } boolean launch_udp_server(int port) { Server s; pthread_t udp; //Creation serveur s = server_create_udp(); if (s == NULL) { addserror("Impossible de créer le serveur UDP"); return false; } if (!s->server_bind(s, port)) { addserror("Impossible de bind le serveur UDP"); return false; } //Lancement thread serveur udp if (pthread_create(&udp, NULL, udp_thread, s) != 0) { adderror("Impossible de créer le thread UDP"); server_close(s); return false; } return true; } boolean launch_tcp_server(int port) { Server* s; pthread_t tcp; //Creation serveur s = malloc(sizeof (Server) * 2); s[0] = server_create_tcp(); s[1] = server_create_tcp(); if (s[0] == NULL || s[1] == NULL) { addserror("Impossible de créer les serveurs TCP"); return false; } if (!(s[0]->server_bind(s[0], port) && s[1]->server_bind(s[1], port + 1))) { addserror("Impossible de bind les serveurs TCP"); return false; } //Lancement Thread attente connexion TCP if (pthread_create(&tcp, NULL, tcp_thread, s) != 0) { adderror("Impossible de créer le thread TCP"); server_close(s[0]); server_close(s[1]); return false; } return true; } boolean receive_client(Client* cli) { char buffer[BUFFER_SIZE]; char* reader, * ressource, * json = NULL; int method, pos, compteur = 0; JsonParser* jp = NULL; arraylist* al = &get; //Attente reception if (cli->main->server_receive(cli->main, buffer, BUFFER_SIZE) == ERR) { //Si la conneion est coupée if (serrno == SEABORT) { //On ferme le client printf("Connexion perdu avec le client %d\n", cli->id); handler_client_end(cli->id, NULL); } addserror("Impossible de recevoire les données du client"); return false; } //Recup la methode if (buffer[0] == 'P') { pos = 5; reader = buffer + pos; method = POST; } else if (buffer[0] == 'G') { pos = 4; reader = buffer + pos; method = GET; } else { //Methode incorrect adderror("Methode incorrect"); send_err_client(cli->id, EREQUEST); return false; } //Recup la ressource while (buffer[pos] != '\n' && buffer[pos] != '\0') { compteur++; pos++; } if (compteur == 0) { adderror("Aucune ressource demandée"); send_err_client(cli->id, EREQUEST); return false; } ressource = malloc(sizeof (char) * (compteur + 1)); memset(ressource, 0, compteur + 1); strncpy(ressource, reader, compteur); //Recup param JSON if (method == POST) { //Si un parametre est present if (buffer[pos] != '\0') { json = reader + compteur + 1; jp = malloc(sizeof (JsonParser)); if (json_parse(jp, json) != JSON_OK) { adderror("Impossible de parser le JSON"); send_err_client(cli->id, EREQUEST); return false; } } //Change la liste à utiliser al = &post; } //Appel le callback if (arraylist_call(al, ressource, cli->id, jp) == ERR) { adderror("Impossible d'executer le callback"); send_err_client(cli->id, EREQUEST); return false; } //Nettoyage free(ressource); if (jp != NULL) { free(jp); } return true; } boolean send_client(int cliId, JsonEncoder* je) { Client* cli; char* answer, * msg; int length; //Recup client cli = get_client(cliId); if (cli == NULL) { adderror("Le client n'existe pas"); return false; } //Preparation message answer = json_encode(je); length = strlen(answer) + 2; msg = malloc(sizeof (char) + length); memset(msg, 0, length); snprintf(msg, length, "%s\n", answer); //Envoi la reponse if (!cli->main->server_send(cli->main, msg)) { //Si la conneion est coupée if (serrno == SEABORT) { //On ferme le client printf("Connexion perdu avec le client %d\n", cliId); handler_client_end(cliId, NULL); } addserror("Impossible de repondre à la requete du client"); free(answer); free(msg); return false; } //Nettoyage free(answer); free(msg); return true; } boolean send_err_client(int cliId, int error) { JsonEncoder je; //Creation JSON ini_encoder(&je); add_integer(&je, "status", error_code[error]); add_string(&je, "message", error_message[error]); //Envoi if (!send_client(cliId, &je)) { adderror("Impossible d'avertir le client de l'erreur"); return false; } return true; } boolean notify_client(Client* cli, char* method, char* ressource, JsonEncoder* param) { int length; char* answer, * msg; //Creation message answer = json_encode(param); length = strlen(method) + 1 + strlen(ressource) + 1 + strlen(answer) + 2; msg = malloc(sizeof (char) +length); memset(msg, 0, length); sprintf(msg, "%s %s\n%s\n", method, ressource, answer); //Envoi la reponse if (!cli->notify->server_send(cli->notify, msg)) { adderror("Impossible de notifier le client"); free(answer); free(msg); return false; } //Nettoyage free(answer); free(msg); return true; } boolean notify_all(char* method, char* ressource, JsonEncoder* param) { Client* cli; boolean res = true; //Parcours tous les clients int nbClient = get_number_client(); for (int i = 0; i < nbClient; i++) { cli = get_client(i); //Si le client existe toujours if (cli == NULL) { continue; } //Lui envoi le message res = res && notify_client(cli, method, ressource, param); } return res; }