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