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