static void s_write_outgoing_stream()

in source/h1_connection.c [847:941]


static void s_write_outgoing_stream(struct aws_h1_connection *connection, bool first_try) {
    AWS_PRECONDITION(aws_channel_thread_is_callers_thread(connection->base.channel_slot->channel));
    AWS_PRECONDITION(connection->thread_data.is_outgoing_stream_task_active);

    /* Just stop if we're no longer writing stream data */
    if (connection->thread_data.is_writing_stopped || connection->thread_data.has_switched_protocols) {
        return;
    }

    /* Determine whether we have data available to send, and end task immediately if there's not.
     * The outgoing stream task will be kicked off again when user adds more data (new stream, new chunk, etc) */
    struct aws_h1_stream *outgoing_stream = s_update_outgoing_stream_ptr(connection);
    bool waiting_for_chunks = aws_h1_encoder_is_waiting_for_chunks(&connection->thread_data.encoder);
    if (!outgoing_stream || waiting_for_chunks) {
        if (!first_try) {
            AWS_LOGF_TRACE(
                AWS_LS_HTTP_CONNECTION,
                "id=%p: Outgoing stream task stopped. outgoing_stream=%p waiting_for_chunks:%d",
                (void *)&connection->base,
                outgoing_stream ? (void *)&outgoing_stream->base : NULL,
                waiting_for_chunks);
        }
        connection->thread_data.is_outgoing_stream_task_active = false;
        return;
    }

    if (first_try) {
        AWS_LOGF_TRACE(AWS_LS_HTTP_CONNECTION, "id=%p: Outgoing stream task has begun.", (void *)&connection->base);
    }

    struct aws_io_message *msg = aws_channel_slot_acquire_max_message_for_write(connection->base.channel_slot);
    if (!msg) {
        AWS_LOGF_ERROR(
            AWS_LS_HTTP_CONNECTION,
            "id=%p: Failed to acquire message from pool, error %d (%s). Closing connection.",
            (void *)&connection->base,
            aws_last_error(),
            aws_error_name(aws_last_error()));
        goto error;
    }

    /* Set up callback so we can send another message when this one completes */
    msg->on_completion = s_on_channel_write_complete;
    msg->user_data = connection;

    /*
     * Fill message data from the outgoing stream.
     * Note that we might be resuming work on a stream from a previous run of this task.
     */
    if (AWS_OP_SUCCESS != aws_h1_encoder_process(&connection->thread_data.encoder, &msg->message_data)) {
        /* Error sending data, abandon ship */
        goto error;
    }

    if (msg->message_data.len > 0) {
        AWS_LOGF_TRACE(
            AWS_LS_HTTP_CONNECTION,
            "id=%p: Outgoing stream task is sending message of size %zu.",
            (void *)&connection->base,
            msg->message_data.len);

        if (aws_channel_slot_send_message(connection->base.channel_slot, msg, AWS_CHANNEL_DIR_WRITE)) {
            AWS_LOGF_ERROR(
                AWS_LS_HTTP_CONNECTION,
                "id=%p: Failed to send message in write direction, error %d (%s). Closing connection.",
                (void *)&connection->base,
                aws_last_error(),
                aws_error_name(aws_last_error()));

            goto error;
        }

    } else {
        /* If message is empty, warn that no work is being done
         * and reschedule the task to try again next tick.
         * It's likely that body isn't ready, so body streaming function has no data to write yet.
         * If this scenario turns out to be common we should implement a "pause" feature. */
        AWS_LOGF_WARN(
            AWS_LS_HTTP_CONNECTION,
            "id=%p: Current outgoing stream %p sent no data, will try again next tick.",
            (void *)&connection->base,
            outgoing_stream ? (void *)&outgoing_stream->base : NULL);

        aws_mem_release(msg->allocator, msg);

        aws_channel_schedule_task_now(connection->base.channel_slot->channel, &connection->outgoing_stream_task);
    }

    return;
error:
    if (msg) {
        aws_mem_release(msg->allocator, msg);
    }
    s_shutdown_due_to_error(connection, aws_last_error());
}