GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/httpclients.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 284 463 61.3%
Functions: 13 15 86.7%
Branches: 131 374 35.0%

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 132 received = 0;
272 134 result = gate_stream_read(strm, &buffer[0], 1, &received);
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 if (GATE_FAILED(result))
274 {
275 break;
276 }
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 if (received == 0)
278 {
279 result = GATE_RESULT_ENDOFSTREAM;
280 break;
281 }
282 134 gate_strbuilder_append_text(&builder, &buffer[0], 1);
283 134 received = gate_strbuilder_length(&builder);
284
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 6 times.
134 if (received >= 4)
285 {
286 128 ptr_content = gate_strbuilder_ptr(&builder, received - 4);
287
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)
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 8 times.
✓ Branch 2 taken 2 times.
10 while (NULL != gate_string_read_line(&line, &header, &header))
324 {
325
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 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 6 gate_string_trim(&line, &line);
340
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 if (!gate_string_is_empty(&line))
341 {
342 4 pos = gate_string_char_pos(&line, ':', 0);
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (pos == GATE_STR_NPOS)
344 {
345 ret = GATE_RESULT_INVALIDCONTENT;
346 break;
347 }
348 4 gate_string_substr(&key, &line, 0, pos);
349 4 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
350 4 gate_string_trim(&value, &value);
351
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (!gate_string_is_empty(&key))
352 {
353 4 gate_map_add(&response->headers, &key, &value);
354 }
355 4 gate_string_release(&key);
356 4 gate_string_release(&value);
357 }
358 }
359 8 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 }
919
920 /* HTTP request succeeded -> receive HTTP response */
921
922 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusCodeSize, 0))
923 {
924 /* no status code, maybe socket was closed ... */
925 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
926 ret = GATE_RESULT_INVALIDINPUT;
927 break;
928 }
929
930 response->status_code = (gate_uint32_t)dwStatusCode;
931
932 dwHeaderSize = 0;
933 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_RAW_HEADERS, NULL, &dwHeaderSize, NULL))
934 {
935 senderror = gate_win32_getlasterror();
936 if (ERROR_INSUFFICIENT_BUFFER != senderror)
937 {
938 /* cannot receive header size */
939 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
940 ret = GATE_RESULT_INVALIDHEADER;
941 break;
942 }
943 }
944
945 if (dwHeaderSize != 0)
946 {
947 /* attach response headers to response object */
948 if (NULL == gate_util_stringmap_create(&response->headers))
949 {
950 ret = GATE_RESULT_OUTOFMEMORY;
951 break;
952 }
953
954 dwHeaderSize += 32; /* add some extra space */
955 ptrresponseheaders = (LPTSTR)gate_mem_alloc(dwHeaderSize * sizeof(TCHAR));
956 if (NULL == ptrresponseheaders)
957 {
958 ret = GATE_RESULT_OUTOFMEMORY;
959 break;
960 }
961
962 if (FALSE == WinInet.HTTPQueryInfo(hrequest, HTTP_QUERY_RAW_HEADERS, ptrresponseheaders, &dwHeaderSize, NULL))
963 {
964 GATE_DEBUG_TRACE("HttpQueryInfo() failed");
965 ret = GATE_RESULT_INVALIDHEADER;
966 break;
967 }
968
969 ptrtstr = ptrresponseheaders;
970
971 while (0 != (headerlength = gate_win32_winstr_length(ptrtstr)))
972 {
973 gate_httpclient_add_header(&response->headers, ptrtstr, headerlength);
974
975 ptrtstr += (headerlength + 1);
976 }
977 }
978
979 /* generate response stream to access downloaded data */
980
981 responseimpl = (gate_http_response_impl_t*)gate_mem_alloc(sizeof(gate_http_response_impl_t));
982 if (responseimpl == NULL)
983 {
984 ret = GATE_RESULT_OUTOFMEMORY;
985 break;
986 }
987 gate_init_http_client_response_vtbl();
988 responseimpl->vtbl = &gate_http_client_response_vtbl;
989 gate_atomic_int_init(&responseimpl->ref_counter, 1);
990 responseimpl->handle = hrequest;
991 hrequest = NULL;
992
993 /* success case */
994 response->response_stream = (gate_stream_t*)responseimpl;
995 ret = GATE_RESULT_OK;
996 } while (0);
997
998 gate_strbuilder_release(&headerbuilder);
999
1000 if (hrequest != NULL)
1001 {
1002 WinInet.InetCloseHandle(hrequest);
1003 }
1004
1005 if (memstream != NULL)
1006 {
1007 memstream->vtbl->release(memstream);
1008 }
1009
1010 if (ptrresponseheaders != NULL)
1011 {
1012 gate_mem_dealloc(ptrresponseheaders);
1013 }
1014
1015 if (GATE_FAILED(ret))
1016 {
1017 gate_http_response_release(response);
1018 }
1019
1020 return ret;
1021 }
1022
1023
1024 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1025 {
1026 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1027 {
1028 return native_httpclient_create(client, server, port, flags);
1029 }
1030 else
1031 {
1032 return wininet_httpclient_create(client, server, port, flags);
1033 }
1034 }
1035
1036 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1037 {
1038 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1039 {
1040 return native_httpclient_send_request(client, request, response);
1041 }
1042 else
1043 {
1044 return wininet_httpclient_send_request(client, request, response);
1045 }
1046 }
1047
1048 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1049 {
1050 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1051 {
1052 return native_httpclient_release(client);
1053 }
1054 else
1055 {
1056 return wininet_httpclient_release(client);
1057 }
1058 }
1059
1060
1061
1062 #endif /* GATE_NET_HTTPCLIENT_WINAPI */
1063
1064
1065
1066 #if defined(GATE_NET_HTTPCLIENT_CURL)
1067
1068 #include "gate/net/platform/libcurl_api.h"
1069 #include "gate/uris.h"
1070 #include "gate/utilities.h"
1071 #include "gate/debugging.h"
1072
1073
1074 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)
1075 {
1076 4 gate_result_t ret = GATE_RESULT_FAILED;
1077
1078 do
1079 {
1080 4 gate_mem_clear(client, sizeof(gate_httpclient_t));
1081
1082 4 ret = load_libcurl_functions();
1083
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
1084
1085
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_string_clone(&client->server, server))
1086 {
1087 ret = GATE_RESULT_OUTOFMEMORY;
1088 break;
1089 }
1090
1091 4 client->flags = flags;
1092 4 client->port = port;
1093
1094 4 ret = GATE_RESULT_OK;
1095 } while (0);
1096
1097 4 return ret;
1098 }
1099
1100
1101 6 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1102 {
1103
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (GATE_FLAG_ENABLED(flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1104 {
1105 2 return native_httpclient_create(client, server, port, flags);
1106 }
1107 else
1108 {
1109 4 return curl_httpclient_create(client, server, port, flags);
1110 }
1111 }
1112
1113 static gate_result_t setup_multipart_formdata(CURL* curl_session, gate_http_request_t* request)
1114 {
1115 gate_result_t ret = GATE_RESULT_FAILED;
1116
1117 /*
1118 // multipart form data
1119 size_t boundarypos = contentType.indexOf("boundary=");
1120 if(boundarypos == StrToken::npos)
1121 {
1122 throw Exception(results::InvalidData, "Missing multipart form data boundary", FuncName);
1123 }
1124 StrToken boundary = contentType.subString(boundarypos + 9);
1125
1126
1127 parts = Http::parseMultipartMessages(strm.str(), boundary);
1128 for(partlist_t::iterator p(parts.begin()), e(parts.end()); p != e; ++p)
1129 {
1130 if(!p->Name.empty())
1131 {
1132 char const* ctype = Http::ContentType_Binary;
1133 if(!p->Type.empty())
1134 {
1135 ctype = p->Type.c_str();
1136 }
1137
1138 if(p->FileName.empty())
1139 {
1140 curl::Code.formadd(&formpost, &formlast,
1141 CURLFORM_COPYNAME, p->Name.c_str(),
1142 CURLFORM_PTRCONTENTS, p->Message.c_str(),
1143 CURLFORM_CONTENTSLENGTH, (long)p->Message.size(),
1144 CURLFORM_CONTENTTYPE, ctype,
1145 CURLFORM_END
1146 );
1147 }
1148 else
1149 {
1150 curl::Code.formadd(&formpost, &formlast,
1151 CURLFORM_COPYNAME, p->Name.c_str(),
1152 CURLFORM_BUFFERPTR, p->Message.c_str(),
1153 CURLFORM_BUFFERLENGTH, (long)p->Message.size(),
1154 CURLFORM_BUFFER, p->FileName.c_str(),
1155 CURLFORM_CONTENTTYPE, ctype,
1156 CURLFORM_END
1157 );
1158 }
1159 }
1160 }
1161
1162 result = curl::Code.easy_setopt(pcurl, CURLOPT_HTTPPOST, formpost);
1163 curl::CurlLib::check_error(result, "curl_easy_setopt(CURLOPT_HTTPPOST)", FuncName);
1164
1165 */
1166
1167 return ret;
1168 }
1169
1170 #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); }
1171
1172 static gate_result_t extract_stream_data(CURL* curl_session, gate_stream_t* source_stream, gate_bool_t put_upload)
1173 {
1174 gate_result_t ret = GATE_RESULT_FAILED;
1175 gate_memstream_t* source_memstream = NULL;
1176 CURLcode curl_code;
1177 char const* current_error_msg = NULL;
1178 char const* memstream_data = NULL;
1179 gate_size_t memstream_data_size = 0;
1180
1181 do
1182 {
1183 if(put_upload)
1184 {
1185 /* PUT */
1186 curl_code = curl.easy_setopt(curl_session, CURLOPT_UPLOAD, (long)1);
1187 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[UPLOAD]");
1188
1189 curl_code = curl.easy_setopt(curl_session, CURLOPT_READDATA, (void*)source_stream);
1190 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[READDATA]");
1191
1192 curl_code = curl.easy_setopt(curl_session, CURLOPT_READFUNCTION, &gate_curl_read_stream_cb);
1193 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[READFUNCTION]");
1194 }
1195 else
1196 {
1197 /* POST */
1198 if (GATE_OBJECT_IMPLEMENTS(source_stream, GATE_INTERFACE_NAME_MEMSTREAM))
1199 {
1200 source_memstream = (gate_memstream_t*)source_stream;
1201 }
1202 else
1203 {
1204 source_memstream = gate_memstream_create(GATE_MAX_BLOCK_COPYBUFFER_LENGTH);
1205 if (NULL == source_memstream)
1206 {
1207 ret = GATE_RESULT_OUTOFMEMORY;
1208 break;
1209 }
1210 ret = gate_stream_transfer(source_stream, (gate_stream_t*)source_memstream);
1211 if (GATE_FAILED(ret))
1212 {
1213 gate_object_release(source_memstream);
1214 break;
1215 }
1216 }
1217 memstream_data = gate_memstream_get_data(source_memstream);
1218 memstream_data_size = gate_memstream_size(source_memstream);
1219
1220 curl_code = curl.easy_setopt(curl_session, CURLOPT_POST, (long)1);
1221 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POST]");
1222
1223 curl_code = curl.easy_setopt(curl_session, CURLOPT_POSTFIELDS, memstream_data);
1224 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POSTFIELDS]");
1225
1226 curl_code = curl.easy_setopt(curl_session, CURLOPT_POSTFIELDSIZE, (long)memstream_data_size);
1227 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[POSTFIELDSIZE]");
1228 }
1229
1230 } while (0);
1231
1232 if ((source_stream != NULL) && (source_memstream != (gate_memstream_t*)source_stream))
1233 {
1234 gate_object_release(source_memstream);
1235 }
1236 return ret;
1237 }
1238
1239 4 static gate_result_t parse_response_header(char const* data, gate_size_t data_len, gate_map_t* m, gate_uint32_t* status)
1240 {
1241 static gate_string_t const http_key = GATE_STRING_INIT_STATIC("HTTP");
1242 static gate_string_t const status_key = GATE_STRING_INIT_STATIC("Status");
1243 static gate_string_t const http_prefix = GATE_STRING_INIT_STATIC("HTTP/");
1244 4 gate_result_t ret = GATE_RESULT_OK;
1245 4 gate_string_t header = GATE_STRING_INIT_EMPTY;
1246 gate_string_t line;
1247 gate_string_t key;
1248 gate_string_t value;
1249 gate_size_t pos;
1250 4 gate_uint64_t ui64 = 0;
1251 do
1252 {
1253 4 gate_mem_clear(m, sizeof(gate_map_t));
1254
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_map_create(m, &gate_compare_string,
1255 sizeof(gate_string_t), &gate_string_copy_constructor, &gate_string_destructor,
1256 sizeof(gate_string_t), &gate_string_copy_constructor, &gate_string_destructor))
1257 {
1258 ret = GATE_RESULT_OUTOFMEMORY;
1259 break;
1260 }
1261
1262
1263
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_string_create(&header, data, data_len))
1264 {
1265 ret = GATE_RESULT_OUTOFMEMORY;
1266 break;
1267 }
1268
1269
2/2
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 4 times.
59 while (gate_string_read_line(&line, &header, &header))
1270 {
1271 55 gate_mem_clear(&key, sizeof(key));
1272 55 gate_mem_clear(&value, sizeof(value));
1273
1274
1275
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 51 times.
55 if (gate_string_starts_with(&line, &http_prefix))
1276 {
1277 4 gate_map_add(m, &http_key, &line);
1278 4 pos = gate_string_char_pos(&line, ' ', gate_string_length(&http_key));
1279
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (GATE_STR_NPOS != pos)
1280 {
1281 4 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
1282 4 gate_string_ltrim(&value, &value);
1283 4 gate_map_add(m, &status_key, &value);
1284
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (status != NULL)
1285 {
1286 4 gate_str_parse_uint64(value.str, value.length, &ui64);
1287 4 *status = (gate_uint32_t)ui64;
1288 }
1289 }
1290 }
1291 else
1292 {
1293 51 pos = gate_string_char_pos(&line, ':', 0);
1294
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 4 times.
51 if (GATE_STR_NPOS != pos)
1295 {
1296 47 gate_string_substr(&key, &line, 0, pos);
1297 47 gate_string_substr(&value, &line, pos + 1, GATE_STR_NPOS);
1298 47 gate_string_trim(&key, &key);
1299 47 gate_string_trim(&value, &value);
1300 47 gate_map_add(m, &key, &value);
1301 }
1302 }
1303 55 gate_string_release(&key);
1304 55 gate_string_release(&value);
1305 55 gate_string_release(&line);
1306 }
1307
1308 } while (0);
1309
1310 4 gate_string_release(&header);
1311
1312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (GATE_FAILED(ret))
1313 {
1314 gate_map_destroy(m);
1315 }
1316
1317 4 return ret;
1318 }
1319
1320 static gate_string_t const gate_uri_scheme_http = GATE_STRING_INIT_STATIC(GATE_URI_SCHEME_HTTP);
1321 static gate_string_t const gate_uri_scheme_https = GATE_STRING_INIT_STATIC(GATE_URI_SCHEME_HTTPS);
1322
1323
1324
1325 4 static gate_result_t curl_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1326 {
1327 static gate_string_t const header_entry_separator = GATE_STRING_INIT_STATIC(": ");
1328 static gate_string_t const header_entry_content_type = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTTYPE);
1329 4 gate_result_t ret = GATE_RESULT_FAILED;
1330 4 CURL* curl_session = NULL;
1331 4 gate_uri_t url = GATE_INIT_EMPTY;
1332 4 gate_string_t str_url = GATE_STRING_INIT_EMPTY;
1333 /*struct curl_httppost* form_last = NULL;*/
1334 CURLcode curl_code;
1335 4 char const* current_error_msg = NULL;
1336 char buffer[4096];
1337 gate_size_t buffer_len;
1338 4 gate_bool_t is_secure = GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE);
1339 gate_bool_t is_get_request;
1340 gate_bool_t is_post_request;
1341 gate_bool_t is_put_request;
1342 4 struct curl_slist* curl_headers = NULL;
1343 gate_map_iterator_t iter;
1344 gate_string_t const* key;
1345 gate_string_t const* value;
1346 char username[256];
1347 char password[256];
1348
1349 4 gate_memstream_t* response_header = NULL;
1350 4 gate_memstream_t* response_content = NULL;
1351
1352 do
1353 {
1354 4 gate_uri_init(&url);
1355 4 gate_http_response_init(response);
1356
1357 4 ret = load_libcurl_functions();
1358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
1359
1360 4 curl_session = curl.easy_init();
1361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (NULL == curl_session)
1362 {
1363 ret = GATE_RESULT_NOTAVAILABLE;
1364 GATE_DEBUG_TRACE_RESULT(ret, "curl_easy_init", "Faile to get CURL context", 0);
1365 break;
1366 }
1367
1368
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (client->port == 0)
1369 {
1370
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 url.port = is_secure ? 443 : 80;
1371 }
1372 else
1373 {
1374 1 url.port = client->port;
1375 }
1376
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);
1377
1378
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_string_is_empty(&request->version))
1379 {
1380 long http_version = CURL_HTTP_VERSION_NONE;
1381 if (gate_string_equals_str(&request->version, "1.0")) { http_version = CURL_HTTP_VERSION_1_0; }
1382 else if (gate_string_equals_str(&request->version, "1.1")) { http_version = CURL_HTTP_VERSION_1_1; }
1383 else if (gate_string_equals_str(&request->version, "2.0")) { http_version = CURL_HTTP_VERSION_2_0; }
1384 else if (gate_string_equals_str(&request->version, "3.0")) { http_version = CURL_HTTP_VERSION_3; }
1385
1386 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTP_VERSION, http_version);
1387 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTP_VERSION]");
1388 }
1389
1390 {
1391 4 long followlocation = 1;
1392 4 long max_redirects = 8;
1393
1394 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_FOLLOWLOCATION, followlocation);
1395
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[FOLLOWLOCATION]");
1396
1397 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_MAXREDIRS, max_redirects);
1398
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[MAXREDIRS]");
1399 }
1400
1401
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_string_is_empty(&request->auth_user))
1402 {
1403 struct curl_version_info_data* version = curl.version_info(CURLVERSION_NOW);
1404 unsigned long auth_bitmask = CURLAUTH_BASIC | CURLAUTH_DIGEST;
1405 /* NOTICE: if we request auth-types not included by libcurl,
1406 we end with useless 401-responses */
1407 if(version->features & CURL_VERSION_SSPI) auth_bitmask |= CURLAUTH_DIGEST | CURLAUTH_NEGOTIATE | CURLAUTH_NTLM;
1408 if(version->features & CURL_VERSION_NTLM) auth_bitmask |= CURLAUTH_NTLM;
1409 if(version->features & CURL_VERSION_GSSNEGOTIATE) auth_bitmask |= CURLAUTH_NEGOTIATE;
1410
1411 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTPAUTH, auth_bitmask);
1412 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTPAUTH]");
1413 curl_code = curl.easy_setopt(curl_session, CURLOPT_UNRESTRICTED_AUTH, 1L);
1414 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[UNRESTRICTED_AUTH]");
1415
1416 #if 1
1417 gate_string_to_buffer(&request->auth_user, username, sizeof(username));
1418 gate_string_to_buffer(&request->auth_pass, password, sizeof(password));
1419 curl_code = curl.easy_setopt(curl_session, CURLOPT_USERNAME, username);
1420 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[USERNAME]");
1421 curl_code = curl.easy_setopt(curl_session, CURLOPT_PASSWORD, password);
1422 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PASSWORD]");
1423 #else
1424 ret = gate_uri_build_user_info(&request->auth_user, &request->auth_pass, &url.user_info);
1425 GATE_BREAK_IF_FAILED(ret);
1426 #endif
1427 }
1428
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_string_clone(&url.host, &client->server))
1429 {
1430 ret = GATE_RESULT_OUTOFMEMORY;
1431 break;
1432 }
1433
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_string_clone(&url.absolute_path, &request->path))
1434 {
1435 ret = GATE_RESULT_OUTOFMEMORY;
1436 break;
1437 }
1438
1439 4 ret = gate_uri_to_string(&url, &str_url, false);
1440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
1441
1442 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_URL, str_url.str);
1443
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]");
1444
1445
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_string_is_empty(&request->proxy_server))
1446 {
1447 GATE_STRING_TO_BUFFER(&request->proxy_server, buffer);
1448 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXY, buffer);
1449 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXY]");
1450
1451 if (request->proxy_port)
1452 {
1453 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYPORT, (long)request->proxy_port);
1454 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYPORT]");
1455 }
1456
1457 if (!gate_string_is_empty(&request->proxy_user))
1458 {
1459 GATE_STRING_TO_BUFFER(&request->proxy_user, buffer);
1460 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYUSERNAME, buffer);
1461 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYUSERNAME]");
1462 }
1463 if (!gate_string_is_empty(&request->proxy_pass))
1464 {
1465 GATE_STRING_TO_BUFFER(&request->proxy_pass, buffer);
1466 curl_code = curl.easy_setopt(curl_session, CURLOPT_PROXYPORT, buffer);
1467 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[PROXYPORT]");
1468 }
1469 }
1470
1471
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_DISABLE_VERIFICATION))
1472 {
1473 2 curl_code = curl.easy_setopt(curl_session, CURLOPT_SSL_VERIFYHOST, 0L);
1474
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]");
1475
1476 2 curl_code = curl.easy_setopt(curl_session, CURLOPT_SSL_VERIFYPEER, 0L);
1477
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]");
1478 }
1479
1480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (request->connect_timeout_ms)
1481 {
1482 curl_code = curl.easy_setopt(curl_session, CURLOPT_CONNECTTIMEOUT_MS, (long)request->connect_timeout_ms);
1483 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CONNECTTIMEOUT_MS]");
1484 }
1485
1486
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)
1487 {
1488 curl_code = curl.easy_setopt(curl_session, CURLOPT_TIMEOUT_MS,
1489 (long)(request->connect_timeout_ms + request->send_timeout_ms + request->receive_timeout_ms));
1490 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[TIMEOUT_MS]");
1491 }
1492
1493
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_SECURE))
1494 {
1495 2 curl_code = curl.easy_setopt(curl_session, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTPS);
1496
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]");
1497 }
1498 else
1499 {
1500 2 curl_code = curl.easy_setopt(curl_session, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTP);
1501
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]");
1502 }
1503
1504 4 is_get_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_GET);
1505 4 is_post_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_POST);
1506 4 is_put_request = gate_string_equals_str(&request->method, GATE_HTTP_METHOD_PUT);
1507
1508
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))
1509 {
1510 key = (gate_string_t const*)gate_map_iterator_key(iter);
1511 value = (gate_string_t const*)gate_map_iterator_value(iter);
1512
1513 if (is_post_request || is_put_request)
1514 {
1515 /* exclude non-supported custom fields in POST mode */
1516 if (gate_string_equals_str(key, GATE_HTTP_HEADER_CONTENTLENGTH)) continue;
1517 }
1518 buffer_len = gate_string_to_buffer(key, buffer, sizeof(buffer));
1519 buffer_len += gate_string_to_buffer(&header_entry_separator, &buffer[buffer_len], sizeof(buffer) - buffer_len);
1520 buffer_len += gate_string_to_buffer(value, &buffer[buffer_len], sizeof(buffer) - buffer_len);
1521
1522 curl_headers = curl.slist_append(curl_headers, buffer);
1523 }
1524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (curl_headers != NULL)
1525 {
1526 curl_code = curl.easy_setopt(curl_session, CURLOPT_HTTPHEADER, curl_headers);
1527 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[HTTPHEADER]");
1528 }
1529
1530
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (request->upload_stream == NULL)
1531 {
1532
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 if (!is_get_request && !is_post_request && !is_put_request)
1533 {
1534 gate_string_to_buffer(&request->method, buffer, sizeof(buffer));
1535 curl_code = curl.easy_setopt(curl_session, CURLOPT_CUSTOMREQUEST, buffer);
1536 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CUSTOMREQUEST]");
1537 }
1538 }
1539 else
1540 {
1541 value = (gate_string_t const*)gate_map_get_value(&request->headers, &header_entry_content_type);
1542 if (is_post_request)
1543 {
1544 if (value && gate_string_starts_with_str(value, GATE_HTTP_HEADERVALUE_CONTENTTYPE_MULTIPARTFORMDATA))
1545 {
1546 ret = setup_multipart_formdata(curl_session, request);
1547 GATE_BREAK_IF_FAILED(ret);
1548 }
1549 else
1550 {
1551 ret = extract_stream_data(curl_session, request->upload_stream, false);
1552 GATE_BREAK_IF_FAILED(ret);
1553 }
1554 }
1555 else
1556 {
1557 ret = extract_stream_data(curl_session, request->upload_stream, is_put_request);
1558 GATE_BREAK_IF_FAILED(ret);
1559
1560 gate_string_to_buffer(&request->method, buffer, sizeof(buffer));
1561 curl_code = curl.easy_setopt(curl_session, CURLOPT_CUSTOMREQUEST, buffer);
1562 GATE_CURL_CHECK_ERROR(curl_code, "curl.easy_setopt[CUSTOMREQUEST]");
1563 }
1564 }
1565
1566
1567
1568 4 response_header = gate_memstream_create(GATE_MAX_COPYBUFFER_LENGTH);
1569
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (NULL == response_header)
1570 {
1571 ret = GATE_RESULT_OUTOFMEMORY;
1572 break;
1573 }
1574 4 response_content = gate_memstream_create(GATE_MAX_BLOCK_COPYBUFFER_LENGTH);
1575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (NULL == response_content)
1576 {
1577 ret = GATE_RESULT_OUTOFMEMORY;
1578 break;
1579 }
1580
1581 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEDATA, response_content);
1582
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]");
1583
1584 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEFUNCTION, &gate_curl_write_stream_cb);
1585
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]");
1586
1587 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_WRITEHEADER, response_header);
1588
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]");
1589
1590 4 curl_code = curl.easy_setopt(curl_session, CURLOPT_HEADERFUNCTION, gate_curl_write_stream_cb);
1591
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]");
1592
1593
1594 4 curl_code = curl.easy_perform(curl_session);
1595
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");
1596
1597 /* HTTP transmission completed -> fill response object */
1598 4 ret = parse_response_header(gate_memstream_get_data(response_header),
1599 4 gate_memstream_size(response_header),
1600 &response->headers, &response->status_code);
1601
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
1602
1603 4 response->response_stream = (gate_stream_t*)response_content;
1604 4 response_content = NULL;
1605
1606 4 ret = GATE_RESULT_OK;
1607
1608 } while (0);
1609
1610
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (curl_session)
1611 {
1612 4 curl.easy_cleanup(curl_session);
1613 4 curl_session = NULL;
1614 }
1615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (curl_headers != NULL)
1616 {
1617 curl.slist_free_all(curl_headers);
1618 }
1619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (response_content != NULL)
1620 {
1621 gate_object_release(response_content);
1622 }
1623
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (response_header != NULL)
1624 {
1625 4 gate_object_release(response_header);
1626 }
1627
1628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (GATE_FAILED(ret))
1629 {
1630 gate_http_response_release(response);
1631 }
1632
1633 4 gate_string_release(&str_url);
1634 4 gate_uri_destroy(&url);
1635 4 return ret;
1636 }
1637
1638 6 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1639 {
1640
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1641 {
1642 2 return native_httpclient_send_request(client, request, response);
1643 }
1644 else
1645 {
1646 4 return curl_httpclient_send_request(client, request, response);
1647 }
1648 }
1649
1650
1651 4 static gate_result_t curl_httpclient_release(gate_httpclient_t* client)
1652 {
1653 4 gate_string_release(&client->server);
1654 4 gate_mem_clear(client, sizeof(gate_httpclient_t));
1655 4 return GATE_RESULT_OK;
1656 }
1657
1658 6 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1659 {
1660
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (GATE_FLAG_ENABLED(client->flags, GATE_HTTPCLIENT_FLAG_NATIVE_IMPL))
1661 {
1662 2 return native_httpclient_release(client);
1663 }
1664 else
1665 {
1666 4 return curl_httpclient_release(client);
1667 }
1668 }
1669
1670 #endif /* GATE_NET_HTTPCLIENT_CURL */
1671
1672
1673 #if defined(GATE_NET_HTTPCLIENT_WASM_IMPL)
1674
1675 #include <emscripten/fetch.h>
1676 #include <emscripten/wasm_worker.h>
1677
1678 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1679 {
1680 gate_mem_clear(client, sizeof(gate_httpclient_t));
1681
1682 if (NULL == gate_string_clone(&client->server, server))
1683 {
1684 return GATE_RESULT_OUTOFMEMORY;
1685 }
1686 client->port = port;
1687 client->flags = flags;
1688 return GATE_RESULT_OK;
1689 }
1690
1691 static void httpclient_on_success(emscripten_fetch_t* fetch)
1692 {
1693 emscripten_semaphore_t* ptr_semaphore = (emscripten_semaphore_t*)fetch->userData;
1694 emscripten_semaphore_release(ptr_semaphore, 1);
1695 }
1696
1697 static void httpclient_on_error(emscripten_fetch_t* fetch)
1698 {
1699 emscripten_semaphore_t* ptr_semaphore = (emscripten_semaphore_t*)fetch->userData;
1700 emscripten_semaphore_release(ptr_semaphore, 1);
1701 }
1702
1703 #define HTTPCLIENT_MAX_HEADERS 32
1704 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1705 {
1706 gate_result_t ret = GATE_RESULT_FAILED;
1707 emscripten_fetch_t* ptr_fetch = NULL;
1708 gate_string_t header_strings[HTTPCLIENT_MAX_HEADERS * 2] = GATE_INIT_EMPTY;
1709 gate_size_t header_strings_count = 0;
1710 char const* headers[HTTPCLIENT_MAX_HEADERS * 2 + 1];
1711 gate_enumerator_t e = GATE_INIT_EMPTY;
1712
1713 gate_map_enumerate(&request->headers, &e);
1714 while (gate_enumerator_valid(&e) && (header_strings_count < HTTPCLIENT_MAX_HEADERS * 2))
1715 {
1716 gate_mapping_t const* ptr_map_entry = (gate_mapping_t const*)gate_enumerator_get(&e);
1717 gate_string_t const* ptr_str = (gate_string_t const*)ptr_map_entry->key;
1718 if (ptr_str->length > 0)
1719 {
1720 if (ptr_str->str[ptr_str->length] == 0)
1721 {
1722 gate_string_clone(&header_strings[header_strings_count], ptr_str);
1723 }
1724 else
1725 {
1726 gate_string_create_copy(&header_strings[header_strings_count], ptr_str);
1727 }
1728 }
1729 headers[header_strings_count] = gate_string_ptr(&header_strings[header_strings_count], 0);
1730 ++header_strings_count;
1731
1732 ptr_str = (gate_string_t const*)ptr_map_entry->value;
1733 if (ptr_str->length > 0)
1734 {
1735 if (ptr_str->str[ptr_str->length] == 0)
1736 {
1737 gate_string_clone(&header_strings[header_strings_count], ptr_str);
1738 }
1739 else
1740 {
1741 gate_string_create_copy(&header_strings[header_strings_count], ptr_str);
1742 }
1743 }
1744 headers[header_strings_count] = gate_string_ptr(&header_strings[header_strings_count], 0);
1745 ++header_strings_count;
1746
1747 gate_enumerator_next(&e);
1748 }
1749 headers[header_strings_count] = NULL;
1750
1751 do
1752 {
1753 char url_buffer[2048];
1754 gate_strbuilder_t builder;
1755 emscripten_semaphore_t sem_completed = EMSCRIPTEN_SEMAPHORE_T_STATIC_INITIALIZER(0);
1756 emscripten_fetch_attr_t attr;
1757
1758 gate_strbuilder_create_static(&builder, url_buffer, sizeof(url_buffer), 0);
1759 gate_strbuilder_append_cstr(&builder, "http://");
1760 gate_strbuilder_append_string(&builder, &client->server);
1761 gate_strbuilder_append_cstr(&builder, ":");
1762 gate_strbuilder_append_uint32(&builder, client->port);
1763 gate_strbuilder_append_string(&builder, &request->path);
1764
1765 emscripten_fetch_attr_init(&attr);
1766 gate_str_print_text(attr.requestMethod, sizeof(attr.requestMethod),
1767 gate_string_ptr(&request->method, 0),
1768 gate_string_length(&request->method));
1769 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
1770 attr.requestHeaders = headers;
1771 attr.onsuccess = &httpclient_on_success;
1772 attr.onerror = &httpclient_on_error;
1773 attr.userData = (emscripten_semaphore_t*)&sem_completed;
1774 ptr_fetch = emscripten_fetch(&attr, url_buffer);
1775
1776 emscripten_semaphore_waitinf_acquire(&sem_completed, 1);
1777
1778 if (response)
1779 {
1780 gate_memstream_t* response_stream;
1781 gate_http_response_init(response);
1782 response_stream = gate_memstream_create(ptr_fetch->numBytes);
1783 gate_stream_write_block((gate_stream_t*)response_stream, ptr_fetch->data, ptr_fetch->numBytes, NULL);
1784 response->status_code = ptr_fetch->status;
1785 response->response_stream = (gate_stream_t*)response_stream;
1786 }
1787 ret = GATE_RESULT_OK;
1788 } while (0);
1789
1790 if (ptr_fetch)
1791 {
1792 emscripten_fetch_close(ptr_fetch);
1793 }
1794
1795 while (header_strings_count > 0)
1796 {
1797 --header_strings_count;
1798 gate_string_release(&header_strings[header_strings_count]);
1799 }
1800
1801 return ret;
1802 }
1803 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1804 {
1805 GATE_UNUSED_ARG(client);
1806 return GATE_RESULT_NOTIMPLEMENTED;
1807 }
1808
1809 #endif /* GATE_NET_HTTPCLIENT_WASM_IMPL */
1810
1811
1812 #if defined(GATE_NET_HTTPCLIENT_NO_IMPL)
1813
1814 gate_result_t gate_httpclient_create(gate_httpclient_t* client, gate_string_t const* server, gate_uint16_t port, gate_enumint_t flags)
1815 {
1816 GATE_UNUSED_ARG(client);
1817 GATE_UNUSED_ARG(server);
1818 GATE_UNUSED_ARG(port);
1819 GATE_UNUSED_ARG(flags);
1820 return GATE_RESULT_NOTIMPLEMENTED;
1821 }
1822 gate_result_t gate_httpclient_send_request(gate_httpclient_t* client, gate_http_request_t* request, gate_http_response_t* response)
1823 {
1824 GATE_UNUSED_ARG(client);
1825 GATE_UNUSED_ARG(request);
1826 GATE_UNUSED_ARG(response);
1827 return GATE_RESULT_NOTIMPLEMENTED;
1828 }
1829 gate_result_t gate_httpclient_release(gate_httpclient_t* client)
1830 {
1831 GATE_UNUSED_ARG(client);
1832 return GATE_RESULT_NOTIMPLEMENTED;
1833 }
1834
1835 #endif /* GATE_NET_HTTPCLIENT_NO_IMPL */
1836