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