source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/http_proxy_io.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: 52.9 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 <stdio.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <limits.h>
9#include "azure_c_shared_utility/gballoc.h"
10#include "azure_c_shared_utility/xio.h"
11#include "azure_c_shared_utility/socketio.h"
12#include "azure_c_shared_utility/crt_abstractions.h"
13#include "azure_c_shared_utility/http_proxy_io.h"
14#include "azure_c_shared_utility/azure_base64.h"
15
16static const char* const OPTION_UNDERLYING_IO_OPTIONS = "underlying_io_options";
17
18typedef enum HTTP_PROXY_IO_STATE_TAG
19{
20 HTTP_PROXY_IO_STATE_CLOSED,
21 HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO,
22 HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE,
23 HTTP_PROXY_IO_STATE_OPEN,
24 HTTP_PROXY_IO_STATE_CLOSING,
25 HTTP_PROXY_IO_STATE_ERROR
26} HTTP_PROXY_IO_STATE;
27
28typedef struct HTTP_PROXY_IO_INSTANCE_TAG
29{
30 HTTP_PROXY_IO_STATE http_proxy_io_state;
31 ON_BYTES_RECEIVED on_bytes_received;
32 void* on_bytes_received_context;
33 ON_IO_ERROR on_io_error;
34 void* on_io_error_context;
35 ON_IO_OPEN_COMPLETE on_io_open_complete;
36 void* on_io_open_complete_context;
37 ON_IO_CLOSE_COMPLETE on_io_close_complete;
38 void* on_io_close_complete_context;
39 char* hostname;
40 int port;
41 char* proxy_hostname;
42 int proxy_port;
43 char* username;
44 char* password;
45 XIO_HANDLE underlying_io;
46 unsigned char* receive_buffer;
47 size_t receive_buffer_size;
48} HTTP_PROXY_IO_INSTANCE;
49
50static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters)
51{
52 HTTP_PROXY_IO_INSTANCE* result;
53
54 if (io_create_parameters == NULL)
55 {
56 /* Codes_SRS_HTTP_PROXY_IO_01_002: [ If io_create_parameters is NULL, http_proxy_io_create shall fail and return NULL. ]*/
57 result = NULL;
58 LogError("NULL io_create_parameters.");
59 }
60 else
61 {
62 /* Codes_SRS_HTTP_PROXY_IO_01_003: [ io_create_parameters shall be used as an HTTP_PROXY_IO_CONFIG*. ]*/
63 HTTP_PROXY_IO_CONFIG* http_proxy_io_config = (HTTP_PROXY_IO_CONFIG*)io_create_parameters;
64 if ((http_proxy_io_config->hostname == NULL) ||
65 (http_proxy_io_config->proxy_hostname == NULL))
66 {
67 /* Codes_SRS_HTTP_PROXY_IO_01_004: [ If the hostname or proxy_hostname member is NULL, then http_proxy_io_create shall fail and return NULL. ]*/
68 result = NULL;
69 LogError("Bad arguments: hostname = %p, proxy_hostname = %p",
70 http_proxy_io_config->hostname, http_proxy_io_config->proxy_hostname);
71 }
72 /* Codes_SRS_HTTP_PROXY_IO_01_095: [ If one of the fields username and password is non-NULL, then the other has to be also non-NULL, otherwise http_proxy_io_create shall fail and return NULL. ]*/
73 else if (((http_proxy_io_config->username == NULL) && (http_proxy_io_config->password != NULL)) ||
74 ((http_proxy_io_config->username != NULL) && (http_proxy_io_config->password == NULL)))
75 {
76 result = NULL;
77 LogError("Bad arguments: username = %p, password = %p",
78 http_proxy_io_config->username, http_proxy_io_config->password);
79 }
80 else
81 {
82 /* Codes_SRS_HTTP_PROXY_IO_01_001: [ http_proxy_io_create shall create a new instance of the HTTP proxy IO. ]*/
83 result = (HTTP_PROXY_IO_INSTANCE*)calloc(1, sizeof(HTTP_PROXY_IO_INSTANCE));
84 if (result == NULL)
85 {
86 /* Codes_SRS_HTTP_PROXY_IO_01_051: [ If allocating memory for the new instance fails, http_proxy_io_create shall fail and return NULL. ]*/
87 LogError("Failed allocating HTTP proxy IO instance.");
88 }
89 else
90 {
91 /* Codes_SRS_HTTP_PROXY_IO_01_005: [ http_proxy_io_create shall copy the hostname, port, username and password values for later use when the actual CONNECT is performed. ]*/
92 /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
93 if (mallocAndStrcpy_s(&result->hostname, http_proxy_io_config->hostname) != 0)
94 {
95 /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
96 LogError("Failed to copy the hostname.");
97 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
98 free(result);
99 result = NULL;
100 }
101 else
102 {
103 /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
104 if (mallocAndStrcpy_s(&result->proxy_hostname, http_proxy_io_config->proxy_hostname) != 0)
105 {
106 /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
107 LogError("Failed to copy the proxy_hostname.");
108 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
109 free(result->hostname);
110 free(result);
111 result = NULL;
112 }
113 else
114 {
115 result->username = NULL;
116 result->password = NULL;
117
118 /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
119 /* Codes_SRS_HTTP_PROXY_IO_01_094: [ username and password shall be optional. ]*/
120 if ((http_proxy_io_config->username != NULL) && (mallocAndStrcpy_s(&result->username, http_proxy_io_config->username) != 0))
121 {
122 /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
123 LogError("Failed to copy the username.");
124 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
125 free(result->proxy_hostname);
126 free(result->hostname);
127 free(result);
128 result = NULL;
129 }
130 else
131 {
132 /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
133 /* Codes_SRS_HTTP_PROXY_IO_01_094: [ username and password shall be optional. ]*/
134 if ((http_proxy_io_config->password != NULL) && (mallocAndStrcpy_s(&result->password, http_proxy_io_config->password) != 0))
135 {
136 /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
137 LogError("Failed to copy the passowrd.");
138 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
139 free(result->username);
140 free(result->proxy_hostname);
141 free(result->hostname);
142 free(result);
143 result = NULL;
144 }
145 else
146 {
147 /* Codes_SRS_HTTP_PROXY_IO_01_010: [ - io_interface_description shall be set to the result of socketio_get_interface_description. ]*/
148 const IO_INTERFACE_DESCRIPTION* underlying_io_interface = socketio_get_interface_description();
149 if (underlying_io_interface == NULL)
150 {
151 /* Codes_SRS_HTTP_PROXY_IO_01_050: [ If socketio_get_interface_description fails, http_proxy_io_create shall fail and return NULL. ]*/
152 LogError("Unable to get the socket IO interface description.");
153 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
154 free(result->password);
155 free(result->username);
156 free(result->proxy_hostname);
157 free(result->hostname);
158 free(result);
159 result = NULL;
160 }
161 else
162 {
163 SOCKETIO_CONFIG socket_io_config;
164
165 /* Codes_SRS_HTTP_PROXY_IO_01_011: [ - xio_create_parameters shall be set to a SOCKETIO_CONFIG* where hostname is set to the proxy_hostname member of io_create_parameters and port is set to the proxy_port member of io_create_parameters. ]*/
166 socket_io_config.hostname = http_proxy_io_config->proxy_hostname;
167 socket_io_config.port = http_proxy_io_config->proxy_port;
168 socket_io_config.accepted_socket = NULL;
169
170 /* Codes_SRS_HTTP_PROXY_IO_01_009: [ http_proxy_io_create shall create a new socket IO by calling xio_create with the arguments: ]*/
171 result->underlying_io = xio_create(underlying_io_interface, &socket_io_config);
172 if (result->underlying_io == NULL)
173 {
174 /* Codes_SRS_HTTP_PROXY_IO_01_012: [ If xio_create fails, http_proxy_io_create shall fail and return NULL. ]*/
175 LogError("Unable to create the underlying IO.");
176 /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
177 free(result->password);
178 free(result->username);
179 free(result->proxy_hostname);
180 free(result->hostname);
181 free(result);
182 result = NULL;
183 }
184 else
185 {
186 result->port = http_proxy_io_config->port;
187 result->proxy_port = http_proxy_io_config->proxy_port;
188 result->receive_buffer = NULL;
189 result->receive_buffer_size = 0;
190 result->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
191 }
192 }
193 }
194 }
195 }
196 }
197 }
198 }
199 }
200
201 return result;
202}
203
204static void http_proxy_io_destroy(CONCRETE_IO_HANDLE http_proxy_io)
205{
206 if (http_proxy_io == NULL)
207 {
208 /* Codes_SRS_HTTP_PROXY_IO_01_014: [ If http_proxy_io is NULL, http_proxy_io_destroy shall do nothing. ]*/
209 LogError("NULL http_proxy_io.");
210 }
211 else
212 {
213 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
214
215 /* Codes_SRS_HTTP_PROXY_IO_01_013: [ http_proxy_io_destroy shall free the HTTP proxy IO instance indicated by http_proxy_io. ]*/
216 if (http_proxy_io_instance->receive_buffer != NULL)
217 {
218 free(http_proxy_io_instance->receive_buffer);
219 }
220
221 /* Codes_SRS_HTTP_PROXY_IO_01_016: [ http_proxy_io_destroy shall destroy the underlying IO created in http_proxy_io_create by calling xio_destroy. ]*/
222 xio_destroy(http_proxy_io_instance->underlying_io);
223 free(http_proxy_io_instance->hostname);
224 free(http_proxy_io_instance->proxy_hostname);
225 free(http_proxy_io_instance->username);
226 free(http_proxy_io_instance->password);
227 free(http_proxy_io_instance);
228 }
229}
230
231static void indicate_open_complete_error_and_close(HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance)
232{
233 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
234 (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
235 http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
236}
237
238// This callback usage needs to be either verified and commented or integrated into
239// the state machine.
240static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result)
241{
242 (void)context;
243 (void)send_result;
244}
245
246static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result)
247{
248 if (context == NULL)
249 {
250 /* Codes_SRS_HTTP_PROXY_IO_01_081: [ on_underlying_io_open_complete called with NULL context shall do nothing. ]*/
251 LogError("NULL context in on_underlying_io_open_complete");
252 }
253 else
254 {
255 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
256 switch (http_proxy_io_instance->http_proxy_io_state)
257 {
258 default:
259 LogError("on_underlying_io_open_complete called in an unexpected state.");
260 break;
261
262 case HTTP_PROXY_IO_STATE_CLOSING:
263 case HTTP_PROXY_IO_STATE_OPEN:
264 /* Codes_SRS_HTTP_PROXY_IO_01_077: [ When on_underlying_io_open_complete is called in after OPEN has completed, the on_io_error callback shall be triggered passing the on_io_error_context argument as context. ]*/
265 http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context);
266 break;
267
268 case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
269 /* Codes_SRS_HTTP_PROXY_IO_01_076: [ When on_underlying_io_open_complete is called while waiting for the CONNECT reply, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
270 LogError("Open complete called again by underlying IO.");
271 indicate_open_complete_error_and_close(http_proxy_io_instance);
272 break;
273
274 case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
275 switch (open_result)
276 {
277 default:
278 case IO_OPEN_ERROR:
279 /* Codes_SRS_HTTP_PROXY_IO_01_078: [ When on_underlying_io_open_complete is called with IO_OPEN_ERROR, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
280 LogError("Underlying IO open failed");
281 indicate_open_complete_error_and_close(http_proxy_io_instance);
282 break;
283
284 case IO_OPEN_CANCELLED:
285 /* Codes_SRS_HTTP_PROXY_IO_01_079: [ When on_underlying_io_open_complete is called with IO_OPEN_CANCELLED, the on_open_complete callback shall be triggered with IO_OPEN_CANCELLED, passing also the on_open_complete_context argument as context. ]*/
286 LogError("Underlying IO open failed");
287 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
288 (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
289 http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED);
290 break;
291
292 case IO_OPEN_OK:
293 {
294 STRING_HANDLE encoded_auth_string;
295
296 /* Codes_SRS_HTTP_PROXY_IO_01_057: [ When on_underlying_io_open_complete is called, the http_proxy_io shall send the CONNECT request constructed per RFC 2817: ]*/
297 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE;
298
299 if (http_proxy_io_instance->username != NULL)
300 {
301 char* plain_auth_string_bytes;
302
303 /* Codes_SRS_HTTP_PROXY_IO_01_060: [ - The value of Proxy-Authorization shall be the constructed according to RFC 2617. ]*/
304 int plain_auth_string_length = (int)(strlen(http_proxy_io_instance->username)+1);
305 if (http_proxy_io_instance->password != NULL)
306 {
307 plain_auth_string_length += (int)strlen(http_proxy_io_instance->password);
308 }
309
310 if (plain_auth_string_length < 0)
311 {
312 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
313 encoded_auth_string = NULL;
314 indicate_open_complete_error_and_close(http_proxy_io_instance);
315 }
316 else
317 {
318 plain_auth_string_bytes = (char*)malloc(plain_auth_string_length + 1);
319 if (plain_auth_string_bytes == NULL)
320 {
321 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
322 encoded_auth_string = NULL;
323 indicate_open_complete_error_and_close(http_proxy_io_instance);
324 }
325 else
326 {
327 /* Codes_SRS_HTTP_PROXY_IO_01_091: [ To receive authorization, the client sends the userid and password, separated by a single colon (":") character, within a base64 [7] encoded string in the credentials. ]*/
328 /* Codes_SRS_HTTP_PROXY_IO_01_092: [ A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server. ]*/
329 /* Codes_SRS_HTTP_PROXY_IO_01_093: [ Userids might be case sensitive. ]*/
330 if (sprintf(plain_auth_string_bytes, "%s:%s", http_proxy_io_instance->username, (http_proxy_io_instance->password == NULL) ? "" : http_proxy_io_instance->password) < 0)
331 {
332 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
333 encoded_auth_string = NULL;
334 indicate_open_complete_error_and_close(http_proxy_io_instance);
335 }
336 else
337 {
338 /* Codes_SRS_HTTP_PROXY_IO_01_061: [ Encoding to Base64 shall be done by calling Azure_Base64_Encode_Bytes. ]*/
339 encoded_auth_string = Azure_Base64_Encode_Bytes((const unsigned char*)plain_auth_string_bytes, plain_auth_string_length);
340 if (encoded_auth_string == NULL)
341 {
342 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
343 LogError("Cannot Base64 encode auth string");
344 indicate_open_complete_error_and_close(http_proxy_io_instance);
345 }
346 }
347
348 free(plain_auth_string_bytes);
349 }
350 }
351 }
352 else
353 {
354 encoded_auth_string = NULL;
355 }
356
357 if ((http_proxy_io_instance->username != NULL) &&
358 (encoded_auth_string == NULL))
359 {
360 LogError("Cannot create authorization header");
361 }
362 else
363 {
364 int connect_request_length;
365 const char* auth_string_payload;
366 /* Codes_SRS_HTTP_PROXY_IO_01_075: [ The Request-URI portion of the Request-Line is always an 'authority' as defined by URI Generic Syntax [2], which is to say the host name and port number destination of the requested connection separated by a colon: ]*/
367 const char request_format[] = "CONNECT %s:%d HTTP/1.1\r\nHost:%s:%d%s%s\r\n\r\n";
368 const char proxy_basic[] = "\r\nProxy-authorization: Basic ";
369 if (http_proxy_io_instance->username != NULL)
370 {
371 auth_string_payload = STRING_c_str(encoded_auth_string);
372 }
373 else
374 {
375 auth_string_payload = "";
376 }
377
378 /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If username and password have been specified in the arguments passed to http_proxy_io_create, then the header Proxy-Authorization shall be added to the request. ]*/
379
380 connect_request_length = (int)(strlen(request_format)+(strlen(http_proxy_io_instance->hostname)*2)+strlen(auth_string_payload)+10);
381 if (http_proxy_io_instance->username != NULL)
382 {
383 connect_request_length += (int)strlen(proxy_basic);
384 }
385
386 if (connect_request_length < 0)
387 {
388 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
389 LogError("Cannot encode the CONNECT request");
390 indicate_open_complete_error_and_close(http_proxy_io_instance);
391 }
392 else
393 {
394 char* connect_request = (char*)malloc(connect_request_length + 1);
395 if (connect_request == NULL)
396 {
397 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
398 LogError("Cannot allocate memory for CONNECT request");
399 indicate_open_complete_error_and_close(http_proxy_io_instance);
400 }
401 else
402 {
403 /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If username and password have been specified in the arguments passed to http_proxy_io_create, then the header Proxy-Authorization shall be added to the request. ]*/
404 connect_request_length = sprintf(connect_request, request_format,
405 http_proxy_io_instance->hostname,
406 http_proxy_io_instance->port,
407 http_proxy_io_instance->hostname,
408 http_proxy_io_instance->port,
409 (http_proxy_io_instance->username != NULL) ? proxy_basic : "",
410 auth_string_payload);
411
412 if (connect_request_length < 0)
413 {
414 /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
415 LogError("Cannot encode the CONNECT request");
416 indicate_open_complete_error_and_close(http_proxy_io_instance);
417 }
418 else
419 {
420 /* Codes_SRS_HTTP_PROXY_IO_01_063: [ The request shall be sent by calling xio_send and passing NULL as on_send_complete callback. ]*/
421 if (xio_send(http_proxy_io_instance->underlying_io, connect_request, connect_request_length, unchecked_on_send_complete, NULL) != 0)
422 {
423 /* Codes_SRS_HTTP_PROXY_IO_01_064: [ If xio_send fails, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
424 LogError("Could not send CONNECT request");
425 indicate_open_complete_error_and_close(http_proxy_io_instance);
426 }
427 }
428
429 free(connect_request);
430 }
431 }
432 }
433
434 if (encoded_auth_string != NULL)
435 {
436 STRING_delete(encoded_auth_string);
437 }
438
439 break;
440 }
441 }
442
443 break;
444 }
445 }
446}
447
448static void on_underlying_io_error(void* context)
449{
450 if (context == NULL)
451 {
452 /* Codes_SRS_HTTP_PROXY_IO_01_088: [ on_underlying_io_error called with NULL context shall do nothing. ]*/
453 LogError("NULL context in on_underlying_io_error");
454 }
455 else
456 {
457 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
458
459 switch (http_proxy_io_instance->http_proxy_io_state)
460 {
461 default:
462 LogError("on_underlying_io_error in invalid state");
463 break;
464
465 case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
466 case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
467 /* Codes_SRS_HTTP_PROXY_IO_01_087: [ If the on_underlying_io_error callback is called while OPENING, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
468 indicate_open_complete_error_and_close(http_proxy_io_instance);
469 break;
470
471 case HTTP_PROXY_IO_STATE_OPEN:
472 /* Codes_SRS_HTTP_PROXY_IO_01_089: [ If the on_underlying_io_error callback is called while the IO is OPEN, the on_io_error callback shall be called with the on_io_error_context argument as context. ]*/
473 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_ERROR;
474 http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context);
475 break;
476 }
477 }
478}
479
480static void on_underlying_io_close_complete(void* context)
481{
482 if (context == NULL)
483 {
484 /* Cdoes_SRS_HTTP_PROXY_IO_01_084: [ on_underlying_io_close_complete called with NULL context shall do nothing. ]*/
485 LogError("NULL context in on_underlying_io_open_complete");
486 }
487 else
488 {
489 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
490
491 switch (http_proxy_io_instance->http_proxy_io_state)
492 {
493 default:
494 LogError("on_underlying_io_close_complete called in an invalid state");
495 break;
496
497 case HTTP_PROXY_IO_STATE_CLOSING:
498 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
499
500 /* Codes_SRS_HTTP_PROXY_IO_01_086: [ If the on_io_close_complete callback passed to http_proxy_io_close was NULL, no callback shall be triggered. ]*/
501 if (http_proxy_io_instance->on_io_close_complete != NULL)
502 {
503 /* Codes_SRS_HTTP_PROXY_IO_01_083: [ on_underlying_io_close_complete while CLOSING shall call the on_io_close_complete callback, passing to it the on_io_close_complete_context as context argument. ]*/
504 http_proxy_io_instance->on_io_close_complete(http_proxy_io_instance->on_io_close_complete_context);
505 }
506
507 break;
508 }
509 }
510}
511
512/*the following function does the same as sscanf(pos2, "%d", &sec)*/
513/*this function only exists because some of platforms do not have sscanf. */
514static int ParseStringToDecimal(const char *src, int* dst)
515{
516 int result;
517 char* next;
518
519 (*dst) = (int)strtol(src, &next, 0);
520 if ((src == next) || ((((*dst) == INT_MAX) || ((*dst) == INT_MIN)) && (errno != 0)))
521 {
522 result = __LINE__;
523 }
524 else
525 {
526 result = 0;
527 }
528
529 return result;
530}
531
532/*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */
533/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */
534static int ParseHttpResponse(const char* src, int* dst)
535{
536 int result;
537 static const char HTTPPrefix[] = "HTTP/";
538 bool fail;
539 const char* runPrefix;
540
541 if ((src == NULL) || (dst == NULL))
542 {
543 result = __LINE__;
544 }
545 else
546 {
547 fail = false;
548 runPrefix = HTTPPrefix;
549
550 while ((*runPrefix) != '\0')
551 {
552 if ((*runPrefix) != (*src))
553 {
554 fail = true;
555 break;
556 }
557 src++;
558 runPrefix++;
559 }
560
561 if (!fail)
562 {
563 while ((*src) != '.')
564 {
565 if ((*src) == '\0')
566 {
567 fail = true;
568 break;
569 }
570 src++;
571 }
572 }
573
574 if (!fail)
575 {
576 while ((*src) != ' ')
577 {
578 if ((*src) == '\0')
579 {
580 fail = true;
581 break;
582 }
583 src++;
584 }
585 }
586
587 if (fail)
588 {
589 result = __LINE__;
590 }
591 else
592 {
593 if (ParseStringToDecimal(src, dst) != 0)
594 {
595 result = __LINE__;
596 }
597 else
598 {
599 result = 0;
600 }
601 }
602 }
603
604 return result;
605}
606
607static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size)
608{
609 if (context == NULL)
610 {
611 /* Codes_SRS_HTTP_PROXY_IO_01_082: [ on_underlying_io_bytes_received called with NULL context shall do nothing. ]*/
612 LogError("NULL context in on_underlying_io_bytes_received");
613 }
614 else
615 {
616 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
617
618 switch (http_proxy_io_instance->http_proxy_io_state)
619 {
620 default:
621 case HTTP_PROXY_IO_STATE_CLOSING:
622 LogError("Bytes received in invalid state");
623 break;
624
625 case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
626 /* Codes_SRS_HTTP_PROXY_IO_01_080: [ If on_underlying_io_bytes_received is called while the underlying IO is being opened, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
627 LogError("Bytes received while opening underlying IO");
628 indicate_open_complete_error_and_close(http_proxy_io_instance);
629 break;
630
631 case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
632 {
633 /* Codes_SRS_HTTP_PROXY_IO_01_065: [ When bytes are received and the response to the CONNECT request was not yet received, the bytes shall be accumulated until a double new-line is detected. ]*/
634 unsigned char* new_receive_buffer = (unsigned char*)realloc(http_proxy_io_instance->receive_buffer, http_proxy_io_instance->receive_buffer_size + size + 1);
635 if (new_receive_buffer == NULL)
636 {
637 /* Codes_SRS_HTTP_PROXY_IO_01_067: [ If allocating memory for the buffered bytes fails, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
638 LogError("Cannot allocate memory for received data");
639 indicate_open_complete_error_and_close(http_proxy_io_instance);
640 }
641 else
642 {
643 http_proxy_io_instance->receive_buffer = new_receive_buffer;
644 memcpy(http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size, buffer, size);
645 http_proxy_io_instance->receive_buffer_size += size;
646 }
647
648 if (http_proxy_io_instance->receive_buffer_size >= 4)
649 {
650 const char* request_end_ptr;
651
652 http_proxy_io_instance->receive_buffer[http_proxy_io_instance->receive_buffer_size] = 0;
653
654 /* Codes_SRS_HTTP_PROXY_IO_01_066: [ When a double new-line is detected the response shall be parsed in order to extract the status code. ]*/
655 if ((http_proxy_io_instance->receive_buffer_size >= 4) &&
656 ((request_end_ptr = strstr((const char*)http_proxy_io_instance->receive_buffer, "\r\n\r\n")) != NULL))
657 {
658 int status_code;
659
660 /* This part should really be done with the HTTPAPI, but that has to be done as a separate step
661 as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */
662
663 if (ParseHttpResponse((const char*)http_proxy_io_instance->receive_buffer, &status_code) != 0)
664 {
665 /* Codes_SRS_HTTP_PROXY_IO_01_068: [ If parsing the CONNECT response fails, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
666 LogError("Cannot decode HTTP response");
667 indicate_open_complete_error_and_close(http_proxy_io_instance);
668 }
669 /* Codes_SRS_HTTP_PROXY_IO_01_069: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/
670 /* Codes_SRS_HTTP_PROXY_IO_01_090: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/
671 else if ((status_code < 200) || (status_code > 299))
672 {
673 /* Codes_SRS_HTTP_PROXY_IO_01_071: [ If the status code is not successful, the on_open_complete callback shall be triggered with IO_OPEN_ERROR, passing also the on_open_complete_context argument as context. ]*/
674 LogError("Bad status (%d) received in CONNECT response", status_code);
675 indicate_open_complete_error_and_close(http_proxy_io_instance);
676 }
677 else
678 {
679 size_t length_remaining = http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size - ((const unsigned char *)request_end_ptr + 4);
680
681 /* Codes_SRS_HTTP_PROXY_IO_01_073: [ Once a success status code was parsed, the IO shall be OPEN. ]*/
682 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPEN;
683 /* Codes_SRS_HTTP_PROXY_IO_01_070: [ When a success status code is parsed, the on_open_complete callback shall be triggered with IO_OPEN_OK, passing also the on_open_complete_context argument as context. ]*/
684 http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_OK);
685
686 if (length_remaining > 0)
687 {
688 /* Codes_SRS_HTTP_PROXY_IO_01_072: [ Any bytes that are extra (not consumed by the CONNECT response), shall be indicated as received by calling the on_bytes_received callback and passing the on_bytes_received_context as context argument. ]*/
689 http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, (const unsigned char*)request_end_ptr + 4, length_remaining);
690 }
691 }
692 }
693 }
694 break;
695 }
696 case HTTP_PROXY_IO_STATE_OPEN:
697 /* Codes_SRS_HTTP_PROXY_IO_01_074: [ If on_underlying_io_bytes_received is called while OPEN, all bytes shall be indicated as received by calling the on_bytes_received callback and passing the on_bytes_received_context as context argument. ]*/
698 http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, buffer, size);
699 break;
700 }
701 }
702}
703
704static int http_proxy_io_open(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context)
705{
706 int result;
707
708 /* Codes_SRS_HTTP_PROXY_IO_01_051: [ The arguments on_io_open_complete_context, on_bytes_received_context and on_io_error_context shall be allowed to be NULL. ]*/
709 /* Codes_SRS_HTTP_PROXY_IO_01_018: [ If any of the arguments http_proxy_io, on_io_open_complete, on_bytes_received or on_io_error are NULL then http_proxy_io_open shall return a non-zero value. ]*/
710 if ((http_proxy_io == NULL) ||
711 (on_io_open_complete == NULL) ||
712 (on_bytes_received == NULL) ||
713 (on_io_error == NULL))
714 {
715 LogError("Bad arguments: http_proxy_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error_context = %p.",
716 http_proxy_io,
717 on_io_open_complete,
718 on_bytes_received,
719 on_io_error);
720 result = __LINE__;
721 }
722 else
723 {
724 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
725
726 if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED)
727 {
728 LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_CLOSED.");
729 result = __LINE__;
730 }
731 else
732 {
733 http_proxy_io_instance->on_bytes_received = on_bytes_received;
734 http_proxy_io_instance->on_bytes_received_context = on_bytes_received_context;
735
736 http_proxy_io_instance->on_io_error = on_io_error;
737 http_proxy_io_instance->on_io_error_context = on_io_error_context;
738
739 http_proxy_io_instance->on_io_open_complete = on_io_open_complete;
740 http_proxy_io_instance->on_io_open_complete_context = on_io_open_complete_context;
741
742 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO;
743
744 /* Codes_SRS_HTTP_PROXY_IO_01_019: [ http_proxy_io_open shall open the underlying IO by calling xio_open on the underlying IO handle created in http_proxy_io_create, while passing to it the callbacks on_underlying_io_open_complete, on_underlying_io_bytes_received and on_underlying_io_error. ]*/
745 if (xio_open(http_proxy_io_instance->underlying_io, on_underlying_io_open_complete, http_proxy_io_instance, on_underlying_io_bytes_received, http_proxy_io_instance, on_underlying_io_error, http_proxy_io_instance) != 0)
746 {
747 /* Codes_SRS_HTTP_PROXY_IO_01_020: [ If xio_open fails, then http_proxy_io_open shall return a non-zero value. ]*/
748 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
749 LogError("Cannot open the underlying IO.");
750 result = __LINE__;
751 }
752 else
753 {
754 /* Codes_SRS_HTTP_PROXY_IO_01_017: [ http_proxy_io_open shall open the HTTP proxy IO and on success it shall return 0. ]*/
755 result = 0;
756 }
757 }
758 }
759
760 return result;
761}
762
763static int http_proxy_io_close(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context)
764{
765 int result = 0;
766
767 /* Codes_SRS_HTTP_PROXY_IO_01_052: [ on_io_close_complete_context shall be allowed to be NULL. ]*/
768 /* Codes_SRS_HTTP_PROXY_IO_01_028: [ on_io_close_complete shall be allowed to be NULL. ]*/
769 if (http_proxy_io == NULL)
770 {
771 /* Codes_SRS_HTTP_PROXY_IO_01_023: [ If the argument http_proxy_io is NULL, http_proxy_io_close shall fail and return a non-zero value. ]*/
772 result = __LINE__;
773 LogError("NULL http_proxy_io.");
774 }
775 else
776 {
777 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
778
779 /* Codes_SRS_HTTP_PROXY_IO_01_027: [ If http_proxy_io_close is called when not open, http_proxy_io_close shall fail and return a non-zero value. ]*/
780 if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSED) ||
781 /* Codes_SRS_HTTP_PROXY_IO_01_054: [ http_proxy_io_close while OPENING shall fail and return a non-zero value. ]*/
782 (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSING))
783 {
784 result = __LINE__;
785 LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_OPEN.");
786 }
787 else if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO) ||
788 (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE))
789 {
790 /* Codes_SRS_HTTP_PROXY_IO_01_053: [ http_proxy_io_close while OPENING shall trigger the on_io_open_complete callback with IO_OPEN_CANCELLED. ]*/
791 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
792 (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
793 http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED);
794
795 /* Codes_SRS_HTTP_PROXY_IO_01_022: [ http_proxy_io_close shall close the HTTP proxy IO and on success it shall return 0. ]*/
796 result = 0;
797 }
798 else
799 {
800 HTTP_PROXY_IO_STATE previous_state = http_proxy_io_instance->http_proxy_io_state;
801
802 http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSING;
803
804 /* Codes_SRS_HTTP_PROXY_IO_01_026: [ The on_io_close_complete and on_io_close_complete_context arguments shall be saved for later use. ]*/
805 http_proxy_io_instance->on_io_close_complete = on_io_close_complete;
806 http_proxy_io_instance->on_io_close_complete_context = on_io_close_complete_context;
807
808 /* Codes_SRS_HTTP_PROXY_IO_01_024: [ http_proxy_io_close shall close the underlying IO by calling xio_close on the IO handle create in http_proxy_io_create, while passing to it the on_underlying_io_close_complete callback. ]*/
809 if (xio_close(http_proxy_io_instance->underlying_io, on_underlying_io_close_complete, http_proxy_io_instance) != 0)
810 {
811 /* Codes_SRS_HTTP_PROXY_IO_01_025: [ If xio_close fails, http_proxy_io_close shall fail and return a non-zero value. ]*/
812 result = __LINE__;
813 http_proxy_io_instance->http_proxy_io_state = previous_state;
814 LogError("Cannot close underlying IO.");
815 }
816 else
817 {
818 /* Codes_SRS_HTTP_PROXY_IO_01_022: [ http_proxy_io_close shall close the HTTP proxy IO and on success it shall return 0. ]*/
819 result = 0;
820 }
821 }
822 }
823
824 return result;
825}
826
827static int http_proxy_io_send(CONCRETE_IO_HANDLE http_proxy_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* on_send_complete_context)
828{
829 int result;
830
831 /* Codes_SRS_HTTP_PROXY_IO_01_032: [ on_send_complete shall be allowed to be NULL. ]*/
832 /* Codes_SRS_HTTP_PROXY_IO_01_030: [ If any of the arguments http_proxy_io or buffer is NULL, http_proxy_io_send shall fail and return a non-zero value. ]*/
833 if ((http_proxy_io == NULL) ||
834 (buffer == NULL) ||
835 /* Codes_SRS_HTTP_PROXY_IO_01_031: [ If size is 0, http_proxy_io_send shall fail and return a non-zero value. ]*/
836 (size == 0))
837 {
838 result = __LINE__;
839 LogError("Bad arguments: http_proxy_io = %p, buffer = %p.",
840 http_proxy_io, buffer);
841 }
842 else
843 {
844 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
845
846 /* Codes_SRS_HTTP_PROXY_IO_01_034: [ If http_proxy_io_send is called when the IO is not open, http_proxy_io_send shall fail and return a non-zero value. ]*/
847 /* Codes_SRS_HTTP_PROXY_IO_01_035: [ If the IO is in an error state (an error was reported through the on_io_error callback), http_proxy_io_send shall fail and return a non-zero value. ]*/
848 if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_OPEN)
849 {
850 result = __LINE__;
851 LogError("Invalid HTTP proxy IO state. Expected state is HTTP_PROXY_IO_STATE_OPEN.");
852 }
853 else
854 {
855 /* Codes_SRS_HTTP_PROXY_IO_01_033: [ http_proxy_io_send shall send the bytes by calling xio_send on the underlying IO created in http_proxy_io_create and passing buffer and size as arguments. ]*/
856 if (xio_send(http_proxy_io_instance->underlying_io, buffer, size, on_send_complete, on_send_complete_context) != 0)
857 {
858 /* Codes_SRS_HTTP_PROXY_IO_01_055: [ If xio_send fails, http_proxy_io_send shall fail and return a non-zero value. ]*/
859 result = __LINE__;
860 LogError("Underlying xio_send failed.");
861 }
862 else
863 {
864 /* Codes_SRS_HTTP_PROXY_IO_01_029: [ http_proxy_io_send shall send the size bytes pointed to by buffer and on success it shall return 0. ]*/
865 result = 0;
866 }
867 }
868 }
869
870 return result;
871}
872
873static void http_proxy_io_dowork(CONCRETE_IO_HANDLE http_proxy_io)
874{
875 if (http_proxy_io == NULL)
876 {
877 /* Codes_SRS_HTTP_PROXY_IO_01_038: [ If the http_proxy_io argument is NULL, http_proxy_io_dowork shall do nothing. ]*/
878 LogError("NULL http_proxy_io.");
879 }
880 else
881 {
882 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
883
884 if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED)
885 {
886 /* Codes_SRS_HTTP_PROXY_IO_01_037: [ http_proxy_io_dowork shall call xio_dowork on the underlying IO created in http_proxy_io_create. ]*/
887 xio_dowork(http_proxy_io_instance->underlying_io);
888 }
889 }
890}
891
892static int http_proxy_io_set_option(CONCRETE_IO_HANDLE http_proxy_io, const char* option_name, const void* value)
893{
894 int result;
895
896 if ((http_proxy_io == NULL) || (option_name == NULL))
897 {
898 /* Codes_SRS_HTTP_PROXY_IO_01_040: [ If any of the arguments http_proxy_io or option_name is NULL, http_proxy_io_set_option shall return a non-zero value. ]*/
899 LogError("Bad arguments: http_proxy_io = %p, option_name = %p",
900 http_proxy_io, option_name);
901 result = MU_FAILURE;
902 }
903 else
904 {
905 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
906
907 /* Codes_SRS_HTTP_PROXY_IO_01_045: [ None. ]*/
908
909 if (strcmp(option_name, OPTION_UNDERLYING_IO_OPTIONS) == 0)
910 {
911 if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, (void*)http_proxy_io_instance->underlying_io) != OPTIONHANDLER_OK)
912 {
913 LogError("failed feeding options to underlying I/O instance");
914 result = MU_FAILURE;
915 }
916 else
917 {
918 result = 0;
919 }
920 }
921 /* Codes_SRS_HTTP_PROXY_IO_01_043: [ If the option_name argument indicates an option that is not handled by http_proxy_io_set_option, then xio_setoption shall be called on the underlying IO created in http_proxy_io_create, passing the option name and value to it. ]*/
922 /* Codes_SRS_HTTP_PROXY_IO_01_056: [ The value argument shall be allowed to be NULL. ]*/
923 else if (xio_setoption(http_proxy_io_instance->underlying_io, option_name, value) != 0)
924 {
925 /* Codes_SRS_HTTP_PROXY_IO_01_044: [ if xio_setoption fails, http_proxy_io_set_option shall return a non-zero value. ]*/
926 LogError("Unrecognized option %s", option_name);
927 result = MU_FAILURE;
928 }
929 else
930 {
931 /* Codes_SRS_HTTP_PROXY_IO_01_042: [ If the option was handled by http_proxy_io_set_option or the underlying IO, then http_proxy_io_set_option shall return 0. ]*/
932 result = 0;
933 }
934 }
935
936 return result;
937}
938
939/*this function will clone an option given by name and value*/
940static void* http_proxy_io_clone_option(const char* name, const void* value)
941{
942 void* result;
943 if (
944 (name == NULL) || (value == NULL)
945 )
946 {
947 LogError("invalid parameter detected: name=%p, value=%p", name, value);
948 result = NULL;
949 }
950 else
951 {
952 if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0)
953 {
954 result = (void*)value;
955 }
956 else
957 {
958 LogError("not handled option : %s", name);
959 result = NULL;
960 }
961 }
962
963 return result;
964}
965
966/*this function destroys an option previously created*/
967static void http_proxy_io_destroy_option(const char* name, const void* value)
968{
969 /*since all options for this layer are actually string copies., disposing of one is just calling free*/
970 if (
971 (name == NULL) || (value == NULL)
972 )
973 {
974 LogError("invalid parameter detected: const char* name=%p, const void* value=%p", name, value);
975 }
976 else
977 {
978 if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0)
979 {
980 OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value);
981 }
982 else
983 {
984 LogError("not handled option : %s", name);
985 }
986 }
987}
988
989static OPTIONHANDLER_HANDLE http_proxy_io_retrieve_options(CONCRETE_IO_HANDLE http_proxy_io)
990{
991 OPTIONHANDLER_HANDLE result;
992
993 if (http_proxy_io == NULL)
994 {
995 /* Codes_SRS_HTTP_PROXY_IO_01_047: [ If the parameter http_proxy_io is NULL then http_proxy_io_retrieve_options shall fail and return NULL. ]*/
996 LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle=%p", http_proxy_io);
997 result = NULL;
998 }
999 else
1000 {
1001 HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
1002
1003 result = OptionHandler_Create(http_proxy_io_clone_option, http_proxy_io_destroy_option, http_proxy_io_set_option);
1004
1005 if (result == NULL)
1006 {
1007 LogError("OptionHandler_Create failed");
1008 }
1009 else
1010 {
1011 OPTIONHANDLER_HANDLE underlying_io_options;
1012
1013 /* Codes_SRS_HTTP_PROXY_IO_01_046: [ http_proxy_io_retrieve_options shall return an OPTIONHANDLER_HANDLE obtained by calling xio_retrieveoptions on the underlying IO created in http_proxy_io_create. ]*/
1014 if ((underlying_io_options = xio_retrieveoptions(http_proxy_io_instance->underlying_io)) == NULL ||
1015 OptionHandler_AddOption(result, OPTION_UNDERLYING_IO_OPTIONS, underlying_io_options) != OPTIONHANDLER_OK)
1016 {
1017 /* Codes_SRS_HTTP_PROXY_IO_01_048: [ If xio_retrieveoptions fails, http_proxy_io_retrieve_options shall return NULL. ]*/
1018 LogError("unable to save underlying_io options");
1019 OptionHandler_Destroy(underlying_io_options);
1020 OptionHandler_Destroy(result);
1021 result = NULL;
1022 }
1023 else
1024 {
1025 // All is fine.
1026 }
1027 }
1028 }
1029 return result;
1030}
1031
1032static const IO_INTERFACE_DESCRIPTION http_proxy_io_interface_description =
1033{
1034 http_proxy_io_retrieve_options,
1035 http_proxy_io_create,
1036 http_proxy_io_destroy,
1037 http_proxy_io_open,
1038 http_proxy_io_close,
1039 http_proxy_io_send,
1040 http_proxy_io_dowork,
1041 http_proxy_io_set_option
1042};
1043
1044const IO_INTERFACE_DESCRIPTION* http_proxy_io_get_interface_description(void)
1045{
1046 /* Codes_SRS_HTTP_PROXY_IO_01_049: [ http_proxy_io_get_interface_description shall return a pointer to an IO_INTERFACE_DESCRIPTION structure that contains pointers to the functions: http_proxy_io_retrieve_options, http_proxy_io_retrieve_create, http_proxy_io_destroy, http_proxy_io_open, http_proxy_io_close, http_proxy_io_send and http_proxy_io_dowork. ]*/
1047 return &http_proxy_io_interface_description;
1048}
Note: See TracBrowser for help on using the repository browser.