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