1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import java.io.EOFException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.ReadableByteChannel;
26
27 import javax.servlet.RequestDispatcher;
28 import javax.servlet.ServletOutputStream;
29 import javax.servlet.ServletRequest;
30 import javax.servlet.ServletResponse;
31
32 import org.eclipse.jetty.http.HttpContent;
33 import org.eclipse.jetty.http.HttpHeader;
34 import org.eclipse.jetty.io.EofException;
35 import org.eclipse.jetty.util.BufferUtil;
36 import org.eclipse.jetty.util.log.Log;
37 import org.eclipse.jetty.util.log.Logger;
38 import org.eclipse.jetty.util.resource.Resource;
39
40
41
42
43
44
45
46
47
48
49
50 public class HttpOutput extends ServletOutputStream
51 {
52 private static Logger LOG = Log.getLogger(HttpOutput.class);
53 private final HttpChannel<?> _channel;
54 private boolean _closed;
55 private long _written;
56 private ByteBuffer _aggregate;
57 private int _bufferSize;
58
59 public HttpOutput(HttpChannel<?> channel)
60 {
61 _channel = channel;
62 _bufferSize = _channel.getHttpConfiguration().getOutputBufferSize();
63 }
64
65 public boolean isWritten()
66 {
67 return _written > 0;
68 }
69
70 public long getWritten()
71 {
72 return _written;
73 }
74
75 public void reset()
76 {
77 _written = 0;
78 _closed = false;
79 }
80
81 public void reopen()
82 {
83 _closed = false;
84 }
85
86 @Override
87 public void close()
88 {
89 if (!_closed)
90 {
91 try
92 {
93 if (BufferUtil.hasContent(_aggregate))
94 _channel.write(_aggregate, !_channel.getResponse().isIncluding());
95 else
96 _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
97 }
98 catch(IOException e)
99 {
100 _channel.getEndPoint().shutdownOutput();
101 LOG.ignore(e);
102 }
103 }
104 _closed = true;
105 if (_aggregate != null)
106 {
107 _channel.getConnector().getByteBufferPool().release(_aggregate);
108 _aggregate = null;
109 }
110 }
111
112 public boolean isClosed()
113 {
114 return _closed;
115 }
116
117 @Override
118 public void flush() throws IOException
119 {
120 if (_closed)
121 throw new EofException();
122
123 if (BufferUtil.hasContent(_aggregate))
124 _channel.write(_aggregate, false);
125 else
126 _channel.write(BufferUtil.EMPTY_BUFFER, false);
127 }
128
129 public boolean closeIfAllContentWritten() throws IOException
130 {
131 return _channel.getResponse().closeIfAllContentWritten(_written);
132 }
133
134 @Override
135 public void write(byte[] b, int off, int len) throws IOException
136 {
137 if (_closed)
138 throw new EOFException();
139
140
141 if (_aggregate == null)
142 {
143
144 int size = getBufferSize();
145
146
147 if (len > size / 2)
148 {
149 _channel.write(ByteBuffer.wrap(b, off, len), false);
150 _written += len;
151 return;
152 }
153
154
155 _aggregate = _channel.getByteBufferPool().acquire(size, false);
156 }
157
158
159 int space = BufferUtil.space(_aggregate);
160 if (len > space)
161 {
162
163 if (BufferUtil.hasContent(_aggregate))
164 {
165 _channel.write(_aggregate, false);
166 space = BufferUtil.space(_aggregate);
167 }
168 }
169
170
171 if (len > space)
172 {
173
174 _channel.write(ByteBuffer.wrap(b, off, len), false);
175 _written += len;
176 return;
177 }
178
179
180 BufferUtil.append(_aggregate, b, off, len);
181 _written += len;
182
183
184 if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
185 _channel.write(_aggregate, false);
186 }
187
188
189 @Override
190 public void write(int b) throws IOException
191 {
192 if (_closed)
193 throw new EOFException();
194
195 if (_aggregate == null)
196 _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
197
198 BufferUtil.append(_aggregate, (byte)b);
199 _written++;
200
201
202 if (!closeIfAllContentWritten() && BufferUtil.isFull(_aggregate))
203 _channel.write(_aggregate, false);
204 }
205
206 @Override
207 public void print(String s) throws IOException
208 {
209 if (isClosed())
210 throw new IOException("Closed");
211 write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
212 }
213
214 public void sendContent(Object content) throws IOException
215 {
216 if (isClosed())
217 throw new IOException("Closed");
218
219 if (content instanceof HttpContent)
220 {
221 HttpContent httpContent = (HttpContent)content;
222 Response response = _channel.getResponse();
223 String contentType = httpContent.getContentType();
224 if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
225 response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType);
226
227 if (httpContent.getContentLength() > 0)
228 response.getHttpFields().putLongField(HttpHeader.CONTENT_LENGTH, httpContent.getContentLength());
229
230 String lm = httpContent.getLastModified();
231 if (lm != null)
232 response.getHttpFields().put(HttpHeader.LAST_MODIFIED, lm);
233 else if (httpContent.getResource() != null)
234 {
235 long lml = httpContent.getResource().lastModified();
236 if (lml != -1)
237 response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
238 }
239
240 content = httpContent.getDirectBuffer();
241 if (content == null)
242 content = httpContent.getIndirectBuffer();
243 if (content == null)
244 content = httpContent.getReadableByteChannel();
245 if (content == null)
246 content = httpContent.getInputStream();
247 }
248 else if (content instanceof Resource)
249 {
250 Resource resource = (Resource)content;
251 _channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified());
252 content = resource.getInputStream();
253 }
254
255
256 if (content instanceof ByteBuffer)
257 {
258 _channel.write((ByteBuffer)content, true);
259 }
260 else if (content instanceof ReadableByteChannel)
261 {
262 ReadableByteChannel channel = (ReadableByteChannel)content;
263 ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), true);
264 try
265 {
266 while(channel.isOpen())
267 {
268 int pos = BufferUtil.flipToFill(buffer);
269 int len=channel.read(buffer);
270 if (len<0)
271 break;
272 BufferUtil.flipToFlush(buffer,pos);
273 _channel.write(buffer,false);
274 }
275 }
276 finally
277 {
278 close();
279 _channel.getByteBufferPool().release(buffer);
280 }
281 }
282 else if (content instanceof InputStream)
283 {
284 InputStream in = (InputStream)content;
285 ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
286 byte[] array = buffer.array();
287 int offset=buffer.arrayOffset();
288 int size=array.length-offset;
289 try
290 {
291 while(true)
292 {
293 int len=in.read(array,offset,size);
294 if (len<0)
295 break;
296 buffer.position(0);
297 buffer.limit(len);
298 _channel.write(buffer,false);
299 }
300 _channel.write(BufferUtil.EMPTY_BUFFER,true);
301 }
302 finally
303 {
304 close();
305 _channel.getByteBufferPool().release(buffer);
306 }
307 }
308 else
309 throw new IllegalArgumentException("unknown content type "+content.getClass());
310 }
311
312 public int getBufferSize()
313 {
314 return _bufferSize;
315 }
316
317 public void setBufferSize(int size)
318 {
319 this._bufferSize = size;
320 }
321
322 public void resetBuffer()
323 {
324 if (BufferUtil.hasContent(_aggregate))
325 BufferUtil.clear(_aggregate);
326 }
327 }