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