h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) { apr_status_t status; h2_stream *stream = NULL; AP_DEBUG_ASSERT(m); if (m->aborted) { return NULL; } status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { h2_io *io = h2_io_set_pop_highest_prio(m->ready_ios); if (io) { stream = h2_stream_set_get(streams, io->id); if (stream) { if (io->rst_error) { h2_stream_rst(stream, io->rst_error); } else { AP_DEBUG_ASSERT(io->response); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_pre"); h2_stream_set_response(stream, io->response, io->bbout); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_post"); } } else { /* We have the io ready, but the stream has gone away, maybe * reset by the client. Should no longer happen since such * streams should clear io's from the ready queue. */ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c, APLOGNO(02953) "h2_mplx(%ld): stream for response %d closed, " "resetting io to close request processing", m->id, io->id); io->orphaned = 1; if (io->task_done) { io_destroy(m, io, 1); } else { /* hang around until the h2_task is done, but * shutdown input and send out any events (e.g. window * updates) asap. */ h2_io_in_shutdown(io); h2_io_rst(io, H2_ERR_STREAM_CLOSED); io_process_events(m, io); } } if (io->output_drained) { apr_thread_cond_signal(io->output_drained); } } apr_thread_mutex_unlock(m->lock); } return stream; }
h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) { apr_status_t status; h2_stream *stream = NULL; int acquired; AP_DEBUG_ASSERT(m); if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { h2_io *io = h2_io_set_pop_highest_prio(m->ready_ios); if (io && !m->aborted) { stream = h2_stream_set_get(streams, io->id); if (stream) { if (io->rst_error) { h2_stream_rst(stream, io->rst_error); } else { AP_DEBUG_ASSERT(io->response); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_pre"); h2_stream_set_response(stream, io->response, io->bbout); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_post"); } } else { /* We have the io ready, but the stream has gone away, maybe * reset by the client. Should no longer happen since such * streams should clear io's from the ready queue. */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, "h2_mplx(%ld): stream for response %d closed, " "resetting io to close request processing", m->id, io->id); h2_io_make_orphaned(io, H2_ERR_STREAM_CLOSED); if (!io->worker_started || io->worker_done) { io_destroy(m, io, 1); } else { /* hang around until the h2_task is done, but * shutdown input and send out any events (e.g. window * updates) asap. */ h2_io_in_shutdown(io); io_process_events(m, io); } } h2_io_signal(io, H2_IO_WRITE); } leave_mutex(m, acquired); } return stream; }
apr_status_t h2_stream_schedule(h2_stream *stream, int eos, h2_stream_pri_cmp *cmp, void *ctx) { apr_status_t status; AP_DEBUG_ASSERT(stream); AP_DEBUG_ASSERT(stream->session); AP_DEBUG_ASSERT(stream->session->mplx); if (!output_open(stream)) { return APR_ECONNRESET; } if (stream->scheduled) { return APR_EINVAL; } if (eos) { close_input(stream); } /* Seeing the end-of-headers, we have everything we need to * start processing it. */ status = h2_request_end_headers(stream->request, stream->pool, eos); if (status == APR_SUCCESS) { if (!eos) { stream->bbin = apr_brigade_create(stream->pool, stream->session->c->bucket_alloc); } stream->input_remaining = stream->request->content_length; status = h2_mplx_process(stream->session->mplx, stream->id, stream->request, eos, cmp, ctx); stream->scheduled = 1; ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, "h2_stream(%ld-%d): scheduled %s %s://%s%s", stream->session->id, stream->id, stream->request->method, stream->request->scheme, stream->request->authority, stream->request->path); } else { h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, "h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s", stream->session->id, stream->id, stream->request->method, stream->request->scheme, stream->request->authority, stream->request->path); } return status; }
apr_status_t h2_stream_write_data(h2_stream *stream, const char *data, size_t len) { apr_status_t status = APR_SUCCESS; AP_DEBUG_ASSERT(stream); if (input_closed(stream) || !stream->request->eoh || !stream->bbin) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, "h2_stream(%ld-%d): writing denied, closed=%d, eoh=%d, bbin=%d", stream->session->id, stream->id, input_closed(stream), stream->request->eoh, !!stream->bbin); return APR_EINVAL; } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, "h2_stream(%ld-%d): add %ld input bytes", stream->session->id, stream->id, (long)len); H2_STREAM_IN(APLOG_TRACE2, stream, "write_data_pre"); if (stream->request->chunked) { /* if input may have a body and we have not seen any * content-length header, we need to chunk the input data. */ status = input_add_data(stream, data, len, 1); } else { stream->input_remaining -= len; if (stream->input_remaining < 0) { ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, stream->session->c, APLOGNO(02961) "h2_stream(%ld-%d): got %ld more content bytes than announced " "in content-length header: %ld", stream->session->id, stream->id, (long)stream->request->content_length, -(long)stream->input_remaining); h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR); return APR_ECONNABORTED; } status = input_add_data(stream, data, len, 0); } if (status == APR_SUCCESS) { status = h2_stream_input_flush(stream); } H2_STREAM_IN(APLOG_TRACE2, stream, "write_data_post"); return status; }
static apr_status_t stream_destroy(h2_session *session, h2_stream *stream, uint32_t error_code) { if (!error_code) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): handled, closing", session->id, (int)stream->id); if (stream->id > session->max_stream_handled) { session->max_stream_handled = stream->id; } } else { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): closing with err=%d %s", session->id, (int)stream->id, (int)error_code, h2_h2_err_description(error_code)); h2_stream_rst(stream, error_code); } return h2_conn_io_writeb(&session->io, h2_bucket_eos_create(session->c->bucket_alloc, stream)); }