static apr_status_t bucket_read(apr_bucket *b, const char **str,
apr_size_t *len, apr_read_type_e block)
{
+ (void)b;
+ (void)block;
*str = NULL;
*len = 0;
return APR_SUCCESS;
static apr_status_t bucket_read(apr_bucket *b, const char **str,
apr_size_t *len, apr_read_type_e block)
{
+ (void)b;
+ (void)block;
*str = NULL;
*len = 0;
return APR_SUCCESS;
apr_status_t h2_conn_io_consider_flush(h2_conn_io *io)
{
apr_status_t status = APR_SUCCESS;
- int flush_now = 0;
/* The HTTP/1.1 network output buffer/flush behaviour does not
* give optimal performance in the HTTP/2 case, as the pattern of
* buckets (data/eor/eos) is different.
- * As long as we do not have found out the "best" way to deal with
+ * As long as we have not found out the "best" way to deal with
* this, force a flush at least every WRITE_BUFFER_SIZE amount
- * of data which seems to work nicely.
+ * of data.
*/
if (io->unflushed) {
apr_off_t len = 0;
apr_brigade_length(io->output, 0, &len);
}
len += io->buflen;
- flush_now = (len >= WRITE_BUFFER_SIZE);
+ if (len >= WRITE_BUFFER_SIZE) {
+ return h2_conn_io_flush(io);
+ }
}
- if (flush_now) {
- return h2_conn_io_flush(io);
- }
return status;
}
return status;
}
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, io->connection,
+ "h2_conn_io: flushed");
io->unflushed = 0;
}
return APR_SUCCESS;
apr_time_t cooldown_usecs;
apr_int64_t warmup_size;
- int write_size;
+ apr_size_t write_size;
apr_time_t last_write;
apr_int64_t bytes_written;
}
if (!apr_is_empty_array(r->content_languages)) {
- int i;
+ unsigned int i;
char *token;
char **languages = (char **)(r->content_languages->elts);
const char *field = apr_table_get(r->headers_out, "Content-Language");
apr_pool_t *pool;
apr_bucket_brigade *bb;
- apr_size_t content_length;
+ apr_off_t content_length;
int chunked;
const char *status;
"http/1.1 required",
};
-const char *h2_h2_err_description(int h2_error)
+const char *h2_h2_err_description(unsigned int h2_error)
{
- if (h2_error >= 0
- && h2_error < (sizeof(h2_err_descr)/sizeof(h2_err_descr[0]))) {
+ if (h2_error < (sizeof(h2_err_descr)/sizeof(h2_err_descr[0]))) {
return h2_err_descr[h2_error];
}
return "unknown http/2 errotr code";
{
apr_hash_t *hash = apr_hash_make(pool);
const char *source;
- int i;
+ unsigned int i;
source = "rfc7540";
for (i = 0; i < RFC7540_names_LEN; ++i) {
/* Need Tlsv1.2 or higher, rfc 7540, ch. 9.2
*/
- val = opt_ssl_var_lookup(pool, s, c, NULL, "SSL_PROTOCOL");
+ val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_PROTOCOL");
if (val && *val) {
if (strncmp("TLS", val, 3)
|| !strcmp("TLSv1", val)
/* Check TLS cipher blacklist
*/
- val = opt_ssl_var_lookup(pool, s, c, NULL, "SSL_CIPHER");
+ val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_CIPHER");
if (val && *val) {
const char *source;
if (cipher_is_blacklisted(val, &source)) {
* @param h2_error http/2 error code, as in rfc 7540, ch. 7
* @return textual description of code or that it is unknown.
*/
-const char *h2_h2_err_description(int h2_error);
+const char *h2_h2_err_description(unsigned int h2_error);
/*
* One time, post config intialization.
return h2_util_bb_has_data_or_eos(io->bbout);
}
-apr_size_t h2_io_out_length(h2_io *io)
+apr_off_t h2_io_out_length(h2_io *io)
{
if (io->bbout) {
apr_off_t len = 0;
apr_status_t h2_io_out_readx(h2_io *io,
h2_io_data_cb *cb, void *ctx,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status;
}
apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
if (io->rst_error) {
return APR_ECONNABORTED;
struct h2_task;
-typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_size_t len);
+typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_off_t len);
typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
*/
apr_status_t h2_io_out_readx(h2_io *io,
h2_io_data_cb *cb, void *ctx,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
apr_status_t h2_io_out_read_to(h2_io *io,
apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb,
apr_size_t maxlen, int *pfile_buckets_allowed);
* Gives the overall length of the data that is currently queued for
* output.
*/
-apr_size_t h2_io_out_length(h2_io *io);
+apr_off_t h2_io_out_length(h2_io *io);
#endif /* defined(__mod_h2__h2_io__) */
apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id,
h2_io_data_cb *cb, void *ctx,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status;
AP_DEBUG_ASSERT(m);
apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id,
apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status;
AP_DEBUG_ASSERT(m);
* Callback invoked for every stream that had input data read since
* the last invocation.
*/
-typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_size_t consumed);
+typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_off_t consumed);
/**
* Invoke the callback for all streams that had bytes read since the last
*/
apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id,
h2_io_data_cb *cb, void *ctx,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
/**
* Reads output data into the given brigade. Will never block, but
*/
apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id,
apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
/**
* Opens the output for the given stream with the specified response.
return 0;
}
+static apr_status_t h2_session_flush(h2_session *session)
+{
+ session->flush = 0;
+ return h2_conn_io_flush(&session->io);
+}
+
/**
* Determine the importance of streams when scheduling tasks.
* - if both stream depend on the same one, compare weights
* following frame types */
switch (frame->hd.type) {
case NGHTTP2_RST_STREAM:
- case NGHTTP2_WINDOW_UPDATE:
case NGHTTP2_PUSH_PROMISE:
- case NGHTTP2_PING:
case NGHTTP2_GOAWAY:
session->flush = 1;
break;
frame->priority.pri_spec.exclusive);
break;
}
+ case NGHTTP2_WINDOW_UPDATE: {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+ "h2_session: stream(%ld-%d): WINDOW_UPDATE "
+ "incr=%d",
+ session->id, (int)frame->hd.stream_id,
+ frame->window_update.window_size_increment);
+ break;
+ }
default:
if (APLOGctrace2(session->c)) {
char buffer[256];
}
static apr_status_t pass_data(void *ctx,
- const char *data, apr_size_t length)
+ const char *data, apr_off_t length)
{
return h2_conn_io_write(&((h2_session*)ctx)->io, data, length);
}
apr_status_t status = APR_SUCCESS;
h2_session *session = (h2_session *)userp;
int stream_id = (int)frame->hd.stream_id;
- const unsigned char padlen = frame->data.padlen;
+ unsigned char padlen;
int eos;
h2_stream *stream;
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
+ if (frame->data.padlen > H2_MAX_PADLEN) {
+ return NGHTTP2_ERR_PROTO;
+ }
+ padlen = (unsigned char)frame->data.padlen;
+
stream = h2_session_get_stream(session, stream_id);
if (!stream) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
}
if (status == APR_SUCCESS) {
- apr_size_t len = length;
- status = h2_stream_readx(stream, pass_data, session,
- &len, &eos);
+ apr_off_t len = length;
+ status = h2_stream_readx(stream, pass_data, session, &len, &eos);
if (status == APR_SUCCESS && len != length) {
status = APR_EINVAL;
}
status = h2_conn_io_writeb(&session->io, b);
if (status == APR_SUCCESS) {
- apr_size_t len = length;
+ apr_off_t len = length;
status = h2_stream_read_to(stream, session->io.output, &len, &eos);
session->io.unflushed = 1;
if (status == APR_SUCCESS && len != length) {
return h2_session_status_from_apr_status(status);
}
-static ssize_t on_data_source_read_length_cb(nghttp2_session *session,
- uint8_t frame_type, int32_t stream_id,
- int32_t session_remote_window_size,
- int32_t stream_remote_window_size,
- uint32_t remote_max_frame_size,
- void *user_data)
-{
- /* DATA frames add 9 bytes header plus 1 byte for padlen and additional
- * padlen bytes. Keep below TLS maximum record size.
- * TODO: respect pad bytes when we have that feature.
- */
- return (16*1024 - 10);
-}
-
#define NGH2_SET_CALLBACK(callbacks, name, fn)\
nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
- NGH2_SET_CALLBACK(*pcb, data_source_read_length, on_data_source_read_length_cb);
return APR_SUCCESS;
}
strlen(err));
nghttp2_session_send(session->ngh2);
}
- h2_conn_io_flush(&session->io);
+ h2_session_flush(session);
}
h2_mplx_abort(session->mplx);
}
return 0;
}
-static void update_window(void *ctx, int stream_id, apr_size_t bytes_read)
+static void update_window(void *ctx, int stream_id, apr_off_t bytes_read)
{
h2_session *session = (h2_session*)ctx;
nghttp2_session_consume(session->ngh2, stream_id, bytes_read);
}
-static apr_status_t h2_session_flush(h2_session *session)
-{
- session->flush = 0;
- return h2_conn_io_flush(&session->io);
-}
-
static apr_status_t h2_session_update_windows(h2_session *session)
{
+ /* TODO: only do this, when we have streams with open input */
return h2_mplx_in_update_windows(session->mplx, update_window, session);
}
}
/* If we have responses ready, submit them now. */
+ /* TODO: only call this when we have unsubmitted streams */
while (!session->aborted
&& (stream = h2_mplx_next_submit(session->mplx, session->streams)) != NULL) {
status = h2_session_handle_response(session, stream);
h2_conn_io_writeb(&session->io,
h2_bucket_eoc_create(session->c->bucket_alloc,
session));
- return h2_conn_io_flush(&session->io);
+ return h2_session_flush(session);
}
static ssize_t stream_data_cb(nghttp2_session *ng2s,
void *puser)
{
h2_session *session = (h2_session *)puser;
- apr_size_t nread = length;
+ apr_off_t nread = length;
int eos = 0;
apr_status_t status;
h2_stream *stream;
"h2_stream_set_response");
}
if (APLOGctrace1(stream->session->c)) {
- apr_size_t len = 0;
+ apr_off_t len = 0;
int eos = 0;
h2_util_bb_avail(stream->bbout, &len, &eos);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c,
}
apr_status_t h2_stream_prep_read(h2_stream *stream,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status = APR_SUCCESS;
const char *src;
apr_status_t h2_stream_readx(h2_stream *stream,
h2_io_data_cb *cb, void *ctx,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status = APR_SUCCESS;
const char *src;
}
*peos = 0;
if (!APR_BRIGADE_EMPTY(stream->bbout)) {
- apr_size_t origlen = *plen;
+ apr_off_t origlen = *plen;
src = "stream";
status = h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos);
}
apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status = APR_SUCCESS;
}
if (APR_BRIGADE_EMPTY(stream->bbout)) {
- apr_size_t tlen = *plen;
+ apr_off_t tlen = *plen;
int eos;
status = h2_mplx_out_read_to(stream->session->mplx, stream->id,
stream->bbout, &tlen, &eos);
{
AP_DEBUG_ASSERT(stream);
stream->suspended = !!suspended;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
+ "h2_stream(%ld-%d): suspended=%d",
+ stream->session->id, stream->id, stream->suspended);
}
int h2_stream_is_suspended(h2_stream *stream)
struct h2_response *response; /* the response, once ready */
apr_bucket_brigade *bbout; /* output DATA */
- apr_size_t data_frames_sent;/* # of DATA frames sent out for this stream */
+ apr_off_t data_frames_sent; /* # of DATA frames sent out for this stream */
};
apr_bucket_brigade *bb);
apr_status_t h2_stream_prep_read(h2_stream *stream,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
apr_status_t h2_stream_readx(h2_stream *stream, h2_io_data_cb *cb,
- void *ctx, apr_size_t *plen, int *peos);
+ void *ctx, apr_off_t *plen, int *peos);
apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
void h2_stream_set_suspended(h2_stream *stream, int suspended);
static const int FILE_MOVE = 1;
static apr_status_t last_not_included(apr_bucket_brigade *bb,
- apr_size_t maxlen,
+ apr_off_t maxlen,
int same_alloc,
int *pfile_buckets_allowed,
apr_bucket **pend)
#define LOG_LEVEL APLOG_INFO
apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from,
- apr_size_t maxlen, int *pfile_handles_allowed,
+ apr_off_t maxlen, int *pfile_handles_allowed,
const char *msg)
{
apr_status_t status = APR_SUCCESS;
}
apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from,
- apr_size_t maxlen, const char *msg)
+ apr_off_t maxlen, const char *msg)
{
apr_status_t status = APR_SUCCESS;
int same_alloc;
return 0;
}
-int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len)
+int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len)
{
apr_bucket *b, *end;
}
apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status;
apr_off_t blen = 0;
apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb,
h2_util_pass_cb *cb, void *ctx,
- apr_size_t *plen, int *peos)
+ apr_off_t *plen, int *peos)
{
apr_status_t status = APR_SUCCESS;
int consume = (cb != NULL);
- apr_size_t written = 0;
- apr_size_t avail = *plen;
+ apr_off_t written = 0;
+ apr_off_t avail = *plen;
apr_bucket *next, *b;
/* Pass data in our brigade through the callback until the length
if (b->length == ((apr_size_t)-1)) {
/* read to determine length */
- status = apr_bucket_read(b, &data, &data_len,
- APR_NONBLOCK_READ);
+ status = apr_bucket_read(b, &data, &data_len, APR_NONBLOCK_READ);
}
else {
data_len = b->length;
apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
apr_bucket_brigade *from,
apr_pool_t *p,
- apr_size_t *plen,
+ apr_off_t *plen,
int *peos)
{
apr_bucket *e;
- apr_size_t len = 0, remain = *plen;
+ apr_off_t len = 0, remain = *plen;
apr_status_t rv;
*peos = 0;
* if needed.
* @param to the brigade to move the data to
* @param from the brigade to get the data from
- * @param maxlen of bytes to move, 0 for all
+ * @param maxlen of bytes to move, <= 0 for all
* @param pfile_buckets_allowed how many file buckets may be moved,
* may be 0 or NULL
* @param msg message for use in logging
*/
apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from,
- apr_size_t maxlen, int *pfile_buckets_allowed,
+ apr_off_t maxlen, int *pfile_buckets_allowed,
const char *msg);
/**
* if needed.
* @param to the brigade to copy the data to
* @param from the brigade to get the data from
- * @param maxlen of bytes to copy, 0 for all
+ * @param maxlen of bytes to copy, <= 0 for all
* @param msg message for use in logging
*/
apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from,
- apr_size_t maxlen, const char *msg);
+ apr_off_t maxlen, const char *msg);
/**
* Return != 0 iff there is a FLUSH or EOS bucket in the brigade.
* @return != 0 iff brigade holds FLUSH or EOS bucket (or both)
*/
int h2_util_has_flush_or_eos(apr_bucket_brigade *bb);
-int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len);
+int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len);
int h2_util_bb_has_data(apr_bucket_brigade *bb);
int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb);
* @param on return, if eos has been reached
*/
apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
typedef apr_status_t h2_util_pass_cb(void *ctx,
- const char *data, apr_size_t len);
+ const char *data, apr_off_t len);
/**
* Read at most *plen bytes from the brigade and pass them into the
*/
apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb,
h2_util_pass_cb *cb, void *ctx,
- apr_size_t *plen, int *peos);
+ apr_off_t *plen, int *peos);
/**
* Logs the bucket brigade (which bucket types with what length)
apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
apr_bucket_brigade *from,
apr_pool_t *p,
- apr_size_t *plen,
+ apr_off_t *plen,
int *peos);
#endif /* defined(__mod_h2__h2_util__) */