1 /* Reattore HTTP Server
2
3 Copyright (C) 2002 Michael Hope <michaelh@juju.net.nz>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 $Id: StartLineParser.java,v 1.6 2003/02/22 04:29:52 michaelh Exp $
20 */
21
22 package juju.reattore.protocol.http.impl;
23
24 import java.util.*;
25 import java.io.IOException;
26
27 import juju.reattore.io.impl.*;
28 import juju.reattore.io.ByteSource;
29 import juju.reattore.protocol.http.ParseException;
30 import juju.reattore.util.CharUtil;
31
32 import org.apache.commons.logging.*;
33
34 /*** Parses a HTTP start (request/response) line.
35
36 @todo Minor/Strict: Doesn't detect invalid leading spaces.
37 */
38 public class StartLineParser {
39
40 private static Log log = LogFactory.getLog(StartLineParser.class);
41
42 private StringBuffer method;
43 private StringBuffer path;
44 private StringBuffer query;
45 private StringBuffer version;
46
47 private static final int IN_SP = 0;
48 private static final int IN_METHOD = 1;
49 private static final int IN_PATH = 2;
50 private static final int IN_QUERY = 3;
51 private static final int IN_ENCODED_1 = 4;
52 private static final int IN_ENCODED_2 = 5;
53 private static final int IN_VERSION = 6;
54 private static final int IN_EOL = 7;
55 private static final int IN_RESET = 8;
56
57 private int state = IN_RESET;
58 private int nextState;
59
60 private Callback callback;
61
62 private int encVal;
63 private StringBuffer encTo;
64
65 /*** May be used to generate events on the end of parse instead of
66 being data driven. Register using #setCallback
67 */
68 public interface Callback {
69 /*** Called when a line has been parsed.
70
71 @param method The parsed method
72 @param path The parsed path
73 @param query The parsed query
74 @param version The parsed version
75 */
76 void onStartLine(String method, String path, String query,
77 String version);
78 };
79
80 /*** Sets what to call when a line has been parsed.
81
82 @param callback The class to call, or null to disable.
83 */
84 public void setCallback(Callback callback) {
85 this.callback = callback;
86 }
87
88 /*** Parse the line.
89
90 @param in Source to parse from
91 @return false means more parsing needed
92 @throws ParseException if an error occurs while parsing.
93 @throws IOException on error.
94 */
95 public boolean add(PushbackByteSource in)
96 throws ParseException, IOException {
97
98 int got;
99
100 if (state == IN_RESET) {
101 reset();
102 }
103
104 while ((got = in.get()) != PushbackByteSource.EOF) {
105 switch (state) {
106 case IN_SP:
107 switch (got) {
108 case ' ':
109 break;
110 default:
111 in.pushback(got);
112 state = nextState;
113 break;
114 }
115 break;
116 case IN_METHOD:
117 switch (got) {
118 case ' ':
119 state = IN_SP;
120 nextState = IN_PATH;
121 break;
122 default:
123 method.append((char)got);
124 break;
125 }
126 break;
127 case IN_PATH:
128 switch (got) {
129 case '\r':
130 state = IN_EOL;
131 break;
132 case '%':
133 encTo = path;
134 nextState = state;
135 state = IN_ENCODED_1;
136 break;
137 case ' ':
138 state = IN_SP;
139 nextState = IN_VERSION;
140 break;
141 case '?':
142 state = IN_QUERY;
143 break;
144 default:
145 path.append((char)got);
146 break;
147 }
148 break;
149 case IN_QUERY:
150 switch (got) {
151 case '\r':
152 state = IN_EOL;
153 break;
154 case '%':
155 encTo = query;
156 nextState = state;
157 state = IN_ENCODED_1;
158 break;
159 case ' ':
160 state = IN_SP;
161 nextState = IN_VERSION;
162 break;
163 case '+':
164 query.append(' ');
165 break;
166 default:
167 query.append((char)got);
168 break;
169 }
170 break;
171 case IN_ENCODED_1:
172 encVal = CharUtil.fromNibble(got) * 16;
173 state = IN_ENCODED_2;
174 break;
175 case IN_ENCODED_2:
176 encTo.append((char)(CharUtil.fromNibble(got) + encVal));
177 state = nextState;
178 break;
179 case IN_VERSION:
180 switch (got) {
181 case '\r':
182 state = IN_EOL;
183 break;
184 default:
185 version.append((char)got);
186 break;
187 }
188 break;
189 case IN_EOL:
190 switch (got) {
191 case '\n':
192 runCallback();
193 state = IN_RESET;
194 return true;
195 default:
196 throw new ParseException();
197 }
198 default:
199 throw new ParseException();
200 }
201 }
202
203 return false;
204 }
205
206 private void runCallback() {
207 if (callback != null) {
208 callback.onStartLine(method.toString(),
209 path.toString(),
210 query.toString(),
211 version.toString());
212 }
213 }
214
215 /*** Resets back to a clean state.
216 */
217 public void reset() {
218 method = new StringBuffer();
219 path = new StringBuffer();
220 query = new StringBuffer();
221 version = new StringBuffer();
222
223 state = IN_SP;
224 nextState = IN_METHOD;
225 }
226
227 /*** Gets the parsed method.
228
229 @return The parsed value, or "" if none.
230 */
231 public String getMethod() {
232 return method.toString();
233 }
234
235 /*** Gets the parsed path.
236
237 @return The parsed value, or "" if none.
238 */
239 public String getPath() {
240 return path.toString();
241 }
242
243 /*** Gets the parsed query.
244
245 @return The parsed value, or "" if none.
246 */
247 public String getQuery() {
248 return query.toString();
249 }
250
251 /*** Gets the parsed version.
252
253 @return The parsed value, or "" if none.
254 */
255 public String getVersion() {
256 return version.toString();
257 }
258 }
This page was automatically generated by Maven