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

Generated by: LCOV version 1.16