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

Generated by: LCOV version 1.16