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.http.HttpField;
25 import org.eclipse.jetty.util.log.Log;
26 import org.eclipse.jetty.util.log.Logger;
27
28 /**
29 * <p>The FastCGI protocol exchanges <em>frames</em>.</p>
30 * <pre>
31 * struct frame {
32 * ubyte version;
33 * ubyte type;
34 * ushort requestId;
35 * ushort contentLength;
36 * ubyte paddingLength;
37 * ubyte reserved;
38 * ubyte[] content;
39 * ubyte[] padding;
40 * }
41 * </pre>
42 * <p>Depending on the {@code type}, the content may have a different format,
43 * so there are specialized content parsers.</p>
44 *
45 * @see HeaderParser
46 * @see ContentParser
47 */
48 public abstract class Parser
49 {
50 private static final Logger LOG = Log.getLogger(Parser.class);
51
52 protected final HeaderParser headerParser = new HeaderParser();
53 private State state = State.HEADER;
54 private int padding;
55
56 /**
57 * @param buffer the bytes to parse
58 * @return true if the caller should stop parsing, false if the caller should continue parsing
59 */
60 public boolean parse(ByteBuffer buffer)
61 {
62 while (true)
63 {
64 switch (state)
65 {
66 case HEADER:
67 {
68 if (!headerParser.parse(buffer))
69 return false;
70 state = State.CONTENT;
71 break;
72 }
73 case CONTENT:
74 {
75 ContentParser contentParser = findContentParser(headerParser.getFrameType());
76 if (headerParser.getContentLength() == 0)
77 {
78 contentParser.noContent();
79 }
80 else
81 {
82 ContentParser.Result result = contentParser.parse(buffer);
83 if (LOG.isDebugEnabled())
84 LOG.debug("Parsed request {} content {} result={}", headerParser.getRequest(), headerParser.getFrameType(), result);
85
86 if (result == ContentParser.Result.PENDING)
87 {
88 // Not enough data, signal to read/parse more.
89 return false;
90 }
91 if (result == ContentParser.Result.ASYNC)
92 {
93 // The content will be processed asynchronously, signal to stop
94 // parsing; the async operation will eventually resume parsing.
95 return true;
96 }
97 }
98 padding = headerParser.getPaddingLength();
99 state = State.PADDING;
100 break;
101 }
102 case PADDING:
103 {
104 if (buffer.remaining() >= padding)
105 {
106 buffer.position(buffer.position() + padding);
107 reset();
108 break;
109 }
110 else
111 {
112 padding -= buffer.remaining();
113 buffer.position(buffer.limit());
114 return false;
115 }
116 }
117 default:
118 {
119 throw new IllegalStateException();
120 }
121 }
122 }
123 }
124
125 protected abstract ContentParser findContentParser(FCGI.FrameType frameType);
126
127 private void reset()
128 {
129 headerParser.reset();
130 state = State.HEADER;
131 padding = 0;
132 }
133
134 public interface Listener
135 {
136 public void onHeader(int request, HttpField field);
137
138 public void onHeaders(int request);
139
140 /**
141 * @param request the request id
142 * @param stream the stream type
143 * @param buffer the content bytes
144 * @return true to signal to the parser to stop parsing, false to continue parsing
145 * @see Parser#parse(java.nio.ByteBuffer)
146 */
147 public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer);
148
149 public void onEnd(int request);
150
151 public void onFailure(int request, Throwable failure);
152
153 public static class Adapter implements Listener
154 {
155 @Override
156 public void onHeader(int request, HttpField field)
157 {
158 }
159
160 @Override
161 public void onHeaders(int request)
162 {
163 }
164
165 @Override
166 public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
167 {
168 return false;
169 }
170
171 @Override
172 public void onEnd(int request)
173 {
174 }
175
176 @Override
177 public void onFailure(int request, Throwable failure)
178 {
179
180 }
181 }
182 }
183
184 private enum State
185 {
186 HEADER, CONTENT, PADDING
187 }
188 }