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 ? "📁" : "🖹"), | ||
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 |