001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: /*
026: */
027: package gov.nist.siplite.parser;
028:
029: import gov.nist.siplite.message.*;
030: import gov.nist.siplite.header.*;
031: import gov.nist.core.*;
032: import java.io.*;
033:
034: import com.sun.midp.log.Logging;
035: import com.sun.midp.log.LogChannels;
036:
037: /**
038: * This implements a pipelined message parser suitable for use
039: * with a stream - oriented input such as TCP. The client uses
040: * this class by instatiating with an input stream from which
041: * input is read and fed to a message parser.
042: * It keeps reading from the input stream and process messages in a
043: * never ending interpreter loop. The message listener interface gets called
044: * for processing messages or for processing errors. The payload specified
045: * by the content-length header is read directly from the input stream.
046: * This can be accessed from the Message using the getContent and
047: * getContentBytes methods provided by the Message class.
048: *
049: * @version JAIN-SIP-1.1
050: *
051: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
052: *
053: * It was noticed that the parser was
054: * blocking so I threw out some cool pipelining which ran fast but only worked
055: * when the phase of the full moon matched its mood. Now things are serialized
056: * and life goes slower but more reliably.
057: *
058: * @see SIPMessageListener
059: */
060: public final class PipelinedMsgParser implements Runnable {
061: /**
062: * The message listener that is registered with this parser.
063: * (The message listener has methods that can process correct
064: * and erroneous messages.)
065: */
066: protected SIPMessageListener sipMessageListener;
067: /** Handle to the preprocssor thread. */
068: private Thread mythread;
069: /* Current message body contents. */
070: // private byte[] messageBody;
071: /* Flag to indicate an error was found. */
072: // private boolean errorFlag;
073: /** Raw data input to be processed. */
074: private InputStream rawInputStream;
075:
076: /** Default constructor. */
077: protected PipelinedMsgParser() {
078: super ();
079:
080: }
081:
082: /**
083: * Constructor when we are given a message listener and an input stream
084: * (could be a TCP connection or a file)
085: * @param sipMessageListener Message listener which has
086: * methods that get called
087: * back from the parser when a parse is complete
088: * @param in Input stream from which to read the input.
089: * @param debug Enable/disable tracing or lexical analyser switch.
090: */
091: public PipelinedMsgParser(SIPMessageListener sipMessageListener,
092: InputStream in, boolean debug) {
093: this ();
094: this .sipMessageListener = sipMessageListener;
095: rawInputStream = in;
096: mythread = new Thread(this );
097:
098: }
099:
100: /**
101: * This is the constructor for the pipelined parser.
102: * @param mhandler a MessageListener implementation that
103: * provides the message handlers to
104: * handle correctly and incorrectly parsed messages.
105: * @param in An input stream to read messages from.
106: */
107: public PipelinedMsgParser(SIPMessageListener mhandler,
108: InputStream in) {
109: this (mhandler, in, false);
110: }
111:
112: /**
113: * This is the constructor for the pipelined parser.
114: * @param in - An input stream to read messages from.
115: */
116: public PipelinedMsgParser(InputStream in) {
117: this (null, in, false);
118: }
119:
120: /**
121: * Start reading and processing input.
122: */
123: public void processInput() {
124: mythread.start();
125: }
126:
127: /**
128: * Create a new pipelined parser from an existing one.
129: * @return A new pipelined parser that reads from the same input
130: * stream.
131: */
132: protected Object clone() {
133: PipelinedMsgParser p = new PipelinedMsgParser();
134:
135: p.rawInputStream = this .rawInputStream;
136: p.sipMessageListener = this .sipMessageListener;
137: return p;
138: }
139:
140: /**
141: * Add a class that implements a MessageListener interface whose
142: * methods get called * on successful parse and error conditons.
143: * @param mlistener a MessageListener
144: * implementation that can react to correct and incorrect
145: * pars.
146: */
147: public void setMessageListener(SIPMessageListener mlistener) {
148: sipMessageListener = mlistener;
149: }
150:
151: /**
152: * Reads a line of input (I cannot use buffered reader because we
153: * may need to switch encodings mid-stream!
154: * @param inputStream source for data to be processed
155: * @return the line of text retrieved
156: * @exception IOException if an error occurs reading from input
157: */
158: private String readLine(InputStream inputStream) throws IOException {
159: StringBuffer retval = new StringBuffer("");
160: while (true) {
161: char ch;
162: int i = inputStream.read();
163: if (i == -1) {
164: throw new IOException("End of stream");
165: } else {
166: ch = (char) i;
167: }
168: if (ch != '\r') {
169: retval.append(ch);
170: }
171: if (ch == '\n') {
172: break;
173: }
174: }
175: return retval.toString();
176: }
177:
178: /**
179: * Reads to the next break (CRLFCRLF sequence).
180: * @param inputStream source for data to be processed
181: * @return the text up to the break character sequence
182: * @exception IOException if an error occurs reading from input
183: */
184: private String readToBreak(InputStream inputStream)
185: throws IOException {
186: StringBuffer retval = new StringBuffer("");
187: boolean flag = false;
188: while (true) {
189: char ch;
190: int i = inputStream.read();
191: if (i == -1)
192: break;
193: else
194: ch = (char) i;
195: if (ch != '\r')
196: retval.append(ch);
197: if (ch == '\n') {
198: if (flag)
199: break;
200: else
201: flag = true;
202: }
203: }
204: return retval.toString();
205: }
206:
207: /**
208: * This is input reading thread for the pipelined parser.
209: * You feed it input through the input stream (see the constructor)
210: * and it calls back an event listener interface for message
211: * processing or error.
212: * It cleans up the input - dealing with things like line continuation
213: *
214: */
215: public void run() {
216:
217: InputStream inputStream = this .rawInputStream;
218:
219: // I cannot use buffered reader here because we may need to switch
220: // encodings to read the message body.
221: try {
222: while (true) {
223: StringBuffer inputBuffer = new StringBuffer();
224:
225: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
226: Logging.report(Logging.INFORMATION,
227: LogChannels.LC_JSR180, "Starting parse!");
228: }
229:
230: String line1;
231: String line2 = null;
232:
233: // ignore blank lines.
234: while (true) {
235: line1 = readLine(inputStream);
236:
237: if (line1.equals("\n")) {
238: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
239: Logging.report(Logging.INFORMATION,
240: LogChannels.LC_JSR180,
241: "Discarding " + line1);
242: }
243: continue;
244: } else {
245: break;
246: }
247: }
248: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
249: Logging.report(Logging.INFORMATION,
250: LogChannels.LC_JSR180, "line1 = " + line1);
251: }
252:
253: inputBuffer.append(line1);
254:
255: while (true) {
256: line2 = readLine(inputStream);
257: inputBuffer.append(line2);
258: if (line2.trim().equals(""))
259: break;
260: }
261:
262: inputBuffer.append(line2);
263: StringMsgParser smp = new StringMsgParser(
264: sipMessageListener);
265: smp.readBody = false;
266: Message sipMessage = null;
267:
268: try {
269: sipMessage = smp.parseSIPMessage(inputBuffer
270: .toString());
271: if (sipMessage == null)
272: continue;
273: } catch (ParseException ex) {
274: // Just ignore the parse exception.
275: continue;
276: }
277:
278: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
279: Logging.report(Logging.INFORMATION,
280: LogChannels.LC_JSR180,
281: "Completed parsing message");
282: }
283:
284: ContentLengthHeader cl = sipMessage
285: .getContentLengthHeader();
286: int contentLength = 0;
287: if (cl != null) {
288: contentLength = cl.getContentLength();
289: } else {
290: contentLength = 0;
291: }
292:
293: if (contentLength == 0) {
294: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
295: Logging.report(Logging.INFORMATION,
296: LogChannels.LC_JSR180,
297: "content length " + contentLength);
298: }
299: sipMessage.removeContent();
300: } else { // deal with the message body.
301: contentLength = cl.getContentLength();
302:
303: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
304: Logging.report(Logging.INFORMATION,
305: LogChannels.LC_JSR180,
306: "content length " + contentLength);
307: }
308:
309: byte[] message_body = new byte[contentLength];
310: int nread = 0;
311:
312: while (nread < contentLength) {
313: int readlength = inputStream.read(message_body,
314: nread, contentLength - nread);
315:
316: if (readlength > 0) {
317: nread += readlength;
318:
319: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
320: Logging.report(Logging.INFORMATION,
321: LogChannels.LC_JSR180, "read "
322: + nread);
323: }
324: } else {
325: break;
326: }
327: }
328:
329: sipMessage.setMessageContent(message_body);
330: }
331:
332: if (sipMessageListener != null) {
333: sipMessageListener.processMessage(sipMessage);
334: }
335: }
336: } catch (IOException ex) {
337: if (sipMessageListener != null) {
338: sipMessageListener.handleIOException();
339: }
340: } finally {
341: }
342: }
343: }
|