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