GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/httpservers.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 453 746 60.7%
Functions: 35 45 77.8%
Branches: 151 353 42.8%

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