You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2015/06/30 17:26:19 UTC
svn commit: r1688474 [8/21] - in /httpd/httpd/trunk/modules/http2: ./ m4/
mod-h2.xcodeproj/ mod-h2.xcodeproj/project.xcworkspace/
mod-h2.xcodeproj/project.xcworkspace/xcshareddata/
mod-h2.xcodeproj/xcuserdata/ mod-h2.xcodeproj/xcuserdata/sei.xcuserdata...
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_util.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_util.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_util.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,703 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include <nghttp2/nghttp2.h>
+
+#include "h2_private.h"
+#include "h2_util.h"
+
+size_t h2_util_hex_dump(char *buffer, size_t maxlen,
+ const char *data, size_t datalen)
+{
+ size_t offset = 0;
+ size_t maxoffset = (maxlen-4);
+ size_t i;
+ for (i = 0; i < datalen && offset < maxoffset; ++i) {
+ const char *sep = (i && i % 16 == 0)? "\n" : " ";
+ int n = apr_snprintf(buffer+offset, maxoffset-offset,
+ "%2x%s", ((unsigned int)data[i]&0xff), sep);
+ offset += n;
+ }
+ strcpy(buffer+offset, (i<datalen)? "..." : "");
+ return strlen(buffer);
+}
+
+size_t h2_util_header_print(char *buffer, size_t maxlen,
+ const char *name, size_t namelen,
+ const char *value, size_t valuelen)
+{
+ size_t offset = 0;
+ size_t i;
+ for (i = 0; i < namelen && offset < maxlen; ++i, ++offset) {
+ buffer[offset] = name[i];
+ }
+ for (i = 0; i < 2 && offset < maxlen; ++i, ++offset) {
+ buffer[offset] = ": "[i];
+ }
+ for (i = 0; i < valuelen && offset < maxlen; ++i, ++offset) {
+ buffer[offset] = value[i];
+ }
+ buffer[offset] = '\0';
+ return offset;
+}
+
+
+char *h2_strlwr(char *s)
+{
+ for (char *p = s; *p; ++p) {
+ if (*p >= 'A' && *p <= 'Z') {
+ *p += 'a' - 'A';
+ }
+ }
+ return s;
+}
+
+void h2_util_camel_case_header(char *s, size_t len)
+{
+ size_t start = 1;
+ for (size_t i = 0; i < len; ++i) {
+ if (start) {
+ if (s[i] >= 'a' && s[i] <= 'z') {
+ s[i] -= 'a' - 'A';
+ }
+
+ start = 0;
+ }
+ else if (s[i] == '-') {
+ start = 1;
+ }
+ }
+}
+
+static const int BASE64URL_TABLE[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1
+};
+
+apr_size_t h2_util_base64url_decode(unsigned char **decoded, const char *encoded,
+ apr_pool_t *pool)
+{
+ const unsigned char *e = (const unsigned char *)encoded;
+ const unsigned char *p = e;
+ int n;
+
+ while (*p && BASE64URL_TABLE[ *p ] == -1) {
+ ++p;
+ }
+ apr_size_t len = p - e;
+ apr_size_t mlen = (len/4)*4;
+ *decoded = apr_pcalloc(pool, len+1);
+
+ apr_size_t i = 0;
+ unsigned char *d = *decoded;
+ for (; i < mlen; i += 4) {
+ n = ((BASE64URL_TABLE[ e[i+0] ] << 18) +
+ (BASE64URL_TABLE[ e[i+1] ] << 12) +
+ (BASE64URL_TABLE[ e[i+2] ] << 6) +
+ BASE64URL_TABLE[ e[i+3] ]);
+ *d++ = n >> 16;
+ *d++ = n >> 8 & 0xffu;
+ *d++ = n & 0xffu;
+ }
+ apr_size_t remain = len - mlen;
+ switch (remain) {
+ case 2:
+ n = ((BASE64URL_TABLE[ e[mlen+0] ] << 18) +
+ (BASE64URL_TABLE[ e[mlen+1] ] << 12));
+ *d++ = n >> 16;
+ break;
+ case 3:
+ n = ((BASE64URL_TABLE[ e[mlen+0] ] << 18) +
+ (BASE64URL_TABLE[ e[mlen+1] ] << 12) +
+ (BASE64URL_TABLE[ e[mlen+2] ] << 6));
+ *d++ = n >> 16;
+ *d++ = n >> 8 & 0xffu;
+ break;
+ default: /* do nothing */
+ break;
+ }
+ return len;
+}
+
+int h2_util_contains_token(apr_pool_t *pool, const char *s, const char *token)
+{
+ if (s) {
+ if (!apr_strnatcasecmp(s, token)) { /* the simple life */
+ return 1;
+ }
+
+ for (char *c = ap_get_token(pool, &s, 0); c && *c;
+ c = *s? ap_get_token(pool, &s, 0) : NULL) {
+ if (!apr_strnatcasecmp(c, token)) { /* seeing the token? */
+ return 1;
+ }
+ while (*s++ == ';') { /* skip parameters */
+ ap_get_token(pool, &s, 0);
+ }
+ if (*s++ != ',') { /* need comma separation */
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+const char *h2_util_first_token_match(apr_pool_t *pool, const char *s,
+ const char *tokens[], apr_size_t len)
+{
+ if (s && *s) {
+ for (char *c = ap_get_token(pool, &s, 0); c && *c;
+ c = *s? ap_get_token(pool, &s, 0) : NULL) {
+ for (apr_size_t i = 0; i < len; ++i) {
+ if (!apr_strnatcasecmp(c, tokens[i])) {
+ return tokens[i];
+ }
+ }
+ while (*s++ == ';') { /* skip parameters */
+ ap_get_token(pool, &s, 0);
+ }
+ if (*s++ != ',') { /* need comma separation */
+ return 0;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* 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
+ * pool gets destroyed too early, the bucket disappears while
+ * still needed.
+ */
+static const int DEEP_COPY = 1;
+static const int FILE_MOVE = 0;
+
+static apr_status_t last_not_included(apr_bucket_brigade *bb,
+ apr_size_t maxlen, int count_virtual,
+ apr_bucket **pend)
+{
+ apr_bucket *b;
+ apr_status_t status = APR_SUCCESS;
+
+ 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));
+ b = APR_BUCKET_NEXT(b)) {
+
+ if (APR_BUCKET_IS_METADATA(b)) {
+ /* included */
+ }
+ else {
+ if (maxlen == 0) {
+ *pend = b;
+ return status;
+ }
+
+ if (b->length == ((apr_size_t)-1)) {
+ const char *ign;
+ apr_size_t ilen;
+ status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+ }
+
+ if (!count_virtual && FILE_MOVE && APR_BUCKET_IS_FILE(b)) {
+ /* this has no memory footprint really unless
+ * it is read, disregard it in length count,
+ * unless we count the virtual buckets */
+ }
+ else if (maxlen < b->length) {
+ apr_bucket_split(b, maxlen);
+ maxlen = 0;
+ }
+ else {
+ maxlen -= b->length;
+ }
+ }
+ }
+ }
+ *pend = APR_BRIGADE_SENTINEL(bb);
+ return status;
+}
+
+#define LOG_LEVEL APLOG_TRACE2
+
+apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from,
+ apr_size_t maxlen, int count_virtual,
+ apr_file_t **pfile, const char *msg)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ AP_DEBUG_ASSERT(to);
+ AP_DEBUG_ASSERT(from);
+ int same_alloc = (to->bucket_alloc == from->bucket_alloc);
+
+ if (!APR_BRIGADE_EMPTY(from)) {
+ apr_bucket *b, *end;
+
+ status = last_not_included(from, maxlen,
+ (count_virtual || !FILE_MOVE), &end);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) {
+ b = APR_BRIGADE_FIRST(from);
+ if (b == end) {
+ break;
+ }
+
+ if (same_alloc || (b->list == to->bucket_alloc)) {
+ /* both brigades use the same bucket_alloc and auto-cleanups
+ * have the same life time. It's therefore safe to just move
+ * directly. */
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(to, b);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, passed bucket(same bucket_alloc) "
+ "%ld-%ld, type=%s",
+ msg, (long)b->start, (long)b->length,
+ APR_BUCKET_IS_METADATA(b)?
+ (APR_BUCKET_IS_EOS(b)? "EOS":
+ (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) :
+ (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA"));
+ }
+ else if (DEEP_COPY) {
+ /* we have not managed the magic of passing buckets from
+ * one thread to another. Any attempts result in
+ * cleanup of pools scrambling memory.
+ */
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc));
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, copied EOS bucket", msg);
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc));
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, copied FLUSH bucket", msg);
+ }
+ else {
+ /* ignore */
+ }
+ }
+ else if (pfile && FILE_MOVE && APR_BUCKET_IS_FILE(b)) {
+ /* We do not want to read files when passing buckets, if
+ * we can avoid it. However, what we've come up so far
+ * is not working corrently, resulting either in crashes or
+ * too many open file descriptors.
+ */
+ apr_bucket_file *f = (apr_bucket_file *)b->data;
+ apr_file_t *fd = f->fd;
+ int setaside = (f->readpool != to->p);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, moving FILE bucket %ld-%ld "
+ "from=%lx(p=%lx) to=%lx(p=%lx), setaside=%d",
+ msg, (long)b->start, (long)b->length,
+ (long)from, (long)from->p,
+ (long)to, (long)to->p, setaside);
+ if (setaside) {
+ status = apr_file_setaside(&fd, fd, to->p);
+ *pfile = fd;
+ if (status != APR_SUCCESS) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p,
+ "h2_util: %s, setaside FILE", msg);
+ return status;
+ }
+ }
+ apr_brigade_insert_file(to, fd, b->start, b->length,
+ to->p);
+ }
+ else {
+ const char *data;
+ apr_size_t len;
+ status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+ if (status == APR_SUCCESS && len > 0) {
+ status = apr_brigade_write(to, NULL, NULL, data, len);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, copied bucket %ld-%ld "
+ "from=%lx(p=%lx) to=%lx(p=%lx)",
+ msg, (long)b->start, (long)b->length,
+ (long)from, (long)from->p,
+ (long)to, (long)to->p);
+ }
+ }
+ apr_bucket_delete(b);
+ }
+ else {
+ apr_bucket_setaside(b, to->p);
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(to, b);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_move: %s, passed setaside bucket %ld-%ld "
+ "from=%lx(p=%lx) to=%lx(p=%lx)",
+ msg, (long)b->start, (long)b->length,
+ (long)from, (long)from->p,
+ (long)to, (long)to->p);
+ }
+ }
+ }
+
+ return status;
+}
+
+apr_status_t h2_util_pass(apr_bucket_brigade *to, apr_bucket_brigade *from,
+ apr_size_t maxlen, int count_virtual,
+ const char *msg)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ AP_DEBUG_ASSERT(to);
+ AP_DEBUG_ASSERT(from);
+
+ if (!APR_BRIGADE_EMPTY(from)) {
+ apr_bucket *b, *end;
+
+ status = last_not_included(from, maxlen, count_virtual, &end);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) {
+ b = APR_BRIGADE_FIRST(from);
+ if (b == end) {
+ break;
+ }
+
+ APR_BUCKET_REMOVE(b);
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (!APR_BUCKET_IS_EOS(b) && !APR_BUCKET_IS_FLUSH(b)) {
+ apr_bucket_delete(b);
+ continue;
+ }
+ }
+ else if (b->length == 0) {
+ apr_bucket_delete(b);
+ continue;
+ }
+
+ APR_BRIGADE_INSERT_TAIL(to, b);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_pass: %s, passed bucket %ld-%ld, type=%s",
+ msg, (long)b->start, (long)b->length,
+ APR_BUCKET_IS_METADATA(b)?
+ (APR_BUCKET_IS_EOS(b)? "EOS":
+ (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) :
+ (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA"));
+ }
+ }
+
+ return status;
+}
+
+apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from,
+ apr_size_t maxlen, const char *msg)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ AP_DEBUG_ASSERT(to);
+ AP_DEBUG_ASSERT(from);
+ int same_alloc = (to->bucket_alloc == from->bucket_alloc);
+
+ if (!APR_BRIGADE_EMPTY(from)) {
+ apr_bucket *b, *end, *cpy;
+
+ status = last_not_included(from, maxlen, 1, &end);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ for (b = APR_BRIGADE_FIRST(from);
+ b != APR_BRIGADE_SENTINEL(from) && b != end;
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (same_alloc) {
+ status = apr_bucket_copy(b, &cpy);
+ if (status != APR_SUCCESS) {
+ break;
+ }
+ APR_BRIGADE_INSERT_TAIL(to, cpy);
+ }
+ else {
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc));
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_copy: %s, copied EOS bucket", msg);
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc));
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_copy: %s, copied FLUSH bucket", msg);
+ }
+ else {
+ /* ignore */
+ }
+ }
+ else {
+ const char *data;
+ apr_size_t len;
+ status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+ if (status == APR_SUCCESS && len > 0) {
+ status = apr_brigade_write(to, NULL, NULL, data, len);
+ ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+ "h2_util_copy: %s, copied bucket %ld-%ld "
+ "from=%lx(p=%lx) to=%lx(p=%lx)",
+ msg, (long)b->start, (long)b->length,
+ (long)from, (long)from->p,
+ (long)to, (long)to->p);
+ }
+ }
+ }
+ }
+ }
+ return status;
+}
+
+int h2_util_has_flush_or_eos(apr_bucket_brigade *bb) {
+ apr_bucket *b;
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len)
+{
+ apr_bucket *b, *end;
+
+ apr_status_t status = last_not_included(bb, len, 1, &end);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb) && b != end;
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (APR_BUCKET_IS_EOS(b)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int h2_util_bb_has_data(apr_bucket_brigade *bb)
+{
+ apr_bucket *b;
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (!APR_BUCKET_IS_METADATA(b)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb)
+{
+ apr_bucket *b;
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ return 1;
+ }
+ }
+ else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb,
+ apr_size_t *plen, int *peos)
+{
+ apr_status_t status;
+ /* test read to determine available length */
+ apr_off_t blen = 0;
+ status = apr_brigade_length(bb, 0, &blen);
+ if (blen < (apr_off_t)*plen) {
+ *plen = blen;
+ }
+ *peos = h2_util_has_eos(bb, *plen);
+ return status;
+}
+
+apr_status_t h2_util_bb_read(apr_bucket_brigade *bb, char *buffer,
+ apr_size_t *plen, int *peos)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_size_t avail = *plen;
+ apr_size_t written = 0;
+
+ /* Copy data in our brigade into the buffer until it is filled or
+ * we encounter an EOS.
+ */
+ *peos = 0;
+ while ((status == APR_SUCCESS) && !APR_BRIGADE_EMPTY(bb)) {
+
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ *peos = 1;
+ }
+ else {
+ /* ignore */
+ }
+ }
+ else if (avail <= 0) {
+ break;
+ }
+ else {
+ const char *data;
+ apr_size_t data_len;
+
+ if (b->length != ((apr_size_t)-1) && b->length > avail) {
+ apr_bucket_split(b, avail);
+ }
+ status = apr_bucket_read(b, &data, &data_len,
+ APR_NONBLOCK_READ);
+ if (status == APR_SUCCESS && data_len > 0) {
+ if (data_len > avail) {
+ apr_bucket_split(b, avail);
+ data_len = avail;
+ }
+ memcpy(buffer, data, data_len);
+ avail -= data_len;
+ buffer += data_len;
+ written += data_len;
+ }
+ }
+ apr_bucket_delete(b);
+ }
+
+ *plen = written;
+ if (status == APR_SUCCESS && !*peos && !*plen) {
+ return APR_EAGAIN;
+ }
+ return status;
+}
+
+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_status_t status = APR_SUCCESS;
+ apr_size_t avail = *plen;
+ apr_size_t written = 0;
+ apr_bucket *next, *b = APR_BRIGADE_FIRST(bb);
+ int consume = (cb != NULL);
+
+ /* Pass data in our brigade through the callback until the length
+ * is satisfied or we encounter an EOS.
+ */
+ *peos = 0;
+ for (b = APR_BRIGADE_FIRST(bb);
+ (status == APR_SUCCESS) && (b != APR_BRIGADE_SENTINEL(bb));
+ b = next) {
+
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ *peos = 1;
+ }
+ else {
+ /* ignore */
+ }
+ }
+ else if (avail <= 0) {
+ break;
+ }
+ else {
+ const char *data = NULL;
+ apr_size_t data_len;
+
+ if (b->length != ((apr_size_t)-1)) {
+ status = apr_bucket_read(b, &data, &data_len,
+ APR_NONBLOCK_READ);
+ }
+ else {
+ data_len = b->length;
+ }
+
+ if (data_len > avail) {
+ apr_bucket_split(b, avail);
+ data_len = avail;
+ }
+
+ if (consume) {
+ if (!data) {
+ status = apr_bucket_read(b, &data, &data_len,
+ APR_NONBLOCK_READ);
+ }
+ if (status == APR_SUCCESS) {
+ status = cb(ctx, data, data_len);
+ }
+ }
+ else {
+ data_len = b->length;
+ }
+ avail -= data_len;
+ written += data_len;
+ }
+
+ next = APR_BUCKET_NEXT(b);
+ if (consume) {
+ apr_bucket_delete(b);
+ }
+ }
+
+ *plen = written;
+ if (status == APR_SUCCESS && !*peos && !*plen) {
+ return APR_EAGAIN;
+ }
+ return status;
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_util.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_util.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_util.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,140 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_util__
+#define __mod_h2__h2_util__
+
+struct nghttp2_frame;
+
+size_t h2_util_hex_dump(char *buffer, size_t maxlen,
+ const char *data, size_t datalen);
+
+size_t h2_util_header_print(char *buffer, size_t maxlen,
+ const char *name, size_t namelen,
+ const char *value, size_t valuelen);
+
+char *h2_strlwr(char *s);
+
+void h2_util_camel_case_header(char *s, size_t len);
+
+/**
+ * Return != 0 iff the string s contains the token, as specified in
+ * HTTP header syntax, rfc7230.
+ */
+int h2_util_contains_token(apr_pool_t *pool, const char *s, const char *token);
+
+const char *h2_util_first_token_match(apr_pool_t *pool, const char *s,
+ const char *tokens[], apr_size_t len);
+
+/**
+ * I always wanted to write my own base64url decoder...not. See
+ * https://tools.ietf.org/html/rfc4648#section-5 for description.
+ */
+apr_size_t h2_util_base64url_decode(unsigned char **decoded,
+ const char *encoded,
+ apr_pool_t *pool);
+
+#define H2_HD_MATCH_LIT(l, name, nlen) \
+ ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+
+#define H2_HD_MATCH_LIT_CS(l, name) \
+ ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+
+#define H2_CREATE_NV_LIT_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
+ nv->namelen = sizeof(NAME) - 1; \
+ nv->value = (uint8_t *)VALUE; \
+ nv->valuelen = strlen(VALUE)
+
+#define H2_CREATE_NV_CS_LIT(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
+ nv->namelen = strlen(NAME); \
+ nv->value = (uint8_t *)VALUE; \
+ nv->valuelen = sizeof(VALUE) - 1
+
+#define H2_CREATE_NV_CS_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
+ nv->namelen = strlen(NAME); \
+ nv->value = (uint8_t *)VALUE; \
+ nv->valuelen = strlen(VALUE)
+
+/**
+ * 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 count_virtual if virtual buckets like FILE do count against maxlen
+ * @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 count_virtual,
+ apr_file_t **pfile, const char *msg);
+
+/**
+ * Copies buckets from one brigade into another. If maxlen > 0, it only
+ * copies up to maxlen bytes into the target brigade, making bucket splits
+ * 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 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);
+
+/**
+ * Pass the buckets from one brigade into another, without any setaside.
+ * Only recommended if both brigades use the same bucket alloc or if
+ * you really know what you are doing.
+ * @param to the brigade to pass the buckets to
+ * @param from the brigade to get the buckets from
+ * @param maxlen of bucket bytes to copy, 0 for all
+ * @param count_virtual if virtual buckets like FILE do count against maxlen
+ * @param msg message for use in logging
+ */
+apr_status_t h2_util_pass(apr_bucket_brigade *to, apr_bucket_brigade *from,
+ apr_size_t maxlen, int count_virtual,
+ const char *msg);
+
+/**
+ * Return != 0 iff there is a FLUSH or EOS bucket in the brigade.
+ * @param bb the brigade to check on
+ * @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_bb_has_data(apr_bucket_brigade *bb);
+int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb);
+
+/**
+ * Check how many bytes of the desired amount are available and if the
+ * end of stream is reached by that amount.
+ * @param bb the brigade to check
+ * @param plen the desired length and, on return, the available length
+ * @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_status_t h2_util_bb_read(apr_bucket_brigade *bb, char *buffer,
+ apr_size_t *plen, int *peos);
+
+typedef apr_status_t h2_util_pass_cb(void *ctx,
+ const char *data, apr_size_t len);
+
+apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb,
+ h2_util_pass_cb *cb, void *ctx,
+ apr_size_t *plen, int *peos);
+
+#endif /* defined(__mod_h2__h2_util__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_version.h.in
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_version.h.in?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_version.h.in (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_version.h.in Tue Jun 30 15:26:16 2015
@@ -0,0 +1,42 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef mod_h2_h2_version_h
+#define mod_h2_h2_version_h
+
+/**
+ * @macro
+ * Version number of the h2 module as c string
+ */
+#define MOD_H2_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * @macro
+ * Numerical representation of the version number of the h2 module
+ * 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_H2_VERSION_NUM @PACKAGE_VERSION_NUM@
+
+
+/**
+ * @macro
+ * != 0 iff the nghttp2 library supports data callbacks.
+ * Disabled for now since it lowers performance.
+ */
+#define NGHTTP2_HAS_DATA_CBXXX @NGHTTP2_HAS_DATA_CB@
+#define NGHTTP2_HAS_DATA_CB 0
+
+#endif /* mod_h2_h2_version_h */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,168 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_thread_cond.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_task.h"
+#include "h2_worker.h"
+
+static void *execute(apr_thread_t *thread, void *wctx)
+{
+ h2_worker *worker = (h2_worker *)wctx;
+ apr_status_t status = APR_SUCCESS;
+ h2_mplx *m;
+ (void)thread;
+
+ /* Furthermore, other code might want to see the socket for
+ * this connection. Allocate one without further function...
+ */
+ status = apr_socket_create(&worker->socket,
+ APR_INET, SOCK_STREAM,
+ APR_PROTO_TCP, worker->pool);
+ if (status != APR_SUCCESS) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, status, worker->pool,
+ "h2_worker(%d): alloc socket", worker->id);
+ worker->worker_done(worker, worker->ctx);
+ return NULL;
+ }
+
+ worker->task = NULL;
+ m = NULL;
+ while (!worker->aborted) {
+ status = worker->get_next(worker, &m, &worker->task, worker->ctx);
+
+ if (worker->task) {
+ h2_task_do(worker->task, worker);
+ worker->task = NULL;
+ apr_thread_cond_signal(h2_worker_get_cond(worker));
+ }
+ }
+
+ status = worker->get_next(worker, &m, NULL, worker->ctx);
+ m = NULL;
+
+ if (worker->socket) {
+ apr_socket_close(worker->socket);
+ worker->socket = NULL;
+ }
+
+ worker->worker_done(worker, worker->ctx);
+ return NULL;
+}
+
+h2_worker *h2_worker_create(int id,
+ apr_pool_t *parent_pool,
+ apr_threadattr_t *attr,
+ h2_worker_mplx_next_fn *get_next,
+ h2_worker_done_fn *worker_done,
+ void *ctx)
+{
+ apr_allocator_t *allocator = NULL;
+ apr_pool_t *pool = NULL;
+
+ apr_status_t status = apr_allocator_create(&allocator);
+ if (status != APR_SUCCESS) {
+ return NULL;
+ }
+
+ status = apr_pool_create_ex(&pool, parent_pool, NULL, allocator);
+ if (status != APR_SUCCESS) {
+ return NULL;
+ }
+ apr_allocator_owner_set(allocator, pool);
+
+ h2_worker *w = apr_pcalloc(pool, sizeof(h2_worker));
+ if (w) {
+ APR_RING_ELEM_INIT(w, link);
+
+ w->id = id;
+ w->pool = pool;
+ w->bucket_alloc = apr_bucket_alloc_create(pool);
+
+ w->get_next = get_next;
+ w->worker_done = worker_done;
+ w->ctx = ctx;
+
+ status = apr_thread_cond_create(&w->io, w->pool);
+ if (status != APR_SUCCESS) {
+ return NULL;
+ }
+
+ apr_thread_create(&w->thread, attr, execute, w, pool);
+ }
+ return w;
+}
+
+apr_status_t h2_worker_destroy(h2_worker *worker)
+{
+ if (worker->io) {
+ apr_thread_cond_destroy(worker->io);
+ worker->io = NULL;
+ }
+ if (worker->pool) {
+ apr_pool_destroy(worker->pool);
+ /* worker is gone */
+ }
+ return APR_SUCCESS;
+}
+
+int h2_worker_get_id(h2_worker *worker)
+{
+ return worker->id;
+}
+
+void h2_worker_abort(h2_worker *worker)
+{
+ worker->aborted = 1;
+}
+
+int h2_worker_is_aborted(h2_worker *worker)
+{
+ return worker->aborted;
+}
+
+apr_thread_t *h2_worker_get_thread(h2_worker *worker)
+{
+ return worker->thread;
+}
+
+apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker)
+{
+ return worker->io;
+}
+
+apr_socket_t *h2_worker_get_socket(h2_worker *worker)
+{
+ return worker->socket;
+}
+
+apr_pool_t *h2_worker_get_pool(h2_worker *worker)
+{
+ return worker->pool;
+}
+
+apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker)
+{
+ return worker->bucket_alloc;
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_worker.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,155 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_worker__
+#define __mod_h2__h2_worker__
+
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task;
+
+/* h2_worker is a basically a apr_thread_t that reads fromt he h2_workers
+ * task queue and runs h2_tasks it is given.
+ */
+typedef struct h2_worker h2_worker;
+
+/* Invoked when the worker wants a new task to process. Will block
+ * until a h2_mplx becomes available or the worker itself
+ * gets aborted (idle timeout, for example). */
+typedef apr_status_t h2_worker_mplx_next_fn(h2_worker *worker,
+ struct h2_mplx **pm,
+ struct h2_task **ptask,
+ void *ctx);
+
+/* Invoked just before the worker thread exits. */
+typedef void h2_worker_done_fn(h2_worker *worker, void *ctx);
+
+
+struct h2_worker {
+ /** Links to the rest of the workers */
+ APR_RING_ENTRY(h2_worker) link;
+
+ int id;
+ apr_thread_t *thread;
+ apr_pool_t *pool;
+ apr_bucket_alloc_t *bucket_alloc;
+ struct apr_thread_cond_t *io;
+ apr_socket_t *socket;
+
+ h2_worker_mplx_next_fn *get_next;
+ h2_worker_done_fn *worker_done;
+ void *ctx;
+
+ int aborted;
+ struct h2_task *task;
+};
+
+/**
+ * The magic pointer value that indicates the head of a h2_worker list
+ * @param b The worker list
+ * @return The magic pointer value
+ */
+#define H2_WORKER_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_worker, link)
+
+/**
+ * Determine if the worker list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_WORKER_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_worker, link)
+
+/**
+ * Return the first worker in a list
+ * @param b The list to query
+ * @return The first worker in the list
+ */
+#define H2_WORKER_LIST_FIRST(b) APR_RING_FIRST(b)
+
+/**
+ * Return the last worker in a list
+ * @param b The list to query
+ * @return The last worker int he list
+ */
+#define H2_WORKER_LIST_LAST(b) APR_RING_LAST(b)
+
+/**
+ * Insert a single worker at the front of a list
+ * @param b The list to add to
+ * @param e The worker to insert
+ */
+#define H2_WORKER_LIST_INSERT_HEAD(b, e) do { \
+ h2_worker *ap__b = (e); \
+ APR_RING_INSERT_HEAD((b), ap__b, h2_worker, link); \
+ } while (0)
+
+/**
+ * Insert a single worker at the end of a list
+ * @param b The list to add to
+ * @param e The worker to insert
+ */
+#define H2_WORKER_LIST_INSERT_TAIL(b, e) do { \
+ h2_worker *ap__b = (e); \
+ APR_RING_INSERT_TAIL((b), ap__b, h2_worker, link); \
+ } while (0)
+
+/**
+ * Get the next worker in the list
+ * @param e The current worker
+ * @return The next worker
+ */
+#define H2_WORKER_NEXT(e) APR_RING_NEXT((e), link)
+/**
+ * Get the previous worker in the list
+ * @param e The current worker
+ * @return The previous worker
+ */
+#define H2_WORKER_PREV(e) APR_RING_PREV((e), link)
+
+/**
+ * Remove a worker from its list
+ * @param e The worker to remove
+ */
+#define H2_WORKER_REMOVE(e) APR_RING_REMOVE((e), link)
+
+
+/* Create a new worker with given id, pool and attributes, callbacks
+ * callback parameter.
+ */
+h2_worker *h2_worker_create(int id,
+ apr_pool_t *pool,
+ apr_threadattr_t *attr,
+ h2_worker_mplx_next_fn *get_next,
+ h2_worker_done_fn *worker_done,
+ void *ctx);
+
+apr_status_t h2_worker_destroy(h2_worker *worker);
+
+void h2_worker_abort(h2_worker *worker);
+
+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);
+
+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__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,344 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <apr_atomic.h>
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_task.h"
+#include "h2_task_queue.h"
+#include "h2_worker.h"
+#include "h2_workers.h"
+
+static int in_list(h2_workers *workers, h2_mplx *m)
+{
+ h2_mplx *e;
+ for (e = H2_MPLX_LIST_FIRST(&workers->mplxs);
+ e != H2_MPLX_LIST_SENTINEL(&workers->mplxs);
+ e = H2_MPLX_NEXT(e)) {
+ if (e == m) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm,
+ h2_task **ptask, void *ctx)
+{
+ apr_status_t status;
+ h2_mplx *m = NULL;
+ h2_task *task = NULL;
+ apr_time_t max_wait, start_wait;
+ int has_more = 0;
+ h2_workers *workers = (h2_workers *)ctx;
+
+ 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);
+ if (*ptask) {
+ return APR_SUCCESS;
+ }
+ }
+
+ if (*pm) {
+ /* Got a mplx handed in, but did not get or want a task from it.
+ * Release it, as the workers reference will be wiped.
+ */
+ h2_mplx_release(*pm);
+ *pm = NULL;
+ }
+
+ if (!ptask) {
+ /* of the worker does not want a next task, we're done.
+ */
+ return APR_SUCCESS;
+ }
+
+ max_wait = apr_time_from_sec(apr_atomic_read32(&workers->max_idle_secs));
+ start_wait = apr_time_now();
+
+ status = apr_thread_mutex_lock(workers->lock);
+ if (status == APR_SUCCESS) {
+ ++workers->idle_worker_count;
+
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+ "h2_worker(%d): looking for work", h2_worker_get_id(worker));
+
+ while (!task && !h2_worker_is_aborted(worker) && !workers->aborted) {
+
+ /* Get the next h2_mplx to process that has a task to hand out.
+ * If it does, place it at the end of the queu and return the
+ * task to the worker.
+ * If it (currently) has no tasks, remove it so that it needs
+ * to register again for scheduling.
+ * If we run out of h2_mplx in the queue, we need to wait for
+ * new mplx to arrive. Depending on how many workers do exist,
+ * we do a timed wait or block indefinitely.
+ */
+ m = NULL;
+ while (!task && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
+ m = H2_MPLX_LIST_FIRST(&workers->mplxs);
+ H2_MPLX_REMOVE(m);
+
+ task = h2_mplx_pop_task(m, &has_more);
+ if (task) {
+ if (has_more) {
+ H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+ }
+ else {
+ has_more = !H2_MPLX_LIST_EMPTY(&workers->mplxs);
+ }
+ break;
+ }
+ }
+
+ if (!task) {
+ /* Need to wait for either a new mplx to arrive.
+ */
+ 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,
+ "h2_workers: aborting idle worker");
+ h2_worker_abort(worker);
+ break;
+ }
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+ "h2_worker(%d): waiting signal (eternal), "
+ "worker_count=%d", worker->id,
+ (int)workers->worker_count);
+ apr_thread_cond_wait(workers->mplx_added, workers->lock);
+ }
+ }
+ }
+
+ /* Here, we either have gotten task and mplx for the worker or
+ * needed to give up with more than enough workers.
+ */
+ if (task) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 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
+ * its ref count.
+ */
+ h2_mplx_reference(m);
+ *pm = m;
+ *ptask = task;
+
+ if (has_more && workers->idle_worker_count > 1) {
+ apr_thread_cond_signal(workers->mplx_added);
+ }
+ status = APR_SUCCESS;
+ }
+ else {
+ status = APR_EOF;
+ }
+
+ --workers->idle_worker_count;
+ apr_thread_mutex_unlock(workers->lock);
+ }
+
+ return status;
+}
+
+static void worker_done(h2_worker *worker, void *ctx)
+{
+ 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,
+ "h2_worker(%d): done", h2_worker_get_id(worker));
+ H2_WORKER_REMOVE(worker);
+ --workers->worker_count;
+ h2_worker_destroy(worker);
+
+ apr_thread_mutex_unlock(workers->lock);
+ }
+}
+
+
+static apr_status_t add_worker(h2_workers *workers)
+{
+ h2_worker *w = h2_worker_create(workers->next_worker_id++,
+ workers->pool, workers->thread_attr,
+ get_mplx_next, worker_done, workers);
+ if (!w) {
+ return APR_ENOMEM;
+ }
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s,
+ "h2_workers: adding worker(%d)", h2_worker_get_id(w));
+ ++workers->worker_count;
+ H2_WORKER_LIST_INSERT_TAIL(&workers->workers, w);
+ return APR_SUCCESS;
+}
+
+static apr_status_t h2_workers_start(h2_workers *workers) {
+ apr_status_t status = apr_thread_mutex_lock(workers->lock);
+ if (status == APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+ "h2_workers: starting");
+
+ while (workers->worker_count < workers->min_size
+ && status == APR_SUCCESS) {
+ status = add_worker(workers);
+ }
+ apr_thread_mutex_unlock(workers->lock);
+ }
+ return status;
+}
+
+h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool,
+ int min_size, int max_size)
+{
+ AP_DEBUG_ASSERT(s);
+ AP_DEBUG_ASSERT(pool);
+ apr_status_t status = APR_SUCCESS;
+
+ h2_workers *workers = apr_pcalloc(pool, sizeof(h2_workers));
+ if (workers) {
+ workers->s = s;
+ workers->pool = pool;
+ workers->min_size = min_size;
+ workers->max_size = max_size;
+ apr_atomic_set32(&workers->max_idle_secs, 10);
+
+ apr_threadattr_create(&workers->thread_attr, workers->pool);
+
+ APR_RING_INIT(&workers->workers, h2_worker, link);
+ APR_RING_INIT(&workers->mplxs, h2_mplx, link);
+
+ status = apr_thread_mutex_create(&workers->lock,
+ APR_THREAD_MUTEX_DEFAULT,
+ workers->pool);
+ if (status == APR_SUCCESS) {
+ status = apr_thread_cond_create(&workers->mplx_added, workers->pool);
+ }
+
+ if (status == APR_SUCCESS) {
+ status = h2_workers_start(workers);
+ }
+
+ if (status != APR_SUCCESS) {
+ h2_workers_destroy(workers);
+ workers = NULL;
+ }
+ }
+ return workers;
+}
+
+void h2_workers_destroy(h2_workers *workers)
+{
+ if (workers->mplx_added) {
+ apr_thread_cond_destroy(workers->mplx_added);
+ workers->mplx_added = NULL;
+ }
+ if (workers->lock) {
+ apr_thread_mutex_destroy(workers->lock);
+ workers->lock = NULL;
+ }
+ while (!H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
+ h2_mplx *m = H2_MPLX_LIST_FIRST(&workers->mplxs);
+ H2_MPLX_REMOVE(m);
+ }
+ while (!H2_WORKER_LIST_EMPTY(&workers->workers)) {
+ h2_worker *w = H2_WORKER_LIST_FIRST(&workers->workers);
+ H2_WORKER_REMOVE(w);
+ }
+}
+
+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,
+ "h2_workers: register mplx(%ld)", m->id);
+ if (in_list(workers, m)) {
+ status = APR_EAGAIN;
+ }
+ else {
+ H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+ status = APR_SUCCESS;
+ }
+
+ if (workers->idle_worker_count > 0) {
+ apr_thread_cond_signal(workers->mplx_added);
+ }
+ else if (workers->worker_count < workers->max_size) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+ "h2_workers: got %d worker, adding 1",
+ workers->worker_count);
+ add_worker(workers);
+ }
+
+ apr_thread_mutex_unlock(workers->lock);
+ }
+ return status;
+}
+
+apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m)
+{
+ apr_status_t status = apr_thread_mutex_lock(workers->lock);
+ if (status == APR_SUCCESS) {
+ status = APR_EAGAIN;
+ if (in_list(workers, m)) {
+ H2_MPLX_REMOVE(m);
+ status = APR_SUCCESS;
+ }
+ apr_thread_mutex_unlock(workers->lock);
+ }
+ return status;
+}
+
+void h2_workers_set_max_idle_secs(h2_workers *workers, int idle_secs)
+{
+ if (idle_secs <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
+ "h2_workers: max_worker_idle_sec value of %d"
+ " is not valid, ignored.", idle_secs);
+ return;
+ }
+ apr_atomic_set32(&workers->max_idle_secs, idle_secs);
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_workers.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,87 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_workers__
+#define __mod_h2__h2_workers__
+
+/* Thread pool specific to executing h2_tasks. Has a minimum and maximum
+ * number of workers it creates. Starts with minimum workers and adds
+ * some on load, reduces the number again when idle.
+ *
+ */
+struct apr_thread_mutex_t;
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task;
+struct h2_task_queue;
+
+typedef struct h2_workers h2_workers;
+
+struct h2_workers {
+ server_rec *s;
+ apr_pool_t *pool;
+ int aborted;
+
+ int next_worker_id;
+ int min_size;
+ int max_size;
+
+ apr_threadattr_t *thread_attr;
+
+ APR_RING_HEAD(h2_worker_list, h2_worker) workers;
+ APR_RING_HEAD(h2_mplx_list, h2_mplx) mplxs;
+
+ int worker_count;
+ volatile apr_uint32_t max_idle_secs;
+ volatile apr_uint32_t idle_worker_count;
+
+ struct apr_thread_mutex_t *lock;
+ struct apr_thread_cond_t *mplx_added;
+};
+
+
+/* Create a worker pool with the given minimum and maximum number of
+ * threads.
+ */
+h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool,
+ int min_size, int max_size);
+
+/* Destroy the worker pool and all its threads.
+ */
+void h2_workers_destroy(h2_workers *workers);
+
+/**
+ * Registers a h2_mplx for task scheduling. If this h2_mplx runs
+ * out of tasks, it will be automatically be unregistered. Should
+ * new tasks arrive, it needs to be registered again.
+ */
+apr_status_t h2_workers_register(h2_workers *workers,
+ struct h2_mplx *m);
+
+/**
+ * Remove a h2_mplx from the worker registry.
+ */
+apr_status_t h2_workers_unregister(h2_workers *workers,
+ struct h2_mplx *m);
+
+/**
+ * Set the amount of seconds a h2_worker should wait for new tasks
+ * before shutting down (if there are more than the minimum number of
+ * workers).
+ */
+void h2_workers_set_max_idle_secs(h2_workers *workers, int idle_secs);
+
+#endif /* defined(__mod_h2__h2_workers__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/m4/h2.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/m4/h2.m4?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/m4/h2.m4 (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/m4/h2.m4 Tue Jun 30 15:26:16 2015
@@ -0,0 +1 @@
+# just so it is not empty
Added: httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,162 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+#include <apr_want.h>
+
+#include <httpd.h>
+#include <http_log.h>
+
+#include "mod_h2.h"
+
+#include <nghttp2/nghttp2.h>
+#include "h2_stream.h"
+#include "h2_alt_svc.h"
+#include "h2_conn.h"
+#include "h2_task.h"
+#include "h2_session.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_h2.h"
+#include "h2_alpn.h"
+#include "h2_upgrade.h"
+#include "h2_version.h"
+
+
+static void h2_hooks(apr_pool_t *pool);
+
+AP_DECLARE_MODULE(h2) = {
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ h2_config_create_svr, /* func to create per server config */
+ h2_config_merge, /* func to merge per server config */
+ h2_cmds, /* command handlers */
+ h2_hooks
+};
+
+/* The module initialization. Called once as apache hook, before any multi
+ * processing (threaded or not) happens. It is typically at least called twice,
+ * see
+ * http://wiki.apache.org/httpd/ModuleLife
+ * Since the first run is just a "practise" run, we want to initialize for real
+ * only on the second try. This defeats the purpose of the first dry run a bit,
+ * since apache wants to verify that a new configuration actually will work.
+ * So if we have trouble with the configuration, this will only be detected
+ * when the server has already switched.
+ * On the other hand, when we initialize lib nghttp2, all possible crazy things
+ * might happen and this might even eat threads. So, better init on the real
+ * invocation, for now at least.
+ */
+static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ void *data = NULL;
+ const char *mod_h2_init_key = "mod_h2_init_counter";
+ nghttp2_info *ngh2;
+ apr_status_t status;
+ (void)plog;(void)ptemp;
+
+ apr_pool_userdata_get(&data, mod_h2_init_key, s->process->pool);
+ if ( data == NULL ) {
+ ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s,
+ "initializing post config dry run");
+ apr_pool_userdata_set((const void *)1, mod_h2_init_key,
+ apr_pool_cleanup_null, s->process->pool);
+ return APR_SUCCESS;
+ }
+
+ ngh2 = nghttp2_version(0);
+ ap_log_error( APLOG_MARK, APLOG_INFO, 0, s,
+ "mod_h2 (v%s, nghttp2 %s), initializing...",
+ MOD_H2_VERSION, ngh2? ngh2->version_str : "unknown");
+
+ switch (h2_conn_mpm_type()) {
+ case H2_MPM_EVENT:
+ case H2_MPM_WORKER:
+ /* all fine, we know these ones */
+ break;
+ case H2_MPM_PREFORK:
+ ap_log_error( APLOG_MARK, APLOG_WARNING, 0, s,
+ "This httpd uses mpm_prefork for multiprocessing. "
+ "Please take notice that mod_h2 always with run "
+ "requests in a multi-threaded environment. If you "
+ "use prefork for single-thread connection handling, "
+ " mod_h2 might pose problems.");
+ break;
+ case H2_MPM_UNKNOWN:
+ /* ??? */
+ ap_log_error( APLOG_MARK, APLOG_ERR, 0, s,
+ "post_config: mpm type unknown");
+ break;
+ }
+
+ status = h2_h2_init(p, s);
+ if (status == APR_SUCCESS) {
+ status = h2_alpn_init(p, s);
+ }
+
+ return status;
+}
+
+/* Runs once per created child process. Perform any process
+ * related initionalization here.
+ */
+static void h2_child_init(apr_pool_t *pool, server_rec *s)
+{
+ /* Set up our connection processing */
+ apr_status_t status = h2_conn_child_init(pool, s);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
+ "initializing connection handling");
+ }
+}
+
+const char *h2_get_protocol(conn_rec *c)
+{
+ return h2_ctx_pnego_get(h2_ctx_get(c));
+}
+
+/* Install this module into the apache2 infrastructure.
+ */
+static void h2_hooks(apr_pool_t *pool)
+{
+ ap_log_perror(APLOG_MARK, APLOG_INFO, 0, pool, "installing hooks");
+
+ static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
+
+ /* Run once after configuration is set, but before mpm children initialize.
+ */
+ ap_hook_post_config(h2_post_config, mod_ssl, NULL, APR_HOOK_MIDDLE);
+
+ /* Run once after a child process has been created.
+ */
+ ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+
+ h2_h2_register_hooks();
+ h2_alpn_register_hooks();
+ h2_upgrade_register_hooks();
+ h2_task_register_hooks();
+
+ h2_alt_svc_register_hooks();
+
+ /* We offer a function to other modules that lets them retrieve
+ * the h2 protocol used on a connection (if any).
+ */
+ APR_REGISTER_OPTIONAL_FN(h2_get_protocol);
+}
+
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/mod_h2.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,28 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mod_h2_mod_h2_h
+#define mod_h2_mod_h2_h
+
+const char *h2_get_protocol(conn_rec *c);
+
+
+/**
+ * An optional function which returns the h2 protocol used on the given
+ * connection and NULL if no h2* protocol is active on it.
+ */
+APR_DECLARE_OPTIONAL_FN(const char *, h2_get_protocol, (conn_rec*));
+
+#endif
Added: httpd/httpd/trunk/modules/http2/sandbox/.gitignore
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/.gitignore?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/.gitignore (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/.gitignore Tue Jun 30 15:26:16 2015
@@ -0,0 +1,2 @@
+install
+gen
Added: httpd/httpd/trunk/modules/http2/sandbox/Makefile.am
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/Makefile.am?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/Makefile.am (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/Makefile.am Tue Jun 30 15:26:16 2015
@@ -0,0 +1,58 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+SUBDIRS = httpd nghttp2 test
+DIST_SUBDIRS = httpd nghttp2 test
+
+INSTALL_DIR = install
+
+EXTRA_DIST = \
+ httpd/Makefile \
+ httpd/*.sh \
+ httpd/patches/* \
+ httpd/packages/* \
+ nghttp2/Makefile \
+ test/Makefile \
+ test/*.sh \
+ test/*.txt \
+ test/htdocs \
+ test/conf \
+ test/clients/Makefile
+
+
+.PHONY: test loadtest start stop restart
+
+start:
+ make -C test start
+
+restart:
+ make -C test restart
+
+stop:
+ make -C test stop
+
+test:
+ make -C test test
+
+loadtest:
+ make -C test loadtest
+
+mpm_worker:
+ make -C test mpm_worker
+
+mpm_event:
+ make -C test mpm_event
+
+mpm_prefork:
+ make -C test mpm_prefork
Added: httpd/httpd/trunk/modules/http2/sandbox/httpd/.gitignore
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/httpd/.gitignore?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/httpd/.gitignore (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/httpd/.gitignore Tue Jun 30 15:26:16 2015
@@ -0,0 +1 @@
+gen
Added: httpd/httpd/trunk/modules/http2/sandbox/httpd/Makefile
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/httpd/Makefile?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/httpd/Makefile (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/httpd/Makefile Tue Jun 30 15:26:16 2015
@@ -0,0 +1,319 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+OS = $(shell uname -s)
+MACHINE = $(shell uname -m)
+
+
+APR_VERSION = 1.5.2
+APR_DIR = apr-$(APR_VERSION)
+APR_TAR = $(APR_DIR).tar.gz
+APR_URL = http://archive.apache.org/dist/apr/$(APR_TAR)
+
+APR-UTIL_VERSION = 1.5.4
+APR-UTIL_DIR = apr-util-$(APR-UTIL_VERSION)
+APR-UTIL_TAR = $(APR-UTIL_DIR).tar.gz
+APR-UTIL_URL = http://archive.apache.org/dist/apr/$(APR-UTIL_TAR)
+
+HTTPD_VERSION = 2.4.12
+HTTPD_DIR = httpd-$(HTTPD_VERSION)
+HTTPD_TAR = httpd-$(HTTPD_VERSION).tar.gz
+HTTPD_URL = https://archive.apache.org/dist/httpd/$(HTTPD_TAR)
+
+HTTPD_CONF = --enable-mpms-shared=all --with-included-apr
+HTTPD_DEPS = $(GEN)/$(HTTPD_DIR)/.apr-extracted
+
+PCRE_VERSION = 8.36
+PCRE_DIR = pcre-$(PCRE_VERSION)
+PCRE_TAR = $(PCRE_DIR).tar.gz
+PCRE_URL = http://sourceforge.net/projects/pcre/files/pcre/$(PCRE_VERSION)/$(PCRE_TAR)
+
+OPENSSL_BASE = http://www.openssl.org
+OPENSSL_DIR = openssl-latest
+OPENSSL_TAR = $(OPENSSL_DIR).tar.gz
+OPENSSL_URL = $(OPENSSL_BASE)/source/latest.tar.gz
+OPENSSL_CONF_CMD = ./config
+OPENSSL_CONF = shared -fPIC
+
+FCGI_VERSION = 2.3.9
+FCGI_DIR = mod_fcgid-$(FCGI_VERSION)
+FCGI_TAR = $(FCGI_DIR).tar.gz
+FCGI_URL = https://archive.apache.org/dist/httpd/mod_fcgid/$(FCGI_TAR)
+
+GEN = gen
+INST_DIR = ../install
+BLD_PREFIX = $(shell dirname $$PWD)/install
+
+CURL_OPTS = --progress-bar
+
+# For OS X, we drag our own PCRE lib in as the one installed is not
+# recognized by the httpd configure (and pbly rightly so).
+# On other OS, we expect a proper pcre installation
+#
+ifeq ($(OS),Darwin)
+ # we need our own apr + apr-util
+ HTTPD_CONF += --with-crypto
+ # we need our own pcre
+ HTTPD_DEPS += $(INST_DIR)/.pcre-installed
+ HTTPD_CONF += --with-pcre=$(BLD_PREFIX)
+endif
+
+OPENSSL_VERSION = $(shell openssl version -v | sed -e 's/OpenSSL *//g' -e 's/[a-z]* .*//g')
+
+ifeq ($(OPENSSL_VERSION), $(filter $(OPENSSL_VERSION),0.9.7 0.9.8 1.0.0 1.0.1))
+ # Very old openssl without alpn support installed, need a newer one
+ HTTPD_DEPS += $(INST_DIR)/.openssl-installed
+ HTTPD_CONF += --enable-ssl --with-ssl=$(BLD_PREFIX)
+ ifeq ($(OS),Darwin)
+ OPENSSL_CONF_CMD = ./Configure
+ OPENSSL_CONF = darwin64-x86_64-cc
+ HTTPD_CONF += --enable-ssl-staticlib-deps
+ endif
+endif
+
+HTTP_PORT = 12345
+HTTPS_PORT = 12346
+
+all: install
+
+distdir:
+
+dirs:
+ @mkdir -p $(GEN)/build
+
+clean:
+ @rm -rf $(GEN)/$(PCRE_DIR) $(GEN)/$(OPENSSL_DIR) $(GEN)/$(HTTPD_DIR) \
+ $(GEN)/build $(INST_DIR)/.httpd-installed
+
+distclean:
+ @rm -rf $(GEN)
+
+install: \
+ $(INST_DIR)/.httpd-installed \
+ $(INST_DIR)/.modules-installed
+
+setup: $(INST_DIR)/.httpd-setup
+
+copy-mod_ssl:
+ @cp mod_ssl-alpn/*.[ch] $(GEN)/$(HTTPD_DIR)/modules/ssl
+ @rm -f $(GEN)/$(HTTPD_DIR)/.httpd-built
+
+################################################################################
+# Install the local httpd
+#
+$(INST_DIR)/.httpd-installed: $(GEN)/$(HTTPD_DIR)/.httpd-built
+ @mkdir -p $(INST_DIR)
+ @echo -n installing httpd locally...
+ @cd $(GEN)/$(HTTPD_DIR)/ && make install
+ @echo done.
+ @touch $(INST_DIR)/.httpd-installed
+
+
+################################################################################
+# Build the local, patched httpd
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-built: \
+ $(GEN)/$(HTTPD_DIR)/.httpd-configured
+ @echo -n building httpd...
+ @cd $(GEN)/$(HTTPD_DIR)/ && make
+ @echo done.
+ @touch $(GEN)/$(HTTPD_DIR)/.httpd-built
+
+################################################################################
+# Configure the local httpd sources
+#
+$(GEN)/$(HTTPD_DIR)/configure:
+ cd $(GEN)/$(HTTPD_DIR)/ && ./buildconf
+
+$(GEN)/$(HTTPD_DIR)/.httpd-configured: \
+ $(HTTPD_DEPS) \
+ $(GEN)/$(HTTPD_DIR)/configure \
+ $(GEN)/$(HTTPD_DIR)/.httpd-patched
+ @echo -n configuring httpd...
+ cd $(GEN)/$(HTTPD_DIR)/ && \
+ ./configure --prefix=$(BLD_PREFIX) $(HTTPD_CONF)
+ @echo done.
+ @touch $(GEN)/$(HTTPD_DIR)/.httpd-configured
+
+################################################################################
+# Patch the local httpd sources
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-patched: \
+ $(GEN)/$(HTTPD_DIR)/.httpd-extracted
+ @echo applying patches...
+ @cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/httpd-2.4.12-alpn-v5.patch
+ @cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/sni_misdirect.patch
+ @echo httpd patched.
+ @touch $(GEN)/$(HTTPD_DIR)/.httpd-patched
+#@cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/alpn-2.4.x.unified.diff.patch
+
+################################################################################
+# Extract apache source tree
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-extracted: \
+ $(GEN)/$(HTTPD_TAR)
+ @rm -rf $(GEN)/$(HTTPD_DIR)
+ @echo -n extracting httpd packages...
+ @cd $(GEN) && tar xfz $(HTTPD_TAR)
+ @echo done.
+ @touch $(GEN)/$(HTTPD_DIR)/.httpd-extracted
+
+################################################################################
+# Extract apr + apr-util into apache source tree (if needed)
+#
+$(GEN)/$(HTTPD_DIR)/.apr-extracted: \
+ $(GEN)/$(HTTPD_DIR)/.httpd-extracted \
+ $(GEN)/$(APR_TAR) $(GEN)/$(APR-UTIL_TAR)
+ @rm -rf $(GEN)/$(HTTPD_DIR)/srclib/apr $(GEN)/$(HTTPD_DIR)/srclib/apr-util
+ @echo -n extracting apr packages...
+ @cd gen/$(HTTPD_DIR)/srclib; tar xfz ../../$(APR_TAR) && mv $(APR_DIR) apr
+ @cd gen/$(HTTPD_DIR)/srclib; tar xfz ../../$(APR-UTIL_TAR) && mv $(APR-UTIL_DIR) apr-util
+ @echo done.
+ @touch $(GEN)/$(HTTPD_DIR)/.apr-extracted
+
+################################################################################
+# Retrieve apache sources (and apr + apr-util if needed)
+#
+$(GEN)/$(HTTPD_TAR):
+ @mkdir -p $(GEN)
+ curl $(CURL_OPTS) $(HTTPD_URL) > $(GEN)/$(HTTPD_TAR)
+
+$(GEN)/$(APR_TAR):
+ @mkdir -p $(GEN)
+ curl $(CURL_OPTS) $(APR_URL) > $(GEN)/$(APR_TAR)
+
+$(GEN)/$(APR-UTIL_TAR):
+ @mkdir -p $(GEN)
+ curl $(CURL_OPTS) $(APR-UTIL_URL) > $(GEN)/$(APR-UTIL_TAR)
+
+################################################################################
+# Build + install additional modules
+#
+$(INST_DIR)/.modules-installed: \
+ $(INST_DIR)/.httpd-installed \
+ $(INST_DIR)/.mod_fcgid-installed
+ @touch $(INST_DIR)/.modules-installed
+
+################################################################################
+# Build + install mod_fcgid
+#
+$(INST_DIR)/.mod_fcgid-installed: \
+ $(INST_DIR)/.httpd-installed \
+ $(GEN)/$(FCGI_DIR)/.mod_fcgid-built
+ @echo -n installing mod_fcgid locally...
+ @cd $(GEN)/$(FCGI_DIR) && make install
+ @echo done.
+ @touch $(INST_DIR)/.mod_fcgid-installed
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-built: \
+ $(GEN)/$(FCGI_DIR)/.mod_fcgid-configured
+ @echo -n building mod_fcgid...
+ @cd $(GEN)/$(FCGI_DIR) && make
+ @echo done.
+ @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-built
+
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-configured: \
+ $(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted
+ @echo -n configuring mod_fcgid...
+ @cd $(GEN)/$(FCGI_DIR) && APXS=../../$(INST_DIR)/bin/apxs ./configure.apxs
+ @echo done.
+ @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-configured
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted: \
+ $(GEN)/$(FCGI_TAR)
+ @rm -rf $(GEN)/$(FCGI_DIR)
+ @echo -n downloading and extracting mod_fcgid...
+ @cd gen; tar xfz $(FCGI_TAR)
+ @echo done.
+ @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted
+
+$(GEN)/$(FCGI_TAR):
+ @mkdir -p $(GEN)
+ curl $(CURL_OPTS) $(FCGI_URL) > $(GEN)/$(FCGI_TAR)
+
+
+################################################################################
+# Build + install a local opensll library (if needed)
+#
+$(INST_DIR)/.openssl-installed: \
+ $(GEN)/$(OPENSSL_DIR)/.built
+ @mkdir -p $(INST_DIR)
+ @echo -n installing openssl locally, may take some time...
+ @cd $(GEN)/$(OPENSSL_DIR) && make install_sw >> ../build.log
+ @echo done.
+ @touch $(INST_DIR)/.openssl-installed
+
+$(GEN)/$(OPENSSL_DIR)/.built: \
+ $(GEN)/$(OPENSSL_DIR)/.configured
+ @echo -n building openssl locally...
+ @cd $(GEN)/$(OPENSSL_DIR) && make
+ @echo done.
+ @touch $(GEN)/$(OPENSSL_DIR)/.built
+
+$(GEN)/$(OPENSSL_DIR)/.configured: \
+ $(GEN)/$(OPENSSL_DIR)/.patched
+ @echo -n configuring openssl...
+ cd $(GEN)/$(OPENSSL_DIR) && $(OPENSSL_CONF_CMD) --openssldir=$(BLD_PREFIX) $(OPENSSL_CONF)
+ @echo done.
+ @touch $(GEN)/$(OPENSSL_DIR)/.configured
+
+$(GEN)/$(OPENSSL_DIR)/.patched: \
+ $(GEN)/$(OPENSSL_DIR)/.extracted
+ @echo applying patches...
+ # experimental patch to solve SNI+ALPN callback ordering in openssl
+ # not vital with latest httpd-alpn patch. We leave this out of our sandbox
+ # to have it running against an unpatched ssl for better test coverage.
+ #@cd gen/$(OPENSSL_DIR) && patch -p1 < ../../patches/openssl-1.0.2-alpn.patch
+ @echo openssl patched.
+ @touch $(GEN)/$(OPENSSL_DIR)/.patched
+
+$(GEN)/$(OPENSSL_DIR)/.extracted:
+ @echo -n downloading and extracting openssl...
+ @mkdir -p $(GEN)
+ @bash get-openssl-latest.sh $(OPENSSL_URL) $(GEN)/$(OPENSSL_DIR)
+ @echo done.
+ @touch $(GEN)/$(OPENSSL_DIR)/.extracted
+
+################################################################################
+# Build + install a local pcre library (if needed)
+#
+$(INST_DIR)/.pcre-installed: \
+ $(GEN)/$(PCRE_DIR)/.pcre-built
+ @mkdir -p $(INST_DIR)
+ @echo -n installing pcre locally...
+ @cd $(GEN)/$(PCRE_DIR) && make install >> ../build.log
+ @echo done.
+ @touch $(INST_DIR)/.pcre-installed
+
+$(GEN)/$(PCRE_DIR)/.pcre-built: \
+ $(GEN)/$(PCRE_DIR)/.pcre-configured
+ @echo -n building pcre locally...
+ @cd $(GEN)/$(PCRE_DIR) && make >> ../build.log
+ @echo done.
+ @touch $(GEN)/$(PCRE_DIR)/.pcre-built
+
+$(GEN)/$(PCRE_DIR)/.pcre-configured: $(GEN)/$(PCRE_TAR)
+ @rm -rf $(GEN)/$(PCRE_DIR)
+ @cd $(GEN) && tar xfz $(PCRE_TAR)
+ @echo -n configuring pcre...
+ @cd $(GEN)/$(PCRE_DIR) && ./configure --prefix=$(BLD_PREFIX) >> ../build.log
+ @echo done.
+ @touch $(GEN)/$(PCRE_DIR)/.pcre-configured
+
+$(GEN)/$(PCRE_TAR): packages/$(PCRE_TAR)
+ @mkdir -p $(GEN)
+ @cp packages/$(PCRE_TAR) $(GEN)
Added: httpd/httpd/trunk/modules/http2/sandbox/httpd/get-openssl-latest.sh
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/httpd/get-openssl-latest.sh?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/httpd/get-openssl-latest.sh (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/httpd/get-openssl-latest.sh Tue Jun 30 15:26:16 2015
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# get-openssl-latest.sh
+# mod-h2
+#
+# Created by Stefan Eissing on 24.03.15.
+# Inspects the given URL for redirects and downloads tar file
+# to real name. Symlinks given tar name to real name
+
+URL=$1
+DESTDIR=$2
+
+usage() {
+ echo "$@"
+ echo "usage: $(basename $0) url dir"
+ echo " download and extract latest openssl url to given directory,"
+ echo " using real version name and symlinks"
+ exit 2
+}
+
+fail() {
+ echo "$@"
+ exit 1
+}
+
+[ -z "$URL" ] && usage "url parameter missing"
+[ -z "$DESTDIR" ] && usage "dir parameter missing"
+
+GEN=$(dirname "$DESTDIR")
+[ -d "$GEN" ] || fail "destination dir $GEN does not exist"
+
+curl -s -D "$GEN"/xxx-header $URL > "$GEN"/xxx-content ||Â fail "error downloading $URL"
+REAL_URL=$( fgrep -i location: < "$GEN"/xxx-header | sed s',.*: ,,' | tr -d '\r\n' )
+
+case "$REAL_URL" in
+ */var/www/*)
+ # currently openssl returns the wrong path - yet the correct tar name
+ REAL_TAR=$(basename $REAL_URL)
+ REAL_URL=$(dirname $URL)/$REAL_TAR
+ ;;
+ *)
+ REAL_TAR=$(basename $REAL_URL)
+ ;;
+esac
+
+echo "downloading latest openssl from $REAL_URL"
+
+REAL_DIR=$(basename $REAL_TAR .tar.gz)
+rm -f "$GEN/$REAL_TAR" "$DESTDIR" "$GEN"/xxx-header "$GEN"/xxx-content
+
+curl -'#' "$REAL_URL" > "$GEN/$REAL_TAR" || fail "error downloading $REAL_URL"
+(cd "$GEN" && tar xfz "$REAL_TAR") || fail "error extracting $GEN/$REAL_TAR"
+[ -d "$GEN/$REAL_DIR" ] || fail "expected directory $GEN/$REAL_DIR"
+(cd $GEN && ln -s "$REAL_DIR" $(basename $DESTDIR))
+
Propchange: httpd/httpd/trunk/modules/http2/sandbox/httpd/get-openssl-latest.sh
------------------------------------------------------------------------------
svn:executable = *
Added: httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile Tue Jun 30 15:26:16 2015
@@ -0,0 +1,25 @@
+top_srcdir = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10
+top_builddir = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10
+srcdir = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+builddir = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+VPATH = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# standard stuff
+#
+
+include $(top_srcdir)/build/special.mk
Added: httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in (added)
+++ httpd/httpd/trunk/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in Tue Jun 30 15:26:16 2015
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# standard stuff
+#
+
+include $(top_srcdir)/build/special.mk