Mercurial Hosting > luan
comparison src/org/eclipse/jetty/http/HttpGenerator.java @ 877:fef4392f4905
remove sendServerVersion
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 04 Oct 2016 14:36:51 -0600 |
parents | 8e9db0bbf4f9 |
children | cb78ee27b0e0 |
comparison
equal
deleted
inserted
replaced
876:2efdb98f3543 | 877:fef4392f4905 |
---|---|
39 * | 39 * |
40 * | 40 * |
41 */ | 41 */ |
42 public class HttpGenerator extends AbstractGenerator | 42 public class HttpGenerator extends AbstractGenerator |
43 { | 43 { |
44 private static final Logger LOG = LoggerFactory.getLogger(HttpGenerator.class); | 44 private static final Logger LOG = LoggerFactory.getLogger(HttpGenerator.class); |
45 | 45 |
46 // Build cache of response lines for status | 46 // Build cache of response lines for status |
47 private static class Status | 47 private static class Status |
48 { | 48 { |
49 Buffer _reason; | 49 Buffer _reason; |
50 Buffer _schemeCode; | 50 Buffer _schemeCode; |
51 Buffer _responseLine; | 51 Buffer _responseLine; |
52 } | 52 } |
53 private static final Status[] __status = new Status[HttpStatus.MAX_CODE+1]; | 53 private static final Status[] __status = new Status[HttpStatus.MAX_CODE+1]; |
54 static | 54 static |
55 { | 55 { |
56 int versionLength=HttpVersions.HTTP_1_1_BUFFER.length(); | 56 int versionLength=HttpVersions.HTTP_1_1_BUFFER.length(); |
57 | 57 |
58 for (int i=0;i<__status.length;i++) | 58 for (int i=0;i<__status.length;i++) |
59 { | 59 { |
60 HttpStatus.Code code = HttpStatus.getCode(i); | 60 HttpStatus.Code code = HttpStatus.getCode(i); |
61 if (code==null) | 61 if (code==null) |
62 continue; | 62 continue; |
63 String reason=code.getMessage(); | 63 String reason=code.getMessage(); |
64 byte[] bytes=new byte[versionLength+5+reason.length()+2]; | 64 byte[] bytes=new byte[versionLength+5+reason.length()+2]; |
65 HttpVersions.HTTP_1_1_BUFFER.peek(0,bytes, 0, versionLength); | 65 HttpVersions.HTTP_1_1_BUFFER.peek(0,bytes, 0, versionLength); |
66 bytes[versionLength+0]=' '; | 66 bytes[versionLength+0]=' '; |
67 bytes[versionLength+1]=(byte)('0'+i/100); | 67 bytes[versionLength+1]=(byte)('0'+i/100); |
68 bytes[versionLength+2]=(byte)('0'+(i%100)/10); | 68 bytes[versionLength+2]=(byte)('0'+(i%100)/10); |
69 bytes[versionLength+3]=(byte)('0'+(i%10)); | 69 bytes[versionLength+3]=(byte)('0'+(i%10)); |
70 bytes[versionLength+4]=' '; | 70 bytes[versionLength+4]=' '; |
71 for (int j=0;j<reason.length();j++) | 71 for (int j=0;j<reason.length();j++) |
72 bytes[versionLength+5+j]=(byte)reason.charAt(j); | 72 bytes[versionLength+5+j]=(byte)reason.charAt(j); |
73 bytes[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN; | 73 bytes[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN; |
74 bytes[versionLength+6+reason.length()]=HttpTokens.LINE_FEED; | 74 bytes[versionLength+6+reason.length()]=HttpTokens.LINE_FEED; |
75 | 75 |
76 __status[i] = new Status(); | 76 __status[i] = new Status(); |
77 __status[i]._reason=new ByteArrayBuffer(bytes,versionLength+5,bytes.length-versionLength-7,Buffer.IMMUTABLE); | 77 __status[i]._reason=new ByteArrayBuffer(bytes,versionLength+5,bytes.length-versionLength-7,Buffer.IMMUTABLE); |
78 __status[i]._schemeCode=new ByteArrayBuffer(bytes,0,versionLength+5,Buffer.IMMUTABLE); | 78 __status[i]._schemeCode=new ByteArrayBuffer(bytes,0,versionLength+5,Buffer.IMMUTABLE); |
79 __status[i]._responseLine=new ByteArrayBuffer(bytes,0,bytes.length,Buffer.IMMUTABLE); | 79 __status[i]._responseLine=new ByteArrayBuffer(bytes,0,bytes.length,Buffer.IMMUTABLE); |
80 } | 80 } |
81 } | 81 } |
82 | 82 |
83 /* ------------------------------------------------------------------------------- */ | 83 /* ------------------------------------------------------------------------------- */ |
84 public static Buffer getReasonBuffer(int code) | 84 public static Buffer getReasonBuffer(int code) |
85 { | 85 { |
86 Status status = code<__status.length?__status[code]:null; | 86 Status status = code<__status.length?__status[code]:null; |
87 if (status!=null) | 87 if (status!=null) |
88 return status._reason; | 88 return status._reason; |
89 return null; | 89 return null; |
90 } | 90 } |
91 | 91 |
92 | 92 |
93 // common _content | 93 // common _content |
94 private static final byte[] LAST_CHUNK = | 94 private static final byte[] LAST_CHUNK = |
95 { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'}; | 95 { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'}; |
96 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012"); | 96 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012"); |
97 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012"); | 97 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012"); |
98 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012"); | 98 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012"); |
99 private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: "); | 99 private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: "); |
100 private static final byte[] CRLF = StringUtil.getBytes("\015\012"); | 100 private static final byte[] CRLF = StringUtil.getBytes("\015\012"); |
101 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012"); | 101 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012"); |
102 private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012"); | 102 private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012"); |
103 | 103 |
104 // other statics | 104 // other statics |
105 private static final int CHUNK_SPACE = 12; | 105 private static final int CHUNK_SPACE = 12; |
106 | 106 |
107 public static void setServerVersion(String version) | 107 public static void setServerVersion(String version) |
108 { | 108 { |
109 SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012"); | 109 SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012"); |
110 } | 110 } |
111 | 111 |
112 // data | 112 // data |
113 protected boolean _bypass = false; // True if _content buffer can be written directly to endp and bypass the content buffer | 113 protected boolean _bypass = false; // True if _content buffer can be written directly to endp and bypass the content buffer |
114 private boolean _needCRLF = false; | 114 private boolean _needCRLF = false; |
115 private boolean _needEOC = false; | 115 private boolean _needEOC = false; |
116 private boolean _bufferChunked = false; | 116 private boolean _bufferChunked = false; |
117 | 117 |
118 | 118 |
119 /* ------------------------------------------------------------------------------- */ | 119 /* ------------------------------------------------------------------------------- */ |
120 /** | 120 /** |
121 * Constructor. | 121 * Constructor. |
122 * | 122 * |
123 * @param buffers buffer pool | 123 * @param buffers buffer pool |
124 * @param io the end point to use | 124 * @param io the end point to use |
125 */ | 125 */ |
126 public HttpGenerator(Buffers buffers, EndPoint io) | 126 public HttpGenerator(Buffers buffers, EndPoint io) |
127 { | 127 { |
128 super(buffers,io); | 128 super(buffers,io); |
129 } | 129 } |
130 | 130 |
131 /* ------------------------------------------------------------------------------- */ | 131 /* ------------------------------------------------------------------------------- */ |
132 @Override | 132 @Override |
133 public void reset() | 133 public void reset() |
134 { | 134 { |
135 if (_persistent!=null && !_persistent && _endp!=null && !_endp.isOutputShutdown()) | 135 if (_persistent!=null && !_persistent && _endp!=null && !_endp.isOutputShutdown()) |
136 { | 136 { |
137 try | 137 try |
138 { | 138 { |
139 _endp.shutdownOutput(); | 139 _endp.shutdownOutput(); |
140 } | 140 } |
141 catch(IOException e) | 141 catch(IOException e) |
142 { | 142 { |
143 LOG.trace("",e); | 143 LOG.trace("",e); |
144 } | 144 } |
145 } | 145 } |
146 super.reset(); | 146 super.reset(); |
147 if (_buffer!=null) | 147 if (_buffer!=null) |
148 _buffer.clear(); | 148 _buffer.clear(); |
149 if (_header!=null) | 149 if (_header!=null) |
150 _header.clear(); | 150 _header.clear(); |
151 if (_content!=null) | 151 if (_content!=null) |
152 _content=null; | 152 _content=null; |
153 _bypass = false; | 153 _bypass = false; |
154 _needCRLF = false; | 154 _needCRLF = false; |
155 _needEOC = false; | 155 _needEOC = false; |
156 _bufferChunked=false; | 156 _bufferChunked=false; |
157 _method=null; | 157 _method=null; |
158 _uri=null; | 158 _uri=null; |
159 _noContent=false; | 159 _noContent=false; |
160 } | 160 } |
161 | 161 |
162 /* ------------------------------------------------------------ */ | 162 /* ------------------------------------------------------------ */ |
163 /** | 163 /** |
164 * Add content. | 164 * Add content. |
165 * | 165 * |
166 * @param content | 166 * @param content |
167 * @param last | 167 * @param last |
168 * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}. | 168 * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}. |
169 * @throws IllegalStateException If the request is not expecting any more content, | 169 * @throws IllegalStateException If the request is not expecting any more content, |
170 * or if the buffers are full and cannot be flushed. | 170 * or if the buffers are full and cannot be flushed. |
171 * @throws IOException if there is a problem flushing the buffers. | 171 * @throws IOException if there is a problem flushing the buffers. |
172 */ | 172 */ |
173 public void addContent(Buffer content, boolean last) throws IOException | 173 public void addContent(Buffer content, boolean last) throws IOException |
174 { | 174 { |
175 if (_noContent) | 175 if (_noContent) |
176 throw new IllegalStateException("NO CONTENT"); | 176 throw new IllegalStateException("NO CONTENT"); |
177 | 177 |
178 if (_last || _state==STATE_END) | 178 if (_last || _state==STATE_END) |
179 { | 179 { |
180 LOG.warn("Ignoring extra content {}",content); | 180 LOG.warn("Ignoring extra content {}",content); |
181 content.clear(); | 181 content.clear(); |
182 return; | 182 return; |
183 } | 183 } |
184 _last = last; | 184 _last = last; |
185 | 185 |
186 // Handle any unfinished business? | 186 // Handle any unfinished business? |
187 if (_content!=null && _content.length()>0 || _bufferChunked) | 187 if (_content!=null && _content.length()>0 || _bufferChunked) |
188 { | 188 { |
189 if (_endp.isOutputShutdown()) | 189 if (_endp.isOutputShutdown()) |
190 throw new EofException(); | 190 throw new EofException(); |
191 flushBuffer(); | 191 flushBuffer(); |
192 if (_content != null && _content.length()>0) | 192 if (_content != null && _content.length()>0) |
193 { | 193 { |
194 if (_bufferChunked) | 194 if (_bufferChunked) |
195 { | 195 { |
196 Buffer nc=_buffers.getBuffer(_content.length()+CHUNK_SPACE+content.length()); | 196 Buffer nc=_buffers.getBuffer(_content.length()+CHUNK_SPACE+content.length()); |
197 nc.put(_content); | 197 nc.put(_content); |
198 nc.put(HttpTokens.CRLF); | 198 nc.put(HttpTokens.CRLF); |
199 BufferUtil.putHexInt(nc, content.length()); | 199 BufferUtil.putHexInt(nc, content.length()); |
200 nc.put(HttpTokens.CRLF); | 200 nc.put(HttpTokens.CRLF); |
201 nc.put(content); | 201 nc.put(content); |
202 content=nc; | 202 content=nc; |
203 } | 203 } |
204 else | 204 else |
205 { | 205 { |
206 Buffer nc=_buffers.getBuffer(_content.length()+content.length()); | 206 Buffer nc=_buffers.getBuffer(_content.length()+content.length()); |
207 nc.put(_content); | 207 nc.put(_content); |
208 nc.put(content); | 208 nc.put(content); |
209 content=nc; | 209 content=nc; |
210 } | 210 } |
211 } | 211 } |
212 } | 212 } |
213 | 213 |
214 _content = content; | 214 _content = content; |
215 _contentWritten += content.length(); | 215 _contentWritten += content.length(); |
216 | 216 |
217 // Handle the _content | 217 // Handle the _content |
218 if (_head) | 218 if (_head) |
219 { | 219 { |
220 content.clear(); | 220 content.clear(); |
221 _content=null; | 221 _content=null; |
222 } | 222 } |
223 else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024)) | 223 else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024)) |
224 { | 224 { |
225 _bypass = true; | 225 _bypass = true; |
226 } | 226 } |
227 else if (!_bufferChunked) | 227 else if (!_bufferChunked) |
228 { | 228 { |
229 // Yes - so we better check we have a buffer | 229 // Yes - so we better check we have a buffer |
230 if (_buffer == null) | 230 if (_buffer == null) |
231 _buffer = _buffers.getBuffer(); | 231 _buffer = _buffers.getBuffer(); |
232 | 232 |
233 // Copy _content to buffer; | 233 // Copy _content to buffer; |
234 int len=_buffer.put(_content); | 234 int len=_buffer.put(_content); |
235 _content.skip(len); | 235 _content.skip(len); |
236 if (_content.length() == 0) | 236 if (_content.length() == 0) |
237 _content = null; | 237 _content = null; |
238 } | 238 } |
239 } | 239 } |
240 | 240 |
241 /* ------------------------------------------------------------ */ | 241 /* ------------------------------------------------------------ */ |
242 /** | 242 /** |
243 * send complete response. | 243 * send complete response. |
244 * | 244 * |
245 * @param response | 245 * @param response |
246 */ | 246 */ |
247 public void sendResponse(Buffer response) throws IOException | 247 public void sendResponse(Buffer response) throws IOException |
248 { | 248 { |
249 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head ) | 249 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head ) |
250 throw new IllegalStateException(); | 250 throw new IllegalStateException(); |
251 | 251 |
252 _last = true; | 252 _last = true; |
253 | 253 |
254 _content = response; | 254 _content = response; |
255 _bypass = true; | 255 _bypass = true; |
256 _state = STATE_FLUSHING; | 256 _state = STATE_FLUSHING; |
257 | 257 |
258 // TODO this is not exactly right, but should do. | 258 // TODO this is not exactly right, but should do. |
259 _contentLength =_contentWritten = response.length(); | 259 _contentLength =_contentWritten = response.length(); |
260 | 260 |
261 } | 261 } |
262 | 262 |
263 /* ------------------------------------------------------------ */ | 263 /* ------------------------------------------------------------ */ |
264 /** Prepare buffer for unchecked writes. | 264 /** Prepare buffer for unchecked writes. |
265 * Prepare the generator buffer to receive unchecked writes | 265 * Prepare the generator buffer to receive unchecked writes |
266 * @return the available space in the buffer. | 266 * @return the available space in the buffer. |
267 * @throws IOException | 267 * @throws IOException |
268 */ | 268 */ |
269 @Override | 269 @Override |
270 public int prepareUncheckedAddContent() throws IOException | 270 public int prepareUncheckedAddContent() throws IOException |
271 { | 271 { |
272 if (_noContent) | 272 if (_noContent) |
273 return -1; | 273 return -1; |
274 | 274 |
275 if (_last || _state==STATE_END) | 275 if (_last || _state==STATE_END) |
276 return -1; | 276 return -1; |
277 | 277 |
278 // Handle any unfinished business? | 278 // Handle any unfinished business? |
279 Buffer content = _content; | 279 Buffer content = _content; |
280 if (content != null && content.length()>0 || _bufferChunked) | 280 if (content != null && content.length()>0 || _bufferChunked) |
281 { | 281 { |
282 flushBuffer(); | 282 flushBuffer(); |
283 if (content != null && content.length()>0 || _bufferChunked) | 283 if (content != null && content.length()>0 || _bufferChunked) |
284 throw new IllegalStateException("FULL"); | 284 throw new IllegalStateException("FULL"); |
285 } | 285 } |
286 | 286 |
287 // we better check we have a buffer | 287 // we better check we have a buffer |
288 if (_buffer == null) | 288 if (_buffer == null) |
289 _buffer = _buffers.getBuffer(); | 289 _buffer = _buffers.getBuffer(); |
290 | 290 |
291 _contentWritten-=_buffer.length(); | 291 _contentWritten-=_buffer.length(); |
292 | 292 |
293 // Handle the _content | 293 // Handle the _content |
294 if (_head) | 294 if (_head) |
295 return Integer.MAX_VALUE; | 295 return Integer.MAX_VALUE; |
296 | 296 |
297 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0); | 297 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0); |
298 } | 298 } |
299 | 299 |
300 /* ------------------------------------------------------------ */ | 300 /* ------------------------------------------------------------ */ |
301 @Override | 301 @Override |
302 public boolean isBufferFull() | 302 public boolean isBufferFull() |
303 { | 303 { |
304 // Should we flush the buffers? | 304 // Should we flush the buffers? |
305 return super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE); | 305 return super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE); |
306 } | 306 } |
307 | 307 |
308 /* ------------------------------------------------------------ */ | 308 /* ------------------------------------------------------------ */ |
309 public void send1xx(int code) throws IOException | 309 public void send1xx(int code) throws IOException |
310 { | 310 { |
311 if (_state != STATE_HEADER) | 311 if (_state != STATE_HEADER) |
312 return; | 312 return; |
313 | 313 |
314 if (code<100||code>199) | 314 if (code<100||code>199) |
315 throw new IllegalArgumentException("!1xx"); | 315 throw new IllegalArgumentException("!1xx"); |
316 Status status=__status[code]; | 316 Status status=__status[code]; |
317 if (status==null) | 317 if (status==null) |
318 throw new IllegalArgumentException(code+"?"); | 318 throw new IllegalArgumentException(code+"?"); |
319 | 319 |
320 // get a header buffer | 320 // get a header buffer |
321 if (_header == null) | 321 if (_header == null) |
322 _header = _buffers.getHeader(); | 322 _header = _buffers.getHeader(); |
323 | 323 |
324 _header.put(status._responseLine); | 324 _header.put(status._responseLine); |
325 _header.put(HttpTokens.CRLF); | 325 _header.put(HttpTokens.CRLF); |
326 | 326 |
327 try | 327 try |
328 { | 328 { |
329 // nasty semi busy flush! | 329 // nasty semi busy flush! |
330 while(_header.length()>0) | 330 while(_header.length()>0) |
331 { | 331 { |
332 int len = _endp.flush(_header); | 332 int len = _endp.flush(_header); |
333 if (len<0) | 333 if (len<0) |
334 throw new EofException(); | 334 throw new EofException(); |
335 if (len==0) | 335 if (len==0) |
336 Thread.sleep(100); | 336 Thread.sleep(100); |
337 } | 337 } |
338 } | 338 } |
339 catch(InterruptedException e) | 339 catch(InterruptedException e) |
340 { | 340 { |
341 LOG.debug("",e); | 341 LOG.debug("",e); |
342 throw new InterruptedIOException(e.toString()); | 342 throw new InterruptedIOException(e.toString()); |
343 } | 343 } |
344 } | 344 } |
345 | 345 |
346 /* ------------------------------------------------------------ */ | 346 /* ------------------------------------------------------------ */ |
347 @Override | 347 @Override |
348 public boolean isRequest() | 348 public boolean isRequest() |
349 { | 349 { |
350 return _method!=null; | 350 return _method!=null; |
351 } | 351 } |
352 | 352 |
353 /* ------------------------------------------------------------ */ | 353 /* ------------------------------------------------------------ */ |
354 @Override | 354 @Override |
355 public boolean isResponse() | 355 public boolean isResponse() |
356 { | 356 { |
357 return _method==null; | 357 return _method==null; |
358 } | 358 } |
359 | 359 |
360 /* ------------------------------------------------------------ */ | 360 /* ------------------------------------------------------------ */ |
361 @Override | 361 @Override |
362 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException | 362 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException |
363 { | 363 { |
364 if (_state != STATE_HEADER) | 364 if (_state != STATE_HEADER) |
365 return; | 365 return; |
366 | 366 |
367 // handle a reset | 367 // handle a reset |
368 if (isResponse() && _status==0) | 368 if (isResponse() && _status==0) |
369 throw new EofException(); | 369 throw new EofException(); |
370 | 370 |
371 if (_last && !allContentAdded) | 371 if (_last && !allContentAdded) |
372 throw new IllegalStateException("last?"); | 372 throw new IllegalStateException("last?"); |
373 _last = _last | allContentAdded; | 373 _last = _last | allContentAdded; |
374 | 374 |
375 // get a header buffer | 375 // get a header buffer |
376 if (_header == null) | 376 if (_header == null) |
377 _header = _buffers.getHeader(); | 377 _header = _buffers.getHeader(); |
378 | 378 |
379 boolean has_server = false; | 379 boolean has_server = false; |
380 | 380 |
381 try | 381 try |
382 { | 382 { |
383 if (isRequest()) | 383 if (isRequest()) |
384 { | 384 { |
385 _persistent=true; | 385 _persistent=true; |
386 | 386 |
387 if (_version == HttpVersions.HTTP_0_9_ORDINAL) | 387 if (_version == HttpVersions.HTTP_0_9_ORDINAL) |
388 { | 388 { |
389 _contentLength = HttpTokens.NO_CONTENT; | 389 _contentLength = HttpTokens.NO_CONTENT; |
390 _header.put(_method); | 390 _header.put(_method); |
391 _header.put((byte)' '); | 391 _header.put((byte)' '); |
392 _header.put(_uri.getBytes("UTF-8")); // TODO check | 392 _header.put(_uri.getBytes("UTF-8")); // TODO check |
393 _header.put(HttpTokens.CRLF); | 393 _header.put(HttpTokens.CRLF); |
394 _state = STATE_FLUSHING; | 394 _state = STATE_FLUSHING; |
395 _noContent=true; | 395 _noContent=true; |
396 return; | 396 return; |
397 } | 397 } |
398 else | 398 else |
399 { | 399 { |
400 _header.put(_method); | 400 _header.put(_method); |
401 _header.put((byte)' '); | 401 _header.put((byte)' '); |
402 _header.put(_uri.getBytes("UTF-8")); // TODO check | 402 _header.put(_uri.getBytes("UTF-8")); // TODO check |
403 _header.put((byte)' '); | 403 _header.put((byte)' '); |
404 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER); | 404 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER); |
405 _header.put(HttpTokens.CRLF); | 405 _header.put(HttpTokens.CRLF); |
406 } | 406 } |
407 } | 407 } |
408 else | 408 else |
409 { | 409 { |
410 // Responses | 410 // Responses |
411 if (_version == HttpVersions.HTTP_0_9_ORDINAL) | 411 if (_version == HttpVersions.HTTP_0_9_ORDINAL) |
412 { | 412 { |
413 _persistent = false; | 413 _persistent = false; |
414 _contentLength = HttpTokens.EOF_CONTENT; | 414 _contentLength = HttpTokens.EOF_CONTENT; |
415 _state = STATE_CONTENT; | 415 _state = STATE_CONTENT; |
416 return; | 416 return; |
417 } | 417 } |
418 else | 418 else |
419 { | 419 { |
420 if (_persistent==null) | 420 if (_persistent==null) |
421 _persistent= (_version > HttpVersions.HTTP_1_0_ORDINAL); | 421 _persistent= (_version > HttpVersions.HTTP_1_0_ORDINAL); |
422 | 422 |
423 // add response line | 423 // add response line |
424 Status status = _status<__status.length?__status[_status]:null; | 424 Status status = _status<__status.length?__status[_status]:null; |
425 | 425 |
426 if (status==null) | 426 if (status==null) |
427 { | 427 { |
428 _header.put(HttpVersions.HTTP_1_1_BUFFER); | 428 _header.put(HttpVersions.HTTP_1_1_BUFFER); |
429 _header.put((byte) ' '); | 429 _header.put((byte) ' '); |
430 _header.put((byte) ('0' + _status / 100)); | 430 _header.put((byte) ('0' + _status / 100)); |
431 _header.put((byte) ('0' + (_status % 100) / 10)); | 431 _header.put((byte) ('0' + (_status % 100) / 10)); |
432 _header.put((byte) ('0' + (_status % 10))); | 432 _header.put((byte) ('0' + (_status % 10))); |
433 _header.put((byte) ' '); | 433 _header.put((byte) ' '); |
434 if (_reason==null) | 434 if (_reason==null) |
435 { | 435 { |
436 _header.put((byte) ('0' + _status / 100)); | 436 _header.put((byte) ('0' + _status / 100)); |
437 _header.put((byte) ('0' + (_status % 100) / 10)); | 437 _header.put((byte) ('0' + (_status % 100) / 10)); |
438 _header.put((byte) ('0' + (_status % 10))); | 438 _header.put((byte) ('0' + (_status % 10))); |
439 } | 439 } |
440 else | 440 else |
441 _header.put(_reason); | 441 _header.put(_reason); |
442 _header.put(HttpTokens.CRLF); | 442 _header.put(HttpTokens.CRLF); |
443 } | 443 } |
444 else | 444 else |
445 { | 445 { |
446 if (_reason==null) | 446 if (_reason==null) |
447 _header.put(status._responseLine); | 447 _header.put(status._responseLine); |
448 else | 448 else |
449 { | 449 { |
450 _header.put(status._schemeCode); | 450 _header.put(status._schemeCode); |
451 _header.put(_reason); | 451 _header.put(_reason); |
452 _header.put(HttpTokens.CRLF); | 452 _header.put(HttpTokens.CRLF); |
453 } | 453 } |
454 } | 454 } |
455 | 455 |
456 if (_status<200 && _status>=100 ) | 456 if (_status<200 && _status>=100 ) |
457 { | 457 { |
458 _noContent=true; | 458 _noContent=true; |
459 _content=null; | 459 _content=null; |
460 if (_buffer!=null) | 460 if (_buffer!=null) |
461 _buffer.clear(); | 461 _buffer.clear(); |
462 // end the header. | 462 // end the header. |
463 | 463 |
464 if (_status!=101 ) | 464 if (_status!=101 ) |
465 { | 465 { |
466 _header.put(HttpTokens.CRLF); | 466 _header.put(HttpTokens.CRLF); |
467 _state = STATE_CONTENT; | 467 _state = STATE_CONTENT; |
468 return; | 468 return; |
469 } | 469 } |
470 } | 470 } |
471 else if (_status==204 || _status==304) | 471 else if (_status==204 || _status==304) |
472 { | 472 { |
473 _noContent=true; | 473 _noContent=true; |
474 _content=null; | 474 _content=null; |
475 if (_buffer!=null) | 475 if (_buffer!=null) |
476 _buffer.clear(); | 476 _buffer.clear(); |
477 } | 477 } |
478 } | 478 } |
479 } | 479 } |
480 | 480 |
481 // Add headers | 481 // Add headers |
482 if (_status>=200 && _date!=null) | 482 if (_status>=200 && _date!=null) |
483 { | 483 { |
484 _header.put(HttpHeaders.DATE_BUFFER); | 484 _header.put(HttpHeaders.DATE_BUFFER); |
485 _header.put((byte)':'); | 485 _header.put((byte)':'); |
486 _header.put((byte)' '); | 486 _header.put((byte)' '); |
487 _header.put(_date); | 487 _header.put(_date); |
488 _header.put(CRLF); | 488 _header.put(CRLF); |
489 } | 489 } |
490 | 490 |
491 // key field values | 491 // key field values |
492 HttpFields.Field content_length = null; | 492 HttpFields.Field content_length = null; |
493 HttpFields.Field transfer_encoding = null; | 493 HttpFields.Field transfer_encoding = null; |
494 boolean keep_alive = false; | 494 boolean keep_alive = false; |
495 boolean close=false; | 495 boolean close=false; |
496 boolean content_type=false; | 496 boolean content_type=false; |
497 StringBuilder connection = null; | 497 StringBuilder connection = null; |
498 | 498 |
499 if (fields != null) | 499 if (fields != null) |
500 { | 500 { |
501 int s=fields.size(); | 501 int s=fields.size(); |
502 for (int f=0;f<s;f++) | 502 for (int f=0;f<s;f++) |
503 { | 503 { |
504 HttpFields.Field field = fields.getField(f); | 504 HttpFields.Field field = fields.getField(f); |
505 if (field==null) | 505 if (field==null) |
506 continue; | 506 continue; |
507 | 507 |
508 switch (field.getNameOrdinal()) | 508 switch (field.getNameOrdinal()) |
509 { | 509 { |
510 case HttpHeaders.CONTENT_LENGTH_ORDINAL: | 510 case HttpHeaders.CONTENT_LENGTH_ORDINAL: |
511 content_length = field; | 511 content_length = field; |
512 _contentLength = field.getLongValue(); | 512 _contentLength = field.getLongValue(); |
513 | 513 |
514 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten) | 514 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten) |
515 content_length = null; | 515 content_length = null; |
516 | 516 |
517 // write the field to the header buffer | 517 // write the field to the header buffer |
518 field.putTo(_header); | 518 field.putTo(_header); |
519 break; | 519 break; |
520 | 520 |
521 case HttpHeaders.CONTENT_TYPE_ORDINAL: | 521 case HttpHeaders.CONTENT_TYPE_ORDINAL: |
522 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT; | 522 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT; |
523 | 523 |
524 // write the field to the header buffer | 524 // write the field to the header buffer |
525 content_type=true; | 525 content_type=true; |
526 field.putTo(_header); | 526 field.putTo(_header); |
527 break; | 527 break; |
528 | 528 |
529 case HttpHeaders.TRANSFER_ENCODING_ORDINAL: | 529 case HttpHeaders.TRANSFER_ENCODING_ORDINAL: |
530 if (_version == HttpVersions.HTTP_1_1_ORDINAL) | 530 if (_version == HttpVersions.HTTP_1_1_ORDINAL) |
531 transfer_encoding = field; | 531 transfer_encoding = field; |
532 // Do NOT add yet! | 532 // Do NOT add yet! |
533 break; | 533 break; |
534 | 534 |
535 case HttpHeaders.CONNECTION_ORDINAL: | 535 case HttpHeaders.CONNECTION_ORDINAL: |
536 if (isRequest()) | 536 if (isRequest()) |
537 field.putTo(_header); | 537 field.putTo(_header); |
538 | 538 |
539 int connection_value = field.getValueOrdinal(); | 539 int connection_value = field.getValueOrdinal(); |
540 switch (connection_value) | 540 switch (connection_value) |
541 { | 541 { |
542 case -1: | 542 case -1: |
543 { | 543 { |
544 String[] values = field.getValue().split(","); | 544 String[] values = field.getValue().split(","); |
545 for (int i=0;values!=null && i<values.length;i++) | 545 for (int i=0;values!=null && i<values.length;i++) |
546 { | 546 { |
547 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim()); | 547 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim()); |
548 | 548 |
549 if (cb!=null) | 549 if (cb!=null) |
550 { | 550 { |
551 switch(cb.getOrdinal()) | 551 switch(cb.getOrdinal()) |
552 { | 552 { |
553 case HttpHeaderValues.CLOSE_ORDINAL: | 553 case HttpHeaderValues.CLOSE_ORDINAL: |
554 close=true; | 554 close=true; |
555 if (isResponse()) | 555 if (isResponse()) |
556 _persistent=false; | 556 _persistent=false; |
557 keep_alive=false; | 557 keep_alive=false; |
558 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT) | 558 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT) |
559 _contentLength = HttpTokens.EOF_CONTENT; | 559 _contentLength = HttpTokens.EOF_CONTENT; |
560 break; | 560 break; |
561 | 561 |
562 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: | 562 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: |
563 if (_version == HttpVersions.HTTP_1_0_ORDINAL) | 563 if (_version == HttpVersions.HTTP_1_0_ORDINAL) |
564 { | 564 { |
565 keep_alive = true; | 565 keep_alive = true; |
566 if (isResponse()) | 566 if (isResponse()) |
567 _persistent = true; | 567 _persistent = true; |
568 } | 568 } |
569 break; | 569 break; |
570 | 570 |
571 default: | 571 default: |
572 if (connection==null) | 572 if (connection==null) |
573 connection=new StringBuilder(); | 573 connection=new StringBuilder(); |
574 else | 574 else |
575 connection.append(','); | 575 connection.append(','); |
576 connection.append(values[i]); | 576 connection.append(values[i]); |
577 } | 577 } |
578 } | 578 } |
579 else | 579 else |
580 { | 580 { |
581 if (connection==null) | 581 if (connection==null) |
582 connection=new StringBuilder(); | 582 connection=new StringBuilder(); |
583 else | 583 else |
584 connection.append(','); | 584 connection.append(','); |
585 connection.append(values[i]); | 585 connection.append(values[i]); |
586 } | 586 } |
587 } | 587 } |
588 | 588 |
589 break; | 589 break; |
590 } | 590 } |
591 case HttpHeaderValues.UPGRADE_ORDINAL: | 591 case HttpHeaderValues.UPGRADE_ORDINAL: |
592 { | 592 { |
593 // special case for websocket connection ordering | 593 // special case for websocket connection ordering |
594 if (isResponse()) | 594 if (isResponse()) |
595 { | 595 { |
596 field.putTo(_header); | 596 field.putTo(_header); |
597 continue; | 597 continue; |
598 } | 598 } |
599 } | 599 } |
600 case HttpHeaderValues.CLOSE_ORDINAL: | 600 case HttpHeaderValues.CLOSE_ORDINAL: |
601 { | 601 { |
602 close=true; | 602 close=true; |
603 if (isResponse()) | 603 if (isResponse()) |
604 _persistent=false; | 604 _persistent=false; |
605 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT) | 605 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT) |
606 _contentLength = HttpTokens.EOF_CONTENT; | 606 _contentLength = HttpTokens.EOF_CONTENT; |
607 break; | 607 break; |
608 } | 608 } |
609 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: | 609 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: |
610 { | 610 { |
611 if (_version == HttpVersions.HTTP_1_0_ORDINAL) | 611 if (_version == HttpVersions.HTTP_1_0_ORDINAL) |
612 { | 612 { |
613 keep_alive = true; | 613 keep_alive = true; |
614 if (isResponse()) | 614 if (isResponse()) |
615 _persistent=true; | 615 _persistent=true; |
616 } | 616 } |
617 break; | 617 break; |
618 } | 618 } |
619 default: | 619 default: |
620 { | 620 { |
621 if (connection==null) | 621 if (connection==null) |
622 connection=new StringBuilder(); | 622 connection=new StringBuilder(); |
623 else | 623 else |
624 connection.append(','); | 624 connection.append(','); |
625 connection.append(field.getValue()); | 625 connection.append(field.getValue()); |
626 } | 626 } |
627 } | 627 } |
628 | 628 |
629 // Do NOT add yet! | 629 // Do NOT add yet! |
630 break; | 630 break; |
631 | 631 |
632 case HttpHeaders.SERVER_ORDINAL: | 632 case HttpHeaders.SERVER_ORDINAL: |
633 if (getSendServerVersion()) | 633 has_server=true; |
634 { | 634 field.putTo(_header); |
635 has_server=true; | 635 break; |
636 field.putTo(_header); | 636 |
637 } | 637 default: |
638 break; | 638 // write the field to the header buffer |
639 | 639 field.putTo(_header); |
640 default: | 640 } |
641 // write the field to the header buffer | 641 } |
642 field.putTo(_header); | 642 } |
643 } | 643 |
644 } | 644 // Calculate how to end _content and connection, _content length and transfer encoding |
645 } | 645 // settings. |
646 | 646 // From RFC 2616 4.4: |
647 // Calculate how to end _content and connection, _content length and transfer encoding | 647 // 1. No body for 1xx, 204, 304 & HEAD response |
648 // settings. | 648 // 2. Force _content-length? |
649 // From RFC 2616 4.4: | 649 // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk |
650 // 1. No body for 1xx, 204, 304 & HEAD response | 650 // 4. Content-Length |
651 // 2. Force _content-length? | 651 // 5. multipart/byteranges |
652 // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk | 652 // 6. close |
653 // 4. Content-Length | 653 switch ((int) _contentLength) |
654 // 5. multipart/byteranges | 654 { |
655 // 6. close | 655 case HttpTokens.UNKNOWN_CONTENT: |
656 switch ((int) _contentLength) | 656 // It may be that we have no _content, or perhaps _content just has not been |
657 { | 657 // written yet? |
658 case HttpTokens.UNKNOWN_CONTENT: | 658 |
659 // It may be that we have no _content, or perhaps _content just has not been | 659 // Response known not to have a body |
660 // written yet? | 660 if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304)) |
661 | 661 _contentLength = HttpTokens.NO_CONTENT; |
662 // Response known not to have a body | 662 else if (_last) |
663 if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304)) | 663 { |
664 _contentLength = HttpTokens.NO_CONTENT; | 664 // we have seen all the _content there is |
665 else if (_last) | 665 _contentLength = _contentWritten; |
666 { | 666 if (content_length == null && (isResponse() || _contentLength>0 || content_type ) && !_noContent) |
667 // we have seen all the _content there is | 667 { |
668 _contentLength = _contentWritten; | 668 // known length but not actually set. |
669 if (content_length == null && (isResponse() || _contentLength>0 || content_type ) && !_noContent) | 669 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER); |
670 { | 670 _header.put(HttpTokens.COLON); |
671 // known length but not actually set. | 671 _header.put((byte) ' '); |
672 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER); | 672 BufferUtil.putDecLong(_header, _contentLength); |
673 _header.put(HttpTokens.COLON); | 673 _header.put(HttpTokens.CRLF); |
674 _header.put((byte) ' '); | 674 } |
675 BufferUtil.putDecLong(_header, _contentLength); | 675 } |
676 _header.put(HttpTokens.CRLF); | 676 else |
677 } | 677 { |
678 } | 678 // No idea, so we must assume that a body is coming |
679 else | 679 _contentLength = (!_persistent || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT; |
680 { | 680 if (isRequest() && _contentLength==HttpTokens.EOF_CONTENT) |
681 // No idea, so we must assume that a body is coming | 681 { |
682 _contentLength = (!_persistent || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT; | 682 _contentLength=HttpTokens.NO_CONTENT; |
683 if (isRequest() && _contentLength==HttpTokens.EOF_CONTENT) | 683 _noContent=true; |
684 { | 684 } |
685 _contentLength=HttpTokens.NO_CONTENT; | 685 } |
686 _noContent=true; | 686 break; |
687 } | 687 |
688 } | 688 case HttpTokens.NO_CONTENT: |
689 break; | 689 if (content_length == null && isResponse() && _status >= 200 && _status != 204 && _status != 304) |
690 | 690 _header.put(CONTENT_LENGTH_0); |
691 case HttpTokens.NO_CONTENT: | 691 break; |
692 if (content_length == null && isResponse() && _status >= 200 && _status != 204 && _status != 304) | 692 |
693 _header.put(CONTENT_LENGTH_0); | 693 case HttpTokens.EOF_CONTENT: |
694 break; | 694 _persistent = isRequest(); |
695 | 695 break; |
696 case HttpTokens.EOF_CONTENT: | 696 |
697 _persistent = isRequest(); | 697 case HttpTokens.CHUNKED_CONTENT: |
698 break; | 698 break; |
699 | 699 |
700 case HttpTokens.CHUNKED_CONTENT: | 700 default: |
701 break; | 701 // TODO - maybe allow forced chunking by setting te ??? |
702 | 702 break; |
703 default: | 703 } |
704 // TODO - maybe allow forced chunking by setting te ??? | 704 |
705 break; | 705 // Add transfer_encoding if needed |
706 } | 706 if (_contentLength == HttpTokens.CHUNKED_CONTENT) |
707 | 707 { |
708 // Add transfer_encoding if needed | 708 // try to use user supplied encoding as it may have other values. |
709 if (_contentLength == HttpTokens.CHUNKED_CONTENT) | 709 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal()) |
710 { | 710 { |
711 // try to use user supplied encoding as it may have other values. | 711 String c = transfer_encoding.getValue(); |
712 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal()) | 712 if (c.endsWith(HttpHeaderValues.CHUNKED)) |
713 { | 713 transfer_encoding.putTo(_header); |
714 String c = transfer_encoding.getValue(); | 714 else |
715 if (c.endsWith(HttpHeaderValues.CHUNKED)) | 715 throw new IllegalArgumentException("BAD TE"); |
716 transfer_encoding.putTo(_header); | 716 } |
717 else | 717 else |
718 throw new IllegalArgumentException("BAD TE"); | 718 _header.put(TRANSFER_ENCODING_CHUNKED); |
719 } | 719 } |
720 else | 720 |
721 _header.put(TRANSFER_ENCODING_CHUNKED); | 721 // Handle connection if need be |
722 } | 722 if (_contentLength==HttpTokens.EOF_CONTENT) |
723 | 723 { |
724 // Handle connection if need be | 724 keep_alive=false; |
725 if (_contentLength==HttpTokens.EOF_CONTENT) | 725 _persistent=false; |
726 { | 726 } |
727 keep_alive=false; | 727 |
728 _persistent=false; | 728 if (isResponse()) |
729 } | 729 { |
730 | 730 if (!_persistent && (close || _version > HttpVersions.HTTP_1_0_ORDINAL)) |
731 if (isResponse()) | 731 { |
732 { | 732 _header.put(CONNECTION_CLOSE); |
733 if (!_persistent && (close || _version > HttpVersions.HTTP_1_0_ORDINAL)) | 733 if (connection!=null) |
734 { | 734 { |
735 _header.put(CONNECTION_CLOSE); | 735 _header.setPutIndex(_header.putIndex()-2); |
736 if (connection!=null) | 736 _header.put((byte)','); |
737 { | 737 _header.put(connection.toString().getBytes()); |
738 _header.setPutIndex(_header.putIndex()-2); | 738 _header.put(CRLF); |
739 _header.put((byte)','); | 739 } |
740 _header.put(connection.toString().getBytes()); | 740 } |
741 _header.put(CRLF); | 741 else if (keep_alive) |
742 } | 742 { |
743 } | 743 _header.put(CONNECTION_KEEP_ALIVE); |
744 else if (keep_alive) | 744 if (connection!=null) |
745 { | 745 { |
746 _header.put(CONNECTION_KEEP_ALIVE); | 746 _header.setPutIndex(_header.putIndex()-2); |
747 if (connection!=null) | 747 _header.put((byte)','); |
748 { | 748 _header.put(connection.toString().getBytes()); |
749 _header.setPutIndex(_header.putIndex()-2); | 749 _header.put(CRLF); |
750 _header.put((byte)','); | 750 } |
751 _header.put(connection.toString().getBytes()); | 751 } |
752 _header.put(CRLF); | 752 else if (connection!=null) |
753 } | 753 { |
754 } | 754 _header.put(CONNECTION_); |
755 else if (connection!=null) | 755 _header.put(connection.toString().getBytes()); |
756 { | 756 _header.put(CRLF); |
757 _header.put(CONNECTION_); | 757 } |
758 _header.put(connection.toString().getBytes()); | 758 } |
759 _header.put(CRLF); | 759 |
760 } | 760 if (!has_server && _status>199) |
761 } | 761 _header.put(SERVER); |
762 | 762 |
763 if (!has_server && _status>199 && getSendServerVersion()) | 763 // end the header. |
764 _header.put(SERVER); | 764 _header.put(HttpTokens.CRLF); |
765 | 765 _state = STATE_CONTENT; |
766 // end the header. | 766 |
767 _header.put(HttpTokens.CRLF); | 767 } |
768 _state = STATE_CONTENT; | 768 catch(ArrayIndexOutOfBoundsException e) |
769 | 769 { |
770 } | 770 throw new RuntimeException("Header>"+_header.capacity(),e); |
771 catch(ArrayIndexOutOfBoundsException e) | 771 } |
772 { | 772 } |
773 throw new RuntimeException("Header>"+_header.capacity(),e); | 773 |
774 } | 774 /* ------------------------------------------------------------ */ |
775 } | 775 /** |
776 | 776 * Complete the message. |
777 /* ------------------------------------------------------------ */ | 777 * |
778 /** | 778 * @throws IOException |
779 * Complete the message. | 779 */ |
780 * | 780 @Override |
781 * @throws IOException | 781 public void complete() throws IOException |
782 */ | 782 { |
783 @Override | 783 if (_state == STATE_END) |
784 public void complete() throws IOException | 784 return; |
785 { | 785 |
786 if (_state == STATE_END) | 786 super.complete(); |
787 return; | 787 |
788 | 788 if (_state < STATE_FLUSHING) |
789 super.complete(); | 789 { |
790 | 790 _state = STATE_FLUSHING; |
791 if (_state < STATE_FLUSHING) | 791 if (_contentLength == HttpTokens.CHUNKED_CONTENT) |
792 { | 792 _needEOC = true; |
793 _state = STATE_FLUSHING; | 793 } |
794 if (_contentLength == HttpTokens.CHUNKED_CONTENT) | 794 |
795 _needEOC = true; | 795 flushBuffer(); |
796 } | 796 } |
797 | 797 |
798 flushBuffer(); | 798 /* ------------------------------------------------------------ */ |
799 } | 799 @Override |
800 | 800 public int flushBuffer() throws IOException |
801 /* ------------------------------------------------------------ */ | 801 { |
802 @Override | 802 try |
803 public int flushBuffer() throws IOException | 803 { |
804 { | 804 |
805 try | 805 if (_state == STATE_HEADER) |
806 { | 806 throw new IllegalStateException("State==HEADER"); |
807 | 807 |
808 if (_state == STATE_HEADER) | 808 prepareBuffers(); |
809 throw new IllegalStateException("State==HEADER"); | 809 |
810 | 810 if (_endp == null) |
811 prepareBuffers(); | 811 { |
812 | 812 if (_needCRLF && _buffer!=null) |
813 if (_endp == null) | 813 _buffer.put(HttpTokens.CRLF); |
814 { | 814 if (_needEOC && _buffer!=null && !_head) |
815 if (_needCRLF && _buffer!=null) | 815 _buffer.put(LAST_CHUNK); |
816 _buffer.put(HttpTokens.CRLF); | 816 _needCRLF=false; |
817 if (_needEOC && _buffer!=null && !_head) | 817 _needEOC=false; |
818 _buffer.put(LAST_CHUNK); | 818 return 0; |
819 _needCRLF=false; | 819 } |
820 _needEOC=false; | 820 |
821 return 0; | 821 int total= 0; |
822 } | 822 |
823 | 823 int len = -1; |
824 int total= 0; | 824 int to_flush = flushMask(); |
825 | 825 int last_flush; |
826 int len = -1; | 826 |
827 int to_flush = flushMask(); | 827 do |
828 int last_flush; | 828 { |
829 | 829 last_flush=to_flush; |
830 do | 830 switch (to_flush) |
831 { | 831 { |
832 last_flush=to_flush; | 832 case 7: |
833 switch (to_flush) | 833 throw new IllegalStateException(); // should never happen! |
834 { | 834 case 6: |
835 case 7: | 835 len = _endp.flush(_header, _buffer, null); |
836 throw new IllegalStateException(); // should never happen! | 836 break; |
837 case 6: | 837 case 5: |
838 len = _endp.flush(_header, _buffer, null); | 838 len = _endp.flush(_header, _content, null); |
839 break; | 839 break; |
840 case 5: | 840 case 4: |
841 len = _endp.flush(_header, _content, null); | 841 len = _endp.flush(_header); |
842 break; | 842 break; |
843 case 4: | 843 case 3: |
844 len = _endp.flush(_header); | 844 len = _endp.flush(_buffer, _content, null); |
845 break; | 845 break; |
846 case 3: | 846 case 2: |
847 len = _endp.flush(_buffer, _content, null); | 847 len = _endp.flush(_buffer); |
848 break; | 848 break; |
849 case 2: | 849 case 1: |
850 len = _endp.flush(_buffer); | 850 len = _endp.flush(_content); |
851 break; | 851 break; |
852 case 1: | 852 case 0: |
853 len = _endp.flush(_content); | 853 { |
854 break; | 854 len=0; |
855 case 0: | 855 // Nothing more we can write now. |
856 { | 856 if (_header != null) |
857 len=0; | 857 _header.clear(); |
858 // Nothing more we can write now. | 858 |
859 if (_header != null) | 859 _bypass = false; |
860 _header.clear(); | 860 _bufferChunked = false; |
861 | 861 |
862 _bypass = false; | 862 if (_buffer != null) |
863 _bufferChunked = false; | 863 { |
864 | 864 _buffer.clear(); |
865 if (_buffer != null) | 865 if (_contentLength == HttpTokens.CHUNKED_CONTENT) |
866 { | 866 { |
867 _buffer.clear(); | 867 // reserve some space for the chunk header |
868 if (_contentLength == HttpTokens.CHUNKED_CONTENT) | 868 _buffer.setPutIndex(CHUNK_SPACE); |
869 { | 869 _buffer.setGetIndex(CHUNK_SPACE); |
870 // reserve some space for the chunk header | 870 |
871 _buffer.setPutIndex(CHUNK_SPACE); | 871 // Special case handling for small left over buffer from |
872 _buffer.setGetIndex(CHUNK_SPACE); | 872 // an addContent that caused a buffer flush. |
873 | 873 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING) |
874 // Special case handling for small left over buffer from | 874 { |
875 // an addContent that caused a buffer flush. | 875 _buffer.put(_content); |
876 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING) | 876 _content.clear(); |
877 { | 877 _content=null; |
878 _buffer.put(_content); | 878 } |
879 _content.clear(); | 879 } |
880 _content=null; | 880 } |
881 } | 881 |
882 } | 882 // Are we completely finished for now? |
883 } | 883 if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0)) |
884 | 884 { |
885 // Are we completely finished for now? | 885 if (_state == STATE_FLUSHING) |
886 if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0)) | 886 _state = STATE_END; |
887 { | 887 |
888 if (_state == STATE_FLUSHING) | 888 if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null) |
889 _state = STATE_END; | 889 _endp.shutdownOutput(); |
890 | 890 } |
891 if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null) | 891 else |
892 _endp.shutdownOutput(); | 892 // Try to prepare more to write. |
893 } | 893 prepareBuffers(); |
894 else | 894 } |
895 // Try to prepare more to write. | 895 |
896 prepareBuffers(); | 896 } |
897 } | 897 |
898 | 898 if (len > 0) |
899 } | 899 total+=len; |
900 | 900 |
901 if (len > 0) | 901 to_flush = flushMask(); |
902 total+=len; | 902 } |
903 | 903 // loop while progress is being made (OR we have prepared some buffers that might make progress) |
904 to_flush = flushMask(); | 904 while (len>0 || (to_flush!=0 && last_flush==0)); |
905 } | 905 |
906 // loop while progress is being made (OR we have prepared some buffers that might make progress) | 906 return total; |
907 while (len>0 || (to_flush!=0 && last_flush==0)); | 907 } |
908 | 908 catch (IOException e) |
909 return total; | 909 { |
910 } | 910 LOG.trace("",e); |
911 catch (IOException e) | 911 throw (e instanceof EofException) ? e:new EofException(e); |
912 { | 912 } |
913 LOG.trace("",e); | 913 } |
914 throw (e instanceof EofException) ? e:new EofException(e); | 914 |
915 } | 915 /* ------------------------------------------------------------ */ |
916 } | 916 private int flushMask() |
917 | 917 { |
918 /* ------------------------------------------------------------ */ | 918 return ((_header != null && _header.length() > 0)?4:0) |
919 private int flushMask() | 919 | ((_buffer != null && _buffer.length() > 0)?2:0) |
920 { | 920 | ((_bypass && _content != null && _content.length() > 0)?1:0); |
921 return ((_header != null && _header.length() > 0)?4:0) | 921 } |
922 | ((_buffer != null && _buffer.length() > 0)?2:0) | 922 |
923 | ((_bypass && _content != null && _content.length() > 0)?1:0); | 923 /* ------------------------------------------------------------ */ |
924 } | 924 private void prepareBuffers() |
925 | 925 { |
926 /* ------------------------------------------------------------ */ | 926 // if we are not flushing an existing chunk |
927 private void prepareBuffers() | 927 if (!_bufferChunked) |
928 { | 928 { |
929 // if we are not flushing an existing chunk | 929 // Refill buffer if possible |
930 if (!_bufferChunked) | 930 if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0) |
931 { | 931 { |
932 // Refill buffer if possible | 932 int len = _buffer.put(_content); |
933 if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0) | 933 _content.skip(len); |
934 { | 934 if (_content.length() == 0) |
935 int len = _buffer.put(_content); | 935 _content = null; |
936 _content.skip(len); | 936 } |
937 if (_content.length() == 0) | 937 |
938 _content = null; | 938 // Chunk buffer if need be |
939 } | 939 if (_contentLength == HttpTokens.CHUNKED_CONTENT) |
940 | 940 { |
941 // Chunk buffer if need be | 941 if (_bypass && (_buffer==null||_buffer.length()==0) && _content!=null) |
942 if (_contentLength == HttpTokens.CHUNKED_CONTENT) | 942 { |
943 { | 943 // this is a bypass write |
944 if (_bypass && (_buffer==null||_buffer.length()==0) && _content!=null) | 944 int size = _content.length(); |
945 { | 945 _bufferChunked = true; |
946 // this is a bypass write | 946 |
947 int size = _content.length(); | 947 if (_header == null) |
948 _bufferChunked = true; | 948 _header = _buffers.getHeader(); |
949 | 949 |
950 if (_header == null) | 950 // if we need CRLF add this to header |
951 _header = _buffers.getHeader(); | 951 if (_needCRLF) |
952 | 952 { |
953 // if we need CRLF add this to header | 953 if (_header.length() > 0) throw new IllegalStateException("EOC"); |
954 if (_needCRLF) | 954 _header.put(HttpTokens.CRLF); |
955 { | 955 _needCRLF = false; |
956 if (_header.length() > 0) throw new IllegalStateException("EOC"); | 956 } |
957 _header.put(HttpTokens.CRLF); | 957 // Add the chunk size to the header |
958 _needCRLF = false; | 958 BufferUtil.putHexInt(_header, size); |
959 } | 959 _header.put(HttpTokens.CRLF); |
960 // Add the chunk size to the header | 960 |
961 BufferUtil.putHexInt(_header, size); | 961 // Need a CRLF after the content |
962 _header.put(HttpTokens.CRLF); | 962 _needCRLF=true; |
963 | 963 } |
964 // Need a CRLF after the content | 964 else if (_buffer!=null) |
965 _needCRLF=true; | 965 { |
966 } | 966 int size = _buffer.length(); |
967 else if (_buffer!=null) | 967 if (size > 0) |
968 { | 968 { |
969 int size = _buffer.length(); | 969 // Prepare a chunk! |
970 if (size > 0) | 970 _bufferChunked = true; |
971 { | 971 |
972 // Prepare a chunk! | 972 // Did we leave space at the start of the buffer. |
973 _bufferChunked = true; | 973 //noinspection ConstantConditions |
974 | 974 if (_buffer.getIndex() == CHUNK_SPACE) |
975 // Did we leave space at the start of the buffer. | 975 { |
976 //noinspection ConstantConditions | 976 // Oh yes, goodie! let's use it then! |
977 if (_buffer.getIndex() == CHUNK_SPACE) | 977 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); |
978 { | 978 _buffer.setGetIndex(_buffer.getIndex() - 2); |
979 // Oh yes, goodie! let's use it then! | 979 BufferUtil.prependHexInt(_buffer, size); |
980 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); | 980 |
981 _buffer.setGetIndex(_buffer.getIndex() - 2); | 981 if (_needCRLF) |
982 BufferUtil.prependHexInt(_buffer, size); | 982 { |
983 | 983 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); |
984 if (_needCRLF) | 984 _buffer.setGetIndex(_buffer.getIndex() - 2); |
985 { | 985 _needCRLF = false; |
986 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); | 986 } |
987 _buffer.setGetIndex(_buffer.getIndex() - 2); | 987 } |
988 _needCRLF = false; | 988 else |
989 } | 989 { |
990 } | 990 // No space so lets use a header buffer. |
991 else | 991 if (_header == null) |
992 { | 992 _header = _buffers.getHeader(); |
993 // No space so lets use a header buffer. | 993 |
994 if (_header == null) | 994 if (_needCRLF) |
995 _header = _buffers.getHeader(); | 995 { |
996 | 996 if (_header.length() > 0) throw new IllegalStateException("EOC"); |
997 if (_needCRLF) | 997 _header.put(HttpTokens.CRLF); |
998 { | 998 _needCRLF = false; |
999 if (_header.length() > 0) throw new IllegalStateException("EOC"); | 999 } |
1000 _header.put(HttpTokens.CRLF); | 1000 BufferUtil.putHexInt(_header, size); |
1001 _needCRLF = false; | 1001 _header.put(HttpTokens.CRLF); |
1002 } | 1002 } |
1003 BufferUtil.putHexInt(_header, size); | 1003 |
1004 _header.put(HttpTokens.CRLF); | 1004 // Add end chunk trailer. |
1005 } | 1005 if (_buffer.space() >= 2) |
1006 | 1006 _buffer.put(HttpTokens.CRLF); |
1007 // Add end chunk trailer. | 1007 else |
1008 if (_buffer.space() >= 2) | 1008 _needCRLF = true; |
1009 _buffer.put(HttpTokens.CRLF); | 1009 } |
1010 else | 1010 } |
1011 _needCRLF = true; | 1011 |
1012 } | 1012 // If we need EOC and everything written |
1013 } | 1013 if (_needEOC && (_content == null || _content.length() == 0)) |
1014 | 1014 { |
1015 // If we need EOC and everything written | 1015 if (_header == null && _buffer == null) |
1016 if (_needEOC && (_content == null || _content.length() == 0)) | 1016 _header = _buffers.getHeader(); |
1017 { | 1017 |
1018 if (_header == null && _buffer == null) | 1018 if (_needCRLF) |
1019 _header = _buffers.getHeader(); | 1019 { |
1020 | 1020 if (_buffer == null && _header != null && _header.space() >= HttpTokens.CRLF.length) |
1021 if (_needCRLF) | 1021 { |
1022 { | 1022 _header.put(HttpTokens.CRLF); |
1023 if (_buffer == null && _header != null && _header.space() >= HttpTokens.CRLF.length) | 1023 _needCRLF = false; |
1024 { | 1024 } |
1025 _header.put(HttpTokens.CRLF); | 1025 else if (_buffer!=null && _buffer.space() >= HttpTokens.CRLF.length) |
1026 _needCRLF = false; | 1026 { |
1027 } | 1027 _buffer.put(HttpTokens.CRLF); |
1028 else if (_buffer!=null && _buffer.space() >= HttpTokens.CRLF.length) | 1028 _needCRLF = false; |
1029 { | 1029 } |
1030 _buffer.put(HttpTokens.CRLF); | 1030 } |
1031 _needCRLF = false; | 1031 |
1032 } | 1032 if (!_needCRLF && _needEOC) |
1033 } | 1033 { |
1034 | 1034 if (_buffer == null && _header != null && _header.space() >= LAST_CHUNK.length) |
1035 if (!_needCRLF && _needEOC) | 1035 { |
1036 { | 1036 if (!_head) |
1037 if (_buffer == null && _header != null && _header.space() >= LAST_CHUNK.length) | 1037 { |
1038 { | 1038 _header.put(LAST_CHUNK); |
1039 if (!_head) | 1039 _bufferChunked=true; |
1040 { | 1040 } |
1041 _header.put(LAST_CHUNK); | 1041 _needEOC = false; |
1042 _bufferChunked=true; | 1042 } |
1043 } | 1043 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length) |
1044 _needEOC = false; | 1044 { |
1045 } | 1045 if (!_head) |
1046 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length) | 1046 { |
1047 { | 1047 _buffer.put(LAST_CHUNK); |
1048 if (!_head) | 1048 _bufferChunked=true; |
1049 { | 1049 } |
1050 _buffer.put(LAST_CHUNK); | 1050 _needEOC = false; |
1051 _bufferChunked=true; | 1051 } |
1052 } | 1052 } |
1053 _needEOC = false; | 1053 } |
1054 } | 1054 } |
1055 } | 1055 } |
1056 } | 1056 |
1057 } | 1057 if (_content != null && _content.length() == 0) |
1058 } | 1058 _content = null; |
1059 | 1059 |
1060 if (_content != null && _content.length() == 0) | 1060 } |
1061 _content = null; | 1061 |
1062 | 1062 public int getBytesBuffered() |
1063 } | 1063 { |
1064 | 1064 return(_header==null?0:_header.length())+ |
1065 public int getBytesBuffered() | 1065 (_buffer==null?0:_buffer.length())+ |
1066 { | 1066 (_content==null?0:_content.length()); |
1067 return(_header==null?0:_header.length())+ | 1067 } |
1068 (_buffer==null?0:_buffer.length())+ | 1068 |
1069 (_content==null?0:_content.length()); | 1069 public boolean isEmpty() |
1070 } | 1070 { |
1071 | 1071 return (_header==null||_header.length()==0) && |
1072 public boolean isEmpty() | 1072 (_buffer==null||_buffer.length()==0) && |
1073 { | 1073 (_content==null||_content.length()==0); |
1074 return (_header==null||_header.length()==0) && | 1074 } |
1075 (_buffer==null||_buffer.length()==0) && | 1075 |
1076 (_content==null||_content.length()==0); | 1076 @Override |
1077 } | 1077 public String toString() |
1078 | 1078 { |
1079 @Override | 1079 Buffer header=_header; |
1080 public String toString() | 1080 Buffer buffer=_buffer; |
1081 { | 1081 Buffer content=_content; |
1082 Buffer header=_header; | 1082 return String.format("%s{s=%d,h=%d,b=%d,c=%d}", |
1083 Buffer buffer=_buffer; | 1083 getClass().getSimpleName(), |
1084 Buffer content=_content; | 1084 _state, |
1085 return String.format("%s{s=%d,h=%d,b=%d,c=%d}", | 1085 header == null ? -1 : header.length(), |
1086 getClass().getSimpleName(), | 1086 buffer == null ? -1 : buffer.length(), |
1087 _state, | 1087 content == null ? -1 : content.length()); |
1088 header == null ? -1 : header.length(), | 1088 } |
1089 buffer == null ? -1 : buffer.length(), | |
1090 content == null ? -1 : content.length()); | |
1091 } | |
1092 } | 1089 } |