You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2015/11/20 16:13:11 UTC
svn commit: r1715371 [6/6] - in /httpd/httpd/branches/2.4.x: ./
docs/manual/mod/ modules/http2/
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_queue.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_queue.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_queue.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_queue.h Fri Nov 20 15:13:11 2015
@@ -19,29 +19,38 @@
struct h2_task;
/**
- * A simple ring of rings that keeps a list of h2_tasks and can
- * be ringed itself, using the APR RING macros.
+ * h2_task_queue keeps a list of sorted h2_task* in ascending order.
*/
typedef struct h2_task_queue h2_task_queue;
struct h2_task_queue {
- APR_RING_ENTRY(h2_task_queue) link;
- APR_RING_HEAD(h2_tasks, h2_task) tasks;
- long id;
+ int *elts;
+ int head;
+ int nelts;
+ int nalloc;
+ apr_pool_t *pool;
};
/**
- * Allocate a new queue from the pool and initialize.
- * @param id the identifier of the queue
- * @param pool the memory pool
+ * Comparator for two task to determine their order.
+ *
+ * @param s1 stream id to compare
+ * @param s2 stream id to compare
+ * @param ctx provided user data
+ * @return value is the same as for strcmp() and has the effect:
+ * == 0: s1 and s2 are treated equal in ordering
+ * < 0: s1 should be sorted before s2
+ * > 0: s2 should be sorted before s1
*/
-h2_task_queue *h2_tq_create(long id, apr_pool_t *pool);
+typedef int h2_tq_cmp(int s1, int s2, void *ctx);
+
/**
- * Release all queue tasks.
- * @param q the queue to destroy
+ * Allocate a new queue from the pool and initialize.
+ * @param id the identifier of the queue
+ * @param pool the memory pool
*/
-void h2_tq_destroy(h2_task_queue *q);
+h2_task_queue *h2_tq_create(apr_pool_t *pool, int capacity);
/**
* Return != 0 iff there are no tasks in the queue.
@@ -50,99 +59,41 @@ void h2_tq_destroy(h2_task_queue *q);
int h2_tq_empty(h2_task_queue *q);
/**
- * Append the task to the end of the queue.
+ * Add a stream idto the queue.
+ *
* @param q the queue to append the task to
- * @param task the task to append
- */
-void h2_tq_append(h2_task_queue *q, struct h2_task *task);
-
-/**
- * Remove a task from the queue. Return APR_SUCCESS if the task
- * was indeed queued, APR_NOTFOUND otherwise.
- * @param q the queue to remove from
- * @param task the task to remove
- */
-apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task);
-
-/**
- * Get the first task from the queue or NULL if the queue is empty. The
- * task will be removed.
- * @param q the queue to pop the first task from
- */
-h2_task *h2_tq_pop_first(h2_task_queue *q);
-
-/*******************************************************************************
- * Queue Manipulation.
- ******************************************************************************/
-
-/**
- * The magic pointer value that indicates the head of a h2_task_queue list
- * @param b The queue list
- * @return The magic pointer value
+ * @param sid the stream id to add
+ * @param cmp the comparator for sorting
+ * @param ctx user data for comparator
*/
-#define H2_TQ_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_task_queue, link)
+void h2_tq_add(h2_task_queue *q, int sid, h2_tq_cmp *cmp, void *ctx);
/**
- * Determine if the queue list is empty
- * @param b The list to check
- * @return true or false
+ * Remove the stream id from the queue. Return != 0 iff task
+ * was found in queue.
+ * @param q the task queue
+ * @param sid the stream id to remove
+ * @return != 0 iff task was found in queue
*/
-#define H2_TQ_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_task_queue, link)
+int h2_tq_remove(h2_task_queue *q, int sid);
/**
- * Return the first queue in a list
- * @param b The list to query
- * @return The first queue in the list
+ * Sort the stream idqueue again. Call if the task ordering
+ * has changed.
+ *
+ * @param q the queue to sort
+ * @param cmp the comparator for sorting
+ * @param ctx user data for the comparator
*/
-#define H2_TQ_LIST_FIRST(b) APR_RING_FIRST(b)
+void h2_tq_sort(h2_task_queue *q, h2_tq_cmp *cmp, void *ctx);
/**
- * Return the last queue in a list
- * @param b The list to query
- * @return The last queue int he list
+ * Get the first stream id from the queue or NULL if the queue is empty.
+ * The task will be removed.
+ *
+ * @param q the queue to get the first task from
+ * @return the first stream id of the queue, 0 if empty
*/
-#define H2_TQ_LIST_LAST(b) APR_RING_LAST(b)
-
-/**
- * Insert a single queue at the front of a list
- * @param b The list to add to
- * @param e The queue to insert
- */
-#define H2_TQ_LIST_INSERT_HEAD(b, e) do { \
-h2_task_queue *ap__b = (e); \
-APR_RING_INSERT_HEAD((b), ap__b, h2_task_queue, link); \
-} while (0)
-
-/**
- * Insert a single queue at the end of a list
- * @param b The list to add to
- * @param e The queue to insert
- */
-#define H2_TQ_LIST_INSERT_TAIL(b, e) do { \
-h2_task_queue *ap__b = (e); \
-APR_RING_INSERT_TAIL((b), ap__b, h2_task_queue, link); \
-} while (0)
-
-/**
- * Get the next queue in the list
- * @param e The current queue
- * @return The next queue
- */
-#define H2_TQ_NEXT(e) APR_RING_NEXT((e), link)
-/**
- * Get the previous queue in the list
- * @param e The current queue
- * @return The previous queue
- */
-#define H2_TQ_PREV(e) APR_RING_PREV((e), link)
-
-/**
- * Remove a queue from its list
- * @param e The queue to remove
- */
-#define H2_TQ_REMOVE(e) APR_RING_REMOVE((e), link)
-
-
-#define H2_TQ_EMPTY(e) H2_TASK_LIST_EMPTY(&(e)->tasks)
+int h2_tq_shift(h2_task_queue *q);
#endif /* defined(__mod_h2__h2_task_queue__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.c?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.c Fri Nov 20 15:13:11 2015
@@ -20,10 +20,12 @@
#include <httpd.h>
#include <http_core.h>
#include <http_log.h>
+#include <http_request.h>
#include <nghttp2/nghttp2.h>
#include "h2_private.h"
+#include "h2_request.h"
#include "h2_util.h"
size_t h2_util_hex_dump(char *buffer, size_t maxlen,
@@ -204,6 +206,10 @@ const char *h2_util_first_token_match(ap
return NULL;
}
+/*******************************************************************************
+ * h2_util for bucket brigades
+ ******************************************************************************/
+
/* DEEP_COPY==0 crashes under load. I think the setaside is fine,
* however buckets moved to another thread will still be
* free'd against the old bucket_alloc. *And* if the old
@@ -214,7 +220,7 @@ static const int DEEP_COPY = 1;
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)
@@ -223,7 +229,7 @@ static apr_status_t last_not_included(ap
apr_status_t status = APR_SUCCESS;
int files_allowed = pfile_buckets_allowed? *pfile_buckets_allowed : 0;
- if (maxlen > 0) {
+ if (maxlen >= 0) {
/* Find the bucket, up to which we reach maxlen/mem bytes */
for (b = APR_BRIGADE_FIRST(bb);
(b != APR_BRIGADE_SENTINEL(bb));
@@ -274,7 +280,7 @@ static apr_status_t last_not_included(ap
#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;
@@ -406,7 +412,7 @@ apr_status_t h2_util_move(apr_bucket_bri
}
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;
@@ -482,7 +488,7 @@ int h2_util_has_flush_or_eos(apr_bucket_
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;
@@ -536,7 +542,7 @@ int h2_util_bb_has_data_or_eos(apr_bucke
}
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;
@@ -549,36 +555,29 @@ apr_status_t h2_util_bb_avail(apr_bucket
else if (blen == 0) {
/* empty brigade, does it have an EOS bucket somwhere? */
*plen = 0;
- *peos = h2_util_has_eos(bb, 0);
+ *peos = h2_util_has_eos(bb, -1);
}
- else if (blen > 0) {
+ else {
/* data in the brigade, limit the length returned. Check for EOS
* bucket only if we indicate data. This is required since plen == 0
* means "the whole brigade" for h2_util_hash_eos()
*/
- if (blen < (apr_off_t)*plen) {
+ if (blen < *plen || *plen < 0) {
*plen = blen;
}
- *peos = (*plen > 0)? h2_util_has_eos(bb, *plen) : 0;
- }
- else if (blen < 0) {
- /* famous SHOULD NOT HAPPEN, sinc we told apr_brigade_length to readall
- */
- *plen = 0;
- *peos = h2_util_has_eos(bb, 0);
- return APR_EINVAL;
+ *peos = h2_util_has_eos(bb, *plen);
}
return APR_SUCCESS;
}
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
@@ -606,8 +605,7 @@ apr_status_t h2_util_bb_readx(apr_bucket
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;
@@ -647,3 +645,343 @@ apr_status_t h2_util_bb_readx(apr_bucket
return status;
}
+void h2_util_bb_log(conn_rec *c, int stream_id, int level,
+ const char *tag, apr_bucket_brigade *bb)
+{
+ char buffer[16 * 1024];
+ const char *line = "(null)";
+ apr_size_t bmax = sizeof(buffer)/sizeof(buffer[0]);
+ int off = 0;
+ apr_bucket *b;
+
+ if (bb) {
+ memset(buffer, 0, bmax--);
+ for (b = APR_BRIGADE_FIRST(bb);
+ bmax && (b != APR_BRIGADE_SENTINEL(bb));
+ b = APR_BUCKET_NEXT(b)) {
+
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ off += apr_snprintf(buffer+off, bmax-off, "eos ");
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ off += apr_snprintf(buffer+off, bmax-off, "flush ");
+ }
+ else if (AP_BUCKET_IS_EOR(b)) {
+ off += apr_snprintf(buffer+off, bmax-off, "eor ");
+ }
+ else {
+ off += apr_snprintf(buffer+off, bmax-off, "meta(unknown) ");
+ }
+ }
+ else {
+ const char *btype = "data";
+ if (APR_BUCKET_IS_FILE(b)) {
+ btype = "file";
+ }
+ else if (APR_BUCKET_IS_PIPE(b)) {
+ btype = "pipe";
+ }
+ else if (APR_BUCKET_IS_SOCKET(b)) {
+ btype = "socket";
+ }
+ else if (APR_BUCKET_IS_HEAP(b)) {
+ btype = "heap";
+ }
+ else if (APR_BUCKET_IS_TRANSIENT(b)) {
+ btype = "transient";
+ }
+ else if (APR_BUCKET_IS_IMMORTAL(b)) {
+ btype = "immortal";
+ }
+#if APR_HAS_MMAP
+ else if (APR_BUCKET_IS_MMAP(b)) {
+ btype = "mmap";
+ }
+#endif
+ else if (APR_BUCKET_IS_POOL(b)) {
+ btype = "pool";
+ }
+
+ off += apr_snprintf(buffer+off, bmax-off, "%s[%ld] ",
+ btype,
+ (long)(b->length == ((apr_size_t)-1)?
+ -1 : b->length));
+ }
+ }
+ line = *buffer? buffer : "(empty)";
+ }
+ ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d)-%s: %s",
+ c->id, stream_id, tag, line);
+
+}
+
+apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+ apr_bucket_brigade *from,
+ apr_pool_t *p,
+ apr_off_t *plen,
+ int *peos)
+{
+ apr_bucket *e;
+ apr_off_t len = 0, remain = *plen;
+ apr_status_t rv;
+
+ *peos = 0;
+
+ while (!APR_BRIGADE_EMPTY(from)) {
+ e = APR_BRIGADE_FIRST(from);
+
+ if (APR_BUCKET_IS_METADATA(e)) {
+ if (APR_BUCKET_IS_EOS(e)) {
+ *peos = 1;
+ }
+ }
+ else {
+ if (remain > 0 && e->length == ((apr_size_t)-1)) {
+ const char *ign;
+ apr_size_t ilen;
+ rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ if (remain < e->length) {
+ if (remain <= 0) {
+ return APR_SUCCESS;
+ }
+ apr_bucket_split(e, remain);
+ }
+ }
+
+ rv = apr_bucket_setaside(e, p);
+
+ /* If the bucket type does not implement setaside, then
+ * (hopefully) morph it into a bucket type which does, and set
+ * *that* aside... */
+ if (rv == APR_ENOTIMPL) {
+ const char *s;
+ apr_size_t n;
+
+ rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ);
+ if (rv == APR_SUCCESS) {
+ rv = apr_bucket_setaside(e, p);
+ }
+ }
+
+ if (rv != APR_SUCCESS) {
+ /* Return an error but still save the brigade if
+ * ->setaside() is really not implemented. */
+ if (rv != APR_ENOTIMPL) {
+ return rv;
+ }
+ }
+
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(to, e);
+ len += e->length;
+ remain -= e->length;
+ }
+
+ *plen = len;
+ return APR_SUCCESS;
+}
+
+/*******************************************************************************
+ * h2_ngheader
+ ******************************************************************************/
+
+int h2_util_ignore_header(const char *name)
+{
+ /* never forward, ch. 8.1.2.2 */
+ return (H2_HD_MATCH_LIT_CS("connection", name)
+ || H2_HD_MATCH_LIT_CS("proxy-connection", name)
+ || H2_HD_MATCH_LIT_CS("upgrade", name)
+ || H2_HD_MATCH_LIT_CS("keep-alive", name)
+ || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
+}
+
+static int count_header(void *ctx, const char *key, const char *value)
+{
+ if (!h2_util_ignore_header(key)) {
+ (*((size_t*)ctx))++;
+ }
+ return 1;
+}
+
+#define NV_ADD_LIT_CS(nv, k, v) add_header(nv, k, sizeof(k) - 1, v, strlen(v))
+#define NV_ADD_CS_CS(nv, k, v) add_header(nv, k, strlen(k), v, strlen(v))
+
+static int add_header(h2_ngheader *ngh,
+ const char *key, size_t key_len,
+ const char *value, size_t val_len)
+{
+ nghttp2_nv *nv = &ngh->nv[ngh->nvlen++];
+
+ nv->name = (uint8_t*)key;
+ nv->namelen = key_len;
+ nv->value = (uint8_t*)value;
+ nv->valuelen = val_len;
+ return 1;
+}
+
+static int add_table_header(void *ctx, const char *key, const char *value)
+{
+ if (!h2_util_ignore_header(key)) {
+ add_header(ctx, key, strlen(key), value, strlen(value));
+ }
+ return 1;
+}
+
+
+h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header)
+{
+ h2_ngheader *ngh;
+ size_t n;
+
+ n = 0;
+ apr_table_do(count_header, &n, header, NULL);
+
+ ngh = apr_pcalloc(p, sizeof(h2_ngheader));
+ ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
+ apr_table_do(add_table_header, ngh, header, NULL);
+
+ return ngh;
+}
+
+h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
+ int http_status,
+ apr_table_t *header)
+{
+ h2_ngheader *ngh;
+ size_t n;
+
+ n = 1;
+ apr_table_do(count_header, &n, header, NULL);
+
+ ngh = apr_pcalloc(p, sizeof(h2_ngheader));
+ ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
+ NV_ADD_LIT_CS(ngh, ":status", apr_psprintf(p, "%d", http_status));
+ apr_table_do(add_table_header, ngh, header, NULL);
+
+ return ngh;
+}
+
+h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
+ const struct h2_request *req)
+{
+
+ h2_ngheader *ngh;
+ size_t n;
+
+ AP_DEBUG_ASSERT(req);
+ AP_DEBUG_ASSERT(req->scheme);
+ AP_DEBUG_ASSERT(req->authority);
+ AP_DEBUG_ASSERT(req->path);
+ AP_DEBUG_ASSERT(req->method);
+
+ n = 4;
+ apr_table_do(count_header, &n, req->headers, NULL);
+
+ ngh = apr_pcalloc(p, sizeof(h2_ngheader));
+ ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
+ NV_ADD_LIT_CS(ngh, ":scheme", req->scheme);
+ NV_ADD_LIT_CS(ngh, ":authority", req->authority);
+ NV_ADD_LIT_CS(ngh, ":path", req->path);
+ NV_ADD_LIT_CS(ngh, ":method", req->method);
+ apr_table_do(add_table_header, ngh, req->headers, NULL);
+
+ return ngh;
+}
+
+/*******************************************************************************
+ * header HTTP/1 <-> HTTP/2 conversions
+ ******************************************************************************/
+
+
+typedef struct {
+ const char *name;
+ size_t len;
+} literal;
+
+#define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) }
+#define H2_ALEN(a) (sizeof(a)/sizeof((a)[0]))
+#define H2_LIT_ARGS(a) (a),H2_ALEN(a)
+
+static literal IgnoredRequestHeaders[] = {
+ H2_DEF_LITERAL("host"),
+ H2_DEF_LITERAL("expect"),
+ H2_DEF_LITERAL("upgrade"),
+ H2_DEF_LITERAL("connection"),
+ H2_DEF_LITERAL("keep-alive"),
+ H2_DEF_LITERAL("http2-settings"),
+ H2_DEF_LITERAL("proxy-connection"),
+ H2_DEF_LITERAL("transfer-encoding"),
+};
+static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */
+ H2_DEF_LITERAL("te"),
+ H2_DEF_LITERAL("host"),
+ H2_DEF_LITERAL("range"),
+ H2_DEF_LITERAL("cookie"),
+ H2_DEF_LITERAL("expect"),
+ H2_DEF_LITERAL("pragma"),
+ H2_DEF_LITERAL("max-forwards"),
+ H2_DEF_LITERAL("cache-control"),
+ H2_DEF_LITERAL("authorization"),
+ H2_DEF_LITERAL("content-length"),
+ H2_DEF_LITERAL("proxy-authorization"),
+};
+static literal IgnoredResponseTrailers[] = {
+ H2_DEF_LITERAL("age"),
+ H2_DEF_LITERAL("date"),
+ H2_DEF_LITERAL("vary"),
+ H2_DEF_LITERAL("cookie"),
+ H2_DEF_LITERAL("expires"),
+ H2_DEF_LITERAL("warning"),
+ H2_DEF_LITERAL("location"),
+ H2_DEF_LITERAL("retry-after"),
+ H2_DEF_LITERAL("cache-control"),
+ H2_DEF_LITERAL("www-authenticate"),
+ H2_DEF_LITERAL("proxy-authenticate"),
+};
+
+static int ignore_header(const literal *lits, size_t llen,
+ const char *name, size_t nlen)
+{
+ const literal *lit;
+ int i;
+
+ for (i = 0; i < llen; ++i) {
+ lit = &lits[i];
+ if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int h2_req_ignore_header(const char *name, size_t len)
+{
+ return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
+}
+
+int h2_req_ignore_trailer(const char *name, size_t len)
+{
+ return (h2_req_ignore_header(name, len)
+ || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
+}
+
+int h2_res_ignore_trailer(const char *name, size_t len)
+{
+ return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
+}
+
+void h2_req_strip_ignored_header(apr_table_t *headers)
+{
+ int i;
+ for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
+ apr_table_unset(headers, IgnoredRequestHeaders[i].name);
+ }
+}
+
+
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.h Fri Nov 20 15:13:11 2015
@@ -16,6 +16,7 @@
#ifndef __mod_h2__h2_util__
#define __mod_h2__h2_util__
+struct h2_request;
struct nghttp2_frame;
size_t h2_util_hex_dump(char *buffer, size_t maxlen,
@@ -29,6 +30,11 @@ char *h2_strlwr(char *s);
void h2_util_camel_case_header(char *s, size_t len);
+int h2_req_ignore_header(const char *name, size_t len);
+int h2_req_ignore_trailer(const char *name, size_t len);
+void h2_req_strip_ignored_header(apr_table_t *headers);
+int h2_res_ignore_trailer(const char *name, size_t len);
+
/**
* Return != 0 iff the string s contains the token, as specified in
* HTTP header syntax, rfc7230.
@@ -67,19 +73,33 @@ apr_size_t h2_util_base64url_decode(cons
nv->value = (uint8_t *)VALUE; \
nv->valuelen = strlen(VALUE)
+int h2_util_ignore_header(const char *name);
+
+typedef struct h2_ngheader {
+ nghttp2_nv *nv;
+ apr_size_t nvlen;
+} h2_ngheader;
+
+h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header);
+h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
+ int http_status,
+ apr_table_t *header);
+h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
+ const struct h2_request *req);
+
/**
* Moves data from one brigade into another. If maxlen > 0, it only
* moves up to maxlen bytes into the target brigade, making bucket splits
* 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);
/**
@@ -88,11 +108,11 @@ apr_status_t h2_util_move(apr_bucket_bri
* 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.
@@ -100,7 +120,7 @@ apr_status_t h2_util_copy(apr_bucket_bri
* @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);
@@ -112,13 +132,52 @@ int h2_util_bb_has_data_or_eos(apr_bucke
* @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
+ * given callback. If cb is NULL, just return the amount of data that
+ * could have been read.
+ * If an EOS was/would be encountered, set *peos != 0.
+ * @param bb the brigade to read from
+ * @param cb the callback to invoke for the read data
+ * @param ctx optional data passed to callback
+ * @param plen inout, as input gives the maximum number of bytes to read,
+ * on return specifies the actual/would be number of bytes
+ * @param peos != 0 iff an EOS bucket was/would be encountered.
+ */
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)
+ * to the log at the given level.
+ * @param c the connection to log for
+ * @param stream_id the stream identifier this brigade belongs to
+ * @param level the log level (as in APLOG_*)
+ * @param tag a short message text about the context
+ * @param bb the brigade to log
+ */
+void h2_util_bb_log(conn_rec *c, int stream_id, int level,
+ const char *tag, apr_bucket_brigade *bb);
+
+/**
+ * Transfer buckets from one brigade to another with a limit on the
+ * maximum amount of bytes transfered.
+ * @param to brigade to transfer buckets to
+ * @param from brigades to remove buckets from
+ * @param p pool that buckets should be setaside to
+ * @param plen maximum bytes to transfer, actual bytes transferred
+ * @param peos if an EOS bucket was transferred
+ */
+apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+ apr_bucket_brigade *from,
+ apr_pool_t *p,
+ apr_off_t *plen,
+ int *peos);
#endif /* defined(__mod_h2__h2_util__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_version.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_version.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_version.h Fri Nov 20 15:13:11 2015
@@ -20,7 +20,7 @@
* @macro
* Version number of the h2 module as c string
*/
-#define MOD_HTTP2_VERSION "1.0.0"
+#define MOD_HTTP2_VERSION "1.0.5-DEV"
/**
* @macro
@@ -28,7 +28,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define MOD_HTTP2_VERSION_NUM 0x010000
+#define MOD_HTTP2_VERSION_NUM 0x010005
#endif /* mod_h2_h2_version_h */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c Fri Nov 20 15:13:11 2015
@@ -22,7 +22,9 @@
#include <http_log.h>
#include "h2_private.h"
+#include "h2_conn.h"
#include "h2_mplx.h"
+#include "h2_request.h"
#include "h2_task.h"
#include "h2_worker.h"
@@ -55,7 +57,7 @@ static void* APR_THREAD_FUNC execute(apr
if (worker->task) {
h2_task_do(worker->task, worker);
worker->task = NULL;
- apr_thread_cond_signal(h2_worker_get_cond(worker));
+ apr_thread_cond_signal(worker->io);
}
}
@@ -71,6 +73,19 @@ static void* APR_THREAD_FUNC execute(apr
return NULL;
}
+static apr_status_t cleanup_join_thread(void *ctx)
+{
+ h2_worker *w = ctx;
+ /* do the join only when the worker is aborted. Otherwise,
+ * we are probably in a process shutdown.
+ */
+ if (w->thread && w->aborted) {
+ apr_status_t rv;
+ apr_thread_join(&rv, w->thread);
+ }
+ return APR_SUCCESS;
+}
+
h2_worker *h2_worker_create(int id,
apr_pool_t *parent_pool,
apr_threadattr_t *attr,
@@ -99,7 +114,6 @@ h2_worker *h2_worker_create(int id,
w->id = id;
w->pool = pool;
- w->bucket_alloc = apr_bucket_alloc_create(pool);
w->get_next = get_next;
w->worker_done = worker_done;
@@ -110,7 +124,9 @@ h2_worker *h2_worker_create(int id,
return NULL;
}
- apr_thread_create(&w->thread, attr, execute, w, pool);
+ apr_pool_pre_cleanup_register(w->pool, w, cleanup_join_thread);
+ apr_thread_create(&w->thread, attr, execute, w, w->pool);
+ apr_pool_create(&w->task_pool, w->pool);
}
return w;
}
@@ -143,28 +159,42 @@ int h2_worker_is_aborted(h2_worker *work
return worker->aborted;
}
-apr_thread_t *h2_worker_get_thread(h2_worker *worker)
+h2_task *h2_worker_create_task(h2_worker *worker, h2_mplx *m,
+ const h2_request *req, int eos)
{
- return worker->thread;
+ h2_task *task;
+
+ /* Create a subpool from the worker one to be used for all things
+ * with life-time of this task execution.
+ */
+ task = h2_task_create(m->id, req, worker->task_pool, m, eos);
+ /* Link the task to the worker which provides useful things such
+ * as mutex, a socket etc. */
+ task->io = worker->io;
+
+ return task;
}
-apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker)
-{
- return worker->io;
+apr_status_t h2_worker_setup_task(h2_worker *worker, h2_task *task) {
+ apr_status_t status;
+
+
+ status = h2_conn_setup(task, apr_bucket_alloc_create(task->pool),
+ worker->thread, worker->socket);
+
+ return status;
}
-apr_socket_t *h2_worker_get_socket(h2_worker *worker)
+void h2_worker_release_task(h2_worker *worker, struct h2_task *task)
{
- return worker->socket;
+ task->io = NULL;
+ task->pool = NULL;
+ apr_pool_clear(worker->task_pool);
}
-apr_pool_t *h2_worker_get_pool(h2_worker *worker)
+apr_socket_t *h2_worker_get_socket(h2_worker *worker)
{
- return worker->pool;
+ return worker->socket;
}
-apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker)
-{
- return worker->bucket_alloc;
-}
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h Fri Nov 20 15:13:11 2015
@@ -18,6 +18,7 @@
struct apr_thread_cond_t;
struct h2_mplx;
+struct h2_request;
struct h2_task;
/* h2_worker is a basically a apr_thread_t that reads fromt he h2_workers
@@ -44,7 +45,7 @@ struct h2_worker {
int id;
apr_thread_t *thread;
apr_pool_t *pool;
- apr_bucket_alloc_t *bucket_alloc;
+ apr_pool_t *task_pool;
struct apr_thread_cond_t *io;
apr_socket_t *socket;
@@ -142,14 +143,11 @@ int h2_worker_get_id(h2_worker *worker);
int h2_worker_is_aborted(h2_worker *worker);
-apr_pool_t *h2_worker_get_pool(h2_worker *worker);
-
-apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker);
+struct h2_task *h2_worker_create_task(h2_worker *worker, struct h2_mplx *m,
+ const struct h2_request *req, int eos);
+apr_status_t h2_worker_setup_task(h2_worker *worker, struct h2_task *task);
+void h2_worker_release_task(h2_worker *worker, struct h2_task *task);
apr_socket_t *h2_worker_get_socket(h2_worker *worker);
-apr_thread_t *h2_worker_get_thread(h2_worker *worker);
-
-struct apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker);
-
#endif /* defined(__mod_h2__h2_worker__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c Fri Nov 20 15:13:11 2015
@@ -42,6 +42,22 @@ static int in_list(h2_workers *workers,
return 0;
}
+static void cleanup_zombies(h2_workers *workers, int lock) {
+ if (lock) {
+ apr_thread_mutex_lock(workers->lock);
+ }
+ while (!H2_WORKER_LIST_EMPTY(&workers->zombies)) {
+ h2_worker *zombie = H2_WORKER_LIST_FIRST(&workers->zombies);
+ H2_WORKER_REMOVE(zombie);
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+ "h2_workers: cleanup zombie %d", zombie->id);
+ h2_worker_destroy(zombie);
+ }
+ if (lock) {
+ apr_thread_mutex_unlock(workers->lock);
+ }
+}
+
/**
* Get the next task for the given worker. Will block until a task arrives
@@ -63,7 +79,7 @@ static apr_status_t get_mplx_next(h2_wor
if (*pm && ptask != NULL) {
/* We have a h2_mplx instance and the worker wants the next task.
* Try to get one from the given mplx. */
- *ptask = h2_mplx_pop_task(*pm, &has_more);
+ *ptask = h2_mplx_pop_task(*pm, worker, &has_more);
if (*ptask) {
return APR_SUCCESS;
}
@@ -108,7 +124,7 @@ static apr_status_t get_mplx_next(h2_wor
m = H2_MPLX_LIST_FIRST(&workers->mplxs);
H2_MPLX_REMOVE(m);
- task = h2_mplx_pop_task(m, &has_more);
+ task = h2_mplx_pop_task(m, worker, &has_more);
if (task) {
if (has_more) {
H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
@@ -123,23 +139,12 @@ static apr_status_t get_mplx_next(h2_wor
if (!task) {
/* Need to wait for either a new mplx to arrive.
*/
+ cleanup_zombies(workers, 0);
+
if (workers->worker_count > workers->min_size) {
apr_time_t now = apr_time_now();
if (now >= (start_wait + max_wait)) {
/* waited long enough without getting a task. */
- status = APR_TIMEUP;
- }
- else {
- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
- "h2_worker(%d): waiting signal, "
- "worker_count=%d", worker->id,
- (int)workers->worker_count);
- status = apr_thread_cond_timedwait(workers->mplx_added,
- workers->lock, max_wait);
- }
-
- if (status == APR_TIMEUP) {
- /* waited long enough */
if (workers->worker_count > workers->min_size) {
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0,
workers->s,
@@ -148,6 +153,12 @@ static apr_status_t get_mplx_next(h2_wor
break;
}
}
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+ "h2_worker(%d): waiting signal, "
+ "worker_count=%d", worker->id,
+ (int)workers->worker_count);
+ apr_thread_cond_timedwait(workers->mplx_added,
+ workers->lock, max_wait);
}
else {
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
@@ -163,7 +174,7 @@ static apr_status_t get_mplx_next(h2_wor
* needed to give up with more than enough workers.
*/
if (task) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
"h2_worker(%d): start task(%s)",
h2_worker_get_id(worker), task->id);
/* Since we hand out a reference to the worker, we increase
@@ -194,11 +205,11 @@ static void worker_done(h2_worker *worke
h2_workers *workers = (h2_workers *)ctx;
apr_status_t status = apr_thread_mutex_lock(workers->lock);
if (status == APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
"h2_worker(%d): done", h2_worker_get_id(worker));
H2_WORKER_REMOVE(worker);
--workers->worker_count;
- h2_worker_destroy(worker);
+ H2_WORKER_LIST_INSERT_TAIL(&workers->zombies, worker);
apr_thread_mutex_unlock(workers->lock);
}
@@ -213,7 +224,7 @@ static apr_status_t add_worker(h2_worker
if (!w) {
return APR_ENOMEM;
}
- ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s,
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
"h2_workers: adding worker(%d)", h2_worker_get_id(w));
++workers->worker_count;
H2_WORKER_LIST_INSERT_TAIL(&workers->workers, w);
@@ -235,15 +246,22 @@ static apr_status_t h2_workers_start(h2_
return status;
}
-h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool,
+h2_workers *h2_workers_create(server_rec *s, apr_pool_t *server_pool,
int min_size, int max_size)
{
apr_status_t status;
h2_workers *workers;
+ apr_pool_t *pool;
+
AP_DEBUG_ASSERT(s);
- AP_DEBUG_ASSERT(pool);
- status = APR_SUCCESS;
+ AP_DEBUG_ASSERT(server_pool);
+ /* let's have our own pool that will be parent to all h2_worker
+ * instances we create. This happens in various threads, but always
+ * guarded by our lock. Without this pool, all subpool creations would
+ * happen on the pool handed to us, which we do not guard.
+ */
+ apr_pool_create(&pool, server_pool);
workers = apr_pcalloc(pool, sizeof(h2_workers));
if (workers) {
workers->s = s;
@@ -255,6 +273,7 @@ h2_workers *h2_workers_create(server_rec
apr_threadattr_create(&workers->thread_attr, workers->pool);
APR_RING_INIT(&workers->workers, h2_worker, link);
+ APR_RING_INIT(&workers->zombies, h2_worker, link);
APR_RING_INIT(&workers->mplxs, h2_mplx, link);
status = apr_thread_mutex_create(&workers->lock,
@@ -278,6 +297,9 @@ h2_workers *h2_workers_create(server_rec
void h2_workers_destroy(h2_workers *workers)
{
+ /* before we go, cleanup any zombie workers that may have accumulated */
+ cleanup_zombies(workers, 1);
+
if (workers->mplx_added) {
apr_thread_cond_destroy(workers->mplx_added);
workers->mplx_added = NULL;
@@ -294,13 +316,17 @@ void h2_workers_destroy(h2_workers *work
h2_worker *w = H2_WORKER_LIST_FIRST(&workers->workers);
H2_WORKER_REMOVE(w);
}
+ if (workers->pool) {
+ apr_pool_destroy(workers->pool);
+ /* workers is gone */
+ }
}
apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m)
{
apr_status_t status = apr_thread_mutex_lock(workers->lock);
if (status == APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, status, workers->s,
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, status, workers->s,
"h2_workers: register mplx(%ld)", m->id);
if (in_list(workers, m)) {
status = APR_EAGAIN;
@@ -320,6 +346,9 @@ apr_status_t h2_workers_register(h2_work
add_worker(workers);
}
+ /* cleanup any zombie workers that may have accumulated */
+ cleanup_zombies(workers, 0);
+
apr_thread_mutex_unlock(workers->lock);
}
return status;
@@ -334,6 +363,9 @@ apr_status_t h2_workers_unregister(h2_wo
H2_MPLX_REMOVE(m);
status = APR_SUCCESS;
}
+ /* cleanup any zombie workers that may have accumulated */
+ cleanup_zombies(workers, 0);
+
apr_thread_mutex_unlock(workers->lock);
}
return status;
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h Fri Nov 20 15:13:11 2015
@@ -42,6 +42,7 @@ struct h2_workers {
apr_threadattr_t *thread_attr;
APR_RING_HEAD(h2_worker_list, h2_worker) workers;
+ APR_RING_HEAD(h2_worker_zombies, h2_worker) zombies;
APR_RING_HEAD(h2_mplx_list, h2_mplx) mplxs;
int worker_count;
Modified: httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp Fri Nov 20 15:13:11 2015
@@ -105,6 +105,14 @@ SOURCE=./h2_alt_svc.c
# End Source File
# Begin Source File
+SOURCE=./h2_bucket_eoc.c
+# End Source File
+# Begin Source File
+
+SOURCE=./h2_bucket_eos.c
+# End Source File
+# Begin Source File
+
SOURCE=./h2_config.c
# End Source File
# Begin Source File
@@ -141,6 +149,10 @@ SOURCE=./h2_mplx.c
# End Source File
# Begin Source File
+SOURCE=./h2_push.c
+# End Source File
+# Begin Source File
+
SOURCE=./h2_request.c
# End Source File
# Begin Source File
@@ -181,10 +193,6 @@ SOURCE=./h2_task_queue.c
# End Source File
# Begin Source File
-SOURCE=./h2_to_h1.c
-# End Source File
-# Begin Source File
-
SOURCE=./h2_util.c
# End Source File
# Begin Source File