Author: rhuijben
Date: Mon Nov 2 21:12:58 2015
New Revision: 1712158
URL: http://svn.apache.org/viewvc?rev=1712158&view=rev
Log:
Implement parsing and logging of the http/2 GOAWAY frame. This is a very
useful debugging tool as some other implementations (google, nghttp2) signal
problems via this system.
* protocols/http2_protocol.c
(serf_bucket_create_numberv): Remove call to abort. Just ignore all
unexpected values as we already do in the second part of processing.
Use assertion for debug mode. Fix indentation.
(http2_handle_goaway): Parse goaway frames and produce logging for them.
(http2_process): Apply a limit to the amount of data to cache for a goaway
frame before processing it.
* serf_private.h
(MIN,
MAX): Ensure that we have these defines.
Modified:
serf/trunk/protocols/http2_protocol.c
serf/trunk/serf_private.h
Modified: serf/trunk/protocols/http2_protocol.c
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1712158&r1=1712157&r2=1712158&view=diff
==============================================================================
--- serf/trunk/protocols/http2_protocol.c (original)
+++ serf/trunk/protocols/http2_protocol.c Mon Nov 2 21:12:58 2015
@@ -59,41 +59,41 @@ serf_bucket_create_numberv(serf_bucket_a
for (c = format; *c; c++)
{
+ SERF_H2_assert(*c >= '1' && *c <= '4');
+
if (*c >= '1' && *c <= '4')
sz += (*c - '0');
- else
- abort(); /* Invalid format */
}
buffer = serf_bucket_mem_alloc(allocator, sz);
r = (void*)buffer;
for (c = format; *c; c++)
{
- apr_uint32_t tmp;
+ apr_uint32_t tmp;
- switch (*c)
- {
- case '1':
- *r++ = va_arg(argp, int) & 0xFF;
- break;
- case '2':
- tmp = va_arg(argp, int);
- *r++ = (tmp >> 8) & 0xFF;
- *r++ = tmp & 0xFF;
- break;
- case '3':
- tmp = va_arg(argp, apr_uint32_t);
- *r++ = (tmp >> 16) & 0xFF;
- *r++ = (tmp >> 8) & 0xFF;
- *r++ = tmp & 0xFF;
- break;
- case '4':
- tmp = va_arg(argp, apr_uint32_t);
- *r++ = (tmp >> 24) & 0xFF;
- *r++ = (tmp >> 16) & 0xFF;
- *r++ = (tmp >> 8) & 0xFF;
- *r++ = tmp & 0xFF;
- break;
+ switch (*c)
+ {
+ case '1':
+ *r++ = va_arg(argp, int) & 0xFF;
+ break;
+ case '2':
+ tmp = va_arg(argp, int);
+ *r++ = (tmp >> 8) & 0xFF;
+ *r++ = tmp & 0xFF;
+ break;
+ case '3':
+ tmp = va_arg(argp, apr_uint32_t);
+ *r++ = (tmp >> 16) & 0xFF;
+ *r++ = (tmp >> 8) & 0xFF;
+ *r++ = tmp & 0xFF;
+ break;
+ case '4':
+ tmp = va_arg(argp, apr_uint32_t);
+ *r++ = (tmp >> 24) & 0xFF;
+ *r++ = (tmp >> 16) & 0xFF;
+ *r++ = (tmp >> 8) & 0xFF;
+ *r++ = tmp & 0xFF;
+ break;
}
}
@@ -714,9 +714,100 @@ http2_handle_goaway(void *baton,
apr_size_t len)
{
serf_http2_protocol_t *h2 = baton;
+ apr_int32_t last_streamid;
+ apr_uint32_t error_code;
+ apr_uint32_t loglevel;
+ const struct goaway_t
+ {
+ unsigned char s3, s2, s1, s0;
+ unsigned char e3, e2, e1, e0;
+ } *goaway;
+
+ if (len < HTTP2_GOWAWAY_DATA_SIZE)
+ return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR;
SERF_H2_assert(h2 != NULL);
+ goaway = (const void *)data;
+
+ last_streamid = ((goaway->s3 & 0x7F) << 24) | (goaway->s2 << 16)
+ | (goaway->s1 << 8) | goaway->s0;
+ error_code = (goaway->e3 << 24) | (goaway->e2 << 16)
+ | (goaway->e1 << 8) | goaway->e0;
+
+ switch (error_code + SERF_ERROR_HTTP2_NO_ERROR)
+ {
+ case SERF_ERROR_HTTP2_PROTOCOL_ERROR:
+ case SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR:
+ case SERF_ERROR_HTTP2_SETTINGS_TIMEOUT:
+ case SERF_ERROR_HTTP2_FRAME_SIZE_ERROR:
+ case SERF_ERROR_HTTP2_COMPRESSION_ERROR:
+ case SERF_ERROR_HTTP2_INADEQUATE_SECURITY:
+ loglevel = LOGLVL_ERROR;
+ break;
+
+ case SERF_ERROR_HTTP2_HTTP_1_1_REQUIRED:
+ case SERF_ERROR_HTTP2_ENHANCE_YOUR_CALM:
+ loglevel = LOGLVL_WARNING;
+ break;
+
+ case SERF_ERROR_HTTP2_REFUSED_STREAM:
+ case SERF_ERROR_HTTP2_CANCEL:
+ case SERF_ERROR_HTTP2_CONNECT_ERROR:
+ case SERF_ERROR_HTTP2_STREAM_CLOSED:
+ /* These errors should have been sent as a stream
+ error. This usually tells us that we have an http/2
+ implementation on the other side that doesn't implement
+ full stream state handling. (See HTTP/2 RFC)*/
+ loglevel = LOGLVL_ERROR;
+ break;
+
+ case SERF_ERROR_HTTP2_NO_ERROR:
+ loglevel = LOGLVL_INFO;
+ break;
+
+ case SERF_ERROR_HTTP2_INTERNAL_ERROR:
+ default:
+ loglevel = LOGLVL_WARNING;
+ break;
+ }
+
+ if (len > HTTP2_GOWAWAY_DATA_SIZE)
+ {
+ char *goaway_text;
+
+ /* The server produced additional information in the error frame
+ Usually this is some literal text explaining what went wrong.
+
+ Copy the text to make it 0 terminated and then log it. */
+
+ /* If this value appears truncated, that may be caused by the
+ limit set in http2_process */
+
+ goaway_text = serf_bstrmemdup(h2->allocator,
+ data + HTTP2_GOWAWAY_DATA_SIZE,
+ len - HTTP2_GOWAWAY_DATA_SIZE);
+
+ serf__log(loglevel, SERF_LOGHTTP2, h2->config,
+ "Received GOAWAY, last-stream=%d, error=%u: %s\n",
+ last_streamid, error_code, goaway_text);
+
+ serf_bucket_mem_free(h2->allocator, goaway_text);
+ }
+ else
+ {
+ serf__log(loglevel, SERF_LOGHTTP2, h2->config,
+ "Received GOAWAY, last-stream=%d, error=%u.\n",
+ last_streamid, error_code);
+ }
+
+ /* ### TODO: If the error is not critical stop creating new frames
+ on this connection, while still going forward with the
+ existing frames.
+
+ We may receive a new error later, signalling a more
+ important problem */
+
return APR_SUCCESS;
}
@@ -1236,7 +1327,10 @@ http2_process(serf_http2_protocol_t *h2)
*/
remaining = (apr_size_t)serf_bucket_get_remaining(body);
- body = serf_bucket_prefix_create(body, remaining,
+ body = serf_bucket_prefix_create(body,
+ MIN(remaining,
+ HTTP2_GOWAWAY_DATA_SIZE
+ + 256),
http2_handle_goaway, h2,
h2->allocator);
Modified: serf/trunk/serf_private.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1712158&r1=1712157&r2=1712158&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Mon Nov 2 21:12:58 2015
@@ -21,6 +21,17 @@
#ifndef _SERF_PRIVATE_H_
#define _SERF_PRIVATE_H_
+ /* Define a MAX macro if we don't already have one */
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+
+ /* Define a MIN macro if we don't already have one */
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+
/* ### what the hell? why does the APR interface have a "size" ??
### the implication is that, if we bust this limit, we'd need to
### stop, rebuild a pollset, and repopulate it. what suckage. */