source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/uhttp/src/uhttp.c@ 457

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

ファイルを追加

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