001: /*
002: * Copyright (c) 2001 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: *
023: */
024:
025: package seda.sandStorm.lib.http;
026:
027: import seda.sandStorm.api.*;
028: import seda.sandStorm.lib.aSocket.*;
029: import seda.sandStorm.lib.util.*;
030: import java.util.*;
031: import java.io.*;
032: import java.net.*;
033:
034: /**
035: * This is a package-internal class which reads HTTP request packets.
036: * An instance of this class is fed ATcpInPackets (via the
037: * <tt>parsePacket</tt> method). When a complete packet has been
038: * read, an httpRequest is pushed to the corresponding SinkIF.
039: * This is the bulk of the HTTP protocol implementation.
040: *
041: * @author Matt Welsh
042: */
043: class httpPacketReader implements httpConst {
044:
045: private static final boolean DEBUG = false;
046:
047: private static final int STATE_START = 0;
048: private static final int STATE_HEADER = 1;
049: private static final int STATE_DONE = 2;
050: private static final int STATE_BODY = 3;
051:
052: private int state;
053: private aSocketInputStream ais;
054: private StreamTokenizer tok;
055:
056: private String request;
057: private String url;
058: private int httpver;
059: private Vector header;
060: private httpConnection conn;
061: private SinkIF compQ;
062:
063: private StringBuffer body;
064: private int content_length = 0;
065:
066: /**
067: * Create an httpPacketReader with the given httpConnection
068: * and completion queue.
069: */
070: httpPacketReader(httpConnection conn, SinkIF compQ) {
071: this .conn = conn;
072: this .compQ = compQ;
073: this .ais = new aSocketInputStream();
074: reset();
075: }
076:
077: /**
078: * Parse the given packet; returns true if a complete HTTP
079: * request has been received and parsed.
080: */
081: boolean parsePacket(ATcpInPacket pkt) throws IOException {
082: if (DEBUG) {
083: System.err.println("\nGPR: pushPacket called, size "
084: + pkt.getBytes().length + ", sequence "
085: + pkt.getSequenceNumber());
086: //System.err.println("GPR: pushPacket called, sequence "+pkt.getSequenceNumber());
087: //System.err.println("httpPacketReader got packet: \n----------------------------------");
088: //String s = new String(pkt.getBytes());
089: //System.err.println(s+"\n----------------------------------");
090: }
091:
092: ais.addPacket(pkt);
093:
094: int origstate;
095:
096: do {
097: origstate = state;
098:
099: switch (state) {
100: case STATE_START:
101: state = parseURL();
102: break;
103:
104: case STATE_HEADER:
105: state = accumulateHeader();
106: break;
107:
108: case STATE_BODY:
109: calcRequestBodyLength();
110: state = readRequestBody();
111: break;
112:
113: case STATE_DONE:
114: processHeader();
115: reset();
116: return true;
117:
118: default:
119: throw new Error("Bad state in pushPacket");
120: }
121:
122: } while (state != origstate);
123:
124: //System.out.println("GPR: multipacket request");
125: return false;
126: }
127:
128: /**
129: * Reset the internal state of the packet reader.
130: */
131: private void reset() {
132: state = STATE_START;
133: ais.clear();
134: tok = new StreamTokenizer(ais);
135: tok.resetSyntax();
136: tok.wordChars((char) 0, (char) 255);
137: tok.whitespaceChars('\u0000', '\u0020');
138: tok.eolIsSignificant(true);
139: request = null;
140: url = null;
141: header = null;
142: httpver = 0;
143: body = new StringBuffer();
144: }
145:
146: /**
147: * Parse the first line of the request header.
148: */
149: private int parseURL() throws IOException {
150: ais.mark(0);
151: String req = nextWord();
152: url = nextWord();
153: String ver = nextWord();
154: if ((req == null) || (url == null) || (ver == null)) {
155: ais.reset();
156: return STATE_START;
157: } else {
158: request = req;
159: if (ver.equals("HTTP/1.0")) {
160: httpver = httpRequest.HTTPVER_10;
161: String tmp = nextWord(); // Throw away EOL
162: return STATE_HEADER;
163: } else if (ver.equals("HTTP/1.1")) {
164: httpver = httpRequest.HTTPVER_11;
165: String tmp = nextWord(); // Throw away EOL
166: return STATE_HEADER;
167: } else {
168: if (!ver.equals(CRLF)) {
169: throw new IOException(
170: "Unknown HTTP version in request: "
171: + httpver);
172: }
173: httpver = httpRequest.HTTPVER_09;
174: return STATE_DONE;
175: }
176: }
177: }
178:
179: /**
180: * Accumulate header lines.
181: */
182: private int accumulateHeader() throws IOException {
183:
184: String line;
185: do {
186: line = nextLine();
187: if (DEBUG)
188: System.err.println("hpr: accumulateHeader() read line "
189: + line);
190:
191: if (line == null) {
192: // End of buffer
193: return STATE_HEADER;
194: } else if (!line.equals("")) {
195: if (header == null)
196: header = new Vector(1);
197: header.addElement(line);
198: }
199:
200: } while (!line.equals(""));
201:
202: if (request.equalsIgnoreCase("post"))
203: return STATE_BODY;
204: else
205: return STATE_DONE;
206: }
207:
208: /**
209: *
210: */
211: public void calcRequestBodyLength()
212: {
213: Enumeration enum = header.elements();
214: while (enum.hasMoreElements())
215: {
216: String h = (String) enum.nextElement();
217: if (h.startsWith("Content-Length"))
218: {
219: try
220: {
221: //System.out.println("httpPacketReader : "+h);
222: StringTokenizer tkx = new StringTokenizer(h,": ",false);
223: tkx.nextToken();
224: Integer cl_i = new Integer( tkx.nextToken() );
225: content_length = cl_i.intValue();
226: //System.out.println("httpPacketReader : Content-Length parsed to "+content_length);
227:
228: }
229: catch (Exception e)
230: {
231: System.out.println("httpPacketReader : Exception parsing Content-Length -> "+e);
232: }
233: }
234: }
235: }
236:
237: /**
238: * Accumulate request body
239: */
240: private int readRequestBody() throws IOException {
241: ais.mark(0);
242:
243: //boolean end = false;
244:
245: while (true) {
246: int type = tok.nextToken();
247: switch (type) {
248: case StreamTokenizer.TT_EOL:
249: body.append(CRLF);
250: break;
251:
252: case StreamTokenizer.TT_EOF:
253: // er det virkelig slut??
254: if (body.length() < this .content_length)
255: return STATE_BODY;
256: else
257: return STATE_DONE;
258:
259: case StreamTokenizer.TT_WORD:
260: body.append(tok.sval);
261: break;
262:
263: case StreamTokenizer.TT_NUMBER:
264: body.append(Double.toString(tok.nval));
265: break;
266:
267: default:
268: continue;
269: }
270: }
271:
272: //return STATE_DONE;
273: }
274:
275: /**
276: * Process the header, possibly pushing an httpRequest to the user.
277: */
278: private void processHeader() throws IOException {
279: httpRequest req = new httpRequest(conn, request, url, httpver,
280: header, "" + body);
281: if (DEBUG)
282: System.err.println("httpPacketReader: Pushing req");
283: if (!compQ.enqueue_lossy(req)) {
284: System.err
285: .println("httpPacketReader: WARNING: Could not enqueue_lossy to user: "
286: + req);
287: }
288: }
289:
290: /**
291: * Read the next whitespace-delimited word from the packet.
292: */
293: private String nextWord() throws IOException {
294: while (true) {
295: int type = tok.nextToken();
296: switch (type) {
297:
298: case StreamTokenizer.TT_EOL:
299: return CRLF;
300:
301: case StreamTokenizer.TT_EOF:
302: return null;
303:
304: case StreamTokenizer.TT_WORD:
305: if (DEBUG)
306: System.err
307: .println("nextWord returning " + tok.sval);
308: return tok.sval;
309:
310: case StreamTokenizer.TT_NUMBER:
311: if (DEBUG)
312: System.err.println("nextWord returning number");
313: return Double.toString(tok.nval);
314:
315: default:
316: continue;
317: }
318: }
319: }
320:
321: /**
322: * Read the next line from the packet.
323: */
324: private String nextLine() throws IOException {
325: ais.mark(0);
326:
327: String line = new String("");
328: boolean first = true;
329:
330: while (true) {
331: switch (tok.nextToken()) {
332:
333: case StreamTokenizer.TT_EOL:
334: //System.err.println("nextLine returning "+line);
335: return line;
336:
337: case StreamTokenizer.TT_EOF:
338: ais.reset();
339: return null;
340:
341: case StreamTokenizer.TT_WORD:
342: //System.err.println("nextLine got word "+tok.sval);
343: if (first) {
344: line = tok.sval;
345: first = false;
346: } else {
347: line += " " + tok.sval;
348: }
349: break;
350:
351: case StreamTokenizer.TT_NUMBER:
352: //System.err.println("nextLine got number "+tok.nval);
353: if (first) {
354: line = Double.toString(tok.nval);
355: first = false;
356: } else {
357: line += " " + Double.toString(tok.nval);
358: }
359: break;
360:
361: default:
362: continue;
363: }
364: }
365: }
366:
367: }
|