view 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 source

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;
			}
		}
	}
}