1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http;
20
21 import java.io.IOException;
22 import java.nio.BufferOverflowException;
23 import java.nio.ByteBuffer;
24 import java.util.Arrays;
25
26 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
27 import org.eclipse.jetty.util.BufferUtil;
28 import org.eclipse.jetty.util.StringUtil;
29 import org.eclipse.jetty.util.log.Log;
30 import org.eclipse.jetty.util.log.Logger;
31
32
33
34
35
36
37
38
39
40 public class HttpGenerator
41 {
42 private final static Logger LOG = Log.getLogger(HttpGenerator.class);
43
44 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
45
46 private final static byte[] __colon_space = new byte[] {':',' '};
47 private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
48 public static final MetaData.Response CONTINUE_100_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,100,null,null,-1);
49 public static final MetaData.Response PROGRESS_102_INFO = new MetaData.Response(HttpVersion.HTTP_1_1,102,null,null,-1);
50 public final static MetaData.Response RESPONSE_500_INFO =
51 new MetaData.Response(HttpVersion.HTTP_1_1,HttpStatus.INTERNAL_SERVER_ERROR_500,null,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0);
52
53
54 public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
55 public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
56
57
58 public static final int CHUNK_SIZE = 12;
59
60 private State _state = State.START;
61 private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
62
63 private long _contentPrepared = 0;
64 private boolean _noContent = false;
65 private Boolean _persistent = null;
66
67 private final int _send;
68 private final static int SEND_SERVER = 0x01;
69 private final static int SEND_XPOWEREDBY = 0x02;
70
71
72
73 public static void setJettyVersion(String serverVersion)
74 {
75 SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012");
76 SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012");
77 SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes("Server: " + serverVersion + "\015\012X-Powered-By: " +
78 serverVersion + "\015\012");
79 }
80
81
82
83 private boolean _needCRLF = false;
84
85
86 public HttpGenerator()
87 {
88 this(false,false);
89 }
90
91
92 public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
93 {
94 _send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
95 }
96
97
98 public void reset()
99 {
100 _state = State.START;
101 _endOfContent = EndOfContent.UNKNOWN_CONTENT;
102 _noContent=false;
103 _persistent = null;
104 _contentPrepared = 0;
105 _needCRLF = false;
106 }
107
108
109 @Deprecated
110 public boolean getSendServerVersion ()
111 {
112 return (_send&SEND_SERVER)!=0;
113 }
114
115
116 @Deprecated
117 public void setSendServerVersion (boolean sendServerVersion)
118 {
119 throw new UnsupportedOperationException();
120 }
121
122
123 public State getState()
124 {
125 return _state;
126 }
127
128
129 public boolean isState(State state)
130 {
131 return _state == state;
132 }
133
134
135 public boolean isIdle()
136 {
137 return _state == State.START;
138 }
139
140
141 public boolean isEnd()
142 {
143 return _state == State.END;
144 }
145
146
147 public boolean isCommitted()
148 {
149 return _state.ordinal() >= State.COMMITTED.ordinal();
150 }
151
152
153 public boolean isChunking()
154 {
155 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
156 }
157
158
159 public boolean isNoContent()
160 {
161 return _noContent;
162 }
163
164
165 public void setPersistent(boolean persistent)
166 {
167 _persistent=persistent;
168 }
169
170
171
172
173
174 public boolean isPersistent()
175 {
176 return Boolean.TRUE.equals(_persistent);
177 }
178
179
180 public boolean isWritten()
181 {
182 return _contentPrepared>0;
183 }
184
185
186 public long getContentPrepared()
187 {
188 return _contentPrepared;
189 }
190
191
192 public void abort()
193 {
194 _persistent=false;
195 _state=State.END;
196 _endOfContent=null;
197 }
198
199
200 public Result generateRequest(MetaData.Request info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
201 {
202 switch(_state)
203 {
204 case START:
205 {
206 if (info==null)
207 return Result.NEED_INFO;
208
209
210 if (header==null)
211 return Result.NEED_HEADER;
212
213
214 if (_persistent==null)
215 {
216 _persistent=info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal();
217 if (!_persistent && HttpMethod.CONNECT.is(info.getMethod()))
218 _persistent=true;
219 }
220
221
222 int pos=BufferUtil.flipToFill(header);
223 try
224 {
225
226 generateRequestLine(info,header);
227
228 if (info.getVersion()==HttpVersion.HTTP_0_9)
229 _noContent=true;
230 else
231 generateHeaders(info,header,content,last);
232
233 boolean expect100 = info.getFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
234
235 if (expect100)
236 {
237 _state = State.COMMITTED;
238 }
239 else
240 {
241
242 int len = BufferUtil.length(content);
243 if (len>0)
244 {
245 _contentPrepared+=len;
246 if (isChunking())
247 prepareChunk(header,len);
248 }
249 _state = last?State.COMPLETING:State.COMMITTED;
250 }
251
252 return Result.FLUSH;
253 }
254 catch(Exception e)
255 {
256 String message= (e instanceof BufferOverflowException)?"Request header too large":e.getMessage();
257 throw new IOException(message,e);
258 }
259 finally
260 {
261 BufferUtil.flipToFlush(header,pos);
262 }
263 }
264
265 case COMMITTED:
266 {
267 int len = BufferUtil.length(content);
268
269 if (len>0)
270 {
271
272 if (isChunking())
273 {
274
275 if (chunk==null)
276 return Result.NEED_CHUNK;
277 BufferUtil.clearToFill(chunk);
278 prepareChunk(chunk,len);
279 BufferUtil.flipToFlush(chunk,0);
280 }
281 _contentPrepared+=len;
282 }
283
284 if (last)
285 _state=State.COMPLETING;
286
287 return len>0?Result.FLUSH:Result.CONTINUE;
288 }
289
290 case COMPLETING:
291 {
292 if (BufferUtil.hasContent(content))
293 {
294 if (LOG.isDebugEnabled())
295 LOG.debug("discarding content in COMPLETING");
296 BufferUtil.clear(content);
297 }
298
299 if (isChunking())
300 {
301
302 if (chunk==null)
303 return Result.NEED_CHUNK;
304 BufferUtil.clearToFill(chunk);
305 prepareChunk(chunk,0);
306 BufferUtil.flipToFlush(chunk,0);
307 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
308 return Result.FLUSH;
309 }
310
311 _state=State.END;
312 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
313 }
314
315 case END:
316 if (BufferUtil.hasContent(content))
317 {
318 if (LOG.isDebugEnabled())
319 LOG.debug("discarding content in COMPLETING");
320 BufferUtil.clear(content);
321 }
322 return Result.DONE;
323
324 default:
325 throw new IllegalStateException();
326 }
327 }
328
329
330 public Result generateResponse(MetaData.Response info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
331 {
332 return generateResponse(info,false,header,chunk,content,last);
333 }
334
335
336 public Result generateResponse(MetaData.Response info, boolean head, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
337 {
338 switch(_state)
339 {
340 case START:
341 {
342 if (info==null)
343 return Result.NEED_INFO;
344
345
346 if (info.getVersion() == HttpVersion.HTTP_0_9)
347 {
348 _persistent = false;
349 _endOfContent=EndOfContent.EOF_CONTENT;
350 if (BufferUtil.hasContent(content))
351 _contentPrepared+=content.remaining();
352 _state = last?State.COMPLETING:State.COMMITTED;
353 return Result.FLUSH;
354 }
355
356
357 if (header==null)
358 return Result.NEED_HEADER;
359
360
361 if (_persistent==null)
362 _persistent=(info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
363
364
365 int pos=BufferUtil.flipToFill(header);
366 try
367 {
368
369 generateResponseLine(info,header);
370
371
372 int status=info.getStatus();
373 if (status>=100 && status<200 )
374 {
375 _noContent=true;
376
377 if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
378 {
379 header.put(HttpTokens.CRLF);
380 _state=State.COMPLETING_1XX;
381 return Result.FLUSH;
382 }
383 }
384 else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
385 {
386 _noContent=true;
387 }
388
389 generateHeaders(info,header,content,last);
390
391
392 int len = BufferUtil.length(content);
393 if (len>0)
394 {
395 _contentPrepared+=len;
396 if (isChunking() && !head)
397 prepareChunk(header,len);
398 }
399 _state = last?State.COMPLETING:State.COMMITTED;
400 }
401 catch(Exception e)
402 {
403 String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
404 throw new IOException(message,e);
405 }
406 finally
407 {
408 BufferUtil.flipToFlush(header,pos);
409 }
410
411 return Result.FLUSH;
412 }
413
414 case COMMITTED:
415 {
416 int len = BufferUtil.length(content);
417
418
419 if (len>0)
420 {
421 if (isChunking())
422 {
423 if (chunk==null)
424 return Result.NEED_CHUNK;
425 BufferUtil.clearToFill(chunk);
426 prepareChunk(chunk,len);
427 BufferUtil.flipToFlush(chunk,0);
428 }
429 _contentPrepared+=len;
430 }
431
432 if (last)
433 {
434 _state=State.COMPLETING;
435 return len>0?Result.FLUSH:Result.CONTINUE;
436 }
437 return len>0?Result.FLUSH:Result.DONE;
438
439 }
440
441 case COMPLETING_1XX:
442 {
443 reset();
444 return Result.DONE;
445 }
446
447 case COMPLETING:
448 {
449 if (BufferUtil.hasContent(content))
450 {
451 if (LOG.isDebugEnabled())
452 LOG.debug("discarding content in COMPLETING");
453 BufferUtil.clear(content);
454 }
455
456 if (isChunking())
457 {
458
459 if (chunk==null)
460 return Result.NEED_CHUNK;
461
462
463 BufferUtil.clearToFill(chunk);
464 prepareChunk(chunk,0);
465 BufferUtil.flipToFlush(chunk,0);
466 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
467 return Result.FLUSH;
468 }
469
470 _state=State.END;
471
472 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
473 }
474
475 case END:
476 if (BufferUtil.hasContent(content))
477 {
478 if (LOG.isDebugEnabled())
479 LOG.debug("discarding content in COMPLETING");
480 BufferUtil.clear(content);
481 }
482 return Result.DONE;
483
484 default:
485 throw new IllegalStateException();
486 }
487 }
488
489
490 private void prepareChunk(ByteBuffer chunk, int remaining)
491 {
492
493 if (_needCRLF)
494 BufferUtil.putCRLF(chunk);
495
496
497 if (remaining>0)
498 {
499 BufferUtil.putHexInt(chunk, remaining);
500 BufferUtil.putCRLF(chunk);
501 _needCRLF=true;
502 }
503 else
504 {
505 chunk.put(LAST_CHUNK);
506 _needCRLF=false;
507 }
508 }
509
510
511 private void generateRequestLine(MetaData.Request request,ByteBuffer header)
512 {
513 header.put(StringUtil.getBytes(request.getMethod()));
514 header.put((byte)' ');
515 header.put(StringUtil.getBytes(request.getURIString()));
516 switch(request.getVersion())
517 {
518 case HTTP_1_0:
519 case HTTP_1_1:
520 header.put((byte)' ');
521 header.put(request.getVersion().toBytes());
522 break;
523 default:
524 throw new IllegalStateException();
525 }
526 header.put(HttpTokens.CRLF);
527 }
528
529
530 private void generateResponseLine(MetaData.Response response, ByteBuffer header)
531 {
532
533 int status=response.getStatus();
534 PreparedResponse preprepared = status<__preprepared.length?__preprepared[status]:null;
535 String reason=response.getReason();
536 if (preprepared!=null)
537 {
538 if (reason==null)
539 header.put(preprepared._responseLine);
540 else
541 {
542 header.put(preprepared._schemeCode);
543 header.put(getReasonBytes(reason));
544 header.put(HttpTokens.CRLF);
545 }
546 }
547 else
548 {
549 header.put(HTTP_1_1_SPACE);
550 header.put((byte) ('0' + status / 100));
551 header.put((byte) ('0' + (status % 100) / 10));
552 header.put((byte) ('0' + (status % 10)));
553 header.put((byte) ' ');
554 if (reason==null)
555 {
556 header.put((byte) ('0' + status / 100));
557 header.put((byte) ('0' + (status % 100) / 10));
558 header.put((byte) ('0' + (status % 10)));
559 }
560 else
561 header.put(getReasonBytes(reason));
562 header.put(HttpTokens.CRLF);
563 }
564 }
565
566
567 private byte[] getReasonBytes(String reason)
568 {
569 if (reason.length()>1024)
570 reason=reason.substring(0,1024);
571 byte[] _bytes = StringUtil.getBytes(reason);
572
573 for (int i=_bytes.length;i-->0;)
574 if (_bytes[i]=='\r' || _bytes[i]=='\n')
575 _bytes[i]='?';
576 return _bytes;
577 }
578
579
580 private void generateHeaders(MetaData _info,ByteBuffer header,ByteBuffer content,boolean last)
581 {
582 final MetaData.Request request=(_info instanceof MetaData.Request)?(MetaData.Request)_info:null;
583 final MetaData.Response response=(_info instanceof MetaData.Response)?(MetaData.Response)_info:null;
584
585
586 int send=_send;
587 HttpField transfer_encoding=null;
588 boolean keep_alive=false;
589 boolean close=false;
590 boolean content_type=false;
591 StringBuilder connection = null;
592
593
594 if (_info.getFields() != null)
595 {
596 for (HttpField field : _info.getFields())
597 {
598 String v = field.getValue();
599 if (v==null || v.length()==0)
600 continue;
601
602 HttpHeader h = field.getHeader();
603
604 switch (h==null?HttpHeader.UNKNOWN:h)
605 {
606 case CONTENT_LENGTH:
607
608 if (_info.getContentLength()>=0)
609 _endOfContent=EndOfContent.CONTENT_LENGTH;
610 break;
611
612 case CONTENT_TYPE:
613 {
614 if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
615 _endOfContent=EndOfContent.SELF_DEFINING_CONTENT;
616
617
618 content_type=true;
619 putTo(field,header);
620 break;
621 }
622
623 case TRANSFER_ENCODING:
624 {
625 if (_info.getVersion() == HttpVersion.HTTP_1_1)
626 transfer_encoding = field;
627
628 break;
629 }
630
631 case CONNECTION:
632 {
633 if (request!=null)
634 putTo(field,header);
635
636
637 HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
638 String[] split = null;
639
640 if (values[0]==null)
641 {
642 split = StringUtil.csvSplit(field.getValue());
643 if (split.length>0)
644 {
645 values=new HttpHeaderValue[split.length];
646 for (int i=0;i<split.length;i++)
647 values[i]=HttpHeaderValue.CACHE.get(split[i]);
648 }
649 }
650
651
652 for (int i=0;i<values.length;i++)
653 {
654 HttpHeaderValue value=values[i];
655 switch (value==null?HttpHeaderValue.UNKNOWN:value)
656 {
657 case UPGRADE:
658 {
659
660 header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
661 header.put(CRLF);
662 break;
663 }
664
665 case CLOSE:
666 {
667 close=true;
668 _persistent=false;
669 if (response!=null)
670 {
671 if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
672 _endOfContent=EndOfContent.EOF_CONTENT;
673 }
674 break;
675 }
676
677 case KEEP_ALIVE:
678 {
679 if (_info.getVersion() == HttpVersion.HTTP_1_0)
680 {
681 keep_alive = true;
682 if (response!=null)
683 _persistent=true;
684 }
685 break;
686 }
687
688 default:
689 {
690 if (connection==null)
691 connection=new StringBuilder();
692 else
693 connection.append(',');
694 connection.append(split==null?field.getValue():split[i]);
695 }
696 }
697 }
698
699
700 break;
701 }
702
703 case SERVER:
704 {
705 send=send&~SEND_SERVER;
706 putTo(field,header);
707 break;
708 }
709
710 default:
711 putTo(field,header);
712 }
713 }
714 }
715
716
717
718
719
720
721
722
723
724
725
726 int status=response!=null?response.getStatus():-1;
727 switch (_endOfContent)
728 {
729 case UNKNOWN_CONTENT:
730
731
732
733
734 if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
735 _endOfContent=EndOfContent.NO_CONTENT;
736 else if (_info.getContentLength()>0)
737 {
738
739 _endOfContent=EndOfContent.CONTENT_LENGTH;
740 long content_length = _info.getContentLength();
741 if ((response!=null || content_length>0 || content_type ) && !_noContent)
742 {
743
744 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
745 BufferUtil.putDecLong(header, content_length);
746 header.put(HttpTokens.CRLF);
747 }
748 }
749 else if (last)
750 {
751
752 _endOfContent=EndOfContent.CONTENT_LENGTH;
753 long content_length = _contentPrepared+BufferUtil.length(content);
754
755
756 if ((response!=null || content_length>0 || content_type ) && !_noContent)
757 {
758 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
759 BufferUtil.putDecLong(header, content_length);
760 header.put(HttpTokens.CRLF);
761 }
762 }
763 else
764 {
765
766 _endOfContent = EndOfContent.CHUNKED_CONTENT;
767
768
769
770
771 if (!isPersistent() || _info.getVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
772 _endOfContent = EndOfContent.EOF_CONTENT;
773 }
774 break;
775
776 case CONTENT_LENGTH:
777 {
778 long content_length = _info.getContentLength();
779 if ((response!=null || content_length>0 || content_type ) && !_noContent)
780 {
781 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
782 BufferUtil.putDecLong(header, content_length);
783 header.put(HttpTokens.CRLF);
784 }
785 break;
786 }
787
788 case SELF_DEFINING_CONTENT:
789 {
790
791 long content_length = _info.getContentLength();
792 if (content_length>0)
793 {
794 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
795 BufferUtil.putDecLong(header, content_length);
796 header.put(HttpTokens.CRLF);
797 }
798 break;
799 }
800 case NO_CONTENT:
801 if (response!=null && status >= 200 && status != 204 && status != 304)
802 header.put(CONTENT_LENGTH_0);
803 break;
804
805 case EOF_CONTENT:
806 _persistent = request!=null;
807 break;
808
809 case CHUNKED_CONTENT:
810 break;
811
812 default:
813 break;
814 }
815
816
817 if (isChunking())
818 {
819
820 if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue()))
821 {
822 String c = transfer_encoding.getValue();
823 if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
824 putTo(transfer_encoding,header);
825 else
826 throw new IllegalArgumentException("BAD TE");
827 }
828 else
829 header.put(TRANSFER_ENCODING_CHUNKED);
830 }
831
832
833 if (_endOfContent==EndOfContent.EOF_CONTENT)
834 {
835 keep_alive=false;
836 _persistent=false;
837 }
838
839
840 if (response!=null)
841 {
842 if (!isPersistent() && (close || _info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
843 {
844 if (connection==null)
845 header.put(CONNECTION_CLOSE);
846 else
847 {
848 header.put(CONNECTION_CLOSE,0,CONNECTION_CLOSE.length-2);
849 header.put((byte)',');
850 header.put(StringUtil.getBytes(connection.toString()));
851 header.put(CRLF);
852 }
853 }
854 else if (keep_alive)
855 {
856 if (connection==null)
857 header.put(CONNECTION_KEEP_ALIVE);
858 else
859 {
860 header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
861 header.put((byte)',');
862 header.put(StringUtil.getBytes(connection.toString()));
863 header.put(CRLF);
864 }
865 }
866 else if (connection!=null)
867 {
868 header.put(HttpHeader.CONNECTION.getBytesColonSpace());
869 header.put(StringUtil.getBytes(connection.toString()));
870 header.put(CRLF);
871 }
872 }
873
874 if (status>199)
875 header.put(SEND[send]);
876
877
878 header.put(HttpTokens.CRLF);
879 }
880
881
882 public static byte[] getReasonBuffer(int code)
883 {
884 PreparedResponse status = code<__preprepared.length?__preprepared[code]:null;
885 if (status!=null)
886 return status._reason;
887 return null;
888 }
889
890
891 @Override
892 public String toString()
893 {
894 return String.format("%s@%x{s=%s}",
895 getClass().getSimpleName(),
896 hashCode(),
897 _state);
898 }
899
900
901
902
903
904 private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
905 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
906 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
907 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
908 private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
909 private static final byte[] CRLF = StringUtil.getBytes("\015\012");
910 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
911 private static final byte[][] SEND = new byte[][]{
912 new byte[0],
913 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
914 StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
915 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
916 };
917
918
919
920
921
922 private static class PreparedResponse
923 {
924 byte[] _reason;
925 byte[] _schemeCode;
926 byte[] _responseLine;
927 }
928 private static final PreparedResponse[] __preprepared = new PreparedResponse[HttpStatus.MAX_CODE+1];
929 static
930 {
931 int versionLength=HttpVersion.HTTP_1_1.toString().length();
932
933 for (int i=0;i<__preprepared.length;i++)
934 {
935 HttpStatus.Code code = HttpStatus.getCode(i);
936 if (code==null)
937 continue;
938 String reason=code.getMessage();
939 byte[] line=new byte[versionLength+5+reason.length()+2];
940 HttpVersion.HTTP_1_1.toBuffer().get(line,0,versionLength);
941 line[versionLength+0]=' ';
942 line[versionLength+1]=(byte)('0'+i/100);
943 line[versionLength+2]=(byte)('0'+(i%100)/10);
944 line[versionLength+3]=(byte)('0'+(i%10));
945 line[versionLength+4]=' ';
946 for (int j=0;j<reason.length();j++)
947 line[versionLength+5+j]=(byte)reason.charAt(j);
948 line[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN;
949 line[versionLength+6+reason.length()]=HttpTokens.LINE_FEED;
950
951 __preprepared[i] = new PreparedResponse();
952 __preprepared[i]._schemeCode = Arrays.copyOfRange(line, 0,versionLength+5);
953 __preprepared[i]._reason = Arrays.copyOfRange(line, versionLength+5, line.length-2);
954 __preprepared[i]._responseLine=line;
955 }
956 }
957
958 private static void putSanitisedName(String s,ByteBuffer buffer)
959 {
960 int l=s.length();
961 for (int i=0;i<l;i++)
962 {
963 char c=s.charAt(i);
964
965 if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
966 buffer.put((byte)'?');
967 else
968 buffer.put((byte)(0xff&c));
969 }
970 }
971
972 private static void putSanitisedValue(String s,ByteBuffer buffer)
973 {
974 int l=s.length();
975 for (int i=0;i<l;i++)
976 {
977 char c=s.charAt(i);
978
979 if (c<0 || c>0xff || c=='\r' || c=='\n')
980 buffer.put((byte)' ');
981 else
982 buffer.put((byte)(0xff&c));
983 }
984 }
985
986 public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
987 {
988 if (field instanceof PreEncodedHttpField)
989 {
990 ((PreEncodedHttpField)field).putTo(bufferInFillMode,HttpVersion.HTTP_1_0);
991 }
992 else
993 {
994 HttpHeader header=field.getHeader();
995 if (header!=null)
996 {
997 bufferInFillMode.put(header.getBytesColonSpace());
998 putSanitisedValue(field.getValue(),bufferInFillMode);
999 }
1000 else
1001 {
1002 putSanitisedName(field.getName(),bufferInFillMode);
1003 bufferInFillMode.put(__colon_space);
1004 putSanitisedValue(field.getValue(),bufferInFillMode);
1005 }
1006
1007 BufferUtil.putCRLF(bufferInFillMode);
1008 }
1009 }
1010
1011 public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
1012 {
1013 for (HttpField field : fields)
1014 {
1015 if (field != null)
1016 putTo(field,bufferInFillMode);
1017 }
1018 BufferUtil.putCRLF(bufferInFillMode);
1019 }
1020 }