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-19 21:50 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 : 22 : #include <stdlib.h> 23 : #include <stdio.h> 24 : #include <string.h> 25 : #include <assert.h> 26 : 27 : typedef struct HTTPHeaders_s { 28 : char* name; 29 : char* value; 30 : } HTTPHeaders_t; 31 : 32 : typedef struct HTTP_s { 33 : sclist_t* headers; 34 : char* body; 35 : } HTTP_t; 36 : 37 3 : HTTP_t* HTTP_new() { 38 : HTTP_t* retval; 39 : 40 3 : if (NULL==(retval=malloc(sizeof(struct HTTP_s)))) { 41 0 : perror("HTTP_new"); 42 0 : exit(EXIT_FAILURE); 43 : } 44 : 45 3 : retval->headers = sclist_new(); 46 : 47 3 : if (NULL==(retval->body=malloc(1))) { 48 0 : perror("HTTP_new empty body allocation"); 49 0 : exit(EXIT_FAILURE); 50 : }; 51 3 : retval->body[0]=0; 52 : 53 3 : return retval; 54 : } 55 : 56 3 : void HTTP_addheader(HTTP_t* http, const char* name, const char* value) { 57 : HTTPHeaders_t* headers; 58 : 59 3 : assert(http); 60 3 : assert(http->headers); 61 3 : assert(http->body); 62 : 63 3 : if (NULL==(headers=malloc(sizeof(struct HTTPHeaders_s)))) { 64 0 : perror("HTTP_addheader"); 65 0 : exit(EXIT_FAILURE); 66 : } 67 3 : if (NULL==(headers->name=malloc(strlen(name)+1))) { 68 0 : perror("HTTP_addheader name malloc"); 69 0 : exit(EXIT_FAILURE); 70 : } 71 3 : strcpy(headers->name,name); 72 3 : if (NULL==(headers->value=malloc(strlen(value)+1))) { 73 0 : perror("HTTP_addheader value malloc"); 74 0 : exit(EXIT_FAILURE); 75 : } 76 3 : strcpy(headers->value,value); 77 3 : sclist_addrecord(http->headers,headers); 78 3 : } 79 : 80 3 : char* HTTP_getheaders(const HTTP_t* http) { 81 3 : sclistrecord_t* current = NULL; 82 : char* retval; 83 : 84 3 : assert(http); 85 3 : assert(http->headers); 86 3 : assert(http->body); 87 : 88 3 : if (NULL==(retval=malloc(1))) { 89 0 : perror("HTTP_getheaders initial malloc"); 90 0 : exit(EXIT_FAILURE); 91 : }; 92 3 : retval[0]=0; 93 : 94 3 : current = sclist_firstrecord(http->headers); 95 6 : while (current) { 96 3 : HTTPHeaders_t* header=sclist_getvalue(current); 97 3 : if (header->name && header->value) { 98 : char* newretval; 99 3 : if (NULL==(newretval=realloc(retval,strlen(retval)+strlen(header->name)+2+strlen(header->value)+2+1))) { 100 0 : perror("HTTP_getheaders inner loop"); 101 0 : exit(EXIT_FAILURE); 102 : } 103 3 : retval=newretval; 104 3 : strcat(retval,header->name); 105 3 : strcat(retval,": "); 106 3 : strcat(retval,header->value); 107 3 : strcat(retval,"\r\n"); 108 : } 109 3 : current=sclist_nextrecord(current); 110 : } 111 3 : return retval; 112 : } 113 : 114 3 : void HTTP_del(HTTP_t* http) { 115 : sclistrecord_t* current; 116 : 117 3 : assert(http); 118 3 : assert(http->headers); 119 3 : assert(http->body); 120 : 121 3 : current = sclist_firstrecord(http->headers); 122 6 : while (current) { 123 3 : HTTPHeaders_t* header=sclist_getvalue(current); 124 3 : if (header->name) 125 3 : free(header->name); 126 3 : if (header->value) 127 3 : free(header->value); 128 3 : current = sclist_nextrecord(current); 129 : } 130 : 131 3 : free(http->body); 132 3 : free(http); 133 3 : } 134 : 135 3 : char* HTTP_getrequest(const HTTPMethod_t method, const char* uri, const HTTPVersion_t version) { 136 : char* retval; 137 : 138 3 : assert(method<=HTTPMETHOD_INVALID); 139 3 : assert(version<=HTTPVERSION_INVALID); 140 : 141 3 : if (NULL==(retval=malloc(7+1+strlen(uri)+1+8+1))) { 142 0 : perror("HTTP_getrequest"); 143 0 : exit(EXIT_FAILURE); 144 : } 145 3 : retval[0]=0; 146 : 147 3 : switch(method) { 148 3 : case HTTPMETHOD_GET: 149 3 : strcat(retval, "GET"); 150 3 : break;; 151 0 : case HTTPMETHOD_HEAD: 152 : case HTTPMETHOD_POST: 153 : case HTTPMETHOD_PUT: 154 : case HTTPMETHOD_DELETE: 155 : case HTTPMETHOD_CONNECT: 156 : case HTTPMETHOD_OPTIONS: 157 : case HTTPMETHOD_TRACE: 158 : case HTTPMETHOD_PATCH: 159 : case HTTPMETHOD_INVALID: 160 : default: 161 0 : fprintf(stderr,"HTTP method not supported\n"); 162 0 : exit(EXIT_FAILURE); 163 : break;; 164 : } 165 3 : strcat(retval," "); 166 : 167 : /* NULL URI allowed, as an empty string */ 168 3 : if (uri) 169 3 : strcat(retval, uri); 170 : 171 3 : strcat(retval," HTTP/"); 172 3 : switch(version) { 173 3 : case HTTPVERSION_HTTP11: 174 : case HTTPVERSION_HTTP11b: 175 3 : strcat(retval, "1.1"); 176 3 : break;; 177 0 : case HTTPVERSION_HTTP09: 178 : case HTTPVERSION_HTTP10: 179 : case HTTPVERSION_HTTP2: 180 : case HTTPVERSION_HTTP3: 181 : case HTTPVERSION_INVALID: 182 : default: 183 0 : fprintf(stderr,"HTTP version not supported\n"); 184 0 : exit(EXIT_FAILURE); 185 : break;; 186 : } 187 3 : strcat(retval, "\r\n"); 188 3 : return retval; 189 : } 190 : 191 3 : void HTTP_setbody(HTTP_t* http, const char* body) { 192 : char* newbody; 193 : 194 3 : assert(http); 195 3 : assert(http->body); 196 : 197 3 : if (NULL==(newbody=realloc(http->body,strlen(body)+1))) { 198 0 : perror("HTTP_setbody"); 199 0 : exit(EXIT_FAILURE); 200 : } 201 : 202 3 : http->body = newbody; 203 3 : strcpy(http->body, body); 204 3 : } 205 : 206 3 : char* HTTP_getbody(HTTP_t* http) { 207 3 : assert(http); 208 3 : assert(http->body); 209 : 210 3 : return http->body; 211 : }