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-20 20:59
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 : char* name;
35 : char* value;
36 : } HTTPHeader_t;
37 :
38 6 : HTTPHeader_t* HTTPHeader_new(const char* name, const char* value) {
39 : HTTPHeader_t* header;
40 :
41 6 : assert(name);
42 6 : assert(value);
43 :
44 6 : if (NULL==(header=malloc(sizeof(struct HTTPHeader_s)))) {
45 0 : perror("HTTPHeader_new");
46 : } else {
47 6 : header->name = strdup(name);
48 6 : header->value = strdup(value);
49 : }
50 6 : return header;
51 : }
52 :
53 6 : void HTTPHeader_del(HTTPHeader_t* header) {
54 6 : assert(header);
55 6 : assert(header->name);
56 6 : assert(header->value);
57 :
58 6 : free(header->name);
59 6 : free(header->value);
60 6 : free(header);
61 6 : }
62 :
63 3 : HTTPHeader_t* HTTPHeader_basicauth(const char* login, const char* pass) {
64 3 : assert(login);
65 3 : assert(pass);
66 :
67 : /* Prepare basic authentication */
68 : char* auth_encoded;
69 : char* auth;
70 : {
71 3 : if (NULL==(auth=malloc(strlen("Basic ")+strlen(login)+1+strlen(pass)+1))) {
72 0 : perror("HTTPHeader_basicauth");
73 0 : return NULL;
74 : };
75 : /* strcpy/strcat expected to be faster than sprintf */
76 3 : strcpy(auth,login);
77 3 : strcat(auth,":");
78 3 : strcat(auth,pass);
79 3 : auth_encoded=base64_encode(auth);
80 3 : free(auth);
81 : }
82 :
83 3 : if (NULL==(auth=malloc(strlen("Basic ")+strlen(auth_encoded)+1))) {
84 0 : perror("HTTPHeader_basicauth");
85 0 : free(auth_encoded);
86 0 : return NULL;
87 : };
88 3 : strcpy(auth,"Basic ");
89 3 : strcat(auth, auth_encoded);
90 3 : return HTTPHeader_new("Authorization",auth);
91 : }
92 :
93 12 : char* HTTPHeader_getname(HTTPHeader_t* header) {
94 12 : assert(header);
95 12 : return header->name;
96 : }
97 :
98 12 : char* HTTPHeader_getvalue(HTTPHeader_t* header) {
99 12 : assert(header);
100 12 : return header->value;
101 : }
102 :
103 3 : HTTP_t* HTTP_new() {
104 : HTTP_t* retval;
105 :
106 3 : if (NULL==(retval=malloc(sizeof(struct HTTP_s)))) {
107 0 : perror("HTTP_new HTTP_s");
108 0 : return NULL;
109 : }
110 :
111 3 : if (NULL==(retval->body=malloc(1))) {
112 0 : perror("HTTP_new body");
113 0 : free(retval);
114 0 : return NULL;
115 : };
116 3 : retval->body[0]=0;
117 :
118 3 : if (NULL==(retval->headers = sclist_new())) {
119 0 : perror("HTTP_new headers");
120 0 : free(retval->body);
121 0 : free(retval);
122 0 : return NULL;
123 : }
124 :
125 3 : return retval;
126 : }
127 :
128 :
129 :
130 :
131 :
132 :
133 :
134 :
135 : typedef struct HTTPHeaders_s {
136 : char* name;
137 : char* value;
138 : } HTTPHeaders_t;
139 :
140 3 : void HTTP_del(HTTP_t* http) {
141 : sclistrecord_t* current;
142 :
143 3 : assert(http);
144 3 : assert(http->headers);
145 3 : assert(http->body);
146 :
147 : /* Use sclist_remrecord() and sclist_del() */
148 3 : current = sclist_firstrecord(http->headers);
149 6 : while (current) {
150 3 : HTTPHeaders_t* header=sclist_getvalue(current);
151 3 : if (header->name)
152 3 : free(header->name);
153 3 : if (header->value)
154 3 : free(header->value);
155 3 : current = sclist_nextrecord(current);
156 : }
157 :
158 3 : free(http->body);
159 3 : free(http);
160 3 : }
161 :
162 :
163 3 : void HTTP_addheader(HTTP_t* http, const char* name, const char* value) {
164 : HTTPHeaders_t* headers;
165 :
166 3 : assert(http);
167 3 : assert(http->headers);
168 3 : assert(http->body);
169 :
170 3 : if (NULL==(headers=malloc(sizeof(struct HTTPHeaders_s)))) {
171 0 : perror("HTTP_addheader");
172 0 : exit(EXIT_FAILURE);
173 : }
174 3 : if (NULL==(headers->name=malloc(strlen(name)+1))) {
175 0 : perror("HTTP_addheader name malloc");
176 0 : exit(EXIT_FAILURE);
177 : }
178 3 : strcpy(headers->name,name);
179 3 : if (NULL==(headers->value=malloc(strlen(value)+1))) {
180 0 : perror("HTTP_addheader value malloc");
181 0 : exit(EXIT_FAILURE);
182 : }
183 3 : strcpy(headers->value,value);
184 3 : sclist_addrecord(http->headers,headers);
185 3 : }
186 :
187 3 : char* HTTP_getheaders(const HTTP_t* http) {
188 3 : sclistrecord_t* current = NULL;
189 : char* retval;
190 :
191 3 : assert(http);
192 3 : assert(http->headers);
193 3 : assert(http->body);
194 :
195 3 : if (NULL==(retval=malloc(1))) {
196 0 : perror("HTTP_getheaders initial malloc");
197 0 : exit(EXIT_FAILURE);
198 : };
199 3 : retval[0]=0;
200 :
201 3 : current = sclist_firstrecord(http->headers);
202 6 : while (current) {
203 3 : HTTPHeaders_t* header=sclist_getvalue(current);
204 3 : if (header->name && header->value) {
205 : char* newretval;
206 3 : if (NULL==(newretval=realloc(retval,strlen(retval)+strlen(header->name)+2+strlen(header->value)+2+1))) {
207 0 : perror("HTTP_getheaders inner loop");
208 0 : exit(EXIT_FAILURE);
209 : }
210 3 : retval=newretval;
211 3 : strcat(retval,header->name);
212 3 : strcat(retval,": ");
213 3 : strcat(retval,header->value);
214 3 : strcat(retval,"\r\n");
215 : }
216 3 : current=sclist_nextrecord(current);
217 : }
218 3 : return retval;
219 : }
220 :
221 3 : char* HTTP_getrequest(const HTTPMethod_t method, const char* uri, const HTTPVersion_t version) {
222 : char* retval;
223 :
224 3 : assert(method<=HTTPMETHOD_INVALID);
225 3 : assert(version<=HTTPVERSION_INVALID);
226 :
227 3 : if (NULL==(retval=malloc(7+1+strlen(uri)+1+8+1))) {
228 0 : perror("HTTP_getrequest");
229 0 : exit(EXIT_FAILURE);
230 : }
231 3 : retval[0]=0;
232 :
233 3 : switch(method) {
234 3 : case HTTPMETHOD_GET:
235 3 : strcat(retval, "GET");
236 3 : break;;
237 0 : case HTTPMETHOD_HEAD:
238 : case HTTPMETHOD_POST:
239 : case HTTPMETHOD_PUT:
240 : case HTTPMETHOD_DELETE:
241 : case HTTPMETHOD_CONNECT:
242 : case HTTPMETHOD_OPTIONS:
243 : case HTTPMETHOD_TRACE:
244 : case HTTPMETHOD_PATCH:
245 : case HTTPMETHOD_INVALID:
246 : default:
247 0 : fprintf(stderr,"HTTP method not supported\n");
248 0 : exit(EXIT_FAILURE);
249 : break;;
250 : }
251 3 : strcat(retval," ");
252 :
253 : /* NULL URI allowed, as an empty string */
254 3 : if (uri)
255 3 : strcat(retval, uri);
256 :
257 3 : strcat(retval," HTTP/");
258 3 : switch(version) {
259 3 : case HTTPVERSION_HTTP11:
260 : case HTTPVERSION_HTTP11b:
261 3 : strcat(retval, "1.1");
262 3 : break;;
263 0 : case HTTPVERSION_HTTP09:
264 : case HTTPVERSION_HTTP10:
265 : case HTTPVERSION_HTTP2:
266 : case HTTPVERSION_HTTP3:
267 : case HTTPVERSION_INVALID:
268 : default:
269 0 : fprintf(stderr,"HTTP version not supported\n");
270 0 : exit(EXIT_FAILURE);
271 : break;;
272 : }
273 3 : strcat(retval, "\r\n");
274 3 : return retval;
275 : }
276 :
277 3 : void HTTP_setbody(HTTP_t* http, const char* body) {
278 : char* newbody;
279 :
280 3 : assert(http);
281 3 : assert(http->body);
282 :
283 3 : if (NULL==(newbody=realloc(http->body,strlen(body)+1))) {
284 0 : perror("HTTP_setbody");
285 0 : exit(EXIT_FAILURE);
286 : }
287 :
288 3 : http->body = newbody;
289 3 : strcpy(http->body, body);
290 3 : }
291 :
292 3 : char* HTTP_getbody(HTTP_t* http) {
293 3 : assert(http);
294 3 : assert(http->body);
295 :
296 3 : return http->body;
297 : }
|