Mercurial Hosting > luan
comparison src/org/eclipse/jetty/http/HttpParser.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children | 8e9db0bbf4f9 |
comparison
equal
deleted
inserted
replaced
801:6a21393191c1 | 802:3428c60d7cfc |
---|---|
1 // | |
2 // ======================================================================== | |
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. | |
4 // ------------------------------------------------------------------------ | |
5 // All rights reserved. This program and the accompanying materials | |
6 // are made available under the terms of the Eclipse Public License v1.0 | |
7 // and Apache License v2.0 which accompanies this distribution. | |
8 // | |
9 // The Eclipse Public License is available at | |
10 // http://www.eclipse.org/legal/epl-v10.html | |
11 // | |
12 // The Apache License v2.0 is available at | |
13 // http://www.opensource.org/licenses/apache2.0.php | |
14 // | |
15 // You may elect to redistribute this code under either of these licenses. | |
16 // ======================================================================== | |
17 // | |
18 | |
19 package org.eclipse.jetty.http; | |
20 | |
21 import java.io.IOException; | |
22 | |
23 import org.eclipse.jetty.io.Buffer; | |
24 import org.eclipse.jetty.io.BufferCache.CachedBuffer; | |
25 import org.eclipse.jetty.io.BufferUtil; | |
26 import org.eclipse.jetty.io.Buffers; | |
27 import org.eclipse.jetty.io.ByteArrayBuffer; | |
28 import org.eclipse.jetty.io.EndPoint; | |
29 import org.eclipse.jetty.io.EofException; | |
30 import org.eclipse.jetty.io.View; | |
31 import org.eclipse.jetty.io.bio.StreamEndPoint; | |
32 import org.eclipse.jetty.util.StringUtil; | |
33 import org.eclipse.jetty.util.log.Log; | |
34 import org.eclipse.jetty.util.log.Logger; | |
35 | |
36 public class HttpParser implements Parser | |
37 { | |
38 private static final Logger LOG = Log.getLogger(HttpParser.class); | |
39 | |
40 // States | |
41 public static final int STATE_START=-14; | |
42 public static final int STATE_FIELD0=-13; | |
43 public static final int STATE_SPACE1=-12; | |
44 public static final int STATE_STATUS=-11; | |
45 public static final int STATE_URI=-10; | |
46 public static final int STATE_SPACE2=-9; | |
47 public static final int STATE_END0=-8; | |
48 public static final int STATE_END1=-7; | |
49 public static final int STATE_FIELD2=-6; | |
50 public static final int STATE_HEADER=-5; | |
51 public static final int STATE_HEADER_NAME=-4; | |
52 public static final int STATE_HEADER_IN_NAME=-3; | |
53 public static final int STATE_HEADER_VALUE=-2; | |
54 public static final int STATE_HEADER_IN_VALUE=-1; | |
55 public static final int STATE_END=0; | |
56 public static final int STATE_EOF_CONTENT=1; | |
57 public static final int STATE_CONTENT=2; | |
58 public static final int STATE_CHUNKED_CONTENT=3; | |
59 public static final int STATE_CHUNK_SIZE=4; | |
60 public static final int STATE_CHUNK_PARAMS=5; | |
61 public static final int STATE_CHUNK=6; | |
62 public static final int STATE_SEEKING_EOF=7; | |
63 | |
64 private final EventHandler _handler; | |
65 private final Buffers _buffers; // source of buffers | |
66 private final EndPoint _endp; | |
67 private Buffer _header; // Buffer for header data (and small _content) | |
68 private Buffer _body; // Buffer for large content | |
69 private Buffer _buffer; // The current buffer in use (either _header or _content) | |
70 private CachedBuffer _cached; | |
71 private final View.CaseInsensitive _tok0; // Saved token: header name, request method or response version | |
72 private final View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code | |
73 private String _multiLineValue; | |
74 private int _responseStatus; // If >0 then we are parsing a response | |
75 private boolean _forceContentBuffer; | |
76 private boolean _persistent; | |
77 | |
78 /* ------------------------------------------------------------------------------- */ | |
79 protected final View _contentView=new View(); // View of the content in the buffer for {@link Input} | |
80 protected int _state=STATE_START; | |
81 protected byte _eol; | |
82 protected int _length; | |
83 protected long _contentLength; | |
84 protected long _contentPosition; | |
85 protected int _chunkLength; | |
86 protected int _chunkPosition; | |
87 private boolean _headResponse; | |
88 | |
89 /* ------------------------------------------------------------------------------- */ | |
90 /** | |
91 * Constructor. | |
92 */ | |
93 public HttpParser(Buffer buffer, EventHandler handler) | |
94 { | |
95 _endp=null; | |
96 _buffers=null; | |
97 _header=buffer; | |
98 _buffer=buffer; | |
99 _handler=handler; | |
100 | |
101 _tok0=new View.CaseInsensitive(_header); | |
102 _tok1=new View.CaseInsensitive(_header); | |
103 } | |
104 | |
105 /* ------------------------------------------------------------------------------- */ | |
106 /** | |
107 * Constructor. | |
108 * @param buffers the buffers to use | |
109 * @param endp the endpoint | |
110 * @param handler the even handler | |
111 */ | |
112 public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler) | |
113 { | |
114 _buffers=buffers; | |
115 _endp=endp; | |
116 _handler=handler; | |
117 _tok0=new View.CaseInsensitive(); | |
118 _tok1=new View.CaseInsensitive(); | |
119 } | |
120 | |
121 /* ------------------------------------------------------------------------------- */ | |
122 public long getContentLength() | |
123 { | |
124 return _contentLength; | |
125 } | |
126 | |
127 /* ------------------------------------------------------------ */ | |
128 public long getContentRead() | |
129 { | |
130 return _contentPosition; | |
131 } | |
132 | |
133 /* ------------------------------------------------------------ */ | |
134 /** Set if a HEAD response is expected | |
135 * @param head | |
136 */ | |
137 public void setHeadResponse(boolean head) | |
138 { | |
139 _headResponse=head; | |
140 } | |
141 | |
142 /* ------------------------------------------------------------------------------- */ | |
143 public int getState() | |
144 { | |
145 return _state; | |
146 } | |
147 | |
148 /* ------------------------------------------------------------------------------- */ | |
149 public boolean inContentState() | |
150 { | |
151 return _state > 0; | |
152 } | |
153 | |
154 /* ------------------------------------------------------------------------------- */ | |
155 public boolean inHeaderState() | |
156 { | |
157 return _state < 0; | |
158 } | |
159 | |
160 /* ------------------------------------------------------------------------------- */ | |
161 public boolean isChunking() | |
162 { | |
163 return _contentLength==HttpTokens.CHUNKED_CONTENT; | |
164 } | |
165 | |
166 /* ------------------------------------------------------------ */ | |
167 public boolean isIdle() | |
168 { | |
169 return isState(STATE_START); | |
170 } | |
171 | |
172 /* ------------------------------------------------------------ */ | |
173 public boolean isComplete() | |
174 { | |
175 return isState(STATE_END); | |
176 } | |
177 | |
178 /* ------------------------------------------------------------ */ | |
179 public boolean isMoreInBuffer() | |
180 throws IOException | |
181 { | |
182 return ( _header!=null && _header.hasContent() || | |
183 _body!=null && _body.hasContent()); | |
184 } | |
185 | |
186 /* ------------------------------------------------------------------------------- */ | |
187 public boolean isState(int state) | |
188 { | |
189 return _state == state; | |
190 } | |
191 | |
192 /* ------------------------------------------------------------------------------- */ | |
193 public boolean isPersistent() | |
194 { | |
195 return _persistent; | |
196 } | |
197 | |
198 /* ------------------------------------------------------------------------------- */ | |
199 public void setPersistent(boolean persistent) | |
200 { | |
201 _persistent = persistent; | |
202 if (!_persistent &&(_state==STATE_END || _state==STATE_START)) | |
203 _state=STATE_SEEKING_EOF; | |
204 } | |
205 | |
206 /* ------------------------------------------------------------------------------- */ | |
207 /** | |
208 * Parse until {@link #STATE_END END} state. | |
209 * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed. | |
210 * @throws IllegalStateException If the buffers have already been partially parsed. | |
211 */ | |
212 public void parse() throws IOException | |
213 { | |
214 if (_state==STATE_END) | |
215 reset(); | |
216 if (_state!=STATE_START) | |
217 throw new IllegalStateException("!START"); | |
218 | |
219 // continue parsing | |
220 while (_state != STATE_END) | |
221 if (parseNext()<0) | |
222 return; | |
223 } | |
224 | |
225 /* ------------------------------------------------------------------------------- */ | |
226 /** | |
227 * Parse until END state. | |
228 * This method will parse any remaining content in the current buffer as long as there is | |
229 * no unconsumed content. It does not care about the {@link #getState current state} of the parser. | |
230 * @see #parse | |
231 * @see #parseNext | |
232 */ | |
233 public boolean parseAvailable() throws IOException | |
234 { | |
235 boolean progress=parseNext()>0; | |
236 | |
237 // continue parsing | |
238 while (!isComplete() && _buffer!=null && _buffer.length()>0 && !_contentView.hasContent()) | |
239 { | |
240 progress |= parseNext()>0; | |
241 } | |
242 return progress; | |
243 } | |
244 | |
245 | |
246 /* ------------------------------------------------------------------------------- */ | |
247 /** | |
248 * Parse until next Event. | |
249 * @return an indication of progress <0 EOF, 0 no progress, >0 progress. | |
250 */ | |
251 public int parseNext() throws IOException | |
252 { | |
253 try | |
254 { | |
255 int progress=0; | |
256 | |
257 if (_state == STATE_END) | |
258 return 0; | |
259 | |
260 if (_buffer==null) | |
261 _buffer=getHeaderBuffer(); | |
262 | |
263 | |
264 if (_state == STATE_CONTENT && _contentPosition == _contentLength) | |
265 { | |
266 _state=STATE_END; | |
267 _handler.messageComplete(_contentPosition); | |
268 return 1; | |
269 } | |
270 | |
271 int length=_buffer.length(); | |
272 | |
273 // Fill buffer if we can | |
274 if (length == 0) | |
275 { | |
276 int filled=-1; | |
277 IOException ex=null; | |
278 try | |
279 { | |
280 filled=fill(); | |
281 LOG.debug("filled {}/{}",filled,_buffer.length()); | |
282 } | |
283 catch(IOException e) | |
284 { | |
285 LOG.debug(this.toString(),e); | |
286 ex=e; | |
287 } | |
288 | |
289 if (filled > 0 ) | |
290 progress++; | |
291 else if (filled < 0 ) | |
292 { | |
293 _persistent=false; | |
294 | |
295 // do we have content to deliver? | |
296 if (_state>STATE_END) | |
297 { | |
298 if (_buffer.length()>0 && !_headResponse) | |
299 { | |
300 Buffer chunk=_buffer.get(_buffer.length()); | |
301 _contentPosition += chunk.length(); | |
302 _contentView.update(chunk); | |
303 _handler.content(chunk); // May recurse here | |
304 } | |
305 } | |
306 | |
307 // was this unexpected? | |
308 switch(_state) | |
309 { | |
310 case STATE_END: | |
311 case STATE_SEEKING_EOF: | |
312 _state=STATE_END; | |
313 break; | |
314 | |
315 case STATE_EOF_CONTENT: | |
316 _state=STATE_END; | |
317 _handler.messageComplete(_contentPosition); | |
318 break; | |
319 | |
320 default: | |
321 _state=STATE_END; | |
322 if (!_headResponse) | |
323 _handler.earlyEOF(); | |
324 _handler.messageComplete(_contentPosition); | |
325 } | |
326 | |
327 if (ex!=null) | |
328 throw ex; | |
329 | |
330 if (!isComplete() && !isIdle()) | |
331 throw new EofException(); | |
332 | |
333 return -1; | |
334 } | |
335 length=_buffer.length(); | |
336 } | |
337 | |
338 | |
339 // Handle header states | |
340 byte ch; | |
341 byte[] array=_buffer.array(); | |
342 int last=_state; | |
343 while (_state<STATE_END && length-->0) | |
344 { | |
345 if (last!=_state) | |
346 { | |
347 progress++; | |
348 last=_state; | |
349 } | |
350 | |
351 ch=_buffer.get(); | |
352 | |
353 if (_eol == HttpTokens.CARRIAGE_RETURN) | |
354 { | |
355 if (ch == HttpTokens.LINE_FEED) | |
356 { | |
357 _eol=HttpTokens.LINE_FEED; | |
358 continue; | |
359 } | |
360 throw new HttpException(HttpStatus.BAD_REQUEST_400); | |
361 } | |
362 _eol=0; | |
363 | |
364 switch (_state) | |
365 { | |
366 case STATE_START: | |
367 _contentLength=HttpTokens.UNKNOWN_CONTENT; | |
368 _cached=null; | |
369 if (ch > HttpTokens.SPACE || ch<0) | |
370 { | |
371 _buffer.mark(); | |
372 _state=STATE_FIELD0; | |
373 } | |
374 break; | |
375 | |
376 case STATE_FIELD0: | |
377 if (ch == HttpTokens.SPACE) | |
378 { | |
379 _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1); | |
380 _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0; | |
381 _state=STATE_SPACE1; | |
382 continue; | |
383 } | |
384 else if (ch < HttpTokens.SPACE && ch>=0) | |
385 { | |
386 throw new HttpException(HttpStatus.BAD_REQUEST_400); | |
387 } | |
388 break; | |
389 | |
390 case STATE_SPACE1: | |
391 if (ch > HttpTokens.SPACE || ch<0) | |
392 { | |
393 _buffer.mark(); | |
394 if (_responseStatus>=0) | |
395 { | |
396 _state=STATE_STATUS; | |
397 _responseStatus=ch-'0'; | |
398 } | |
399 else | |
400 _state=STATE_URI; | |
401 } | |
402 else if (ch < HttpTokens.SPACE) | |
403 { | |
404 throw new HttpException(HttpStatus.BAD_REQUEST_400); | |
405 } | |
406 break; | |
407 | |
408 case STATE_STATUS: | |
409 if (ch == HttpTokens.SPACE) | |
410 { | |
411 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); | |
412 _state=STATE_SPACE2; | |
413 continue; | |
414 } | |
415 else if (ch>='0' && ch<='9') | |
416 { | |
417 _responseStatus=_responseStatus*10+(ch-'0'); | |
418 continue; | |
419 } | |
420 else if (ch < HttpTokens.SPACE && ch>=0) | |
421 { | |
422 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); | |
423 _eol=ch; | |
424 _state=STATE_HEADER; | |
425 _tok0.setPutIndex(_tok0.getIndex()); | |
426 _tok1.setPutIndex(_tok1.getIndex()); | |
427 _multiLineValue=null; | |
428 continue; | |
429 } | |
430 // not a digit, so must be a URI | |
431 _state=STATE_URI; | |
432 _responseStatus=-1; | |
433 break; | |
434 | |
435 case STATE_URI: | |
436 if (ch == HttpTokens.SPACE) | |
437 { | |
438 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); | |
439 _state=STATE_SPACE2; | |
440 continue; | |
441 } | |
442 else if (ch < HttpTokens.SPACE && ch>=0) | |
443 { | |
444 // HTTP/0.9 | |
445 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null); | |
446 _persistent=false; | |
447 _state=STATE_SEEKING_EOF; | |
448 _handler.headerComplete(); | |
449 _handler.messageComplete(_contentPosition); | |
450 return 1; | |
451 } | |
452 break; | |
453 | |
454 case STATE_SPACE2: | |
455 if (ch > HttpTokens.SPACE || ch<0) | |
456 { | |
457 _buffer.mark(); | |
458 _state=STATE_FIELD2; | |
459 } | |
460 else if (ch < HttpTokens.SPACE) | |
461 { | |
462 if (_responseStatus>0) | |
463 { | |
464 _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); | |
465 _eol=ch; | |
466 _state=STATE_HEADER; | |
467 _tok0.setPutIndex(_tok0.getIndex()); | |
468 _tok1.setPutIndex(_tok1.getIndex()); | |
469 _multiLineValue=null; | |
470 } | |
471 else | |
472 { | |
473 // HTTP/0.9 | |
474 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null); | |
475 _persistent=false; | |
476 _state=STATE_SEEKING_EOF; | |
477 _handler.headerComplete(); | |
478 _handler.messageComplete(_contentPosition); | |
479 return 1; | |
480 } | |
481 } | |
482 break; | |
483 | |
484 case STATE_FIELD2: | |
485 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) | |
486 { | |
487 Buffer version; | |
488 if (_responseStatus>0) | |
489 _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark()); | |
490 else | |
491 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark())); | |
492 _eol=ch; | |
493 _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL; | |
494 _state=STATE_HEADER; | |
495 _tok0.setPutIndex(_tok0.getIndex()); | |
496 _tok1.setPutIndex(_tok1.getIndex()); | |
497 _multiLineValue=null; | |
498 continue; | |
499 } | |
500 break; | |
501 | |
502 case STATE_HEADER: | |
503 switch(ch) | |
504 { | |
505 case HttpTokens.COLON: | |
506 case HttpTokens.SPACE: | |
507 case HttpTokens.TAB: | |
508 { | |
509 // header value without name - continuation? | |
510 _length=-1; | |
511 _state=STATE_HEADER_VALUE; | |
512 break; | |
513 } | |
514 | |
515 default: | |
516 { | |
517 // handler last header if any | |
518 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null) | |
519 { | |
520 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0); | |
521 _cached=null; | |
522 Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue); | |
523 | |
524 int ho=HttpHeaders.CACHE.getOrdinal(header); | |
525 if (ho >= 0) | |
526 { | |
527 int vo; | |
528 | |
529 switch (ho) | |
530 { | |
531 case HttpHeaders.CONTENT_LENGTH_ORDINAL: | |
532 if (_contentLength != HttpTokens.CHUNKED_CONTENT ) | |
533 { | |
534 try | |
535 { | |
536 _contentLength=BufferUtil.toLong(value); | |
537 } | |
538 catch(NumberFormatException e) | |
539 { | |
540 LOG.ignore(e); | |
541 throw new HttpException(HttpStatus.BAD_REQUEST_400); | |
542 } | |
543 if (_contentLength <= 0) | |
544 _contentLength=HttpTokens.NO_CONTENT; | |
545 } | |
546 break; | |
547 | |
548 case HttpHeaders.TRANSFER_ENCODING_ORDINAL: | |
549 value=HttpHeaderValues.CACHE.lookup(value); | |
550 vo=HttpHeaderValues.CACHE.getOrdinal(value); | |
551 if (HttpHeaderValues.CHUNKED_ORDINAL == vo) | |
552 _contentLength=HttpTokens.CHUNKED_CONTENT; | |
553 else | |
554 { | |
555 String c=value.toString(StringUtil.__ISO_8859_1); | |
556 if (c.endsWith(HttpHeaderValues.CHUNKED)) | |
557 _contentLength=HttpTokens.CHUNKED_CONTENT; | |
558 | |
559 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0) | |
560 throw new HttpException(400,null); | |
561 } | |
562 break; | |
563 | |
564 case HttpHeaders.CONNECTION_ORDINAL: | |
565 switch(HttpHeaderValues.CACHE.getOrdinal(value)) | |
566 { | |
567 case HttpHeaderValues.CLOSE_ORDINAL: | |
568 _persistent=false; | |
569 break; | |
570 | |
571 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: | |
572 _persistent=true; | |
573 break; | |
574 | |
575 case -1: // No match, may be multi valued | |
576 { | |
577 for (String v : value.toString().split(",")) | |
578 { | |
579 switch(HttpHeaderValues.CACHE.getOrdinal(v.trim())) | |
580 { | |
581 case HttpHeaderValues.CLOSE_ORDINAL: | |
582 _persistent=false; | |
583 break; | |
584 | |
585 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: | |
586 _persistent=true; | |
587 break; | |
588 } | |
589 } | |
590 break; | |
591 } | |
592 } | |
593 } | |
594 } | |
595 | |
596 _handler.parsedHeader(header, value); | |
597 _tok0.setPutIndex(_tok0.getIndex()); | |
598 _tok1.setPutIndex(_tok1.getIndex()); | |
599 _multiLineValue=null; | |
600 } | |
601 _buffer.setMarkIndex(-1); | |
602 | |
603 // now handle ch | |
604 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) | |
605 { | |
606 // is it a response that cannot have a body? | |
607 if (_responseStatus > 0 && // response | |
608 (_responseStatus == 304 || // not-modified response | |
609 _responseStatus == 204 || // no-content response | |
610 _responseStatus < 200)) // 1xx response | |
611 _contentLength=HttpTokens.NO_CONTENT; // ignore any other headers set | |
612 // else if we don't know framing | |
613 else if (_contentLength == HttpTokens.UNKNOWN_CONTENT) | |
614 { | |
615 if (_responseStatus == 0 // request | |
616 || _responseStatus == 304 // not-modified response | |
617 || _responseStatus == 204 // no-content response | |
618 || _responseStatus < 200) // 1xx response | |
619 _contentLength=HttpTokens.NO_CONTENT; | |
620 else | |
621 _contentLength=HttpTokens.EOF_CONTENT; | |
622 } | |
623 | |
624 _contentPosition=0; | |
625 _eol=ch; | |
626 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) | |
627 _eol=_buffer.get(); | |
628 | |
629 // We convert _contentLength to an int for this switch statement because | |
630 // we don't care about the amount of data available just whether there is some. | |
631 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength) | |
632 { | |
633 case HttpTokens.EOF_CONTENT: | |
634 _state=STATE_EOF_CONTENT; | |
635 _handler.headerComplete(); // May recurse here ! | |
636 break; | |
637 | |
638 case HttpTokens.CHUNKED_CONTENT: | |
639 _state=STATE_CHUNKED_CONTENT; | |
640 _handler.headerComplete(); // May recurse here ! | |
641 break; | |
642 | |
643 case HttpTokens.NO_CONTENT: | |
644 _handler.headerComplete(); | |
645 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; | |
646 _handler.messageComplete(_contentPosition); | |
647 return 1; | |
648 | |
649 default: | |
650 _state=STATE_CONTENT; | |
651 _handler.headerComplete(); // May recurse here ! | |
652 break; | |
653 } | |
654 return 1; | |
655 } | |
656 else | |
657 { | |
658 // New header | |
659 _length=1; | |
660 _buffer.mark(); | |
661 _state=STATE_HEADER_NAME; | |
662 | |
663 // try cached name! | |
664 if (array!=null) | |
665 { | |
666 _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1); | |
667 | |
668 if (_cached!=null) | |
669 { | |
670 _length=_cached.length(); | |
671 _buffer.setGetIndex(_buffer.markIndex()+_length); | |
672 length=_buffer.length(); | |
673 } | |
674 } | |
675 } | |
676 } | |
677 } | |
678 | |
679 break; | |
680 | |
681 case STATE_HEADER_NAME: | |
682 switch(ch) | |
683 { | |
684 case HttpTokens.CARRIAGE_RETURN: | |
685 case HttpTokens.LINE_FEED: | |
686 if (_length > 0) | |
687 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
688 _eol=ch; | |
689 _state=STATE_HEADER; | |
690 break; | |
691 case HttpTokens.COLON: | |
692 if (_length > 0 && _cached==null) | |
693 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
694 _length=-1; | |
695 _state=STATE_HEADER_VALUE; | |
696 break; | |
697 case HttpTokens.SPACE: | |
698 case HttpTokens.TAB: | |
699 break; | |
700 default: | |
701 { | |
702 _cached=null; | |
703 if (_length == -1) | |
704 _buffer.mark(); | |
705 _length=_buffer.getIndex() - _buffer.markIndex(); | |
706 _state=STATE_HEADER_IN_NAME; | |
707 } | |
708 } | |
709 | |
710 break; | |
711 | |
712 case STATE_HEADER_IN_NAME: | |
713 switch(ch) | |
714 { | |
715 case HttpTokens.CARRIAGE_RETURN: | |
716 case HttpTokens.LINE_FEED: | |
717 if (_length > 0) | |
718 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
719 _eol=ch; | |
720 _state=STATE_HEADER; | |
721 break; | |
722 case HttpTokens.COLON: | |
723 if (_length > 0 && _cached==null) | |
724 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
725 _length=-1; | |
726 _state=STATE_HEADER_VALUE; | |
727 break; | |
728 case HttpTokens.SPACE: | |
729 case HttpTokens.TAB: | |
730 _state=STATE_HEADER_NAME; | |
731 break; | |
732 default: | |
733 { | |
734 _cached=null; | |
735 _length++; | |
736 } | |
737 } | |
738 break; | |
739 | |
740 case STATE_HEADER_VALUE: | |
741 switch(ch) | |
742 { | |
743 case HttpTokens.CARRIAGE_RETURN: | |
744 case HttpTokens.LINE_FEED: | |
745 if (_length > 0) | |
746 { | |
747 if (_tok1.length() == 0) | |
748 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
749 else | |
750 { | |
751 // Continuation line! | |
752 if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); | |
753 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
754 _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); | |
755 } | |
756 } | |
757 _eol=ch; | |
758 _state=STATE_HEADER; | |
759 break; | |
760 case HttpTokens.SPACE: | |
761 case HttpTokens.TAB: | |
762 break; | |
763 default: | |
764 { | |
765 if (_length == -1) | |
766 _buffer.mark(); | |
767 _length=_buffer.getIndex() - _buffer.markIndex(); | |
768 _state=STATE_HEADER_IN_VALUE; | |
769 } | |
770 } | |
771 break; | |
772 | |
773 case STATE_HEADER_IN_VALUE: | |
774 switch(ch) | |
775 { | |
776 case HttpTokens.CARRIAGE_RETURN: | |
777 case HttpTokens.LINE_FEED: | |
778 if (_length > 0) | |
779 { | |
780 if (_tok1.length() == 0) | |
781 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
782 else | |
783 { | |
784 // Continuation line! | |
785 if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); | |
786 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); | |
787 _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); | |
788 } | |
789 } | |
790 _eol=ch; | |
791 _state=STATE_HEADER; | |
792 break; | |
793 case HttpTokens.SPACE: | |
794 case HttpTokens.TAB: | |
795 _state=STATE_HEADER_VALUE; | |
796 break; | |
797 default: | |
798 _length++; | |
799 } | |
800 break; | |
801 } | |
802 } // end of HEADER states loop | |
803 | |
804 // ========================== | |
805 | |
806 // Handle HEAD response | |
807 if (_responseStatus>0 && _headResponse) | |
808 { | |
809 _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; | |
810 _handler.messageComplete(_contentLength); | |
811 } | |
812 | |
813 | |
814 // ========================== | |
815 | |
816 // Handle _content | |
817 length=_buffer.length(); | |
818 Buffer chunk; | |
819 last=_state; | |
820 while (_state > STATE_END && length > 0) | |
821 { | |
822 if (last!=_state) | |
823 { | |
824 progress++; | |
825 last=_state; | |
826 } | |
827 | |
828 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED) | |
829 { | |
830 _eol=_buffer.get(); | |
831 length=_buffer.length(); | |
832 continue; | |
833 } | |
834 _eol=0; | |
835 switch (_state) | |
836 { | |
837 case STATE_EOF_CONTENT: | |
838 chunk=_buffer.get(_buffer.length()); | |
839 _contentPosition += chunk.length(); | |
840 _contentView.update(chunk); | |
841 _handler.content(chunk); // May recurse here | |
842 // TODO adjust the _buffer to keep unconsumed content | |
843 return 1; | |
844 | |
845 case STATE_CONTENT: | |
846 { | |
847 long remaining=_contentLength - _contentPosition; | |
848 if (remaining == 0) | |
849 { | |
850 _state=_persistent?STATE_END:STATE_SEEKING_EOF; | |
851 _handler.messageComplete(_contentPosition); | |
852 return 1; | |
853 } | |
854 | |
855 if (length > remaining) | |
856 { | |
857 // We can cast reamining to an int as we know that it is smaller than | |
858 // or equal to length which is already an int. | |
859 length=(int)remaining; | |
860 } | |
861 | |
862 chunk=_buffer.get(length); | |
863 _contentPosition += chunk.length(); | |
864 _contentView.update(chunk); | |
865 _handler.content(chunk); // May recurse here | |
866 | |
867 if(_contentPosition == _contentLength) | |
868 { | |
869 _state=_persistent?STATE_END:STATE_SEEKING_EOF; | |
870 _handler.messageComplete(_contentPosition); | |
871 } | |
872 // TODO adjust the _buffer to keep unconsumed content | |
873 return 1; | |
874 } | |
875 | |
876 case STATE_CHUNKED_CONTENT: | |
877 { | |
878 ch=_buffer.peek(); | |
879 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) | |
880 _eol=_buffer.get(); | |
881 else if (ch <= HttpTokens.SPACE) | |
882 _buffer.get(); | |
883 else | |
884 { | |
885 _chunkLength=0; | |
886 _chunkPosition=0; | |
887 _state=STATE_CHUNK_SIZE; | |
888 } | |
889 break; | |
890 } | |
891 | |
892 case STATE_CHUNK_SIZE: | |
893 { | |
894 ch=_buffer.get(); | |
895 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) | |
896 { | |
897 _eol=ch; | |
898 | |
899 if (_chunkLength == 0) | |
900 { | |
901 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) | |
902 _eol=_buffer.get(); | |
903 _state=_persistent?STATE_END:STATE_SEEKING_EOF; | |
904 _handler.messageComplete(_contentPosition); | |
905 return 1; | |
906 } | |
907 else | |
908 _state=STATE_CHUNK; | |
909 } | |
910 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON) | |
911 _state=STATE_CHUNK_PARAMS; | |
912 else if (ch >= '0' && ch <= '9') | |
913 _chunkLength=_chunkLength * 16 + (ch - '0'); | |
914 else if (ch >= 'a' && ch <= 'f') | |
915 _chunkLength=_chunkLength * 16 + (10 + ch - 'a'); | |
916 else if (ch >= 'A' && ch <= 'F') | |
917 _chunkLength=_chunkLength * 16 + (10 + ch - 'A'); | |
918 else | |
919 throw new IOException("bad chunk char: " + ch); | |
920 break; | |
921 } | |
922 | |
923 case STATE_CHUNK_PARAMS: | |
924 { | |
925 ch=_buffer.get(); | |
926 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) | |
927 { | |
928 _eol=ch; | |
929 if (_chunkLength == 0) | |
930 { | |
931 if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) | |
932 _eol=_buffer.get(); | |
933 _state=_persistent?STATE_END:STATE_SEEKING_EOF; | |
934 _handler.messageComplete(_contentPosition); | |
935 return 1; | |
936 } | |
937 else | |
938 _state=STATE_CHUNK; | |
939 } | |
940 break; | |
941 } | |
942 | |
943 case STATE_CHUNK: | |
944 { | |
945 int remaining=_chunkLength - _chunkPosition; | |
946 if (remaining == 0) | |
947 { | |
948 _state=STATE_CHUNKED_CONTENT; | |
949 break; | |
950 } | |
951 else if (length > remaining) | |
952 length=remaining; | |
953 chunk=_buffer.get(length); | |
954 _contentPosition += chunk.length(); | |
955 _chunkPosition += chunk.length(); | |
956 _contentView.update(chunk); | |
957 _handler.content(chunk); // May recurse here | |
958 // TODO adjust the _buffer to keep unconsumed content | |
959 return 1; | |
960 } | |
961 | |
962 case STATE_SEEKING_EOF: | |
963 { | |
964 // Close if there is more data than CRLF | |
965 if (_buffer.length()>2) | |
966 { | |
967 _state=STATE_END; | |
968 _endp.close(); | |
969 } | |
970 else | |
971 { | |
972 // or if the data is not white space | |
973 while (_buffer.length()>0) | |
974 if (!Character.isWhitespace(_buffer.get())) | |
975 { | |
976 _state=STATE_END; | |
977 _endp.close(); | |
978 _buffer.clear(); | |
979 } | |
980 } | |
981 | |
982 _buffer.clear(); | |
983 break; | |
984 } | |
985 } | |
986 | |
987 length=_buffer.length(); | |
988 } | |
989 | |
990 return progress; | |
991 } | |
992 catch(HttpException e) | |
993 { | |
994 _persistent=false; | |
995 _state=STATE_SEEKING_EOF; | |
996 throw e; | |
997 } | |
998 } | |
999 | |
1000 /* ------------------------------------------------------------------------------- */ | |
1001 /** fill the buffers from the endpoint | |
1002 * | |
1003 */ | |
1004 protected int fill() throws IOException | |
1005 { | |
1006 // Do we have a buffer? | |
1007 if (_buffer==null) | |
1008 _buffer=getHeaderBuffer(); | |
1009 | |
1010 // Is there unconsumed content in body buffer | |
1011 if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent()) | |
1012 { | |
1013 _buffer=_body; | |
1014 return _buffer.length(); | |
1015 } | |
1016 | |
1017 // Shall we switch to a body buffer? | |
1018 if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null)) | |
1019 { | |
1020 if (_body==null) | |
1021 _body=_buffers.getBuffer(); | |
1022 _buffer=_body; | |
1023 } | |
1024 | |
1025 // Do we have somewhere to fill from? | |
1026 if (_endp != null ) | |
1027 { | |
1028 // Shall we compact the body? | |
1029 if (_buffer==_body || _state>STATE_END) | |
1030 { | |
1031 _buffer.compact(); | |
1032 } | |
1033 | |
1034 // Are we full? | |
1035 if (_buffer.space() == 0) | |
1036 { | |
1037 LOG.warn("HttpParser Full for {} ",_endp); | |
1038 _buffer.clear(); | |
1039 throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "Request Entity Too Large: "+(_buffer==_body?"body":"head")); | |
1040 } | |
1041 | |
1042 try | |
1043 { | |
1044 int filled = _endp.fill(_buffer); | |
1045 return filled; | |
1046 } | |
1047 catch(IOException e) | |
1048 { | |
1049 LOG.debug(e); | |
1050 throw (e instanceof EofException) ? e:new EofException(e); | |
1051 } | |
1052 } | |
1053 | |
1054 return -1; | |
1055 } | |
1056 | |
1057 /* ------------------------------------------------------------------------------- */ | |
1058 public void reset() | |
1059 { | |
1060 // reset state | |
1061 _contentView.setGetIndex(_contentView.putIndex()); | |
1062 _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF); | |
1063 _contentLength=HttpTokens.UNKNOWN_CONTENT; | |
1064 _contentPosition=0; | |
1065 _length=0; | |
1066 _responseStatus=0; | |
1067 | |
1068 // Consume LF if CRLF | |
1069 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED) | |
1070 _eol=_buffer.get(); | |
1071 | |
1072 if (_body!=null && _body.hasContent()) | |
1073 { | |
1074 // There is content in the body after the end of the request. | |
1075 // This is probably a pipelined header of the next request, so we need to | |
1076 // copy it to the header buffer. | |
1077 if (_header==null) | |
1078 getHeaderBuffer(); | |
1079 else | |
1080 { | |
1081 _header.setMarkIndex(-1); | |
1082 _header.compact(); | |
1083 } | |
1084 int take=_header.space(); | |
1085 if (take>_body.length()) | |
1086 take=_body.length(); | |
1087 _body.peek(_body.getIndex(),take); | |
1088 _body.skip(_header.put(_body.peek(_body.getIndex(),take))); | |
1089 } | |
1090 | |
1091 if (_header!=null) | |
1092 { | |
1093 _header.setMarkIndex(-1); | |
1094 _header.compact(); | |
1095 } | |
1096 if (_body!=null) | |
1097 _body.setMarkIndex(-1); | |
1098 | |
1099 _buffer=_header; | |
1100 returnBuffers(); | |
1101 } | |
1102 | |
1103 | |
1104 /* ------------------------------------------------------------------------------- */ | |
1105 public void returnBuffers() | |
1106 { | |
1107 if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null) | |
1108 { | |
1109 if (_buffer==_body) | |
1110 _buffer=_header; | |
1111 if (_buffers!=null) | |
1112 _buffers.returnBuffer(_body); | |
1113 _body=null; | |
1114 } | |
1115 | |
1116 if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null) | |
1117 { | |
1118 if (_buffer==_header) | |
1119 _buffer=null; | |
1120 _buffers.returnBuffer(_header); | |
1121 _header=null; | |
1122 } | |
1123 } | |
1124 | |
1125 /* ------------------------------------------------------------------------------- */ | |
1126 public void setState(int state) | |
1127 { | |
1128 this._state=state; | |
1129 _contentLength=HttpTokens.UNKNOWN_CONTENT; | |
1130 } | |
1131 | |
1132 /* ------------------------------------------------------------------------------- */ | |
1133 public String toString(Buffer buf) | |
1134 { | |
1135 return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode(); | |
1136 } | |
1137 | |
1138 /* ------------------------------------------------------------------------------- */ | |
1139 @Override | |
1140 public String toString() | |
1141 { | |
1142 return String.format("%s{s=%d,l=%d,c=%d}", | |
1143 getClass().getSimpleName(), | |
1144 _state, | |
1145 _length, | |
1146 _contentLength); | |
1147 } | |
1148 | |
1149 /* ------------------------------------------------------------ */ | |
1150 public Buffer getHeaderBuffer() | |
1151 { | |
1152 if (_header == null) | |
1153 { | |
1154 _header=_buffers.getHeader(); | |
1155 _tok0.update(_header); | |
1156 _tok1.update(_header); | |
1157 } | |
1158 return _header; | |
1159 } | |
1160 | |
1161 /* ------------------------------------------------------------ */ | |
1162 public Buffer getBodyBuffer() | |
1163 { | |
1164 return _body; | |
1165 } | |
1166 | |
1167 /* ------------------------------------------------------------ */ | |
1168 /** | |
1169 * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used. | |
1170 */ | |
1171 public void setForceContentBuffer(boolean force) | |
1172 { | |
1173 _forceContentBuffer=force; | |
1174 } | |
1175 | |
1176 /* ------------------------------------------------------------ */ | |
1177 public Buffer blockForContent(long maxIdleTime) throws IOException | |
1178 { | |
1179 if (_contentView.length()>0) | |
1180 return _contentView; | |
1181 | |
1182 if (getState() <= STATE_END || isState(STATE_SEEKING_EOF)) | |
1183 return null; | |
1184 | |
1185 try | |
1186 { | |
1187 parseNext(); | |
1188 | |
1189 // parse until some progress is made (or IOException thrown for timeout) | |
1190 while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen()) | |
1191 { | |
1192 if (!_endp.isBlocking()) | |
1193 { | |
1194 if (parseNext()>0) | |
1195 continue; | |
1196 | |
1197 if (!_endp.blockReadable(maxIdleTime)) | |
1198 { | |
1199 _endp.close(); | |
1200 throw new EofException("timeout"); | |
1201 } | |
1202 } | |
1203 | |
1204 parseNext(); | |
1205 } | |
1206 } | |
1207 catch(IOException e) | |
1208 { | |
1209 // TODO is this needed? | |
1210 _endp.close(); | |
1211 throw e; | |
1212 } | |
1213 | |
1214 return _contentView.length()>0?_contentView:null; | |
1215 } | |
1216 | |
1217 /* ------------------------------------------------------------ */ | |
1218 /* (non-Javadoc) | |
1219 * @see java.io.InputStream#available() | |
1220 */ | |
1221 public int available() throws IOException | |
1222 { | |
1223 if (_contentView!=null && _contentView.length()>0) | |
1224 return _contentView.length(); | |
1225 | |
1226 if (_endp.isBlocking()) | |
1227 { | |
1228 if (_state>0 && _endp instanceof StreamEndPoint) | |
1229 return ((StreamEndPoint)_endp).getInputStream().available()>0?1:0; | |
1230 | |
1231 return 0; | |
1232 } | |
1233 | |
1234 parseNext(); | |
1235 return _contentView==null?0:_contentView.length(); | |
1236 } | |
1237 | |
1238 /* ------------------------------------------------------------ */ | |
1239 /* ------------------------------------------------------------ */ | |
1240 /* ------------------------------------------------------------ */ | |
1241 public static abstract class EventHandler | |
1242 { | |
1243 public abstract void content(Buffer ref) throws IOException; | |
1244 | |
1245 public void headerComplete() throws IOException | |
1246 { | |
1247 } | |
1248 | |
1249 public void messageComplete(long contentLength) throws IOException | |
1250 { | |
1251 } | |
1252 | |
1253 /** | |
1254 * This is the method called by parser when a HTTP Header name and value is found | |
1255 */ | |
1256 public void parsedHeader(Buffer name, Buffer value) throws IOException | |
1257 { | |
1258 } | |
1259 | |
1260 /** | |
1261 * This is the method called by parser when the HTTP request line is parsed | |
1262 */ | |
1263 public abstract void startRequest(Buffer method, Buffer url, Buffer version) | |
1264 throws IOException; | |
1265 | |
1266 /** | |
1267 * This is the method called by parser when the HTTP request line is parsed | |
1268 */ | |
1269 public abstract void startResponse(Buffer version, int status, Buffer reason) | |
1270 throws IOException; | |
1271 | |
1272 public void earlyEOF() | |
1273 {} | |
1274 } | |
1275 | |
1276 | |
1277 | |
1278 | |
1279 } |