GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/httpclients.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 278 434 64.1%
Functions: 13 15 86.7%
Branches: 127 320 39.7%

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