GCC Code Coverage Report


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