LCOV - code coverage report
Current view: top level - src - libhttp.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 115 238 48.3 %
Date: 2024-11-23 16:58:52 Functions: 13 19 68.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-23 16:25
      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* self;
      35             :     char* name;
      36             :     char* value;
      37             : } HTTPHeader_t;
      38             : 
      39           0 : HTTPHeader_t* HTTPHeader_setname(HTTPHeader_t* header, const char* name) {
      40             :     char* newname;
      41             : 
      42           0 :     assert(header);
      43           0 :     assert(header->name);
      44           0 :     assert(header->value);
      45             : 
      46           0 :     if (NULL==(newname=realloc(header->name,strlen(name)+1))) {
      47           0 :         perror("HTTPHeader_setname realloc");
      48           0 :         return NULL;
      49             :     }
      50           0 :     header->name = newname;
      51           0 :     strcpy(header->name,name);
      52           0 :     return header;
      53             : }
      54             : 
      55           9 : char* HTTPHeader_getname(HTTPHeader_t* header) {
      56           9 :     assert(header);
      57           9 :     assert(header->name);
      58           9 :     assert(header->value);
      59             : 
      60           9 :     return header->name;
      61             : }
      62             : 
      63           0 : HTTPHeader_t* HTTPHeader_setvalue(HTTPHeader_t* header, const char* value) {
      64             :     char* newvalue;
      65             : 
      66           0 :     assert(header);
      67           0 :     assert(header->name);
      68           0 :     assert(header->value);
      69             : 
      70           0 :     if (NULL==(newvalue=realloc(header->value,strlen(value)+1))) {
      71           0 :         perror("HTTPHeader_setvalue realloc");
      72           0 :         return NULL;
      73             :     }
      74           0 :     header->value = newvalue;
      75           0 :     strcpy(header->value,value);
      76           0 :     return header;
      77             : }
      78             : 
      79           9 : char* HTTPHeader_getvalue(HTTPHeader_t* header) {
      80           9 :     assert(header);
      81           9 :     assert(header->name);
      82           9 :     assert(header->value);
      83             : 
      84           9 :     return header->value;
      85             : }
      86             : 
      87          15 : static HTTPHeader_t* HTTPHeader_new(const char* name, const char* value) {
      88             :     HTTPHeader_t* header;
      89             : 
      90          15 :     assert(name);
      91          15 :     assert(value);
      92             : 
      93          15 :     if (NULL==(header=malloc(sizeof(struct HTTPHeader_s)))) {
      94           0 :         perror("HTTPHeader_new");
      95           0 :         return NULL;
      96             :     }
      97          15 :     if (NULL==(header->name=strdup(name))) {
      98           0 :         perror("HTTPHeader_new name");
      99           0 :         free(header);
     100           0 :         return NULL;
     101             :     }
     102          15 :     if (NULL==(header->value=strdup(value))) {
     103           0 :         perror("HTTPHeader_new value");
     104           0 :         free(header->name);
     105           0 :         free(header);
     106           0 :         return NULL;
     107             :     }
     108          15 :     header->self = NULL;
     109          15 :     return header;
     110             : }
     111             : 
     112          15 : static void HTTPHeader_del(HTTPHeader_t* header) {
     113          15 :     assert(header);
     114          15 :     assert(header->name);
     115          15 :     assert(header->value);
     116             : 
     117          15 :     free(header->name);
     118          15 :     free(header->value);
     119          15 :     free(header);
     120          15 : }
     121             : 
     122          23 : HTTP_t* HTTP_new() {
     123             :     HTTP_t* http;
     124             : 
     125          23 :     if (NULL==(http=malloc(sizeof(struct HTTP_s)))) {
     126           0 :         perror("HTTP_new HTTP_s");
     127           0 :         return NULL;
     128             :     }
     129             : 
     130          23 :     if (NULL==(http->body=strdup(""))) {
     131           0 :         perror("HTTP_new body");
     132           0 :         free(http);
     133           0 :         return NULL;
     134             :     };
     135             : 
     136          23 :     if (NULL==(http->headers = sclist_new())) {
     137           0 :         perror("HTTP_new headers");
     138           0 :         free(http->body);
     139           0 :         free(http);
     140           0 :         return NULL;
     141             :     }
     142             : 
     143          23 :     return http;
     144             : }
     145             : 
     146          23 : void HTTP_del(HTTP_t* http) {
     147             :     sclistrecord_t* headerlst_entry;
     148             : 
     149          23 :     assert(http);
     150          21 :     assert(http->headers);
     151          21 :     assert(http->body);
     152             : 
     153          21 :     headerlst_entry = sclist_firstrecord(http->headers);
     154          36 :     while (headerlst_entry) {
     155             :         HTTPHeader_t* headerlst_value;
     156             : 
     157             :         /* Free payload */
     158          15 :         headerlst_value=sclist_getvalue(headerlst_entry);
     159          15 :         HTTPHeader_del(headerlst_value);
     160             : 
     161             :         /* Remember the entry to delete before moving to the next one */
     162          15 :         sclistrecord_t* tmp = headerlst_entry;
     163          15 :         headerlst_entry = sclist_nextrecord(headerlst_entry);
     164          15 :         sclist_remrecord(http->headers,tmp);
     165             :     }
     166             : 
     167          21 :     free(http->body);
     168          21 :     free(http);
     169          21 : }
     170             : 
     171          19 : HTTP_t* HTTP_setbody(HTTP_t* http, const char* body) {
     172             :     char* newbody;
     173             : 
     174          19 :     assert(http);
     175          17 :     assert(http->headers);
     176          17 :     assert(http->body);
     177          17 :     assert(body);
     178             : 
     179          15 :     if (NULL==(newbody=realloc(http->body,strlen(body)+1))) {
     180           0 :         perror("HTTP_setbody realloc");
     181           0 :         return NULL;
     182             :     }
     183             : 
     184          15 :     http->body = newbody;
     185          15 :     strcpy(http->body, body);
     186          15 :     return http;
     187             : }
     188             : 
     189          20 : char* HTTP_getbody(HTTP_t* http) {
     190          20 :     assert(http);
     191          18 :     assert(http->headers);
     192          18 :     assert(http->body);
     193             : 
     194          18 :     return http->body;
     195             : }
     196             : 
     197          15 : HTTPHeader_t* HTTP_addheader(HTTP_t* http, const char* name, const char* value) {
     198             :     HTTPHeader_t* header;
     199             : 
     200          15 :     assert(http);
     201          15 :     assert(http->headers);
     202          15 :     assert(http->body);
     203          15 :     assert(name);
     204          15 :     assert(value);
     205             : 
     206          15 :     if (NULL==(header = HTTPHeader_new(name,value))) {
     207           0 :         perror("HTTP_addheader HTTPHeader_new");
     208           0 :         return NULL;
     209             :     }
     210             : 
     211          15 :     if (NULL==(header->self=sclist_addrecord(http->headers,header))) {
     212           0 :         perror("HTTP_addheader");
     213           0 :         HTTPHeader_del(header);
     214           0 :         return NULL;
     215             :     }
     216          15 :     return header;
     217             : }
     218             : 
     219           0 : HTTPHeader_t* HTTP_addbasicauth(HTTP_t* http, const char* login, const char* pass) {
     220             :     char* auth_encoded;
     221             :     char* auth;
     222             :     HTTPHeader_t* header;
     223             : 
     224           0 :     assert(http);
     225           0 :     assert(http->headers);
     226           0 :     assert(http->body);
     227           0 :     assert(login);
     228           0 :     assert(pass);
     229             : 
     230           0 :     if (NULL==(auth=malloc(strlen(login)+1+strlen(pass)+1))) {
     231           0 :         perror("HTTP_addbasicauth credentials");
     232           0 :         return NULL;
     233             :     }
     234             :     /* strcpy/strcat expected to be faster than sprintf */
     235           0 :     strcpy(auth,login);
     236           0 :     strcat(auth,":");
     237           0 :     strcat(auth,pass);
     238           0 :     auth_encoded=base64_encode(auth);
     239           0 :     free(auth);
     240             : 
     241           0 :     if (NULL==(auth=malloc(strlen("Basic ")+strlen(auth_encoded)+1))) {
     242           0 :         perror("HTTP_addbasicauth value");
     243           0 :         free(auth_encoded);
     244           0 :         return NULL;
     245             :     }
     246           0 :     strcpy(auth,"Basic ");
     247           0 :     strcat(auth, auth_encoded);
     248           0 :     free(auth_encoded);
     249             : 
     250           0 :     if (NULL==(header=HTTP_addheader(http,"Authorization",auth))) {
     251           0 :         perror("HTTP_addbasicauth addheader");
     252           0 :         free(auth);
     253           0 :         return NULL;
     254             :     }
     255           0 :     free(auth);
     256           0 :     return header;
     257             : }
     258             : 
     259           3 : HTTPHeader_t* HTTP_firstheader(const HTTP_t* http) {
     260             :     sclistrecord_t* headerlst_entry;
     261             : 
     262           3 :     assert(http);
     263           3 :     assert(http->headers);
     264           3 :     assert(http->body);
     265             : 
     266           3 :     if (NULL==(headerlst_entry=sclist_firstrecord(http->headers)))
     267           3 :         return NULL;
     268           0 :     return sclist_getvalue(headerlst_entry);
     269             : }
     270             : 
     271           0 : HTTPHeader_t* HTTP_nextheader(const HTTPHeader_t* header) {
     272             :     sclistrecord_t* headerlst_entry;
     273             : 
     274           0 :     assert(header);
     275           0 :     assert(header->self);
     276           0 :     assert(header->name);
     277           0 :     assert(header->value);
     278             : 
     279           0 :     headerlst_entry = header->self;
     280             : 
     281             :     /* EOL reached */
     282           0 :     if (NULL==(headerlst_entry = sclist_nextrecord(headerlst_entry)))
     283           0 :         return NULL;
     284             :     else
     285           0 :         return sclist_getvalue(headerlst_entry);
     286             : }
     287             : 
     288           0 : HTTPHeader_t* HTTP_findheader(const HTTPHeader_t* start, const char* name) {
     289             :     sclistrecord_t* headerlst_entry;
     290             : 
     291           0 :     assert(start);
     292           0 :     assert(start->self);
     293           0 :     assert(start->name);
     294           0 :     assert(start->value);
     295           0 :     assert(name);
     296             : 
     297           0 :     headerlst_entry = start->self;
     298             : 
     299           0 :     while (headerlst_entry) {
     300             :         HTTPHeader_t* headerlst_value;
     301             :         char* header_name;
     302             : 
     303           0 :         headerlst_value = sclist_getvalue(headerlst_entry);
     304           0 :         assert(headerlst_value);
     305           0 :         header_name = HTTPHeader_getname(headerlst_value);
     306           0 :         if (0==strcmp(header_name,name))
     307           0 :             break;
     308           0 :         headerlst_entry=sclist_nextrecord(headerlst_entry);
     309             :     }
     310           0 :     if (!headerlst_entry)
     311             :         /* Not found */
     312           0 :         return NULL;
     313             :     else
     314             :         /* Found */
     315           0 :         return sclist_getvalue(headerlst_entry);
     316             : }
     317             : 
     318             : 
     319           0 : HTTP_t* HTTP_remheader(HTTP_t* http, HTTPHeader_t* header) {
     320           0 :     assert(http);
     321           0 :     assert(http->headers);
     322           0 :     assert(http->body);
     323           0 :     assert(header);
     324           0 :     assert(header->self);
     325           0 :     assert(header->name);
     326           0 :     assert(header->value);
     327             : 
     328             :     /* Remove from the header list */
     329           0 :     sclist_remrecord(http->headers, header->self);
     330             :     /** @todo: make sclist_remrecord return a status (found/notfound) and use it */
     331             : #if 0
     332             :     if (NULL==sclist_remrecord(http->headers, header->sclistrecord)) {
     333             :         perror("HTTP_addheader");
     334             :         return NULL;
     335             :     }
     336             : #endif
     337           0 :     header->self = NULL;
     338           0 :     HTTPHeader_del(header);
     339             : 
     340           0 :     return http;
     341             : }
     342             : 
     343          36 : static char* strconcat(char* dst, char* src) {
     344             :     char* newdst;
     345             : 
     346          36 :     assert(dst);
     347          36 :     assert(src);
     348             : 
     349          36 :     if (NULL==(newdst=realloc(dst,strlen(dst)+strlen(src)+1))) {
     350           0 :         perror("strconcat");
     351           0 :         return NULL;
     352             :     }
     353          36 :     dst = newdst;
     354          36 :     strcat(dst,src);
     355          36 :     return dst;
     356             : }
     357             : 
     358           3 : char* HTTP_buildheaders(const HTTP_t* http) {
     359           3 :     char* headers_str=NULL;
     360             :     sclistrecord_t* headerlst_entry;
     361             : 
     362           3 :     assert(http);
     363           3 :     assert(http->headers);
     364           3 :     assert(http->body);
     365             : 
     366           3 :     if (NULL==(headers_str=strdup(""))) {
     367           0 :         perror("HTTP_buildheaders headers_str");
     368           0 :         return NULL;
     369             :     }
     370          12 :     for (headerlst_entry=sclist_firstrecord(http->headers); headerlst_entry; headerlst_entry = sclist_nextrecord(headerlst_entry)) {
     371             :         HTTPHeader_t* headerlst_value;
     372             : 
     373           9 :         headerlst_value = sclist_getvalue(headerlst_entry);
     374           9 :         assert(headerlst_value);
     375             :         /** @todo: OOM tests */
     376           9 :         headers_str=strconcat(headers_str,HTTPHeader_getname(headerlst_value));
     377           9 :         headers_str=strconcat(headers_str,": ");
     378           9 :         headers_str=strconcat(headers_str,HTTPHeader_getvalue(headerlst_value));
     379           9 :         headers_str=strconcat(headers_str,"\r\n");
     380             :     }
     381             : 
     382           3 :     return headers_str;
     383             : }
     384             : 
     385             : 
     386             : 
     387             : 
     388             : 
     389             : 
     390             : 
     391             : 
     392             : 
     393             : 
     394             : 
     395             : 
     396             : 
     397           3 : char* HTTP_buildrequest(const HTTPMethod_t method, const char* uri, const HTTPVersion_t version) {
     398             :     char* retval;
     399             : 
     400           3 :     assert(method<=HTTPMETHOD_INVALID);
     401           3 :     assert(uri);
     402           3 :     assert(version<=HTTPVERSION_INVALID);
     403             : 
     404           3 :     if (NULL==(retval=malloc(7+1+strlen(uri)+1+8+1))) {
     405           0 :         perror("HTTP_getrequest");
     406           0 :         exit(EXIT_FAILURE);
     407             :     }
     408           3 :     retval[0]=0;
     409             : 
     410           3 :     switch(method) {
     411           3 :     case HTTPMETHOD_GET:
     412           3 :         strcat(retval, "GET");
     413           3 :         break;;
     414           0 :     case HTTPMETHOD_HEAD:
     415             :     case HTTPMETHOD_POST:
     416             :     case HTTPMETHOD_PUT:
     417             :     case HTTPMETHOD_DELETE:
     418             :     case HTTPMETHOD_CONNECT:
     419             :     case HTTPMETHOD_OPTIONS:
     420             :     case HTTPMETHOD_TRACE:
     421             :     case HTTPMETHOD_PATCH:
     422             :     case HTTPMETHOD_INVALID:
     423             :     default:
     424           0 :         fprintf(stderr,"HTTP method not supported\n");
     425           0 :         exit(EXIT_FAILURE);
     426             :         break;;
     427             :     }
     428             : 
     429           3 :     if (uri[0]) {
     430           3 :         strcat(retval," ");
     431           3 :         strcat(retval, uri);
     432             :     }
     433             : 
     434           3 :     strcat(retval," HTTP/");
     435           3 :     switch(version) {
     436           3 :     case HTTPVERSION_HTTP11:
     437             :     case HTTPVERSION_HTTP11b:
     438           3 :         strcat(retval, "1.1");
     439           3 :         break;;
     440           0 :     case HTTPVERSION_HTTP09:
     441             :     case HTTPVERSION_HTTP10:
     442             :     case HTTPVERSION_HTTP2:
     443             :     case HTTPVERSION_HTTP3:
     444             :     case HTTPVERSION_INVALID:
     445             :     default:
     446           0 :         fprintf(stderr,"HTTP version not supported\n");
     447           0 :         exit(EXIT_FAILURE);
     448             :         break;;
     449             :     }
     450           3 :     strcat(retval, "\r\n");
     451           3 :     return retval;
     452             : }
     453             : 

Generated by: LCOV version 1.16