text: // Copyright (c) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdbool.h>
  #include <stdint.h>
  #include <limits.h>
  #include <stddef.h>
  #include "azure_c_shared_utility/gballoc.h"
  #include "azure_c_shared_utility/xio.h"
  #include "azure_c_shared_utility/socketio.h"
  #include "azure_c_shared_utility/crt_abstractions.h"
  #include "azure_c_shared_utility/http_proxy_io.h"
  #include "azure_c_shared_utility/azure_base64.h"
  
  typedef enum HTTP_PROXY_IO_STATE_TAG
  {
      HTTP_PROXY_IO_STATE_CLOSED,
      HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO,
      HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE,
      HTTP_PROXY_IO_STATE_OPEN,
      HTTP_PROXY_IO_STATE_CLOSING,
      HTTP_PROXY_IO_STATE_ERROR
  } HTTP_PROXY_IO_STATE;
  
  typedef struct HTTP_PROXY_IO_INSTANCE_TAG
  {
      HTTP_PROXY_IO_STATE http_proxy_io_state;
      ON_BYTES_RECEIVED on_bytes_received;
      void* on_bytes_received_context;
      ON_IO_ERROR on_io_error;
      void* on_io_error_context;
      ON_IO_OPEN_COMPLETE on_io_open_complete;
      void* on_io_open_complete_context;
      ON_IO_CLOSE_COMPLETE on_io_close_complete;
      void* on_io_close_complete_context;
      char* hostname;
      int port;
      char* proxy_hostname;
      int proxy_port;
      char* username;
      char* password;
      XIO_HANDLE underlying_io;
      unsigned char* receive_buffer;
      size_t receive_buffer_size;
  } HTTP_PROXY_IO_INSTANCE;
  
  static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters)
  {
      HTTP_PROXY_IO_INSTANCE* result;
  
      if (io_create_parameters == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_002: [ If io_create_parameters is NULL, http_proxy_io_create shall fail and return NULL. ]*/
          result = NULL;
          LogError("NULL io_create_parameters.");
      }
      else
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_003: [ io_create_parameters shall be used as an HTTP_PROXY_IO_CONFIG*. ]*/
          HTTP_PROXY_IO_CONFIG* http_proxy_io_config = (HTTP_PROXY_IO_CONFIG*)io_create_parameters;
          if ((http_proxy_io_config->hostname == NULL) ||
              (http_proxy_io_config->proxy_hostname == NULL))
          {
              /* 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. ]*/
              result = NULL;
              LogError("Bad arguments: hostname = %p, proxy_hostname = %p",
                  http_proxy_io_config->hostname, http_proxy_io_config->proxy_hostname);
          }
          /* 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. ]*/
          else if (((http_proxy_io_config->username == NULL) && (http_proxy_io_config->password != NULL)) ||
              ((http_proxy_io_config->username != NULL) && (http_proxy_io_config->password == NULL)))
          {
              result = NULL;
              LogError("Bad arguments: username = %p, password = %p",
                  http_proxy_io_config->username, http_proxy_io_config->password);
          }
          else
          {
              /* Codes_SRS_HTTP_PROXY_IO_01_001: [ http_proxy_io_create shall create a new instance of the HTTP proxy IO. ]*/
              result = (HTTP_PROXY_IO_INSTANCE*)malloc(sizeof(HTTP_PROXY_IO_INSTANCE));
              if (result == NULL)
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_051: [ If allocating memory for the new instance fails, http_proxy_io_create shall fail and return NULL. ]*/
                  LogError("Failed allocating HTTP proxy IO instance.");
              }
              else
              {
                  /* 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. ]*/
                  /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
                  if (mallocAndStrcpy_s(&result->hostname, http_proxy_io_config->hostname) != 0)
                  {
                      /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
                      LogError("Failed to copy the hostname.");
                      /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                      free(result);
                      result = NULL;
                  }
                  else
                  {
                      /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
                      if (mallocAndStrcpy_s(&result->proxy_hostname, http_proxy_io_config->proxy_hostname) != 0)
                      {
                          /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
                          LogError("Failed to copy the proxy_hostname.");
                          /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                          free(result->hostname);
                          free(result);
                          result = NULL;
                      }
                      else
                      {
                          result->username = NULL;
                          result->password = NULL;
  
                          /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
                          /* Codes_SRS_HTTP_PROXY_IO_01_094: [ username and password shall be optional. ]*/
                          if ((http_proxy_io_config->username != NULL) && (mallocAndStrcpy_s(&result->username, http_proxy_io_config->username) != 0))
                          {
                              /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
                              LogError("Failed to copy the username.");
                              /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                              free(result->proxy_hostname);
                              free(result->hostname);
                              free(result);
                              result = NULL;
                          }
                          else
                          {
                              /* Codes_SRS_HTTP_PROXY_IO_01_006: [ hostname and proxy_hostname, username and password shall be copied by calling mallocAndStrcpy_s. ]*/
                              /* Codes_SRS_HTTP_PROXY_IO_01_094: [ username and password shall be optional. ]*/
                              if ((http_proxy_io_config->password != NULL) && (mallocAndStrcpy_s(&result->password, http_proxy_io_config->password) != 0))
                              {
                                  /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If mallocAndStrcpy_s fails then http_proxy_io_create shall fail and return NULL. ]*/
                                  LogError("Failed to copy the passowrd.");
                                  /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                                  free(result->username);
                                  free(result->proxy_hostname);
                                  free(result->hostname);
                                  free(result);
                                  result = NULL;
                              }
                              else
                              {
                                  /* Codes_SRS_HTTP_PROXY_IO_01_010: [ - io_interface_description shall be set to the result of socketio_get_interface_description. ]*/
                                  const IO_INTERFACE_DESCRIPTION* underlying_io_interface = socketio_get_interface_description();
                                  if (underlying_io_interface == NULL)
                                  {
                                      /* Codes_SRS_HTTP_PROXY_IO_01_050: [ If socketio_get_interface_description fails, http_proxy_io_create shall fail and return NULL. ]*/
                                      LogError("Unable to get the socket IO interface description.");
                                      /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                                      free(result->password);
                                      free(result->username);
                                      free(result->proxy_hostname);
                                      free(result->hostname);
                                      free(result);
                                      result = NULL;
                                  }
                                  else
                                  {
                                      SOCKETIO_CONFIG socket_io_config;
  
                                      /* 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. ]*/
                                      socket_io_config.hostname = http_proxy_io_config->proxy_hostname;
                                      socket_io_config.port = http_proxy_io_config->proxy_port;
                                      socket_io_config.accepted_socket = NULL;
  
                                      /* Codes_SRS_HTTP_PROXY_IO_01_009: [ http_proxy_io_create shall create a new socket IO by calling xio_create with the arguments: ]*/
                                      result->underlying_io = xio_create(underlying_io_interface, &socket_io_config);
                                      if (result->underlying_io == NULL)
                                      {
                                          /* Codes_SRS_HTTP_PROXY_IO_01_012: [ If xio_create fails, http_proxy_io_create shall fail and return NULL. ]*/
                                          LogError("Unable to create the underlying IO.");
                                          /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When http_proxy_io_create fails, all allocated resources up to that point shall be freed. ]*/
                                          free(result->password);
                                          free(result->username);
                                          free(result->proxy_hostname);
                                          free(result->hostname);
                                          free(result);
                                          result = NULL;
                                      }
                                      else
                                      {
                                          result->port = http_proxy_io_config->port;
                                          result->proxy_port = http_proxy_io_config->proxy_port;
                                          result->receive_buffer = NULL;
                                          result->receive_buffer_size = 0;
                                          result->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
          }
      }
  
      return result;
  }
  
  static void http_proxy_io_destroy(CONCRETE_IO_HANDLE http_proxy_io)
  {
      if (http_proxy_io == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_014: [ If http_proxy_io is NULL, http_proxy_io_destroy shall do nothing. ]*/
          LogError("NULL http_proxy_io.");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          /* Codes_SRS_HTTP_PROXY_IO_01_013: [ http_proxy_io_destroy shall free the HTTP proxy IO instance indicated by http_proxy_io. ]*/
          if (http_proxy_io_instance->receive_buffer != NULL)
          {
              free(http_proxy_io_instance->receive_buffer);
          }
  
          /* 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. ]*/
          xio_destroy(http_proxy_io_instance->underlying_io);
          free(http_proxy_io_instance->hostname);
          free(http_proxy_io_instance->proxy_hostname);
          free(http_proxy_io_instance->username);
          free(http_proxy_io_instance->password);
          free(http_proxy_io_instance);
      }
  }
  
  static void indicate_open_complete_error_and_close(HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance)
  {
      http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
      (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
      http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
  }
  
  // This callback usage needs to be either verified and commented or integrated into
  // the state machine.
  static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result)
  {
      (void)context;
      (void)send_result;
  }
  
  static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result)
  {
      if (context == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_081: [ on_underlying_io_open_complete called with NULL context shall do nothing. ]*/
          LogError("NULL context in on_underlying_io_open_complete");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
          switch (http_proxy_io_instance->http_proxy_io_state)
          {
          default:
              LogError("on_underlying_io_open_complete called in an unexpected state.");
              break;
  
          case HTTP_PROXY_IO_STATE_CLOSING:
          case HTTP_PROXY_IO_STATE_OPEN:
              /* 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. ]*/
              http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context);
              break;
  
          case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
              /* 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. ]*/
              LogError("Open complete called again by underlying IO.");
              indicate_open_complete_error_and_close(http_proxy_io_instance);
              break;
  
          case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
              switch (open_result)
              {
              default:
              case IO_OPEN_ERROR:
                  /* 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. ]*/
                  LogError("Underlying IO open failed");
                  indicate_open_complete_error_and_close(http_proxy_io_instance);
                  break;
  
              case IO_OPEN_CANCELLED:
                  /* 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. ]*/
                  LogError("Underlying IO open failed");
                  http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
                  (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
                  http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED);
                  break;
  
              case IO_OPEN_OK:
              {
                  STRING_HANDLE encoded_auth_string;
  
                  /* 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: ]*/
                  http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE;
  
                  if (http_proxy_io_instance->username != NULL)
                  {
                      char* plain_auth_string_bytes;
  
                      /* Codes_SRS_HTTP_PROXY_IO_01_060: [ - The value of Proxy-Authorization shall be the constructed according to RFC 2617. ]*/
                      int plain_auth_string_length = (int)(strlen(http_proxy_io_instance->username)+1);
                      if (http_proxy_io_instance->password != NULL)
                      {
                          plain_auth_string_length += (int)strlen(http_proxy_io_instance->password);
                      }
  
                      if (plain_auth_string_length < 0)
                      {
                          /* 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. ]*/
                          encoded_auth_string = NULL;
                          indicate_open_complete_error_and_close(http_proxy_io_instance);
                      }
                      else
                      {
                          plain_auth_string_bytes = (char*)malloc(plain_auth_string_length + 1);
                          if (plain_auth_string_bytes == NULL)
                          {
                              /* 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. ]*/
                              encoded_auth_string = NULL;
                              indicate_open_complete_error_and_close(http_proxy_io_instance);
                          }
                          else
                          {
                              /* 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. ]*/
                              /* 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. ]*/
                              /* Codes_SRS_HTTP_PROXY_IO_01_093: [ Userids might be case sensitive. ]*/
                              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)
                              {
                                  /* 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. ]*/
                                  encoded_auth_string = NULL;
                                  indicate_open_complete_error_and_close(http_proxy_io_instance);
                              }
                              else
                              {
                                  /* Codes_SRS_HTTP_PROXY_IO_01_061: [ Encoding to Base64 shall be done by calling Azure_Base64_Encode_Bytes. ]*/
                                  encoded_auth_string = Azure_Base64_Encode_Bytes((const unsigned char*)plain_auth_string_bytes, plain_auth_string_length);
                                  if (encoded_auth_string == NULL)
                                  {
                                      /* 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. ]*/
                                      LogError("Cannot Base64 encode auth string");
                                      indicate_open_complete_error_and_close(http_proxy_io_instance);
                                  }
                              }
  
                              free(plain_auth_string_bytes);
                          }
                      }
                  }
                  else
                  {
                      encoded_auth_string = NULL;
                  }
  
                  if ((http_proxy_io_instance->username != NULL) &&
                      (encoded_auth_string == NULL))
                  {
                      LogError("Cannot create authorization header");
                  }
                  else
                  {
                      int connect_request_length;
                      const char* auth_string_payload;
                      /* 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: ]*/
                      const char request_format[] = "CONNECT %s:%d HTTP/1.1\r\nHost:%s:%d%s%s\r\n\r\n";
                      const char proxy_basic[] = "\r\nProxy-authorization: Basic ";
                      if (http_proxy_io_instance->username != NULL)
                      {
                          auth_string_payload = STRING_c_str(encoded_auth_string);
                      }
                      else
                      {
                          auth_string_payload = "";
                      }
  
                      /* 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. ]*/
  
                      connect_request_length = (int)(strlen(request_format)+(strlen(http_proxy_io_instance->hostname)*2)+strlen(auth_string_payload)+10);
                      if (http_proxy_io_instance->username != NULL)
                      {
                          connect_request_length += (int)strlen(proxy_basic);
                      }
  
                      if (connect_request_length < 0)
                      {
                          /* 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. ]*/
                          LogError("Cannot encode the CONNECT request");
                          indicate_open_complete_error_and_close(http_proxy_io_instance);
                      }
                      else
                      {
                          char* connect_request = (char*)malloc(connect_request_length + 1);
                          if (connect_request == NULL)
                          {
                              /* 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. ]*/
                              LogError("Cannot allocate memory for CONNECT request");
                              indicate_open_complete_error_and_close(http_proxy_io_instance);
                          }
                          else
                          {
                              /* 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. ]*/
                              connect_request_length = sprintf(connect_request, request_format,
                                  http_proxy_io_instance->hostname,
                                  http_proxy_io_instance->port,
                                  http_proxy_io_instance->hostname,
                                  http_proxy_io_instance->port,
                                  (http_proxy_io_instance->username != NULL) ? proxy_basic : "",
                                  auth_string_payload);
  
                              if (connect_request_length < 0)
                              {
                                  /* 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. ]*/
                                  LogError("Cannot encode the CONNECT request");
                                  indicate_open_complete_error_and_close(http_proxy_io_instance);
                              }
                              else
                              {
                                  /* Codes_SRS_HTTP_PROXY_IO_01_063: [ The request shall be sent by calling xio_send and passing NULL as on_send_complete callback. ]*/
                                  if (xio_send(http_proxy_io_instance->underlying_io, connect_request, connect_request_length, unchecked_on_send_complete, NULL) != 0)
                                  {
                                      /* 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. ]*/
                                      LogError("Could not send CONNECT request");
                                      indicate_open_complete_error_and_close(http_proxy_io_instance);
                                  }
                              }
  
                              free(connect_request);
                          }
                      }
                  }
  
                  if (encoded_auth_string != NULL)
                  {
                      STRING_delete(encoded_auth_string);
                  }
  
                  break;
              }
              }
  
              break;
          }
      }
  }
  
  static void on_underlying_io_error(void* context)
  {
      if (context == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_088: [ on_underlying_io_error called with NULL context shall do nothing. ]*/
          LogError("NULL context in on_underlying_io_error");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
  
          switch (http_proxy_io_instance->http_proxy_io_state)
          {
          default:
              LogError("on_underlying_io_error in invalid state");
              break;
  
          case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
          case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
              /* 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. ]*/
              indicate_open_complete_error_and_close(http_proxy_io_instance);
              break;
  
          case HTTP_PROXY_IO_STATE_OPEN:
              /* 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. ]*/
              http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_ERROR;
              http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context);
              break;
          }
      }
  }
  
  static void on_underlying_io_close_complete(void* context)
  {
      if (context == NULL)
      {
          /* Cdoes_SRS_HTTP_PROXY_IO_01_084: [ on_underlying_io_close_complete called with NULL context shall do nothing. ]*/
          LogError("NULL context in on_underlying_io_open_complete");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
  
          switch (http_proxy_io_instance->http_proxy_io_state)
          {
          default:
              LogError("on_underlying_io_close_complete called in an invalid state");
              break;
  
          case HTTP_PROXY_IO_STATE_CLOSING:
              http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
  
              /* 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. ]*/
              if (http_proxy_io_instance->on_io_close_complete != NULL)
              {
                  /* 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. ]*/
                  http_proxy_io_instance->on_io_close_complete(http_proxy_io_instance->on_io_close_complete_context);
              }
  
              break;
          }
      }
  }
  
  /*the following function does the same as sscanf(pos2, "%d", &sec)*/
  /*this function only exists because some of platforms do not have sscanf. */
  static int ParseStringToDecimal(const char *src, int* dst)
  {
      int result;
      char* next;
  
      (*dst) = (int)strtol(src, &next, 0);
      if ((src == next) || ((((*dst) == INT_MAX) || ((*dst) == INT_MIN)) && (errno != 0)))
      {
          result = __LINE__;
      }
      else
      {
          result = 0;
      }
  
      return result;
  }
  
  /*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */
  /*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. */
  static int ParseHttpResponse(const char* src, int* dst)
  {
      int result;
      static const char HTTPPrefix[] = "HTTP/";
      bool fail;
      const char* runPrefix;
  
      if ((src == NULL) || (dst == NULL))
      {
          result = __LINE__;
      }
      else
      {
          fail = false;
          runPrefix = HTTPPrefix;
  
          while ((*runPrefix) != '\0')
          {
              if ((*runPrefix) != (*src))
              {
                  fail = true;
                  break;
              }
              src++;
              runPrefix++;
          }
  
          if (!fail)
          {
              while ((*src) != '.')
              {
                  if ((*src) == '\0')
                  {
                      fail = true;
                      break;
                  }
                  src++;
              }
          }
  
          if (!fail)
          {
              while ((*src) != ' ')
              {
                  if ((*src) == '\0')
                  {
                      fail = true;
                      break;
                  }
                  src++;
              }
          }
  
          if (fail)
          {
              result = __LINE__;
          }
          else
          {
              if (ParseStringToDecimal(src, dst) != 0)
              {
                  result = __LINE__;
              }
              else
              {
                  result = 0;
              }
          }
      }
  
      return result;
  }
  
  static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size)
  {
      if (context == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_082: [ on_underlying_io_bytes_received called with NULL context shall do nothing. ]*/
          LogError("NULL context in on_underlying_io_bytes_received");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context;
  
          switch (http_proxy_io_instance->http_proxy_io_state)
          {
          default:
          case HTTP_PROXY_IO_STATE_CLOSING:
              LogError("Bytes received in invalid state");
              break;
  
          case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO:
              /* 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. ]*/
              LogError("Bytes received while opening underlying IO");
              indicate_open_complete_error_and_close(http_proxy_io_instance);
              break;
  
          case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE:
          {
              /* 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. ]*/
              unsigned char* new_receive_buffer = (unsigned char*)realloc(http_proxy_io_instance->receive_buffer, http_proxy_io_instance->receive_buffer_size + size + 1);
              if (new_receive_buffer == NULL)
              {
                  /* 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. ]*/
                  LogError("Cannot allocate memory for received data");
                  indicate_open_complete_error_and_close(http_proxy_io_instance);
              }
              else
              {
                  http_proxy_io_instance->receive_buffer = new_receive_buffer;
                  memcpy(http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size, buffer, size);
                  http_proxy_io_instance->receive_buffer_size += size;
              }
  
              if (http_proxy_io_instance->receive_buffer_size >= 4)
              {
                  const char* request_end_ptr;
  
                  http_proxy_io_instance->receive_buffer[http_proxy_io_instance->receive_buffer_size] = 0;
  
                  /* 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. ]*/
                  if ((http_proxy_io_instance->receive_buffer_size >= 4) &&
                      ((request_end_ptr = strstr((const char*)http_proxy_io_instance->receive_buffer, "\r\n\r\n")) != NULL))
                  {
                      int status_code;
  
                      /* This part should really be done with the HTTPAPI, but that has to be done as a separate step
                      as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */
  
                      if (ParseHttpResponse((const char*)http_proxy_io_instance->receive_buffer, &status_code) != 0)
                      {
                          /* 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. ]*/
                          LogError("Cannot decode HTTP response");
                          indicate_open_complete_error_and_close(http_proxy_io_instance);
                      }
                      /* 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. ]*/
                      /* 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. ]*/
                      else if ((status_code < 200) || (status_code > 299))
                      {
                          /* 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. ]*/
                          LogError("Bad status (%d) received in CONNECT response", status_code);
                          indicate_open_complete_error_and_close(http_proxy_io_instance);
                      }
                      else
                      {
                          size_t length_remaining = http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size - ((const unsigned char *)request_end_ptr + 4);
  
                          /* Codes_SRS_HTTP_PROXY_IO_01_073: [ Once a success status code was parsed, the IO shall be OPEN. ]*/
                          http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPEN;
                          /* 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. ]*/
                          http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_OK);
  
                          if (length_remaining > 0)
                          {
                              /* 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. ]*/
                              http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, (const unsigned char*)request_end_ptr + 4, length_remaining);
                          }
                      }
                  }
              }
              break;
          }
          case HTTP_PROXY_IO_STATE_OPEN:
              /* 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. ]*/
              http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, buffer, size);
              break;
          }
      }
  }
  
  static 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)
  {
      int result;
  
      /* 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. ]*/
      /* 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. ]*/
      if ((http_proxy_io == NULL) ||
          (on_io_open_complete == NULL) ||
          (on_bytes_received == NULL) ||
          (on_io_error == NULL))
      {
          LogError("Bad arguments: http_proxy_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error_context = %p.",
              http_proxy_io,
              (void*)on_io_open_complete,
              (void*)on_bytes_received,
              (void*)on_io_error);
          result = __LINE__;
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED)
          {
              LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_CLOSED.");
              result = __LINE__;
          }
          else
          {
              http_proxy_io_instance->on_bytes_received = on_bytes_received;
              http_proxy_io_instance->on_bytes_received_context = on_bytes_received_context;
  
              http_proxy_io_instance->on_io_error = on_io_error;
              http_proxy_io_instance->on_io_error_context = on_io_error_context;
  
              http_proxy_io_instance->on_io_open_complete = on_io_open_complete;
              http_proxy_io_instance->on_io_open_complete_context = on_io_open_complete_context;
  
              http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO;
  
              /* 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. ]*/
              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)
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_020: [ If xio_open fails, then http_proxy_io_open shall return a non-zero value. ]*/
                  http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
                  LogError("Cannot open the underlying IO.");
                  result = __LINE__;
              }
              else
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_017: [ http_proxy_io_open shall open the HTTP proxy IO and on success it shall return 0. ]*/
                  result = 0;
              }
          }
      }
  
      return result;
  }
  
  static 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)
  {
      int result = 0;
  
      /* Codes_SRS_HTTP_PROXY_IO_01_052: [ on_io_close_complete_context shall be allowed to be NULL. ]*/
      /* Codes_SRS_HTTP_PROXY_IO_01_028: [ on_io_close_complete shall be allowed to be NULL. ]*/
      if (http_proxy_io == NULL)
      {
          /* 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. ]*/
          result = __LINE__;
          LogError("NULL http_proxy_io.");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          /* 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. ]*/
          if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSED) ||
              /* Codes_SRS_HTTP_PROXY_IO_01_054: [ http_proxy_io_close while OPENING shall fail and return a non-zero value. ]*/
              (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSING))
          {
              result = __LINE__;
              LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_OPEN.");
          }
          else if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO) ||
              (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE))
          {
              /* 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. ]*/
              http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
              (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
              http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED);
  
              /* Codes_SRS_HTTP_PROXY_IO_01_022: [ http_proxy_io_close shall close the HTTP proxy IO and on success it shall return 0. ]*/
              result = 0;
          }
          else
          {
              HTTP_PROXY_IO_STATE previous_state = http_proxy_io_instance->http_proxy_io_state;
  
              http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSING;
  
              /* 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. ]*/
              http_proxy_io_instance->on_io_close_complete = on_io_close_complete;
              http_proxy_io_instance->on_io_close_complete_context = on_io_close_complete_context;
  
              /* 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. ]*/
              if (xio_close(http_proxy_io_instance->underlying_io, on_underlying_io_close_complete, http_proxy_io_instance) != 0)
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_025: [ If xio_close fails, http_proxy_io_close shall fail and return a non-zero value. ]*/
                  result = __LINE__;
                  http_proxy_io_instance->http_proxy_io_state = previous_state;
                  LogError("Cannot close underlying IO.");
              }
              else
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_022: [ http_proxy_io_close shall close the HTTP proxy IO and on success it shall return 0. ]*/
                  result = 0;
              }
          }
      }
  
      return result;
  }
  
  static 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)
  {
      int result;
  
      /* Codes_SRS_HTTP_PROXY_IO_01_032: [ on_send_complete shall be allowed to be NULL. ]*/
      /* 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. ]*/
      if ((http_proxy_io == NULL) ||
          (buffer == NULL) ||
          /* Codes_SRS_HTTP_PROXY_IO_01_031: [ If size is 0, http_proxy_io_send shall fail and return a non-zero value. ]*/
          (size == 0))
      {
          result = __LINE__;
          LogError("Bad arguments: http_proxy_io = %p, buffer = %p.",
              http_proxy_io, buffer);
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          /* 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. ]*/
          /* 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. ]*/
          if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_OPEN)
          {
              result = __LINE__;
              LogError("Invalid HTTP proxy IO state. Expected state is HTTP_PROXY_IO_STATE_OPEN.");
          }
          else
          {
              /* 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. ]*/
              if (xio_send(http_proxy_io_instance->underlying_io, buffer, size, on_send_complete, on_send_complete_context) != 0)
              {
                  /* Codes_SRS_HTTP_PROXY_IO_01_055: [ If xio_send fails, http_proxy_io_send shall fail and return a non-zero value. ]*/
                  result = __LINE__;
                  LogError("Underlying xio_send failed.");
              }
              else
              {
                  /* 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. ]*/
                  result = 0;
              }
          }
      }
  
      return result;
  }
  
  static void http_proxy_io_dowork(CONCRETE_IO_HANDLE http_proxy_io)
  {
      if (http_proxy_io == NULL)
      {
          /* Codes_SRS_HTTP_PROXY_IO_01_038: [ If the http_proxy_io argument is NULL, http_proxy_io_dowork shall do nothing. ]*/
          LogError("NULL http_proxy_io.");
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED)
          {
              /* 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. ]*/
              xio_dowork(http_proxy_io_instance->underlying_io);
          }
      }
  }
  
  static int http_proxy_io_set_option(CONCRETE_IO_HANDLE http_proxy_io, const char* option_name, const void* value)
  {
      int result;
  
      if ((http_proxy_io == NULL) || (option_name == NULL))
      {
          /* 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. ]*/
          LogError("Bad arguments: http_proxy_io = %p, option_name = %p",
              http_proxy_io, option_name);
          result = __LINE__;
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          /* Codes_SRS_HTTP_PROXY_IO_01_045: [ None. ]*/
  
          /* 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. ]*/
          /* Codes_SRS_HTTP_PROXY_IO_01_056: [ The value argument shall be allowed to be NULL. ]*/
          if (xio_setoption(http_proxy_io_instance->underlying_io, option_name, value) != 0)
          {
              /* Codes_SRS_HTTP_PROXY_IO_01_044: [ if xio_setoption fails, http_proxy_io_set_option shall return a non-zero value. ]*/
              LogError("Unrecognized option");
              result = __LINE__;
          }
          else
          {
              /* 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. ]*/
              result = 0;
          }
      }
  
      return result;
  }
  
  static OPTIONHANDLER_HANDLE http_proxy_io_retrieve_options(CONCRETE_IO_HANDLE http_proxy_io)
  {
      OPTIONHANDLER_HANDLE result;
  
      if (http_proxy_io == NULL)
      {
          /* 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. ]*/
          LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle=%p", http_proxy_io);
          result = NULL;
      }
      else
      {
          HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io;
  
          /* 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. ]*/
          result = xio_retrieveoptions(http_proxy_io_instance->underlying_io);
          if (result == NULL)
          {
              /* Codes_SRS_HTTP_PROXY_IO_01_048: [ If xio_retrieveoptions fails, http_proxy_io_retrieve_options shall return NULL. ]*/
              LogError("unable to create option handler");
          }
      }
      return result;
  }
  
  static const IO_INTERFACE_DESCRIPTION http_proxy_io_interface_description =
  {
      http_proxy_io_retrieve_options,
      http_proxy_io_create,
      http_proxy_io_destroy,
      http_proxy_io_open,
      http_proxy_io_close,
      http_proxy_io_send,
      http_proxy_io_dowork,
      http_proxy_io_set_option
  };
  
  const IO_INTERFACE_DESCRIPTION* http_proxy_io_get_interface_description(void)
  {
      /* 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. ]*/
      return &http_proxy_io_interface_description;
  }
  