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