1 //
2 // ========================================================================
3 // Copyright (c) 1995-2017 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 static org.eclipse.jetty.http.HttpTokens.*;
22
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
25
26 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
27 import org.eclipse.jetty.util.ArrayTernaryTrie;
28 import org.eclipse.jetty.util.ArrayTrie;
29 import org.eclipse.jetty.util.BufferUtil;
30 import org.eclipse.jetty.util.HostPort;
31 import org.eclipse.jetty.util.StringUtil;
32 import org.eclipse.jetty.util.Trie;
33 import org.eclipse.jetty.util.TypeUtil;
34 import org.eclipse.jetty.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36
37
38 /* ------------------------------------------------------------ */
39 /** A Parser for HTTP 0.9, 1.0 and 1.1
40 * <p>
41 * This parser parses HTTP client and server messages from buffers
42 * passed in the {@link #parseNext(ByteBuffer)} method. The parsed
43 * elements of the HTTP message are passed as event calls to the
44 * {@link HttpHandler} instance the parser is constructed with.
45 * If the passed handler is a {@link RequestHandler} then server side
46 * parsing is performed and if it is a {@link ResponseHandler}, then
47 * client side parsing is done.
48 * </p>
49 * <p>
50 * The contract of the {@link HttpHandler} API is that if a call returns
51 * true then the call to {@link #parseNext(ByteBuffer)} will return as
52 * soon as possible also with a true response. Typically this indicates
53 * that the parsing has reached a stage where the caller should process
54 * the events accumulated by the handler. It is the preferred calling
55 * style that handling such as calling a servlet to process a request,
56 * should be done after a true return from {@link #parseNext(ByteBuffer)}
57 * rather than from within the scope of a call like
58 * {@link RequestHandler#messageComplete()}
59 * </p>
60 * <p>
61 * For performance, the parse is heavily dependent on the
62 * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a
63 * single pass for both the structure ( : and CRLF ) and semantic (which
64 * header and value) of a header. Specifically the static {@link HttpHeader#CACHE}
65 * is used to lookup common combinations of headers and values
66 * (eg. "Connection: close"), or just header names (eg. "Connection:" ).
67 * For headers who's value is not known statically (eg. Host, COOKIE) then a
68 * per parser dynamic Trie of {@link HttpFields} from previous parsed messages
69 * is used to help the parsing of subsequent messages.
70 * </p>
71 * <p>
72 * If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
73 * then the parser will strictly pass on the exact strings received for methods and header
74 * fields. Otherwise a fast case insensitive string lookup is used that may alter the
75 * case of the method and/or headers
76 * </p>
77 */
78 public class HttpParser
79 {
80 public static final Logger LOG = Log.getLogger(HttpParser.class);
81 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
82 public final static int INITIAL_URI_LENGTH=256;
83
84 /**
85 * Cache of common {@link HttpField}s including: <UL>
86 * <LI>Common static combinations such as:<UL>
87 * <li>Connection: close
88 * <li>Accept-Encoding: gzip
89 * <li>Content-Length: 0
90 * </ul>
91 * <li>Combinations of Content-Type header for common mime types by common charsets
92 * <li>Most common headers with null values so that a lookup will at least
93 * determine the header name even if the name:value combination is not cached
94 * </ul>
95 */
96 public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
97
98 // States
99 public enum State
100 {
101 START,
102 METHOD,
103 RESPONSE_VERSION,
104 SPACE1,
105 STATUS,
106 URI,
107 SPACE2,
108 REQUEST_VERSION,
109 REASON,
110 PROXY,
111 HEADER,
112 HEADER_IN_NAME,
113 HEADER_VALUE,
114 HEADER_IN_VALUE,
115 CONTENT,
116 EOF_CONTENT,
117 CHUNKED_CONTENT,
118 CHUNK_SIZE,
119 CHUNK_PARAMS,
120 CHUNK,
121 CHUNK_END,
122 END,
123 CLOSED
124 }
125
126 private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction
127 private final HttpHandler<ByteBuffer> _handler;
128 private final RequestHandler<ByteBuffer> _requestHandler;
129 private final ResponseHandler<ByteBuffer> _responseHandler;
130 private final int _maxHeaderBytes;
131 private final boolean _strict;
132 private HttpField _field;
133 private HttpHeader _header;
134 private String _headerString;
135 private HttpHeaderValue _value;
136 private String _valueString;
137 private int _responseStatus;
138 private int _headerBytes;
139 private boolean _host;
140
141 /* ------------------------------------------------------------------------------- */
142 private volatile State _state=State.START;
143 private volatile boolean _eof;
144 private volatile boolean _closed;
145 private HttpMethod _method;
146 private String _methodString;
147 private HttpVersion _version;
148 private ByteBuffer _uri=ByteBuffer.allocate(INITIAL_URI_LENGTH); // Tune?
149 private EndOfContent _endOfContent;
150 private long _contentLength;
151 private long _contentPosition;
152 private int _chunkLength;
153 private int _chunkPosition;
154 private boolean _headResponse;
155 private boolean _cr;
156 private ByteBuffer _contentChunk;
157 private Trie<HttpField> _connectionFields;
158
159 private int _length;
160 private final StringBuilder _string=new StringBuilder();
161
162 static
163 {
164 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
165 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
166 CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
167 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
168 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
169 CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
170 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
171 CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
172 CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
173 CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
174 CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
175 CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
176 CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
177 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
178 CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
179 CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
180 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
181 CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
182 CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
183 CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
184
185 // Add common Content types as fields
186 for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"})
187 {
188 HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type);
189 CACHE.put(field);
190
191 for (String charset : new String[]{"UTF-8","ISO-8859-1"})
192 {
193 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
194 CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset));
195 }
196 }
197
198 // Add headers with null values so HttpParser can avoid looking up name again for unknown values
199 for (HttpHeader h:HttpHeader.values())
200 if (!CACHE.put(new HttpField(h,(String)null)))
201 throw new IllegalStateException("CACHE FULL");
202 // Add some more common headers
203 CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
204 CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
205 CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
206 CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
207 CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
208 }
209
210 /* ------------------------------------------------------------------------------- */
211 public HttpParser(RequestHandler<ByteBuffer> handler)
212 {
213 this(handler,-1,__STRICT);
214 }
215
216 /* ------------------------------------------------------------------------------- */
217 public HttpParser(ResponseHandler<ByteBuffer> handler)
218 {
219 this(handler,-1,__STRICT);
220 }
221
222 /* ------------------------------------------------------------------------------- */
223 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
224 {
225 this(handler,maxHeaderBytes,__STRICT);
226 }
227
228 /* ------------------------------------------------------------------------------- */
229 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
230 {
231 this(handler,maxHeaderBytes,__STRICT);
232 }
233
234 /* ------------------------------------------------------------------------------- */
235 public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
236 {
237 _handler=handler;
238 _requestHandler=handler;
239 _responseHandler=null;
240 _maxHeaderBytes=maxHeaderBytes;
241 _strict=strict;
242 }
243
244 /* ------------------------------------------------------------------------------- */
245 public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
246 {
247 _handler=handler;
248 _requestHandler=null;
249 _responseHandler=handler;
250 _maxHeaderBytes=maxHeaderBytes;
251 _strict=strict;
252 }
253
254 /* ------------------------------------------------------------------------------- */
255 public long getContentLength()
256 {
257 return _contentLength;
258 }
259
260 /* ------------------------------------------------------------ */
261 public long getContentRead()
262 {
263 return _contentPosition;
264 }
265
266 /* ------------------------------------------------------------ */
267 /** Set if a HEAD response is expected
268 * @param head
269 */
270 public void setHeadResponse(boolean head)
271 {
272 _headResponse=head;
273 }
274
275 /* ------------------------------------------------------------------------------- */
276 protected void setResponseStatus(int status)
277 {
278 _responseStatus=status;
279 }
280
281 /* ------------------------------------------------------------------------------- */
282 public State getState()
283 {
284 return _state;
285 }
286
287 /* ------------------------------------------------------------------------------- */
288 public boolean inContentState()
289 {
290 return _state.ordinal()>=State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal();
291 }
292
293 /* ------------------------------------------------------------------------------- */
294 public boolean inHeaderState()
295 {
296 return _state.ordinal() < State.CONTENT.ordinal();
297 }
298
299 /* ------------------------------------------------------------------------------- */
300 public boolean isChunking()
301 {
302 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
303 }
304
305 /* ------------------------------------------------------------ */
306 public boolean isStart()
307 {
308 return isState(State.START);
309 }
310
311 /* ------------------------------------------------------------ */
312 public boolean isClosed()
313 {
314 return isState(State.CLOSED);
315 }
316
317 /* ------------------------------------------------------------ */
318 public boolean isIdle()
319 {
320 return isState(State.START)||isState(State.END)||isState(State.CLOSED);
321 }
322
323 /* ------------------------------------------------------------ */
324 public boolean isComplete()
325 {
326 return isState(State.END)||isState(State.CLOSED);
327 }
328
329 /* ------------------------------------------------------------------------------- */
330 public boolean isState(State state)
331 {
332 return _state == state;
333 }
334
335 /* ------------------------------------------------------------------------------- */
336 @SuppressWarnings("serial")
337 private static class BadMessageException extends RuntimeException
338 {
339 private final int _code;
340
341 private BadMessageException()
342 {
343 this(400,null);
344 }
345
346 private BadMessageException(int code)
347 {
348 this(code,null);
349 }
350
351 private BadMessageException(String message)
352 {
353 this(400,message);
354 }
355
356 private BadMessageException(int code,String message)
357 {
358 super(message);
359 _code=code;
360 }
361 }
362
363 /* ------------------------------------------------------------------------------- */
364 private byte next(ByteBuffer buffer)
365 {
366 byte ch = buffer.get();
367
368 if (_cr)
369 {
370 if (ch!=LINE_FEED)
371 throw new BadMessageException("Bad EOL");
372 _cr=false;
373 return ch;
374 }
375
376 if (ch>=0 && ch<SPACE)
377 {
378 if (ch==CARRIAGE_RETURN)
379 {
380 if (buffer.hasRemaining())
381 {
382 if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
383 _headerBytes++;
384 ch=buffer.get();
385 if (ch!=LINE_FEED)
386 throw new BadMessageException("Bad EOL");
387 }
388 else
389 {
390 _cr=true;
391 // Can return 0 here to indicate the need for more characters,
392 // because a real 0 in the buffer would cause a BadMessage below
393 return 0;
394 }
395 }
396 // Only LF or TAB acceptable special characters
397 else if (!(ch==LINE_FEED || ch==TAB))
398 throw new IllegalCharacterException(_state,ch,buffer);
399 }
400
401 return ch;
402 }
403
404 /* ------------------------------------------------------------------------------- */
405 /* Quick lookahead for the start state looking for a request method or a HTTP version,
406 * otherwise skip white space until something else to parse.
407 */
408 private boolean quickStart(ByteBuffer buffer)
409 {
410 if (_requestHandler!=null)
411 {
412 _method = HttpMethod.lookAheadGet(buffer);
413 if (_method!=null)
414 {
415 _methodString = _method.asString();
416 buffer.position(buffer.position()+_methodString.length()+1);
417
418 setState(State.SPACE1);
419 return false;
420 }
421 }
422 else if (_responseHandler!=null)
423 {
424 _version = HttpVersion.lookAheadGet(buffer);
425 if (_version!=null)
426 {
427 buffer.position(buffer.position()+_version.asString().length()+1);
428 setState(State.SPACE1);
429 return false;
430 }
431 }
432
433 // Quick start look
434 while (_state==State.START && buffer.hasRemaining())
435 {
436 int ch=next(buffer);
437
438 if (ch > SPACE)
439 {
440 _string.setLength(0);
441 _string.append((char)ch);
442 setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
443 return false;
444 }
445 else if (ch==0)
446 break;
447 else if (ch<0)
448 throw new BadMessageException();
449
450 // count this white space as a header byte to avoid DOS
451 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
452 {
453 LOG.warn("padding is too large >"+_maxHeaderBytes);
454 throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
455 }
456 }
457 return false;
458 }
459
460 /* ------------------------------------------------------------------------------- */
461 private void setString(String s)
462 {
463 _string.setLength(0);
464 _string.append(s);
465 _length=s.length();
466 }
467
468 /* ------------------------------------------------------------------------------- */
469 private String takeString()
470 {
471 _string.setLength(_length);
472 String s =_string.toString();
473 _string.setLength(0);
474 _length=-1;
475 return s;
476 }
477
478 /* ------------------------------------------------------------------------------- */
479 /* Parse a request or response line
480 */
481 private boolean parseLine(ByteBuffer buffer)
482 {
483 boolean handle=false;
484
485 // Process headers
486 while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !handle)
487 {
488 // process each character
489 byte ch=next(buffer);
490 if (ch==0)
491 break;
492
493 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
494 {
495 if (_state==State.URI)
496 {
497 LOG.warn("URI is too large >"+_maxHeaderBytes);
498 throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
499 }
500 else
501 {
502 if (_requestHandler!=null)
503 LOG.warn("request is too large >"+_maxHeaderBytes);
504 else
505 LOG.warn("response is too large >"+_maxHeaderBytes);
506 throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
507 }
508 }
509
510 switch (_state)
511 {
512 case METHOD:
513 if (ch == SPACE)
514 {
515 _length=_string.length();
516 _methodString=takeString();
517 HttpMethod method=HttpMethod.CACHE.get(_methodString);
518 if (method!=null && !_strict)
519 _methodString=method.asString();
520 setState(State.SPACE1);
521 }
522 else if (ch < SPACE)
523 {
524 if (ch==LINE_FEED)
525 throw new BadMessageException("No URI");
526 else
527 throw new IllegalCharacterException(_state,ch,buffer);
528 }
529 else
530 _string.append((char)ch);
531 break;
532
533 case RESPONSE_VERSION:
534 if (ch == HttpTokens.SPACE)
535 {
536 _length=_string.length();
537 String version=takeString();
538 _version=HttpVersion.CACHE.get(version);
539 if (_version==null)
540 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
541 setState(State.SPACE1);
542 }
543 else if (ch < HttpTokens.SPACE)
544 throw new IllegalCharacterException(_state,ch,buffer);
545 else
546 _string.append((char)ch);
547 break;
548
549 case SPACE1:
550 if (ch > HttpTokens.SPACE || ch<0)
551 {
552 if (_responseHandler!=null)
553 {
554 setState(State.STATUS);
555 setResponseStatus(ch-'0');
556 }
557 else
558 {
559 _uri.clear();
560 setState(State.URI);
561 // quick scan for space or EoBuffer
562 if (buffer.hasArray())
563 {
564 byte[] array=buffer.array();
565 int p=buffer.arrayOffset()+buffer.position();
566 int l=buffer.arrayOffset()+buffer.limit();
567 int i=p;
568 while (i<l && array[i]>HttpTokens.SPACE)
569 i++;
570
571 int len=i-p;
572 _headerBytes+=len;
573
574 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
575 {
576 LOG.warn("URI is too large >"+_maxHeaderBytes);
577 throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
578 }
579 if (_uri.remaining()<=len)
580 {
581 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()+2*len);
582 _uri.flip();
583 uri.put(_uri);
584 _uri=uri;
585 }
586 _uri.put(array,p-1,len+1);
587 buffer.position(i-buffer.arrayOffset());
588 }
589 else
590 _uri.put(ch);
591 }
592 }
593 else if (ch < HttpTokens.SPACE)
594 {
595 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
596 }
597 break;
598
599 case STATUS:
600 if (ch == HttpTokens.SPACE)
601 {
602 setState(State.SPACE2);
603 }
604 else if (ch>='0' && ch<='9')
605 {
606 _responseStatus=_responseStatus*10+(ch-'0');
607 }
608 else if (ch < HttpTokens.SPACE && ch>=0)
609 {
610 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
611 setState(State.HEADER);
612 }
613 else
614 {
615 throw new BadMessageException();
616 }
617 break;
618
619 case URI:
620 if (ch == HttpTokens.SPACE)
621 {
622 setState(State.SPACE2);
623 }
624 else if (ch < HttpTokens.SPACE && ch>=0)
625 {
626 // HTTP/0.9
627 _uri.flip();
628 handle=_requestHandler.startRequest(_method,_methodString,_uri,null)||handle;
629 setState(State.END);
630 BufferUtil.clear(buffer);
631 handle=_handler.headerComplete()||handle;
632 handle=_handler.messageComplete()||handle;
633 return handle;
634 }
635 else
636 {
637 if (!_uri.hasRemaining())
638 {
639 ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()*2);
640 _uri.flip();
641 uri.put(_uri);
642 _uri=uri;
643 }
644 _uri.put(ch);
645 }
646 break;
647
648 case SPACE2:
649 if (ch > HttpTokens.SPACE)
650 {
651 _string.setLength(0);
652 _string.append((char)ch);
653 if (_responseHandler!=null)
654 {
655 _length=1;
656 setState(State.REASON);
657 }
658 else
659 {
660 setState(State.REQUEST_VERSION);
661
662 // try quick look ahead for HTTP Version
663 HttpVersion version;
664 if (buffer.position()>0 && buffer.hasArray())
665 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
666 else
667 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
668 if (version==null)
669 {
670 if (_method==HttpMethod.PROXY)
671 {
672 if (!(_requestHandler instanceof ProxyHandler))
673 throw new BadMessageException();
674
675 _uri.flip();
676 String protocol=BufferUtil.toString(_uri);
677 // This is the proxy protocol, so we can assume entire first line is in buffer else 400
678 buffer.position(buffer.position()-1);
679 String sAddr = getProxyField(buffer);
680 String dAddr = getProxyField(buffer);
681 int sPort = BufferUtil.takeInt(buffer);
682 next(buffer);
683 int dPort = BufferUtil.takeInt(buffer);
684 next(buffer);
685 _state=State.START;
686 ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
687 return false;
688 }
689 }
690 else
691 {
692 int pos = buffer.position()+version.asString().length()-1;
693 if (pos<buffer.limit())
694 {
695 byte n=buffer.get(pos);
696 if (n==HttpTokens.CARRIAGE_RETURN)
697 {
698 _cr=true;
699 _version=version;
700 _string.setLength(0);
701 buffer.position(pos+1);
702 }
703 else if (n==HttpTokens.LINE_FEED)
704 {
705 _version=version;
706 _string.setLength(0);
707 buffer.position(pos);
708 }
709 }
710 }
711 }
712 }
713 else if (ch == HttpTokens.LINE_FEED)
714 {
715 if (_responseHandler!=null)
716 {
717 handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
718 setState(State.HEADER);
719 }
720 else
721 {
722 // HTTP/0.9
723 _uri.flip();
724 handle=_requestHandler.startRequest(_method,_methodString,_uri, null)||handle;
725 setState(State.END);
726 BufferUtil.clear(buffer);
727 handle=_handler.headerComplete()||handle;
728 handle=_handler.messageComplete()||handle;
729 return handle;
730 }
731 }
732 else if (ch<0)
733 throw new BadMessageException();
734 break;
735
736 case REQUEST_VERSION:
737 if (ch == HttpTokens.LINE_FEED)
738 {
739 if (_version==null)
740 {
741 _length=_string.length();
742 _version=HttpVersion.CACHE.get(takeString());
743 }
744 if (_version==null)
745 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
746
747 // Should we try to cache header fields?
748 if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
749 {
750 int header_cache = _handler.getHeaderCacheSize();
751 _connectionFields=new ArrayTernaryTrie<>(header_cache);
752 }
753
754 setState(State.HEADER);
755 _uri.flip();
756 handle=_requestHandler.startRequest(_method,_methodString,_uri, _version)||handle;
757 continue;
758 }
759 else if (ch>=HttpTokens.SPACE)
760 _string.append((char)ch);
761 else
762 throw new BadMessageException();
763
764 break;
765
766 case REASON:
767 if (ch == HttpTokens.LINE_FEED)
768 {
769 String reason=takeString();
770
771 setState(State.HEADER);
772 handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
773 continue;
774 }
775 else if (ch>=HttpTokens.SPACE)
776 {
777 _string.append((char)ch);
778 if (ch!=' '&&ch!='\t')
779 _length=_string.length();
780 }
781 else
782 throw new BadMessageException();
783 break;
784
785 default:
786 throw new IllegalStateException(_state.toString());
787
788 }
789 }
790
791 return handle;
792 }
793
794 private boolean handleKnownHeaders(ByteBuffer buffer)
795 {
796 boolean add_to_connection_trie=false;
797 switch (_header)
798 {
799 case CONTENT_LENGTH:
800 if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
801 {
802 try
803 {
804 _contentLength=Long.parseLong(_valueString);
805 }
806 catch(NumberFormatException e)
807 {
808 LOG.ignore(e);
809 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
810 }
811 if (_contentLength <= 0)
812 _endOfContent=EndOfContent.NO_CONTENT;
813 else
814 _endOfContent=EndOfContent.CONTENT_LENGTH;
815 }
816 break;
817
818 case TRANSFER_ENCODING:
819 if (_value==HttpHeaderValue.CHUNKED)
820 _endOfContent=EndOfContent.CHUNKED_CONTENT;
821 else
822 {
823 if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
824 _endOfContent=EndOfContent.CHUNKED_CONTENT;
825 else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
826 {
827 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
828 }
829 }
830 break;
831
832 case HOST:
833 add_to_connection_trie=_connectionFields!=null && _field==null;
834 _host=true;
835
836 if (_valueString==null || _valueString.length()==0)
837 {
838 break;
839 }
840
841 try
842 {
843 HostPort authority = new HostPort(_valueString);
844 if (_requestHandler!=null)
845 _requestHandler.parsedHostHeader(authority.getHost(),authority.getPort());
846 }
847 catch (final Exception e)
848 {
849 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header")
850 {{initCause(e);}};
851 }
852
853 break;
854
855 case CONNECTION:
856 // Don't cache if not persistent
857 if (_valueString!=null && _valueString.contains("close"))
858 {
859 _closed=true;
860 _connectionFields=null;
861 }
862 break;
863
864 case AUTHORIZATION:
865 case ACCEPT:
866 case ACCEPT_CHARSET:
867 case ACCEPT_ENCODING:
868 case ACCEPT_LANGUAGE:
869 case COOKIE:
870 case CACHE_CONTROL:
871 case USER_AGENT:
872 add_to_connection_trie=_connectionFields!=null && _field==null;
873 break;
874
875 default: break;
876 }
877
878 if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
879 {
880 _field=new HttpField(_header,_valueString);
881 _connectionFields.put(_field);
882 }
883
884 return false;
885 }
886
887
888 /* ------------------------------------------------------------------------------- */
889 /*
890 * Parse the message headers and return true if the handler has signaled for a return
891 */
892 protected boolean parseHeaders(ByteBuffer buffer)
893 {
894 boolean handle=false;
895
896 // Process headers
897 while (_state.ordinal()<State.CONTENT.ordinal() && buffer.hasRemaining() && !handle)
898 {
899 // process each character
900 byte ch=next(buffer);
901 if (ch==0)
902 break;
903
904 if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
905 {
906 LOG.warn("Header is too large >"+_maxHeaderBytes);
907 throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
908 }
909
910 switch (_state)
911 {
912 case HEADER:
913 switch(ch)
914 {
915 case HttpTokens.COLON:
916 case HttpTokens.SPACE:
917 case HttpTokens.TAB:
918 {
919 // header value without name - continuation?
920 if (_valueString==null)
921 {
922 _string.setLength(0);
923 _length=0;
924 }
925 else
926 {
927 setString(_valueString);
928 _string.append(' ');
929 _length++;
930 _valueString=null;
931 }
932 setState(State.HEADER_VALUE);
933 break;
934 }
935
936 default:
937 {
938 // handler last header if any. Delayed to here just in case there was a continuation line (above)
939 if (_headerString!=null || _valueString!=null)
940 {
941 // Handle known headers
942 if (_header!=null && handleKnownHeaders(buffer))
943 {
944 _headerString=_valueString=null;
945 _header=null;
946 _value=null;
947 _field=null;
948 return true;
949 }
950 handle=_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString))||handle;
951 }
952 _headerString=_valueString=null;
953 _header=null;
954 _value=null;
955 _field=null;
956
957 // now handle the ch
958 if (ch == HttpTokens.LINE_FEED)
959 {
960 _contentPosition=0;
961
962 // End of headers!
963
964 // Was there a required host header?
965 if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
966 {
967 throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
968 }
969
970 // is it a response that cannot have a body?
971 if (_responseHandler !=null && // response
972 (_responseStatus == 304 || // not-modified response
973 _responseStatus == 204 || // no-content response
974 _responseStatus < 200)) // 1xx response
975 _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
976
977 // else if we don't know framing
978 else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
979 {
980 if (_responseStatus == 0 // request
981 || _responseStatus == 304 // not-modified response
982 || _responseStatus == 204 // no-content response
983 || _responseStatus < 200) // 1xx response
984 _endOfContent=EndOfContent.NO_CONTENT;
985 else
986 _endOfContent=EndOfContent.EOF_CONTENT;
987 }
988
989 // How is the message ended?
990 switch (_endOfContent)
991 {
992 case EOF_CONTENT:
993 setState(State.EOF_CONTENT);
994 handle=_handler.headerComplete()||handle;
995 return handle;
996
997 case CHUNKED_CONTENT:
998 setState(State.CHUNKED_CONTENT);
999 handle=_handler.headerComplete()||handle;
1000 return handle;
1001
1002 case NO_CONTENT:
1003 handle=_handler.headerComplete()||handle;
1004 setState(State.END);
1005 handle=_handler.messageComplete()||handle;
1006 return handle;
1007
1008 default:
1009 setState(State.CONTENT);
1010 handle=_handler.headerComplete()||handle;
1011 return handle;
1012 }
1013 }
1014 else if (ch<=HttpTokens.SPACE)
1015 throw new BadMessageException();
1016 else
1017 {
1018 if (buffer.hasRemaining())
1019 {
1020 // Try a look ahead for the known header name and value.
1021 HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
1022 if (field==null)
1023 field=CACHE.getBest(buffer,-1,buffer.remaining());
1024
1025 if (field!=null)
1026 {
1027 final String n;
1028 final String v;
1029
1030 if (_strict)
1031 {
1032 // Have to get the fields exactly from the buffer to match case
1033 String fn=field.getName();
1034 String fv=field.getValue();
1035 n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
1036 if (fv==null)
1037 v=null;
1038 else
1039 {
1040 v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
1041 field=new HttpField(field.getHeader(),n,v);
1042 }
1043 }
1044 else
1045 {
1046 n=field.getName();
1047 v=field.getValue();
1048 }
1049
1050 _header=field.getHeader();
1051 _headerString=n;
1052
1053 if (v==null)
1054 {
1055 // Header only
1056 setState(State.HEADER_VALUE);
1057 _string.setLength(0);
1058 _length=0;
1059 buffer.position(buffer.position()+n.length()+1);
1060 break;
1061 }
1062 else
1063 {
1064 // Header and value
1065 int pos=buffer.position()+n.length()+v.length()+1;
1066 byte b=buffer.get(pos);
1067
1068 if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
1069 {
1070 _field=field;
1071 _valueString=v;
1072 setState(State.HEADER_IN_VALUE);
1073
1074 if (b==HttpTokens.CARRIAGE_RETURN)
1075 {
1076 _cr=true;
1077 buffer.position(pos+1);
1078 }
1079 else
1080 buffer.position(pos);
1081 break;
1082 }
1083 else
1084 {
1085 setState(State.HEADER_IN_VALUE);
1086 setString(v);
1087 buffer.position(pos);
1088 break;
1089 }
1090 }
1091 }
1092 }
1093
1094 // New header
1095 setState(State.HEADER_IN_NAME);
1096 _string.setLength(0);
1097 _string.append((char)ch);
1098 _length=1;
1099 }
1100 }
1101 }
1102 break;
1103
1104 case HEADER_IN_NAME:
1105 if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
1106 {
1107 if (_headerString==null)
1108 {
1109 _headerString=takeString();
1110 _header=HttpHeader.CACHE.get(_headerString);
1111 }
1112 _length=-1;
1113
1114 setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
1115 break;
1116 }
1117
1118 if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
1119 {
1120 if (_header!=null)
1121 {
1122 setString(_header.asString());
1123 _header=null;
1124 _headerString=null;
1125 }
1126
1127 _string.append((char)ch);
1128 if (ch>HttpTokens.SPACE)
1129 _length=_string.length();
1130 break;
1131 }
1132
1133 throw new IllegalCharacterException(_state,ch,buffer);
1134
1135 case HEADER_VALUE:
1136 if (ch>HttpTokens.SPACE || ch<0)
1137 {
1138 _string.append((char)(0xff&ch));
1139 _length=_string.length();
1140 setState(State.HEADER_IN_VALUE);
1141 break;
1142 }
1143
1144 if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
1145 break;
1146
1147 if (ch==HttpTokens.LINE_FEED)
1148 {
1149 if (_length > 0)
1150 {
1151 _value=null;
1152 _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
1153 }
1154 setState(State.HEADER);
1155 break;
1156 }
1157
1158 throw new IllegalCharacterException(_state,ch,buffer);
1159
1160 case HEADER_IN_VALUE:
1161 if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
1162 {
1163 if (_valueString!=null)
1164 {
1165 setString(_valueString);
1166 _valueString=null;
1167 _field=null;
1168 }
1169 _string.append((char)(0xff&ch));
1170 if (ch>HttpTokens.SPACE || ch<0)
1171 _length=_string.length();
1172 break;
1173 }
1174
1175 if (ch==HttpTokens.LINE_FEED)
1176 {
1177 if (_length > 0)
1178 {
1179 _value=null;
1180 _valueString=takeString();
1181 _length=-1;
1182 }
1183 setState(State.HEADER);
1184 break;
1185 }
1186
1187 throw new IllegalCharacterException(_state,ch,buffer);
1188
1189 default:
1190 throw new IllegalStateException(_state.toString());
1191
1192 }
1193 }
1194
1195 return handle;
1196 }
1197
1198 /* ------------------------------------------------------------------------------- */
1199 /**
1200 * Parse until next Event.
1201 * @return True if an {@link RequestHandler} method was called and it returned true;
1202 */
1203 public boolean parseNext(ByteBuffer buffer)
1204 {
1205 if (DEBUG)
1206 LOG.debug("parseNext s={} {}",_state,BufferUtil.toDetailString(buffer));
1207 try
1208 {
1209 // Start a request/response
1210 if (_state==State.START)
1211 {
1212 _version=null;
1213 _method=null;
1214 _methodString=null;
1215 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1216 _header=null;
1217 if (quickStart(buffer))
1218 return true;
1219 }
1220
1221 // Request/response line
1222 if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
1223 {
1224 if (parseLine(buffer))
1225 return true;
1226 }
1227
1228 // parse headers
1229 if (_state.ordinal()>= State.HEADER.ordinal() && _state.ordinal()<State.CONTENT.ordinal())
1230 {
1231 if (parseHeaders(buffer))
1232 return true;
1233 }
1234
1235 // parse content
1236 if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.END.ordinal())
1237 {
1238 // Handle HEAD response
1239 if (_responseStatus>0 && _headResponse)
1240 {
1241 setState(State.END);
1242 return _handler.messageComplete();
1243 }
1244 else
1245 {
1246 if (parseContent(buffer))
1247 return true;
1248 }
1249 }
1250
1251 // handle end states
1252 if (_state==State.END)
1253 {
1254 // eat white space
1255 while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE)
1256 buffer.get();
1257 }
1258 else if (_state==State.CLOSED)
1259 {
1260 if (BufferUtil.hasContent(buffer))
1261 {
1262 // Just ignore data when closed
1263 _headerBytes+=buffer.remaining();
1264 BufferUtil.clear(buffer);
1265 if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
1266 {
1267 // Don't want to waste time reading data of a closed request
1268 throw new IllegalStateException("too much data after closed");
1269 }
1270 }
1271 }
1272
1273 // Handle EOF
1274 if (_eof && !buffer.hasRemaining())
1275 {
1276 switch(_state)
1277 {
1278 case CLOSED:
1279 break;
1280
1281 case START:
1282 setState(State.CLOSED);
1283 _handler.earlyEOF();
1284 break;
1285
1286 case END:
1287 setState(State.CLOSED);
1288 break;
1289
1290 case EOF_CONTENT:
1291 setState(State.CLOSED);
1292 return _handler.messageComplete();
1293
1294 case CONTENT:
1295 case CHUNKED_CONTENT:
1296 case CHUNK_SIZE:
1297 case CHUNK_PARAMS:
1298 case CHUNK:
1299 setState(State.CLOSED);
1300 _handler.earlyEOF();
1301 break;
1302
1303 default:
1304 if (DEBUG)
1305 LOG.debug("{} EOF in {}",this,_state);
1306 setState(State.CLOSED);
1307 _handler.badMessage(400,null);
1308 break;
1309 }
1310 }
1311
1312 return false;
1313 }
1314 catch(BadMessageException e)
1315 {
1316 BufferUtil.clear(buffer);
1317
1318 LOG.warn("badMessage: "+e._code+(e.getMessage()!=null?" "+e.getMessage():"")+" for "+_handler);
1319 if (DEBUG)
1320 LOG.debug(e);
1321 setState(State.CLOSED);
1322 _handler.badMessage(e._code, e.getMessage());
1323 return false;
1324 }
1325 catch(Exception e)
1326 {
1327 BufferUtil.clear(buffer);
1328
1329 LOG.warn("badMessage: "+e.toString()+" for "+_handler);
1330 if (DEBUG)
1331 LOG.debug(e);
1332
1333 if (_state.ordinal()<=State.END.ordinal())
1334 {
1335 setState(State.CLOSED);
1336 _handler.badMessage(400,null);
1337 }
1338 else
1339 {
1340 _handler.earlyEOF();
1341 setState(State.CLOSED);
1342 }
1343
1344 return false;
1345 }
1346 }
1347
1348 protected boolean parseContent(ByteBuffer buffer)
1349 {
1350 int remaining=buffer.remaining();
1351 if (remaining==0 && _state==State.CONTENT)
1352 {
1353 long content=_contentLength - _contentPosition;
1354 if (content == 0)
1355 {
1356 setState(State.END);
1357 return _handler.messageComplete();
1358 }
1359 }
1360
1361 // Handle _content
1362 byte ch;
1363 while (_state.ordinal() < State.END.ordinal() && remaining>0)
1364 {
1365 switch (_state)
1366 {
1367 case EOF_CONTENT:
1368 _contentChunk=buffer.asReadOnlyBuffer();
1369 _contentPosition += remaining;
1370 buffer.position(buffer.position()+remaining);
1371 if (_handler.content(_contentChunk))
1372 return true;
1373 break;
1374
1375 case CONTENT:
1376 {
1377 long content=_contentLength - _contentPosition;
1378 if (content == 0)
1379 {
1380 setState(State.END);
1381 return _handler.messageComplete();
1382 }
1383 else
1384 {
1385 _contentChunk=buffer.asReadOnlyBuffer();
1386
1387 // limit content by expected size
1388 if (remaining > content)
1389 {
1390 // We can cast remaining to an int as we know that it is smaller than
1391 // or equal to length which is already an int.
1392 _contentChunk.limit(_contentChunk.position()+(int)content);
1393 }
1394
1395 _contentPosition += _contentChunk.remaining();
1396 buffer.position(buffer.position()+_contentChunk.remaining());
1397
1398 if (_handler.content(_contentChunk))
1399 return true;
1400
1401 if(_contentPosition == _contentLength)
1402 {
1403 setState(State.END);
1404 return _handler.messageComplete();
1405 }
1406 }
1407 break;
1408 }
1409
1410 case CHUNKED_CONTENT:
1411 {
1412 ch=next(buffer);
1413 if (ch>HttpTokens.SPACE)
1414 {
1415 _chunkLength=TypeUtil.convertHexDigit(ch);
1416 _chunkPosition=0;
1417 setState(State.CHUNK_SIZE);
1418 }
1419
1420 break;
1421 }
1422
1423 case CHUNK_SIZE:
1424 {
1425 ch=next(buffer);
1426 if (ch==0)
1427 break;
1428 if (ch == HttpTokens.LINE_FEED)
1429 {
1430 if (_chunkLength == 0)
1431 setState(State.CHUNK_END);
1432 else
1433 setState(State.CHUNK);
1434 }
1435 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
1436 setState(State.CHUNK_PARAMS);
1437 else
1438 _chunkLength=_chunkLength * 16 + TypeUtil.convertHexDigit(ch);
1439 break;
1440 }
1441
1442 case CHUNK_PARAMS:
1443 {
1444 ch=next(buffer);
1445 if (ch == HttpTokens.LINE_FEED)
1446 {
1447 if (_chunkLength == 0)
1448 setState(State.CHUNK_END);
1449 else
1450 setState(State.CHUNK);
1451 }
1452 break;
1453 }
1454
1455 case CHUNK:
1456 {
1457 int chunk=_chunkLength - _chunkPosition;
1458 if (chunk == 0)
1459 {
1460 setState(State.CHUNKED_CONTENT);
1461 }
1462 else
1463 {
1464 _contentChunk=buffer.asReadOnlyBuffer();
1465
1466 if (remaining > chunk)
1467 _contentChunk.limit(_contentChunk.position()+chunk);
1468 chunk=_contentChunk.remaining();
1469
1470 _contentPosition += chunk;
1471 _chunkPosition += chunk;
1472 buffer.position(buffer.position()+chunk);
1473 if (_handler.content(_contentChunk))
1474 return true;
1475 }
1476 break;
1477 }
1478
1479 case CHUNK_END:
1480 {
1481 // TODO handle chunk trailer
1482 ch=next(buffer);
1483 if (ch==0)
1484 break;
1485 if (ch == HttpTokens.LINE_FEED)
1486 {
1487 setState(State.END);
1488 return _handler.messageComplete();
1489 }
1490 throw new IllegalCharacterException(_state,ch,buffer);
1491 }
1492
1493 case CLOSED:
1494 {
1495 BufferUtil.clear(buffer);
1496 return false;
1497 }
1498
1499 default:
1500 break;
1501
1502 }
1503
1504 remaining=buffer.remaining();
1505 }
1506 return false;
1507 }
1508
1509 /* ------------------------------------------------------------------------------- */
1510 public boolean isAtEOF()
1511
1512 {
1513 return _eof;
1514 }
1515
1516 /* ------------------------------------------------------------------------------- */
1517 public void atEOF()
1518
1519 {
1520 if (DEBUG)
1521 LOG.debug("atEOF {}", this);
1522 _eof=true;
1523 }
1524
1525 /* ------------------------------------------------------------------------------- */
1526 public void close()
1527 {
1528 if (DEBUG)
1529 LOG.debug("close {}", this);
1530 setState(State.CLOSED);
1531 }
1532
1533 /* ------------------------------------------------------------------------------- */
1534 public void reset()
1535 {
1536 if (DEBUG)
1537 LOG.debug("reset {}", this);
1538 // reset state
1539 if (_state==State.CLOSED)
1540 return;
1541 if (_closed)
1542 {
1543 setState(State.CLOSED);
1544 return;
1545 }
1546
1547 setState(State.START);
1548 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
1549 _contentLength=-1;
1550 _contentPosition=0;
1551 _responseStatus=0;
1552 _contentChunk=null;
1553 _headerBytes=0;
1554 _host=false;
1555 }
1556
1557 /* ------------------------------------------------------------------------------- */
1558 protected void setState(State state)
1559 {
1560 if (DEBUG)
1561 LOG.debug("{} --> {}",_state,state);
1562 _state=state;
1563 }
1564
1565 /* ------------------------------------------------------------------------------- */
1566 public Trie<HttpField> getFieldCache()
1567 {
1568 return _connectionFields;
1569 }
1570
1571 /* ------------------------------------------------------------------------------- */
1572 private String getProxyField(ByteBuffer buffer)
1573 {
1574 _string.setLength(0);
1575 _length=0;
1576
1577 while (buffer.hasRemaining())
1578 {
1579 // process each character
1580 byte ch=next(buffer);
1581 if (ch<=' ')
1582 return _string.toString();
1583 _string.append((char)ch);
1584 }
1585 throw new BadMessageException();
1586 }
1587
1588 /* ------------------------------------------------------------------------------- */
1589 @Override
1590 public String toString()
1591 {
1592 return String.format("%s{s=%s,%d of %d}",
1593 getClass().getSimpleName(),
1594 _state,
1595 _contentPosition,
1596 _contentLength);
1597 }
1598
1599 /* ------------------------------------------------------------ */
1600 /* ------------------------------------------------------------ */
1601 /* ------------------------------------------------------------ */
1602 /* Event Handler interface
1603 * These methods return true if the caller should process the events
1604 * so far received (eg return from parseNext and call HttpChannel.handle).
1605 * If multiple callbacks are called in sequence (eg
1606 * headerComplete then messageComplete) from the same point in the parsing
1607 * then it is sufficient for the caller to process the events only once.
1608 */
1609 public interface HttpHandler<T>
1610 {
1611 public boolean content(T item);
1612
1613 public boolean headerComplete();
1614
1615 public boolean messageComplete();
1616
1617 /**
1618 * This is the method called by parser when a HTTP Header name and value is found
1619 * @param field The field parsed
1620 * @return True if the parser should return to its caller
1621 */
1622 public boolean parsedHeader(HttpField field);
1623
1624 /* ------------------------------------------------------------ */
1625 /** Called to signal that an EOF was received unexpectedly
1626 * during the parsing of a HTTP message
1627 */
1628 public void earlyEOF();
1629
1630 /* ------------------------------------------------------------ */
1631 /** Called to signal that a bad HTTP message has been received.
1632 * @param status The bad status to send
1633 * @param reason The textual reason for badness
1634 */
1635 public void badMessage(int status, String reason);
1636
1637 /* ------------------------------------------------------------ */
1638 /** @return the size in bytes of the per parser header cache
1639 */
1640 public int getHeaderCacheSize();
1641 }
1642
1643 /* ------------------------------------------------------------------------------- */
1644 /* ------------------------------------------------------------------------------- */
1645 /* ------------------------------------------------------------------------------- */
1646 public interface ProxyHandler
1647 {
1648 void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
1649 }
1650
1651 /* ------------------------------------------------------------------------------- */
1652 /* ------------------------------------------------------------------------------- */
1653 /* ------------------------------------------------------------------------------- */
1654 public interface RequestHandler<T> extends HttpHandler<T>
1655 {
1656 /**
1657 * This is the method called by parser when the HTTP request line is parsed
1658 * @param method The method as enum if of a known type
1659 * @param methodString The method as a string
1660 * @param uri The raw bytes of the URI. These are copied into a ByteBuffer that will not be changed until this parser is reset and reused.
1661 * @param version
1662 * @return true if handling parsing should return.
1663 */
1664 public abstract boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version);
1665
1666 /**
1667 * This is the method called by the parser after it has parsed the host header (and checked it's format). This is
1668 * called after the {@link HttpHandler#parsedHeader(HttpField)} methods and before
1669 * HttpHandler#headerComplete();
1670 */
1671 public abstract boolean parsedHostHeader(String host,int port);
1672 }
1673
1674 /* ------------------------------------------------------------------------------- */
1675 /* ------------------------------------------------------------------------------- */
1676 /* ------------------------------------------------------------------------------- */
1677 public interface ResponseHandler<T> extends HttpHandler<T>
1678 {
1679 /**
1680 * This is the method called by parser when the HTTP request line is parsed
1681 */
1682 public abstract boolean startResponse(HttpVersion version, int status, String reason);
1683 }
1684
1685 /* ------------------------------------------------------------------------------- */
1686 @SuppressWarnings("serial")
1687 private static class IllegalCharacterException extends BadMessageException
1688 {
1689 private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
1690 {
1691 super(400,String.format("Illegal character 0x%X",ch));
1692 // Bug #460642 - don't reveal buffers to end user
1693 LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));
1694 }
1695 }
1696 }