diff src/goodjava/webserver/ChunkedOutputStream.java @ 2008:bba3e529e346 default tip

chunked encoding
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 27 Aug 2025 01:14:17 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/goodjava/webserver/ChunkedOutputStream.java	Wed Aug 27 01:14:17 2025 -0600
@@ -0,0 +1,88 @@
+package goodjava.webserver;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+
+public class ChunkedOutputStream extends ByteArrayOutputStream {
+	private static final byte[] end = "0\r\n\r\n".getBytes();
+	private static final int OPEN = 1;
+	private static final int CLOSING = 2;
+	private static final int CLOSED = 3;
+
+	private final Response response;
+	private int status = OPEN;
+	private int i = 0;
+
+	public ChunkedOutputStream(Response response) {
+		if(response==null) throw new NullPointerException();
+		this.response = response;
+		response.headers.put("Transfer-Encoding","chunked");
+		response.body = new ChunkedInputStream();
+	}
+
+	@Override public synchronized void close() {
+		if( status == OPEN ) {
+			status = CLOSING;
+			notifyAll();
+		}
+	}
+
+	@Override public synchronized void write(int b) {
+		super.write(b);
+		notifyAll();
+	}
+
+	@Override public synchronized void write(byte b[], int off, int len) {
+		super.write(b,off,len);
+		notifyAll();
+	}
+
+	@Override public synchronized void reset() {
+		super.reset();
+		i = 0;
+	}
+
+	private class ChunkedInputStream extends InputStream {
+
+		@Override public int read() {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override public int read(byte[] b,int off,int len) throws IOException {
+			synchronized(ChunkedOutputStream.this) {
+				if( i == count ) {
+					if( status == CLOSED )
+						return -1;
+					if( status == CLOSING ) {
+						System.arraycopy(end,0,b,off,end.length);
+						status = CLOSED;
+						return end.length;
+					}
+					try {
+						ChunkedOutputStream.this.wait();
+					} catch(InterruptedException e) {
+						throw new RuntimeException(e);
+					}
+					return read(b,off,len);
+				}
+				int offOld = off;
+				int left = count - i;
+				int extra = Integer.toHexString(left).length() + 4;
+				int n = Math.min( len - extra, left );
+				byte[] hex = Integer.toHexString(n).getBytes();
+				System.arraycopy( hex, 0, b, off, hex.length );
+				off += hex.length;
+				b[off++] = '\r';  b[off++] = '\n';
+				System.arraycopy(buf,i,b,off,n);
+				off += n;
+				b[off++] = '\r';  b[off++] = '\n';
+				i += n;
+				if( i == count )
+					ChunkedOutputStream.this.reset();
+				return off - offOld;
+			}
+		}
+	}
+}