001: package com.meterware.pseudoserver;
002:
003: /********************************************************************************************************************
004: * $Id: ReceivedHttpMessage.java,v 1.3 2004/06/16 00:10:53 russgold Exp $
005: *
006: * Copyright (c) 2003-2004, Russell Gold
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
009: * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
010: * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
011: * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included in all copies or substantial portions
014: * of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
017: * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
019: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020: * DEALINGS IN THE SOFTWARE.
021: *
022: *******************************************************************************************************************/
023:
024: import java.util.Enumeration;
025: import java.util.Hashtable;
026: import java.io.*;
027:
028: /**
029: *
030: * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
031: **/
032: abstract class ReceivedHttpMessage {
033:
034: private static final int CR = 13;
035: private static final int LF = 10;
036: private Reader _reader;
037: private Hashtable _headers = new Hashtable();
038: private byte[] _requestBody;
039: private String _messageHeader;
040:
041: ReceivedHttpMessage(InputStream inputStream) throws IOException {
042: _messageHeader = readHeaderLine(inputStream);
043: interpretMessageHeader(_messageHeader);
044: readHeaders(inputStream);
045: readMessageBody(inputStream);
046: }
047:
048: public String toString() {
049: StringBuffer sb = new StringBuffer(getClassName()).append("[ ");
050: appendMessageHeader(sb);
051: sb.append("\n");
052: for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
053: Object key = e.nextElement();
054: sb.append(" ").append(key).append(": ").append(
055: _headers.get(key)).append("\n");
056: }
057: sb.append(" body contains ").append(getBody().length).append(
058: " byte(s)]");
059: return sb.toString();
060: }
061:
062: private String readHeaderLine(InputStream inputStream)
063: throws IOException {
064: return new String(readDelimitedChunk(inputStream));
065: }
066:
067: private byte[] readDelimitedChunk(InputStream inputStream)
068: throws IOException {
069: ByteArrayOutputStream baos = new ByteArrayOutputStream();
070: int b = inputStream.read();
071: while (b != CR) {
072: baos.write(b);
073: b = inputStream.read();
074: }
075:
076: b = inputStream.read();
077: if (b != LF)
078: throw new IOException("Bad header line termination: " + b);
079: return baos.toByteArray();
080: }
081:
082: void appendContents(StringBuffer sb) {
083: for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
084: Object key = e.nextElement();
085: sb.append(" ").append(key).append(": ").append(
086: _headers.get(key)).append("\n");
087: }
088: sb.append(" body contains ").append(getBody().length).append(
089: " byte(s)");
090: }
091:
092: Reader getReader() {
093: return _reader;
094: }
095:
096: String getHeader(String name) {
097: return (String) _headers.get(name.toUpperCase());
098: }
099:
100: byte[] getBody() {
101: return _requestBody;
102: }
103:
104: private void readMessageBody(InputStream inputStream)
105: throws IOException {
106: if ("chunked".equalsIgnoreCase(getHeader("Transfer-Encoding"))) {
107: ByteArrayOutputStream baos = new ByteArrayOutputStream();
108: while (getNextChunkLength(inputStream) > 0) {
109: baos.write(readDelimitedChunk(inputStream));
110: }
111: flushChunkTrailer(inputStream);
112: _requestBody = baos.toByteArray();
113: } else {
114: int totalExpected = getContentLength();
115: ByteArrayOutputStream baos = new ByteArrayOutputStream(
116: totalExpected);
117: byte[] buffer = new byte[1024];
118: int total = 0;
119: int count = -1;
120: while ((total < totalExpected)
121: && ((count = inputStream.read(buffer)) != -1)) {
122: baos.write(buffer, 0, count);
123: total += count;
124: }
125: baos.flush();
126: _requestBody = baos.toByteArray();
127: }
128: _reader = new InputStreamReader(new ByteArrayInputStream(
129: _requestBody));
130: }
131:
132: private void flushChunkTrailer(InputStream inputStream)
133: throws IOException {
134: byte[] line;
135: do {
136: line = readDelimitedChunk(inputStream);
137: } while (line.length > 0);
138: }
139:
140: private int getNextChunkLength(InputStream inputStream)
141: throws IOException {
142: try {
143: return Integer.parseInt(readHeaderLine(inputStream));
144: } catch (NumberFormatException e) {
145: throw new IOException("Unabled to read chunk length: " + e);
146: }
147: }
148:
149: private int getContentLength() {
150: try {
151: return Integer.parseInt(getHeader("Content-Length"));
152: } catch (NumberFormatException e) {
153: return 0;
154: }
155: }
156:
157: private void readHeaders(InputStream inputStream)
158: throws IOException {
159: String lastHeader = null;
160:
161: String header = readHeaderLine(inputStream);
162: while (header.length() > 0) {
163: if (header.charAt(0) <= ' ') {
164: if (lastHeader == null)
165: continue;
166: _headers.put(lastHeader, _headers.get(lastHeader)
167: + header.trim());
168: } else {
169: lastHeader = header.substring(0, header.indexOf(':'))
170: .toUpperCase();
171: _headers.put(lastHeader, header.substring(
172: header.indexOf(':') + 1).trim());
173: }
174: header = readHeaderLine(inputStream);
175: }
176: }
177:
178: private String getClassName() {
179: return getClass().getName().substring(
180: getClass().getName().lastIndexOf('.') + 1);
181: }
182:
183: abstract void appendMessageHeader(StringBuffer sb);
184:
185: abstract void interpretMessageHeader(String messageHeader);
186:
187: }
|