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