2008
|
1 package goodjava.webserver;
|
|
2
|
|
3 import java.io.ByteArrayOutputStream;
|
|
4 import java.io.InputStream;
|
|
5 import java.io.IOException;
|
|
6
|
|
7
|
|
8 public class ChunkedOutputStream extends ByteArrayOutputStream {
|
|
9 private static final byte[] end = "0\r\n\r\n".getBytes();
|
|
10 private static final int OPEN = 1;
|
|
11 private static final int CLOSING = 2;
|
|
12 private static final int CLOSED = 3;
|
|
13
|
|
14 private final Response response;
|
|
15 private int status = OPEN;
|
|
16 private int i = 0;
|
|
17
|
|
18 public ChunkedOutputStream(Response response) {
|
|
19 if(response==null) throw new NullPointerException();
|
|
20 this.response = response;
|
|
21 response.headers.put("Transfer-Encoding","chunked");
|
|
22 response.body = new ChunkedInputStream();
|
|
23 }
|
|
24
|
|
25 @Override public synchronized void close() {
|
|
26 if( status == OPEN ) {
|
|
27 status = CLOSING;
|
|
28 notifyAll();
|
|
29 }
|
|
30 }
|
|
31
|
|
32 @Override public synchronized void write(int b) {
|
|
33 super.write(b);
|
|
34 notifyAll();
|
|
35 }
|
|
36
|
|
37 @Override public synchronized void write(byte b[], int off, int len) {
|
|
38 super.write(b,off,len);
|
|
39 notifyAll();
|
|
40 }
|
|
41
|
|
42 @Override public synchronized void reset() {
|
|
43 super.reset();
|
|
44 i = 0;
|
|
45 }
|
|
46
|
|
47 private class ChunkedInputStream extends InputStream {
|
|
48
|
|
49 @Override public int read() {
|
|
50 throw new UnsupportedOperationException();
|
|
51 }
|
|
52
|
|
53 @Override public int read(byte[] b,int off,int len) throws IOException {
|
|
54 synchronized(ChunkedOutputStream.this) {
|
|
55 if( i == count ) {
|
|
56 if( status == CLOSED )
|
|
57 return -1;
|
|
58 if( status == CLOSING ) {
|
|
59 System.arraycopy(end,0,b,off,end.length);
|
|
60 status = CLOSED;
|
|
61 return end.length;
|
|
62 }
|
|
63 try {
|
|
64 ChunkedOutputStream.this.wait();
|
|
65 } catch(InterruptedException e) {
|
|
66 throw new RuntimeException(e);
|
|
67 }
|
|
68 return read(b,off,len);
|
|
69 }
|
|
70 int offOld = off;
|
|
71 int left = count - i;
|
|
72 int extra = Integer.toHexString(left).length() + 4;
|
|
73 int n = Math.min( len - extra, left );
|
|
74 byte[] hex = Integer.toHexString(n).getBytes();
|
|
75 System.arraycopy( hex, 0, b, off, hex.length );
|
|
76 off += hex.length;
|
|
77 b[off++] = '\r'; b[off++] = '\n';
|
|
78 System.arraycopy(buf,i,b,off,n);
|
|
79 off += n;
|
|
80 b[off++] = '\r'; b[off++] = '\n';
|
|
81 i += n;
|
|
82 if( i == count )
|
|
83 ChunkedOutputStream.this.reset();
|
|
84 return off - offOld;
|
|
85 }
|
|
86 }
|
|
87 }
|
|
88 }
|