|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
#include <stddef.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <httpd.h>
|
|
Packit |
90a5c9 |
#include <http_core.h>
|
|
Packit |
90a5c9 |
#include <http_connection.h>
|
|
Packit |
90a5c9 |
#include <http_log.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <nghttp2/nghttp2.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_private.h"
|
|
Packit |
90a5c9 |
#include "h2.h"
|
|
Packit |
90a5c9 |
#include "h2_bucket_beam.h"
|
|
Packit |
90a5c9 |
#include "h2_conn.h"
|
|
Packit |
90a5c9 |
#include "h2_config.h"
|
|
Packit |
90a5c9 |
#include "h2_h2.h"
|
|
Packit |
90a5c9 |
#include "h2_mplx.h"
|
|
Packit |
90a5c9 |
#include "h2_push.h"
|
|
Packit |
90a5c9 |
#include "h2_request.h"
|
|
Packit |
90a5c9 |
#include "h2_headers.h"
|
|
Packit |
90a5c9 |
#include "h2_session.h"
|
|
Packit |
90a5c9 |
#include "h2_stream.h"
|
|
Packit |
90a5c9 |
#include "h2_task.h"
|
|
Packit |
90a5c9 |
#include "h2_ctx.h"
|
|
Packit |
90a5c9 |
#include "h2_task.h"
|
|
Packit |
90a5c9 |
#include "h2_util.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *h2_ss_str(h2_stream_state_t state)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
switch (state) {
|
|
Packit |
90a5c9 |
case H2_SS_IDLE:
|
|
Packit |
90a5c9 |
return "IDLE";
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_L:
|
|
Packit |
90a5c9 |
return "RESERVED_LOCAL";
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_R:
|
|
Packit |
90a5c9 |
return "RESERVED_REMOTE";
|
|
Packit |
90a5c9 |
case H2_SS_OPEN:
|
|
Packit |
90a5c9 |
return "OPEN";
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_L:
|
|
Packit |
90a5c9 |
return "HALF_CLOSED_LOCAL";
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_R:
|
|
Packit |
90a5c9 |
return "HALF_CLOSED_REMOTE";
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED:
|
|
Packit |
90a5c9 |
return "CLOSED";
|
|
Packit |
90a5c9 |
case H2_SS_CLEANUP:
|
|
Packit |
90a5c9 |
return "CLEANUP";
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return "UNKNOWN";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *h2_stream_state_str(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return h2_ss_str(stream->state);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Abbreviations for stream transit tables */
|
|
Packit |
90a5c9 |
#define S_XXX (-2) /* Programming Error */
|
|
Packit |
90a5c9 |
#define S_ERR (-1) /* Protocol Error */
|
|
Packit |
90a5c9 |
#define S_NOP (0) /* No Change */
|
|
Packit |
90a5c9 |
#define S_IDL (H2_SS_IDL + 1)
|
|
Packit |
90a5c9 |
#define S_RS_L (H2_SS_RSVD_L + 1)
|
|
Packit |
90a5c9 |
#define S_RS_R (H2_SS_RSVD_R + 1)
|
|
Packit |
90a5c9 |
#define S_OPEN (H2_SS_OPEN + 1)
|
|
Packit |
90a5c9 |
#define S_CL_L (H2_SS_CLOSED_L + 1)
|
|
Packit |
90a5c9 |
#define S_CL_R (H2_SS_CLOSED_R + 1)
|
|
Packit |
90a5c9 |
#define S_CLS (H2_SS_CLOSED + 1)
|
|
Packit |
90a5c9 |
#define S_CLN (H2_SS_CLEANUP + 1)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* state transisitions when certain frame types are sent */
|
|
Packit |
90a5c9 |
static int trans_on_send[][H2_SS_MAX] = {
|
|
Packit |
90a5c9 |
/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_NOP, S_NOP, S_ERR, S_NOP, S_NOP, },/* DATA */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_CL_R, S_NOP, S_NOP, S_ERR, S_NOP, S_NOP, },/* HEADERS */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* PRIORITY */
|
|
Packit |
90a5c9 |
{ S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* RST_STREAM */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* SETTINGS */
|
|
Packit |
90a5c9 |
{ S_RS_L,S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PUSH_PROMISE */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PING */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* GOAWAY */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* WINDOW_UPDATE */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* CONT */
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
/* state transisitions when certain frame types are received */
|
|
Packit |
90a5c9 |
static int trans_on_recv[][H2_SS_MAX] = {
|
|
Packit |
90a5c9 |
/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_NOP, S_ERR, S_NOP, S_NOP, S_NOP, },/* DATA */
|
|
Packit |
90a5c9 |
{ S_OPEN,S_CL_L, S_ERR, S_NOP, S_ERR, S_NOP, S_NOP, S_NOP, },/* HEADERS */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* PRIORITY */
|
|
Packit |
90a5c9 |
{ S_ERR, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* RST_STREAM */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* SETTINGS */
|
|
Packit |
90a5c9 |
{ S_RS_R,S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PUSH_PROMISE */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* PING */
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, },/* GOAWAY */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* WINDOW_UPDATE */
|
|
Packit |
90a5c9 |
{ S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, S_NOP, },/* CONT */
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
/* state transisitions when certain events happen */
|
|
Packit |
90a5c9 |
static int trans_on_event[][H2_SS_MAX] = {
|
|
Packit |
90a5c9 |
/*S_IDLE,S_RS_R, S_RS_L, S_OPEN, S_CL_R, S_CL_L, S_CLS, S_CLN, */
|
|
Packit |
90a5c9 |
{ S_XXX, S_ERR, S_ERR, S_CL_L, S_CLS, S_XXX, S_XXX, S_XXX, },/* EV_CLOSED_L*/
|
|
Packit |
90a5c9 |
{ S_ERR, S_ERR, S_ERR, S_CL_R, S_ERR, S_CLS, S_NOP, S_NOP, },/* EV_CLOSED_R*/
|
|
Packit |
90a5c9 |
{ S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* EV_CANCELLED*/
|
|
Packit |
90a5c9 |
{ S_NOP, S_XXX, S_XXX, S_XXX, S_XXX, S_CLS, S_CLN, S_XXX, },/* EV_EOS_SENT*/
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int on_map(h2_stream_state_t state, int map[H2_SS_MAX])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int op = map[state];
|
|
Packit |
90a5c9 |
switch (op) {
|
|
Packit |
90a5c9 |
case S_XXX:
|
|
Packit |
90a5c9 |
case S_ERR:
|
|
Packit |
90a5c9 |
return op;
|
|
Packit |
90a5c9 |
case S_NOP:
|
|
Packit |
90a5c9 |
return state;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return op-1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int on_frame(h2_stream_state_t state, int frame_type,
|
|
Packit |
90a5c9 |
int frame_map[][H2_SS_MAX], apr_size_t maxlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_assert(frame_type >= 0);
|
|
Packit |
90a5c9 |
ap_assert(state >= 0);
|
|
Packit |
90a5c9 |
if (frame_type >= maxlen) {
|
|
Packit |
90a5c9 |
return state; /* NOP, ignore unknown frame types */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return on_map(state, frame_map[frame_type]);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int on_frame_send(h2_stream_state_t state, int frame_type)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return on_frame(state, frame_type, trans_on_send, H2_ALEN(trans_on_send));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int on_frame_recv(h2_stream_state_t state, int frame_type)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return on_frame(state, frame_type, trans_on_recv, H2_ALEN(trans_on_recv));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int on_event(h2_stream* stream, h2_stream_event_t ev)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->monitor && stream->monitor->on_event) {
|
|
Packit |
90a5c9 |
stream->monitor->on_event(stream->monitor->ctx, stream, ev);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (ev < H2_ALEN(trans_on_event)) {
|
|
Packit |
90a5c9 |
return on_map(stream->state, trans_on_event[ev]);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return stream->state;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void H2_STREAM_OUT_LOG(int lvl, h2_stream *s, const char *tag)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (APLOG_C_IS_LEVEL(s->session->c, lvl)) {
|
|
Packit |
90a5c9 |
conn_rec *c = s->session->c;
|
|
Packit |
90a5c9 |
char buffer[4 * 1024];
|
|
Packit |
90a5c9 |
apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
len = h2_util_bb_print(buffer, bmax, tag, "", s->out_buffer);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, lvl, 0, c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(s, "out-buffer(%s)"), len? buffer : "empty");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t setup_input(h2_stream *stream) {
|
|
Packit |
90a5c9 |
if (stream->input == NULL) {
|
|
Packit |
90a5c9 |
int empty = (stream->input_eof
|
|
Packit |
90a5c9 |
&& (!stream->in_buffer
|
|
Packit |
90a5c9 |
|| APR_BRIGADE_EMPTY(stream->in_buffer)));
|
|
Packit |
90a5c9 |
if (!empty) {
|
|
Packit |
90a5c9 |
h2_beam_create(&stream->input, stream->pool, stream->id,
|
|
Packit |
90a5c9 |
"input", H2_BEAM_OWNER_SEND, 0,
|
|
Packit |
90a5c9 |
stream->session->s->timeout);
|
|
Packit |
90a5c9 |
h2_beam_send_from(stream->input, stream->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t close_input(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
conn_rec *c = stream->session->c;
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
stream->input_eof = 1;
|
|
Packit |
90a5c9 |
if (stream->input && h2_beam_is_closed(stream->input)) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "closing input"));
|
|
Packit |
90a5c9 |
if (stream->rst_error) {
|
|
Packit |
90a5c9 |
return APR_ECONNRESET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->trailers && !apr_is_empty_table(stream->trailers)) {
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
h2_headers *r;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!stream->in_buffer) {
|
|
Packit |
90a5c9 |
stream->in_buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r = h2_headers_create(HTTP_OK, stream->trailers, NULL,
|
|
Packit |
90a5c9 |
stream->in_trailer_octets, stream->pool);
|
|
Packit |
90a5c9 |
stream->trailers = NULL;
|
|
Packit |
90a5c9 |
b = h2_bucket_headers_create(c->bucket_alloc, r);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(stream->in_buffer, b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
b = apr_bucket_eos_create(c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(stream->in_buffer, b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "added trailers"));
|
|
Packit |
90a5c9 |
h2_stream_dispatch(stream, H2_SEV_IN_DATA_PENDING);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (stream->input) {
|
|
Packit |
90a5c9 |
h2_stream_flush_input(stream);
|
|
Packit |
90a5c9 |
return h2_beam_close(stream->input);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t close_output(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (!stream->output || h2_beam_is_closed(stream->output)) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "closing output"));
|
|
Packit |
90a5c9 |
return h2_beam_leave(stream->output);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void on_state_enter(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->monitor && stream->monitor->on_state_enter) {
|
|
Packit |
90a5c9 |
stream->monitor->on_state_enter(stream->monitor->ctx, stream);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void on_state_event(h2_stream *stream, h2_stream_event_t ev)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->monitor && stream->monitor->on_state_event) {
|
|
Packit |
90a5c9 |
stream->monitor->on_state_event(stream->monitor->ctx, stream, ev);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void on_state_invalid(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->monitor && stream->monitor->on_state_invalid) {
|
|
Packit |
90a5c9 |
stream->monitor->on_state_invalid(stream->monitor->ctx, stream);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* stream got an event/frame invalid in its state */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "invalid state event"));
|
|
Packit |
90a5c9 |
switch (stream->state) {
|
|
Packit |
90a5c9 |
case H2_SS_OPEN:
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_L:
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_R:
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_L:
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_R:
|
|
Packit |
90a5c9 |
h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t transit(h2_stream *stream, int new_state)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (new_state == stream->state) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (new_state < 0) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_LOG(APLOGNO(03081), stream, "invalid transition"));
|
|
Packit |
90a5c9 |
on_state_invalid(stream);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "transit to [%s]"), h2_ss_str(new_state));
|
|
Packit |
90a5c9 |
stream->state = new_state;
|
|
Packit |
90a5c9 |
switch (new_state) {
|
|
Packit |
90a5c9 |
case H2_SS_IDLE:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_L:
|
|
Packit |
90a5c9 |
close_input(stream);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_RSVD_R:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_OPEN:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_L:
|
|
Packit |
90a5c9 |
close_output(stream);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED_R:
|
|
Packit |
90a5c9 |
close_input(stream);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED:
|
|
Packit |
90a5c9 |
close_input(stream);
|
|
Packit |
90a5c9 |
close_output(stream);
|
|
Packit |
90a5c9 |
if (stream->out_buffer) {
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(stream->out_buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case H2_SS_CLEANUP:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
on_state_enter(stream);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_set_monitor(h2_stream *stream, h2_stream_monitor *monitor)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
stream->monitor = monitor;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_dispatch(h2_stream *stream, h2_stream_event_t ev)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int new_state;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "dispatch event %d"), ev);
|
|
Packit |
90a5c9 |
new_state = on_event(stream, ev);
|
|
Packit |
90a5c9 |
if (new_state < 0) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_LOG(APLOGNO(10002), stream, "invalid event %d"), ev);
|
|
Packit |
90a5c9 |
on_state_invalid(stream);
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(new_state > S_XXX);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (new_state == stream->state) {
|
|
Packit |
90a5c9 |
/* nop */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "non-state event %d"), ev);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
on_state_event(stream, ev);
|
|
Packit |
90a5c9 |
transit(stream, new_state);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void set_policy_for(h2_stream *stream, h2_request *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int enabled = h2_session_push_enabled(stream->session);
|
|
Packit |
90a5c9 |
stream->push_policy = h2_push_policy_determine(r->headers, stream->pool,
|
|
Packit |
90a5c9 |
enabled);
|
|
Packit |
90a5c9 |
r->serialize = h2_config_geti(stream->session->config, H2_CONF_SER_HEADERS);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int new_state, eos = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new_state = on_frame_send(stream->state, ftype);
|
|
Packit |
90a5c9 |
if (new_state < 0) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "invalid frame %d send"), ftype);
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(new_state > S_XXX);
|
|
Packit |
90a5c9 |
return transit(stream, new_state);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
++stream->out_frames;
|
|
Packit |
90a5c9 |
stream->out_frame_octets += frame_len;
|
|
Packit |
90a5c9 |
switch (ftype) {
|
|
Packit |
90a5c9 |
case NGHTTP2_DATA:
|
|
Packit |
90a5c9 |
eos = (flags & NGHTTP2_FLAG_END_STREAM);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case NGHTTP2_HEADERS:
|
|
Packit |
90a5c9 |
eos = (flags & NGHTTP2_FLAG_END_STREAM);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case NGHTTP2_PUSH_PROMISE:
|
|
Packit |
90a5c9 |
/* start pushed stream */
|
|
Packit |
90a5c9 |
ap_assert(stream->request == NULL);
|
|
Packit |
90a5c9 |
ap_assert(stream->rtmp != NULL);
|
|
Packit |
90a5c9 |
status = h2_request_end_headers(stream->rtmp, stream->pool, 1, 0);
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
set_policy_for(stream, stream->rtmp);
|
|
Packit |
90a5c9 |
stream->request = stream->rtmp;
|
|
Packit |
90a5c9 |
stream->rtmp = NULL;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "send frame %d, eos=%d"), ftype, eos);
|
|
Packit |
90a5c9 |
status = transit(stream, new_state);
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS && eos) {
|
|
Packit |
90a5c9 |
status = transit(stream, on_event(stream, H2_SEV_CLOSED_L));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_recv_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int new_state, eos = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new_state = on_frame_recv(stream->state, ftype);
|
|
Packit |
90a5c9 |
if (new_state < 0) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "invalid frame %d recv"), ftype);
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(new_state > S_XXX);
|
|
Packit |
90a5c9 |
return transit(stream, new_state);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (ftype) {
|
|
Packit |
90a5c9 |
case NGHTTP2_DATA:
|
|
Packit |
90a5c9 |
eos = (flags & NGHTTP2_FLAG_END_STREAM);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case NGHTTP2_HEADERS:
|
|
Packit |
90a5c9 |
eos = (flags & NGHTTP2_FLAG_END_STREAM);
|
|
Packit |
90a5c9 |
if (stream->state == H2_SS_OPEN) {
|
|
Packit |
90a5c9 |
/* trailer HEADER */
|
|
Packit |
90a5c9 |
if (!eos) {
|
|
Packit |
90a5c9 |
h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
stream->in_trailer_octets += frame_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* request HEADER */
|
|
Packit |
90a5c9 |
ap_assert(stream->request == NULL);
|
|
Packit |
90a5c9 |
if (stream->rtmp == NULL) {
|
|
Packit |
90a5c9 |
/* This can only happen, if the stream has received no header
|
|
Packit |
90a5c9 |
* name/value pairs at all. The lastest nghttp2 version have become
|
|
Packit |
90a5c9 |
* pretty good at detecting this early. In any case, we have
|
|
Packit |
90a5c9 |
* to abort the connection here, since this is clearly a protocol error */
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = h2_request_end_headers(stream->rtmp, stream->pool, eos, frame_len);
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
set_policy_for(stream, stream->rtmp);
|
|
Packit |
90a5c9 |
stream->request = stream->rtmp;
|
|
Packit |
90a5c9 |
stream->rtmp = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = transit(stream, new_state);
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS && eos) {
|
|
Packit |
90a5c9 |
status = transit(stream, on_event(stream, H2_SEV_CLOSED_R));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_flush_input(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->in_buffer && !APR_BRIGADE_EMPTY(stream->in_buffer)) {
|
|
Packit |
90a5c9 |
setup_input(stream);
|
|
Packit |
90a5c9 |
status = h2_beam_send(stream->input, stream->in_buffer, APR_BLOCK_READ);
|
|
Packit |
90a5c9 |
stream->in_last_write = apr_time_now();
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (stream->input_eof
|
|
Packit |
90a5c9 |
&& stream->input && !h2_beam_is_closed(stream->input)) {
|
|
Packit |
90a5c9 |
status = h2_beam_close(stream->input);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_recv_DATA(h2_stream *stream, uint8_t flags,
|
|
Packit |
90a5c9 |
const uint8_t *data, size_t len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_session *session = stream->session;
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
stream->in_data_frames++;
|
|
Packit |
90a5c9 |
if (len > 0) {
|
|
Packit |
90a5c9 |
if (APLOGctrace3(session->c)) {
|
|
Packit |
90a5c9 |
const char *load = apr_pstrndup(stream->pool, (const char *)data, len);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "recv DATA, len=%d: -->%s<--"),
|
|
Packit |
90a5c9 |
(int)len, load);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "recv DATA, len=%d"), (int)len);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
stream->in_data_octets += len;
|
|
Packit |
90a5c9 |
if (!stream->in_buffer) {
|
|
Packit |
90a5c9 |
stream->in_buffer = apr_brigade_create(stream->pool,
|
|
Packit |
90a5c9 |
session->c->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_brigade_write(stream->in_buffer, NULL, NULL, (const char *)data, len);
|
|
Packit |
90a5c9 |
h2_stream_dispatch(stream, H2_SEV_IN_DATA_PENDING);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void prep_output(h2_stream *stream) {
|
|
Packit |
90a5c9 |
conn_rec *c = stream->session->c;
|
|
Packit |
90a5c9 |
if (!stream->out_buffer) {
|
|
Packit |
90a5c9 |
stream->out_buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_stream *h2_stream_create(int id, apr_pool_t *pool, h2_session *session,
|
|
Packit |
90a5c9 |
h2_stream_monitor *monitor, int initiated_on)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
stream->id = id;
|
|
Packit |
90a5c9 |
stream->initiated_on = initiated_on;
|
|
Packit |
90a5c9 |
stream->created = apr_time_now();
|
|
Packit |
90a5c9 |
stream->state = H2_SS_IDLE;
|
|
Packit |
90a5c9 |
stream->pool = pool;
|
|
Packit |
90a5c9 |
stream->session = session;
|
|
Packit |
90a5c9 |
stream->monitor = monitor;
|
|
Packit |
90a5c9 |
stream->max_mem = session->max_stream_mem;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifdef H2_NG2_LOCAL_WIN_SIZE
|
|
Packit |
90a5c9 |
stream->in_window_size =
|
|
Packit |
90a5c9 |
nghttp2_session_get_stream_local_window_size(
|
|
Packit |
90a5c9 |
stream->session->ngh2, stream->id);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_LOG(APLOGNO(03082), stream, "created"));
|
|
Packit |
90a5c9 |
on_state_enter(stream);
|
|
Packit |
90a5c9 |
return stream;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_cleanup(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(stream);
|
|
Packit |
90a5c9 |
if (stream->out_buffer) {
|
|
Packit |
90a5c9 |
/* remove any left over output buckets that may still have
|
|
Packit |
90a5c9 |
* references into request pools */
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(stream->out_buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (stream->input) {
|
|
Packit |
90a5c9 |
h2_beam_abort(stream->input);
|
|
Packit |
90a5c9 |
status = h2_beam_wait_empty(stream->input, APR_NONBLOCK_READ);
|
|
Packit |
90a5c9 |
if (status == APR_EAGAIN) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "wait on input drain"));
|
|
Packit |
90a5c9 |
status = h2_beam_wait_empty(stream->input, APR_BLOCK_READ);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "input drain returned"));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_destroy(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_assert(stream);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "destroy"));
|
|
Packit |
90a5c9 |
apr_pool_destroy(stream->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_prep_processing(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->request) {
|
|
Packit |
90a5c9 |
const h2_request *r = stream->request;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "schedule %s %s://%s%s chunked=%d"),
|
|
Packit |
90a5c9 |
r->method, r->scheme, r->authority, r->path, r->chunked);
|
|
Packit |
90a5c9 |
setup_input(stream);
|
|
Packit |
90a5c9 |
stream->scheduled = 1;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_rst(h2_stream *stream, int error_code)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
stream->rst_error = error_code;
|
|
Packit |
90a5c9 |
if (stream->input) {
|
|
Packit |
90a5c9 |
h2_beam_abort(stream->input);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (stream->output) {
|
|
Packit |
90a5c9 |
h2_beam_leave(stream->output);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "reset, error=%d"), error_code);
|
|
Packit |
90a5c9 |
h2_stream_dispatch(stream, H2_SEV_CANCELLED);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_set_request_rec(h2_stream *stream,
|
|
Packit |
90a5c9 |
request_rec *r, int eos)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_request *req;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(stream->request == NULL);
|
|
Packit |
90a5c9 |
ap_assert(stream->rtmp == NULL);
|
|
Packit |
90a5c9 |
if (stream->rst_error) {
|
|
Packit |
90a5c9 |
return APR_ECONNRESET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = h2_request_rcreate(&req, stream->pool, r);
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
|
|
Packit |
90a5c9 |
H2_STRM_LOG(APLOGNO(03058), stream,
|
|
Packit |
90a5c9 |
"set_request_rec %s host=%s://%s%s"),
|
|
Packit |
90a5c9 |
req->method, req->scheme, req->authority, req->path);
|
|
Packit |
90a5c9 |
stream->rtmp = req;
|
|
Packit |
90a5c9 |
/* simulate the frames that led to this */
|
|
Packit |
90a5c9 |
return h2_stream_recv_frame(stream, NGHTTP2_HEADERS,
|
|
Packit |
90a5c9 |
NGHTTP2_FLAG_END_STREAM, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void h2_stream_set_request(h2_stream *stream, const h2_request *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_assert(stream->request == NULL);
|
|
Packit |
90a5c9 |
ap_assert(stream->rtmp == NULL);
|
|
Packit |
90a5c9 |
stream->rtmp = h2_request_clone(stream->pool, r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void set_error_response(h2_stream *stream, int http_status)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (!h2_stream_is_ready(stream)) {
|
|
Packit |
90a5c9 |
conn_rec *c = stream->session->c;
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
h2_headers *response;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
response = h2_headers_die(http_status, stream->request, stream->pool);
|
|
Packit |
90a5c9 |
prep_output(stream);
|
|
Packit |
90a5c9 |
b = apr_bucket_eos_create(c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b);
|
|
Packit |
90a5c9 |
b = h2_bucket_headers_create(c->bucket_alloc, response);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t add_trailer(h2_stream *stream,
|
|
Packit |
90a5c9 |
const char *name, size_t nlen,
|
|
Packit |
90a5c9 |
const char *value, size_t vlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
conn_rec *c = stream->session->c;
|
|
Packit |
90a5c9 |
char *hname, *hvalue;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (nlen == 0 || name[0] == ':') {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c,
|
|
Packit |
90a5c9 |
H2_STRM_LOG(APLOGNO(03060), stream,
|
|
Packit |
90a5c9 |
"pseudo header in trailer"));
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (h2_req_ignore_trailer(name, nlen)) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!stream->trailers) {
|
|
Packit |
90a5c9 |
stream->trailers = apr_table_make(stream->pool, 5);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
hname = apr_pstrndup(stream->pool, name, nlen);
|
|
Packit |
90a5c9 |
hvalue = apr_pstrndup(stream->pool, value, vlen);
|
|
Packit |
90a5c9 |
h2_util_camel_case_header(hname, nlen);
|
|
Packit |
90a5c9 |
apr_table_mergen(stream->trailers, hname, hvalue);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_add_header(h2_stream *stream,
|
|
Packit |
90a5c9 |
const char *name, size_t nlen,
|
|
Packit |
90a5c9 |
const char *value, size_t vlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_session *session = stream->session;
|
|
Packit |
90a5c9 |
int error = 0;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->has_response) {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
++stream->request_headers_added;
|
|
Packit |
90a5c9 |
if (name[0] == ':') {
|
|
Packit |
90a5c9 |
if ((vlen) > session->s->limit_req_line) {
|
|
Packit |
90a5c9 |
/* pseudo header: approximation of request line size check */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "pseudo %s too long"), name);
|
|
Packit |
90a5c9 |
error = HTTP_REQUEST_URI_TOO_LARGE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if ((nlen + 2 + vlen) > session->s->limit_req_fieldsize) {
|
|
Packit |
90a5c9 |
/* header too long */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "header %s too long"), name);
|
|
Packit |
90a5c9 |
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->request_headers_added > session->s->limit_req_fields + 4) {
|
|
Packit |
90a5c9 |
/* too many header lines, include 4 pseudo headers */
|
|
Packit |
90a5c9 |
if (stream->request_headers_added
|
|
Packit |
90a5c9 |
> session->s->limit_req_fields + 4 + 100) {
|
|
Packit |
90a5c9 |
/* yeah, right */
|
|
Packit |
90a5c9 |
h2_stream_rst(stream, H2_ERR_ENHANCE_YOUR_CALM);
|
|
Packit |
90a5c9 |
return APR_ECONNRESET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "too many header lines"));
|
|
Packit |
90a5c9 |
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (error) {
|
|
Packit |
90a5c9 |
set_error_response(stream, error);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_SS_IDLE == stream->state) {
|
|
Packit |
90a5c9 |
if (!stream->rtmp) {
|
|
Packit |
90a5c9 |
stream->rtmp = h2_req_create(stream->id, stream->pool,
|
|
Packit |
90a5c9 |
NULL, NULL, NULL, NULL, NULL, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = h2_request_add_header(stream->rtmp, stream->pool,
|
|
Packit |
90a5c9 |
name, nlen, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_SS_OPEN == stream->state) {
|
|
Packit |
90a5c9 |
status = add_trailer(stream, name, nlen, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
status = APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "header %s not accepted"), name);
|
|
Packit |
90a5c9 |
h2_stream_dispatch(stream, H2_SEV_CANCELLED);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_bucket *get_first_headers_bucket(apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (bb) {
|
|
Packit |
90a5c9 |
apr_bucket *b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
while (b != APR_BRIGADE_SENTINEL(bb)) {
|
|
Packit |
90a5c9 |
if (H2_BUCKET_IS_HEADERS(b)) {
|
|
Packit |
90a5c9 |
return b;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
b = APR_BUCKET_NEXT(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t add_buffered_data(h2_stream *stream, apr_off_t requested,
|
|
Packit |
90a5c9 |
apr_off_t *plen, int *peos, int *is_all,
|
|
Packit |
90a5c9 |
h2_headers **pheaders)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket *b, *e;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*peos = 0;
|
|
Packit |
90a5c9 |
*plen = 0;
|
|
Packit |
90a5c9 |
*is_all = 0;
|
|
Packit |
90a5c9 |
if (pheaders) {
|
|
Packit |
90a5c9 |
*pheaders = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "add_buffered_data");
|
|
Packit |
90a5c9 |
b = APR_BRIGADE_FIRST(stream->out_buffer);
|
|
Packit |
90a5c9 |
while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(b);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_METADATA(b)) {
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_FLUSH(b)) {
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_BUCKET_IS_EOS(b)) {
|
|
Packit |
90a5c9 |
*peos = 1;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_BUCKET_IS_HEADERS(b)) {
|
|
Packit |
90a5c9 |
if (*plen > 0) {
|
|
Packit |
90a5c9 |
/* data before the response, can only return up to here */
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (pheaders) {
|
|
Packit |
90a5c9 |
*pheaders = h2_bucket_headers_get(b);
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "prep, -> response %d"),
|
|
Packit |
90a5c9 |
(*pheaders)->status);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return APR_EAGAIN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (b->length == 0) {
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_assert(b->length != (apr_size_t)-1);
|
|
Packit |
90a5c9 |
*plen += b->length;
|
|
Packit |
90a5c9 |
if (*plen >= requested) {
|
|
Packit |
90a5c9 |
*plen = requested;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
b = e;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*is_all = 1;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_out_prepare(h2_stream *stream, apr_off_t *plen,
|
|
Packit |
90a5c9 |
int *peos, h2_headers **pheaders)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_off_t requested, missing, max_chunk = H2_DATA_CHUNK_SIZE;
|
|
Packit |
90a5c9 |
conn_rec *c;
|
|
Packit |
90a5c9 |
int complete;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(stream);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->rst_error) {
|
|
Packit |
90a5c9 |
*plen = 0;
|
|
Packit |
90a5c9 |
*peos = 1;
|
|
Packit |
90a5c9 |
return APR_ECONNRESET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
c = stream->session->c;
|
|
Packit |
90a5c9 |
prep_output(stream);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* determine how much we'd like to send. We cannot send more than
|
|
Packit |
90a5c9 |
* is requested. But we can reduce the size in case the master
|
|
Packit |
90a5c9 |
* connection operates in smaller chunks. (TSL warmup) */
|
|
Packit |
90a5c9 |
if (stream->session->io.write_size > 0) {
|
|
Packit |
90a5c9 |
max_chunk = stream->session->io.write_size - 9; /* header bits */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
requested = (*plen > 0)? H2MIN(*plen, max_chunk) : max_chunk;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* count the buffered data until eos or a headers bucket */
|
|
Packit |
90a5c9 |
status = add_buffered_data(stream, requested, plen, peos, &complete, pheaders);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status == APR_EAGAIN) {
|
|
Packit |
90a5c9 |
/* TODO: ugly, someone needs to retrieve the response first */
|
|
Packit |
90a5c9 |
h2_mplx_keep_active(stream->session->mplx, stream);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "prep, response eagain"));
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (pheaders && *pheaders) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If there we do not have enough buffered data to satisfy the requested
|
|
Packit |
90a5c9 |
* length *and* we counted the _complete_ buffer (and did not stop in the middle
|
|
Packit |
90a5c9 |
* because of meta data there), lets see if we can read more from the
|
|
Packit |
90a5c9 |
* output beam */
|
|
Packit |
90a5c9 |
missing = H2MIN(requested, stream->max_mem) - *plen;
|
|
Packit |
90a5c9 |
if (complete && !*peos && missing > 0) {
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_EOF;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->output) {
|
|
Packit |
90a5c9 |
H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre");
|
|
Packit |
90a5c9 |
rv = h2_beam_receive(stream->output, stream->out_buffer,
|
|
Packit |
90a5c9 |
APR_NONBLOCK_READ, stream->max_mem - *plen);
|
|
Packit |
90a5c9 |
H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "post");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* count the buffer again, now that we have read output */
|
|
Packit |
90a5c9 |
status = add_buffered_data(stream, requested, plen, peos, &complete, pheaders);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_STATUS_IS_EOF(rv)) {
|
|
Packit |
90a5c9 |
apr_bucket *eos = apr_bucket_eos_create(c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(stream->out_buffer, eos);
|
|
Packit |
90a5c9 |
*peos = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit |
90a5c9 |
/* we set this is the status of this call only if there
|
|
Packit |
90a5c9 |
* is no buffered data, see check below */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* real error reading. Give this back directly, even though
|
|
Packit |
90a5c9 |
* we may have something buffered. */
|
|
Packit |
90a5c9 |
status = rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
if (*peos || *plen) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "prepare, len=%ld eos=%d"),
|
|
Packit |
90a5c9 |
(long)*plen, *peos);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
status = (stream->output && h2_beam_is_closed(stream->output))? APR_EOF : APR_EAGAIN;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "prepare, no data"));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int is_not_headers(apr_bucket *b)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return !H2_BUCKET_IS_HEADERS(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_off_t *plen, int *peos)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
conn_rec *c = stream->session->c;
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (stream->rst_error) {
|
|
Packit |
90a5c9 |
return APR_ECONNRESET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = h2_append_brigade(bb, stream->out_buffer, plen, peos, is_not_headers);
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS && !*peos && !*plen) {
|
|
Packit |
90a5c9 |
status = APR_EAGAIN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "read_to, len=%ld eos=%d"),
|
|
Packit |
90a5c9 |
(long)*plen, *peos);
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_submit_pushes(h2_stream *stream, h2_headers *response)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_array_header_t *pushes;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
pushes = h2_push_collect_update(stream, stream->request, response);
|
|
Packit |
90a5c9 |
if (pushes && !apr_is_empty_array(pushes)) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
|
|
Packit |
90a5c9 |
H2_STRM_MSG(stream, "found %d push candidates"),
|
|
Packit |
90a5c9 |
pushes->nelts);
|
|
Packit |
90a5c9 |
for (i = 0; i < pushes->nelts; ++i) {
|
|
Packit |
90a5c9 |
h2_push *push = APR_ARRAY_IDX(pushes, i, h2_push*);
|
|
Packit |
90a5c9 |
h2_stream *s = h2_session_push(stream->session, stream, push);
|
|
Packit |
90a5c9 |
if (!s) {
|
|
Packit |
90a5c9 |
status = APR_ECONNRESET;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_t *h2_stream_get_trailers(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const h2_priority *h2_stream_get_priority(h2_stream *stream,
|
|
Packit |
90a5c9 |
h2_headers *response)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (response && stream->initiated_on) {
|
|
Packit |
90a5c9 |
const char *ctype = apr_table_get(response->headers, "content-type");
|
|
Packit |
90a5c9 |
if (ctype) {
|
|
Packit |
90a5c9 |
/* FIXME: Not good enough, config needs to come from request->server */
|
|
Packit |
90a5c9 |
return h2_config_get_priority(stream->session->config, ctype);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int h2_stream_is_ready(h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (stream->has_response) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (stream->out_buffer && get_first_headers_bucket(stream->out_buffer)) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int h2_stream_was_closed(const h2_stream *stream)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
switch (stream->state) {
|
|
Packit |
90a5c9 |
case H2_SS_CLOSED:
|
|
Packit |
90a5c9 |
case H2_SS_CLEANUP:
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_stream_in_consumed(h2_stream *stream, apr_off_t amount)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_session *session = stream->session;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (amount > 0) {
|
|
Packit |
90a5c9 |
apr_off_t consumed = amount;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (consumed > 0) {
|
|
Packit |
90a5c9 |
int len = (consumed > INT_MAX)? INT_MAX : (int)consumed;
|
|
Packit |
90a5c9 |
nghttp2_session_consume(session->ngh2, stream->id, len);
|
|
Packit |
90a5c9 |
consumed -= len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifdef H2_NG2_LOCAL_WIN_SIZE
|
|
Packit |
90a5c9 |
if (1) {
|
|
Packit |
90a5c9 |
int cur_size = nghttp2_session_get_stream_local_window_size(
|
|
Packit |
90a5c9 |
session->ngh2, stream->id);
|
|
Packit |
90a5c9 |
int win = stream->in_window_size;
|
|
Packit |
90a5c9 |
int thigh = win * 8/10;
|
|
Packit |
90a5c9 |
int tlow = win * 2/10;
|
|
Packit |
90a5c9 |
const int win_max = 2*1024*1024;
|
|
Packit |
90a5c9 |
const int win_min = 32*1024;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Work in progress, probably should add directives for these
|
|
Packit |
90a5c9 |
* values once this stabilizes somewhat. The general idea is
|
|
Packit |
90a5c9 |
* to adapt stream window sizes if the input window changes
|
|
Packit |
90a5c9 |
* a) very quickly (< good RTT) from full to empty
|
|
Packit |
90a5c9 |
* b) only a little bit (> bad RTT)
|
|
Packit |
90a5c9 |
* where in a) it grows and in b) it shrinks again.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (cur_size > thigh && amount > thigh && win < win_max) {
|
|
Packit |
90a5c9 |
/* almost empty again with one reported consumption, how
|
|
Packit |
90a5c9 |
* long did this take? */
|
|
Packit |
90a5c9 |
long ms = apr_time_msec(apr_time_now() - stream->in_last_write);
|
|
Packit |
90a5c9 |
if (ms < 40) {
|
|
Packit |
90a5c9 |
win = H2MIN(win_max, win + (64*1024));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (cur_size < tlow && amount < tlow && win > win_min) {
|
|
Packit |
90a5c9 |
/* staying full, for how long already? */
|
|
Packit |
90a5c9 |
long ms = apr_time_msec(apr_time_now() - stream->in_last_write);
|
|
Packit |
90a5c9 |
if (ms > 700) {
|
|
Packit |
90a5c9 |
win = H2MAX(win_min, win - (32*1024));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (win != stream->in_window_size) {
|
|
Packit |
90a5c9 |
stream->in_window_size = win;
|
|
Packit |
90a5c9 |
nghttp2_session_set_local_window_size(session->ngh2,
|
|
Packit |
90a5c9 |
NGHTTP2_FLAG_NONE, stream->id, win);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
|
|
Packit |
90a5c9 |
"h2_stream(%ld-%d): consumed %ld bytes, window now %d/%d",
|
|
Packit |
90a5c9 |
session->id, stream->id, (long)amount,
|
|
Packit |
90a5c9 |
cur_size, stream->in_window_size);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|