LCOV - code coverage report
Current view: top level - src - libhttp.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 199 248 80.2 %
Date: 2024-11-25 12:28:50 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-25 12:22
      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          19 : HTTP_t* HTTP_remheader(HTTP_t* http, HTTPHeader_t* header) {
     336          19 :     assert(http);
     337          17 :     assert(http->headers);
     338          17 :     assert(http->body);
     339          17 :     assert(header);
     340          15 :     assert(header->self);
     341          15 :     assert(header->name);
     342          15 :     assert(header->value);
     343             : 
     344             :     /* Remove from the header list */
     345          15 :     if (NULL==sclist_remrecord(http->headers, header->self)) {
     346           3 :         fprintf(stderr,"ERROR: HTTP_remheader not found\n");
     347           3 :         return NULL;
     348             :     }
     349          12 :     header->self = NULL;
     350          12 :     HTTPHeader_del(header);
     351             : 
     352          12 :     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("ERROR: 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("ERROR: 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("PANIC: 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,"PANIC: 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,"PANIC: 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