LCOV - code coverage report
Current view: top level - src - libhttp.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 216 297 72.7 %
Date: 2024-11-29 19:32:20 Functions: 19 19 100.0 %

          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-29 19:13
      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             : #ifdef HAVE_CONFIG_H
      20             : #include "config.h"
      21             : #endif
      22             : 
      23             : #include "libhttp.h"
      24             : #include "sclist.h"
      25             : #include "base64.h"                             /* Base64 encoder and decoder */
      26             : 
      27             : #include <stdlib.h>
      28             : #include <stdio.h>
      29             : #include <string.h>
      30             : #include <assert.h>
      31             : 
      32             : typedef struct HTTP_s {
      33             :     sclist_t* headers;
      34             :     char* body;
      35             : } HTTP_t;
      36             : 
      37             : typedef struct HTTPHeader_s {
      38             :     sclistrecord_t* self;
      39             :     char* name;
      40             :     char* value;
      41             : } HTTPHeader_t;
      42             : 
      43          10 : HTTPHeader_t* HTTPHeader_setname(HTTPHeader_t* header, const char* name) {
      44             :     char* newname;
      45             : 
      46          10 :     assert(header);
      47           8 :     assert(header->name);
      48           8 :     assert(header->value);
      49           8 :     assert(name);
      50             : 
      51           6 :     if (name[0]==0) {
      52           3 :         fprintf(stderr,"ERROR: HTTP_setname empty name\n");
      53           3 :         return NULL;
      54             :     }
      55             : 
      56           3 :     if (NULL==(newname=realloc(header->name,strlen(name)+1))) {
      57           0 :         perror("ERROR: HTTPHeader_setname realloc");
      58           0 :         return NULL;
      59             :     }
      60           3 :     header->name = newname;
      61           3 :     strcpy(header->name,name);
      62           3 :     return header;
      63             : }
      64             : 
      65         222 : char* HTTPHeader_getname(HTTPHeader_t* header) {
      66         222 :     assert(header);
      67         220 :     assert(header->name);
      68         220 :     assert(header->value);
      69             : 
      70         220 :     return header->name;
      71             : }
      72             : 
      73          10 : HTTPHeader_t* HTTPHeader_setvalue(HTTPHeader_t* header, const char* value) {
      74             :     char* newvalue;
      75             : 
      76          10 :     assert(header);
      77           8 :     assert(header->name);
      78           8 :     assert(header->value);
      79           8 :     assert(value);
      80             : 
      81           6 :     if (NULL==(newvalue=realloc(header->value,strlen(value)+1))) {
      82           0 :         perror("ERROR: HTTPHeader_setvalue realloc");
      83           0 :         return NULL;
      84             :     }
      85           6 :     header->value = newvalue;
      86           6 :     strcpy(header->value,value);
      87           6 :     return header;
      88             : }
      89             : 
      90         153 : char* HTTPHeader_getvalue(HTTPHeader_t* header) {
      91         153 :     assert(header);
      92         151 :     assert(header->name);
      93         151 :     assert(header->value);
      94             : 
      95         151 :     return header->value;
      96             : }
      97             : 
      98         195 : static HTTPHeader_t* HTTPHeader_new(const char* name, const char* value) {
      99             :     HTTPHeader_t* header;
     100             : 
     101         195 :     assert(name);
     102         195 :     assert(value);
     103             : 
     104         195 :     if (NULL==(header=malloc(sizeof(struct HTTPHeader_s)))) {
     105           0 :         perror("ERROR: HTTPHeader_new");
     106           0 :         return NULL;
     107             :     }
     108         195 :     if (NULL==(header->name=strdup(name))) {
     109           0 :         perror("ERROR: HTTPHeader_new name");
     110           0 :         free(header);
     111           0 :         return NULL;
     112             :     }
     113         195 :     if (NULL==(header->value=strdup(value))) {
     114           0 :         perror("ERROR: HTTPHeader_new value");
     115           0 :         free(header->name);
     116           0 :         free(header);
     117           0 :         return NULL;
     118             :     }
     119         195 :     header->self = NULL;
     120         195 :     return header;
     121             : }
     122             : 
     123         183 : static void HTTPHeader_del(HTTPHeader_t* header) {
     124         183 :     assert(header);
     125         183 :     assert(header->name);
     126         183 :     assert(header->value);
     127             : 
     128         183 :     free(header->name);
     129         183 :     free(header->value);
     130         183 :     free(header);
     131         183 : }
     132             : 
     133         142 : HTTP_t* HTTP_new() {
     134             :     HTTP_t* http;
     135             : 
     136         142 :     if (NULL==(http=malloc(sizeof(struct HTTP_s)))) {
     137           0 :         perror("ERROR: HTTP_new HTTP_s");
     138           0 :         return NULL;
     139             :     }
     140             : 
     141         142 :     if (NULL==(http->body=strdup(""))) {
     142           0 :         perror("ERROR: HTTP_new body");
     143           0 :         free(http);
     144           0 :         return NULL;
     145             :     };
     146             : 
     147         142 :     if (NULL==(http->headers = sclist_new())) {
     148           0 :         fprintf(stderr,"ERROR: HTTP_new headers\n");
     149           0 :         free(http->body);
     150           0 :         free(http);
     151           0 :         return NULL;
     152             :     }
     153             : 
     154         142 :     return http;
     155             : }
     156             : 
     157         122 : void HTTP_del(HTTP_t* http) {
     158             :     sclistrecord_t* headerlst_entry;
     159             : 
     160         122 :     assert(http);
     161         120 :     assert(http->headers);
     162         120 :     assert(http->body);
     163             : 
     164         120 :     headerlst_entry = sclist_firstrecord(http->headers);
     165         291 :     while (headerlst_entry) {
     166             :         HTTPHeader_t* headerlst_value;
     167             : 
     168             :         /* Free payload */
     169         171 :         headerlst_value=sclist_getvalue(headerlst_entry);
     170         171 :         HTTPHeader_del(headerlst_value);
     171             : 
     172             :         /* Remember the entry to delete before moving to the next one */
     173         171 :         sclistrecord_t* tmp = headerlst_entry;
     174         171 :         headerlst_entry = sclist_nextrecord(headerlst_entry);
     175         171 :         sclist_remrecord(http->headers,tmp);
     176             :     }
     177         120 :     sclist_del(http->headers);
     178             : 
     179         120 :     free(http->body);
     180         120 :     free(http);
     181         120 : }
     182             : 
     183          19 : HTTP_t* HTTP_setbody(HTTP_t* http, const char* body) {
     184             :     char* newbody;
     185             : 
     186          19 :     assert(http);
     187          17 :     assert(http->headers);
     188          17 :     assert(http->body);
     189          17 :     assert(body);
     190             : 
     191          15 :     if (NULL==(newbody=realloc(http->body,strlen(body)+1))) {
     192           0 :         perror("ERROR: HTTP_setbody realloc");
     193           0 :         return NULL;
     194             :     }
     195             : 
     196          15 :     http->body = newbody;
     197          15 :     strcpy(http->body, body);
     198          15 :     return http;
     199             : }
     200             : 
     201          17 : char* HTTP_getbody(HTTP_t* http) {
     202          17 :     assert(http);
     203          15 :     assert(http->headers);
     204          15 :     assert(http->body);
     205             : 
     206          15 :     return http->body;
     207             : }
     208             : 
     209         204 : HTTPHeader_t* HTTP_addheader(HTTP_t* http, const char* name, const char* value) {
     210             :     HTTPHeader_t* header;
     211             : 
     212         204 :     assert(http);
     213         202 :     assert(http->headers);
     214         202 :     assert(http->body);
     215         202 :     assert(name);
     216         200 :     assert(value);
     217             : 
     218         198 :     if (name[0]==0) {
     219           3 :         fprintf(stderr,"ERROR: HTTP_addheader empty name\n");
     220           3 :         return NULL;
     221             :     }
     222             : 
     223         195 :     if (NULL==(header = HTTPHeader_new(name,value))) {
     224           0 :         fprintf(stderr,"ERROR: HTTP_addheader HTTPHeader_new\n");
     225           0 :         return NULL;
     226             :     }
     227             : 
     228         195 :     if (NULL==(header->self=sclist_addrecord(http->headers,header))) {
     229           0 :         fprintf(stderr,"ERROR: HTTP_addheader\n");
     230           0 :         HTTPHeader_del(header);
     231           0 :         return NULL;
     232             :     }
     233         195 :     return header;
     234             : }
     235             : 
     236          15 : HTTPHeader_t* HTTP_addbasicauth(HTTP_t* http, const char* login, const char* pass) {
     237             :     char* auth_encoded;
     238             :     char* auth;
     239             :     HTTPHeader_t* header;
     240             : 
     241          15 :     assert(http);
     242          13 :     assert(http->headers);
     243          13 :     assert(http->body);
     244          13 :     assert(login);
     245          11 :     assert(pass);
     246             : 
     247           9 :     if (NULL==(auth=malloc(strlen(login)+1+strlen(pass)+1))) {
     248           0 :         perror("ERROR: HTTP_addbasicauth credentials");
     249           0 :         return NULL;
     250             :     }
     251             :     /* strcpy/strcat expected to be faster than sprintf */
     252           9 :     strcpy(auth,login);
     253           9 :     strcat(auth,":");
     254           9 :     strcat(auth,pass);
     255           9 :     auth_encoded=base64_encode(auth);
     256           9 :     free(auth);
     257             : 
     258           9 :     if (NULL==(auth=malloc(strlen("Basic ")+strlen(auth_encoded)+1))) {
     259           0 :         perror("ERROR: HTTP_addbasicauth value");
     260           0 :         free(auth_encoded);
     261           0 :         return NULL;
     262             :     }
     263           9 :     strcpy(auth,"Basic ");
     264           9 :     strcat(auth, auth_encoded);
     265           9 :     free(auth_encoded);
     266             : 
     267           9 :     if (NULL==(header=HTTP_addheader(http,"Authorization",auth))) {
     268           0 :         fprintf(stderr,"ERROR: HTTP_addbasicauth addheader\n");
     269           0 :         free(auth);
     270           0 :         return NULL;
     271             :     }
     272           9 :     free(auth);
     273           9 :     return header;
     274             : }
     275             : 
     276          84 : HTTPHeader_t* HTTP_firstheader(const HTTP_t* http) {
     277             :     sclistrecord_t* headerlst_entry;
     278             : 
     279          84 :     assert(http);
     280          82 :     assert(http->headers);
     281          82 :     assert(http->body);
     282             : 
     283          82 :     if (NULL==(headerlst_entry=sclist_firstrecord(http->headers)))
     284          11 :         return NULL;
     285          71 :     return sclist_getvalue(headerlst_entry);
     286             : }
     287             : 
     288          52 : HTTPHeader_t* HTTP_nextheader(const HTTPHeader_t* header) {
     289             :     sclistrecord_t* headerlst_entry;
     290             : 
     291          52 :     assert(header);
     292          48 :     assert(header->self);
     293          48 :     assert(header->name);
     294          48 :     assert(header->value);
     295             : 
     296          48 :     headerlst_entry = header->self;
     297             : 
     298             :     /* EOL reached */
     299          48 :     if (NULL==(headerlst_entry = sclist_nextrecord(headerlst_entry)))
     300          21 :         return NULL;
     301             :     else
     302          27 :         return sclist_getvalue(headerlst_entry);
     303             : }
     304             : 
     305          37 : HTTPHeader_t* HTTP_findheader(const HTTPHeader_t* start, const char* name) {
     306             :     sclistrecord_t* headerlst_entry;
     307             : 
     308          37 :     assert(start);
     309          35 :     assert(start->self);
     310          35 :     assert(start->name);
     311          35 :     assert(start->value);
     312          35 :     assert(name);
     313             : 
     314          33 :     headerlst_entry = start->self;
     315             : 
     316          75 :     while (headerlst_entry) {
     317             :         HTTPHeader_t* headerlst_value;
     318             :         char* header_name;
     319             : 
     320          69 :         headerlst_value = sclist_getvalue(headerlst_entry);
     321          69 :         assert(headerlst_value);
     322          69 :         header_name = HTTPHeader_getname(headerlst_value);
     323          69 :         if (0==strcmp(header_name,name))
     324          27 :             break;
     325          42 :         headerlst_entry=sclist_nextrecord(headerlst_entry);
     326             :     }
     327          33 :     if (!headerlst_entry)
     328             :         /* Not found */
     329           6 :         return NULL;
     330             :     else
     331             :         /* Found */
     332          27 :         return sclist_getvalue(headerlst_entry);
     333             : }
     334             : 
     335             : 
     336          19 : HTTP_t* HTTP_remheader(HTTP_t* http, HTTPHeader_t* header) {
     337          19 :     assert(http);
     338          17 :     assert(http->headers);
     339          17 :     assert(http->body);
     340          17 :     assert(header);
     341          15 :     assert(header->self);
     342          15 :     assert(header->name);
     343          15 :     assert(header->value);
     344             : 
     345             :     /* Remove from the header list */
     346          15 :     if (NULL==sclist_remrecord(http->headers, header->self)) {
     347           3 :         fprintf(stderr,"ERROR: HTTP_remheader not found\n");
     348           3 :         return NULL;
     349             :     }
     350          12 :     header->self = NULL;
     351          12 :     HTTPHeader_del(header);
     352             : 
     353          12 :     return http;
     354             : }
     355             : 
     356          87 : static char* strconcat(char* dst, const char* src) {
     357             :     char* newdst;
     358             : 
     359          87 :     assert(dst);
     360          87 :     assert(src);
     361             : 
     362          87 :     if (NULL==(newdst=realloc(dst,strlen(dst)+strlen(src)+1))) {
     363           0 :         perror("ERROR: strconcat");
     364           0 :         return NULL;
     365             :     }
     366          87 :     dst = newdst;
     367          87 :     strcat(dst,src);
     368          87 :     return dst;
     369             : }
     370             : 
     371          14 : char* HTTP_buildheaders(const HTTP_t* http) {
     372          14 :     char* headers_str=NULL;
     373             :     sclistrecord_t* headerlst_entry;
     374             : 
     375          14 :     assert(http);
     376          12 :     assert(http->headers);
     377          12 :     assert(http->body);
     378             : 
     379          12 :     if (NULL==(headers_str=strdup(""))) {
     380           0 :         perror("ERROR: HTTP_buildheaders headers_str");
     381           0 :         return NULL;
     382             :     }
     383          12 :     for (
     384          12 :         headerlst_entry=sclist_firstrecord(http->headers);
     385          27 :         headerlst_entry;
     386          15 :         headerlst_entry = sclist_nextrecord(headerlst_entry)
     387             :     ) {
     388             :         HTTPHeader_t* headerlst_value;
     389             : 
     390          15 :         headerlst_value = sclist_getvalue(headerlst_entry);
     391          15 :         assert(headerlst_value);
     392             :         /** @bug possible memory leak, check strconcat return values */
     393          15 :         headers_str=strconcat(headers_str,HTTPHeader_getname(headerlst_value));
     394          15 :         headers_str=strconcat(headers_str,": ");
     395          15 :         headers_str=strconcat(headers_str,HTTPHeader_getvalue(headerlst_value));
     396          15 :         headers_str=strconcat(headers_str,"\r\n");
     397             :     }
     398             : 
     399          12 :     return headers_str;
     400             : }
     401             : 
     402             : 
     403             : 
     404             : 
     405             : 
     406             : 
     407             : 
     408             : 
     409             : 
     410             : 
     411             : 
     412             : 
     413             : /* @todo redesign headers and implementation to identify different errors */
     414           3 : char* HTTP_buildrequest(const HTTP_t* http, const HTTPMethod_t method, const char* uri, const HTTPVersion_t version) {
     415             :     char* request;
     416             :     char* newrequest;
     417             :     char* headers;
     418             : 
     419           3 :     assert(method<=HTTPMETHOD_INVALID);
     420           3 :     assert(uri);
     421           3 :     assert(version<=HTTPVERSION_INVALID);
     422             : 
     423           3 :     if (NULL==(request=strdup(""))) {
     424           0 :         perror("ERROR: HTTP_getrequest strdup");
     425           0 :         return NULL;
     426             :     }
     427             : 
     428           3 :     switch(method) {
     429           3 :     case HTTPMETHOD_GET:
     430           3 :         if (NULL==(newrequest=strconcat(request, "GET"))) {
     431           0 :             fprintf(stderr,"ERROR: HTTP_buildrequest method\n");
     432           0 :             free(request);
     433           0 :             return NULL;
     434             :         }
     435           3 :         request = newrequest;
     436           3 :         break;;
     437           0 :     case HTTPMETHOD_HEAD:
     438             :     case HTTPMETHOD_POST:
     439             :     case HTTPMETHOD_PUT:
     440             :     case HTTPMETHOD_DELETE:
     441             :     case HTTPMETHOD_CONNECT:
     442             :     case HTTPMETHOD_OPTIONS:
     443             :     case HTTPMETHOD_TRACE:
     444             :     case HTTPMETHOD_PATCH:
     445             :     case HTTPMETHOD_INVALID:
     446             :     default:
     447           0 :         fprintf(stderr,"ERROR: HTTP method not supported\n");
     448           0 :         free(request);
     449           0 :         return NULL;
     450             :         break;;
     451             :     }
     452             : 
     453           3 :     if (uri[0]) {
     454           3 :         if (NULL==(newrequest=strconcat(request, " "))) {
     455           0 :             fprintf(stderr,"ERROR: HTTP_buildrequest uri\n");
     456           0 :             free(request);
     457           0 :             return NULL;
     458             :         }
     459           3 :         request = newrequest;
     460           3 :         if (NULL==(newrequest=strconcat(request, uri))) {
     461           0 :             fprintf(stderr,"ERROR: HTTP_buildrequest uri\n");
     462           0 :             free(request);
     463           0 :             return NULL;
     464             :         }
     465           3 :         request = newrequest;
     466             :     }
     467             : 
     468           3 :     if (NULL==(newrequest=strconcat(request, " HTTP/"))) {
     469           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest HTTP\n");
     470           0 :         free(request);
     471           0 :         return NULL;
     472             :     }
     473           3 :     request = newrequest;
     474           3 :     switch(version) {
     475           3 :     case HTTPVERSION_HTTP11:
     476             :     case HTTPVERSION_HTTP11b:
     477           3 :         if (NULL==(newrequest=strconcat(request, "1.1"))) {
     478           0 :             fprintf(stderr,"ERROR: HTTP_buildrequest Version\n");
     479           0 :             free(request);
     480           0 :             return NULL;
     481             :         }
     482           3 :         request = newrequest;
     483           3 :         break;;
     484           0 :     case HTTPVERSION_HTTP09:
     485             :     case HTTPVERSION_HTTP10:
     486             :     case HTTPVERSION_HTTP2:
     487             :     case HTTPVERSION_HTTP3:
     488             :     case HTTPVERSION_INVALID:
     489             :     default:
     490           0 :         fprintf(stderr,"PANIC: HTTP version not supported\n");
     491           0 :         free(request);
     492           0 :         return NULL;
     493             :         break;;
     494             :     }
     495           3 :     if (NULL==(newrequest=strconcat(request, "\r\n"))) {
     496           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest Version\n");
     497           0 :         free(request);
     498           0 :         return NULL;
     499             :     }
     500           3 :     request = newrequest;
     501             : 
     502           3 :     if (NULL==(headers=HTTP_buildheaders(http))) {
     503           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest buildheaders\n");
     504           0 :         free(request);
     505           0 :         return NULL;
     506             :     }
     507             : 
     508           3 :     if (NULL==(newrequest=strconcat(request, headers))) {
     509           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest header separator\n");
     510           0 :         free(request);
     511           0 :         return NULL;
     512             :     }
     513           3 :     request = newrequest;
     514           3 :     free(headers);
     515             : 
     516           3 :     if (NULL==(newrequest=strconcat(request, "\r\n"))) {
     517           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest header separator\n");
     518           0 :         free(request);
     519           0 :         return NULL;
     520             :     }
     521           3 :     request = newrequest;
     522             : 
     523           3 :     if (NULL==(newrequest=strconcat(request, http->body))) {
     524           0 :         fprintf(stderr,"ERROR: HTTP_buildrequest body\n");
     525           0 :         free(request);
     526           0 :         return NULL;
     527             :     }
     528           3 :     request = newrequest;
     529             : 
     530           3 :     return request;
     531             : }
     532             : 

Generated by: LCOV version 1.16