> On Aug 6, 2015, at 01:15, James Peach <jpe...@apache.org> wrote: > > >> On Aug 5, 2015, at 9:05 AM, zw...@apache.org wrote: >> >> Repository: trafficserver >> Updated Branches: >> refs/heads/master 7d63eae28 -> 0fc4dcdea >> >> >> TS-3497: Define Http2Error to classify errors >> >> This closes #267 >> >> >> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo >> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/0fc4dcde >> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/0fc4dcde >> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/0fc4dcde >> >> Branch: refs/heads/master >> Commit: 0fc4dcdea9449929c440e54d301144edefbb721f >> Parents: 7d63eae >> Author: Masakazu Kitajo <m4s...@gmail.com> >> Authored: Wed Aug 5 01:08:19 2015 +0900 >> Committer: Leif Hedstrom <zw...@apache.org> >> Committed: Wed Aug 5 10:03:49 2015 -0600 >> >> ---------------------------------------------------------------------- >> proxy/http2/HTTP2.h | 20 +++ >> proxy/http2/Http2ConnectionState.cc | 211 ++++++++++++++++++------------- >> 2 files changed, 145 insertions(+), 86 deletions(-) >> ---------------------------------------------------------------------- >> >> >> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/0fc4dcde/proxy/http2/HTTP2.h >> ---------------------------------------------------------------------- >> diff --git a/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h >> index a398e0e..3183ec0 100644 >> --- a/proxy/http2/HTTP2.h >> +++ b/proxy/http2/HTTP2.h >> @@ -78,6 +78,14 @@ extern RecRawStatBlock *http2_rsb; // Container for >> statistics. >> // 6.9.1 The Flow Control Window >> static const Http2WindowSize HTTP2_MAX_WINDOW_SIZE = 0x7FFFFFFF; >> >> +// 5.4. Error Handling >> +enum Http2ErrorClass { >> + HTTP2_ERROR_CLASS_NONE, >> + HTTP2_ERROR_CLASS_CONNECTION, >> + HTTP2_ERROR_CLASS_STREAM, >> +}; >> + >> +// 7. Error Codes >> enum Http2ErrorCode { >> HTTP2_ERROR_NO_ERROR = 0, >> HTTP2_ERROR_PROTOCOL_ERROR = 1, >> @@ -213,6 +221,18 @@ struct Http2FrameHeader { >> Http2StreamId streamid; >> }; >> >> +// 5.4. Error Handling >> +struct Http2Error { >> + Http2Error(const Http2ErrorClass error_class = HTTP2_ERROR_CLASS_NONE, >> const Http2ErrorCode error_code = HTTP2_ERROR_NO_ERROR) >> + { >> + cls = error_class; >> + code = error_code; >> + }; > > Add a > static Http2Error success() { > return Http2Error(HTTP2_ERROR_CLASS_NONE, HTTP2_ERROR_NO_ERROR); > } > > then all the times you return Http2Error(HTTP2_ERROR_CLASS_NONE) will read > Http2Error::success(), which is easier to read. I'd also add > > bool is_success() const { … }
Adding Http2Error::success() would be nice. As in is_success(), in the end we need to check the member variable if it returns false. I understand the point is that it’s a error but actually is success though; Should we also add is_connection_error(), is_stream_error() and a getter for error code to encapsulate the variables completely? It makes Http2Error more C++ style. > > >> + >> + Http2ErrorClass cls; >> + Http2ErrorCode code; >> +}; >> + >> // 6.5.1. SETTINGS Format >> struct Http2SettingsParameter { >> uint16_t id; >> >> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/0fc4dcde/proxy/http2/Http2ConnectionState.cc >> ---------------------------------------------------------------------- >> diff --git a/proxy/http2/Http2ConnectionState.cc >> b/proxy/http2/Http2ConnectionState.cc >> index 3f61b4b..c30dd7a 100644 >> --- a/proxy/http2/Http2ConnectionState.cc >> +++ b/proxy/http2/Http2ConnectionState.cc >> @@ -30,7 +30,7 @@ >> // Currently use only HTTP/1.1 for requesting to origin server >> const static char *HTTP2_FETCHING_HTTP_VERSION = "HTTP/1.1"; >> >> -typedef Http2ErrorCode (*http2_frame_dispatch)(Http2ClientSession &, >> Http2ConnectionState &, const Http2Frame &); >> +typedef Http2Error (*http2_frame_dispatch)(Http2ClientSession &, >> Http2ConnectionState &, const Http2Frame &); >> >> static const int buffer_size_index[HTTP2_FRAME_TYPE_MAX] = { >> BUFFER_SIZE_INDEX_8K, // HTTP2_FRAME_TYPE_DATA >> @@ -60,7 +60,7 @@ read_rcv_buffer(char *buf, size_t bufsize, unsigned >> &nbytes, const Http2Frame &f >> return end - buf; >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_data_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const >> Http2Frame &frame) >> { >> char buf[BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_DATA])]; >> @@ -71,24 +71,25 @@ rcv_data_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received DATA frame.", >> cs.connection_id()); >> >> + // If a DATA frame is received whose stream identifier field is 0x0, the >> recipient MUST >> + // respond with a connection error of type PROTOCOL_ERROR. >> if (!http2_is_client_streamid(id)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> Http2Stream *stream = cstate.find_stream(id); >> if (stream == NULL) { >> if (id <= cstate.get_latest_stream_id()) { >> - return HTTP2_ERROR_STREAM_CLOSED; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_STREAM_CLOSED); >> } else { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> // If a DATA frame is received whose stream is not in "open" or "half >> closed (local)" state, >> // the recipient MUST respond with a stream error of type STREAM_CLOSED. >> if (stream->get_state() != HTTP2_STREAM_STATE_OPEN && stream->get_state() >> != HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) { >> - cstate.send_rst_stream_frame(id, HTTP2_ERROR_STREAM_CLOSED); >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_STREAM_CLOSED); >> } >> >> if (frame.header().flags & HTTP2_FLAGS_DATA_PADDED) { >> @@ -98,7 +99,7 @@ rcv_data_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> // If the length of the padding is the length of the >> // frame payload or greater, the recipient MUST treat this as a >> // connection error of type PROTOCOL_ERROR. >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> @@ -106,21 +107,24 @@ rcv_data_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> if (frame.header().flags & HTTP2_FLAGS_DATA_END_STREAM) { >> if (!stream->change_state(frame.header().type, frame.header().flags)) { >> cstate.send_rst_stream_frame(id, HTTP2_ERROR_STREAM_CLOSED); >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> if (!stream->payload_length_is_valid()) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> // If Data length is 0, do nothing. >> if (payload_length == 0) { >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> // Check whether Window Size is acceptable >> - if (cstate.server_rwnd < payload_length || stream->server_rwnd < >> payload_length) { >> - return HTTP2_ERROR_FLOW_CONTROL_ERROR; >> + if (cstate.server_rwnd < payload_length) { >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FLOW_CONTROL_ERROR); >> + } >> + if (stream->server_rwnd < payload_length) { >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, >> HTTP2_ERROR_FLOW_CONTROL_ERROR); >> } >> >> // Update Window size >> @@ -151,10 +155,10 @@ rcv_data_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> cstate.send_window_update_frame(stream->get_id(), diff_size); >> } >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_headers_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, >> const Http2Frame &frame) >> { >> char >> buf[BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS])]; >> @@ -166,36 +170,37 @@ rcv_headers_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Ht >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received HEADERS frame.", >> cs.connection_id()); >> >> if (!http2_is_client_streamid(id)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> Http2Stream *stream = cstate.find_stream(id); >> - if (stream == NULL && id <= cstate.get_latest_stream_id()) { >> - return HTTP2_ERROR_STREAM_CLOSED; >> + if (id <= cstate.get_latest_stream_id()) { >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_STREAM_CLOSED); >> } >> >> // Create new stream >> stream = cstate.create_stream(id); >> if (!stream) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // keep track of how many bytes we get in the frame >> stream->request_header_length += payload_length; >> if (stream->request_header_length > Http2::max_request_header_size) { >> Error("HTTP/2 payload for headers exceeded: %u", >> stream->request_header_length); >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + // XXX Should we respond with 431 (Request Header Fields Too Large) ? >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // A receiver MUST treat the receipt of any other type of frame or >> // a frame on a different stream as a connection error of type >> PROTOCOL_ERROR. >> if (cstate.get_continued_id() != 0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // Change state. If changing is invalid, raise PROTOCOL_ERROR >> if (!stream->change_state(frame.header().type, frame.header().flags)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // Check whether padding exists or not. >> @@ -203,14 +208,14 @@ rcv_headers_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Ht >> frame.reader()->memcpy(buf, HTTP2_HEADERS_PADLEN_LEN, nbytes); >> nbytes += HTTP2_HEADERS_PADLEN_LEN; >> if (!http2_parse_headers_parameter(make_iovec(buf, >> HTTP2_HEADERS_PADLEN_LEN), params)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> if (params.pad_length > payload_length) { >> // If the length of the padding is the length of the >> // frame payload or greater, the recipient MUST treat this as a >> // connection error of type PROTOCOL_ERROR. >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } else { >> params.pad_length = 0; >> @@ -222,7 +227,7 @@ rcv_headers_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Ht >> frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, nbytes); >> nbytes += HTTP2_PRIORITY_LEN; >> if (!http2_parse_priority_parameter(make_iovec(buf, HTTP2_PRIORITY_LEN), >> params.priority)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> @@ -243,11 +248,11 @@ rcv_headers_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Ht >> // connection error of type COMPRESSION_ERROR if it does >> // not decompress a header block. >> if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) >> { >> - return HTTP2_ERROR_COMPRESSION_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_COMPRESSION_ERROR); >> } >> >> if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> remaining_bytes = header_block_fragment.iov_len - decoded_bytes; >> @@ -266,10 +271,10 @@ rcv_headers_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Ht >> stream->init_fetcher(cstate); >> } >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_priority_frame(Http2ClientSession &cs, Http2ConnectionState & >> /*cstate*/, const Http2Frame &frame) >> { >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] received PRIORITY frame", >> cs.connection_id()); >> @@ -277,22 +282,22 @@ rcv_priority_frame(Http2ClientSession &cs, >> Http2ConnectionState & /*cstate*/, co >> // If a PRIORITY frame is received with a stream identifier of 0x0, the >> // recipient MUST respond with a connection error of type PROTOCOL_ERROR. >> if (frame.header().streamid == 0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // A PRIORITY frame with a length other than 5 octets MUST be treated as >> // a stream error (Section 5.4.2) of type FRAME_SIZE_ERROR. >> if (frame.header().length != HTTP2_PRIORITY_LEN) { >> - return HTTP2_ERROR_FRAME_SIZE_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> } >> >> // TODO Pick stream dependencies and weight >> // Supporting PRIORITY is not essential so its temporarily ignored. >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_rst_stream_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, >> const Http2Frame &frame) >> { >> Http2RstStream rst_stream; >> @@ -301,35 +306,41 @@ rcv_rst_stream_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const >> >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received RST_STREAM frame.", >> cs.connection_id()); >> >> + // RST_STREAM frames MUST be associated with a stream. If a RST_STREAM >> + // frame is received with a stream identifier of 0x0, the recipient MUST >> + // treat this as a connection error (Section 5.4.1) of type >> + // PROTOCOL_ERROR. >> Http2StreamId stream_id = frame.header().streamid; >> >> if (!http2_is_client_streamid(stream_id)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> Http2Stream *stream = cstate.find_stream(stream_id); >> if (stream == NULL) { >> if (stream_id <= cstate.get_latest_stream_id()) { >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } else { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> + // A RST_STREAM frame with a length other than 4 octets MUST be treated >> + // as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. >> if (frame.header().length != HTTP2_RST_STREAM_LEN) { >> - return HTTP2_ERROR_FRAME_SIZE_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> } >> >> - if (stream != NULL && !stream->change_state(frame.header().type, >> frame.header().flags)) { >> + if (stream == NULL || !stream->change_state(frame.header().type, >> frame.header().flags)) { >> // If a RST_STREAM frame identifying an idle stream is received, the >> // recipient MUST treat this as a connection error of type PROTOCOL_ERROR. >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> end = frame.reader()->memcpy(buf, sizeof(buf), 0); >> >> if (!http2_parse_rst_stream(make_iovec(buf, end - buf), rst_stream)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> if (stream != NULL) { >> @@ -338,10 +349,10 @@ rcv_rst_stream_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const >> cstate.delete_stream(stream); >> } >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_settings_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, >> const Http2Frame &frame) >> { >> Http2SettingsParameter param; >> @@ -351,26 +362,44 @@ rcv_settings_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const H >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received SETTINGS frame.", >> cs.connection_id()); >> >> // 6.5 The stream identifier for a SETTINGS frame MUST be zero. >> + // If an endpoint receives a SETTINGS frame whose stream identifier field >> is >> + // anything other than 0x0, the endpoint MUST respond with a connection >> + // error (Section 5.4.1) of type PROTOCOL_ERROR. >> if (frame.header().streamid != 0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // 6.5 Receipt of a SETTINGS frame with the ACK flag set and a >> // length field value other than 0 MUST be treated as a connection >> // error of type FRAME_SIZE_ERROR. >> if (frame.header().flags & HTTP2_FLAGS_SETTINGS_ACK) { >> - return frame.header().length == 0 ? HTTP2_ERROR_NO_ERROR : >> HTTP2_ERROR_FRAME_SIZE_ERROR; >> + if (frame.header().length == 0) { >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> + } else { >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> + } >> + } >> + >> + // A SETTINGS frame with a length other than a multiple of 6 octets MUST >> + // be treated as a connection error (Section 5.4.1) of type >> + // FRAME_SIZE_ERROR. >> + if (frame.header().length % 6 != 0) { >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> } >> >> while (nbytes < frame.header().length) { >> unsigned read_bytes = read_rcv_buffer(buf, sizeof(buf), nbytes, frame); >> >> if (!http2_parse_settings_parameter(make_iovec(buf, read_bytes), param)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> if (!http2_settings_parameter_is_valid(param)) { >> - return param.id == HTTP2_SETTINGS_INITIAL_WINDOW_SIZE ? >> HTTP2_ERROR_FLOW_CONTROL_ERROR : HTTP2_ERROR_PROTOCOL_ERROR; >> + if (param.id == HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) { >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FLOW_CONTROL_ERROR); >> + } else { >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> + } >> } >> >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] setting param=%d value=%u", >> cs.connection_id(), param.id, param.value); >> @@ -391,21 +420,21 @@ rcv_settings_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const H >> Http2Frame ackFrame(HTTP2_FRAME_TYPE_SETTINGS, 0, HTTP2_FLAGS_SETTINGS_ACK); >> cstate.ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &ackFrame); >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_push_promise_frame(Http2ClientSession &cs, Http2ConnectionState & >> /*cstate*/, const Http2Frame & /*frame*/) >> { >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] received PUSH_PROMISE frame", >> cs.connection_id()); >> >> // 8.2. A client cannot push. Thus, servers MUST treat the receipt of a >> // PUSH_PROMISE frame as a connection error of type PROTOCOL_ERROR. >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // 6.7. PING >> -static Http2ErrorCode >> +static Http2Error >> rcv_ping_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const >> Http2Frame &frame) >> { >> uint8_t opaque_data[HTTP2_PING_LEN]; >> @@ -415,18 +444,18 @@ rcv_ping_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> // If a PING frame is received with a stream identifier field value other >> than >> // 0x0, the recipient MUST respond with a connection error of type >> PROTOCOL_ERROR. >> if (frame.header().streamid != 0x0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // Receipt of a PING frame with a length field value other than 8 MUST >> // be treated as a connection error (Section 5.4.1) of type >> FRAME_SIZE_ERROR. >> if (frame.header().length != HTTP2_PING_LEN) { >> - return HTTP2_ERROR_FRAME_SIZE_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> } >> >> // An endpoint MUST NOT respond to PING frames containing this flag. >> if (frame.header().flags & HTTP2_FLAGS_PING_ACK) { >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> frame.reader()->memcpy(opaque_data, HTTP2_PING_LEN, 0); >> @@ -434,10 +463,10 @@ rcv_ping_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Http2 >> // ACK (0x1): An endpoint MUST set this flag in PING responses. >> cstate.send_ping_frame(frame.header().streamid, HTTP2_FLAGS_PING_ACK, >> opaque_data); >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_goaway_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const >> Http2Frame &frame) >> { >> Http2Goaway goaway; >> @@ -449,14 +478,14 @@ rcv_goaway_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Htt >> // An endpoint MUST treat a GOAWAY frame with a stream identifier other >> // than 0x0 as a connection error of type PROTOCOL_ERROR. >> if (frame.header().streamid != 0x0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> while (nbytes < frame.header().length) { >> unsigned read_bytes = read_rcv_buffer(buf, sizeof(buf), nbytes, frame); >> >> if (!http2_parse_goaway(make_iovec(buf, read_bytes), goaway)) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> @@ -466,10 +495,10 @@ rcv_goaway_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, const Htt >> cstate.handleEvent(HTTP2_SESSION_EVENT_FINI, NULL); >> // eventProcessor.schedule_imm(&cs, ET_NET, VC_EVENT_ERROR); >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_window_update_frame(Http2ClientSession &cs, Http2ConnectionState >> &cstate, const Http2Frame &frame) >> { >> char buf[HTTP2_WINDOW_UPDATE_LEN]; >> @@ -481,7 +510,7 @@ rcv_window_update_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, co >> // A WINDOW_UPDATE frame with a length other than 4 octets MUST be >> // treated as a connection error of type FRAME_SIZE_ERROR. >> if (frame.header().length != HTTP2_WINDOW_UPDATE_LEN) { >> - return HTTP2_ERROR_FRAME_SIZE_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_FRAME_SIZE_ERROR); >> } >> >> if (sid == 0) { >> @@ -492,7 +521,7 @@ rcv_window_update_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, co >> // A receiver MUST treat the receipt of a WINDOW_UPDATE frame with a >> connection >> // flow control window increment of 0 as a connection error of type >> PROTOCOL_ERROR; >> if (size == 0) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> cstate.client_rwnd += size; >> @@ -503,9 +532,9 @@ rcv_window_update_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, co >> >> if (stream == NULL) { >> if (sid <= cstate.get_latest_stream_id()) { >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } else { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> @@ -515,8 +544,7 @@ rcv_window_update_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, co >> // A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an >> // flow control window increment of 0 as a stream error of type >> PROTOCOL_ERROR; >> if (size == 0) { >> - cstate.send_rst_stream_frame(sid, HTTP2_ERROR_PROTOCOL_ERROR); >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> stream->client_rwnd += size; >> @@ -526,10 +554,10 @@ rcv_window_update_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, co >> } >> } >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> -static Http2ErrorCode >> +static Http2Error >> rcv_continuation_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, >> const Http2Frame &frame) >> { >> char >> buf[BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_CONTINUATION])]; >> @@ -539,25 +567,31 @@ rcv_continuation_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, con >> DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received CONTINUATION frame.", >> cs.connection_id()); >> >> // Find opened stream >> + // CONTINUATION frames MUST be associated with a stream. If a >> + // CONTINUATION frame is received whose stream identifier field is 0x0, >> + // the recipient MUST respond with a connection error (Section 5.4.1) of >> + // type PROTOCOL_ERROR. >> Http2Stream *stream = cstate.find_stream(stream_id); >> if (stream == NULL) { >> if (stream_id <= cstate.get_latest_stream_id()) { >> - return HTTP2_ERROR_STREAM_CLOSED; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_STREAM_CLOSED); >> } else { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> } >> >> // A CONTINUATION frame MUST be preceded by a HEADERS, PUSH_PROMISE or >> - // CONTINUATION frame without the END_HEADERS flag set. >> + // CONTINUATION frame without the END_HEADERS flag set. A recipient >> + // that observes violation of this rule MUST respond with a connection >> + // error (Section 5.4.1) of type PROTOCOL_ERROR. >> if (stream->get_state() != HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && >> stream->get_state() != HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> // A receiver MUST treat the receipt of any other type of frame or >> // a frame on a different stream as a connection error of type >> PROTOCOL_ERROR. >> if (stream->get_id() != cstate.get_continued_id()) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> const IOVec remaining_data = cstate.get_continued_headers(); >> @@ -575,7 +609,8 @@ rcv_continuation_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, con >> stream->request_header_length += frame.header().length; >> if (stream->request_header_length > Http2::max_request_header_size) { >> Error("HTTP/2 payload for headers exceeded: %u", >> stream->request_header_length); >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + // XXX Should we respond with 431 (Request Header Fields Too Large) ? >> + return Http2Error(HTTP2_ERROR_CLASS_STREAM, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> bool cont = nbytes < frame.header().length || !(frame.header().flags & >> HTTP2_FLAGS_HEADERS_END_HEADERS); >> @@ -585,11 +620,11 @@ rcv_continuation_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, con >> // connection error of type COMPRESSION_ERROR if it does >> // not decompress a header block. >> if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) >> { >> - return HTTP2_ERROR_COMPRESSION_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_COMPRESSION_ERROR); >> } >> >> if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) { >> - return HTTP2_ERROR_PROTOCOL_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_PROTOCOL_ERROR); >> } >> >> remaining_bytes = header_block_fragment.iov_len - decoded_bytes; >> @@ -609,7 +644,7 @@ rcv_continuation_frame(Http2ClientSession &cs, >> Http2ConnectionState &cstate, con >> stream->init_fetcher(cstate); >> } >> >> - return HTTP2_ERROR_NO_ERROR; >> + return Http2Error(HTTP2_ERROR_CLASS_NONE); >> } >> >> static const http2_frame_dispatch frame_handlers[HTTP2_FRAME_TYPE_MAX] = { >> @@ -663,7 +698,7 @@ Http2ConnectionState::main_event_handler(int event, void >> *edata) >> case HTTP2_SESSION_EVENT_RECV: { >> Http2Frame *frame = (Http2Frame *)edata; >> Http2StreamId last_streamid = frame->header().streamid; >> - Http2ErrorCode error; >> + Http2Error error; >> >> // 5.5 Extending HTTP/2 >> // Implementations MUST discard frames that have unknown or unsupported >> types. >> @@ -676,17 +711,21 @@ Http2ConnectionState::main_event_handler(int event, >> void *edata) >> if (frame_handlers[frame->header().type]) { >> error = frame_handlers[frame->header().type](*this->ua_session, *this, >> *frame); >> } else { >> - error = HTTP2_ERROR_INTERNAL_ERROR; >> + error = Http2Error(HTTP2_ERROR_CLASS_CONNECTION, >> HTTP2_ERROR_INTERNAL_ERROR); >> } >> >> - if (error != HTTP2_ERROR_NO_ERROR) { >> - this->send_goaway_frame(last_streamid, error); >> - cleanup_streams(); >> - // XXX We need to think a bit harder about how to coordinate the >> client session and the >> - // protocol connection. At this point, the protocol is shutting down, >> but there's no way >> - // to tell that to the client session. Perhaps this could be solved >> by implementing the >> - // half-closed state ... >> - SET_HANDLER(&Http2ConnectionState::state_closed); >> + if (error.cls != HTTP2_ERROR_CLASS_NONE) { >> + if (error.cls == HTTP2_ERROR_CLASS_CONNECTION) { >> + this->send_goaway_frame(last_streamid, error.code); >> + cleanup_streams(); >> + // XXX We need to think a bit harder about how to coordinate the >> client session and the >> + // protocol connection. At this point, the protocol is shutting >> down, but there's no way >> + // to tell that to the client session. Perhaps this could be solved >> by implementing the >> + // half-closed state ... >> + SET_HANDLER(&Http2ConnectionState::state_closed); >> + } else if (error.cls == HTTP2_ERROR_CLASS_STREAM) { >> + this->send_rst_stream_frame(last_streamid, error.code); >> + } >> } >> >> return 0; >> >