static void s_ws_bootstrap_cancel_setup_due_to_err()

in source/websocket_bootstrap.c [78:237]


static void s_ws_bootstrap_cancel_setup_due_to_err(
    struct aws_websocket_client_bootstrap *ws_bootstrap,
    struct aws_http_connection *http_connection,
    int error_code);
static void s_ws_bootstrap_on_http_setup(struct aws_http_connection *http_connection, int error_code, void *user_data);
static void s_ws_bootstrap_on_http_shutdown(
    struct aws_http_connection *http_connection,
    int error_code,
    void *user_data);
static int s_ws_bootstrap_on_handshake_response_headers(
    struct aws_http_stream *stream,
    enum aws_http_header_block header_block,
    const struct aws_http_header *header_array,
    size_t num_headers,
    void *user_data);
static int s_ws_bootstrap_on_handshake_response_header_block_done(
    struct aws_http_stream *stream,
    enum aws_http_header_block header_block,
    void *user_data);
static void s_ws_bootstrap_on_stream_complete(struct aws_http_stream *stream, int error_code, void *user_data);

int aws_websocket_client_connect(const struct aws_websocket_client_connection_options *options) {
    aws_http_fatal_assert_library_initialized();
    AWS_ASSERT(options);

    /* Validate options */
    struct aws_byte_cursor path;
    aws_http_message_get_request_path(options->handshake_request, &path);
    if (!options->allocator || !options->bootstrap || !options->socket_options || !options->host.len || !path.len ||
        !options->on_connection_setup) {

        AWS_LOGF_ERROR(AWS_LS_HTTP_WEBSOCKET_SETUP, "id=static: Missing required websocket connection options.");
        return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
    }

    struct aws_byte_cursor method;
    aws_http_message_get_request_method(options->handshake_request, &method);
    if (aws_http_str_to_method(method) != AWS_HTTP_METHOD_GET) {

        AWS_LOGF_ERROR(AWS_LS_HTTP_WEBSOCKET_SETUP, "id=static: Websocket request must have method be 'GET'.");
        return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
    }

    bool all_frame_callbacks_set =
        options->on_incoming_frame_begin && options->on_incoming_frame_payload && options->on_incoming_frame_begin;

    bool no_frame_callbacks_set =
        !options->on_incoming_frame_begin && !options->on_incoming_frame_payload && !options->on_incoming_frame_begin;

    if (!(all_frame_callbacks_set || no_frame_callbacks_set)) {
        AWS_LOGF_ERROR(
            AWS_LS_HTTP_WEBSOCKET_SETUP,
            "id=static: Invalid websocket connection options,"
            " either all frame-handling callbacks must be set, or none must be set.");
        return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
    }

    if (!options->handshake_request) {
        AWS_LOGF_ERROR(
            AWS_LS_HTTP_WEBSOCKET_SETUP,
            "id=static: Invalid connection options, missing required request for websocket client handshake.");
        return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
    }

    /* Create bootstrap */
    struct aws_websocket_client_bootstrap *ws_bootstrap =
        aws_mem_calloc(options->allocator, 1, sizeof(struct aws_websocket_client_bootstrap));
    if (!ws_bootstrap) {
        goto error;
    }

    ws_bootstrap->alloc = options->allocator;
    ws_bootstrap->initial_window_size = options->initial_window_size;
    ws_bootstrap->manual_window_update = options->manual_window_management;
    ws_bootstrap->user_data = options->user_data;
    ws_bootstrap->websocket_setup_callback = options->on_connection_setup;
    ws_bootstrap->websocket_shutdown_callback = options->on_connection_shutdown;
    ws_bootstrap->websocket_frame_begin_callback = options->on_incoming_frame_begin;
    ws_bootstrap->websocket_frame_payload_callback = options->on_incoming_frame_payload;
    ws_bootstrap->websocket_frame_complete_callback = options->on_incoming_frame_complete;
    ws_bootstrap->handshake_request = options->handshake_request;
    ws_bootstrap->response_status = AWS_HTTP_STATUS_CODE_UNKNOWN;

    /* Pre-allocate space for response headers */
    /* Values are just guesses */
    size_t estimated_response_headers = aws_http_message_get_header_count(ws_bootstrap->handshake_request) + 10;
    size_t estimated_response_header_length = 64;

    int err = aws_array_list_init_dynamic(
        &ws_bootstrap->response_headers,
        ws_bootstrap->alloc,
        estimated_response_headers,
        sizeof(struct aws_http_header));
    if (err) {
        goto error;
    }

    err = aws_byte_buf_init(
        &ws_bootstrap->response_storage,
        ws_bootstrap->alloc,
        estimated_response_headers * estimated_response_header_length);
    if (err) {
        goto error;
    }

    /* Initiate HTTP connection */
    struct aws_http_client_connection_options http_options = AWS_HTTP_CLIENT_CONNECTION_OPTIONS_INIT;
    http_options.allocator = ws_bootstrap->alloc;
    http_options.bootstrap = options->bootstrap;
    http_options.host_name = options->host;
    http_options.socket_options = options->socket_options;
    http_options.tls_options = options->tls_options;
    http_options.proxy_options = options->proxy_options;
    http_options.initial_window_size = 1024; /* Adequate space for response data to trickle in */

    /* TODO: websockets has issues if back-pressure is disabled on the whole channel. This should be fixed. */
    http_options.manual_window_management = true;

    http_options.user_data = ws_bootstrap;
    http_options.on_setup = s_ws_bootstrap_on_http_setup;
    http_options.on_shutdown = s_ws_bootstrap_on_http_shutdown;

    /* Infer port, if not explicitly specified in URI */
    http_options.port = options->port;
    if (!http_options.port) {
        http_options.port = options->tls_options ? 443 : 80;
    }

    err = s_system_vtable->aws_http_client_connect(&http_options);
    if (err) {
        AWS_LOGF_ERROR(
            AWS_LS_HTTP_WEBSOCKET_SETUP,
            "id=static: Websocket failed to initiate HTTP connection, error %d (%s)",
            aws_last_error(),
            aws_error_name(aws_last_error()));
        goto error_already_logged;
    }

    /* Success! (so far) */
    AWS_LOGF_TRACE(
        AWS_LS_HTTP_WEBSOCKET_SETUP,
        "id=%p: Websocket setup begun, connecting to " PRInSTR ":%" PRIu16 PRInSTR,
        (void *)ws_bootstrap,
        AWS_BYTE_CURSOR_PRI(options->host),
        options->port,
        AWS_BYTE_CURSOR_PRI(path));

    return AWS_OP_SUCCESS;

error:
    AWS_LOGF_ERROR(
        AWS_LS_HTTP_WEBSOCKET_SETUP,
        "id=static: Failed to initiate websocket connection, error %d (%s)",
        aws_last_error(),
        aws_error_name(aws_last_error()));

error_already_logged:
    s_ws_bootstrap_destroy(ws_bootstrap);
    return AWS_OP_ERR;
}