source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/src/uhttp.c@ 464

Last change on this file since 464 was 464, checked in by coas-nagasima, 3 years ago

WolfSSLとAzure IoT SDKを更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 58.0 KB
Line 
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#include <stdlib.h>
5#include <stddef.h>
6#include <stdbool.h>
7#include <ctype.h>
8
9#include "umock_c/umock_c_prod.h"
10#include "azure_c_shared_utility/gballoc.h"
11
12#include <string.h>
13#include "azure_uhttp_c/uhttp.h"
14#include "azure_c_shared_utility/httpheaders.h"
15#include "azure_c_shared_utility/crt_abstractions.h"
16#include "azure_c_shared_utility/strings.h"
17#include "azure_c_shared_utility/xlogging.h"
18#include "azure_c_shared_utility/buffer_.h"
19#include "azure_c_shared_utility/singlylinkedlist.h"
20#include "azure_c_shared_utility/shared_util_options.h"
21#include "azure_c_shared_utility/optimize_size.h"
22
23#define MAX_HOSTNAME 64
24#define TIME_MAX_BUFFER 16
25#define HTTP_CRLF_LEN 2
26#define HTTP_END_TOKEN_LEN 4
27#define MAX_CONTENT_LENGTH 16
28
29static const char* HTTP_REQUEST_LINE_FMT = "%s %s HTTP/1.1\r\n";
30static const char* HTTP_HOST = "Host";
31// The following header names MUST be lowercase as they are used for HTTP response header comparison:
32static const char* HTTP_CONTENT_LEN = "content-length";
33static const char* HTTP_TRANSFER_ENCODING = "transfer-encoding";
34static const char* HTTP_CRLF_VALUE = "\r\n";
35
36typedef enum RESPONSE_MESSAGE_STATE_TAG
37{
38 state_initial,
39 state_opening,
40 state_open,
41 state_process_status_line,
42 state_process_headers,
43 state_process_body,
44 state_process_chunked_body,
45
46 state_send_user_callback,
47 state_parse_complete,
48
49 state_closing,
50 state_closed,
51 state_error
52} RESPONSE_MESSAGE_STATE;
53
54typedef struct HTTP_RECV_DATA_TAG
55{
56 ON_HTTP_REQUEST_CALLBACK on_request_callback;
57 void* user_ctx;
58 int status_code;
59 RESPONSE_MESSAGE_STATE recv_state;
60 HTTP_HEADERS_HANDLE resp_header;
61 BUFFER_HANDLE msg_body;
62 size_t total_body_len;
63 BUFFER_HANDLE accrual_buff;
64 bool chunked_reply;
65} HTTP_RECV_DATA;
66
67typedef struct HTTP_CLIENT_HANDLE_DATA_TAG
68{
69 XIO_HANDLE xio_handle;
70 ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect;
71 void* connect_user_ctx;
72 ON_HTTP_ERROR_CALLBACK on_error;
73 void* error_user_ctx;
74 ON_HTTP_CLOSED_CALLBACK on_close_callback;
75 void* close_user_ctx;
76 HTTP_RECV_DATA recv_msg;
77 bool chunk_request;
78 bool trace_on;
79 bool trace_body;
80 char* host_name;
81 int port_num;
82 SINGLYLINKEDLIST_HANDLE data_list;
83 bool cert_type_ecc;
84 char* x509_cert;
85 char* x509_pk;
86 char* certificate;
87 int connected;
88} HTTP_CLIENT_HANDLE_DATA;
89
90typedef struct HTTP_SEND_DATA_TAG
91{
92 HTTP_CLIENT_REQUEST_TYPE request_type;
93 STRING_HANDLE relative_path;
94 STRING_HANDLE header_line;
95 BUFFER_HANDLE content;
96} HTTP_SEND_DATA;
97
98static void send_complete_callback(void* context, IO_SEND_RESULT send_result)
99{
100 (void)context;(void)send_result;
101}
102
103static int initialize_received_data(HTTP_CLIENT_HANDLE_DATA* http_data)
104{
105 int result = 0;
106
107 // Initialize data if necessary
108 if (http_data->recv_msg.resp_header == NULL)
109 {
110 http_data->recv_msg.resp_header = HTTPHeaders_Alloc();
111 if (http_data->recv_msg.resp_header == NULL)
112 {
113 /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
114 LogError("Failure creating Http header.");
115 result = MU_FAILURE;
116 }
117 }
118 if (result == 0 && http_data->recv_msg.accrual_buff == NULL)
119 {
120 http_data->recv_msg.accrual_buff = BUFFER_new();
121 if (http_data->recv_msg.accrual_buff == NULL)
122 {
123 /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
124 LogError("Failure creating accrual buffer.");
125 result = MU_FAILURE;
126 }
127 }
128 http_data->recv_msg.chunked_reply = false;
129 return result;
130}
131
132static int process_status_code_line(const unsigned char* buffer, size_t len, size_t* position, int* statusLen)
133{
134 int result = MU_FAILURE;
135 size_t index;
136 int spaceFound = 0;
137 const char* initSpace = NULL;
138 char status_code[4] = { 0 };
139
140 for (index = 0; index < len; index++)
141 {
142 if (buffer[index] == ' ')
143 {
144 if (spaceFound == 1)
145 {
146 (void)memcpy(status_code, initSpace, 3);
147 status_code[3] = '\0';
148 }
149 else
150 {
151 initSpace = (const char*)buffer + index + 1;
152 }
153 spaceFound++;
154 }
155 else if (buffer[index] == '\n')
156 {
157 *statusLen = (int)atol(status_code);
158 if (index < len)
159 {
160 *position = index + 1;
161 }
162 else
163 {
164 *position = index;
165 }
166 result = 0;
167 break;
168 }
169 }
170 return result;
171}
172
173static int process_header_line(const unsigned char* buffer, size_t len, size_t* position, HTTP_HEADERS_HANDLE resp_header, size_t* contentLen, bool* isChunked)
174{
175 int result = MU_FAILURE;
176 size_t index;
177 const unsigned char* targetPos = buffer;
178 bool crlfEncounted = false;
179 bool colonEncountered = false;
180 char* headerKey = NULL;
181 bool continueProcessing = true;
182
183 for (index = 0; index < len && continueProcessing; index++)
184 {
185 if (buffer[index] == ':' && !colonEncountered)
186 {
187 colonEncountered = true;
188 size_t keyLen = (&buffer[index]) - targetPos;
189
190 if (keyLen == 0)
191 {
192 LogError("Invalid header name with zero length.");
193 result = MU_FAILURE;
194 continueProcessing = false;
195 }
196 else
197 {
198 if (headerKey != NULL)
199 {
200 free(headerKey);
201 headerKey = NULL;
202 }
203 headerKey = (char*)malloc(keyLen + 1);
204 if (headerKey == NULL)
205 {
206 result = MU_FAILURE;
207 continueProcessing = false;
208 }
209 else
210 {
211 memcpy(headerKey, targetPos, keyLen);
212 headerKey[keyLen] = '\0';
213
214 // Convert to lower case
215 for (size_t inner = 0; inner < keyLen; inner++)
216 {
217 headerKey[inner] = (char)tolower(headerKey[inner]);
218 }
219
220 targetPos = buffer+index+1;
221 crlfEncounted = false;
222 }
223 }
224 }
225 else if (buffer[index] == '\r')
226 {
227 if (headerKey != NULL)
228 {
229 // Remove leading spaces
230 while (*targetPos == 32) { targetPos++; }
231
232 size_t valueLen = (&buffer[index])-targetPos;
233 char* headerValue = (char*)malloc(valueLen+1);
234 if (headerValue == NULL)
235 {
236 result = MU_FAILURE;
237 continueProcessing = false;
238 }
239 else
240 {
241 memcpy(headerValue, targetPos, valueLen);
242 headerValue[valueLen] = '\0';
243
244 if (HTTPHeaders_AddHeaderNameValuePair(resp_header, headerKey, headerValue) != HTTP_HEADERS_OK)
245 {
246 result = MU_FAILURE;
247 continueProcessing = false;
248 }
249 else
250 {
251 if (strcmp(headerKey, HTTP_CONTENT_LEN) == 0)
252 {
253 *isChunked = false;
254 *contentLen = atol(headerValue);
255 }
256 else if (strcmp(headerKey, HTTP_TRANSFER_ENCODING) == 0)
257 {
258 *isChunked = true;
259 *contentLen = 0;
260 }
261 if (index < len)
262 {
263 *position = index;
264 }
265 else
266 {
267 *position = index-1;
268 }
269 }
270 }
271 free(headerKey);
272 headerKey = NULL;
273 free(headerValue);
274 }
275 }
276 else if (buffer[index] == '\n')
277 {
278 if (crlfEncounted)
279 {
280 if (index < len)
281 {
282 *position = index+1;
283 }
284 else
285 {
286 *position = index;
287 }
288 result = 0;
289 break;
290 }
291 else
292 {
293 colonEncountered = false;
294 crlfEncounted = true;
295 targetPos = buffer+index+1;
296 }
297 }
298 else
299 {
300 crlfEncounted = false;
301 }
302 }
303 if (headerKey != NULL)
304 {
305 free(headerKey);
306 }
307 return result;
308}
309
310static int write_text_line(HTTP_CLIENT_HANDLE_DATA* http_data, const char* text_line)
311{
312 int result;
313 if (xio_send(http_data->xio_handle, text_line, strlen(text_line), send_complete_callback, NULL) != 0)
314 {
315 LogError("Failure calling xio_send.");
316 result = MU_FAILURE;
317 }
318 else
319 {
320 result = 0;
321 if (http_data->trace_on)
322 {
323 LOG(AZ_LOG_TRACE, LOG_LINE, "%s", text_line);
324 }
325 }
326 return result;
327}
328
329static int write_data_line(HTTP_CLIENT_HANDLE_DATA* http_data, const unsigned char* data_line, size_t length)
330{
331 int result;
332 if (xio_send(http_data->xio_handle, data_line, length, send_complete_callback, NULL) != 0)
333 {
334 LogError("Failure calling xio_send.");
335 result = MU_FAILURE;
336 }
337 else
338 {
339 result = 0;
340 if (http_data->trace_on)
341 {
342 if (length > 0)
343 {
344 if (http_data->trace_body)
345 {
346 LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n%.*s\r\n", (int)length, (int)length, data_line);
347 }
348 else
349 {
350 LOG(AZ_LOG_TRACE, LOG_LINE, "<data> len: %d\r\n", (int)length);
351 }
352 }
353 else
354 {
355 LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n", (int)length);
356 }
357 }
358 }
359 return result;
360}
361
362static int convert_char_to_hex(const unsigned char* hexText, size_t len)
363{
364 int result = 0;
365 for (size_t index = 0; index < len; index++)
366 {
367 if (hexText[index] == ';')
368 {
369 break;
370 }
371 else
372 {
373 int accumulator = 0;
374 if (hexText[index] >= 48 && hexText[index] <= 57)
375 {
376 accumulator = hexText[index] - 48;
377 }
378 else if (hexText[index] >= 65 && hexText[index] <= 70)
379 {
380 accumulator = hexText[index] - 55;
381 }
382 else if (hexText[index] >= 97 && hexText[index] <= 102)
383 {
384 accumulator = hexText[index] - 87;
385 }
386 if (index > 0)
387 {
388 result = result << 4;
389 }
390 result += accumulator;
391 }
392 }
393 return result;
394}
395
396static int setup_init_recv_msg(HTTP_RECV_DATA* recv_msg)
397{
398 int result;
399 recv_msg->status_code = 0;
400 recv_msg->recv_state = state_initial;
401 recv_msg->total_body_len = 0;
402 if (recv_msg->resp_header != NULL)
403 {
404 HTTPHeaders_Free(recv_msg->resp_header);
405 }
406 if (recv_msg->msg_body != NULL)
407 {
408 BUFFER_delete(recv_msg->msg_body);
409 }
410 if ((recv_msg->resp_header = HTTPHeaders_Alloc()) == NULL)
411 {
412 /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
413 LogError("Failure allocating http http_data items");
414 result = HTTP_CLIENT_ERROR;
415 }
416 else if ((recv_msg->msg_body = BUFFER_new()) == NULL)
417 {
418 /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
419 LogError("Failure allocating http data items");
420 HTTPHeaders_Free(recv_msg->resp_header);
421 recv_msg->resp_header = NULL;
422 result = HTTP_CLIENT_ERROR;
423 }
424 else
425 {
426 result = 0;
427 }
428 return result;
429}
430
431static void on_bytes_received(void* context, const unsigned char* buffer, size_t len)
432{
433 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
434
435 if (http_data != NULL && buffer != NULL && len > 0 && http_data->recv_msg.recv_state != state_error)
436 {
437 if (http_data->recv_msg.recv_state == state_parse_complete)
438 {
439 // The callback is getting called during a new send.
440 setup_init_recv_msg(&http_data->recv_msg);
441
442 }
443
444 if (http_data->recv_msg.recv_state == state_initial || http_data->recv_msg.recv_state == state_open)
445 {
446 if (initialize_received_data(http_data) != 0)
447 {
448 http_data->recv_msg.recv_state = state_error;
449 }
450 else
451 {
452 http_data->recv_msg.recv_state = state_process_status_line;
453 }
454 }
455
456 // Put the data in the buffer
457 if (BUFFER_append_build(http_data->recv_msg.accrual_buff, buffer, len) != 0)
458 {
459 /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
460 LogError("Failure appending bytes to buffer.");
461 http_data->recv_msg.recv_state = state_error;
462 }
463
464 if (http_data->recv_msg.recv_state == state_process_status_line)
465 {
466 size_t index = 0;
467 const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
468 size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
469
470 int lineComplete = process_status_code_line(stored_bytes, stored_len, &index, &http_data->recv_msg.status_code);
471 if (lineComplete == 0 && http_data->recv_msg.status_code > 0)
472 {
473 if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
474 {
475 LogError("Failure appending bytes to buffer.");
476 http_data->recv_msg.recv_state = state_error;
477 }
478 else
479 {
480 http_data->recv_msg.recv_state = state_process_headers;
481 }
482 }
483 }
484
485 if (http_data->recv_msg.recv_state == state_process_headers)
486 {
487 size_t index = 0;
488 const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
489 size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
490
491 int headerComplete = process_header_line(stored_bytes, stored_len, &index, http_data->recv_msg.resp_header, &http_data->recv_msg.total_body_len, &http_data->recv_msg.chunked_reply);
492 if (headerComplete == 0)
493 {
494 if (http_data->recv_msg.total_body_len == 0)
495 {
496 if (http_data->recv_msg.chunked_reply)
497 {
498
499 /* Codes_SRS_UHTTP_07_054: [ If the http header does not include a content length then it indicates a chunk response. ] */
500 http_data->recv_msg.recv_state = state_process_chunked_body;
501 }
502 else
503 {
504 // Content len is 0 so we are finished with the body
505 http_data->recv_msg.recv_state = state_send_user_callback;
506 }
507 }
508 else
509 {
510 http_data->recv_msg.recv_state = state_process_body;
511 }
512 }
513 if (index > 0)
514 {
515 if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
516 {
517 LogError("Failure appending bytes to buffer.");
518 http_data->recv_msg.recv_state = state_error;
519 }
520 }
521 }
522
523 if (http_data->recv_msg.recv_state == state_process_body)
524 {
525 if (http_data->recv_msg.total_body_len != 0)
526 {
527 size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
528
529 if ((http_data->recv_msg.total_body_len == stored_len) || (http_data->recv_msg.total_body_len == (stored_len - HTTP_END_TOKEN_LEN)))
530 {
531 if (http_data->recv_msg.msg_body != NULL)
532 {
533 BUFFER_delete(http_data->recv_msg.msg_body);
534 }
535 if ((http_data->recv_msg.msg_body = BUFFER_clone(http_data->recv_msg.accrual_buff)) == NULL)
536 {
537 LogError("Failure cloning BUFFER.");
538 http_data->recv_msg.recv_state = state_error;
539 }
540 else
541 {
542 http_data->recv_msg.recv_state = state_send_user_callback;
543 }
544 }
545 else if (stored_len > http_data->recv_msg.total_body_len)
546 {
547 LogError("Failure bytes encountered is greater then body length.");
548 http_data->recv_msg.recv_state = state_error;
549 }
550 }
551 }
552
553 if (http_data->recv_msg.recv_state == state_process_chunked_body)
554 {
555 const unsigned char* iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
556 const unsigned char* initial_pos = iterator;
557 const unsigned char* begin = iterator;
558 const unsigned char* end = iterator;
559 size_t accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
560
561 /* Codes_SRS_UHTTP_07_059: [ on_bytes_received shall loop throught the stored data to find the /r/n separator. ] */
562 while (iterator < (initial_pos + accural_len))
563 {
564 if (*iterator == '\r')
565 {
566 // Don't need anything
567 end = iterator;
568 iterator++;
569 }
570 else if (*iterator == '\n')
571 {
572 size_t data_length = 0;
573
574 /* Codes_SRS_UHTTP_07_055: [ on_bytes_received shall convert the hexs length supplied in the response to the data length of the chunked data. ] */
575 size_t hex_len = end - begin;
576 data_length = convert_char_to_hex(begin, hex_len);
577 if (data_length == 0)
578 {
579 if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
580 {
581 http_data->recv_msg.recv_state = state_send_user_callback;
582 }
583 else
584 {
585 // Need to continue parsing
586 http_data->recv_msg.recv_state = state_process_headers;
587 }
588 break;
589 }
590 else if ((data_length + HTTP_CRLF_LEN) < accural_len - (iterator - initial_pos))
591 {
592 /* Codes_SRS_UHTTP_07_056: [ After the response chunk is parsed it shall be placed in a BUFFER_HANDLE. ] */
593 iterator += 1;
594 if (BUFFER_append_build(http_data->recv_msg.msg_body, iterator, data_length) != 0)
595 {
596 /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the stop processing the request. ] */
597 LogError("Failure building buffer for chunked data.");
598 http_data->recv_msg.recv_state = state_error;
599 }
600 else
601 {
602 /* Codes_SRS_UHTTP_07_060: [ if the data_length specified in the chunk is beyond the amount of data recieved, the parsing shall end and wait for more data. ] */
603 if (iterator + (data_length + HTTP_CRLF_LEN) > initial_pos + accural_len)
604 {
605 LogError("Invalid length specified.");
606 http_data->recv_msg.recv_state = state_error;
607 break;
608 }
609 else if (iterator + (data_length + HTTP_CRLF_LEN) == initial_pos + accural_len)
610 {
611 if (BUFFER_shrink(http_data->recv_msg.accrual_buff, accural_len, false) != 0)
612 {
613 LogError("Failure shrinking accural buffer.");
614 http_data->recv_msg.recv_state = state_error;
615 }
616 break;
617 }
618 else
619 {
620 // Move the iterator beyond the data we read and the /r/n
621 iterator += (data_length + HTTP_CRLF_LEN);
622 }
623
624 /* Codes_SRS_UHTTP_07_058: [ Once a chunk size value of 0 is encountered on_bytes_received shall call the on_request_callback with the http message ] */
625 if (*iterator == '0' && (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN))
626 {
627 if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
628 {
629 http_data->recv_msg.recv_state = state_send_user_callback;
630 }
631 else
632 {
633 // Need to continue parsing
634 http_data->recv_msg.recv_state = state_process_headers;
635 }
636 break;
637 }
638 else
639 {
640 size_t shrink_len = iterator - initial_pos;
641 if (shrink_len > 0)
642 {
643 if (BUFFER_shrink(http_data->recv_msg.accrual_buff, shrink_len, false) != 0)
644 {
645 LogError("Failure shrinking accrual buffer.");
646 http_data->recv_msg.recv_state = state_error;
647 }
648 else
649 {
650 accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
651 initial_pos = iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
652 }
653 }
654 }
655 }
656 begin = end = iterator;
657 }
658 else
659 {
660 break;
661 }
662 }
663 else
664 {
665 end = iterator;
666 iterator++;
667 }
668 }
669 }
670
671 if (http_data->recv_msg.recv_state == state_send_user_callback || http_data->recv_msg.recv_state == state_error)
672 {
673 const unsigned char* reply_data = NULL;
674 size_t reply_len = 0;
675 HTTP_CALLBACK_REASON http_reason = HTTP_CALLBACK_REASON_OK;
676 if (http_data->recv_msg.msg_body != NULL)
677 {
678 reply_data = BUFFER_u_char(http_data->recv_msg.msg_body);
679 reply_len = BUFFER_length(http_data->recv_msg.msg_body);
680 }
681 if (http_data->recv_msg.recv_state == state_error)
682 {
683 http_reason = HTTP_CALLBACK_REASON_PARSING_ERROR;
684 }
685 if (http_data->trace_on)
686 {
687 LOG(AZ_LOG_TRACE, LOG_LINE, "\r\nHTTP Status: %d\r\n", http_data->recv_msg.status_code);
688
689 // Loop through headers
690 size_t count;
691 if (HTTPHeaders_GetHeaderCount(http_data->recv_msg.resp_header, &count) == 0)
692 {
693 for (size_t index = 0; index < count; index++)
694 {
695 char* header;
696 if (HTTPHeaders_GetHeader(http_data->recv_msg.resp_header, index, &header) == HTTP_HEADERS_OK)
697 {
698 LOG(AZ_LOG_TRACE, LOG_LINE, "%s", header);
699 free(header);
700 }
701 }
702 }
703 if (http_data->trace_body && reply_len > 0)
704 {
705 LOG(AZ_LOG_TRACE, LOG_LINE, "\r\n%.*s\r\n", (int)reply_len, reply_data);
706 }
707 }
708 http_data->recv_msg.on_request_callback(http_data->recv_msg.user_ctx, http_reason, reply_data, reply_len, http_data->recv_msg.status_code, http_data->recv_msg.resp_header);
709 http_data->recv_msg.recv_state = state_parse_complete;
710 }
711
712 if (http_data->recv_msg.recv_state == state_parse_complete)
713 {
714 HTTPHeaders_Free(http_data->recv_msg.resp_header);
715 http_data->recv_msg.resp_header = NULL;
716 BUFFER_delete(http_data->recv_msg.msg_body);
717 http_data->recv_msg.msg_body = NULL;
718 BUFFER_delete(http_data->recv_msg.accrual_buff);
719 http_data->recv_msg.accrual_buff = NULL;
720 }
721 }
722}
723
724static void on_xio_close_complete(void* context)
725{
726 if (context != NULL)
727 {
728 /* Codes_SRS_UHTTP_07_045: [ If on_close_callback is not NULL, on_close_callback shall be called once the underlying xio is closed. ] */
729 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
730 if (http_data->on_close_callback)
731 {
732 http_data->on_close_callback(http_data->close_user_ctx);
733 }
734 http_data->recv_msg.recv_state = state_closed;
735 http_data->connected = 0;
736 }
737}
738
739static void on_xio_open_complete(void* context, IO_OPEN_RESULT open_result)
740{
741 /* Codes_SRS_UHTTP_07_049: [ If not NULL uhttp_client_open shall call the on_connect callback with the callback_ctx, once the underlying xio's open is complete. ] */
742 if (context != NULL)
743 {
744 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
745 if (open_result == IO_OPEN_OK)
746 {
747 /* Codes_SRS_UHTTP_07_042: [ If the underlying socket opens successfully the on_connect callback shall be call with HTTP_CALLBACK_REASON_OK... ] */
748 if (http_data->on_connect != NULL)
749 {
750 http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OK);
751 }
752 http_data->recv_msg.recv_state = state_open;
753 http_data->connected = 1;
754 }
755 else
756 {
757 /* Codes_SRS_UHTTP_07_043: [ Otherwise on_connect callback shall be call with HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
758 if (http_data->on_connect != NULL)
759 {
760 http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OPEN_FAILED);
761 }
762 }
763 }
764 else
765 {
766 LogError("Context on_xio_open_complete is NULL");
767 }
768}
769
770static void on_io_error(void* context)
771{
772 /* Codes_SRS_UHTTP_07_050: [ if context is NULL on_io_error shall do nothing. ] */
773 if (context != NULL)
774 {
775 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
776 /* Codes_SRS_UHTTP_07_051: [ if on_error callback is not NULL, on_io_error shall call on_error callback. ] */
777 if (http_data->on_error)
778 {
779 http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_ERROR);
780 }
781 http_data->connected = 0;
782 }
783 else
784 {
785 LogError("Context on_io_error is NULL");
786 }
787}
788
789static int construct_http_headers(HTTP_HEADERS_HANDLE http_header, size_t content_len, STRING_HANDLE buffData, bool chunk_data, const char* hostname, int port_num)
790{
791 (void)chunk_data;
792 int result = 0;
793 size_t headerCnt = 0;
794 if ( (http_header != NULL) && HTTPHeaders_GetHeaderCount(http_header, &headerCnt) != HTTP_HEADERS_OK)
795 {
796 LogError("Failed in HTTPHeaders_GetHeaderCount");
797 result = MU_FAILURE;
798 }
799 else
800 {
801 bool hostname_found = false;
802 for (size_t index = 0; index < headerCnt && result == 0; index++)
803 {
804 char* header;
805 if (HTTPHeaders_GetHeader(http_header, index, &header) != HTTP_HEADERS_OK)
806 {
807 result = MU_FAILURE;
808 LogError("Failed in HTTPHeaders_GetHeader");
809 }
810 else
811 {
812 size_t dataLen = strlen(header)+2;
813 char* sendData = malloc(dataLen+1);
814 if (sendData == NULL)
815 {
816 result = MU_FAILURE;
817 LogError("Failed in allocating header data");
818 }
819 else
820 {
821 if (strcmp(header, HTTP_HOST) == 0)
822 {
823 hostname_found = true;
824 }
825
826 if (snprintf(sendData, dataLen+1, "%s\r\n", header) <= 0)
827 {
828 result = MU_FAILURE;
829 LogError("Failed in constructing header data");
830 }
831 else
832 {
833 if (STRING_concat(buffData, sendData) != 0)
834 {
835 result = MU_FAILURE;
836 LogError("Failed in building header data");
837 }
838 }
839 free(sendData);
840 }
841 free(header);
842 }
843 }
844 if (!hostname_found)
845 {
846 // calculate the size of the host header
847 size_t host_len = strlen(HTTP_HOST) + strlen(hostname) + MAX_CONTENT_LENGTH + 2;
848 char* host_header = malloc(host_len + 1);
849 if (host_header == NULL)
850 {
851 LogError("Failed allocating host header");
852 result = MU_FAILURE;
853 }
854 else
855 {
856 if (snprintf(host_header, host_len + 1, "%s: %s:%d\r\n", HTTP_HOST, hostname, port_num) <= 0)
857 {
858 LogError("Failed constructing host header");
859 result = MU_FAILURE;
860 }
861 else if (STRING_concat(buffData, host_header) != 0)
862 {
863 LogError("Failed adding the host header to the http item");
864 result = MU_FAILURE;
865 }
866 free(host_header);
867 }
868 }
869
870 if (result == 0)
871 {
872 /* Codes_SRS_UHTTP_07_015: [uhttp_client_execute_request shall add the Content-Length to the request if the contentLength is > 0] */
873 size_t fmtLen = strlen(HTTP_CONTENT_LEN) + HTTP_CRLF_LEN + 8;
874 char* content = malloc(fmtLen+1);
875 if (content == NULL)
876 {
877 LogError("Failed allocating chunk header");
878 result = MU_FAILURE;
879 }
880 else
881 {
882 /* Codes_SRS_UHTTP_07_015: [on_bytes_received shall add the Content-Length http header item to the request.] */
883 if (sprintf(content, "%s: %u%s", HTTP_CONTENT_LEN, (unsigned int)content_len, HTTP_CRLF_VALUE) <= 0)
884 {
885 result = MU_FAILURE;
886 LogError("Failed allocating content len header data");
887 }
888 else
889 {
890 if (STRING_concat(buffData, content) != 0)
891 {
892 result = MU_FAILURE;
893 LogError("Failed building content len header data");
894 }
895 }
896 free(content);
897 }
898
899 if (STRING_concat(buffData, "\r\n") != 0)
900 {
901 result = MU_FAILURE;
902 LogError("Failed sending header finalization data");
903 }
904 }
905 }
906 return result;
907}
908
909static STRING_HANDLE construct_http_data(HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, STRING_HANDLE http_line)
910{
911 STRING_HANDLE result;
912
913 const char* method = (request_type == HTTP_CLIENT_REQUEST_GET) ? "GET"
914 : (request_type == HTTP_CLIENT_REQUEST_OPTIONS) ? "OPTIONS"
915 : (request_type == HTTP_CLIENT_REQUEST_POST) ? "POST"
916 : (request_type == HTTP_CLIENT_REQUEST_PUT) ? "PUT"
917 : (request_type == HTTP_CLIENT_REQUEST_DELETE) ? "DELETE"
918 : (request_type == HTTP_CLIENT_REQUEST_PATCH) ? "PATCH"
919 : NULL;
920 /* Codes_SRS_UHTTP_07_014: [If the request_type is not a valid request http_client_execute_request shall return HTTP_CLIENT_ERROR] */
921 if (method == NULL)
922 {
923 LogError("Invalid request method %s specified", method);
924 result = NULL;
925 }
926 else
927 {
928 size_t buffLen = strlen(HTTP_REQUEST_LINE_FMT) + strlen(method) + strlen(relative_path);
929 char* request = malloc(buffLen+1);
930 if (request == NULL)
931 {
932 result = NULL;
933 LogError("Failure allocating Request data");
934 }
935 else
936 {
937 if (snprintf(request, buffLen + 1, HTTP_REQUEST_LINE_FMT, method, relative_path) <= 0)
938 {
939 result = NULL;
940 LogError("Failure writing request buffer");
941 }
942 else
943 {
944 result = STRING_construct(request);
945 if (result == NULL)
946 {
947 LogError("Failure creating buffer object");
948 }
949 else if (STRING_concat_with_STRING(result, http_line) != 0)
950 {
951 STRING_delete(result);
952 result = NULL;
953 LogError("Failure writing request buffers");
954 }
955 }
956 free(request);
957 }
958 }
959 return result;
960}
961
962static int send_http_data(HTTP_CLIENT_HANDLE_DATA* http_data, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path,
963 STRING_HANDLE http_line)
964{
965 int result;
966 STRING_HANDLE transmit_data = construct_http_data(request_type, relative_path, http_line);
967 if (transmit_data == NULL)
968 {
969 LogError("Failure constructing http data");
970 result = MU_FAILURE;
971 }
972 else
973 {
974 /* Tests_SRS_UHTTP_07_016: [http_client_execute_request shall transmit the http headers data through a call to xio_send;]*/
975 if (write_text_line(http_data, STRING_c_str(transmit_data) ) != 0)
976 {
977 result = MU_FAILURE;
978 LogError("Failure writing request buffer");
979 }
980 else
981 {
982 result = 0;
983 }
984 STRING_delete(transmit_data);
985 }
986 return result;
987}
988
989HTTP_CLIENT_HANDLE uhttp_client_create(const IO_INTERFACE_DESCRIPTION* io_interface_desc, const void* xio_param, ON_HTTP_ERROR_CALLBACK on_http_error, void* callback_ctx)
990{
991 HTTP_CLIENT_HANDLE_DATA* result;
992 /* Codes_SRS_UHTTP_07_002: [If io_interface_desc is NULL, uhttp_client_create shall return NULL.] */
993 if (io_interface_desc == NULL)
994 {
995 LogError("Invalid Parameter io_interface_desc is NULL");
996 result = NULL;
997 }
998 else
999 {
1000 result = malloc(sizeof(HTTP_CLIENT_HANDLE_DATA));
1001 if (result == NULL)
1002 {
1003 /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */
1004 LogError("Failure allocating http_client_handle");
1005 }
1006 else
1007 {
1008 memset(result, 0, sizeof(HTTP_CLIENT_HANDLE_DATA) );
1009 if ((result->data_list = singlylinkedlist_create() ) == NULL)
1010 {
1011 /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */
1012 LogError("Failure allocating data list");
1013 free(result);
1014 result = NULL;
1015 }
1016 else if ((result->xio_handle = xio_create(io_interface_desc, xio_param) ) == NULL)
1017 {
1018 /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
1019 LogError("xio create failed");
1020 singlylinkedlist_destroy(result->data_list);
1021 free(result);
1022 result = NULL;
1023 }
1024 else
1025 {
1026 /* Codes_SRS_UHTTP_07_001: [uhttp_client_create shall return an initialize the http client handle.] */
1027 result->on_error = on_http_error;
1028 result->error_user_ctx = callback_ctx;
1029 result->recv_msg.recv_state = state_initial;
1030 result->chunk_request = false;
1031 result->trace_on = false;
1032 }
1033 }
1034 }
1035 return (HTTP_CLIENT_HANDLE)result;
1036}
1037
1038void uhttp_client_destroy(HTTP_CLIENT_HANDLE handle)
1039{
1040 /* Codes_SRS_UHTTP_07_004: [ If handle is NULL then uhttp_client_destroy shall do nothing ] */
1041 if (handle != NULL)
1042 {
1043 /* Codes_SRS_UHTTP_07_005: [uhttp_client_destroy shall free any resource that is allocated in this translation unit] */
1044 if(handle->host_name != NULL)
1045 {
1046 free(handle->host_name);
1047 handle->host_name = NULL;
1048 }
1049 singlylinkedlist_destroy(handle->data_list);
1050 xio_destroy(handle->xio_handle);
1051 free(handle->certificate);
1052 free(handle->x509_pk);
1053 free(handle->x509_cert);
1054 free(handle);
1055 }
1056}
1057
1058HTTP_CLIENT_RESULT uhttp_client_open(HTTP_CLIENT_HANDLE handle, const char* host, int port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect, void* callback_ctx)
1059{
1060 HTTP_CLIENT_RESULT result;
1061 if (handle == NULL || host == NULL)
1062 {
1063 /* Codes_SRS_UHTTP_07_006: [If handle, io_interface_desc or host is NULL then `uhttp_client_open` shall return HTTP_CLIENT_INVALID_ARG] */
1064 LogError("Invalid handle value");
1065 result = HTTP_CLIENT_INVALID_ARG;
1066 }
1067 else
1068 {
1069 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
1070
1071 if ((http_data->recv_msg.recv_state != state_initial) &&
1072 (http_data->recv_msg.recv_state != state_error) &&
1073 (http_data->recv_msg.recv_state != state_closed))
1074 {
1075 LogError("Unable to open previously open client.");
1076 result = HTTP_CLIENT_INVALID_STATE;
1077 }
1078 else
1079 {
1080 if (http_data->host_name != NULL)
1081 {
1082 free(http_data->host_name);
1083 handle->host_name = NULL;
1084 }
1085
1086 if (mallocAndStrcpy_s(&http_data->host_name, host) != 0)
1087 {
1088 LogError("copying hostname has failed");
1089 result = HTTP_CLIENT_ERROR;
1090 }
1091 /* Codes_SRS_UHTTP_07_007: [http_client_connect shall attempt to open the xio_handle. ] */
1092 else
1093 {
1094 result = HTTP_CLIENT_OK;
1095 http_data->recv_msg.recv_state = state_opening;
1096 http_data->on_connect = on_connect;
1097 http_data->connect_user_ctx = callback_ctx;
1098 http_data->port_num = port_num;
1099
1100 if ((http_data->x509_cert != NULL) && (http_data->x509_pk != NULL))
1101 {
1102 if ((xio_setoption(http_data->xio_handle, SU_OPTION_X509_CERT, http_data->x509_cert) != 0) ||
1103 (xio_setoption(http_data->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_data->x509_pk) != 0))
1104 {
1105 LogError("Failed setting x509 certificate");
1106 result = HTTP_CLIENT_ERROR;
1107 free(http_data->host_name);
1108 http_data->host_name = NULL;
1109 http_data->on_connect = NULL;
1110 http_data->connect_user_ctx = NULL;
1111 http_data->port_num = 0;
1112 }
1113 }
1114
1115 if ((result == HTTP_CLIENT_OK) && (http_data->certificate != NULL))
1116 {
1117 if (xio_setoption(http_data->xio_handle, OPTION_TRUSTED_CERT, http_data->certificate) != 0)
1118 {
1119 LogError("Failed setting Trusted certificate");
1120 result = HTTP_CLIENT_ERROR;
1121 free(http_data->host_name);
1122 http_data->host_name = NULL;
1123 http_data->on_connect = NULL;
1124 http_data->connect_user_ctx = NULL;
1125 http_data->port_num = 0;
1126 }
1127 }
1128
1129 if (result == HTTP_CLIENT_OK)
1130 {
1131 if (xio_open(http_data->xio_handle, on_xio_open_complete, http_data, on_bytes_received, http_data, on_io_error, http_data) != 0)
1132 {
1133 /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
1134 LogError("opening xio failed");
1135 free(http_data->host_name);
1136 http_data->host_name = NULL;
1137 http_data->on_connect = NULL;
1138 http_data->connect_user_ctx = NULL;
1139 http_data->port_num = 0;
1140 http_data->recv_msg.recv_state = state_error;
1141
1142 result = HTTP_CLIENT_OPEN_FAILED;
1143 }
1144 else
1145 {
1146 /* Codes_SRS_UHTTP_07_008: [If http_client_connect succeeds then it shall return HTTP_CLIENT_OK] */
1147 result = HTTP_CLIENT_OK;
1148 }
1149 }
1150 }
1151 }
1152 }
1153 return result;
1154}
1155
1156void uhttp_client_close(HTTP_CLIENT_HANDLE handle, ON_HTTP_CLOSED_CALLBACK on_close_callback, void* callback_ctx)
1157{
1158 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
1159 /* Codes_SRS_UHTTP_07_009: [If handle is NULL then http_client_close shall do nothing] */
1160 /* Codes_SRS_UHTTP_07_049: [ If the state has been previously set to state_closed, uhttp_client_close shall do nothing. ] */
1161 if (http_data != NULL && http_data->recv_msg.recv_state != state_closed && http_data->recv_msg.recv_state != state_closing)
1162 {
1163 http_data->on_close_callback = on_close_callback;
1164 http_data->close_user_ctx = callback_ctx;
1165 /* Codes_SRS_UHTTP_07_010: [If the xio_handle is NOT NULL http_client_close shall call xio_close to close the handle] */
1166 (void)xio_close(http_data->xio_handle, on_xio_close_complete, http_data);
1167
1168 LIST_ITEM_HANDLE pending_list_item;
1169 while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL)
1170 {
1171 HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item);
1172 if (send_data != NULL)
1173 {
1174 STRING_delete(send_data->relative_path);
1175 BUFFER_delete(send_data->content);
1176 STRING_delete(send_data->header_line);
1177 free(send_data);
1178 }
1179 singlylinkedlist_remove(http_data->data_list, pending_list_item);
1180 }
1181
1182 http_data->recv_msg.status_code = 0;
1183 http_data->recv_msg.recv_state = state_closed;
1184 http_data->recv_msg.total_body_len = 0;
1185 free(http_data->host_name);
1186 http_data->host_name = NULL;
1187
1188 /* Codes_SRS_UHTTP_07_011: [http_client_close shall free any HTTPHeader object that has not been freed] */
1189 if (http_data->recv_msg.resp_header != NULL)
1190 {
1191 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1192 http_data->recv_msg.resp_header = NULL;
1193 }
1194 if (http_data->recv_msg.msg_body != NULL)
1195 {
1196 BUFFER_delete(http_data->recv_msg.msg_body);
1197 http_data->recv_msg.msg_body = NULL;
1198 }
1199 }
1200}
1201
1202HTTP_CLIENT_RESULT uhttp_client_execute_request(HTTP_CLIENT_HANDLE handle, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path,
1203 HTTP_HEADERS_HANDLE http_header_handle, const unsigned char* content, size_t content_len, ON_HTTP_REQUEST_CALLBACK on_request_callback, void* callback_ctx)
1204{
1205 HTTP_CLIENT_RESULT result;
1206 LIST_ITEM_HANDLE list_item;
1207
1208 /* Codes_SRS_UHTTP_07_012: [If handle, relativePath, or httpHeadersHandle is NULL then http_client_execute_request shall return HTTP_CLIENT_INVALID_ARG] */
1209 if (handle == NULL || on_request_callback == NULL ||
1210 (content != NULL && content_len == 0) || (content == NULL && content_len != 0) )
1211 {
1212 result = HTTP_CLIENT_INVALID_ARG;
1213 LogError("Invalid parameter sent to execute_request");
1214 }
1215 else
1216 {
1217 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
1218
1219 http_data->recv_msg.on_request_callback = on_request_callback;
1220 http_data->recv_msg.user_ctx = callback_ctx;
1221 if (setup_init_recv_msg(&http_data->recv_msg) != 0)
1222 {
1223 /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
1224 LogError("Failure allocating http http_data items");
1225 result = HTTP_CLIENT_ERROR;
1226 }
1227 else
1228 {
1229 HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)malloc(sizeof(HTTP_SEND_DATA));
1230 if (send_data == NULL)
1231 {
1232 LogError("Failure allocating http data items");
1233 BUFFER_delete(http_data->recv_msg.msg_body);
1234 http_data->recv_msg.msg_body = NULL;
1235 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1236 http_data->recv_msg.resp_header = NULL;
1237 result = HTTP_CLIENT_ERROR;
1238 }
1239 else
1240 {
1241 memset(send_data, 0, sizeof(HTTP_SEND_DATA));
1242 /* Codes_SRS_UHTTP_07_041: [HTTP_CLIENT_REQUEST_TYPE shall support all request types specified under section 9.1.2 in the spec.] */
1243 send_data->request_type = request_type;
1244 if ( (content_len > 0) && (send_data->content = BUFFER_create(content, content_len)) == NULL)
1245 {
1246 LogError("Failure allocating content buffer");
1247 result = HTTP_CLIENT_ERROR;
1248 BUFFER_delete(http_data->recv_msg.msg_body);
1249 http_data->recv_msg.msg_body = NULL;
1250 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1251 http_data->recv_msg.resp_header = NULL;
1252 free(send_data);
1253 }
1254 else if ((send_data->header_line = STRING_new()) == NULL)
1255 {
1256 LogError("Failure allocating content buffer");
1257 result = HTTP_CLIENT_ERROR;
1258 BUFFER_delete(send_data->content);
1259 BUFFER_delete(http_data->recv_msg.msg_body);
1260 http_data->recv_msg.msg_body = NULL;
1261 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1262 http_data->recv_msg.resp_header = NULL;
1263 free(send_data);
1264 }
1265 else if (construct_http_headers(http_header_handle, content_len, send_data->header_line, false, http_data->host_name, http_data->port_num))
1266 {
1267 LogError("Failure allocating content buffer");
1268 result = HTTP_CLIENT_ERROR;
1269 BUFFER_delete(send_data->content);
1270 STRING_delete(send_data->header_line);
1271 BUFFER_delete(http_data->recv_msg.msg_body);
1272 http_data->recv_msg.msg_body = NULL;
1273 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1274 http_data->recv_msg.resp_header = NULL;
1275 free(send_data);
1276 }
1277 else if ((list_item = singlylinkedlist_add(http_data->data_list, send_data)) == NULL)
1278 {
1279 STRING_delete(send_data->header_line);
1280 BUFFER_delete(send_data->content);
1281 LogError("Failure adding send data to list");
1282 result = HTTP_CLIENT_ERROR;
1283 BUFFER_delete(http_data->recv_msg.msg_body);
1284 http_data->recv_msg.msg_body = NULL;
1285 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1286 http_data->recv_msg.resp_header = NULL;
1287 free(send_data);
1288 }
1289 else
1290 {
1291 if (relative_path != NULL)
1292 {
1293 if ((send_data->relative_path = STRING_construct(relative_path)) == NULL)
1294 {
1295 (void)singlylinkedlist_remove(http_data->data_list, list_item);
1296 STRING_delete(send_data->header_line);
1297 BUFFER_delete(send_data->content);
1298 LogError("Failure allocating relative path buffer");
1299 BUFFER_delete(http_data->recv_msg.msg_body);
1300 http_data->recv_msg.msg_body = NULL;
1301 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1302 http_data->recv_msg.resp_header = NULL;
1303 free(send_data);
1304 result = HTTP_CLIENT_ERROR;
1305 }
1306 else
1307 {
1308 /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */
1309 result = HTTP_CLIENT_OK;
1310 }
1311 }
1312 else
1313 {
1314 if ((send_data->relative_path = STRING_construct("/")) == NULL)
1315 {
1316 (void)singlylinkedlist_remove(http_data->data_list, list_item);
1317 STRING_delete(send_data->header_line);
1318 BUFFER_delete(send_data->content);
1319 LogError("Failure allocating relative path buffer");
1320 BUFFER_delete(http_data->recv_msg.msg_body);
1321 http_data->recv_msg.msg_body = NULL;
1322 HTTPHeaders_Free(http_data->recv_msg.resp_header);
1323 http_data->recv_msg.resp_header = NULL;
1324 free(send_data);
1325 result = HTTP_CLIENT_ERROR;
1326 }
1327 else
1328 {
1329 /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */
1330 result = HTTP_CLIENT_OK;
1331 }
1332 }
1333 }
1334 }
1335 }
1336 }
1337 return result;
1338}
1339
1340void uhttp_client_dowork(HTTP_CLIENT_HANDLE handle)
1341{
1342 if (handle != NULL)
1343 {
1344 /* Codes_SRS_UHTTP_07_037: [http_client_dowork shall call the underlying xio_dowork function. ] */
1345 HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
1346 xio_dowork(http_data->xio_handle);
1347
1348 // Wait till I'm connected
1349 if (handle->connected == 1)
1350 {
1351 LIST_ITEM_HANDLE pending_list_item;
1352 /* Codes_SRS_UHTTP_07_016: [http_client_dowork shall iterate through the queued Data using the xio interface to send the http request in the following ways...] */
1353 while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL)
1354 {
1355 HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item);
1356 if (send_data != NULL)
1357 {
1358 size_t content_len = BUFFER_length(send_data->content);
1359 /* Codes_SRS_UHTTP_07_052: [uhttp_client_dowork shall call xio_send to transmits the header information... ] */
1360 if (send_http_data(http_data, send_data->request_type, STRING_c_str(send_data->relative_path), send_data->header_line) != 0)
1361 {
1362 LogError("Failure writing content buffer");
1363 if (http_data->on_error)
1364 {
1365 http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED);
1366 }
1367 }
1368 else if (content_len > 0)
1369 {
1370 /* Codes_SRS_UHTTP_07_053: [ Then uhttp_client_dowork shall use xio_send to transmit the content of the http request. ] */
1371 if (write_data_line(http_data, BUFFER_u_char(send_data->content), content_len) != 0)
1372 {
1373 LogError("Failure writing content buffer");
1374 if (http_data->on_error)
1375 {
1376 http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED);
1377 }
1378 }
1379 }
1380
1381 /* Codes_SRS_UHTTP_07_046: [ http_client_dowork shall free resouces queued to send to the http endpoint. ] */
1382 STRING_delete(send_data->relative_path);
1383 BUFFER_delete(send_data->content);
1384 STRING_delete(send_data->header_line);
1385 free(send_data);
1386 }
1387 (void)singlylinkedlist_remove(http_data->data_list, pending_list_item);
1388 }
1389 }
1390 }
1391}
1392
1393HTTP_CLIENT_RESULT uhttp_client_set_trace(HTTP_CLIENT_HANDLE handle, bool trace_on, bool trace_data)
1394{
1395 HTTP_CLIENT_RESULT result;
1396 if (handle == NULL)
1397 {
1398 /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
1399 result = HTTP_CLIENT_INVALID_ARG;
1400 LogError("invalid parameter (NULL) passed to http_client_set_trace");
1401 }
1402 else
1403 {
1404 /* Codes_SRS_UHTTP_07_039: [http_client_set_trace shall set the HTTP tracing to the trace_on variable.] */
1405 handle->trace_on = trace_on;
1406 handle->trace_body = trace_data;
1407 /* Codes_SRS_UHTTP_07_040: [if http_client_set_trace finishes successfully then it shall return HTTP_CLIENT_OK.] */
1408 result = HTTP_CLIENT_OK;
1409 }
1410 return result;
1411}
1412
1413HTTP_CLIENT_RESULT uhttp_client_set_X509_cert(HTTP_CLIENT_HANDLE handle, bool ecc_type, const char* certificate, const char* private_key)
1414{
1415 HTTP_CLIENT_RESULT result;
1416 if (handle == NULL || certificate == NULL || private_key == NULL)
1417 {
1418 /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
1419 result = HTTP_CLIENT_INVALID_ARG;
1420 LogError("invalid parameter handle: %p certificate: %p private_key: %p", handle, certificate, private_key);
1421 }
1422 else if (handle->recv_msg.recv_state != state_initial)
1423 {
1424 result = HTTP_CLIENT_INVALID_STATE;
1425 LogError("You must set the X509 certificates before opening the connection");
1426 }
1427 else
1428 {
1429 handle->cert_type_ecc = ecc_type;
1430 if (mallocAndStrcpy_s(&handle->x509_cert, certificate) != 0)
1431 {
1432 result = HTTP_CLIENT_ERROR;
1433 LogError("failure allocating certificate");
1434 }
1435 else if (mallocAndStrcpy_s(&handle->x509_pk, private_key) != 0)
1436 {
1437 free(handle->x509_cert);
1438 handle->x509_cert = NULL;
1439 result = HTTP_CLIENT_ERROR;
1440 LogError("failure allocating private key");
1441 }
1442 else
1443 {
1444 result = HTTP_CLIENT_OK;
1445 }
1446 }
1447 return result;
1448}
1449
1450HTTP_CLIENT_RESULT uhttp_client_set_trusted_cert(HTTP_CLIENT_HANDLE handle, const char* certificate)
1451{
1452 HTTP_CLIENT_RESULT result;
1453 if (handle == NULL || certificate == NULL)
1454 {
1455 /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
1456 result = HTTP_CLIENT_INVALID_ARG;
1457 LogError("invalid parameter handle: %p certificate: %p", handle, certificate);
1458 }
1459 else if (handle->recv_msg.recv_state != state_initial)
1460 {
1461 result = HTTP_CLIENT_INVALID_STATE;
1462 LogError("You must set the certificates before opening the connection");
1463 }
1464 else
1465 {
1466 if (mallocAndStrcpy_s(&handle->certificate, certificate) != 0)
1467 {
1468 result = HTTP_CLIENT_ERROR;
1469 LogError("failure allocating certificate");
1470 }
1471 else
1472 {
1473 result = HTTP_CLIENT_OK;
1474 }
1475 }
1476 return result;
1477}
1478
1479const char* uhttp_client_get_trusted_cert(HTTP_CLIENT_HANDLE handle)
1480{
1481 const char* result;
1482 if (handle == NULL)
1483 {
1484 result = NULL;
1485 LogError("invalid parameter NULL handle");
1486 }
1487 else
1488 {
1489 result = handle->certificate;
1490 }
1491 return result;
1492}
1493
1494HTTP_CLIENT_RESULT uhttp_client_set_option(HTTP_CLIENT_HANDLE handle, const char* optionName, const void* value)
1495{
1496 HTTP_CLIENT_RESULT result;
1497 if (handle == NULL)
1498 {
1499 /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
1500 result = HTTP_CLIENT_INVALID_ARG;
1501 LogError("invalid parameter handle: %p", handle);
1502 }
1503 else
1504 {
1505 int setoption_result = xio_setoption(handle->xio_handle, optionName, value);
1506 if (setoption_result != 0)
1507 {
1508 LogError("xio_setoption fails, returns %d", setoption_result);
1509 result = HTTP_CLIENT_ERROR;
1510 }
1511 else
1512 {
1513 result = HTTP_CLIENT_OK;
1514 }
1515
1516 }
1517
1518 return result;
1519}
1520
1521XIO_HANDLE uhttp_client_get_underlying_xio(HTTP_CLIENT_HANDLE handle)
1522{
1523 XIO_HANDLE result;
1524 if (handle == NULL)
1525 {
1526 LogError("invalid parameter handle: %p", handle);
1527 result = NULL;
1528 }
1529 else
1530 {
1531 result = handle->xio_handle;
1532 }
1533 return result;
1534}
Note: See TracBrowser for help on using the repository browser.