apr_status_t h2_stream_prep_read(h2_stream *stream, apr_size_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; const char *src; if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) { src = "stream"; status = h2_util_bb_avail(stream->bbout, plen, peos); if (status == APR_SUCCESS && !*peos && !*plen) { apr_brigade_cleanup(stream->bbout); return h2_stream_prep_read(stream, plen, peos); } } else { src = "mplx"; status = h2_mplx_out_readx(stream->m, stream->id, NULL, NULL, plen, peos); } if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c, "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d", stream->m->id, stream->id, src, (long)*plen, *peos); return status; }
apr_status_t h2_stream_prep_read(h2_stream *stream, apr_off_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; const char *src; apr_table_t *trailers = NULL; int test_read = (*plen == 0); if (stream->rst_error) { return APR_ECONNRESET; } H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_pre"); if (!APR_BRIGADE_EMPTY(stream->bbout)) { src = "stream"; status = h2_util_bb_avail(stream->bbout, plen, peos); if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { apr_brigade_cleanup(stream->bbout); return h2_stream_prep_read(stream, plen, peos); } trailers = stream->response? stream->response->trailers : NULL; } else { src = "mplx"; status = h2_mplx_out_readx(stream->session->mplx, stream->id, NULL, NULL, plen, peos, &trailers); if (trailers && stream->response) { h2_response_set_trailers(stream->response, trailers); } } if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_post"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s", stream->session->id, stream->id, src, (long)*plen, *peos, trailers? "yes" : "no"); return status; }
/* The session wants to send more DATA for the given stream. */ static ssize_t stream_data_cb(nghttp2_session *ng2s, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *puser) { h2_session *session = (h2_session *)puser; apr_size_t nread = length; int eos = 0; apr_status_t status; h2_stream *stream; AP_DEBUG_ASSERT(session); (void)ng2s; (void)buf; (void)source; stream = h2_stream_set_get(session->streams, stream_id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c, APLOGNO(02937) "h2_stream(%ld-%d): data requested but stream not found", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream)); status = h2_stream_prep_read(stream, &nread, &eos); if (nread) { *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; } switch (status) { case APR_SUCCESS: break; case APR_EAGAIN: /* If there is no data available, our session will automatically * suspend this stream and not ask for more data until we resume * it. Remember at our h2_stream that we need to do this. */ nread = 0; h2_stream_set_suspended(stream, 1); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): suspending stream", session->id, (int)stream_id); return NGHTTP2_ERR_DEFERRED; case APR_EOF: nread = 0; eos = 1; break; default: nread = 0; ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, APLOGNO(02938) "h2_stream(%ld-%d): reading data", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } if (eos) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (ssize_t)nread; }
static ssize_t stream_data_cb(nghttp2_session *ng2s, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *puser) { h2_session *session = (h2_session *)puser; apr_size_t nread = length; int eos = 0; apr_status_t status; h2_stream *stream; AP_DEBUG_ASSERT(session); /* The session wants to send more DATA for the stream. We need * to find out how much of the requested length we can send without * blocking. * Indicate EOS when we encounter it or DEFERRED if the stream * should be suspended. * TODO: for handling of TRAILERS, the EOF indication needs * to be aware of that. */ (void)ng2s; (void)buf; (void)source; stream = h2_stream_set_get(session->streams, stream_id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, APLOGNO(02937) "h2_stream(%ld-%d): data requested but stream not found", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream)); status = h2_stream_prep_read(stream, &nread, &eos); if (nread) { *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; } switch (status) { case APR_SUCCESS: break; case APR_ECONNRESET: return nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, stream->id, stream->rst_error); case APR_EAGAIN: /* If there is no data available, our session will automatically * suspend this stream and not ask for more data until we resume * it. Remember at our h2_stream that we need to do this. */ nread = 0; h2_stream_set_suspended(stream, 1); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): suspending stream", session->id, (int)stream_id); return NGHTTP2_ERR_DEFERRED; case APR_EOF: nread = 0; eos = 1; break; default: nread = 0; ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, APLOGNO(02938) "h2_stream(%ld-%d): reading data", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } if (eos) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } return (ssize_t)nread; }