LCOV - code coverage report
Current view: top level - src - libhttp.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 128 199 64.3 %
Date: 2024-11-22 08:29:37 Functions: 14 17 82.4 %

          Line data    Source code
       1             : /**
       2             :  *    @file  libhttp.c
       3             :  *   @brief  HTTP parsing and building library
       4             :  *
       5             :  *  @author  François Cerbelle (Fanfan), francois@cerbelle.net
       6             :  *
       7             :  *  @internal
       8             :  *       Created:  15/11/2024
       9             :  *      Revision:  none
      10             :  * Last modified:  2024-11-22 08:26
      11             :  *      Compiler:  gcc
      12             :  *  Organization:  Cerbelle.net
      13             :  *     Copyright:  Copyright (c) 2024, François Cerbelle
      14             :  *
      15             :  *  This source code is released for free distribution under the terms of the
      16             :  *  GNU General Public License as published by the Free Software Foundation.
      17             :  */
      18             : 
      19             : #include "libhttp.h"
      20             : #include "sclist.h"
      21             : #include "base64.h"                             /* Base64 encoder and decoder */
      22             : 
      23             : #include <stdlib.h>
      24             : #include <stdio.h>
      25             : #include <string.h>
      26             : #include <assert.h>
      27             : 
      28             : typedef struct HTTP_s {
      29             :     sclist_t* headers;
      30             :     char* body;
      31             : } HTTP_t;
      32             : 
      33             : typedef struct HTTPHeader_s {
      34             :     sclistrecord_t* sclistrecord;
      35             :     char* name;
      36             :     char* value;
      37             : } HTTPHeader_t;
      38             : 
      39          28 : HTTPHeader_t* HTTPHeader_new(const char* name, const char* value) {
      40             :     HTTPHeader_t* header;
      41             : 
      42          28 :     assert(name);
      43          26 :     assert(value);
      44             : 
      45          26 :     if (NULL==(header=malloc(sizeof(struct HTTPHeader_s)))) {
      46           0 :         perror("HTTPHeader_new");
      47           0 :         return NULL;
      48             :     }
      49          26 :     if (NULL==(header->name = strdup(name))) {
      50           0 :         perror("HTTPHeader_new name");
      51           0 :         free(header);
      52           0 :         return NULL;
      53             :     }
      54          26 :     if (NULL==(header->value = strdup(value))) {
      55           0 :         perror("HTTPHeader_new value");
      56           0 :         free(header->name);
      57           0 :         free(header);
      58           0 :         return NULL;
      59             :     }
      60          26 :     header->sclistrecord = NULL;
      61          26 :     return header;
      62             : }
      63             : 
      64          21 : void          HTTPHeader_del(HTTPHeader_t* header) {
      65          21 :     assert(header);
      66          19 :     assert(header->name);
      67          19 :     assert(header->value);
      68             : 
      69          19 :     free(header->name);
      70          19 :     free(header->value);
      71          19 :     free(header);
      72          19 : }
      73             : 
      74           3 : HTTPHeader_t* HTTPHeader_basicauth(const char* login, const char* pass) {
      75             :     char* auth_encoded;
      76             :     char* auth;
      77             : 
      78           3 :     assert(login);
      79           3 :     assert(pass);
      80             : 
      81           3 :     if (NULL==(auth=malloc(strlen("Basic ")+strlen(login)+1+strlen(pass)+1))) {
      82           0 :         perror("HTTPHeader_basicauth");
      83           0 :         return NULL;
      84             :     }
      85             :     /* strcpy/strcat expected to be faster than sprintf */
      86           3 :     strcpy(auth,login);
      87           3 :     strcat(auth,":");
      88           3 :     strcat(auth,pass);
      89           3 :     auth_encoded=base64_encode(auth);
      90           3 :     free(auth);
      91             : 
      92           3 :     if (NULL==(auth=malloc(strlen("Basic ")+strlen(auth_encoded)+1))) {
      93           0 :         perror("HTTPHeader_basicauth");
      94           0 :         free(auth_encoded);
      95           0 :         return NULL;
      96             :     }
      97           3 :     strcpy(auth,"Basic ");
      98           3 :     strcat(auth, auth_encoded);
      99             : 
     100           3 :     return HTTPHeader_new("Authorization",auth);
     101             : }
     102             : 
     103          20 : char*         HTTPHeader_getname(HTTPHeader_t* header) {
     104          20 :     assert(header);
     105          20 :     return header->name;
     106             : }
     107             : 
     108          20 : char*         HTTPHeader_getvalue(HTTPHeader_t* header) {
     109          20 :     assert(header);
     110          20 :     return header->value;
     111             : }
     112             : 
     113          27 : HTTP_t* HTTP_new() {
     114             :     HTTP_t* retval;
     115             : 
     116          27 :     if (NULL==(retval=malloc(sizeof(struct HTTP_s)))) {
     117           0 :         perror("HTTP_new HTTP_s");
     118           0 :         return NULL;
     119             :     }
     120             : 
     121          27 :     if (NULL==(retval->body=malloc(1))) {
     122           0 :         perror("HTTP_new body");
     123           0 :         free(retval);
     124           0 :         return NULL;
     125             :     };
     126          27 :     retval->body[0]=0;
     127             : 
     128          27 :     if (NULL==(retval->headers = sclist_new())) {
     129           0 :         perror("HTTP_new headers");
     130           0 :         free(retval->body);
     131           0 :         free(retval);
     132           0 :         return NULL;
     133             :     }
     134             : 
     135          27 :     return retval;
     136             : }
     137             : 
     138          21 : void HTTP_del(HTTP_t* http) {
     139             :     sclistrecord_t* current;
     140             : 
     141          21 :     assert(http);
     142          19 :     assert(http->headers);
     143          19 :     assert(http->body);
     144             : 
     145          19 :     current = sclist_firstrecord(http->headers);
     146          26 :     while (current) {
     147           9 :         HTTPHeader_t* header=sclist_getvalue(current);
     148           9 :         if (header->name)
     149           9 :             free(header->name);
     150           9 :         if (header->value)
     151           9 :             free(header->value);
     152           9 :         sclistrecord_t* tmp = current;
     153           9 :         current = sclist_nextrecord(current);
     154           9 :         sclist_remrecord(http->headers,tmp);
     155             :     }
     156             : 
     157          17 :     free(http->body);
     158          17 :     free(http);
     159          17 : }
     160             : 
     161          13 : char* HTTP_setbody(HTTP_t* http, const char* body) {
     162             :     char* newbody;
     163             : 
     164          13 :     assert(http);
     165          11 :     assert(body);
     166           9 :     assert(http->body);
     167             : 
     168           9 :     if (NULL==(newbody=realloc(http->body,strlen(body)+1))) {
     169           0 :         perror("HTTP_setbody");
     170           0 :         return NULL;
     171             :     }
     172             : 
     173           9 :     http->body = newbody;
     174           9 :     strcpy(http->body, body);
     175           9 :     return http->body;
     176             : }
     177             : 
     178          12 : char* HTTP_getbody(HTTP_t* http) {
     179          12 :     assert(http);
     180          12 :     assert(http->body);
     181             : 
     182          12 :     return http->body;
     183             : }
     184             : 
     185          22 : HTTP_t* HTTP_addheader(HTTP_t* http, HTTPHeader_t* header) {
     186          22 :     assert(http);
     187          20 :     assert(http->headers);
     188          20 :     assert(http->body);
     189          20 :     assert(header);
     190             : 
     191             :     /* Header already belonging to an header list */
     192          18 :     if (header->sclistrecord) {
     193           3 :         perror("HTTP_addheader, header already in a list");
     194           3 :         return NULL;
     195             :     }
     196             : 
     197          15 :     if (NULL==(header->sclistrecord=sclist_addrecord(http->headers,header))) {
     198           0 :         perror("HTTP_addheader");
     199           0 :         return NULL;
     200             :     }
     201          15 :     return http;
     202             : }
     203             : 
     204           0 : HTTPHeader_t* HTTP_firstheader(const HTTP_t* http) {
     205           0 :     assert(http);
     206           0 :     assert(http->headers);
     207           0 :     sclistrecord_t* header_item=sclist_firstrecord(http->headers);
     208           0 :     if (!header_item)
     209           0 :         return NULL;
     210           0 :     return sclist_getvalue(header_item);
     211             : }
     212             : 
     213           0 : HTTPHeader_t* HTTP_nextheader(const HTTPHeader_t* header) {
     214             :     sclistrecord_t* list_it;
     215             : 
     216           0 :     assert(header);
     217             : 
     218           0 :     list_it = header->sclistrecord;
     219             : 
     220             :     /* EOL reached */
     221           0 :     if (NULL==(list_it = sclist_nextrecord(list_it)))
     222           0 :         return NULL;
     223             :     else
     224           0 :         return sclist_getvalue(list_it);
     225             : }
     226             : 
     227           0 : HTTPHeader_t* HTTP_findheader(const HTTPHeader_t* start, const char* name, const char* value) {
     228             :     sclistrecord_t* header_it;
     229             : 
     230           0 :     assert(start);
     231           0 :     assert(start->sclistrecord);
     232           0 :     assert(name);
     233             :     /* value can be null for "any" */
     234             : 
     235           0 :     header_it = start->sclistrecord;
     236             : 
     237             :     /* Find the record pointer in the list */
     238           0 :     for (; header_it; header_it=sclist_nextrecord(header_it)) {
     239           0 :         HTTPHeader_t* header_ptr = sclist_getvalue(header_it);
     240           0 :         assert(header_ptr);
     241           0 :         char* header_name = HTTPHeader_getname(header_ptr);
     242           0 :         if (0==strcmp(header_name,name)) {
     243           0 :             if (NULL==value)
     244           0 :                 break;
     245           0 :             char* header_value = HTTPHeader_getvalue(header_ptr);
     246           0 :             assert(header_value);
     247           0 :             if (0==strcmp(header_value,value))
     248           0 :                 break;
     249             :         }
     250             :     }
     251           0 :     if (!header_it)
     252           0 :         return NULL;
     253             :     else
     254           0 :         return sclist_getvalue(header_it);
     255             : }
     256             : 
     257           6 : HTTP_t* HTTP_delheader(HTTP_t* http, HTTPHeader_t* header) {
     258           6 :     assert(http);
     259           6 :     assert(http->headers);
     260           6 :     assert(http->body);
     261           6 :     assert(header);
     262             : 
     263             :     /* Calling function has the header pointer, can and need to free it */
     264           6 :     sclist_remrecord(http->headers, header->sclistrecord);
     265             :     /** @todo: make sclist_remrecord return a status and use it */
     266             : #if 0
     267             :     if (NULL==sclist_remrecord(http->headers, header->sclistrecord)) {
     268             :         perror("HTTP_addheader");
     269             :         return NULL;
     270             :     }
     271             : #endif
     272           6 :     header->sclistrecord = NULL;
     273             : 
     274           6 :     return http;
     275             : }
     276             : 
     277           3 : char* HTTP_buildrequest(const HTTPMethod_t method, const char* uri, const HTTPVersion_t version) {
     278             :     char* retval;
     279             : 
     280           3 :     assert(method<=HTTPMETHOD_INVALID);
     281           3 :     assert(version<=HTTPVERSION_INVALID);
     282             : 
     283             :     /* NULL URI allowed, as an empty string */
     284           3 :     if (NULL==(retval=malloc(7+1+(uri?strlen(uri)+1:0)+8+1))) {
     285           0 :         perror("HTTP_getrequest");
     286           0 :         exit(EXIT_FAILURE);
     287             :     }
     288           3 :     retval[0]=0;
     289             : 
     290           3 :     switch(method) {
     291           3 :     case HTTPMETHOD_GET:
     292           3 :         strcat(retval, "GET");
     293           3 :         break;;
     294           0 :     case HTTPMETHOD_HEAD:
     295             :     case HTTPMETHOD_POST:
     296             :     case HTTPMETHOD_PUT:
     297             :     case HTTPMETHOD_DELETE:
     298             :     case HTTPMETHOD_CONNECT:
     299             :     case HTTPMETHOD_OPTIONS:
     300             :     case HTTPMETHOD_TRACE:
     301             :     case HTTPMETHOD_PATCH:
     302             :     case HTTPMETHOD_INVALID:
     303             :     default:
     304           0 :         fprintf(stderr,"HTTP method not supported\n");
     305           0 :         exit(EXIT_FAILURE);
     306             :         break;;
     307             :     }
     308             : 
     309             :     /* NULL URI allowed, as an empty string */
     310           3 :     if (uri) {
     311           3 :         strcat(retval," ");
     312           3 :         strcat(retval, uri);
     313             :     }
     314             : 
     315           3 :     strcat(retval," HTTP/");
     316           3 :     switch(version) {
     317           3 :     case HTTPVERSION_HTTP11:
     318             :     case HTTPVERSION_HTTP11b:
     319           3 :         strcat(retval, "1.1");
     320           3 :         break;;
     321           0 :     case HTTPVERSION_HTTP09:
     322             :     case HTTPVERSION_HTTP10:
     323             :     case HTTPVERSION_HTTP2:
     324             :     case HTTPVERSION_HTTP3:
     325             :     case HTTPVERSION_INVALID:
     326             :     default:
     327           0 :         fprintf(stderr,"HTTP version not supported\n");
     328           0 :         exit(EXIT_FAILURE);
     329             :         break;;
     330             :     }
     331           3 :     strcat(retval, "\r\n");
     332           3 :     return retval;
     333             : }
     334             : 
     335          36 : static char* strconcat(char* dst, char* src) {
     336             :     char* newdst;
     337             : 
     338          36 :     assert(dst);
     339          36 :     assert(src);
     340             : 
     341          36 :     if (NULL==(newdst=realloc(dst,strlen(dst)+strlen(src)+1))) {
     342           0 :         perror("strconcat");
     343           0 :         return NULL;
     344             :     }
     345          36 :     dst = newdst;
     346          36 :     strcat(dst,src);
     347          36 :     return dst;
     348             : }
     349             : 
     350           3 : char*         HTTP_buildheaders(const HTTP_t* http) {
     351           3 :     char* headers=NULL;
     352             :     sclistrecord_t* sclistrecord;
     353             :     HTTPHeader_t* header;
     354             : 
     355           3 :     assert(http);
     356           3 :     assert(http->headers);
     357           3 :     assert(http->body);
     358             : 
     359           3 :     if (NULL==(headers=strdup(""))) {
     360           0 :         perror("HTTP_buildheaders headers");
     361           0 :         return NULL;
     362             :     }
     363          12 :     for (sclistrecord=sclist_firstrecord(http->headers); sclistrecord; sclistrecord = sclist_nextrecord(sclistrecord)) {
     364           9 :         header = sclist_getvalue(sclistrecord);
     365           9 :         assert(header);
     366             :         /** @todo: OOM tests */
     367           9 :         headers=strconcat(headers,HTTPHeader_getname(header));
     368           9 :         headers=strconcat(headers,": ");
     369           9 :         headers=strconcat(headers,HTTPHeader_getvalue(header));
     370           9 :         headers=strconcat(headers,"\r\n");
     371             :     }
     372             : 
     373             : 
     374           3 :     return headers;
     375             : }

Generated by: LCOV version 1.16