static h2_stream *resume_on_data(void *ctx, h2_stream *stream) { h2_session *session = (h2_session *)ctx; assert(session); assert(stream); if (h2_stream_is_suspended(stream)) { ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, stream->pool, "h2_stream(%ld-%d): suspended, checking for DATA", h2_mplx_get_id(stream->m), stream->id); if (h2_mplx_out_has_data_for(stream->m, h2_stream_get_id(stream))) { h2_stream_set_suspended(stream, 0); int rv = nghttp2_session_resume_data(session->ngh2, h2_stream_get_id(stream)); ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)? APLOG_ERR : APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): resuming stream %s", session->id, stream->id, nghttp2_strerror(rv)); } } return NULL; }
static int resume_on_data(void *ctx, h2_stream *stream) { resume_ctx *rctx = (resume_ctx*)ctx; h2_session *session = rctx->session; AP_DEBUG_ASSERT(session); AP_DEBUG_ASSERT(stream); if (h2_stream_is_suspended(stream)) { if (h2_mplx_out_has_data_for(stream->m, stream->id)) { int rv; h2_stream_set_suspended(stream, 0); ++rctx->resume_count; rv = nghttp2_session_resume_data(session->ngh2, stream->id); ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)? APLOG_ERR : APLOG_DEBUG, 0, session->c, APLOGNO(02936) "h2_stream(%ld-%d): resuming stream %s", session->id, stream->id, nghttp2_strerror(rv)); } } return 1; }
/* 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; }
/* 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; assert(session); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_stream(%ld-%d): requesting %ld bytes", session->id, (int)stream_id, (long)length); h2_stream *stream = h2_stream_set_get(session->streams, stream_id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, "h2_stream(%ld-%d): data requested but stream not found", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } assert(!h2_stream_is_suspended(stream)); /* Try to pop data buckets from our queue for this stream * until we see EOS or the buffer is full. */ ssize_t total_read = 0; int eos = 0; int done = 0; size_t left = length; while (left > 0 && !done && !eos) { apr_status_t status = APR_SUCCESS; h2_bucket *bucket = stream->cur_out; stream->cur_out = NULL; if (!bucket) { status = h2_stream_read(stream, &bucket, &eos); } switch (status) { case APR_SUCCESS: { /* This copies out the data and modifies the bucket to * reflect the amount "moved". This is easy, as this callback * runs in the connection thread alone and is the sole owner * of data in this queue. */ assert(bucket); size_t nread = h2_bucket_move(bucket, (char*)buf, left); if (bucket->data_len > 0) { /* we could not move all, remember it for next time */ stream->cur_out = bucket; eos = 0; } else { h2_bucket_destroy(bucket); } total_read += nread; buf += nread; left -= nread; } 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. */ done = 1; if (total_read == 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; } break; case APR_EOF: eos = 1; done = 1; break; default: ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, "h2_stream(%ld-%d): reading data", session->id, (int)stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } } if (eos) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_stream(%ld-%d): requested %ld, " "sending %ld data bytes (eos=%d)", session->id, (int)stream_id, (long)length, (long)total_read, eos); return total_read; }
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; }