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 }