GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/httpservers.c
Date: 2025-09-22 22:57:09
Exec Total Coverage
Lines: 453 746 60.7%
Functions: 35 45 77.8%
Branches: 150 351 42.7%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/net/httpservers.h"
30 #include "gate/net/sockettools.h"
31 #include "gate/streams.h"
32 #include "gate/utilities.h"
33 #include "gate/debugging.h"
34 #include "gate/uris.h"
35 #include "gate/files.h"
36
37
38
39 //static gate_string_t const header_key_status = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_STATUS);
40 static gate_string_t const header_key_content_length = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTLENGTH);
41 //static gate_string_t const header_key_content_type = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTTYPE);
42 //static gate_string_t const header_key_content_encoding = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTENCODING);
43 //static gate_string_t const header_key_accept_encoding = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_ACCEPTENCODING);
44 static gate_string_t const header_key_server = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_SERVER);
45 //static gate_string_t const header_key_connection = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONNECTION);
46 //static gate_string_t const header_key_host = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_HOST);
47 //static gate_string_t const header_key_date = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_DATE);
48 //static gate_string_t const header_key_content_disposition = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTDISPOSITION);
49 //static gate_string_t const header_key_authorization = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_AUTHORIZATION);
50 //static gate_string_t const header_key_location = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_LOCATION);
51 //static gate_string_t const header_key_cookie = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_COOKIE);
52 //static gate_string_t const header_key_user_agent = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_USERAGENT);
53 //static gate_string_t const header_key_upgrade = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_UPGRADE);
54 //static gate_string_t const header_key_referer = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_REFERER);
55 //static gate_string_t const header_key_origin = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_ORIGIN);
56
57
58 //static gate_string_t const header_value_connection_close = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONNECTION_CLOSE);
59 //static gate_string_t const header_value_connection_keep_alive = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONNECTION_KEEPALIVE);
60 //static gate_string_t const header_value_connection_upgrade = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONNECTION_UPGRADE);
61 //static gate_string_t const header_value_upgrade_websocket = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_UPGRADE_WEBSOCKET);
62 //static gate_string_t const header_value_multipart_form_data = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONTENTTYPE_MULTIPARTFORMDATA);
63 //static gate_string_t const header_value_urlencoded = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONTENTTYPE_URLENCODED);
64
65 static gate_string_t const header_value_server_default = GATE_STRING_INIT_STATIC("OpenGate-HTTP-Server");
66
67
68 2 gate_result_t gate_httpserver_config_init(gate_httpserver_config_t* config)
69 {
70 2 gate_mem_clear(config, sizeof(gate_httpserver_config_t));
71 2 return GATE_RESULT_OK;
72 }
73 3 gate_result_t gate_httpserver_config_copy(gate_httpserver_config_t* dest, gate_httpserver_config_t const* src)
74 {
75 3 gate_mem_clear(dest, sizeof(gate_httpserver_config_t));
76
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (NULL == gate_string_clone(&dest->address, &src->address))
77 {
78 ✗ return GATE_RESULT_OUTOFMEMORY;
79 }
80 3 dest->port = src->port;
81 3 dest->secure = src->secure;
82 3 dest->max_connections = src->max_connections;
83
84 3 return GATE_RESULT_OK;
85 }
86 6 void gate_httpserver_config_destroy(gate_httpserver_config_t* config)
87 {
88 6 gate_string_release(&config->address);
89 6 gate_mem_clear(config, sizeof(gate_httpserver_config_t));
90 6 }
91
92
93 GATE_INTERFACE(gate_httpserver_session)
94 {
95 GATE_METHOD0(char const*, get_interface_name);
96 GATE_METHOD0(void, release);
97 GATE_METHOD0(int, retain);
98
99 GATE_METHOD3(gate_result_t, read, char* buffer, gate_size_t bufferlength, gate_size_t * returned);
100 GATE_METHOD3(gate_result_t, peek, char* buffer, gate_size_t bufferlength, gate_size_t * returned);
101 GATE_METHOD3(gate_result_t, write, char const* buffer, gate_size_t bufferlength, gate_size_t * written);
102 GATE_METHOD0(gate_result_t, flush);
103
104 GATE_METHOD2(gate_result_t, get_header, gate_string_t const* name, gate_string_t * value);
105 GATE_METHOD2(gate_result_t, set_header, gate_string_t const* name, gate_string_t const* value);
106 GATE_METHOD1(gate_result_t, set_status, gate_uint32_t status);
107 };
108
109
110 typedef struct gate_httpserver_host_class
111 {
112 gate_channel_id_t channel;
113 gate_httpserver_config_t config;
114 gate_httpserver_callback_t callback;
115 void* userparam;
116 } gate_httpserver_host_t;
117
118
119 1 static gate_result_t gate_httpserver_host_cctor(void* dest, void const* src)
120 {
121 1 gate_httpserver_host_t const* ptr_src = (gate_httpserver_host_t const*)src;
122 1 gate_httpserver_host_t* ptr_dst = (gate_httpserver_host_t*)dest;
123
124 1 ptr_dst->channel = ptr_src->channel;
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_clone(&ptr_dst->config.address, &ptr_src->config.address))
126 {
127 ✗ return GATE_RESULT_OUTOFMEMORY;
128 }
129 1 ptr_dst->config.port = ptr_src->config.port;
130 1 ptr_dst->config.secure = ptr_src->config.secure;
131 1 ptr_dst->config.max_connections = ptr_src->config.max_connections;
132 1 ptr_dst->callback = ptr_src->callback;
133 1 ptr_dst->userparam = ptr_src->userparam;
134
135 1 return GATE_RESULT_OK;
136 }
137
138 2 static void gate_httpserver_host_dtor(void* dest)
139 {
140 2 gate_httpserver_host_t* ptr = (gate_httpserver_host_t*)dest;
141
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr != NULL)
142 {
143 2 gate_httpserver_config_destroy(&ptr->config);
144 }
145 2 }
146
147
148 #define CONNECTION_STATE_HEADERS_REQUIRED 1 /**< further readings required to complete HTTP header */
149 #define CONNECTION_STATE_HEADERS_RECEIVED 2 /**< HTTP header is received, content upload in progress */
150 #define CONNECTION_STATE_REQUEST_COMPLETED 3 /**< HTTP request completed */
151 #define CONNECTION_STATE_RESPONSE_SENDING_HEADERS 4 /**< HTTP response headers will be sent now */
152 #define CONNECTION_STATE_RESPONSE_HEADERS_SENT 5 /**< HTTP response headers sent content download in progress */
153 #define CONNECTION_STATE_RESPONSE_SENDING_CONTENT 6 /**< HTTP response content transmission in progress */
154 #define CONNECTION_STATE_RESPONSE_COMPLETED 7 /**< HTTP response completed */
155 #define CONNECTION_STATE_DISCONNECTED 8 /**< object is disconnected */
156
157 typedef struct gate_httpserver_connection_class
158 {
159 GATE_INTERFACE_VTBL(gate_httpserver_response) const* vtbl;
160
161 gate_atomic_int_t ref_counter;
162 gate_atomic_ptr_t server;
163
164 gate_channel_id_t channel; /**< socket channel to client */
165 gate_atomic_int_t state; /**< current state */
166 gate_channel_id_t parent_channel; /**< server socket that accepted the active channel */
167 gate_httpserver_callback_t callback; /**< callback that handles requests for this connection */
168 void* userparam; /**< user param of callback */
169 gate_memstream_t* read_buffer;
170
171 gate_uint64_t request_upload_size;
172 gate_bool_t request_read_until_shutdown; /* true: continue reading until end-of-stream, false: use content-length */
173 gate_bool_t request_completed;
174 gate_http_request_t request;
175
176 gate_uint32_t response_code;
177 gate_map_t response_headers;
178 gate_bool_t response_headers_sent;
179 gate_memstream_t* response_buffer;
180 gate_uint64_t response_length;
181 gate_uint64_t response_transferred;
182 gate_bool_t response_flushed;
183 gate_atomic_int64_t bytes_to_transfer;
184 } gate_httpserver_connection_t;
185
186 ✗ static char const* http_response_get_interface_name(void* obj)
187 {
188 (void)obj;
189 ✗ return GATE_INTERFACE_NAME_HTTP_RESPONSE;
190 }
191 13 static void http_response_release(void* obj)
192 {
193 13 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
194
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11 times.
13 if (0 == gate_atomic_int_dec(&self->ref_counter))
195 {
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (self->read_buffer)
197 {
198 ✗ gate_object_release(self->read_buffer);
199 }
200 2 gate_http_request_release(&self->request);
201 2 gate_map_destroy(&self->response_headers);
202
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (self->response_buffer)
203 {
204 2 gate_object_release(self->response_buffer);
205 }
206 2 gate_mem_dealloc(self);
207 }
208 13 }
209 11 static int http_response_retain(void* obj)
210 {
211 11 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
212 11 return gate_atomic_int_inc(&self->ref_counter);
213 }
214
215 ✗ static gate_result_t http_response_read(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
216 {
217 ✗ gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
218 ✗ if (self->request.upload_stream)
219 {
220 ✗ return gate_stream_read(self->request.upload_stream, buffer, bufferlength, returned);
221 }
222 else
223 {
224 ✗ if (returned)
225 {
226 ✗ *returned = 0;
227 }
228 ✗ return GATE_RESULT_OK;
229 }
230 }
231 ✗ static gate_result_t http_response_peek(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
232 {
233 ✗ gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
234 ✗ if (self->request.upload_stream)
235 {
236 ✗ return gate_stream_peek(self->request.upload_stream, buffer, bufferlength, returned);
237 }
238 else
239 {
240 ✗ if (returned)
241 {
242 ✗ *returned = 0;
243 }
244 ✗ return GATE_RESULT_OK;
245 }
246 }
247 2 static gate_result_t http_response_write(void* obj, char const* buffer, gate_size_t bufferlength, gate_size_t* written)
248 {
249 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
250
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!self->response_buffer)
251 {
252 2 self->response_buffer = gate_memstream_create(1024);
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!self->response_buffer)
254 {
255 ✗ return GATE_RESULT_OUTOFMEMORY;
256 }
257 }
258 2 return gate_stream_write(self->response_buffer, buffer, bufferlength, written);
259 }
260 2 static gate_result_t http_response_flush(void* obj)
261 {
262 2 gate_result_t ret = GATE_RESULT_FAILED;
263 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
264 gate_httpserver_t* ptr_server;
265 2 char const* ptr_content = NULL;
266 2 gate_size_t content_len = 0;
267
268 do
269 {
270 2 ptr_server = (gate_httpserver_t*)gate_atomic_ptr_get(&self->server);
271
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_server)
272 {
273 ✗ ret = GATE_RESULT_INVALIDSTATE;
274 ✗ break;
275 }
276
277
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (CONNECTION_STATE_RESPONSE_HEADERS_SENT != gate_atomic_int_xchg_if(
278 &self->state, CONNECTION_STATE_RESPONSE_HEADERS_SENT, CONNECTION_STATE_RESPONSE_SENDING_CONTENT))
279 {
280 ✗ ret = GATE_RESULT_INVALIDSTATE;
281 ✗ break;
282 }
283
284
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (self->response_buffer)
285 {
286 2 ptr_content = gate_memstream_get_data(self->response_buffer);
287 2 content_len = gate_memstream_size(self->response_buffer);
288 }
289
290 2 gate_atomic_int64_add(&self->bytes_to_transfer, (gate_int64_t)content_len);
291
292 2 ret = gate_socketqueue_begin_write(ptr_server->queue, self->channel, ptr_content, content_len, NULL);
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
294 {
295 ✗ gate_atomic_int64_add(&self->bytes_to_transfer, -((gate_int64_t)content_len));
296 ✗ gate_atomic_int_set(&self->state, CONNECTION_STATE_DISCONNECTED);
297 ✗ break;
298 }
299
300 2 self->response_flushed = true;
301 } while (0);
302
303
304 2 return ret;
305 }
306
307 2 static gate_result_t http_response_get_header(void* obj, gate_string_t const* name, gate_string_t* value)
308 {
309 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
310 2 gate_string_t const* ptr_value = (gate_string_t const*)gate_map_get_value(&self->response_headers, name);
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_value)
312 {
313 ✗ return GATE_RESULT_NOMATCH;
314 }
315
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (value)
316 {
317
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(value, ptr_value))
318 {
319 ✗ return GATE_RESULT_OUTOFMEMORY;
320 }
321 }
322 2 return GATE_RESULT_OK;
323 }
324 2 static gate_result_t http_response_set_header(void* obj, gate_string_t const* name, gate_string_t const* value)
325 {
326 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (value == NULL)
328 {
329 ✗ gate_map_remove(&self->response_headers, name);
330 }
331 else
332 {
333
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!gate_map_iterator_valid(gate_map_add(&self->response_headers, name, value)))
334 {
335 ✗ return GATE_RESULT_OUTOFMEMORY;
336 }
337 }
338 2 return GATE_RESULT_OK;
339 }
340 2 static gate_result_t http_response_set_status(void* obj, gate_uint32_t status)
341 {
342 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
343 2 self->response_code = status;
344 2 return GATE_RESULT_OK;
345 }
346 4 static gate_bool_t http_response_are_headers_sent(void* obj)
347 {
348 4 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
349 4 return self->response_headers_sent;
350 }
351 2 static gate_result_t http_response_send_headers(void* obj)
352 {
353 2 gate_result_t ret = GATE_RESULT_FAILED;
354 2 gate_httpserver_connection_t* self = (gate_httpserver_connection_t*)obj;
355
356 2 gate_httpserver_t* ptr_server = (gate_httpserver_t*)gate_atomic_ptr_get(&self->server);
357 2 gate_strbuilder_t builder = GATE_INIT_EMPTY;
358 gate_map_iterator_t iter;
359 gate_string_t const* key;
360 gate_string_t const* value;
361 char const* ptr_content;
362 gate_size_t content_len;
363
364 do
365 {
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_server)
367 {
368 ✗ ret = GATE_RESULT_INVALIDSTATE;
369 ✗ break;
370 }
371
372
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (CONNECTION_STATE_REQUEST_COMPLETED != gate_atomic_int_xchg_if(
373 &self->state, CONNECTION_STATE_REQUEST_COMPLETED, CONNECTION_STATE_RESPONSE_SENDING_HEADERS))
374 {
375 ✗ ret = GATE_RESULT_INVALIDSTATE;
376 ✗ break;
377 }
378
379 2 gate_strbuilder_create(&builder, 1024);
380 2 gate_strbuilder_append(&builder,
381 GATE_PRINT_CSTR, "HTTP/",
382 GATE_PRINT_STRING, &self->request.version,
383 GATE_PRINT_CSTR, " ",
384 GATE_PRINT_UI32, self->response_code,
385 GATE_PRINT_CSTR, " ",
386 GATE_PRINT_CSTR, gate_http_response_status_text(self->response_code),
387 GATE_PRINT_CRLF, GATE_PRINT_END);
388
389 2 iter = gate_map_first(&self->response_headers);
390
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 while (gate_map_iterator_valid(iter))
391 {
392 4 key = (gate_string_t const*)gate_map_iterator_key(iter);
393 4 value = (gate_string_t const*)gate_map_iterator_value(iter);
394
395 4 gate_strbuilder_append(&builder,
396 GATE_PRINT_STRING, key,
397 GATE_PRINT_CSTR, ": ",
398 GATE_PRINT_STRING, value,
399 GATE_PRINT_CRLF, GATE_PRINT_END);
400 4 iter = gate_map_iterator_next(iter);
401 }
402 2 gate_strbuilder_append(&builder,
403 GATE_PRINT_CRLF, GATE_PRINT_END);
404
405 2 ptr_content = gate_strbuilder_ptr(&builder, 0);
406 2 content_len = gate_strbuilder_length(&builder);
407
408 2 gate_atomic_int64_add(&self->bytes_to_transfer, (gate_int64_t)content_len);
409 2 ret = gate_socketqueue_begin_write(ptr_server->queue, self->channel, ptr_content, content_len, NULL);
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
411 {
412 ✗ gate_atomic_int64_add(&self->bytes_to_transfer, -((gate_int64_t)content_len));
413 ✗ break;
414 }
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
416
417 2 self->response_headers_sent = true;
418 } while (0);
419
420 2 gate_strbuilder_release(&builder);
421
422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
423 {
424 ✗ gate_atomic_int_set(&self->state, CONNECTION_STATE_DISCONNECTED);
425 }
426 else
427 {
428 2 gate_atomic_int_xchg_if(&self->state,
429 CONNECTION_STATE_RESPONSE_SENDING_HEADERS,
430 CONNECTION_STATE_RESPONSE_HEADERS_SENT);
431
432 }
433
434
435 2 return ret;
436 }
437
438 static GATE_INTERFACE_VTBL(gate_httpserver_response) gate_http_response_vtbl;
439 2 static void gate_init_http_response_vtbl()
440 {
441
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!gate_http_response_vtbl.get_interface_name)
442 {
443 GATE_INTERFACE_VTBL(gate_httpserver_response) const local_vtbl =
444 {
445 &http_response_get_interface_name,
446 &http_response_release,
447 &http_response_retain,
448
449 &http_response_read,
450 &http_response_peek,
451 &http_response_write,
452 &http_response_flush,
453
454 &http_response_set_status,
455 &http_response_get_header,
456 &http_response_set_header,
457 &http_response_send_headers,
458 &http_response_are_headers_sent
459 };
460 1 gate_http_response_vtbl = local_vtbl;
461 }
462 2 }
463
464
465
466
467
468
469 2 static gate_httpserver_connection_t* gate_httpserver_connection_create()
470 {
471 2 gate_httpserver_connection_t* ret = NULL;
472 gate_httpserver_connection_t* ptr;
473 gate_result_t result;
474
475 do
476 {
477 2 ptr = (gate_httpserver_connection_t*)gate_mem_alloc(sizeof(gate_httpserver_connection_t));
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr)
479 {
480 ✗ break;
481 }
482
483 2 gate_mem_clear(ptr, sizeof(gate_httpserver_connection_t));
484 2 gate_init_http_response_vtbl();
485 2 ptr->vtbl = &gate_http_response_vtbl;
486 2 gate_atomic_int_init(&ptr->ref_counter, 1);
487 2 gate_atomic_int_init(&ptr->state, CONNECTION_STATE_HEADERS_REQUIRED);
488
489 2 ptr->read_buffer = gate_memstream_create(1024);
490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr->read_buffer)
491 {
492 ✗ gate_mem_dealloc(ret);
493 ✗ ret = NULL;
494 }
495
496 2 result = gate_http_request_init(&ptr->request, NULL, NULL);
497
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(result);
498
499
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_util_stringmap_create(&ptr->response_headers))
500 {
501 ✗ break;
502 }
503 2 gate_util_stringmap_add_string(&ptr->response_headers, &header_key_server, &header_value_server_default);
504
505 /* success state reached: */
506 2 ret = ptr;
507 2 ptr = NULL;
508 } while (0);
509
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ptr)
511 {
512 ✗ http_response_release(ptr);
513 }
514
515 2 return ret;
516 }
517
518
519 2 static gate_httpserver_connection_t* gate_httpserver_add_connection(gate_httpserver_t* server, gate_channel_id_t channel_id)
520 {
521 2 gate_httpserver_connection_t* ret = NULL;
522 gate_map_iterator_t iter;
523
524 2 iter = gate_map_get(&server->connections, &channel_id);
525
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_map_iterator_valid(iter))
526 {
527 ✗ ret = *((gate_httpserver_connection_t**)gate_map_iterator_value(iter));
528 }
529 else
530 {
531 2 ret = gate_httpserver_connection_create();
532
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (NULL != ret)
533 {
534 2 gate_atomic_ptr_init(&ret->server, server);
535 2 ret->channel = channel_id;
536 2 gate_map_add(&server->connections, &channel_id, &ret);
537 }
538 }
539 2 return ret;
540 }
541
542 9 static gate_httpserver_connection_t* gate_httpserver_get_connection(gate_httpserver_t* server, gate_channel_id_t channel_id)
543 {
544 9 gate_httpserver_connection_t* ret = NULL;
545 9 gate_httpserver_connection_t** ptr_conn = (gate_httpserver_connection_t**)gate_map_get_value(&server->connections, &channel_id);
546
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
9 if (ptr_conn && *ptr_conn)
547 {
548 7 ret = *ptr_conn;
549 }
550 9 return ret;
551 }
552
553 2 static void gate_httpserver_detach_connection(gate_httpserver_connection_t* conn)
554 {
555
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (conn)
556 {
557 2 gate_atomic_int_set(&conn->state, CONNECTION_STATE_DISCONNECTED);
558 2 gate_atomic_ptr_set(&conn->server, NULL);
559 }
560 2 }
561
562 2 static gate_bool_t gate_httpserver_remove_connection(gate_httpserver_t* server, gate_channel_id_t channel_id)
563 {
564 2 gate_httpserver_connection_t** ptr_conn = (gate_httpserver_connection_t**)gate_map_get_value(&server->connections, &channel_id);
565
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_conn)
566 {
567 2 gate_httpserver_detach_connection(*ptr_conn);
568 }
569 2 return gate_map_remove(&server->connections, &channel_id);
570 }
571
572 static gate_string_t const http_verb_get = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_GET);
573 static gate_string_t const http_verb_post = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_POST);
574 static gate_string_t const http_verb_put = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_PUT);
575 static gate_string_t const http_verb_head = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_HEAD);
576 static gate_string_t const http_verb_delete = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_DELETE);
577 static gate_string_t const http_verb_option = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_OPTION);
578
579 static gate_string_t const http_request_line_version_token = GATE_STRING_INIT_STATIC("HTTP/");
580
581 2 static gate_result_t gate_httpserver_parse_request_headline(gate_string_t const* line, gate_http_request_t* request)
582 {
583 2 gate_result_t ret = GATE_RESULT_FAILED;
584 gate_size_t pos;
585 gate_size_t start;
586 2 gate_string_t method = GATE_STRING_INIT_EMPTY;
587 2 gate_string_t version = GATE_STRING_INIT_EMPTY;
588
589 do
590 {
591 2 pos = gate_string_char_pos(line, ' ', 0);
592
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_STR_NPOS == pos)
593 {
594 ✗ break;
595 }
596
597 2 gate_string_substr(&method, line, 0, pos);
598
599
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (gate_string_equals(&method, &http_verb_get)) { gate_string_duplicate(&request->method, &http_verb_get); }
600
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 else if (gate_string_equals(&method, &http_verb_post)) { gate_string_duplicate(&request->method, &http_verb_post); }
601 ✗ else if (gate_string_equals(&method, &http_verb_put)) { gate_string_duplicate(&request->method, &http_verb_put); }
602 ✗ else if (gate_string_equals(&method, &http_verb_head)) { gate_string_duplicate(&request->method, &http_verb_head); }
603 ✗ else if (gate_string_equals(&method, &http_verb_delete)) { gate_string_duplicate(&request->method, &http_verb_delete); }
604 ✗ else if (gate_string_equals(&method, &http_verb_option)) { gate_string_duplicate(&request->method, &http_verb_option); }
605 else
606 {
607 ✗ gate_string_duplicate(&request->method, &method);
608 }
609
610 2 start = pos + 1;
611
612 2 pos = gate_string_char_pos(line, ' ', start);
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_STR_NPOS == pos)
614 {
615 ✗ break;
616 }
617 2 gate_string_substr(&request->path, line, start, pos - start);
618 2 gate_string_substr(&version, line, pos + 1, GATE_STR_NPOS);
619
620
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!gate_string_starts_with(&version, &http_request_line_version_token))
621 {
622 ✗ break;
623 }
624 2 gate_string_substr(&request->version, &version, gate_string_length(&http_request_line_version_token), GATE_STR_NPOS);
625
626 2 ret = GATE_RESULT_OK;
627 } while (0);
628
629 2 gate_string_release(&version);
630 2 gate_string_release(&method);
631
632 2 return ret;
633 }
634
635 static gate_string_t header_keyvalue_delimiter = GATE_STRING_INIT_STATIC(": ");
636
637 6 static gate_result_t gate_httpserver_parse_header_entry(gate_string_t const* line, gate_map_t* mapping)
638 {
639 6 gate_result_t ret = GATE_RESULT_FAILED;
640 gate_size_t pos;
641 6 gate_string_t key = GATE_STRING_INIT_EMPTY;
642 6 gate_string_t value = GATE_STRING_INIT_EMPTY;
643
644 do
645 {
646 6 pos = gate_string_pos(line, &header_keyvalue_delimiter, 0);
647
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (pos == GATE_STR_NPOS)
648 {
649 ✗ break;
650 }
651
652 6 gate_string_substr(&key, line, 0, pos);
653 6 gate_string_substr(&value, line, pos + gate_string_length(&header_keyvalue_delimiter), GATE_STR_NPOS);
654
655
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if (gate_map_iterator_valid(gate_util_stringmap_add_string(mapping, &key, &value)))
656 {
657 6 ret = GATE_RESULT_OK;
658 }
659
660 } while (0);
661 6 gate_string_release(&key);
662 6 gate_string_release(&value);
663 6 return ret;
664 }
665
666 static gate_string_t header_entry_delimiter = GATE_STRING_INIT_STATIC("\r\n");
667
668 2 static gate_result_t gate_httpserver_parse_request_header(gate_string_t const* header, gate_http_request_t* request)
669 {
670 2 gate_result_t ret = GATE_RESULT_FAILED;
671 2 gate_size_t start = 0;
672 gate_size_t pos;
673 2 gate_string_t line = GATE_STRING_INIT_EMPTY;
674
675 do
676 {
677 2 pos = gate_string_pos(header, &header_entry_delimiter, start);
678 2 gate_string_substr(&line, header, start, pos - start);
679 2 start = pos + 2;
680
681 2 ret = gate_httpserver_parse_request_headline(&line, request);
682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
683 2 gate_string_release(&line);
684
685 for (;;)
686 {
687 8 pos = gate_string_pos(header, &header_entry_delimiter, start);
688
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (pos == GATE_STR_NPOS)
689 {
690 2 break;
691 }
692 6 gate_string_substr(&line, header, start, pos - start);
693 6 start = pos + 2;
694
695 6 ret = gate_httpserver_parse_header_entry(&line, &request->headers);
696
697 6 gate_string_release(&line);
698 }
699
700 2 ret = GATE_RESULT_OK;
701 } while (0);
702
703 2 gate_string_release(&line);
704
705 2 return ret;
706 }
707
708
709 1 static gate_bool_t retrieve_content_length(gate_map_t const* mapping, gate_uint64_t* ptr_length)
710 {
711 1 gate_string_t const* ptr = (gate_string_t const*)gate_map_get_value(mapping, &header_key_content_length);
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr)
713 {
714 ✗ if (0 != gate_string_parse_uint(ptr, ptr_length))
715 {
716 ✗ return true;
717 }
718 }
719 1 return false;
720 }
721
722 #define GATE_HTTPSERVER_MAX_HEADER_LEN 16384
723
724 3 static void gate_httpserver_update_state(gate_httpserver_t* server, gate_httpserver_connection_t* connection)
725 {
726 3 gate_int32_t state = gate_atomic_int_get(&connection->state);
727 char const* ptr_data;
728 gate_size_t sz;
729 gate_size_t pos;
730 gate_size_t written;
731 gate_result_t result;
732 3 gate_string_t http_header = GATE_STRING_INIT_EMPTY;
733
734 (void)server;
735
736
2/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 switch (state)
737 {
738 2 case CONNECTION_STATE_HEADERS_REQUIRED:
739 {
740 2 ptr_data = gate_memstream_get_data(connection->read_buffer);
741 2 sz = gate_memstream_size(connection->read_buffer);
742 2 pos = gate_str_pos(ptr_data, sz, "\r\n\r\n", 4, 0);
743
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (pos == GATE_STR_NPOS)
744 {
745 /* header not complete */
746 ✗ if (sz > GATE_HTTPSERVER_MAX_HEADER_LEN)
747 {
748 /* error: header not within allowed size range*/
749 ✗ gate_atomic_int_set(&connection->state, CONNECTION_STATE_DISCONNECTED);
750 }
751 else
752 {
753 /* await more data from remote peer */
754 }
755 ✗ break;
756 }
757
758
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_create(&http_header, ptr_data, pos + 2))
759 {
760 /* error: out of memory */
761 ✗ gate_atomic_int_set(&connection->state, CONNECTION_STATE_DISCONNECTED);
762 ✗ break;
763 }
764
765 2 result = gate_httpserver_parse_request_header(&http_header, &connection->request);
766
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
767 {
768 /* error: failed to parse HTTP header */
769 ✗ gate_atomic_int_set(&connection->state, CONNECTION_STATE_DISCONNECTED);
770 ✗ break;
771 }
772
773 2 gate_memstream_discard(connection->read_buffer, pos + 4);
774
775
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (gate_string_equals(&connection->request.method, &http_verb_get)
776
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 || gate_string_equals(&connection->request.method, &http_verb_head)
777
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 || gate_string_equals(&connection->request.method, &http_verb_delete))
778 {
779 /* non-content request detected */
780 1 connection->request_upload_size = 0;
781 1 connection->request_completed = true;
782 }
783 else
784 {
785
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (retrieve_content_length(&connection->request.headers, &connection->request_upload_size))
786 {
787
788 ✗ connection->request_upload_size = gate_memstream_size(connection->read_buffer);
789 }
790 else
791 {
792
793 1 connection->request_read_until_shutdown = true;
794 }
795 }
796
797 /* update state to HEADERS-RECEIVED */
798 2 gate_atomic_int_xchg_if(&connection->state, CONNECTION_STATE_HEADERS_REQUIRED, CONNECTION_STATE_HEADERS_RECEIVED);
799 /* no "break", try processing next variant */
800 }
801 3 case CONNECTION_STATE_HEADERS_RECEIVED:
802 {
803 3 ptr_data = gate_memstream_get_data(connection->read_buffer);
804 3 sz = gate_memstream_size(connection->read_buffer);
805
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!connection->request_completed)
806 {
807
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!connection->request_read_until_shutdown)
808 {
809 ✗ if (sz >= connection->request_upload_size)
810 {
811 /* we have already all bytes of the upload */
812 ✗ connection->request_completed = true;
813 }
814 }
815 }
816
817
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!connection->request_completed)
818 {
819 /* upload not complete, continue to read */
820 1 break;
821 }
822
823
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (sz == connection->request_upload_size)
824 {
825 2 connection->request.upload_stream = (gate_stream_t*)connection->read_buffer;
826 2 connection->read_buffer = NULL;
827 }
828 else
829 {
830 ✗ connection->request.upload_stream = (gate_stream_t*)gate_memstream_create(sz);
831 ✗ if (!connection->request.upload_stream)
832 {
833 /* error */
834 ✗ gate_atomic_int_set(&connection->state, CONNECTION_STATE_DISCONNECTED);
835 ✗ break;
836 }
837 ✗ gate_stream_write(connection->request.upload_stream, ptr_data, sz, &written);
838 ✗ gate_memstream_discard(connection->read_buffer, sz);
839 }
840
841 /* update state to REQUEST-COMPLETED */
842 2 gate_atomic_int_xchg_if(&connection->state, CONNECTION_STATE_HEADERS_RECEIVED, CONNECTION_STATE_REQUEST_COMPLETED);
843 /* no break wantet here! */
844 }
845 2 case CONNECTION_STATE_REQUEST_COMPLETED:
846 case CONNECTION_STATE_RESPONSE_HEADERS_SENT:
847 case CONNECTION_STATE_RESPONSE_COMPLETED:
848 {
849 2 break;
850 }
851 }
852
853 3 gate_string_release(&http_header);
854 3 }
855
856
857 3 static gate_result_t add_bytes_to_connection_read_buffer(
858 gate_httpserver_t* ptr_server, gate_httpserver_connection_t* ptr_connection, char const* buffer, gate_size_t buffer_length)
859 {
860 3 gate_result_t result = GATE_RESULT_FAILED;
861
862
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (0 == buffer_length)
863 {
864 1 GATE_DEBUG_TRACE_MSG_VALUE("http-event:read (0 bytes)", ptr_connection->channel);
865 1 ptr_connection->request_completed = true;
866
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_connection->request_read_until_shutdown)
867 {
868 1 ptr_connection->request_upload_size = gate_memstream_size(ptr_connection->read_buffer);
869 1 ptr_connection->request_read_until_shutdown = false;
870 }
871 1 result = GATE_RESULT_OK;
872 }
873 else
874 {
875 2 gate_size_t written = 0;
876 2 result = gate_stream_write_block((gate_stream_t*)ptr_connection->read_buffer,
877 buffer, buffer_length, &written);
878 2 GATE_DEBUG_ASSERT(written == buffer_length);
879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
880 {
881 ✗ gate_atomic_int_set(&ptr_connection->state, CONNECTION_STATE_DISCONNECTED);
882 }
883 }
884
885
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (GATE_SUCCEEDED(result))
886 {
887 3 gate_httpserver_update_state(ptr_server, ptr_connection);
888 }
889
890 3 return result;
891 }
892
893
894 22 static void gate_httpserver_queue_event(void* callback_data, gate_uint32_t result_type,
895 gate_channel_id_t channel_id, gate_result_t result_code,
896 char const* buffer, gate_size_t buffer_length,
897 void* user_param)
898 {
899 22 gate_httpserver_t* ptr_server = (gate_httpserver_t*)callback_data;
900 gate_result_t result;
901
902 22 gate_httpserver_connection_t* ptr_connection = NULL;
903 gate_httpserver_host_t const* ptr_host;
904 22 gate_httpserver_connection_t* next_callback_connection = NULL;
905 gate_channel_id_t parent_channel;
906 gate_map_iterator_t iter;
907 gate_httpserver_connection_t* const* ptrptr_conn;
908
909 (void)result_code;
910 (void)user_param;
911
912
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 if (channel_id != GATE_QUEUE_INVALID_ID)
913 {
914 9 ptr_connection = gate_httpserver_get_connection(ptr_server, channel_id);
915 }
916
917
918
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 15 times.
22 if (ptr_connection)
919 {
920 7 gate_object_retain(ptr_connection);
921 }
922
923 22 result = gate_mutex_acquire(&ptr_server->lock);
924
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (GATE_FAILED(result))
925 {
926 ✗ return;
927 }
928
929
5/7
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
22 switch (result_type)
930 {
931 2 case GATE_DATAQUEUE_RESULT_OPENNEW:
932 {
933 /* new incomming connection attempt*/
934
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (GATE_SUCCEEDED(result_type))
935 {
936 2 GATE_DEBUG_TRACE_MSG_VALUE("http-event:open-new OK", channel_id);
937
938 /* NOTICE: parent (server) connection channel is stored in buffer */
939 2 gate_mem_copy(&parent_channel, buffer, buffer_length);
940
941 2 ptr_host = (gate_httpserver_host_t const*)gate_map_get_value(&ptr_server->hosts, &parent_channel);
942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_host)
943 {
944 ✗ GATE_DEBUG_TRACE_MSG_VALUE("http-event:open-new ERROR no-host-match", channel_id);
945 ✗ gate_socketqueue_close(ptr_server->queue, channel_id);
946 ✗ break;
947 }
948
949 2 ptr_connection = gate_httpserver_add_connection(ptr_server, channel_id);
950
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (NULL == ptr_connection)
951 {
952 ✗ GATE_DEBUG_TRACE_MSG_VALUE("http-event:open-new ERROR new-state", channel_id);
953 ✗ gate_socketqueue_close(ptr_server->queue, channel_id);
954 ✗ break;
955 }
956 2 ptr_connection->parent_channel = parent_channel;
957 2 ptr_connection->callback = ptr_host->callback;
958 2 ptr_connection->userparam = ptr_host->userparam;
959
960 2 result = gate_socketqueue_begin_read(ptr_server->queue, channel_id, 4096, ptr_connection);
961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
962 {
963 ✗ GATE_DEBUG_TRACE_MSG_VALUE("http-event:open-new ERROR read", channel_id);
964 ✗ gate_atomic_int_set(&ptr_connection->state, CONNECTION_STATE_DISCONNECTED);
965 ✗ break;
966 }
967 }
968 else
969 {
970 ✗ GATE_DEBUG_TRACE_MSG_VALUE("http-event:open-new ERROR", channel_id);
971 }
972
973 2 break;
974 }
975 1 case GATE_DATAQUEUE_RESULT_CLOSE:
976 {
977 1 gate_bool_t close_connection = false;
978
979 1 GATE_DEBUG_TRACE_MSG_VALUE("http-event:close", channel_id);
980
981
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result_type))
982 {
983 ✗ close_connection = true;
984 }
985
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 else if (gate_atomic_int_get(&ptr_connection->state) != CONNECTION_STATE_HEADERS_RECEIVED)
986 {
987 /* connection was closed before or after reading-request content */
988 ✗ GATE_DEBUG_TRACE("Connection closed in incomplete or wrong state");
989 ✗ close_connection = true;
990 }
991 else /* successful close on upload -> end-of-read-side -> upload complete */
992 {
993 1 result = add_bytes_to_connection_read_buffer(ptr_server, ptr_connection, NULL, 0);
994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_TRACE_FAILED(result, "add_bytes_to_connection_read_buffer()");
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
996
997
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_connection->request_completed)
998 {
999 /* we can invoke the callback with a new complete decoded request object */
1000 1 next_callback_connection = ptr_connection;
1001 /* socket is kept open to send response */
1002 }
1003 else
1004 {
1005 ✗ GATE_DEBUG_TRACE("Connection closed, but request-completed state not reached");
1006 ✗ close_connection = true;
1007 }
1008 }
1009
1010
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (close_connection)
1011 {
1012 ✗ gate_httpserver_remove_connection(ptr_server, channel_id);
1013 ✗ gate_socketqueue_close(ptr_server->queue, channel_id);
1014 }
1015 1 break;
1016 }
1017 2 case GATE_DATAQUEUE_RESULT_READ:
1018 {
1019
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (GATE_SUCCEEDED(result_type))
1020 {
1021 2 GATE_DEBUG_TRACE_MSG_VALUE("http-event:read", channel_id);
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_connection)
1023 {
1024 ✗ GATE_DEBUG_TRACE("No connection pointer");
1025 ✗ break;
1026 }
1027
1028 2 result = add_bytes_to_connection_read_buffer(ptr_server, ptr_connection, buffer, buffer_length);
1029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_DEBUG_TRACE_FAILED(result, "add_bytes_to_connection_read_buffer()");
1030
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(result);
1031
1032
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_atomic_int_get(&ptr_connection->state) == CONNECTION_STATE_DISCONNECTED)
1033 {
1034 ✗ GATE_DEBUG_TRACE("Disconnect state");
1035 ✗ break;
1036 }
1037
1038
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (ptr_connection->request_completed)
1039 {
1040 /* invoke callback with new complete request object */
1041 1 next_callback_connection = ptr_connection;
1042 }
1043 else
1044 {
1045 /* read more bytes from this socket */
1046 1 result = gate_socketqueue_begin_read(ptr_server->queue, channel_id, 4096, ptr_connection);
1047
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
1048 {
1049 ✗ GATE_DEBUG_TRACE("gate_socketqueue_begin_read() failed");
1050 ✗ gate_atomic_int_set(&ptr_connection->state, CONNECTION_STATE_DISCONNECTED);
1051 ✗ break;
1052 }
1053 }
1054 }
1055 2 break;
1056 }
1057 4 case GATE_DATAQUEUE_RESULT_WRITE:
1058 {
1059 4 GATE_DEBUG_TRACE_MSG_VALUE("http-event:write", channel_id);
1060
1061
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (GATE_SUCCEEDED(result))
1062 {
1063 4 gate_atomic_int64_add(&ptr_connection->bytes_to_transfer, -((gate_int64_t)buffer_length));
1064
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (ptr_connection->response_flushed)
1065 {
1066
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (0 == gate_atomic_int64_get(&ptr_connection->bytes_to_transfer))
1067 {
1068 2 gate_atomic_int_set(&ptr_connection->state, CONNECTION_STATE_DISCONNECTED);
1069 }
1070 }
1071 }
1072 4 break;
1073 }
1074 ✗ case GATE_DATAQUEUE_RESULT_ERROR:
1075 {
1076 ✗ GATE_DEBUG_TRACE_MSG_VALUE("http-event:error", channel_id);
1077 ✗ if (ptr_connection)
1078 {
1079 ✗ gate_atomic_int_set(&ptr_connection->state, CONNECTION_STATE_DISCONNECTED);
1080 }
1081 ✗ break;
1082 }
1083 13 case GATE_DATAQUEUE_RESULT_HEARTBEAT:
1084 {
1085 13 iter = gate_map_first(&ptr_server->connections);
1086
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 13 times.
20 while (gate_map_iterator_valid(iter))
1087 {
1088 7 ptrptr_conn = (gate_httpserver_connection_t* const*)gate_map_iterator_value(iter);
1089
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (ptrptr_conn)
1090 {
1091 7 ptr_connection = *ptrptr_conn;
1092
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (ptr_connection)
1093 {
1094
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (gate_atomic_int_get(&ptr_connection->state) == CONNECTION_STATE_DISCONNECTED)
1095 {
1096 /* move object into ptr_connection to be handled afterwards */
1097 ✗ gate_object_retain(ptr_connection);
1098 ✗ break;
1099 }
1100 7 ptr_connection = NULL; /* reset pointer to not be used afterwards */
1101 }
1102 }
1103 7 iter = gate_map_iterator_next(iter);
1104 }
1105 13 break;
1106 }
1107 }
1108
1109
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 if (ptr_connection)
1110 {
1111
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
9 if (gate_atomic_int_get(&ptr_connection->state) == CONNECTION_STATE_DISCONNECTED)
1112 {
1113 2 GATE_DEBUG_TRACE_MSG_VALUE("http-event: connection-state == disconnected", channel_id);
1114
1115 2 gate_socketqueue_close(ptr_server->queue, ptr_connection->channel);
1116 2 gate_httpserver_remove_connection(ptr_server, ptr_connection->channel);
1117 }
1118 }
1119
1120 22 gate_mutex_release(&ptr_server->lock);
1121
1122
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
22 if (next_callback_connection && next_callback_connection->callback)
1123 {
1124 4 next_callback_connection->callback(ptr_server,
1125 2 (gate_httpserver_host_id_t)next_callback_connection->parent_channel,
1126 2 &next_callback_connection->request,
1127 (gate_httpserver_response_t*)next_callback_connection,
1128 next_callback_connection->userparam);
1129 }
1130
1131
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 if (ptr_connection)
1132 {
1133 9 gate_object_release(ptr_connection);
1134 }
1135 }
1136
1137
1138 ✗ gate_result_t gate_httpserver_response_set_header_str(gate_httpserver_response_t* response, char const* name, char const* value)
1139 {
1140 ✗ gate_result_t ret = GATE_RESULT_FAILED;
1141 ✗ gate_string_t str_name = GATE_STRING_INIT_EMPTY;
1142 ✗ gate_string_t str_value = GATE_STRING_INIT_EMPTY;
1143
1144 do
1145 {
1146 ✗ if (NULL == gate_string_create(&str_name, name, gate_str_length(name)))
1147 {
1148 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1149 ✗ break;
1150 }
1151
1152 ✗ if (NULL == gate_string_create(&str_value, value, gate_str_length(value)))
1153 {
1154 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1155 ✗ break;
1156 }
1157
1158 ✗ ret = gate_httpserver_response_set_header(response, &str_name, &str_value);
1159 } while (0);
1160
1161 ✗ gate_string_release(&str_name);
1162 ✗ gate_string_release(&str_value);
1163 ✗ return ret;
1164 }
1165
1166 ✗ gate_result_t gate_httpserver_response_set_header_int(gate_httpserver_response_t* response, char const* name, gate_int64_t value)
1167 {
1168 ✗ gate_result_t ret = GATE_RESULT_FAILED;
1169 ✗ gate_string_t str_name = GATE_STRING_INIT_EMPTY;
1170 ✗ gate_string_t str_value = GATE_STRING_INIT_EMPTY;
1171 char int_buffer[256];
1172 gate_size_t int_buffer_used;
1173
1174 do
1175 {
1176 ✗ int_buffer_used = gate_str_print_int64(int_buffer, sizeof(int_buffer), value);
1177
1178 ✗ if (NULL == gate_string_create(&str_name, name, gate_str_length(name)))
1179 {
1180 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1181 ✗ break;
1182 }
1183
1184 ✗ if (NULL == gate_string_create(&str_value, int_buffer, int_buffer_used))
1185 {
1186 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1187 ✗ break;
1188 }
1189
1190 ✗ ret = gate_httpserver_response_set_header(response, &str_name, &str_value);
1191 } while (0);
1192
1193 ✗ gate_string_release(&str_name);
1194 ✗ gate_string_release(&str_value);
1195 ✗ return ret;
1196 }
1197
1198
1199
1200 2 gate_result_t gate_httpserver_create(gate_httpserver_t* server)
1201 {
1202 2 gate_result_t ret = GATE_RESULT_FAILED;
1203 do
1204 {
1205 2 gate_mem_clear(server, sizeof(gate_httpserver_t));
1206 2 ret = gate_mutex_create(&server->lock);
1207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
1208
1209
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_map_create(&server->hosts, &gate_compare_ptr,
1210 sizeof(gate_httpserver_host_id_t), NULL, NULL,
1211 sizeof(gate_httpserver_host_t), &gate_httpserver_host_cctor, &gate_httpserver_host_dtor))
1212 {
1213 ✗ gate_mutex_destroy(&server->lock);
1214 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1215 ✗ break;
1216 }
1217
1218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_map_create(&server->connections, &gate_compare_uintptr,
1219 sizeof(gate_channel_id_t), NULL, NULL,
1220 sizeof(gate_httpserver_connection_t*), &gate_object_ptr_copyctor, &gate_object_ptr_dtor))
1221 {
1222 ✗ gate_map_destroy(&server->hosts);
1223 ✗ gate_mutex_destroy(&server->lock);
1224 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1225 ✗ break;
1226 }
1227
1228
1229 } while (0);
1230 2 return ret;
1231 }
1232
1233 2 static gate_result_t gate_httpserver_create_queue(gate_httpserver_t* server)
1234 {
1235
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (NULL == server->queue)
1236 {
1237 1 return gate_socketqueue_create(&server->queue, 1000);
1238 }
1239 else
1240 {
1241 /* already created */
1242 1 return GATE_RESULT_OK;
1243 }
1244 }
1245
1246 1 gate_result_t gate_httpserver_add_host(gate_httpserver_t* server,
1247 gate_httpserver_config_t const* config,
1248 gate_httpserver_callback_t callback,
1249 void* userparam,
1250 gate_httpserver_host_id_t* ptr_host_id)
1251 {
1252 1 gate_result_t ret = GATE_RESULT_FAILED;
1253 char address_buffer[1024];
1254 1 gate_strbuilder_t address_builder = GATE_INIT_EMPTY;
1255 1 gate_string_t address = GATE_STRING_INIT_EMPTY;
1256 1 gate_httpserver_host_t host = GATE_INIT_EMPTY;
1257
1258
1259 1 ret = gate_mutex_acquire(&server->lock);
1260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
1261 {
1262 ✗ return ret;
1263 }
1264
1265 do
1266 {
1267 1 gate_strbuilder_create_static(&address_builder, address_buffer, sizeof(address_buffer), 0);
1268 1 gate_strbuilder_append_string(&address_builder, &config->address);
1269 1 gate_strbuilder_append_cstr(&address_builder, ":");
1270 1 gate_strbuilder_append_uint16(&address_builder, config->port);
1271 1 gate_strbuilder_to_string(&address_builder, &address);
1272
1273 1 ret = gate_httpserver_create_queue(server);
1274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1275
1276 1 ret = gate_socketqueue_open(server->queue, &address, GATE_SOCKETQUEUE_OPEN_SERVER, server, &host.channel);
1277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1278
1279 1 host.callback = callback;
1280 1 host.userparam = userparam;
1281 1 ret = gate_httpserver_config_copy(&host.config, config);
1282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1283
1284
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_add(&server->hosts, &host.channel, &host))
1285 {
1286 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1287 ✗ break;
1288 }
1289
1290
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_host_id)
1291 {
1292 1 *ptr_host_id = host.channel;
1293 }
1294
1295 } while (0);
1296
1297 1 gate_mutex_release(&server->lock);
1298
1299 1 gate_string_release(&address);
1300 1 gate_strbuilder_release(&address_builder);
1301 1 gate_httpserver_host_dtor(&host);
1302
1303 1 return ret;
1304 }
1305
1306 ✗ static gate_result_t gate_httpserver_stop_queue(gate_socketqueue_t* queue)
1307 {
1308 ✗ gate_result_t ret = GATE_RESULT_OK;
1309 ✗ gate_socketqueue_stop(queue);
1310 ✗ return ret;
1311 }
1312
1313 1 gate_result_t gate_httpserver_remove_host(gate_httpserver_t* server, gate_httpserver_host_id_t host_id)
1314 {
1315 1 gate_result_t ret = GATE_RESULT_FAILED;
1316 1 gate_httpserver_host_t* ptr_host = NULL;
1317
1318 1 ret = gate_mutex_acquire(&server->lock);
1319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
1320 {
1321 ✗ return ret;
1322 }
1323
1324 do
1325 {
1326 1 ptr_host = (gate_httpserver_host_t*)gate_map_get_value(&server->hosts, &host_id);
1327
1328
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ptr_host)
1329 {
1330 ✗ ret = GATE_RESULT_NOMATCH;
1331 ✗ break;
1332 }
1333
1334 1 gate_map_remove(&server->hosts, &host_id);
1335
1336 1 ret = GATE_RESULT_OK;
1337
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (server->queue)
1338 {
1339 1 ret = gate_socketqueue_close(server->queue, host_id);
1340 }
1341 } while (0);
1342
1343 1 gate_mutex_release(&server->lock);
1344
1345 1 return ret;
1346 }
1347 3 gate_result_t gate_httpserver_remove_all_hosts(gate_httpserver_t* server)
1348 {
1349 3 gate_result_t ret = GATE_RESULT_OK;
1350 gate_map_iterator_t iter;
1351 gate_channel_id_t const* ptr_channel_id;
1352 3 gate_httpserver_connection_t** ptr_conn = NULL;
1353
1354 3 ret = gate_mutex_acquire(&server->lock);
1355
1356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (GATE_FAILED(ret))
1357 {
1358 ✗ return ret;
1359 }
1360
1361 do
1362 {
1363
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (server->queue)
1364 {
1365 2 iter = gate_map_first(&server->hosts);
1366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 while (gate_map_iterator_valid(iter))
1367 {
1368 ✗ ptr_channel_id = (gate_channel_id_t const*)gate_map_iterator_key(iter);
1369 ✗ if (ptr_channel_id)
1370 {
1371 ✗ gate_socketqueue_close(server->queue, *ptr_channel_id);
1372 }
1373 ✗ iter = gate_map_iterator_next(iter);
1374 }
1375
1376 2 iter = gate_map_first(&server->connections);
1377
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 while (gate_map_iterator_valid(iter))
1378 {
1379 ✗ ptr_channel_id = (gate_channel_id_t const*)gate_map_iterator_key(iter);
1380 ✗ ptr_conn = (gate_httpserver_connection_t**)gate_map_iterator_value(iter);
1381 ✗ if (ptr_conn)
1382 {
1383 ✗ gate_httpserver_detach_connection(*ptr_conn);
1384 }
1385 ✗ if (ptr_channel_id)
1386 {
1387 ✗ gate_socketqueue_close(server->queue, *ptr_channel_id);
1388 }
1389 ✗ iter = gate_map_iterator_next(iter);
1390 }
1391 }
1392 3 gate_map_clear(&server->connections);
1393 3 gate_map_clear(&server->hosts);
1394
1395 3 ret = GATE_RESULT_OK;
1396 } while (0);
1397
1398 3 gate_mutex_release(&server->lock);
1399
1400 3 return ret;
1401 }
1402
1403 1 gate_result_t gate_httpserver_start(gate_httpserver_t* server)
1404 {
1405 gate_result_t ret;
1406
1407 1 ret = gate_mutex_acquire(&server->lock);
1408
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
1409 {
1410 ✗ return ret;
1411 }
1412
1413 do
1414 {
1415 1 ret = gate_httpserver_create_queue(server);
1416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1417
1418 1 ret = gate_socketqueue_set_callback(server->queue, &gate_httpserver_queue_event, server);
1419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1420
1421 1 ret = gate_socketqueue_start(server->queue);
1422 } while (0);
1423
1424 1 gate_mutex_release(&server->lock);
1425 1 return ret;
1426 }
1427
1428 3 gate_result_t gate_httpserver_stop(gate_httpserver_t* server)
1429 {
1430 gate_result_t ret;
1431 gate_socketqueue_t* ptr_queue;
1432
1433 3 ret = gate_mutex_acquire(&server->lock);
1434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (GATE_FAILED(ret))
1435 {
1436 ✗ return ret;
1437 }
1438
1439 3 ptr_queue = server->queue;
1440
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (ptr_queue)
1441 {
1442 2 gate_object_retain(ptr_queue);
1443 }
1444 3 gate_mutex_release(&server->lock);
1445
1446
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (ptr_queue)
1447 {
1448 2 ret = gate_socketqueue_stop(server->queue);
1449 2 gate_object_release(ptr_queue);
1450 }
1451
1452 3 return ret;
1453 }
1454
1455
1456 2 gate_result_t gate_httpserver_destroy(gate_httpserver_t* server)
1457 {
1458 2 gate_result_t ret = GATE_RESULT_FAILED;
1459
1460
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (server)
1461 {
1462 2 gate_httpserver_remove_all_hosts(server);
1463
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (server->queue)
1464 {
1465 1 gate_socketqueue_close_all(server->queue);
1466 }
1467 2 gate_httpserver_stop(server);
1468
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (server->queue)
1469 {
1470 1 gate_object_release(server->queue);
1471 }
1472
1473 2 gate_map_destroy(&server->connections);
1474 2 gate_map_destroy(&server->hosts);
1475
1476
1477 2 gate_mutex_destroy(&server->lock);
1478 2 ret = GATE_RESULT_OK;
1479 }
1480 2 return ret;
1481
1482 }
1483
1484
1485 ✗ gate_result_t gate_httpserver_filehandler_config_init(gate_httpserver_filehandler_config_t* cfg,
1486 gate_string_t const* request_root_path,
1487 gate_string_t const* file_root_path,
1488 gate_bool_t webdav)
1489 {
1490 ✗ gate_result_t ret = GATE_RESULT_OK;
1491 ✗ gate_mem_clear(cfg, sizeof(gate_httpserver_filehandler_config_t));
1492
1493 do
1494 {
1495 ✗ if (NULL == gate_string_clone(&cfg->http_request_root_path, request_root_path))
1496 {
1497 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1498 ✗ break;
1499 }
1500 ✗ if (NULL == gate_string_clone(&cfg->filesystem_root_path, file_root_path))
1501 {
1502 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1503 ✗ break;
1504 }
1505 ✗ cfg->enable_webdav = webdav;
1506 } while (0);
1507
1508 ✗ if (GATE_FAILED(ret))
1509 {
1510 ✗ gate_httpserver_filehandler_config_release(cfg);
1511 }
1512
1513 ✗ return ret;
1514 }
1515
1516 ✗ gate_result_t gate_httpserver_filehandler_config_copy(void* dest, void const* src)
1517 {
1518 ✗ gate_httpserver_filehandler_config_t const* ptr_src = (gate_httpserver_filehandler_config_t const*)src;
1519 ✗ gate_httpserver_filehandler_config_t* ptr_dst = (gate_httpserver_filehandler_config_t*)dest;
1520
1521 ✗ return gate_httpserver_filehandler_config_init(ptr_dst, &ptr_src->http_request_root_path,
1522 ✗ &ptr_src->filesystem_root_path, ptr_src->enable_webdav);
1523 }
1524
1525 ✗ void gate_httpserver_filehandler_config_release(void* config)
1526 {
1527 ✗ gate_httpserver_filehandler_config_t* ptr = (gate_httpserver_filehandler_config_t*)config;
1528 ✗ if (ptr)
1529 {
1530 ✗ gate_string_release(&ptr->http_request_root_path);
1531 ✗ gate_string_release(&ptr->filesystem_root_path);
1532 ✗ gate_mem_clear(ptr, sizeof(gate_httpserver_filehandler_config_t));
1533 }
1534 ✗ }
1535
1536 ✗ gate_result_t gate_httpserver_filehandler_process(gate_httpserver_filehandler_config_t const* config,
1537 gate_http_request_t const* http_request,
1538 gate_httpserver_response_t* http_response)
1539 {
1540 gate_result_t ret;
1541 gate_result_t result;
1542 ✗ gate_string_t abs_path = GATE_INIT_EMPTY;
1543 ✗ gate_string_t sub_path = GATE_INIT_EMPTY;
1544 ✗ gate_strbuilder_t builder = GATE_INIT_EMPTY;
1545 ✗ gate_string_t file_path = GATE_INIT_EMPTY;
1546 ✗ gate_string_t path_components[192] = GATE_INIT_EMPTY;
1547 ✗ gate_string_t link_path_components[2] = GATE_INIT_EMPTY;
1548 ✗ gate_string_t link_path = GATE_INIT_EMPTY;
1549 ✗ gate_size_t components_count = 0;
1550 ✗ gate_enumint_t file_attribs = 0;
1551 ✗ gate_enumint_t file_access = 0;
1552 ✗ gate_int64_t file_size = 0;
1553 ✗ gate_controlstream_t* ptr_file = NULL;
1554 ✗ gate_stringstream_t* ptr_strstream = NULL;
1555 ✗ gate_stream_t* ptr_strm = NULL;
1556 ✗ gate_string_t mime_type = GATE_INIT_EMPTY;
1557 gate_file_dirreader_t dirreader;
1558 char dirbuffer[GATE_MAX_FILEPATH_LENGTH];
1559 char linkbuffer[GATE_MAX_FILEPATH_LENGTH];
1560
1561 do
1562 {
1563 ✗ ret = gate_uri_parse_path(&http_request->path, &abs_path, NULL);
1564 ✗ GATE_BREAK_IF_FAILED(ret);
1565
1566 ✗ if (!gate_string_starts_with(&abs_path, &config->http_request_root_path))
1567 {
1568 ✗ ret = GATE_RESULT_NOMATCH;
1569 ✗ break;
1570 }
1571 ✗ gate_string_substr(&sub_path, &abs_path, gate_string_length(&config->http_request_root_path), GATE_STR_NPOS);
1572 ✗ if (!gate_string_ends_with_char(&config->http_request_root_path, '/'))
1573 {
1574 ✗ if (!gate_string_starts_with_char(&sub_path, '/'))
1575 {
1576 ✗ ret = GATE_RESULT_NOMATCH;
1577 ✗ break;
1578 }
1579 ✗ gate_string_substr(&sub_path, &sub_path, 1, GATE_STR_NPOS);
1580 }
1581
1582 ✗ while (gate_string_ends_with_char(&sub_path, '/'))
1583 {
1584 ✗ gate_string_substr(&sub_path, &sub_path, 0, gate_string_length(&sub_path) - 1);
1585 }
1586 ✗ while (gate_string_starts_with_char(&sub_path, '/'))
1587 {
1588 ✗ gate_string_substr(&sub_path, &sub_path, 1, GATE_STR_NPOS);
1589 }
1590
1591 ✗ gate_string_duplicate(&path_components[0], &config->filesystem_root_path);
1592
1593 ✗ components_count = 1 + gate_file_split_path_components(&sub_path, '/', &path_components[1], sizeof(path_components) / sizeof(path_components[0]) - 1);
1594
1595 ✗ gate_strbuilder_create(&builder, 256);
1596
1597 ✗ ret = gate_file_build_path_components(&builder, gate_file_path_separator_char, path_components, components_count);
1598 ✗ GATE_BREAK_IF_FAILED(ret);
1599
1600 ✗ if (NULL == gate_strbuilder_to_string(&builder, &file_path))
1601 {
1602 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1603 ✗ break;
1604 }
1605
1606 ✗ gate_strbuilder_release(&builder);
1607
1608 ✗ ret = gate_file_get_attributes(&file_path, &file_attribs, &file_access);
1609 ✗ GATE_BREAK_IF_FAILED(ret);
1610
1611 ✗ if (GATE_FLAG_ENABLED(file_attribs, GATE_FILEENTRY_ATTRIB_VOLUME) || GATE_FLAG_ENABLED(file_attribs, GATE_FILEENTRY_ATTRIB_DIRECTORY))
1612 {
1613 ✗ ptr_strstream = gate_stringstream_create(256);
1614 ✗ if (NULL == ptr_strstream)
1615 {
1616 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1617 ✗ break;
1618 }
1619 ✗ ptr_strm = (gate_stream_t*)ptr_strstream;
1620 ✗ gate_stream_print(ptr_strm,
1621 GATE_PRINT_CSTR, "<html><body>", GATE_PRINT_NEWLINE,
1622 GATE_PRINT_CSTR, "<h1>", GATE_PRINT_STRING, &http_request->path, GATE_PRINT_CSTR, "</h1>", GATE_PRINT_NEWLINE,
1623 GATE_PRINT_CSTR, "<hr>", GATE_PRINT_NEWLINE,
1624 GATE_PRINT_END);
1625
1626 ✗ ret = gate_file_dir_open(&file_path, &dirreader);
1627 ✗ if (GATE_SUCCEEDED(ret))
1628 {
1629 ✗ gate_string_duplicate(&link_path_components[0], &abs_path);
1630 ✗ while (0 != gate_file_dir_read(&dirreader, dirbuffer, sizeof(dirbuffer), true))
1631 {
1632 ✗ gate_bool_t is_dir = false;
1633 gate_string_t file_abs_path;
1634 ✗ gate_enumint_t file_attribs = 0;
1635 gate_enumint_t file_access_bits;
1636 ✗ gate_size_t len = gate_file_build_path(linkbuffer, sizeof(linkbuffer), file_path.str, file_path.length, dirbuffer, gate_str_length(dirbuffer));
1637 ✗ gate_string_create_static_len(&file_abs_path, linkbuffer, len);
1638 ✗ ret = gate_file_get_attributes(&file_abs_path, &file_attribs, &file_access_bits);
1639 ✗ if (GATE_SUCCEEDED(ret))
1640 {
1641 ✗ is_dir = GATE_FLAG_ENABLED(file_attribs, GATE_FILEENTRY_ATTRIB_DIRECTORY)
1642 ✗ || GATE_FLAG_ENABLED(file_attribs, GATE_FILEENTRY_ATTRIB_VOLUME);
1643 }
1644
1645 ✗ gate_string_create_static(&link_path_components[1], dirbuffer);
1646
1647 ✗ gate_strbuilder_create_static(&builder, linkbuffer, sizeof(linkbuffer), 0);
1648
1649 ✗ gate_file_build_path_components(&builder, '/', link_path_components, 2);
1650 ✗ gate_strbuilder_to_string(&builder, &link_path);
1651
1652 ✗ gate_stream_print(
1653 ptr_strm,
1654 GATE_PRINT_CSTR, "<div>",
1655 GATE_PRINT_CSTR, (is_dir ? "&#x01f4c1;" : "&#x01f5b9;"),
1656 GATE_PRINT_CSTR, " <a href=\"",
1657 GATE_PRINT_STRING, &link_path,
1658 GATE_PRINT_CSTR, "\">",
1659 GATE_PRINT_CSTR, dirbuffer,
1660 GATE_PRINT_CSTR, "</a></div>",
1661 GATE_PRINT_NEWLINE,
1662 GATE_PRINT_END);
1663
1664 ✗ gate_string_release(&link_path);
1665 ✗ gate_string_release(&link_path_components[1]);
1666 ✗ gate_strbuilder_release(&builder);
1667 }
1668 ✗ gate_file_dir_close(&dirreader);
1669 }
1670 ✗ gate_stream_print(ptr_strm,
1671 GATE_PRINT_CSTR, "</body></html>", GATE_PRINT_NEWLINE,
1672 GATE_PRINT_END);
1673
1674 ✗ file_size = gate_stringstream_size(ptr_strstream);
1675 }
1676 else
1677 {
1678 ✗ ret = gate_file_get_size(&file_path, &file_size);
1679 ✗ GATE_BREAK_IF_FAILED(ret);
1680
1681 ✗ ret = gate_file_openstream(&file_path, GATE_STREAM_OPEN_READ, &ptr_file);
1682 ✗ GATE_BREAK_IF_FAILED(ret);
1683
1684 ✗ ptr_strm = (gate_stream_t*)ptr_file;
1685
1686 ✗ result = gate_http_resolve_mime_type(&file_path, &mime_type);
1687 ✗ if (GATE_SUCCEEDED(result))
1688 {
1689 ✗ gate_httpserver_response_set_header_str(http_response, GATE_HTTP_HEADER_CONTENTTYPE, mime_type.str);
1690 ✗ gate_string_release(&mime_type);
1691 }
1692 else
1693 {
1694 ✗ gate_httpserver_response_set_header_str(http_response, GATE_HTTP_HEADER_CONTENTTYPE, GATE_HTTP_HEADERVALUE_CONTENTTYPE_OCTETSTREAM);
1695 }
1696 }
1697
1698 ✗ ret = gate_httpserver_response_set_status(http_response, 200);
1699 ✗ GATE_BREAK_IF_FAILED(ret);
1700
1701 ✗ ret = gate_httpserver_response_set_header_int(http_response, GATE_HTTP_HEADER_CONTENTLENGTH, file_size);
1702 ✗ GATE_BREAK_IF_FAILED(ret);
1703
1704 ✗ ret = gate_httpserver_response_send_headers(http_response);
1705 ✗ GATE_BREAK_IF_FAILED(ret);
1706
1707 ✗ ret = gate_stream_transfer_limit(ptr_strm, (gate_uint64_t)file_size, (gate_stream_t*)http_response, NULL);
1708 ✗ GATE_BREAK_IF_FAILED(ret);
1709
1710 ✗ ret = gate_stream_flush(http_response);
1711 ✗ GATE_BREAK_IF_FAILED(ret);
1712
1713 } while (0);
1714
1715 ✗ while (components_count > 0)
1716 {
1717 ✗ --components_count;
1718 ✗ gate_string_release(&path_components[components_count]);
1719 }
1720
1721 ✗ if (ptr_strm)
1722 {
1723 ✗ gate_object_release(ptr_strm);
1724 }
1725 ✗ gate_string_release(&file_path);
1726 ✗ gate_string_release(&link_path_components[1]);
1727 ✗ gate_string_release(&link_path_components[0]);
1728 ✗ gate_strbuilder_release(&builder);
1729 ✗ gate_string_release(&sub_path);
1730 ✗ gate_string_release(&abs_path);
1731
1732 ✗ return ret;
1733 }
1734