001: package vicazh.hyperpool.stream.net.http;
002:
003: import java.io.*;
004: import java.util.*;
005:
006: /**
007: * This class is the superclass of all http stream data
008: *
009: * @author Victor Zhigunov
010: * @version 0.4.0
011: */
012: abstract public class Stream extends vicazh.hyperpool.stream.net.Stream {
013: public Stream() {
014: }
015:
016: public Session session;
017:
018: protected ByteArrayOutputStream stream;
019:
020: private Map<String, String> fields;
021:
022: /**
023: * @param session
024: * parent session
025: * @param outputstream
026: * linked output stream
027: */
028: public Stream(Session session, OutputStream outputstream) {
029: super (session.connection, outputstream);
030: this .session = session;
031: stream = new ByteArrayOutputStream();
032: fields = new HashMap<String, String>();
033: }
034:
035: protected int mode;
036:
037: protected final static int HEAD = 0;
038:
039: protected final static int FIELD = 1;
040:
041: protected final static int CONTENT = 2;
042:
043: protected final static int CHUNK = 3;
044:
045: protected final static int POSTCHUNK = 4;
046:
047: protected boolean isChunked;
048:
049: protected long length = -1;
050:
051: public void write(int b) throws IOException {
052: if (mode == CONTENT)
053: content(b);
054: else {
055: if (b == 10) {
056: byte[] a = stream.toByteArray();
057: String s = new String(a, 0, a.length > 0
058: && a[a.length - 1] == 13 ? a.length - 1
059: : a.length);
060: stream.reset();
061: if (mode == HEAD) {
062: if (s.length() == 0)
063: line(s);
064: else {
065: if (s.equals("0"))
066: line(s);
067: else
068: head(s);
069: }
070: } else if (mode == FIELD) {
071: if (s.length() == 0)
072: header();
073: else {
074: int j = s.indexOf(':');
075: field(s.substring(0, j),
076: s.charAt(j + 1) == ' ' ? s
077: .substring(j + 2) : s
078: .substring(j + 1));
079: }
080: } else if (mode == CHUNK) {
081: line(s);
082: if (s.length() != 0) {
083: length = Long.parseLong(new StringTokenizer(s)
084: .nextToken(), 16);
085: if (length == 0)
086: mode = POSTCHUNK;
087: else
088: mode = CONTENT;
089: }
090: } else {
091: line(s);
092: end();
093: set();
094: outputstream = null;
095: }
096: } else
097: stream.write(b);
098: }
099: }
100:
101: /**
102: * Writes the content to this stream
103: *
104: * @param b
105: * the <code>byte</code>
106: */
107: public void content(int b) throws IOException {
108: super .write(b);
109: if (length > 0)
110: if (--length == 0)
111: if (isChunked)
112: mode = CHUNK;
113: else {
114: end();
115: set();
116: outputstream = null;
117: }
118: }
119:
120: /**
121: * Writes the first line to this stream
122: *
123: * @param s
124: * the <code>String</code> to be written
125: */
126: public void head(String s) throws IOException {
127: line(s);
128: mode = FIELD;
129: }
130:
131: /**
132: * Writes the field to this stream
133: *
134: * @param s1
135: * the name
136: * @param s2
137: * the value
138: */
139: public void field(String s1, String s2) throws IOException {
140: if (s1.equalsIgnoreCase("Content-Length"))
141: length = Long.parseLong(s2.trim());
142: else if (s1.equalsIgnoreCase("Transfer-Encoding")
143: && s2.equalsIgnoreCase("chunked"))
144: isChunked = true;
145: fields.put(s1.toLowerCase(), s2);
146: line(s1 + ": " + s2);
147: }
148:
149: /**
150: * Writes the line to this stream
151: *
152: * @param s
153: * the <code>String</code> to be written
154: */
155: protected void line(String s) throws IOException {
156: for (byte b : (s + "\r\n").getBytes())
157: super .write(b);
158: }
159:
160: /**
161: * Writes the empty line after all fields
162: */
163: public void header() throws IOException {
164: line("");
165: if (isContent())
166: mode = isChunked ? CHUNK : CONTENT;
167: else {
168: end();
169: set();
170: outputstream = null;
171: }
172: }
173:
174: abstract protected boolean isContent();
175:
176: public void setFields(Map<String, String> fields) {
177: this .fields = fields;
178: }
179:
180: public Map<String, String> getFields() {
181: return fields;
182: }
183:
184: /**
185: * The ended state of the stream
186: */
187: public boolean isEnd;
188:
189: /**
190: * End this stream
191: */
192: synchronized public void end() {
193: isEnd = true;
194: notifyAll();
195: }
196:
197: public void close() throws IOException {
198: if (!isEnd)
199: end();
200: super .close();
201: }
202:
203: abstract protected void set() throws IOException;
204: }
|