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.fcgi.parser;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.fcgi.FCGI;
24 import org.eclipse.jetty.util.log.Log;
25 import org.eclipse.jetty.util.log.Logger;
26
27 /**
28 * <p>Parser for FastCGI frame headers.</p>
29 * <pre>
30 * struct frame_header {
31 * ubyte version;
32 * ubyte type;
33 * ushort requestId;
34 * ushort contentLength;
35 * ubyte paddingLength;
36 * ubyte reserved;
37 * }
38 * </pre>
39 *
40 * @see Parser
41 */
42 public class HeaderParser
43 {
44 private static final Logger LOG = Log.getLogger(Parser.class);
45
46 private State state = State.VERSION;
47 private int cursor;
48 private int version;
49 private int type;
50 private int request;
51 private int length;
52 private int padding;
53
54 /**
55 * Parses the bytes in the given {@code buffer} as FastCGI header bytes
56 *
57 * @param buffer the bytes to parse
58 * @return whether there were enough bytes for a FastCGI header
59 */
60 public boolean parse(ByteBuffer buffer)
61 {
62 while (buffer.hasRemaining())
63 {
64 switch (state)
65 {
66 case VERSION:
67 {
68 version = buffer.get() & 0xFF;
69 state = State.TYPE;
70 break;
71 }
72 case TYPE:
73 {
74 type = buffer.get() & 0xFF;
75 state = State.REQUEST;
76 break;
77 }
78 case REQUEST:
79 {
80 if (buffer.remaining() >= 2)
81 {
82 request = buffer.getShort() & 0xFF_FF;
83 state = State.LENGTH;
84 }
85 else
86 {
87 state = State.REQUEST_BYTES;
88 cursor = 0;
89 }
90 break;
91 }
92 case REQUEST_BYTES:
93 {
94 int halfShort = buffer.get() & 0xFF;
95 request = (request << 8) + halfShort;
96 if (++cursor == 2)
97 state = State.LENGTH;
98 break;
99 }
100 case LENGTH:
101 {
102 if (buffer.remaining() >= 2)
103 {
104 length = buffer.getShort() & 0xFF_FF;
105 state = State.PADDING;
106 }
107 else
108 {
109 state = State.LENGTH_BYTES;
110 cursor = 0;
111 }
112 break;
113 }
114 case LENGTH_BYTES:
115 {
116 int halfShort = buffer.get() & 0xFF;
117 length = (length << 8) + halfShort;
118 if (++cursor == 2)
119 state = State.PADDING;
120 break;
121 }
122 case PADDING:
123 {
124 padding = buffer.get() & 0xFF;
125 state = State.RESERVED;
126 break;
127 }
128 case RESERVED:
129 {
130 buffer.get();
131 if (LOG.isDebugEnabled())
132 LOG.debug("Parsed request {} header {} length={}", getRequest(), getFrameType(), getContentLength());
133 return true;
134 }
135 default:
136 {
137 throw new IllegalStateException();
138 }
139 }
140 }
141 return false;
142 }
143
144 public FCGI.FrameType getFrameType()
145 {
146 return FCGI.FrameType.from(type);
147 }
148
149 public int getRequest()
150 {
151 return request;
152 }
153
154 public int getContentLength()
155 {
156 return length;
157 }
158
159 public int getPaddingLength()
160 {
161 return padding;
162 }
163
164 protected void reset()
165 {
166 state = State.VERSION;
167 cursor = 0;
168 version = 0;
169 type = 0;
170 request = 0;
171 length = 0;
172 padding = 0;
173 }
174
175 private enum State
176 {
177 VERSION, TYPE, REQUEST, REQUEST_BYTES, LENGTH, LENGTH_BYTES, PADDING, RESERVED
178 }
179 }