GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/httpclients.c
Date: 2026-09-21 06:26:40
Exec Total Coverage
Lines: 314 435 72.2%
Functions: 14 15 93.3%
Branches: 150 360 41.7%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, 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/httpclients.h"
30 #include "gate/results.h"
31 #include "gate/utilities.h"
32
33 #if defined(GATE_SYS_WIN)
34 # define GATE_NET_HTTPCLIENT_NATIVE_IMPL 1
35 # if defined(GATE_SYS_WINSTORE) || defined(GATE_SYS_WIN16)
36 # define GATE_NET_HTTPCLIENT_NO_IMPL 1
37 # else
38 //# define GATE_NET_HTTPCLIENT_CURL
39 # define GATE_NET_HTTPCLIENT_WINAPI 1
40 # endif
41
42 #elif defined(GATE_SYS_WASM)
43 # define GATE_NET_HTTPCLIENT_WASM_IMPL 1
44 #else
45 # define GATE_NET_HTTPCLIENT_NATIVE_IMPL
46 # define GATE_NET_HTTPCLIENT_CURL
47 #endif
48
49 #if defined(GATE_NET_HTTPCLIENT_NATIVE_IMPL)
50
51 #include "gate/net/sockets.h"
52 #include "gate/net/sslsessions.h"
53 #include "gate/net/nameresolvers.h"
54 #include "gate/net/sockettools.h"
55
56 2 static gate_result_t native_httpclient_release(gate_httpclient_t* client)
57 {
58
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (client)
59 {
60 2 gate_string_release(&client->server);
61 2 gate_mem_clear(client, sizeof(gate_httpclient_t));
62 }
63 2 return GATE_RESULT_OK;
64 }
65
66 2 static gate_result_t native_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
67 {
68 2 gate_result_t ret = GATE_RESULT_FAILED;
69 gate_socket_endpoint_t ep;
70 2 gate_size_t sz = sizeof(ep);
71 gate_uint32_t ipaddr;
72
73 do
74 {
75
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!client || !server)
76 {
77 ret = GATE_RESULT_INVALIDARG;
78 break;
79 }
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (port == 0)
81 {
82 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_SECURE))
83 {
84 port = 443;
85 }
86 else
87 {
88 port = 80;
89 }
90 }
91
92 2 gate_mem_clear(client, sizeof(gate_httpclient_t));
93 2 client->flags = flags;
94 2 client->port = port;
95
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&client->server, server))
96 {
97 ret = GATE_RESULT_OUTOFMEMORY;
98 break;
99 }
100 2 ret = gate_socket_init();
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
102
103 2 ret = gate_net_resolve_host(server, GATE_SOCKET_FAMILY_INET4, &ep, &sz);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
105
106 2 ipaddr = *((gate_uint32_t*)&ep.ip4.address.item[0]);
107 2 *((gate_uint32_t*)&client->handles[0]) = ipaddr;
108 2 ret = GATE_RESULT_OK;
109 } while (0);
110
111
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (GATE_FAILED(ret) && client)
112 {
113 native_httpclient_release(client);
114 }
115 2 return ret;
116 }
117
118 static gate_string_t const default_http_version = GATE_STRING_INIT_STATIC("1.1");
119
120 2 static gate_result_t native_create_http_header(
121 gate_http_request_t* ptr_request,
122 gate_string_t* ptr_out_header,
123 gate_bool_t* ptr_out_connclose)
124 {
125 gate_result_t ret;
126 2 gate_stringstream_t* ss = NULL;
127 do
128 {
129 gate_string_t const* value_connection;
130 2 ss = gate_stringstream_create(256);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ss == NULL)
132 {
133 ret = GATE_RESULT_OUTOFMEMORY;
134 break;
135 }
136
137 2 ret = gate_http_request_build_header(ptr_request, (gate_stream_t*)ss);
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
139
140 2 ret = gate_stringstream_to_string(ss, ptr_out_header);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
142
143 2 value_connection = gate_util_stringmap_get(&ptr_request->headers, GATE_HTTP_HEADER_CONNECTION);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (value_connection == NULL)
145 {
146 *ptr_out_connclose = true;
147 }
148 else
149 {
150 2 *ptr_out_connclose = gate_string_equals_str_ic(value_connection, GATE_HTTP_HEADERVALUE_CONNECTION_KEEPALIVE);
151 }
152 2 ret = GATE_RESULT_OK;
153 } while (0);
154
155
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ss != NULL)
156 {
157 2 gate_object_release(ss);
158 }
159 2 return ret;
160 }
161
162 2 static gate_string_t* native_read_response_header(gate_stream_t* strm, gate_string_t* header)
163 {
164 2 gate_string_t* ret = NULL;
165 2 gate_strbuilder_t builder = GATE_INIT_EMPTY;
166 char buffer[4];
167 gate_size_t received;
168 gate_result_t result;
169 char const* ptr_content;
170 do
171 {
172 2 gate_strbuilder_create(&builder, 512);
173
174 for (;;)
175 {
176 132 received = 0;
177 134 result = gate_stream_read(strm, &buffer[0], 1, &received);
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 if (GATE_FAILED(result))
179 {
180 break;
181 }
182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 if (received == 0)
183 {
184 result = GATE_RESULT_ENDOFSTREAM;
185 break;
186 }
187 134 gate_strbuilder_append_text(&builder, &buffer[0], 1);
188 134 received = gate_strbuilder_length(&builder);
189
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 6 times.
134 if (received >= 4)
190 {
191 128 ptr_content = gate_strbuilder_ptr(&builder, received - 4);
192
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 126 times.
128 if (gate_str_compare(ptr_content, 4, "\r\n\r\n", 4) == 0)
193 {
194 /* end of header found */
195 2 break;
196 }
197 }
198 }
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(result);
200
201 2 ret = gate_strbuilder_to_string(&builder, header);
202 } while (0);
203
204 2 gate_strbuilder_release(&builder);
205
206 2 return ret;
207 }
208
209 2 static gate_result_t native_parse_response_header(gate_string_t const* http_header, gate_http_response_t* response)
210 {
211 2 gate_result_t ret = GATE_RESULT_FAILED;
212 2 gate_string_t line = GATE_STRING_INIT_EMPTY;
213 2 gate_string_t header = GATE_STRING_INIT_EMPTY;
214 2 gate_string_t key = GATE_STRING_INIT_EMPTY;
215 2 gate_string_t value = GATE_STRING_INIT_EMPTY;
216 gate_size_t pos;
217 2 gate_bool_t first_line = true;
218 2 gate_uint64_t status = 0;
219
220 do
221 {
222
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&header, http_header))
223 {
224 ret = GATE_RESULT_OUTOFMEMORY;
225 break;
226 }
227 2 ret = GATE_RESULT_OK;
228
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 while (NULL != gate_string_read_line(&line, &header, &header))
229 {
230
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (first_line)
231 {
232 /* "HTTP/x.y 222 TEXT" */
233
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!gate_string_starts_with_str(&line, "HTTP/"))
234 {
235 ret = GATE_RESULT_INVALIDHEADER;
236 break;
237 }
238 2 gate_str_parse_uint64(gate_string_ptr(&line, 9), 3, &status);
239 2 response->status_code = (gate_uint32_t)status;
240 2 first_line = false;
241 }
242 else
243 {
244 6 gate_string_trim(&line, &line);
245
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 if (!gate_string_is_empty(&line))
246 {
247 4 pos = gate_string_char_pos(&line, ':', 0);
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (pos == GATE_STR_NPOS)
249 {
250 ret = GATE_RESULT_INVALIDCONTENT;
251 break;
252 }
253 4 gate_string_substr(&key, &line, 0, pos);
254 4 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
255 4 gate_string_trim(&value, &value);
256
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (!gate_string_is_empty(&key))
257 {
258 4 gate_map_add(&response->headers, &key, &value);
259 }
260 4 gate_string_release(&key);
261 4 gate_string_release(&value);
262 }
263 }
264 8 gate_string_release(&line);
265 }
266 } while (0);
267
268 2 gate_string_release(&header);
269 2 gate_string_release(&line);
270 2 return ret;
271 }
272
273 2 gate_result_t native_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* ptr_request, gate_http_response_t* response)
274 {
275 2 gate_result_t ret = GATE_RESULT_FAILED;
276 2 gate_string_t request_header = GATE_STRING_INIT_EMPTY;
277 2 gate_string_t response_header = GATE_STRING_INIT_EMPTY;
278 gate_socket_endpoint_t ep;
279 2 gate_controlstream_t* ptr_controlstream = NULL;
280 2 gate_stream_t* ptr_sockstream = NULL;
281 2 gate_stream_t* ptr_sslstream = NULL;
282 gate_ssl_session_params_t ssl_params;
283 2 gate_size_t written = 0;
284 2 gate_bool_t keep_alive = false;
285 2 gate_http_request_t request = GATE_INIT_EMPTY;
286
287 do
288 {
289 2 ret = gate_http_request_copy(&request, ptr_request);
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
291
292 2 ret = gate_http_response_init(response);
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
294
295
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_string_is_empty(&request.host))
296 {
297 gate_string_duplicate(&request.host, &client->server);
298 }
299
300 2 ret = native_create_http_header(&request, &request_header, &keep_alive);
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
302
303 2 ep.ip4.family = GATE_SOCKET_FAMILY_INET4;
304 2 *((gate_uint32_t*)&ep.ip4.address.item[0]) = *((gate_uint32_t*)&client->handles[0]);
305 2 ep.ip4.port = client->port;
306
307 2 ret = gate_socketstream_create_endpoint(&ep, &ptr_controlstream, false);
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
309 2 ptr_sockstream = (gate_stream_t*)ptr_controlstream;
310
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE))
312 {
313 gate_mem_clear(&ssl_params, sizeof(ssl_params));
314 ssl_params.server_side = false;
315 ssl_params.session_version = GATE_SSL_SESSION_TYPE_TLS_1_2;
316 ret = gate_ssl_stream_create(&ssl_params, ptr_sockstream, &ptr_sslstream);
317 GATE_BREAK_IF_FAILED(ret);
318
319 gate_object_release(ptr_sockstream);
320 ptr_controlstream = NULL;
321 ptr_sockstream = ptr_sslstream;
322 ptr_sslstream = NULL;
323
324 /* ensure tls negotiation: */
325 ret = gate_stream_flush(ptr_sockstream);
326 GATE_BREAK_IF_FAILED(ret);
327 }
328
329 /* send HTTP header */
330 2 ret = gate_stream_write_block(ptr_sockstream, gate_string_ptr(&request_header, 0), gate_string_length(&request_header), &written);
331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
332
333
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (request.upload_stream)
334 {
335 /* send HTTP upload content */
336 1 gate_size_t transferred = 0;
337 do
338 {
339 2 ret = gate_http_request_build_upload_chunk(&request, ptr_sockstream, 0, &transferred);
340
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 } while (GATE_SUCCEEDED(ret) && (transferred > 0));
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
342 }
343
344
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (!keep_alive && (ptr_controlstream != NULL))
345 {
346 /* socket shutdown-send */
347 2 gate_stream_close(ptr_controlstream, GATE_STREAM_CLOSE_OUTPUT);
348 }
349
350 /* await response */
351
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == native_read_response_header(ptr_sockstream, &response_header))
352 {
353 ret = GATE_RESULT_INVALIDHEADER;
354 break;
355 }
356
357 2 ret = native_parse_response_header(&response_header, response);
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
359
360 2 response->response_stream = ptr_sockstream;
361 2 ptr_sockstream = NULL;
362
363 } while (0);
364
365 2 gate_http_request_release(&request);
366
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ptr_sockstream)
368 {
369 gate_object_release(ptr_sockstream);
370 }
371
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
372 {
373 gate_http_response_release(response);
374 }
375 2 gate_string_release(&response_header);
376 2 gate_string_release(&request_header);
377 2 return ret;
378 }
379
380
381 #endif
382
383
384 #if defined(GATE_NET_HTTPCLIENT_WINAPI)
385
386 #include "gate/net/platform/wininet_api.h"
387 #include "gate/utilities.h"
388 #include "gate/debugging.h"
389
390
391 static gate_result_t wininet_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
392 {
393 gate_result_t ret;
394 HINTERNET hinet = NULL;
395 HINTERNET hconnect = NULL;
396 DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG; /* INTERNET_OPEN_TYPE_DIRECT */
397 DWORD dwFlags = 0;
398 TCHAR httphost[4096];
399 gate_bool_t secure = GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_SECURE);
400 INTERNET_PORT httpport = (port == 0) ? (secure ? 443 : 80) : port;
401
402 do
403 {
404 ret = load_wininet_api();
405 GATE_BREAK_IF_FAILED_TRACE(ret, "load_wininet_api() failed");
406
407 gate_mem_clear(client, sizeof(gate_httpclient_t));
408
409 if (0 == gate_win32_utf8_2_winstr(server->str, server->length, httphost, sizeof(httphost) / sizeof(httphost[0])))
410 {
411 ret = GATE_RESULT_INVALIDARG;
412 break;
413 }
414
415 hinet = WinInet.InetOpen(_T("OpenGATE HTTP Client"), dwAccessType, NULL, NULL, dwFlags);
416 if (hinet == NULL)
417 {
418 GATE_DEBUG_TRACE("InternetOpen() failed");
419 gate_win32_print_lasterror(&ret, NULL, 0);
420 break;
421 }
422
423 hconnect = WinInet.InetConnect(hinet, httphost, httpport, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)client);
424 if (hconnect == NULL)
425 {
426 GATE_DEBUG_TRACE("InternetConnect() failed");
427 gate_win32_print_lasterror(&ret, NULL, 0);
428 break;
429 }
430
431 if (NULL == gate_string_clone(&client->server, server))
432 {
433 ret = GATE_RESULT_OUTOFMEMORY;
434 break;
435 }
436
437 client->flags = flags;
438 client->port = port;
439 client->handles[0] = hinet;
440 client->handles[1] = hconnect;
441 hinet = NULL;
442 hconnect = NULL;
443 ret = GATE_RESULT_OK;
444 } while (0);
445
446 if (hconnect != NULL)
447 {
448 WinInet.InetCloseHandle(hconnect);
449 }
450 if (hinet != NULL)
451 {
452 WinInet.InetCloseHandle(hinet);
453 }
454 return ret;
455 }
456
457 gate_result_t wininet_httpclient_release(gate_httpclient_t* client)
458 {
459 gate_result_t ret = GATE_RESULT_OK;
460 HINTERNET hinet = (HINTERNET)client->handles[0];
461 HINTERNET hconnect = (HINTERNET)client->handles[1];
462
463 if (hconnect)
464 {
465 WinInet.InetCloseHandle(hconnect);
466 }
467 if (hinet)
468 {
469 WinInet.InetCloseHandle(hinet);
470 }
471
472 gate_string_release(&client->server);
473 gate_mem_clear(client, sizeof(gate_httpclient_t));
474
475 return ret;
476 }
477
478
479 static void gate_http_response_release_impl(void* self);
480 static int gate_http_response_retain_impl(void* self);
481 static char const* gate_http_response_interface_name_impl(void* self);
482 static gate_result_t gate_http_response_read_impl(void* self, char* buffer, gate_size_t bufferlength, gate_size_t* returned);
483 static gate_result_t gate_http_response_peek_impl(void* self, char* buffer, gate_size_t bufferlength, gate_size_t* returned);
484 static gate_result_t gate_http_response_write_impl(void* self, char const* buffer, gate_size_t bufferlength, gate_size_t* written);
485 static gate_result_t gate_http_response_flush_impl(void* self);
486
487
488 static GATE_INTERFACE_VTBL(gate_stream) gate_http_client_response_vtbl;
489 static void gate_init_http_client_response_vtbl()
490 {
491 if (!gate_http_client_response_vtbl.get_interface_name)
492 {
493 GATE_INTERFACE_VTBL(gate_stream) const local_vtbl =
494 {
495 &gate_http_response_interface_name_impl,
496 &gate_http_response_release_impl,
497 &gate_http_response_retain_impl,
498 &gate_http_response_read_impl,
499 &gate_http_response_peek_impl,
500 &gate_http_response_write_impl,
501 &gate_http_response_flush_impl
502 };
503 gate_http_client_response_vtbl = local_vtbl;
504 }
505 }
506
507
508 typedef struct gate_http_response_impl
509 {
510 GATE_INTERFACE_VTBL(gate_stream)* vtbl;
511
512 gate_atomic_int_t ref_counter;
513 HINTERNET handle;
514 } gate_http_response_impl_t;
515
516 static void gate_http_response_release_impl(void* self)
517 {
518 gate_http_response_impl_t* impl = (gate_http_response_impl_t*)self;
519 if (gate_atomic_int_dec(&impl->ref_counter) == 0)
520 {
521 WinInet.InetCloseHandle(impl->handle);
522 gate_mem_dealloc(impl);
523 }
524 }
525 static int gate_http_response_retain_impl(void* self)
526 {
527 gate_http_response_impl_t* impl = (gate_http_response_impl_t*)self;
528 return gate_atomic_int_inc(&impl->ref_counter);
529 }
530 static char const* gate_http_response_interface_name_impl(void* self)
531 {
532 GATE_UNUSED_ARG(self);
533 return GATE_INTERFACE_NAME_STREAM;
534 }
535 static gate_result_t gate_http_response_read_impl(void* self, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
536 {
537 gate_http_response_impl_t* impl = (gate_http_response_impl_t*)self;
538 gate_result_t ret;
539 DWORD bytesreceived = 0;
540 if (0 == WinInet.InetReadFile(impl->handle, buffer, (DWORD)bufferlength, &bytesreceived))
541 {
542 GATE_DEBUG_TRACE("InternetReadFile() failed");
543 gate_win32_print_lasterror(&ret, NULL, 0);
544 }
545 else
546 {
547 if (returned != NULL)
548 {
549 *returned = (gate_size_t)bytesreceived;
550 }
551 ret = GATE_RESULT_OK;
552 }
553 return ret;
554 }
555 static gate_result_t gate_http_response_peek_impl(void* self, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
556 {
557 GATE_UNUSED_ARG(self);
558 GATE_UNUSED_ARG(buffer);
559 GATE_UNUSED_ARG(bufferlength);
560 GATE_UNUSED_ARG(returned);
561 return GATE_RESULT_NOTSUPPORTED;
562 }
563 static gate_result_t gate_http_response_write_impl(void* self, char const* buffer, gate_size_t bufferlength, gate_size_t* written)
564 {
565 GATE_UNUSED_ARG(self);
566 GATE_UNUSED_ARG(buffer);
567 GATE_UNUSED_ARG(bufferlength);
568 GATE_UNUSED_ARG(written);
569 return GATE_RESULT_NOTSUPPORTED;
570 }
571 static gate_result_t gate_http_response_flush_impl(void* self)
572 {
573 GATE_UNUSED_ARG(self);
574 return GATE_RESULT_NOTSUPPORTED;
575 }
576
577 static gate_bool_t gate_httpclient_add_header(gate_map_t* headers, LPCTSTR headerline, gate_size_t headerlinelength)
578 {
579 gate_bool_t ret = false;
580 char key[1024];
581 char value[4096];
582 gate_size_t keylength;
583 gate_size_t valuelength;
584 gate_string_t strkey = GATE_STRING_INIT_EMPTY;
585 gate_string_t strvalue = GATE_STRING_INIT_EMPTY;
586 gate_string_t strkey2 = GATE_STRING_INIT_EMPTY;
587 gate_string_t strvalue2 = GATE_STRING_INIT_EMPTY;
588 gate_size_t pos = gate_win32_winstr_char_pos(headerline, headerlinelength, ':', 0);
589
590 do
591 {
592 if (pos != GATE_STR_NPOS)
593 {
594 keylength = gate_win32_winstr_2_utf8(&headerline[0], pos, key, sizeof(key));
595 valuelength = gate_win32_winstr_2_utf8(&headerline[pos + 1], headerlinelength - pos - 1, value, sizeof(value));
596
597 if (NULL == gate_string_create(&strkey, key, keylength)) break;
598 if (NULL == gate_string_create(&strvalue, value, valuelength)) break;
599
600 gate_string_trim(&strkey2, &strkey);
601 gate_string_trim(&strvalue2, &strvalue);
602
603 ret = (NULL != gate_util_stringmap_add_string(headers, &strkey2, &strvalue2)) ? true : false;
604 }
605 } while (0);
606
607 gate_string_release(&strkey2);
608 gate_string_release(&strvalue2);
609
610 gate_string_release(&strkey);
611 gate_string_release(&strvalue);
612 return ret;
613 }
614
615 #ifndef INTERNET_OPTION_CLIENT_CERT_CONTEXT
616 #define INTERNET_OPTION_CLIENT_CERT_CONTEXT 84
617 #endif
618
619 static gate_result_t wininet_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
620 {
621 gate_result_t ret;
622 HINTERNET hconnect = (HINTERNET)client->handles[1];
623 HINTERNET hrequest = NULL;
624 gate_uint32_t flags = client->flags;
625
626 DWORD dwFlags = 0
627 | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
628 | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_UI
629 | INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION
630 ;
631
632 DWORD dwReqFlags = 0;
633 DWORD dwReqFlagsSize = sizeof(dwReqFlags);
634 DWORD dwTimeOut;
635 DWORD dwHttpVersion = 0x02;
636 DWORD dwPathSize;
637 DWORD dwIndex;
638 DWORD dwStatusCode = 0;
639 DWORD dwStatusCodeSize = (DWORD)sizeof(dwStatusCode);
640 DWORD dwHeaderSize;
641 INTERNET_PROXY_INFO proxyinfo;
642
643 TCHAR user[128] = GATE_INIT_EMPTY;
644 TCHAR pass[128] = GATE_INIT_EMPTY;
645 TCHAR proxy[128] = GATE_INIT_EMPTY;
646 TCHAR proxyuser[128] = GATE_INIT_EMPTY;
647 TCHAR proxypass[128] = GATE_INIT_EMPTY;
648 TCHAR version[32] = GATE_INIT_EMPTY;
649 TCHAR verb[32] = GATE_INIT_EMPTY;
650 TCHAR path[2048] = GATE_INIT_EMPTY;
651 TCHAR referrer[512] = GATE_INIT_EMPTY;
652 TCHAR headers[4096] = GATE_INIT_EMPTY;
653 LPTSTR accepttypes[64] = GATE_INIT_EMPTY;
654 LPTSTR ptrresponseheaders = NULL;
655 LPTSTR ptrtstr = NULL;
656 LPTSTR ptrversion = NULL;
657 LPTSTR ptrrefer = NULL;
658
659 gate_size_t headerlength;
660 gate_strbuilder_t headerbuilder;
661 gate_map_iterator_t iterator;
662 gate_string_t str;
663 gate_memstream_t* memstream = NULL;
664 char const* uploadptr = NULL;
665 gate_size_t uploadsize = 0;
666 DWORD senderror;
667 gate_http_response_impl_t* responseimpl = NULL;
668 gate_string_t const* ptr_referrer = NULL;
669
670 do
671 {
672 ret = load_wininet_api();
673 GATE_BREAK_IF_FAILED_TRACE(ret, "load_wininet_api()");
674
675 gate_mem_clear(response, sizeof(gate_http_response_t));
676
677 gate_strbuilder_create(&headerbuilder, 0);
678
679 gate_win32_string_2_winstr(&request->auth_user, user, sizeof(user) / sizeof(user[0]));
680 gate_win32_string_2_winstr(&request->auth_pass, pass, sizeof(pass) / sizeof(pass[0]));
681 gate_win32_string_2_winstr(&request->proxy_server, proxy, sizeof(proxy) / sizeof(proxy[0]));
682 gate_win32_string_2_winstr(&request->proxy_user, proxyuser, sizeof(proxyuser) / sizeof(proxyuser[0]));
683 gate_win32_string_2_winstr(&request->proxy_pass, proxypass, sizeof(proxypass) / sizeof(proxypass[0]));
684 if (gate_win32_string_2_winstr(&request->version, version, sizeof(version) / sizeof(version[0])))
685 {
686 ptrversion = &version[0];
687 }
688 gate_win32_string_2_winstr(&request->method, verb, sizeof(verb) / sizeof(verb[0]));
689 gate_win32_string_2_winstr(&request->path, path, sizeof(path) / sizeof(path[0]));
690 ptr_referrer = gate_util_stringmap_get(&request->headers, GATE_HTTP_HEADER_REFERER);
691 if (ptr_referrer)
692 {
693 if (gate_win32_string_2_winstr(ptr_referrer, referrer, sizeof(referrer) / sizeof(referrer[0])))
694 {
695 ptrrefer = &referrer[0];
696 }
697 }
698
699 if (request->upload_stream != NULL)
700 {
701 ret = gate_util_buffer_load(&uploadptr, &uploadsize, request->upload_stream, &memstream);
702 GATE_BREAK_IF_FAILED_TRACE(ret, "gate_util_buffer_load() failed");
703 }
704
705 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE))
706 {
707 dwFlags |= INTERNET_FLAG_SECURE;
708 }
709
710 hrequest = WinInet.HTTPOpenRequest(hconnect, verb, path, ptrversion, ptrrefer, (LPCTSTR*)&accepttypes[0], dwFlags, (DWORD_PTR)client);
711 if ((NULL == hrequest) && (GetLastError() == ERROR_INVALID_PARAMETER))
712 {
713 /* Win95 / IE3 does not support some caching flags, try again without them */
714 dwFlags &= ~((DWORD)INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_UI);
715 hrequest = WinInet.HTTPOpenRequest(hconnect, verb, path, ptrversion, ptrrefer, (LPCTSTR*)&accepttypes[0], dwFlags, (DWORD_PTR)client);
716 }
717 if (NULL == hrequest)
718 {
719 GATE_DEBUG_TRACE_MSG_VALUE("HttpOpenRequest() failed", gate_platform_get_last_error());
720 gate_win32_print_lasterror(&ret, NULL, 0);
721 break;
722 }
723
724 WinInet.InetQueryOption(hrequest, INTERNET_OPTION_SECURITY_FLAGS, &dwReqFlags, &dwReqFlagsSize);
725 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_DISABLE_VERIFICATION))
726 {
727 dwReqFlags |= (SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
728 | SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_REVOCATION
729 | SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTP | SECURITY_FLAG_IGNORE_REDIRECT_TO_HTTPS
730 );
731 }
732 WinInet.InetSetOption(hrequest, INTERNET_OPTION_SECURITY_FLAGS, &dwReqFlags, sizeof(dwReqFlags));
733
734 {
735 /* in case of Windows 10+, enable HTTP 2 support: */
736 #ifndef INTERNET_OPTION_HTTP_PROTOCOL_USED
737 # define INTERNET_OPTION_HTTP_PROTOCOL_USED 149
738 #endif
739 WinInet.InetSetOption(hrequest, INTERNET_OPTION_HTTP_PROTOCOL_USED, &dwHttpVersion, sizeof(dwHttpVersion));
740 }
741
742 if (request->connect_timeout_ms != 0)
743 {
744 /* setup connect timeout in milliseconds */
745 dwTimeOut = (DWORD)request->connect_timeout_ms;
746 WinInet.InetSetOption(hrequest, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeOut, sizeof(dwTimeOut));
747 }
748 if (request->send_timeout_ms != 0)
749 {
750 /* setup send timeout in milliseconds */
751 dwTimeOut = (DWORD)request->send_timeout_ms;
752 WinInet.InetSetOption(hrequest, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeOut, sizeof(dwTimeOut));
753 }
754 if (request->receive_timeout_ms != 0)
755 {
756 /* setup receive timeout in milliseconds */
757 dwTimeOut = (DWORD)request->receive_timeout_ms;
758 WinInet.InetSetOption(hrequest, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeOut, sizeof(dwTimeOut));
759 }
760
761 if ((user[0] != 0) || (pass[0] != 0))
762 {
763 /* setup user/password authentication */
764 WinInet.InetSetOption(hrequest, INTERNET_OPTION_USERNAME, (user[0] == 0) ? NULL : &user[0], (DWORD)gate_win32_winstr_length(user));
765 WinInet.InetSetOption(hrequest, INTERNET_OPTION_PASSWORD, (pass[0] == 0) ? NULL : &pass[0], (DWORD)gate_win32_winstr_length(pass));
766 }
767
768 if (proxy[0] != 0)
769 {
770 gate_mem_clear(&proxyinfo, sizeof(proxyinfo));
771 proxyinfo.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
772 proxyinfo.lpszProxy = proxy;
773 WinInet.InetSetOption(hrequest, INTERNET_OPTION_PROXY, &proxyinfo, sizeof(proxyinfo));
774 }
775
776 if ((proxyuser[0] != 0) || (proxypass[0] != 0))
777 {
778 WinInet.InetSetOption(hrequest, INTERNET_OPTION_PROXY_USERNAME, (proxyuser[0] == 0) ? NULL : &proxyuser[0], (DWORD)gate_win32_winstr_length(proxyuser));
779 WinInet.InetSetOption(hrequest, INTERNET_OPTION_PROXY_PASSWORD, (proxypass[0] == 0) ? NULL : &proxypass[0], (DWORD)gate_win32_winstr_length(proxypass));
780 }
781
782 iterator = gate_map_first(&request->headers);
783 while (gate_map_iterator_valid(iterator))
784 {
785 str = GATE_MAP_ITER_KEY(gate_string_t, iterator);
786 gate_strbuilder_append_text(&headerbuilder, str.str, str.length);
787 gate_strbuilder_append_text(&headerbuilder, ": ", 2);
788 str = GATE_MAP_ITER_VALUE(gate_string_t, iterator);
789 gate_strbuilder_append_text(&headerbuilder, str.str, str.length);
790 gate_strbuilder_append_text(&headerbuilder, "\r\n", 2);
791
792 iterator = gate_map_iterator_next(iterator);
793 }
794
795 headerlength = gate_win32_utf8_2_winstr(headerbuilder.buffer->data, headerbuilder.length, headers, sizeof(headers) / sizeof(headers[0]));
796 if (FALSE == WinInet.HTTPSendRequest(hrequest, headers, (DWORD)headerlength, (LPVOID)uploadptr, (DWORD)uploadsize))
797 {
798 senderror = gate_win32_getlasterror();
799
800 #ifndef ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION
801 #define ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION (INTERNET_ERROR_BASE + 168)
802 #endif
803
804 if (ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION == senderror)
805 {
806 /* HTTP redirection was issued -> confirm automatically and retry */
807 WinInet.InetErrorDlg(NULL, hrequest, senderror, FLAGS_ERROR_UI_FLAGS_NO_UI, NULL);
808
809 dwPathSize = sizeof(path);
810 dwIndex = 0;
811 WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_LOCATION, path, &dwPathSize, &dwIndex);
812 }
813 else if (ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED == senderror)
814 {
815 /* If optional client certificate was requested, try to continue without certificate */
816 WinInet.InetErrorDlg(NULL, hrequest, senderror, FLAGS_ERROR_UI_FLAGS_NO_UI, NULL);
817
818 WinInet.InetSetOption(hrequest, INTERNET_OPTION_CLIENT_CERT_CONTEXT, NULL, 0);
819 }
820 else
821 {
822 /* any other error will cancel the operation */
823 GATE_DEBUG_TRACE("HttpSendRequest() failed");
824 ret = GATE_RESULT_FAILED;
825 break;
826 }
827
828 /* second attempt to access http service */
829 if (FALSE == WinInet.HTTPSendRequest(hrequest, headers, (DWORD)headerlength, (LPVOID)uploadptr, (DWORD)uploadsize))
830 {
831 GATE_DEBUG_TRACE("HttpSendRequest() failed");
832 ret = GATE_RESULT_FAILED;
833 break;
834 }
835 }
836
837 /* HTTP request succeeded -> receive HTTP response */
838
839 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusCodeSize, 0))
840 {
841 /* no status code, maybe socket was closed ... */
842 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
843 ret = GATE_RESULT_INVALIDINPUT;
844 break;
845 }
846
847 response->status_code = (gate_uint32_t)dwStatusCode;
848
849 dwHeaderSize = 0;
850 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_RAW_HEADERS, NULL, &dwHeaderSize, NULL))
851 {
852 senderror = gate_win32_getlasterror();
853 if (ERROR_INSUFFICIENT_BUFFER != senderror)
854 {
855 /* cannot receive header size */
856 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
857 ret = GATE_RESULT_INVALIDHEADER;
858 break;
859 }
860 }
861
862 if (dwHeaderSize != 0)
863 {
864 /* attach response headers to response object */
865 if (NULL == gate_util_stringmap_create(&response->headers))
866 {
867 ret = GATE_RESULT_OUTOFMEMORY;
868 break;
869 }
870
871 dwHeaderSize += 32; /* add some extra space */
872 ptrresponseheaders = (LPTSTR)gate_mem_alloc(dwHeaderSize * sizeof(TCHAR));
873 if (NULL == ptrresponseheaders)
874 {
875 ret = GATE_RESULT_OUTOFMEMORY;
876 break;
877 }
878
879 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_RAW_HEADERS, ptrresponseheaders, &dwHeaderSize, NULL))
880 {
881 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
882 ret = GATE_RESULT_INVALIDHEADER;
883 break;
884 }
885
886 ptrtstr = ptrresponseheaders;
887
888 while (0 != (headerlength = gate_win32_winstr_length(ptrtstr)))
889 {
890 gate_httpclient_add_header(&response->headers, ptrtstr, headerlength);
891
892 ptrtstr += (headerlength + 1);
893 }
894 }
895
896 /* generate response stream to access downloaded data */
897
898 responseimpl = (gate_http_response_impl_t*)gate_mem_alloc(sizeof(gate_http_response_impl_t));
899 if (responseimpl == NULL)
900 {
901 ret = GATE_RESULT_OUTOFMEMORY;
902 break;
903 }
904 gate_init_http_client_response_vtbl();
905 responseimpl->vtbl = &gate_http_client_response_vtbl;
906 gate_atomic_int_init(&responseimpl->ref_counter, 1);
907 responseimpl->handle = hrequest;
908 hrequest = NULL;
909
910 /* success case */
911 response->response_stream = (gate_stream_t*)responseimpl;
912 ret = GATE_RESULT_OK;
913 } while (0);
914
915 gate_strbuilder_release(&headerbuilder);
916
917 if (hrequest != NULL)
918 {
919 WinInet.InetCloseHandle(hrequest);
920 }
921
922 if (memstream != NULL)
923 {
924 memstream->vtbl->release(memstream);
925 }
926
927 if (ptrresponseheaders != NULL)
928 {
929 gate_mem_dealloc(ptrresponseheaders);
930 }
931
932 if (GATE_FAILED(ret))
933 {
934 gate_http_response_release(response);
935 }
936
937 return ret;
938 }
939
940
941 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
942 {
943 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
944 {
945 return native_httpclient_create(client, server, port, flags);
946 }
947 else
948 {
949 return wininet_httpclient_create(client, server, port, flags);
950 }
951 }
952
953 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
954 {
955 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
956 {
957 return native_httpclient_send_request(client, request, response);
958 }
959 else
960 {
961 return wininet_httpclient_send_request(client, request, response);
962 }
963 }
964
965 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
966 {
967 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
968 {
969 return native_httpclient_release(client);
970 }
971 else
972 {
973 return wininet_httpclient_release(client);
974 }
975 }
976
977
978
979 #endif /* GATE_NET_HTTPCLIENT_WINAPI */
980
981
982
983 #if defined(GATE_NET_HTTPCLIENT_CURL)
984
985 #include "gate/net/platform/libcurl_api.h"
986 #include "gate/uris.h"
987 #include "gate/utilities.h"
988 #include "gate/debugging.h"
989
990
991 19 static gate_result_t curl_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
992 {
993 19 gate_result_t ret = GATE_RESULT_FAILED;
994
995 do
996 {
997 19 gate_mem_clear(client, sizeof(gate_httpclient_t));
998
999 19 ret = load_libcurl_functions();
1000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 GATE_BREAK_IF_FAILED(ret);
1001
1002
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (NULL == gate_string_clone(&client->server, server))
1003 {
1004 ret = GATE_RESULT_OUTOFMEMORY;
1005 break;
1006 }
1007
1008 19 client->flags = flags;
1009 19 client->port = port;
1010
1011 19 ret = GATE_RESULT_OK;
1012 } while (0);
1013
1014 19 return ret;
1015 }
1016
1017
1018 21 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1019 {
1020
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
21 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1021 {
1022 2 return native_httpclient_create(client, server, port, flags);
1023 }
1024 else
1025 {
1026 19 return curl_httpclient_create(client, server, port, flags);
1027 }
1028 }
1029
1030 static gate_result_t setup_multipart_formdata(CURL* curl_session, gate_http_request_t* request)
1031 {
1032 gate_result_t ret = GATE_RESULT_FAILED;
1033
1034 /*
1035 // multipart form data
1036 size_t boundarypos = contentType.indexOf("boundary=");
1037 if(boundarypos == StrToken::npos)
1038 {
1039 throw Exception(results::InvalidData, "Missing multipart form data boundary", FuncName);
1040 }
1041 StrToken boundary = contentType.subString(boundarypos + 9);
1042
1043
1044 parts = Http::parseMultipartMessages(strm.str(), boundary);
1045 for(partlist_t::iterator p(parts.begin()), e(parts.end()); p != e; ++p)
1046 {
1047 if(!p->Name.empty())
1048 {
1049 char const* ctype = Http::ContentType_Binary;
1050 if(!p->Type.empty())
1051 {
1052 ctype = p->Type.c_str();
1053 }
1054
1055 if(p->FileName.empty())
1056 {
1057 curl::Code.formadd(&formpost, &formlast,
1058 CURLFORM_COPYNAME, p->Name.c_str(),
1059 CURLFORM_PTRCONTENTS, p->Message.c_str(),
1060 CURLFORM_CONTENTSLENGTH, (long)p->Message.size(),
1061 CURLFORM_CONTENTTYPE, ctype,
1062 CURLFORM_END
1063 );
1064 }
1065 else
1066 {
1067 curl::Code.formadd(&formpost, &formlast,
1068 CURLFORM_COPYNAME, p->Name.c_str(),
1069 CURLFORM_BUFFERPTR, p->Message.c_str(),
1070 CURLFORM_BUFFERLENGTH, (long)p->Message.size(),
1071 CURLFORM_BUFFER, p->FileName.c_str(),
1072 CURLFORM_CONTENTTYPE, ctype,
1073 CURLFORM_END
1074 );
1075 }
1076 }
1077 }
1078
1079 result = curl::Code.easy_setopt(pcurl, CURLOPT_HTTPPOST, formpost);
1080 curl::CurlLib::check_error(result, "curl_easy_setopt(CURLOPT_HTTPPOST)", FuncName);
1081
1082 */
1083
1084 return ret;
1085 }
1086
1087 #define GATE_CURL_CHECK_ERROR(curl_code, origin) { ret = gate_curl_map_result(curl_code, &current_error_msg); GATE_DEBUG_TRACE_RESULT(ret, origin, current_error_msg, 0); GATE_BREAK_IF_FAILED(ret); }
1088
1089 1 static gate_result_t extract_stream_data(CURL* curl_session, gate_stream_t* source_stream, gate_bool_t put_upload)
1090 {
1091 1 gate_result_t ret = GATE_RESULT_FAILED;
1092 1 gate_memstream_t* source_memstream = NULL;
1093 CURLcode curl_code;
1094 1 char const* current_error_msg = NULL;
1095 1 char const* memstream_data = NULL;
1096 1 gate_size_t memstream_data_size = 0;
1097
1098 do
1099 {
1100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(put_upload)
1101 {
1102 /* PUT */
1103 curl_code = curl.easy_setopt(curl_session, CURLOPT_UPLOAD, (long)1);
1104 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[UPLOAD]");
1105
1106 curl_code = curl.easy_setopt(curl_session, CURLOPT_READDATA, (void*)source_stream);
1107 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[READDATA]");
1108
1109 curl_code = curl.easy_setopt(curl_session, CURLOPT_READFUNCTION, &gate_curl_read_stream_cb);
1110 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[READFUNCTION]");
1111 }
1112 else
1113 {
1114 /* POST */
1115
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GATE_OBJECT_IMPLEMENTS(source_stream, GATE_INTERFACE_NAME_MEMSTREAM))
1116 {
1117 source_memstream = (gate_memstream_t*)source_stream;
1118 }
1119 else
1120 {
1121 1 source_memstream = gate_memstream_create(GATE_MAX_BLOCK_COPYBUFFER_LENGTH);
1122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == source_memstream)
1123 {
1124 ret = GATE_RESULT_OUTOFMEMORY;
1125 break;
1126 }
1127 1 ret = gate_stream_transfer(source_stream, (gate_stream_t*)source_memstream);
1128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
1129 {
1130 gate_object_release(source_memstream);
1131 break;
1132 }
1133 }
1134 1 memstream_data = gate_memstream_get_data(source_memstream);
1135 1 memstream_data_size = gate_memstream_size(source_memstream);
1136
1137 1 curl_code = curl.easy_setopt(curl_session, CURLOPT_POST, (long)1);
1138
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POST]");
1139
1140 1 curl_code = curl.easy_setopt(curl_session, CURLOPT_POSTFIELDS, memstream_data);
1141
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POSTFIELDS]");
1142
1143 1 curl_code = curl.easy_setopt(curl_session, CURLOPT_POSTFIELDSIZE, (long)memstream_data_size);
1144
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POSTFIELDSIZE]");
1145 }
1146
1147 } while (0);
1148
1149
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if ((source_stream != NULL) && (source_memstream != (gate_memstream_t*)source_stream))
1150 {
1151 1 gate_object_release(source_memstream);
1152 }
1153 1 return ret;
1154 }
1155
1156 19 static gate_result_t parse_response_header(char const* data, gate_size_t data_len, gate_map_t* m, gate_uint32_t* status)
1157 {
1158 static gate_string_t const http_key = GATE_STRING_INIT_STATIC("HTTP");
1159 static gate_string_t const status_key = GATE_STRING_INIT_STATIC("Status");
1160 static gate_string_t const http_prefix = GATE_STRING_INIT_STATIC("HTTP/");
1161 19 gate_result_t ret = GATE_RESULT_OK;
1162 19 gate_string_t header = GATE_STRING_INIT_EMPTY;
1163 gate_string_t line;
1164 gate_string_t key;
1165 gate_string_t value;
1166 gate_size_t pos;
1167 19 gate_uint64_t ui64 = 0;
1168 do
1169 {
1170 19 gate_mem_clear(m, sizeof(gate_map_t));
1171
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (NULL == gate_map_create(m, &gate_compare_string,
1172 sizeof(gate_string_t), &gate_string_copy_constructor, &gate_string_destructor,
1173 sizeof(gate_string_t), &gate_string_copy_constructor, &gate_string_destructor))
1174 {
1175 ret = GATE_RESULT_OUTOFMEMORY;
1176 break;
1177 }
1178
1179
1180
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (NULL == gate_string_create(&header, data, data_len))
1181 {
1182 ret = GATE_RESULT_OUTOFMEMORY;
1183 break;
1184 }
1185
1186
2/2
✓ Branch 1 taken 166 times.
✓ Branch 2 taken 19 times.
185 while (gate_string_read_line(&line, &header, &header))
1187 {
1188 166 gate_mem_clear(&key, sizeof(key));
1189 166 gate_mem_clear(&value, sizeof(value));
1190
1191
1192
2/2
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 147 times.
166 if (gate_string_starts_with(&line, &http_prefix))
1193 {
1194 19 gate_map_add(m, &http_key, &line);
1195 19 pos = gate_string_char_pos(&line, ' ', gate_string_length(&http_key));
1196
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (GATE_STR_NPOS != pos)
1197 {
1198 19 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
1199 19 gate_string_ltrim(&value, &value);
1200 19 gate_map_add(m, &status_key, &value);
1201
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (status != NULL)
1202 {
1203 19 gate_str_parse_uint64(value.str, value.length, &ui64);
1204 19 *status = (gate_uint32_t)ui64;
1205 }
1206 }
1207 }
1208 else
1209 {
1210 147 pos = gate_string_char_pos(&line, ':', 0);
1211
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 19 times.
147 if (GATE_STR_NPOS != pos)
1212 {
1213 128 gate_string_substr(&key, &line, 0, pos);
1214 128 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
1215 128 gate_string_trim(&key, &key);
1216 128 gate_string_trim(&value, &value);
1217 128 gate_map_add(m, &key, &value);
1218 }
1219 }
1220 166 gate_string_release(&key);
1221 166 gate_string_release(&value);
1222 166 gate_string_release(&line);
1223 }
1224
1225 } while (0);
1226
1227 19 gate_string_release(&header);
1228
1229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (GATE_FAILED(ret))
1230 {
1231 gate_map_destroy(m);
1232 }
1233
1234 19 return ret;
1235 }
1236
1237 static gate_string_t const gate_uri_scheme_http = GATE_STRING_INIT_STATIC(GATE_URI_SCHEME_HTTP);
1238 static gate_string_t const gate_uri_scheme_https = GATE_STRING_INIT_STATIC(GATE_URI_SCHEME_HTTPS);
1239
1240
1241
1242 19 static gate_result_t curl_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1243 {
1244 static gate_string_t const header_entry_separator = GATE_STRING_INIT_STATIC(": ");
1245 static gate_string_t const header_entry_content_type = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTTYPE);
1246 19 gate_result_t ret = GATE_RESULT_FAILED;
1247 19 CURL* curl_session = NULL;
1248 19 gate_uri_t url = GATE_INIT_EMPTY;
1249 19 gate_string_t str_url = GATE_STRING_INIT_EMPTY;
1250 /*struct curl_httppost* form_last = NULL;*/
1251 CURLcode curl_code;
1252 19 char const* current_error_msg = NULL;
1253 char buffer[4096];
1254 gate_size_t buffer_len;
1255 19 gate_bool_t is_secure = GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE);
1256 gate_bool_t is_get_request;
1257 gate_bool_t is_post_request;
1258 gate_bool_t is_put_request;
1259 19 struct curl_slist* curl_headers = NULL;
1260 gate_map_iterator_t iter;
1261 gate_string_t const* key;
1262 gate_string_t const* value;
1263 char username[256];
1264 char password[256];
1265
1266 19 gate_memstream_t* response_header = NULL;
1267 19 gate_memstream_t* response_content = NULL;
1268
1269 do
1270 {
1271 19 gate_uri_init(&url);
1272 19 gate_http_response_init(response);
1273
1274 19 ret = load_libcurl_functions();
1275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 GATE_BREAK_IF_FAILED(ret);
1276
1277 19 curl_session = curl.easy_init();
1278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (NULL == curl_session)
1279 {
1280 ret = GATE_RESULT_NOTAVAILABLE;
1281 GATE_DEBUG_TRACE_RESULT(ret, "curl_easy_init", "Faile to get CURL context", 0);
1282 break;
1283 }
1284
1285
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
19 if (client->port == 0)
1286 {
1287
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 url.port = is_secure ? 443 : 80;
1288 }
1289 else
1290 {
1291 5 url.port = client->port;
1292 }
1293
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
19 gate_string_duplicate(&url.scheme, is_secure ? &gate_uri_scheme_https : &gate_uri_scheme_http);
1294
1295
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (!gate_string_is_empty(&request->version))
1296 {
1297 long http_version = CURL_HTTP_VERSION_NONE;
1298 if (gate_string_equals_str(&request->version, "1.0")) { http_version = CURL_HTTP_VERSION_1_0; }
1299 else if (gate_string_equals_str(&request->version, "1.1")) { http_version = CURL_HTTP_VERSION_1_1; }
1300 else if (gate_string_equals_str(&request->version, "2.0")) { http_version = CURL_HTTP_VERSION_2_0; }
1301 else if (gate_string_equals_str(&request->version, "3.0")) { http_version = CURL_HTTP_VERSION_3; }
1302
1303 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTP_VERSION, http_version);
1304 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTP_VERSION]");
1305 }
1306
1307 {
1308 19 long followlocation = 1;
1309 19 long max_redirects = 8;
1310
1311 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_FOLLOWLOCATION, followlocation);
1312
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[FOLLOWLOCATION]");
1313
1314 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_MAXREDIRS, max_redirects);
1315
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[MAXREDIRS]");
1316 }
1317
1318
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (!gate_string_is_empty(&request->auth_user))
1319 {
1320 struct curl_version_info_data* version = curl.version_info(CURLVERSION_NOW);
1321 unsigned long auth_bitmask = CURLAUTH_BASIC | CURLAUTH_DIGEST;
1322 /* NOTICE: if we request auth-types not included by libcurl,
1323 we end with useless 401-responses */
1324 if(version->features & CURL_VERSION_SSPI) auth_bitmask |= CURLAUTH_DIGEST | CURLAUTH_NEGOTIATE | CURLAUTH_NTLM;
1325 if(version->features & CURL_VERSION_NTLM) auth_bitmask |= CURLAUTH_NTLM;
1326 if(version->features & CURL_VERSION_GSSNEGOTIATE) auth_bitmask |= CURLAUTH_NEGOTIATE;
1327
1328 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTPAUTH, auth_bitmask);
1329 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTPAUTH]");
1330 curl_code = curl.easy_setopt(curl_session, CURLOPT_UNRESTRICTED_AUTH, 1L);
1331 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[UNRESTRICTED_AUTH]");
1332
1333 #if 1
1334 gate_string_to_buffer(&request->auth_user, username, sizeof(username));
1335 gate_string_to_buffer(&request->auth_pass, password, sizeof(password));
1336 curl_code = curl.easy_setopt(curl_session, CURLOPT_USERNAME, username);
1337 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[USERNAME]");
1338 curl_code = curl.easy_setopt(curl_session, CURLOPT_PASSWORD, password);
1339 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PASSWORD]");
1340 #else
1341 ret = gate_uri_build_user_info(&request->auth_user, &request->auth_pass, &url.user_info);
1342 GATE_BREAK_IF_FAILED(ret);
1343 #endif
1344 }
1345
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (NULL == gate_string_clone(&url.host, &client->server))
1346 {
1347 ret = GATE_RESULT_OUTOFMEMORY;
1348 break;
1349 }
1350
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (NULL == gate_string_clone(&url.absolute_path, &request->path))
1351 {
1352 ret = GATE_RESULT_OUTOFMEMORY;
1353 break;
1354 }
1355
1356 19 ret = gate_uri_to_string(&url, &str_url, false);
1357
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 GATE_BREAK_IF_FAILED(ret);
1358
1359 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_URL, str_url.str);
1360
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[URL]");
1361
1362
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (!gate_string_is_empty(&request->proxy_server))
1363 {
1364 GATE_STRING_TO_BUFFER(&request->proxy_server, buffer);
1365 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXY, buffer);
1366 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXY]");
1367
1368 if (request->proxy_port)
1369 {
1370 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYPORT, (long)request->proxy_port);
1371 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYPORT]");
1372 }
1373
1374 if (!gate_string_is_empty(&request->proxy_user))
1375 {
1376 GATE_STRING_TO_BUFFER(&request->proxy_user, buffer);
1377 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYUSERNAME, buffer);
1378 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYUSERNAME]");
1379 }
1380 if (!gate_string_is_empty(&request->proxy_pass))
1381 {
1382 GATE_STRING_TO_BUFFER(&request->proxy_pass, buffer);
1383 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYPORT, buffer);
1384 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYPORT]");
1385 }
1386 }
1387
1388
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
19 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_DISABLE_VERIFICATION))
1389 {
1390 14 curl_code = curl.easy_setopt(curl_session, CURLOPT_SSL_VERIFYHOST, 0L);
1391
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[SSL_VERIFYHOST]");
1392
1393 14 curl_code = curl.easy_setopt(curl_session, CURLOPT_SSL_VERIFYPEER, 0L);
1394
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[SSL_VERIFYPEER]");
1395 }
1396
1397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (request->connect_timeout_ms)
1398 {
1399 curl_code = curl.easy_setopt(curl_session, CURLOPT_CONNECTTIMEOUT_MS, (long)request->connect_timeout_ms);
1400 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CONNECTTIMEOUT_MS]");
1401 }
1402
1403
3/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 if (request->connect_timeout_ms || request->send_timeout_ms || request->receive_timeout_ms)
1404 {
1405 curl_code = curl.easy_setopt(curl_session, CURLOPT_TIMEOUT_MS,
1406 (long)(request->connect_timeout_ms + request->send_timeout_ms + request->receive_timeout_ms));
1407 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[TIMEOUT_MS]");
1408 }
1409
1410
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
19 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE))
1411 {
1412 14 curl_code = curl.easy_setopt(curl_session, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTPS);
1413
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[REDIR_PROTOCOLS]");
1414 }
1415 else
1416 {
1417 5 curl_code = curl.easy_setopt(curl_session, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTP);
1418
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[REDIR_PROTOCOLS]");
1419 }
1420
1421 19 is_get_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_GET);
1422 19 is_post_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_POST);
1423 19 is_put_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_PUT);
1424
1425
2/2
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 19 times.
24 for (iter = gate_map_first(&request->headers); gate_map_iterator_valid(iter); iter = gate_map_iterator_next(iter))
1426 {
1427 5 key = (gate_string_t const*)gate_map_iterator_key(iter);
1428 5 value = (gate_string_t const*)gate_map_iterator_value(iter);
1429
1430
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 if (is_post_request || is_put_request)
1431 {
1432 /* exclude non-supported custom fields in POST mode */
1433
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_string_equals_str(key, GATE_HTTP_HEADER_CONTENTLENGTH)) continue;
1434 }
1435 5 buffer_len = gate_string_to_buffer(key, buffer, sizeof(buffer));
1436 5 buffer_len += gate_string_to_buffer(&header_entry_separator, &buffer[buffer_len], sizeof(buffer) - buffer_len);
1437 5 buffer_len += gate_string_to_buffer(value, &buffer[buffer_len], sizeof(buffer) - buffer_len);
1438
1439 5 curl_headers = curl.slist_append(curl_headers, buffer);
1440 }
1441
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 14 times.
19 if (curl_headers != NULL)
1442 {
1443 5 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTPHEADER, curl_headers);
1444
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTPHEADER]");
1445 }
1446
1447
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
19 if (request->upload_stream == NULL)
1448 {
1449
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
18 if (!is_get_request && !is_post_request && !is_put_request)
1450 {
1451 gate_string_to_buffer(&request->method, buffer, sizeof(buffer));
1452 curl_code = curl.easy_setopt(curl_session, CURLOPT_CUSTOMREQUEST, buffer);
1453 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CUSTOMREQUEST]");
1454 }
1455 }
1456 else
1457 {
1458 1 value = (gate_string_t const*)gate_map_get_value(&request->headers, &header_entry_content_type);
1459
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (is_post_request)
1460 {
1461
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 if (value && gate_string_starts_with_str(value, GATE_HTTP_HEADERVALUE_CONTENTTYPE_MULTIPARTFORMDATA))
1462 {
1463 ret = setup_multipart_formdata(curl_session, request);
1464 GATE_BREAK_IF_FAILED(ret);
1465 }
1466 else
1467 {
1468 1 ret = extract_stream_data(curl_session, request->upload_stream, false);
1469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1470 }
1471 }
1472 else
1473 {
1474 ret = extract_stream_data(curl_session, request->upload_stream, is_put_request);
1475 GATE_BREAK_IF_FAILED(ret);
1476
1477 gate_string_to_buffer(&request->method, buffer, sizeof(buffer));
1478 curl_code = curl.easy_setopt(curl_session, CURLOPT_CUSTOMREQUEST, buffer);
1479 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CUSTOMREQUEST]");
1480 }
1481 }
1482
1483
1484
1485 19 response_header = gate_memstream_create(GATE_MAX_COPYBUFFER_LENGTH);
1486
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (NULL == response_header)
1487 {
1488 ret = GATE_RESULT_OUTOFMEMORY;
1489 break;
1490 }
1491 19 response_content = gate_memstream_create(GATE_MAX_BLOCK_COPYBUFFER_LENGTH);
1492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (NULL == response_content)
1493 {
1494 ret = GATE_RESULT_OUTOFMEMORY;
1495 break;
1496 }
1497
1498 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEDATA, response_content);
1499
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[WRITEDATA]");
1500
1501 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEFUNCTION, &gate_curl_write_stream_cb);
1502
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[WRITEFUNCTION]");
1503
1504 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEHEADER, response_header);
1505
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[WRITEHEADER]");
1506
1507 19 curl_code = curl.easy_setopt(curl_session, CURLOPT_HEADERFUNCTION, gate_curl_write_stream_cb);
1508
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HEADERFUNCTION]");
1509
1510
1511 19 curl_code = curl.easy_perform(curl_session);
1512
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_perform");
1513
1514 /* HTTP transmission completed -> fill response object */
1515 19 ret = parse_response_header(gate_memstream_get_data(response_header),
1516 19 gate_memstream_size(response_header),
1517 &response->headers, &response->status_code);
1518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 GATE_BREAK_IF_FAILED(ret);
1519
1520 19 response->response_stream = (gate_stream_t*)response_content;
1521 19 response_content = NULL;
1522
1523 19 ret = GATE_RESULT_OK;
1524
1525 } while (0);
1526
1527
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (curl_session)
1528 {
1529 19 curl.easy_cleanup(curl_session);
1530 19 curl_session = NULL;
1531 }
1532
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 14 times.
19 if (curl_headers != NULL)
1533 {
1534 5 curl.slist_free_all(curl_headers);
1535 }
1536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (response_content != NULL)
1537 {
1538 gate_object_release(response_content);
1539 }
1540
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (response_header != NULL)
1541 {
1542 19 gate_object_release(response_header);
1543 }
1544
1545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (GATE_FAILED(ret))
1546 {
1547 gate_http_response_release(response);
1548 }
1549
1550 19 gate_string_release(&str_url);
1551 19 gate_uri_destroy(&url);
1552 19 return ret;
1553 }
1554
1555 21 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1556 {
1557
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
21 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1558 {
1559 2 return native_httpclient_send_request(client, request, response);
1560 }
1561 else
1562 {
1563 19 return curl_httpclient_send_request(client, request, response);
1564 }
1565 }
1566
1567
1568 19 static gate_result_t curl_httpclient_release(gate_httpclient_t* client)
1569 {
1570 19 gate_string_release(&client->server);
1571 19 gate_mem_clear(client, sizeof(gate_httpclient_t));
1572 19 return GATE_RESULT_OK;
1573 }
1574
1575 21 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1576 {
1577
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
21 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1578 {
1579 2 return native_httpclient_release(client);
1580 }
1581 else
1582 {
1583 19 return curl_httpclient_release(client);
1584 }
1585 }
1586
1587 #endif /* GATE_NET_HTTPCLIENT_CURL */
1588
1589
1590 #if defined(GATE_NET_HTTPCLIENT_WASM_IMPL)
1591
1592 #include <emscripten/fetch.h>
1593 #include <emscripten/wasm_worker.h>
1594
1595 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1596 {
1597 gate_mem_clear(client, sizeof(gate_httpclient_t));
1598
1599 if (NULL == gate_string_clone(&client->server, server))
1600 {
1601 return GATE_RESULT_OUTOFMEMORY;
1602 }
1603 client->port = port;
1604 client->flags = flags;
1605 return GATE_RESULT_OK;
1606 }
1607
1608 static void httpclient_on_success(emscripten_fetch_t* fetch)
1609 {
1610 emscripten_semaphore_t* ptr_semaphore = (emscripten_semaphore_t*)fetch->userData;
1611 emscripten_semaphore_release(ptr_semaphore, 1);
1612 }
1613
1614 static void httpclient_on_error(emscripten_fetch_t* fetch)
1615 {
1616 emscripten_semaphore_t* ptr_semaphore = (emscripten_semaphore_t*)fetch->userData;
1617 emscripten_semaphore_release(ptr_semaphore, 1);
1618 }
1619
1620 #define HTTPCLIENT_MAX_HEADERS 32
1621 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1622 {
1623 gate_result_t ret = GATE_RESULT_FAILED;
1624 emscripten_fetch_t* ptr_fetch = NULL;
1625 gate_string_t header_strings[HTTPCLIENT_MAX_HEADERS * 2] = GATE_INIT_EMPTY;
1626 gate_size_t header_strings_count = 0;
1627 char const* headers[HTTPCLIENT_MAX_HEADERS * 2 + 1];
1628 gate_enumerator_t e = GATE_INIT_EMPTY;
1629
1630 gate_map_enumerate(&request->headers, &e);
1631 while (gate_enumerator_valid(&e) && (header_strings_count < HTTPCLIENT_MAX_HEADERS * 2))
1632 {
1633 gate_mapping_t const* ptr_map_entry = (gate_mapping_t const*)gate_enumerator_get(&e);
1634 gate_string_t const* ptr_str = (gate_string_t const*)ptr_map_entry->key;
1635 if (ptr_str->length > 0)
1636 {
1637 if (ptr_str->str[ptr_str->length] == 0)
1638 {
1639 gate_string_clone(&header_strings[header_strings_count], ptr_str);
1640 }
1641 else
1642 {
1643 gate_string_create_copy(&header_strings[header_strings_count], ptr_str);
1644 }
1645 }
1646 headers[header_strings_count] = gate_string_ptr(&header_strings[header_strings_count], 0);
1647 ++header_strings_count;
1648
1649 ptr_str = (gate_string_t const*)ptr_map_entry->value;
1650 if (ptr_str->length > 0)
1651 {
1652 if (ptr_str->str[ptr_str->length] == 0)
1653 {
1654 gate_string_clone(&header_strings[header_strings_count], ptr_str);
1655 }
1656 else
1657 {
1658 gate_string_create_copy(&header_strings[header_strings_count], ptr_str);
1659 }
1660 }
1661 headers[header_strings_count] = gate_string_ptr(&header_strings[header_strings_count], 0);
1662 ++header_strings_count;
1663
1664 gate_enumerator_next(&e);
1665 }
1666 headers[header_strings_count] = NULL;
1667
1668 do
1669 {
1670 char url_buffer[2048];
1671 gate_strbuilder_t builder;
1672 emscripten_semaphore_t sem_completed = EMSCRIPTEN_SEMAPHORE_T_STATIC_INITIALIZER(0);
1673 emscripten_fetch_attr_t attr;
1674
1675 gate_strbuilder_create_static(&builder, url_buffer, sizeof(url_buffer), 0);
1676 gate_strbuilder_append_cstr(&builder, "http://");
1677 gate_strbuilder_append_string(&builder, &client->server);
1678 gate_strbuilder_append_cstr(&builder, ":");
1679 gate_strbuilder_append_uint32(&builder, client->port);
1680 gate_strbuilder_append_string(&builder, &request->path);
1681
1682 emscripten_fetch_attr_init(&attr);
1683 gate_str_print_text(attr.requestMethod, sizeof(attr.requestMethod),
1684 gate_string_ptr(&request->method, 0),
1685 gate_string_length(&request->method));
1686 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
1687 attr.requestHeaders = headers;
1688 attr.onsuccess = &httpclient_on_success;
1689 attr.onerror = &httpclient_on_error;
1690 attr.userData = (emscripten_semaphore_t*)&sem_completed;
1691 ptr_fetch = emscripten_fetch(&attr, url_buffer);
1692
1693 emscripten_semaphore_waitinf_acquire(&sem_completed, 1);
1694
1695 if (response)
1696 {
1697 gate_memstream_t* response_stream;
1698 gate_http_response_init(response);
1699 response_stream = gate_memstream_create(ptr_fetch->numBytes);
1700 gate_stream_write_block((gate_stream_t*)response_stream, ptr_fetch->data, ptr_fetch->numBytes, NULL);
1701 response->status_code = ptr_fetch->status;
1702 response->response_stream = (gate_stream_t*)response_stream;
1703 }
1704 ret = GATE_RESULT_OK;
1705 } while (0);
1706
1707 if (ptr_fetch)
1708 {
1709 emscripten_fetch_close(ptr_fetch);
1710 }
1711
1712 while (header_strings_count > 0)
1713 {
1714 --header_strings_count;
1715 gate_string_release(&header_strings[header_strings_count]);
1716 }
1717
1718 return ret;
1719 }
1720 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1721 {
1722 GATE_UNUSED_ARG(client);
1723 return GATE_RESULT_NOTIMPLEMENTED;
1724 }
1725
1726 #endif /* GATE_NET_HTTPCLIENT_WASM_IMPL */
1727
1728
1729 #if defined(GATE_NET_HTTPCLIENT_NO_IMPL)
1730
1731 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1732 {
1733 GATE_UNUSED_ARG(client);
1734 GATE_UNUSED_ARG(server);
1735 GATE_UNUSED_ARG(port);
1736 GATE_UNUSED_ARG(flags);
1737 return GATE_RESULT_NOTIMPLEMENTED;
1738 }
1739 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1740 {
1741 GATE_UNUSED_ARG(client);
1742 GATE_UNUSED_ARG(request);
1743 GATE_UNUSED_ARG(response);
1744 return GATE_RESULT_NOTIMPLEMENTED;
1745 }
1746 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1747 {
1748 GATE_UNUSED_ARG(client);
1749 return GATE_RESULT_NOTIMPLEMENTED;
1750 }
1751
1752 #endif /* GATE_NET_HTTPCLIENT_NO_IMPL */
1753