1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 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
26 public abstract class Parser
27 {
28 protected final HeaderParser headerParser = new HeaderParser();
29 private State state = State.HEADER;
30 private int padding;
31
32 /**
33 * @param buffer the bytes to parse
34 * @return true if the caller should stop parsing, false if the caller should continue parsing
35 */
36 public boolean parse(ByteBuffer buffer)
37 {
38 while (true)
39 {
40 switch (state)
41 {
42 case HEADER:
43 {
44 if (!headerParser.parse(buffer))
45 return false;
46 state = State.CONTENT;
47 break;
48 }
49 case CONTENT:
50 {
51 ContentParser contentParser = findContentParser(headerParser.getFrameType());
52 if (headerParser.getContentLength() == 0)
53 {
54 contentParser.noContent();
55 }
56 else
57 {
58 ContentParser.Result result = contentParser.parse(buffer);
59 if (result == ContentParser.Result.PENDING)
60 {
61 // Not enough data, signal to read/parse more.
62 return false;
63 }
64 if (result == ContentParser.Result.ASYNC)
65 {
66 // The content will be processed asynchronously, signal to stop
67 // parsing; the async operation will eventually resume parsing.
68 return true;
69 }
70 }
71 padding = headerParser.getPaddingLength();
72 state = State.PADDING;
73 break;
74 }
75 case PADDING:
76 {
77 if (buffer.remaining() >= padding)
78 {
79 buffer.position(buffer.position() + padding);
80 reset();
81 break;
82 }
83 else
84 {
85 padding -= buffer.remaining();
86 buffer.position(buffer.limit());
87 return false;
88 }
89 }
90 default:
91 {
92 throw new IllegalStateException();
93 }
94 }
95 }
96 }
97
98 protected abstract ContentParser findContentParser(FCGI.FrameType frameType);
99
100 private void reset()
101 {
102 headerParser.reset();
103 state = State.HEADER;
104 padding = 0;
105 }
106
107 public interface Listener
108 {
109 public void onHeader(int request, HttpField field);
110
111 public void onHeaders(int request);
112
113 /**
114 * @param request the request id
115 * @param stream the stream type
116 * @param buffer the content bytes
117 * @return true to signal to the parser to stop parsing, false to continue parsing
118 * @see Parser#parse(java.nio.ByteBuffer)
119 */
120 public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer);
121
122 public void onEnd(int request);
123
124 public void onFailure(int request, Throwable failure);
125
126 public static class Adapter implements Listener
127 {
128 @Override
129 public void onHeader(int request, HttpField field)
130 {
131 }
132
133 @Override
134 public void onHeaders(int request)
135 {
136 }
137
138 @Override
139 public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
140 {
141 return false;
142 }
143
144 @Override
145 public void onEnd(int request)
146 {
147 }
148
149 @Override
150 public void onFailure(int request, Throwable failure)
151 {
152
153 }
154 }
155 }
156
157 private enum State
158 {
159 HEADER, CONTENT, PADDING
160 }
161 }