LCOV - code coverage report
Current view: top level - src - libhttp.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 159 246 64.6 %
Date: 2024-11-24 17:24:48 Functions: 16 19 84.2 %

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

Generated by: LCOV version 1.16