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 StringBuffer connection = null;
391
392 if (fields != null)
393 {
394 Iterator iter = fields.getFields();
395
396 while (iter.hasNext())
397 {
398 HttpFields.Field field = (HttpFields.Field) iter.next();
399
400 switch (field.getNameOrdinal())
401 {
402 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
403 content_length = field;
404 _contentLength = field.getLongValue();
405
406 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
407 content_length = null;
408
409
410 field.put(_header);
411 break;
412
413 case HttpHeaders.CONTENT_TYPE_ORDINAL:
414 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
415
416
417 field.put(_header);
418 break;
419
420 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
421 if (_version == HttpVersions.HTTP_1_1_ORDINAL) transfer_encoding = field;
422
423 break;
424
425 case HttpHeaders.CONNECTION_ORDINAL:
426 if (_method!=null)
427 field.put(_header);
428
429 int connection_value = field.getValueOrdinal();
430 switch (connection_value)
431 {
432 case -1:
433 {
434 String[] values = field.getValue().split(",");
435 for (int i=0;values!=null && i<values.length;i++)
436 {
437 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
438
439 if (cb!=null)
440 {
441 switch(cb.getOrdinal())
442 {
443 case HttpHeaderValues.CLOSE_ORDINAL:
444 close=true;
445 if (_method==null)
446 _close=true;
447 keep_alive=false;
448 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
449 _contentLength = HttpTokens.EOF_CONTENT;
450 break;
451
452 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
453 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
454 {
455 keep_alive = true;
456 if (_method==null)
457 _close = false;
458 }
459 break;
460
461 default:
462 if (connection==null)
463 connection=new StringBuffer();
464 else
465 connection.append(',');
466 connection.append(values[i]);
467 }
468 }
469 else
470 {
471 if (connection==null)
472 connection=new StringBuffer();
473 else
474 connection.append(',');
475 connection.append(values[i]);
476 }
477 }
478
479 break;
480 }
481 case HttpHeaderValues.CLOSE_ORDINAL:
482 {
483 close=true;
484 if (_method==null)
485 _close=true;
486 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
487 _contentLength = HttpTokens.EOF_CONTENT;
488 break;
489 }
490 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
491 {
492 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
493 {
494 keep_alive = true;
495 if (_method==null)
496 _close = false;
497 }
498 break;
499 }
500 default:
501 {
502 if (connection==null)
503 connection=new StringBuffer();
504 else
505 connection.append(',');
506 connection.append(field.getValue());
507 }
508 }
509
510
511 break;
512
513 case HttpHeaders.SERVER_ORDINAL:
514 if (getSendServerVersion())
515 {
516 has_server=true;
517 field.put(_header);
518 }
519 break;
520
521 default:
522
523 field.put(_header);
524 }
525 }
526 }
527
528
529
530
531
532
533
534
535
536
537 switch ((int) _contentLength)
538 {
539 case HttpTokens.UNKNOWN_CONTENT:
540
541
542
543
544 if (_contentWritten == 0 && _method==null && (_status < 200 || _status == 204 || _status == 304))
545 _contentLength = HttpTokens.NO_CONTENT;
546 else if (_last)
547 {
548
549 _contentLength = _contentWritten;
550 if (content_length == null)
551 {
552
553 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
554 _header.put(HttpTokens.COLON);
555 _header.put((byte) ' ');
556 BufferUtil.putDecLong(_header, _contentLength);
557 _header.put(HttpTokens.CRLF);
558 }
559 }
560 else
561 {
562
563 _contentLength = (_close || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
564 if (_method!=null && _contentLength==HttpTokens.EOF_CONTENT)
565 {
566 _contentLength=HttpTokens.NO_CONTENT;
567 _noContent=true;
568 }
569 }
570 break;
571
572 case HttpTokens.NO_CONTENT:
573 if (content_length == null && _method==null && _status >= 200 && _status != 204 && _status != 304)
574 _header.put(CONTENT_LENGTH_0);
575 break;
576
577 case HttpTokens.EOF_CONTENT:
578 _close = _method==null;
579 break;
580
581 case HttpTokens.CHUNKED_CONTENT:
582 break;
583
584 default:
585
586 break;
587 }
588
589
590 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
591 {
592
593 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
594 {
595 String c = transfer_encoding.getValue();
596 if (c.endsWith(HttpHeaderValues.CHUNKED))
597 transfer_encoding.put(_header);
598 else
599 throw new IllegalArgumentException("BAD TE");
600 }
601 else
602 _header.put(TRANSFER_ENCODING_CHUNKED);
603 }
604
605
606 if (_contentLength==HttpTokens.EOF_CONTENT)
607 {
608 keep_alive=false;
609 _close=true;
610 }
611
612 if (_method==null)
613 {
614 if (_close && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
615 {
616 _header.put(CONNECTION_CLOSE);
617 if (connection!=null)
618 {
619 _header.setPutIndex(_header.putIndex()-2);
620 _header.put((byte)',');
621 _header.put(connection.toString().getBytes());
622 _header.put(CRLF);
623 }
624 }
625 else if (keep_alive)
626 {
627 _header.put(CONNECTION_KEEP_ALIVE);
628 if (connection!=null)
629 {
630 _header.setPutIndex(_header.putIndex()-2);
631 _header.put((byte)',');
632 _header.put(connection.toString().getBytes());
633 _header.put(CRLF);
634 }
635 }
636 else if (connection!=null)
637 {
638 _header.put(CONNECTION_);
639 _header.put(connection.toString().getBytes());
640 _header.put(CRLF);
641 }
642 }
643
644 if (!has_server && _status>100 && getSendServerVersion())
645 _header.put(SERVER);
646
647
648 _header.put(HttpTokens.CRLF);
649
650 _state = STATE_CONTENT;
651
652 }
653
654
655
656
657
658
659
660 public void complete() throws IOException
661 {
662 if (_state == STATE_END)
663 return;
664
665 super.complete();
666
667 if (_state < STATE_FLUSHING)
668 {
669 _state = STATE_FLUSHING;
670 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
671 _needEOC = true;
672 }
673
674 flush();
675 }
676
677
678 public long flush() throws IOException
679 {
680 try
681 {
682 if (_state == STATE_HEADER)
683 throw new IllegalStateException("State==HEADER");
684
685 prepareBuffers();
686
687 if (_endp == null)
688 {
689 if (_needCRLF && _buffer!=null)
690 _buffer.put(HttpTokens.CRLF);
691 if (_needEOC && _buffer!=null && !_head)
692 _buffer.put(LAST_CHUNK);
693 _needCRLF=false;
694 _needEOC=false;
695 return 0;
696 }
697
698
699 int total= 0;
700 long last_len = -1;
701 Flushing: while (true)
702 {
703 int len = -1;
704 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);
705 switch (to_flush)
706 {
707 case 7:
708 throw new IllegalStateException();
709 case 6:
710 len = _endp.flush(_header, _buffer, null);
711 break;
712 case 5:
713 len = _endp.flush(_header, _content, null);
714 break;
715 case 4:
716 len = _endp.flush(_header);
717 break;
718 case 3:
719 throw new IllegalStateException();
720 case 2:
721 len = _endp.flush(_buffer);
722 break;
723 case 1:
724 len = _endp.flush(_content);
725 break;
726 case 0:
727 {
728
729 if (_header != null)
730 _header.clear();
731
732 _bypass = false;
733 _bufferChunked = false;
734
735 if (_buffer != null)
736 {
737 _buffer.clear();
738 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
739 {
740
741 _buffer.setPutIndex(CHUNK_SPACE);
742 _buffer.setGetIndex(CHUNK_SPACE);
743
744
745
746 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
747 {
748 _buffer.put(_content);
749 _content.clear();
750 _content = null;
751 break Flushing;
752 }
753 }
754 }
755
756
757 if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0))
758 {
759 if (_state == STATE_FLUSHING)
760 _state = STATE_END;
761 if (_state==STATE_END && _close && _status!=100)
762 _endp.close();
763
764 break Flushing;
765 }
766
767
768 prepareBuffers();
769 }
770 }
771
772
773
774 if (len > 0)
775 total+=len;
776 else
777 break Flushing;
778
779 last_len = len;
780 }
781
782 return total;
783 }
784 catch (IOException e)
785 {
786 Log.ignore(e);
787 throw (e instanceof EofException) ? e:new EofException(e);
788 }
789 }
790
791
792 private void prepareBuffers()
793 {
794
795 if (!_bufferChunked)
796 {
797
798 if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
799 {
800 int len = _buffer.put(_content);
801 _content.skip(len);
802 if (_content.length() == 0)
803 _content = null;
804 }
805
806
807 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
808 {
809 int size = _buffer == null ? 0 : _buffer.length();
810 if (size > 0)
811 {
812
813 _bufferChunked = true;
814
815
816 if (_buffer.getIndex() == CHUNK_SPACE)
817 {
818
819 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
820 _buffer.setGetIndex(_buffer.getIndex() - 2);
821 BufferUtil.prependHexInt(_buffer, size);
822
823 if (_needCRLF)
824 {
825 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
826 _buffer.setGetIndex(_buffer.getIndex() - 2);
827 _needCRLF = false;
828 }
829 }
830 else
831 {
832
833 if (_needCRLF)
834 {
835 if (_header.length() > 0) throw new IllegalStateException("EOC");
836 _header.put(HttpTokens.CRLF);
837 _needCRLF = false;
838 }
839 BufferUtil.putHexInt(_header, size);
840 _header.put(HttpTokens.CRLF);
841 }
842
843
844 if (_buffer.space() >= 2)
845 _buffer.put(HttpTokens.CRLF);
846 else
847 _needCRLF = true;
848 }
849
850
851 if (_needEOC && (_content == null || _content.length() == 0))
852 {
853 if (_needCRLF)
854 {
855 if (_buffer == null && _header.space() >= 2)
856 {
857 _header.put(HttpTokens.CRLF);
858 _needCRLF = false;
859 }
860 else if (_buffer!=null && _buffer.space() >= 2)
861 {
862 _buffer.put(HttpTokens.CRLF);
863 _needCRLF = false;
864 }
865 }
866
867 if (!_needCRLF && _needEOC)
868 {
869 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
870 {
871 if (!_head)
872 {
873 _header.put(LAST_CHUNK);
874 _bufferChunked=true;
875 }
876 _needEOC = false;
877 }
878 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
879 {
880 if (!_head)
881 {
882 _buffer.put(LAST_CHUNK);
883 _bufferChunked=true;
884 }
885 _needEOC = false;
886 }
887 }
888 }
889 }
890 }
891
892 if (_content != null && _content.length() == 0)
893 _content = null;
894
895 }
896
897 public int getBytesBuffered()
898 {
899 return(_header==null?0:_header.length())+
900 (_buffer==null?0:_buffer.length())+
901 (_content==null?0:_content.length());
902 }
903
904 public boolean isEmpty()
905 {
906 return (_header==null||_header.length()==0) &&
907 (_buffer==null||_buffer.length()==0) &&
908 (_content==null||_content.length()==0);
909 }
910
911 public String toString()
912 {
913 return "HttpGenerator s="+_state+
914 " h="+(_header==null?"null":(""+_header.length()))+
915 " b="+(_buffer==null?"null":(""+_buffer.length()))+
916 " c="+(_content==null?"null":(""+_content.length()));
917 }
918 }