static void on_underlying_io_open_complete()

in src/http_proxy_io.c [247:451]


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
                    {
                        size_t malloc_size = safe_add_size_t((size_t)plain_auth_string_length, 1);
                        if (malloc_size == SIZE_MAX ||
                            (plain_auth_string_bytes = (char*)malloc(malloc_size)) == 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;
                            plain_auth_string_bytes = 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->hostname == NULL ||
                    (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;
                        size_t malloc_size = safe_add_size_t((size_t)connect_request_length, 1);
                        if (malloc_size == SIZE_MAX ||
                            (connect_request = (char*)malloc(malloc_size)) == 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;
        }
    }
}