1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mortbay.jetty;
17
18 import java.io.IOException;
19 import java.util.Iterator;
20
21 import org.mortbay.io.Buffer;
22 import org.mortbay.io.BufferUtil;
23 import org.mortbay.io.Buffers;
24 import org.mortbay.io.EndPoint;
25 import org.mortbay.io.Portable;
26 import org.mortbay.io.BufferCache.CachedBuffer;
27 import org.mortbay.log.Log;
28 import org.mortbay.util.QuotedStringTokenizer;
29
30
31
32
33
34
35
36
37 public class HttpGenerator extends AbstractGenerator
38 {
39
40 private static byte[] LAST_CHUNK =
41 { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
42 private static byte[] CONTENT_LENGTH_0 = Portable.getBytes("Content-Length: 0\015\012");
43 private static byte[] CONNECTION_KEEP_ALIVE = Portable.getBytes("Connection: keep-alive\015\012");
44 private static byte[] CONNECTION_CLOSE = Portable.getBytes("Connection: close\015\012");
45 private static byte[] CONNECTION_ = Portable.getBytes("Connection: ");
46 private static byte[] CRLF = Portable.getBytes("\015\012");
47 private static byte[] TRANSFER_ENCODING_CHUNKED = Portable.getBytes("Transfer-Encoding: chunked\015\012");
48 private static byte[] SERVER = Portable.getBytes("Server: Jetty(6.0.x)\015\012");
49
50
51 private static int CHUNK_SPACE = 12;
52
53 public static void setServerVersion(String version)
54 {
55 SERVER=Portable.getBytes("Server: Jetty("+version+")\015\012");
56 }
57
58
59 private boolean _bypass = false;
60 private boolean _needCRLF = false;
61 private boolean _needEOC = false;
62 private boolean _bufferChunked = false;
63
64
65
66
67
68
69
70
71
72
73 public HttpGenerator(Buffers buffers, EndPoint io, int headerBufferSize, int contentBufferSize)
74 {
75 super(buffers,io,headerBufferSize,contentBufferSize);
76 }
77
78
79 public void reset(boolean returnBuffers)
80 {
81 super.reset(returnBuffers);
82 _bypass = false;
83 _needCRLF = false;
84 _needEOC = false;
85 _bufferChunked=false;
86 _method=null;
87 _uri=null;
88 _noContent=false;
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public void addContent(Buffer content, boolean last) throws IOException
105 {
106 if (_noContent)
107 throw new IllegalStateException("NO CONTENT");
108
109 if (_last || _state==STATE_END)
110 {
111 Log.debug("Ignoring extra content {}",content);
112 content.clear();
113 return;
114 }
115 _last = last;
116
117
118 if (_content!=null && _content.length()>0 || _bufferChunked)
119 {
120 if (!_endp.isOpen())
121 throw new EofException();
122 flush();
123 if (_content != null && _content.length()>0 || _bufferChunked)
124 throw new IllegalStateException("FULL");
125 }
126
127 _content = content;
128 _contentWritten += content.length();
129
130
131 if (_head)
132 {
133 content.clear();
134 _content=null;
135 }
136 else if (_endp != null && _buffer == null && content.length() > 0 && _last)
137 {
138
139
140 _bypass = true;
141 }
142 else
143 {
144
145 if (_buffer == null)
146 _buffer = _buffers.getBuffer(_contentBufferSize);
147
148
149 int len=_buffer.put(_content);
150 _content.skip(len);
151 if (_content.length() == 0)
152 _content = null;
153 }
154 }
155
156
157
158
159
160
161
162 public void sendResponse(Buffer response) throws IOException
163 {
164 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head )
165 throw new IllegalStateException();
166
167 _last = true;
168
169 _content = response;
170 _bypass = true;
171 _state = STATE_FLUSHING;
172
173
174 _contentLength =_contentWritten = response.length();
175
176 }
177
178
179
180
181
182
183
184
185
186 public boolean addContent(byte b) throws IOException
187 {
188 if (_noContent)
189 throw new IllegalStateException("NO CONTENT");
190
191 if (_last || _state==STATE_END)
192 {
193 Log.debug("Ignoring extra content {}",new Byte(b));
194 return false;
195 }
196
197
198 if (_content != null && _content.length()>0 || _bufferChunked)
199 {
200 flush();
201 if (_content != null && _content.length()>0 || _bufferChunked)
202 throw new IllegalStateException("FULL");
203 }
204
205 _contentWritten++;
206
207
208 if (_head)
209 return false;
210
211
212 if (_buffer == null)
213 _buffer = _buffers.getBuffer(_contentBufferSize);
214
215
216 _buffer.put(b);
217
218 return _buffer.space()<=(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
219 }
220
221
222
223
224
225
226
227 protected int prepareUncheckedAddContent() throws IOException
228 {
229 if (_noContent)
230 return -1;
231
232 if (_last || _state==STATE_END)
233 return -1;
234
235
236 Buffer content = _content;
237 if (content != null && content.length()>0 || _bufferChunked)
238 {
239 flush();
240 if (content != null && content.length()>0 || _bufferChunked)
241 throw new IllegalStateException("FULL");
242 }
243
244
245 if (_buffer == null)
246 _buffer = _buffers.getBuffer(_contentBufferSize);
247
248 _contentWritten-=_buffer.length();
249
250
251 if (_head)
252 return Integer.MAX_VALUE;
253
254 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
255 }
256
257
258 public boolean isBufferFull()
259 {
260
261 boolean full = super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE);
262 return full;
263 }
264
265
266 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
267 {
268 if (_state != STATE_HEADER)
269 return;
270
271
272 if (_method==null && _status==0)
273 throw new EofException();
274
275 if (_last && !allContentAdded)
276 throw new IllegalStateException("last?");
277 _last = _last | allContentAdded;
278
279
280 if (_header == null)
281 _header = _buffers.getBuffer(_headerBufferSize);
282
283 boolean has_server = false;
284
285 if (_method!=null)
286 {
287 _close = false;
288
289 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
290 {
291 _contentLength = HttpTokens.NO_CONTENT;
292 _header.put(_method);
293 _header.put((byte)' ');
294 _header.put(_uri.getBytes("utf-8"));
295 _header.put(HttpTokens.CRLF);
296 _state = STATE_FLUSHING;
297 _noContent=true;
298 return;
299 }
300 else
301 {
302 _header.put(_method);
303 _header.put((byte)' ');
304 _header.put(_uri.getBytes("utf-8"));
305 _header.put((byte)' ');
306 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER);
307 _header.put(HttpTokens.CRLF);
308 }
309 }
310 else
311 {
312
313 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
314 {
315 _close = true;
316 _contentLength = HttpTokens.EOF_CONTENT;
317 _state = STATE_CONTENT;
318 return;
319 }
320 else
321 {
322 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
323 _close = true;
324
325
326 Buffer line = HttpStatus.getResponseLine(_status);
327
328
329 if (line==null)
330 {
331 if (_reason==null)
332 _reason=getReasonBuffer(_status);
333
334 _header.put(HttpVersions.HTTP_1_1_BUFFER);
335 _header.put((byte) ' ');
336 _header.put((byte) ('0' + _status / 100));
337 _header.put((byte) ('0' + (_status % 100) / 10));
338 _header.put((byte) ('0' + (_status % 10)));
339 _header.put((byte) ' ');
340 if (_reason==null)
341 {
342 _header.put((byte) ('0' + _status / 100));
343 _header.put((byte) ('0' + (_status % 100) / 10));
344 _header.put((byte) ('0' + (_status % 10)));
345 }
346 else
347 _header.put(_reason);
348 _header.put(HttpTokens.CRLF);
349 }
350 else
351 {
352 if (_reason==null)
353 _header.put(line);
354 else
355 {
356 _header.put(line.array(), 0, HttpVersions.HTTP_1_1_BUFFER.length() + 5);
357 _header.put(_reason);
358 _header.put(HttpTokens.CRLF);
359 }
360 }
361
362 if (_status<200 && _status>=100 )
363 {
364 _noContent=true;
365 _content=null;
366 if (_buffer!=null)
367 _buffer.clear();
368
369 _header.put(HttpTokens.CRLF);
370 _state = STATE_CONTENT;
371 return;
372 }
373
374 if (_status==204 || _status==304)
375 {
376 _noContent=true;
377 _content=null;
378 if (_buffer!=null)
379 _buffer.clear();
380 }
381 }
382 }
383
384
385
386
387 HttpFields.Field content_length = null;
388 HttpFields.Field transfer_encoding = null;
389 boolean keep_alive = false;
390 boolean close=false;
391 boolean content_type =false;
392 StringBuffer connection = null;
393
394 if (fields != null)
395 {
396 Iterator iter = fields.getFields();
397
398 while (iter.hasNext())
399 {
400 HttpFields.Field field = (HttpFields.Field) iter.next();
401
402 switch (field.getNameOrdinal())
403 {
404 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
405 content_length = field;
406 _contentLength = field.getLongValue();
407
408 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
409 content_length = null;
410
411
412 field.put(_header);
413 break;
414
415 case HttpHeaders.CONTENT_TYPE_ORDINAL:
416 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer()))
417 _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
418 content_type=true;
419
420 field.put(_header);
421 break;
422
423 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
424 if (_version == HttpVersions.HTTP_1_1_ORDINAL) transfer_encoding = field;
425
426 break;
427
428 case HttpHeaders.CONNECTION_ORDINAL:
429 if (_method!=null)
430 field.put(_header);
431
432 int connection_value = field.getValueOrdinal();
433 switch (connection_value)
434 {
435 case -1:
436 {
437
438 QuotedStringTokenizer tok = new QuotedStringTokenizer(field.getValue(), ",");
439 while(tok.hasMoreTokens())
440 {
441 String token=tok.nextToken().trim();
442 CachedBuffer cb = HttpHeaderValues.CACHE.get(token);
443 if (cb!=null)
444 {
445 switch(cb.getOrdinal())
446 {
447 case HttpHeaderValues.CLOSE_ORDINAL:
448 close=true;
449 if (_method==null)
450 _close=true;
451 keep_alive=false;
452 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
453 _contentLength = HttpTokens.EOF_CONTENT;
454 break;
455
456 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
457 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
458 {
459 keep_alive = true;
460 if (_method==null)
461 _close = false;
462 }
463 break;
464
465 default:
466 if (connection==null)
467 connection=new StringBuffer();
468 else
469 connection.append(',');
470 connection.append(token);
471 }
472 }
473 else
474 {
475 if (connection==null)
476 connection=new StringBuffer();
477 else
478 connection.append(',');
479 connection.append(token);
480 }
481 }
482
483 break;
484 }
485 case HttpHeaderValues.CLOSE_ORDINAL:
486 {
487 close=true;
488 if (_method==null)
489 _close=true;
490 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
491 _contentLength = HttpTokens.EOF_CONTENT;
492 break;
493 }
494 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
495 {
496 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
497 {
498 keep_alive = true;
499 if (_method==null)
500 _close = false;
501 }
502 break;
503 }
504 default:
505 {
506 if (connection==null)
507 connection=new StringBuffer();
508 else
509 connection.append(',');
510 connection.append(field.getValue());
511 }
512 }
513
514
515 break;
516
517 case HttpHeaders.SERVER_ORDINAL:
518 if (getSendServerVersion())
519 {
520 has_server=true;
521 field.put(_header);
522 }
523 break;
524
525 default:
526
527 field.put(_header);
528 }
529 }
530 }
531
532
533
534
535
536
537
538
539
540
541 switch ((int) _contentLength)
542 {
543 case HttpTokens.UNKNOWN_CONTENT:
544
545
546
547
548 if (_contentWritten == 0 && _method==null && (_status < 200 || _status == 204 || _status == 304))
549 _contentLength = HttpTokens.NO_CONTENT;
550 else if (_last)
551 {
552
553 _contentLength = _contentWritten;
554 if (content_length == null && (_method==null || content_type || _contentLength>0))
555 {
556
557 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
558 _header.put(HttpTokens.COLON);
559 _header.put((byte) ' ');
560 BufferUtil.putDecLong(_header, _contentLength);
561 _header.put(HttpTokens.CRLF);
562 }
563 }
564 else
565 {
566
567 _contentLength = (_close || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
568 if (_method!=null && _contentLength==HttpTokens.EOF_CONTENT)
569 {
570 _contentLength=HttpTokens.NO_CONTENT;
571 _noContent=true;
572 }
573 }
574 break;
575
576 case HttpTokens.NO_CONTENT:
577 if (content_length == null && _method==null && _status >= 200 && _status != 204 && _status != 304)
578 _header.put(CONTENT_LENGTH_0);
579 break;
580
581 case HttpTokens.EOF_CONTENT:
582 _close = _method==null;
583 break;
584
585 case HttpTokens.CHUNKED_CONTENT:
586 break;
587
588 default:
589
590 break;
591 }
592
593
594 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
595 {
596
597 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
598 {
599 String c = transfer_encoding.getValue();
600 if (c.endsWith(HttpHeaderValues.CHUNKED))
601 transfer_encoding.put(_header);
602 else
603 throw new IllegalArgumentException("BAD TE");
604 }
605 else
606 _header.put(TRANSFER_ENCODING_CHUNKED);
607 }
608
609
610 if (_contentLength==HttpTokens.EOF_CONTENT)
611 {
612 keep_alive=false;
613 _close=true;
614 }
615
616 if (_method==null)
617 {
618 if (_close && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
619 {
620 _header.put(CONNECTION_CLOSE);
621 if (connection!=null)
622 {
623 _header.setPutIndex(_header.putIndex()-2);
624 _header.put((byte)',');
625 _header.put(connection.toString().getBytes());
626 _header.put(CRLF);
627 }
628 }
629 else if (keep_alive)
630 {
631 _header.put(CONNECTION_KEEP_ALIVE);
632 if (connection!=null)
633 {
634 _header.setPutIndex(_header.putIndex()-2);
635 _header.put((byte)',');
636 _header.put(connection.toString().getBytes());
637 _header.put(CRLF);
638 }
639 }
640 else if (connection!=null)
641 {
642 _header.put(CONNECTION_);
643 _header.put(connection.toString().getBytes());
644 _header.put(CRLF);
645 }
646 }
647
648 if (!has_server && _status>100 && getSendServerVersion())
649 _header.put(SERVER);
650
651
652 _header.put(HttpTokens.CRLF);
653
654 _state = STATE_CONTENT;
655
656 }
657
658
659
660
661
662
663
664 public void complete() throws IOException
665 {
666 if (_state == STATE_END)
667 return;
668
669 super.complete();
670
671 if (_state < STATE_FLUSHING)
672 {
673 _state = STATE_FLUSHING;
674 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
675 _needEOC = true;
676 }
677
678 flush();
679 }
680
681
682 public long flush() throws IOException
683 {
684 try
685 {
686 if (_state == STATE_HEADER)
687 throw new IllegalStateException("State==HEADER");
688
689 prepareBuffers();
690
691 if (_endp == null)
692 {
693 if (_needCRLF && _buffer!=null)
694 _buffer.put(HttpTokens.CRLF);
695 if (_needEOC && _buffer!=null && !_head)
696 _buffer.put(LAST_CHUNK);
697 _needCRLF=false;
698 _needEOC=false;
699 return 0;
700 }
701
702
703 int total= 0;
704 long last_len = -1;
705 Flushing: while (true)
706 {
707 int len = -1;
708 int to_flush = ((_header != null && _header.length() > 0)?4:0) | ((_buffer != null && _buffer.length() > 0)?2:0) | ((_bypass && _content != null && _content.length() > 0)?1:0);
709 switch (to_flush)
710 {
711 case 7:
712 throw new IllegalStateException();
713 case 6:
714 len = _endp.flush(_header, _buffer, null);
715 break;
716 case 5:
717 len = _endp.flush(_header, _content, null);
718 break;
719 case 4:
720 len = _endp.flush(_header);
721 break;
722 case 3:
723 throw new IllegalStateException();
724 case 2:
725 len = _endp.flush(_buffer);
726 break;
727 case 1:
728 len = _endp.flush(_content);
729 break;
730 case 0:
731 {
732
733 if (_header != null)
734 _header.clear();
735
736 _bypass = false;
737 _bufferChunked = false;
738
739 if (_buffer != null)
740 {
741 _buffer.clear();
742 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
743 {
744
745 _buffer.setPutIndex(CHUNK_SPACE);
746 _buffer.setGetIndex(CHUNK_SPACE);
747
748
749
750 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
751 {
752 _buffer.put(_content);
753 _content.clear();
754 _content = null;
755 break Flushing;
756 }
757 }
758 }
759
760
761 if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0))
762 {
763 if (_state == STATE_FLUSHING)
764 _state = STATE_END;
765 if (_state==STATE_END && _close && _status!=100)
766 _endp.shutdownOutput();
767
768 break Flushing;
769 }
770
771
772 prepareBuffers();
773 }
774 }
775
776
777
778 if (len > 0)
779 total+=len;
780 else
781 break Flushing;
782
783 last_len = len;
784 }
785
786 return total;
787 }
788 catch (IOException e)
789 {
790 Log.ignore(e);
791 throw (e instanceof EofException) ? e:new EofException(e);
792 }
793 }
794
795
796 private void prepareBuffers()
797 {
798
799 if (!_bufferChunked)
800 {
801
802 if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
803 {
804 int len = _buffer.put(_content);
805 _content.skip(len);
806 if (_content.length() == 0)
807 _content = null;
808 }
809
810
811 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
812 {
813 int size = _buffer == null ? 0 : _buffer.length();
814 if (size > 0)
815 {
816
817 _bufferChunked = true;
818
819
820 if (_buffer.getIndex() == CHUNK_SPACE)
821 {
822
823 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
824 _buffer.setGetIndex(_buffer.getIndex() - 2);
825 BufferUtil.prependHexInt(_buffer, size);
826
827 if (_needCRLF)
828 {
829 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
830 _buffer.setGetIndex(_buffer.getIndex() - 2);
831 _needCRLF = false;
832 }
833 }
834 else
835 {
836
837 if (_needCRLF)
838 {
839 if (_header.length() > 0) throw new IllegalStateException("EOC");
840 _header.put(HttpTokens.CRLF);
841 _needCRLF = false;
842 }
843 BufferUtil.putHexInt(_header, size);
844 _header.put(HttpTokens.CRLF);
845 }
846
847
848 if (_buffer.space() >= 2)
849 _buffer.put(HttpTokens.CRLF);
850 else
851 _needCRLF = true;
852 }
853
854
855 if (_needEOC && (_content == null || _content.length() == 0))
856 {
857 if (_needCRLF)
858 {
859 if (_buffer == null && _header.space() >= 2)
860 {
861 _header.put(HttpTokens.CRLF);
862 _needCRLF = false;
863 }
864 else if (_buffer!=null && _buffer.space() >= 2)
865 {
866 _buffer.put(HttpTokens.CRLF);
867 _needCRLF = false;
868 }
869 }
870
871 if (!_needCRLF && _needEOC)
872 {
873 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
874 {
875 if (!_head)
876 {
877 _header.put(LAST_CHUNK);
878 _bufferChunked=true;
879 }
880 _needEOC = false;
881 }
882 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
883 {
884 if (!_head)
885 {
886 _buffer.put(LAST_CHUNK);
887 _bufferChunked=true;
888 }
889 _needEOC = false;
890 }
891 }
892 }
893 }
894 }
895
896 if (_content != null && _content.length() == 0)
897 _content = null;
898
899 }
900
901 public int getBytesBuffered()
902 {
903 return(_header==null?0:_header.length())+
904 (_buffer==null?0:_buffer.length())+
905 (_content==null?0:_content.length());
906 }
907
908 public boolean isEmpty()
909 {
910 return (_header==null||_header.length()==0) &&
911 (_buffer==null||_buffer.length()==0) &&
912 (_content==null||_content.length()==0);
913 }
914
915 public String toString()
916 {
917 return "HttpGenerator s="+_state+
918 " h="+(_header==null?"null":(""+_header.length()))+
919 " b="+(_buffer==null?"null":(""+_buffer.length()))+
920 " c="+(_content==null?"null":(""+_content.length()));
921 }
922 }