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