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.io.IOException;
22 import java.nio.BufferOverflowException;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
25 import java.util.Arrays;
26
27 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
28 import org.eclipse.jetty.util.BufferUtil;
29 import org.eclipse.jetty.util.StringUtil;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32
33
34
35
36
37
38
39
40
41
42
43 public class HttpGenerator
44 {
45 private final static Logger LOG = Log.getLogger(HttpGenerator.class);
46
47 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
48
49 private final static byte[] __colon_space = new byte[] {':',' '};
50 private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
51 public static final ResponseInfo CONTINUE_100_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,100,null,false);
52 public static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
53 public final static ResponseInfo RESPONSE_500_INFO =
54 new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0,HttpStatus.INTERNAL_SERVER_ERROR_500,null,false);
55
56
57 public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
58 public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
59
60
61 public static final int CHUNK_SIZE = 12;
62
63 private State _state = State.START;
64 private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
65
66 private long _contentPrepared = 0;
67 private boolean _noContent = false;
68 private Boolean _persistent = null;
69
70 private final int _send;
71 private final static int SEND_SERVER = 0x01;
72 private final static int SEND_XPOWEREDBY = 0x02;
73
74
75
76 public static void setJettyVersion(String serverVersion)
77 {
78 SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012");
79 SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012");
80 SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes("Server: " + serverVersion + "\015\012X-Powered-By: " +
81 serverVersion + "\015\012");
82 }
83
84
85
86 private boolean _needCRLF = false;
87
88
89 public HttpGenerator()
90 {
91 this(false,false);
92 }
93
94
95 public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
96 {
97 _send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
98 }
99
100
101 public void reset()
102 {
103 _state = State.START;
104 _endOfContent = EndOfContent.UNKNOWN_CONTENT;
105 _noContent=false;
106 _persistent = null;
107 _contentPrepared = 0;
108 _needCRLF = false;
109 }
110
111
112 @Deprecated
113 public boolean getSendServerVersion ()
114 {
115 return (_send&SEND_SERVER)!=0;
116 }
117
118
119 @Deprecated
120 public void setSendServerVersion (boolean sendServerVersion)
121 {
122 throw new UnsupportedOperationException();
123 }
124
125
126 public State getState()
127 {
128 return _state;
129 }
130
131
132 public boolean isState(State state)
133 {
134 return _state == state;
135 }
136
137
138 public boolean isIdle()
139 {
140 return _state == State.START;
141 }
142
143
144 public boolean isEnd()
145 {
146 return _state == State.END;
147 }
148
149
150 public boolean isCommitted()
151 {
152 return _state.ordinal() >= State.COMMITTED.ordinal();
153 }
154
155
156 public boolean isChunking()
157 {
158 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
159 }
160
161
162 public boolean isNoContent()
163 {
164 return _noContent;
165 }
166
167
168 public void setPersistent(boolean persistent)
169 {
170 _persistent=persistent;
171 }
172
173
174
175
176
177 public boolean isPersistent()
178 {
179 return Boolean.TRUE.equals(_persistent);
180 }
181
182
183 public boolean isWritten()
184 {
185 return _contentPrepared>0;
186 }
187
188
189 public long getContentPrepared()
190 {
191 return _contentPrepared;
192 }
193
194
195 public void abort()
196 {
197 _persistent=false;
198 _state=State.END;
199 _endOfContent=null;
200 }
201
202
203 public Result generateRequest(RequestInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
204 {
205 switch(_state)
206 {
207 case START:
208 {
209 if (info==null)
210 return Result.NEED_INFO;
211
212
213 if (header==null)
214 return Result.NEED_HEADER;
215
216
217 if (_persistent==null)
218 _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
219
220
221 int pos=BufferUtil.flipToFill(header);
222 try
223 {
224
225 generateRequestLine(info,header);
226
227 if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
228 _noContent=true;
229 else
230 generateHeaders(info,header,content,last);
231
232 boolean expect100 = info.getHttpFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
233
234 if (expect100)
235 {
236 _state = State.COMMITTED;
237 }
238 else
239 {
240
241 int len = BufferUtil.length(content);
242 if (len>0)
243 {
244 _contentPrepared+=len;
245 if (isChunking())
246 prepareChunk(header,len);
247 }
248 _state = last?State.COMPLETING:State.COMMITTED;
249 }
250
251 return Result.FLUSH;
252 }
253 catch(Exception e)
254 {
255 String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
256 throw new IOException(message,e);
257 }
258 finally
259 {
260 BufferUtil.flipToFlush(header,pos);
261 }
262 }
263
264 case COMMITTED:
265 {
266 int len = BufferUtil.length(content);
267
268 if (len>0)
269 {
270
271 if (isChunking())
272 {
273
274 if (chunk==null)
275 return Result.NEED_CHUNK;
276 BufferUtil.clearToFill(chunk);
277 prepareChunk(chunk,len);
278 BufferUtil.flipToFlush(chunk,0);
279 }
280 _contentPrepared+=len;
281 }
282
283 if (last)
284 {
285 _state=State.COMPLETING;
286 return len>0?Result.FLUSH:Result.CONTINUE;
287 }
288
289 return Result.FLUSH;
290 }
291
292 case COMPLETING:
293 {
294 if (BufferUtil.hasContent(content))
295 {
296 if (LOG.isDebugEnabled())
297 LOG.debug("discarding content in COMPLETING");
298 BufferUtil.clear(content);
299 }
300
301 if (isChunking())
302 {
303
304 if (chunk==null)
305 return Result.NEED_CHUNK;
306 BufferUtil.clearToFill(chunk);
307 prepareChunk(chunk,0);
308 BufferUtil.flipToFlush(chunk,0);
309 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
310 return Result.FLUSH;
311 }
312
313 _state=State.END;
314 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
315 }
316
317 case END:
318 if (BufferUtil.hasContent(content))
319 {
320 if (LOG.isDebugEnabled())
321 LOG.debug("discarding content in COMPLETING");
322 BufferUtil.clear(content);
323 }
324 return Result.DONE;
325
326 default:
327 throw new IllegalStateException();
328 }
329 }
330
331
332 public Result generateResponse(ResponseInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
333 {
334 switch(_state)
335 {
336 case START:
337 {
338 if (info==null)
339 return Result.NEED_INFO;
340
341
342 if (info.getHttpVersion() == HttpVersion.HTTP_0_9)
343 {
344 _persistent = false;
345 _endOfContent=EndOfContent.EOF_CONTENT;
346 if (BufferUtil.hasContent(content))
347 _contentPrepared+=content.remaining();
348 _state = last?State.COMPLETING:State.COMMITTED;
349 return Result.FLUSH;
350 }
351
352
353 if (header==null)
354 return Result.NEED_HEADER;
355
356
357 if (_persistent==null)
358 _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
359
360
361 int pos=BufferUtil.flipToFill(header);
362 try
363 {
364
365 generateResponseLine(info,header);
366
367
368 int status=info.getStatus();
369 if (status>=100 && status<200 )
370 {
371 _noContent=true;
372
373 if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
374 {
375 header.put(HttpTokens.CRLF);
376 _state=State.COMPLETING_1XX;
377 return Result.FLUSH;
378 }
379 }
380 else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
381 {
382 _noContent=true;
383 }
384
385 generateHeaders(info,header,content,last);
386
387
388 int len = BufferUtil.length(content);
389 if (len>0)
390 {
391 _contentPrepared+=len;
392 if (isChunking() && !info.isHead())
393 prepareChunk(header,len);
394 }
395 _state = last?State.COMPLETING:State.COMMITTED;
396 }
397 catch(Exception e)
398 {
399 String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
400 throw new IOException(message,e);
401 }
402 finally
403 {
404 BufferUtil.flipToFlush(header,pos);
405 }
406
407 return Result.FLUSH;
408 }
409
410 case COMMITTED:
411 {
412 int len = BufferUtil.length(content);
413
414
415 if (len>0)
416 {
417 if (isChunking())
418 {
419 if (chunk==null)
420 return Result.NEED_CHUNK;
421 BufferUtil.clearToFill(chunk);
422 prepareChunk(chunk,len);
423 BufferUtil.flipToFlush(chunk,0);
424 }
425 _contentPrepared+=len;
426 }
427
428 if (last)
429 {
430 _state=State.COMPLETING;
431 return len>0?Result.FLUSH:Result.CONTINUE;
432 }
433 return len>0?Result.FLUSH:Result.DONE;
434
435 }
436
437 case COMPLETING_1XX:
438 {
439 reset();
440 return Result.DONE;
441 }
442
443 case COMPLETING:
444 {
445 if (BufferUtil.hasContent(content))
446 {
447 if (LOG.isDebugEnabled())
448 LOG.debug("discarding content in COMPLETING");
449 BufferUtil.clear(content);
450 }
451
452 if (isChunking())
453 {
454
455 if (chunk==null)
456 return Result.NEED_CHUNK;
457
458
459 BufferUtil.clearToFill(chunk);
460 prepareChunk(chunk,0);
461 BufferUtil.flipToFlush(chunk,0);
462 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
463 return Result.FLUSH;
464 }
465
466 _state=State.END;
467
468 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
469 }
470
471 case END:
472 if (BufferUtil.hasContent(content))
473 {
474 if (LOG.isDebugEnabled())
475 LOG.debug("discarding content in COMPLETING");
476 BufferUtil.clear(content);
477 }
478 return Result.DONE;
479
480 default:
481 throw new IllegalStateException();
482 }
483 }
484
485
486 private void prepareChunk(ByteBuffer chunk, int remaining)
487 {
488
489 if (_needCRLF)
490 BufferUtil.putCRLF(chunk);
491
492
493 if (remaining>0)
494 {
495 BufferUtil.putHexInt(chunk, remaining);
496 BufferUtil.putCRLF(chunk);
497 _needCRLF=true;
498 }
499 else
500 {
501 chunk.put(LAST_CHUNK);
502 _needCRLF=false;
503 }
504 }
505
506
507 private void generateRequestLine(RequestInfo request,ByteBuffer header)
508 {
509 header.put(StringUtil.getBytes(request.getMethod()));
510 header.put((byte)' ');
511 header.put(StringUtil.getBytes(request.getUri()));
512 switch(request.getHttpVersion())
513 {
514 case HTTP_1_0:
515 case HTTP_1_1:
516 header.put((byte)' ');
517 header.put(request.getHttpVersion().toBytes());
518 break;
519 default:
520 throw new IllegalStateException();
521 }
522 header.put(HttpTokens.CRLF);
523 }
524
525
526 private void generateResponseLine(ResponseInfo response, ByteBuffer header)
527 {
528
529 int status=response.getStatus();
530 PreparedResponse preprepared = status<__preprepared.length?__preprepared[status]:null;
531 String reason=response.getReason();
532 if (preprepared!=null)
533 {
534 if (reason==null)
535 header.put(preprepared._responseLine);
536 else
537 {
538 header.put(preprepared._schemeCode);
539 header.put(getReasonBytes(reason));
540 header.put(HttpTokens.CRLF);
541 }
542 }
543 else
544 {
545 header.put(HTTP_1_1_SPACE);
546 header.put((byte) ('0' + status / 100));
547 header.put((byte) ('0' + (status % 100) / 10));
548 header.put((byte) ('0' + (status % 10)));
549 header.put((byte) ' ');
550 if (reason==null)
551 {
552 header.put((byte) ('0' + status / 100));
553 header.put((byte) ('0' + (status % 100) / 10));
554 header.put((byte) ('0' + (status % 10)));
555 }
556 else
557 header.put(getReasonBytes(reason));
558 header.put(HttpTokens.CRLF);
559 }
560 }
561
562
563 private byte[] getReasonBytes(String reason)
564 {
565 if (reason.length()>1024)
566 reason=reason.substring(0,1024);
567 byte[] _bytes = StringUtil.getBytes(reason);
568
569 for (int i=_bytes.length;i-->0;)
570 if (_bytes[i]=='\r' || _bytes[i]=='\n')
571 _bytes[i]='?';
572 return _bytes;
573 }
574
575
576 private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
577 {
578 final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
579 final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
580
581
582 int send=_send;
583 HttpField transfer_encoding=null;
584 boolean keep_alive=false;
585 boolean close=false;
586 boolean content_type=false;
587 StringBuilder connection = null;
588
589
590 if (_info.getHttpFields() != null)
591 {
592 for (HttpField field : _info.getHttpFields())
593 {
594 HttpHeader h = field.getHeader();
595
596 switch (h==null?HttpHeader.UNKNOWN:h)
597 {
598 case CONTENT_LENGTH:
599
600 if (_info.getContentLength()>=0)
601 _endOfContent=EndOfContent.CONTENT_LENGTH;
602 break;
603
604 case CONTENT_TYPE:
605 {
606 if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
607 _endOfContent=EndOfContent.SELF_DEFINING_CONTENT;
608
609
610 content_type=true;
611 putTo(field,header);
612 break;
613 }
614
615 case TRANSFER_ENCODING:
616 {
617 if (_info.getHttpVersion() == HttpVersion.HTTP_1_1)
618 transfer_encoding = field;
619
620 break;
621 }
622
623 case CONNECTION:
624 {
625 if (request!=null)
626 putTo(field,header);
627
628
629 HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
630 String[] split = null;
631
632 if (values[0]==null)
633 {
634 split = field.getValue().split("\\s*,\\s*");
635 if (split.length>0)
636 {
637 values=new HttpHeaderValue[split.length];
638 for (int i=0;i<split.length;i++)
639 values[i]=HttpHeaderValue.CACHE.get(split[i]);
640 }
641 }
642
643
644 for (int i=0;i<values.length;i++)
645 {
646 HttpHeaderValue value=values[i];
647 switch (value==null?HttpHeaderValue.UNKNOWN:value)
648 {
649 case UPGRADE:
650 {
651
652 header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
653 header.put(CRLF);
654 break;
655 }
656
657 case CLOSE:
658 {
659 close=true;
660 if (response!=null)
661 {
662 _persistent=false;
663 if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
664 _endOfContent=EndOfContent.EOF_CONTENT;
665 }
666 break;
667 }
668
669 case KEEP_ALIVE:
670 {
671 if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
672 {
673 keep_alive = true;
674 if (response!=null)
675 _persistent=true;
676 }
677 break;
678 }
679
680 default:
681 {
682 if (connection==null)
683 connection=new StringBuilder();
684 else
685 connection.append(',');
686 connection.append(split==null?field.getValue():split[i]);
687 }
688 }
689 }
690
691
692 break;
693 }
694
695 case SERVER:
696 {
697 send=send&~SEND_SERVER;
698 putTo(field,header);
699 break;
700 }
701
702 default:
703 putTo(field,header);
704 }
705 }
706 }
707
708
709
710
711
712
713
714
715
716
717
718 int status=response!=null?response.getStatus():-1;
719 switch (_endOfContent)
720 {
721 case UNKNOWN_CONTENT:
722
723
724
725
726 if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
727 _endOfContent=EndOfContent.NO_CONTENT;
728 else if (_info.getContentLength()>0)
729 {
730
731 _endOfContent=EndOfContent.CONTENT_LENGTH;
732 long content_length = _info.getContentLength();
733 if ((response!=null || content_length>0 || content_type ) && !_noContent)
734 {
735
736 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
737 BufferUtil.putDecLong(header, content_length);
738 header.put(HttpTokens.CRLF);
739 }
740 }
741 else if (last)
742 {
743
744 _endOfContent=EndOfContent.CONTENT_LENGTH;
745 long content_length = _contentPrepared+BufferUtil.length(content);
746
747
748 if ((response!=null || content_length>0 || content_type ) && !_noContent)
749 {
750 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
751 BufferUtil.putDecLong(header, content_length);
752 header.put(HttpTokens.CRLF);
753 }
754 }
755 else
756 {
757
758 _endOfContent = EndOfContent.CHUNKED_CONTENT;
759
760
761
762
763 if (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
764 _endOfContent = EndOfContent.EOF_CONTENT;
765 }
766 break;
767
768 case CONTENT_LENGTH:
769 long content_length = _info.getContentLength();
770 if ((response!=null || content_length>0 || content_type ) && !_noContent)
771 {
772
773 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
774 BufferUtil.putDecLong(header, content_length);
775 header.put(HttpTokens.CRLF);
776 }
777 break;
778
779 case NO_CONTENT:
780 if (response!=null && status >= 200 && status != 204 && status != 304)
781 header.put(CONTENT_LENGTH_0);
782 break;
783
784 case EOF_CONTENT:
785 _persistent = request!=null;
786 break;
787
788 case CHUNKED_CONTENT:
789 break;
790
791 default:
792 break;
793 }
794
795
796 if (isChunking())
797 {
798
799 if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue()))
800 {
801 String c = transfer_encoding.getValue();
802 if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
803 putTo(transfer_encoding,header);
804 else
805 throw new IllegalArgumentException("BAD TE");
806 }
807 else
808 header.put(TRANSFER_ENCODING_CHUNKED);
809 }
810
811
812 if (_endOfContent==EndOfContent.EOF_CONTENT)
813 {
814 keep_alive=false;
815 _persistent=false;
816 }
817
818
819 if (response!=null)
820 {
821 if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
822 {
823 if (connection==null)
824 header.put(CONNECTION_CLOSE);
825 else
826 {
827 header.put(CONNECTION_CLOSE,0,CONNECTION_CLOSE.length-2);
828 header.put((byte)',');
829 header.put(StringUtil.getBytes(connection.toString()));
830 header.put(CRLF);
831 }
832 }
833 else if (keep_alive)
834 {
835 if (connection==null)
836 header.put(CONNECTION_KEEP_ALIVE);
837 else
838 {
839 header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
840 header.put((byte)',');
841 header.put(StringUtil.getBytes(connection.toString()));
842 header.put(CRLF);
843 }
844 }
845 else if (connection!=null)
846 {
847 header.put(HttpHeader.CONNECTION.getBytesColonSpace());
848 header.put(StringUtil.getBytes(connection.toString()));
849 header.put(CRLF);
850 }
851 }
852
853 if (status>199)
854 header.put(SEND[send]);
855
856
857 header.put(HttpTokens.CRLF);
858 }
859
860
861 public static byte[] getReasonBuffer(int code)
862 {
863 PreparedResponse status = code<__preprepared.length?__preprepared[code]:null;
864 if (status!=null)
865 return status._reason;
866 return null;
867 }
868
869
870 @Override
871 public String toString()
872 {
873 return String.format("%s{s=%s}",
874 getClass().getSimpleName(),
875 _state);
876 }
877
878
879
880
881
882 private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
883 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
884 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
885 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
886 private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
887 private static final byte[] CRLF = StringUtil.getBytes("\015\012");
888 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
889 private static final byte[][] SEND = new byte[][]{
890 new byte[0],
891 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
892 StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
893 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
894 };
895
896
897
898
899
900 private static class PreparedResponse
901 {
902 byte[] _reason;
903 byte[] _schemeCode;
904 byte[] _responseLine;
905 }
906 private static final PreparedResponse[] __preprepared = new PreparedResponse[HttpStatus.MAX_CODE+1];
907 static
908 {
909 int versionLength=HttpVersion.HTTP_1_1.toString().length();
910
911 for (int i=0;i<__preprepared.length;i++)
912 {
913 HttpStatus.Code code = HttpStatus.getCode(i);
914 if (code==null)
915 continue;
916 String reason=code.getMessage();
917 byte[] line=new byte[versionLength+5+reason.length()+2];
918 HttpVersion.HTTP_1_1.toBuffer().get(line,0,versionLength);
919 line[versionLength+0]=' ';
920 line[versionLength+1]=(byte)('0'+i/100);
921 line[versionLength+2]=(byte)('0'+(i%100)/10);
922 line[versionLength+3]=(byte)('0'+(i%10));
923 line[versionLength+4]=' ';
924 for (int j=0;j<reason.length();j++)
925 line[versionLength+5+j]=(byte)reason.charAt(j);
926 line[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN;
927 line[versionLength+6+reason.length()]=HttpTokens.LINE_FEED;
928
929 __preprepared[i] = new PreparedResponse();
930 __preprepared[i]._schemeCode = Arrays.copyOfRange(line, 0,versionLength+5);
931 __preprepared[i]._reason = Arrays.copyOfRange(line, versionLength+5, line.length-2);
932 __preprepared[i]._responseLine=line;
933 }
934 }
935
936 public static class Info
937 {
938 final HttpVersion _httpVersion;
939 final HttpFields _httpFields;
940 final long _contentLength;
941
942 private Info(HttpVersion httpVersion, HttpFields httpFields, long contentLength)
943 {
944 _httpVersion = httpVersion;
945 _httpFields = httpFields;
946 _contentLength = contentLength;
947 }
948
949 public HttpVersion getHttpVersion()
950 {
951 return _httpVersion;
952 }
953 public HttpFields getHttpFields()
954 {
955 return _httpFields;
956 }
957 public long getContentLength()
958 {
959 return _contentLength;
960 }
961 }
962
963 public static class RequestInfo extends Info
964 {
965 private final String _method;
966 private final String _uri;
967
968 public RequestInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, String method, String uri)
969 {
970 super(httpVersion,httpFields,contentLength);
971 _method = method;
972 _uri = uri;
973 }
974
975 public String getMethod()
976 {
977 return _method;
978 }
979
980 public String getUri()
981 {
982 return _uri;
983 }
984
985 @Override
986 public String toString()
987 {
988 return String.format("RequestInfo{%s %s %s,%d}",_method,_uri,_httpVersion,_contentLength);
989 }
990 }
991
992 public static class ResponseInfo extends Info
993 {
994 private final int _status;
995 private final String _reason;
996 private final boolean _head;
997
998 public ResponseInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, int status, String reason, boolean head)
999 {
1000 super(httpVersion,httpFields,contentLength);
1001 _status = status;
1002 _reason = reason;
1003 _head = head;
1004 }
1005
1006 public boolean isInformational()
1007 {
1008 return _status>=100 && _status<200;
1009 }
1010
1011 public int getStatus()
1012 {
1013 return _status;
1014 }
1015
1016 public String getReason()
1017 {
1018 return _reason;
1019 }
1020
1021 public boolean isHead()
1022 {
1023 return _head;
1024 }
1025
1026 @Override
1027 public String toString()
1028 {
1029 return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
1030 }
1031 }
1032
1033 private static void putSanitisedName(String s,ByteBuffer buffer)
1034 {
1035 int l=s.length();
1036 for (int i=0;i<l;i++)
1037 {
1038 char c=s.charAt(i);
1039
1040 if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
1041 buffer.put((byte)'?');
1042 else
1043 buffer.put((byte)(0xff&c));
1044 }
1045 }
1046
1047 private static void putSanitisedValue(String s,ByteBuffer buffer)
1048 {
1049 int l=s.length();
1050 for (int i=0;i<l;i++)
1051 {
1052 char c=s.charAt(i);
1053
1054 if (c<0 || c>0xff || c=='\r' || c=='\n')
1055 buffer.put((byte)' ');
1056 else
1057 buffer.put((byte)(0xff&c));
1058 }
1059 }
1060
1061 public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
1062 {
1063 if (field instanceof CachedHttpField)
1064 {
1065 ((CachedHttpField)field).putTo(bufferInFillMode);
1066 }
1067 else
1068 {
1069 HttpHeader header=field.getHeader();
1070 if (header!=null)
1071 {
1072 bufferInFillMode.put(header.getBytesColonSpace());
1073 putSanitisedValue(field.getValue(),bufferInFillMode);
1074 }
1075 else
1076 {
1077 putSanitisedName(field.getName(),bufferInFillMode);
1078 bufferInFillMode.put(__colon_space);
1079 putSanitisedValue(field.getValue(),bufferInFillMode);
1080 }
1081
1082 BufferUtil.putCRLF(bufferInFillMode);
1083 }
1084 }
1085
1086 public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
1087 {
1088 for (HttpField field : fields)
1089 {
1090 if (field != null)
1091 putTo(field,bufferInFillMode);
1092 }
1093 BufferUtil.putCRLF(bufferInFillMode);
1094 }
1095
1096 public static class CachedHttpField extends HttpField
1097 {
1098 private final byte[] _bytes;
1099 public CachedHttpField(HttpHeader header,String value)
1100 {
1101 super(header,value);
1102 int cbl=header.getBytesColonSpace().length;
1103 _bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2);
1104 System.arraycopy(value.getBytes(StandardCharsets.ISO_8859_1),0,_bytes,cbl,value.length());
1105 _bytes[_bytes.length-2]=(byte)'\r';
1106 _bytes[_bytes.length-1]=(byte)'\n';
1107 }
1108
1109 public void putTo(ByteBuffer bufferInFillMode)
1110 {
1111 bufferInFillMode.put(_bytes);
1112 }
1113 }
1114 }