1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/http2/decoder/payload_decoders/ping_payload_decoder.h"
6 
7 #include "quiche/http2/decoder/http2_frame_decoder_listener.h"
8 #include "quiche/http2/http2_constants.h"
9 #include "quiche/common/platform/api/quiche_logging.h"
10 
11 namespace http2 {
12 namespace {
13 constexpr auto kOpaqueSize = Http2PingFields::EncodedSize();
14 }
15 
StartDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)16 DecodeStatus PingPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
17                                                       DecodeBuffer* db) {
18   const Http2FrameHeader& frame_header = state->frame_header();
19   const uint32_t total_length = frame_header.payload_length;
20 
21   QUICHE_DVLOG(2) << "PingPayloadDecoder::StartDecodingPayload: "
22                   << frame_header;
23   QUICHE_DCHECK_EQ(Http2FrameType::PING, frame_header.type);
24   QUICHE_DCHECK_LE(db->Remaining(), total_length);
25   QUICHE_DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::ACK));
26 
27   // Is the payload entirely in the decode buffer and is it the correct size?
28   // Given the size of the header and payload (17 bytes total), this is most
29   // likely the case the vast majority of the time.
30   if (db->Remaining() == kOpaqueSize && total_length == kOpaqueSize) {
31     // Special case this situation as it allows us to avoid any copying;
32     // the other path makes two copies, first into the buffer in
33     // Http2StructureDecoder as it accumulates the 8 bytes of opaque data,
34     // and a second copy into the Http2PingFields member of in this class.
35     // This supports the claim that this decoder is (mostly) non-buffering.
36     static_assert(sizeof(Http2PingFields) == kOpaqueSize,
37                   "If not, then can't enter this block!");
38     auto* ping = reinterpret_cast<const Http2PingFields*>(db->cursor());
39     if (frame_header.IsAck()) {
40       state->listener()->OnPingAck(frame_header, *ping);
41     } else {
42       state->listener()->OnPing(frame_header, *ping);
43     }
44     db->AdvanceCursor(kOpaqueSize);
45     return DecodeStatus::kDecodeDone;
46   }
47   state->InitializeRemainders();
48   return HandleStatus(
49       state, state->StartDecodingStructureInPayload(&ping_fields_, db));
50 }
51 
ResumeDecodingPayload(FrameDecoderState * state,DecodeBuffer * db)52 DecodeStatus PingPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
53                                                        DecodeBuffer* db) {
54   QUICHE_DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
55                   << state->remaining_payload();
56   QUICHE_DCHECK_EQ(Http2FrameType::PING, state->frame_header().type);
57   QUICHE_DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
58   return HandleStatus(
59       state, state->ResumeDecodingStructureInPayload(&ping_fields_, db));
60 }
61 
HandleStatus(FrameDecoderState * state,DecodeStatus status)62 DecodeStatus PingPayloadDecoder::HandleStatus(FrameDecoderState* state,
63                                               DecodeStatus status) {
64   QUICHE_DVLOG(2) << "HandleStatus: status=" << status
65                   << "; remaining_payload=" << state->remaining_payload();
66   if (status == DecodeStatus::kDecodeDone) {
67     if (state->remaining_payload() == 0) {
68       const Http2FrameHeader& frame_header = state->frame_header();
69       if (frame_header.IsAck()) {
70         state->listener()->OnPingAck(frame_header, ping_fields_);
71       } else {
72         state->listener()->OnPing(frame_header, ping_fields_);
73       }
74       return DecodeStatus::kDecodeDone;
75     }
76     // Payload is too long.
77     return state->ReportFrameSizeError();
78   }
79   // Not done decoding the structure. Either we've got more payload to decode,
80   // or we've run out because the payload is too short.
81   QUICHE_DCHECK(
82       (status == DecodeStatus::kDecodeInProgress &&
83        state->remaining_payload() > 0) ||
84       (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
85       << "\n status=" << status
86       << "; remaining_payload=" << state->remaining_payload();
87   return status;
88 }
89 
90 }  // namespace http2
91