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