1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.http2.parser;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.http2.ErrorCode;
24 import org.eclipse.jetty.http2.Flags;
25 import org.eclipse.jetty.http2.frames.PingFrame;
26
27 public class PingBodyParser extends BodyParser
28 {
29 private State state = State.PREPARE;
30 private int cursor;
31 private byte[] payload;
32
33 public PingBodyParser(HeaderParser headerParser, Parser.Listener listener)
34 {
35 super(headerParser, listener);
36 }
37
38 private void reset()
39 {
40 state = State.PREPARE;
41 cursor = 0;
42 payload = null;
43 }
44
45 @Override
46 public boolean parse(ByteBuffer buffer)
47 {
48 while (buffer.hasRemaining())
49 {
50 switch (state)
51 {
52 case PREPARE:
53 {
54 // SPEC: wrong streamId is treated as connection error.
55 if (getStreamId() != 0)
56 return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_ping_frame");
57 // SPEC: wrong body length is treated as connection error.
58 if (getBodyLength() != 8)
59 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_ping_frame");
60 state = State.PAYLOAD;
61 break;
62 }
63 case PAYLOAD:
64 {
65 payload = new byte[8];
66 if (buffer.remaining() >= 8)
67 {
68 buffer.get(payload);
69 return onPing(payload);
70 }
71 else
72 {
73 state = State.PAYLOAD_BYTES;
74 cursor = 8;
75 }
76 break;
77 }
78 case PAYLOAD_BYTES:
79 {
80 payload[8 - cursor] = buffer.get();
81 --cursor;
82 if (cursor == 0)
83 return onPing(payload);
84 break;
85 }
86 default:
87 {
88 throw new IllegalStateException();
89 }
90 }
91 }
92 return false;
93 }
94
95 private boolean onPing(byte[] payload)
96 {
97 PingFrame frame = new PingFrame(payload, hasFlag(Flags.ACK));
98 reset();
99 notifyPing(frame);
100 return true;
101 }
102
103 private enum State
104 {
105 PREPARE, PAYLOAD, PAYLOAD_BYTES
106 }
107 }