blob: f53d40e5ee619da1b767bda3b19314f0b4124a6e [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.net.httpserver;
27
28import java.io.*;
29import java.net.*;
30import com.sun.net.httpserver.*;
31import com.sun.net.httpserver.spi.*;
32
33/**
34 * a class which allows the caller to write an arbitrary
35 * number of bytes to an underlying stream.
36 * normal close() does not close the underlying stream
37 *
38 * This class is buffered.
39 *
40 * Each chunk is written in one go as :-
41 * abcd\r\nxxxxxxxxxxxxxx\r\n
42 *
43 * abcd is the chunk-size, and xxx is the chunk data
44 * If the length is less than 4 chars (in size) then the buffer
45 * is written with an offset.
46 * Final chunk is:
47 * 0\r\n\r\n
48 */
49
50class ChunkedOutputStream extends FilterOutputStream
51{
52 private boolean closed = false;
53 /* max. amount of user data per chunk */
54 final static int CHUNK_SIZE = 4096;
55 /* allow 4 bytes for chunk-size plus 4 for CRLFs */
56 final static int OFFSET = 6; /* initial <=4 bytes for len + CRLF */
57 private int pos = OFFSET;
58 private int count = 0;
59 private byte[] buf = new byte [CHUNK_SIZE+OFFSET+2];
60 ExchangeImpl t;
61
62 ChunkedOutputStream (ExchangeImpl t, OutputStream src) {
63 super (src);
64 this.t = t;
65 }
66
67 public void write (int b) throws IOException {
68 if (closed) {
69 throw new StreamClosedException ();
70 }
71 buf [pos++] = (byte)b;
72 count ++;
73 if (count == CHUNK_SIZE) {
74 writeChunk();
75 }
76 }
77
78 public void write (byte[]b, int off, int len) throws IOException {
79 if (closed) {
80 throw new StreamClosedException ();
81 }
82 int remain = CHUNK_SIZE - count;
83 if (len > remain) {
84 System.arraycopy (b,off,buf,pos,remain);
85 count = CHUNK_SIZE;
86 writeChunk();
87 len -= remain;
88 off += remain;
89 while (len > CHUNK_SIZE) {
90 System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE);
91 len -= CHUNK_SIZE;
92 off += CHUNK_SIZE;
93 count = CHUNK_SIZE;
94 writeChunk();
95 }
96 pos = OFFSET;
97 }
98 if (len > 0) {
99 System.arraycopy (b,off,buf,pos,len);
100 count += len;
101 pos += len;
102 }
103 }
104
105 /**
106 * write out a chunk , and reset the pointers
107 * chunk does not have to be CHUNK_SIZE bytes
108 * count must == number of user bytes (<= CHUNK_SIZE)
109 */
110 private void writeChunk () throws IOException {
111 char[] c = Integer.toHexString (count).toCharArray();
112 int clen = c.length;
113 int startByte = 4 - clen;
114 int i;
115 for (i=0; i<clen; i++) {
116 buf[startByte+i] = (byte)c[i];
117 }
118 buf[startByte + (i++)] = '\r';
119 buf[startByte + (i++)] = '\n';
120 buf[startByte + (i++) + count] = '\r';
121 buf[startByte + (i++) + count] = '\n';
122 out.write (buf, startByte, i+count);
123 count = 0;
124 pos = OFFSET;
125 }
126
127 public void close () throws IOException {
128 if (closed) {
129 return;
130 }
131 flush();
132 try {
133 /* write an empty chunk */
134 writeChunk();
135 out.flush();
136 LeftOverInputStream is = t.getOriginalInputStream();
137 if (!is.isClosed()) {
138 is.close();
139 }
140 /* some clients close the connection before empty chunk is sent */
141 } catch (IOException e) {
142
143 } finally {
144 closed = true;
145 }
146
147 WriteFinishedEvent e = new WriteFinishedEvent (t);
148 t.getHttpContext().getServerImpl().addEvent (e);
149 }
150
151 public void flush () throws IOException {
152 if (closed) {
153 throw new StreamClosedException ();
154 }
155 if (count > 0) {
156 writeChunk();
157 }
158 out.flush();
159 }
160}