URL: https://linuxfr.org/users/superzen/journaux/ulfius-framework-pour-faire-des-api-web-en-c Title: Ulfius: framework pour faire des API Web en C Authors: Babelouest Date: 2016年06月01日T23:01:33+02:00 License: CC By-SA Tags: rest, api, c, langage_c et framework Score: 33 TL;DR ===== [Ulfius](https://github.com/babelouest/ulfius) est un framework pour développer des webservices et des API REST en C facilement et rapidement. Intro ===== Ca fait quelques mois que je travaille sur ce projet, et il a atteint une certaine maturité pour avoir envie d'en parler plus largement en espérant lui donner un nouvel élan. Il y a quelques temps, je parlais [ici](https://linuxfr.org/users/superzen/journaux/angharad-mon-systeme-de-domotique-maison) de mon projet de serveur domotique à base d'API REST en C. Depuis, je continue à le faire [évoluer](https://github.com/babelouest/angharad) [tranquillement](https://github.com/babelouest/sagremor), et j'en reparlerai peut-être bientôt de ce que c'est devenu. Et bien que je sois content de [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) pour la partie serveur web, je trouvais cette lib quand même assez lourde pour développer les API REST, j'accumulais une dette technique de plus en plus grande, et la lisibilité et la maintenabilité s'en [ressentaient](https://github.com/babelouest/benoic/blob/v2.0/webserver.c). Lors du commencement de la 3e génération de mon système domotique, j'ai voulu me faciliter la tâche, et créer un framework web plus simple à utiliser, pour me concentrer sur la programmation des API en tant que telle, et ne pas dupliquer inutilement le code de préparation des paramètres en entrée et en sortie. J'ai aussi voulu intégrer de manière plus facile du JSON dans les API. Pour ca, [Jansson](http://www.digip.org/jansson/) est un très bon outil facile à utiliser. En m'inspirant vaguement de ce que j'ai vu dans d'autres frameworks web, comme [NodeJS express](http://expressjs.com/fr/), j'ai voulu utiliser des endpoints avec une syntaxe simple, des urls paramétrables, des paramètre faciles à lire et à envoyer, et des fonctions callbacks qui traitent la requête http. Le but étant d'avoir un service web rapide à l'exécution et avec une utilisation mémoire petite. Grands principes ================ Webservice ---------- Une application Ulfius utilisera une instance par port TCP, dans cette instance elle déclarera des endpoints, et pour chaque endpoint elle donnera un format d'URL et la fonction callback qui sera appelée lorsque l'URL spécifiée sera appelée par un client. Une instance Ulfius est non bloquante, elle tourne dans un thread séparée, et l'application peut donc vaquer à d'autres occupations pendant que le service web tourne. Un petit exemple de webservice qui fait un Hello World: ```c /** * test.c * Small Hello World! example * to compile with gcc, run the following command * gcc -o test test.c -lulfius */ #include #include #include #define PORT 8080 /** * Callback function for the web application on /helloworld url call */ int callback_hello_world (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_response(response, 200, "Hello World!"); return U_OK; } /** * main function */ int main(void) { struct _u_instance instance; // Initialize instance with the port number if (ulfius_init_instance(&instance, PORT, NULL) != U_OK) { fprintf(stderr, "Error ulfius_init_instance, abort\n"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", "/helloworld", NULL, NULL, NULL, NULL, &callback_hello_world, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { fprintf(stderr, "Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } ``` Dans cet exemple, on initialise l'instance ulfius avec `ulfius_init_instance`, on y ajoute un endpoint qui va se déclencher lors d'un `GET /helloworld` et appeler la fonction callback `callback_hello_world`. La fonction callback a trois paramètres en entrée: une structure contenant les paramètres de la requête, une structure initialisée pour la réponse, et un pointeur user_data facultatif qui contiendra ce que l'utilisateur voudra bien, comme la configuration de son application et les handlers de connexion de base de donnée par exemple. Les structures `struct _u_request` et `struct _u_response` se veulent lisibles et contiennent ce dont on a besoin pour son API: ```c struct _u_request { char * http_verb; char * http_url; int check_server_certificate; struct sockaddr * client_address; char * auth_basic_user; char * auth_basic_password; struct _u_map * map_url; struct _u_map * map_header; struct _u_map * map_cookie; struct _u_map * map_post_body; json_t * json_body; json_error_t * json_error; int json_has_error; void * binary_body; size_t binary_body_length; }; struct _u_response { long status; char * protocol; struct _u_map * map_header; unsigned int nb_cookies; struct _u_cookie * map_cookie; char * string_body; json_t * json_body; void * binary_body; unsigned int binary_body_length; int (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max); void (* stream_callback_free) (void * stream_user_data); size_t stream_size; unsigned int stream_block_size; void * stream_user_data; }; ``` Envoi de requête http --------------------- Si l'on veut faire des requêtes HTTP sortantes, pour simplifier la vie, il y a des fonctions qui englobent les appels [libcurl](https://curl.haxx.se/libcurl/) nécessaires pour envoyer une requête HTTP via un `struct _u_request` et récupèrent la réponse dans un `struct _u_response` (les mêmes que pour les webservices): ```c int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response); ``` Pour envoyer un mail via smtp, y'a aussi une fonction pour ca: ```c int ulfius_send_smtp_email(const char * host, const int port, const int use_tls, const int verify_certificate, const char * user, const char * password, const char * from, const char * to, const char * cc, const char * bcc, const char * subject, const char * mail_body); ``` La [documentation](https://github.com/babelouest/ulfius/blob/master/README.md) est plus complète pour ceux qui se voudraient en savoir plus sur l'API Ulfius. Plus loin ========= Ce framework répond à mes besoins initiaux pour implémenter facilement des API REST et au delà, mais il est basé sur Libmicrohttpd qui a ses propres limitations, comme l'absence de websocket ou encore la gestion du protocole HTTP/2 pour ceux que j'ai pu identifier. Le framework a aussi la manie de faire des mallocs quand il en a besoin, et la gestion mémoire n'est pas optimale à cause de ca, il y a probablement beaucoup de fragmentation. Pour ce dernier point, étant donné que je n'ai pas relevé de faiblesse lors de l'exécution, je n'ai pas ressentit le besoin, c'est pour cela que je n'ai pas cherché à gérer la mémoire de manière plus poussée, et même sur un RPi Zero, ca tourne plutôt bien et plutôt vite. Cela dit, je pense que ce n'est pas un gros travail de changer la gestion mémoire, du moment que je j'ai de bonnes bases. C'est sous licence LGPL, et si ca peut servir à d'autres, gênez-vous pas! Lien du projet sur Github: [Ulfius](https://github.com/babelouest/ulfius)

AltStyle によって変換されたページ (->オリジナル) /