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