2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.http;
21 import java.io.IOException;
22 import java.nio.BufferOverflowException;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
25 import java.util.Arrays;
27 import org.eclipse.jetty.http.HttpTokens.EndOfContent;
28 import org.eclipse.jetty.util.BufferUtil;
29 import org.eclipse.jetty.util.StringUtil;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
33 /* ------------------------------------------------------------ */
35 * HttpGenerator. Builds HTTP Messages.
37 * If the system property "org.eclipse.jetty.http.HttpGenerator.STRICT" is set to true,
38 * then the generator will strictly pass on the exact strings received from methods and header
39 * fields. Otherwise a fast case insensitive string lookup is used that may alter the
40 * case and white space of some methods/headers
43 public class HttpGenerator
45 private final static Logger LOG = Log.getLogger(HttpGenerator.class);
47 public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT");
49 private final static byte[] __colon_space = new byte[] {':',' '};
50 private final static HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE};
51 public static final ResponseInfo CONTINUE_100_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,100,null,false);
52 public static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
53 public final static ResponseInfo RESPONSE_500_INFO =
54 new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0,HttpStatus.INTERNAL_SERVER_ERROR_500,null,false);
57 public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
58 public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
61 public static final int CHUNK_SIZE = 12;
63 private State _state = State.START;
64 private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
66 private long _contentPrepared = 0;
67 private boolean _noContent = false;
68 private Boolean _persistent = null;
70 private final int _send;
71 private final static int SEND_SERVER = 0x01;
72 private final static int SEND_XPOWEREDBY = 0x02;
75 /* ------------------------------------------------------------------------------- */
76 public static void setJettyVersion(String serverVersion)
78 SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012");
79 SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012");
80 SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes("Server: " + serverVersion + "\015\012X-Powered-By: " +
81 serverVersion + "\015\012");
84 /* ------------------------------------------------------------------------------- */
86 private boolean _needCRLF = false;
88 /* ------------------------------------------------------------------------------- */
89 public HttpGenerator()
94 /* ------------------------------------------------------------------------------- */
95 public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
97 _send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
100 /* ------------------------------------------------------------------------------- */
103 _state = State.START;
104 _endOfContent = EndOfContent.UNKNOWN_CONTENT;
107 _contentPrepared = 0;
111 /* ------------------------------------------------------------ */
113 public boolean getSendServerVersion ()
115 return (_send&SEND_SERVER)!=0;
118 /* ------------------------------------------------------------ */
120 public void setSendServerVersion (boolean sendServerVersion)
122 throw new UnsupportedOperationException();
125 /* ------------------------------------------------------------ */
126 public State getState()
131 /* ------------------------------------------------------------ */
132 public boolean isState(State state)
134 return _state == state;
137 /* ------------------------------------------------------------ */
138 public boolean isIdle()
140 return _state == State.START;
143 /* ------------------------------------------------------------ */
144 public boolean isEnd()
146 return _state == State.END;
149 /* ------------------------------------------------------------ */
150 public boolean isCommitted()
152 return _state.ordinal() >= State.COMMITTED.ordinal();
155 /* ------------------------------------------------------------ */
156 public boolean isChunking()
158 return _endOfContent==EndOfContent.CHUNKED_CONTENT;
161 /* ------------------------------------------------------------ */
162 public void setPersistent(boolean persistent)
164 _persistent=persistent;
167 /* ------------------------------------------------------------ */
169 * @return true if known to be persistent
171 public boolean isPersistent()
173 return Boolean.TRUE.equals(_persistent);
176 /* ------------------------------------------------------------ */
177 public boolean isWritten()
179 return _contentPrepared>0;
182 /* ------------------------------------------------------------ */
183 public long getContentPrepared()
185 return _contentPrepared;
188 /* ------------------------------------------------------------ */
196 /* ------------------------------------------------------------ */
197 public Result generateRequest(RequestInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
204 return Result.NEED_INFO;
206 // Do we need a request header
208 return Result.NEED_HEADER;
210 // If we have not been told our persistence, set the default
211 if (_persistent==null)
212 _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
214 // prepare the header
215 int pos=BufferUtil.flipToFill(header);
218 // generate ResponseLine
219 generateRequestLine(info,header);
221 if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
224 generateHeaders(info,header,content,last);
226 boolean expect100 = info.getHttpFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
230 _state = State.COMMITTED;
234 // handle the content.
235 int len = BufferUtil.length(content);
238 _contentPrepared+=len;
240 prepareChunk(header,len);
242 _state = last?State.COMPLETING:State.COMMITTED;
249 String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
250 throw new IOException(message,e);
254 BufferUtil.flipToFlush(header,pos);
260 int len = BufferUtil.length(content);
264 // Do we need a chunk buffer?
267 // Do we need a chunk buffer?
269 return Result.NEED_CHUNK;
270 BufferUtil.clearToFill(chunk);
271 prepareChunk(chunk,len);
272 BufferUtil.flipToFlush(chunk,0);
274 _contentPrepared+=len;
279 _state=State.COMPLETING;
280 return len>0?Result.FLUSH:Result.CONTINUE;
288 if (BufferUtil.hasContent(content))
290 LOG.debug("discarding content in COMPLETING");
291 BufferUtil.clear(content);
296 // Do we need a chunk buffer?
298 return Result.NEED_CHUNK;
299 BufferUtil.clearToFill(chunk);
300 prepareChunk(chunk,0);
301 BufferUtil.flipToFlush(chunk,0);
302 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
307 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
311 if (BufferUtil.hasContent(content))
313 LOG.debug("discarding content in COMPLETING");
314 BufferUtil.clear(content);
319 throw new IllegalStateException();
323 /* ------------------------------------------------------------ */
324 public Result generateResponse(ResponseInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
331 return Result.NEED_INFO;
334 if (info.getHttpVersion() == HttpVersion.HTTP_0_9)
337 _endOfContent=EndOfContent.EOF_CONTENT;
338 if (BufferUtil.hasContent(content))
339 _contentPrepared+=content.remaining();
340 _state = last?State.COMPLETING:State.COMMITTED;
344 // Do we need a response header
346 return Result.NEED_HEADER;
348 // If we have not been told our persistence, set the default
349 if (_persistent==null)
350 _persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
352 // prepare the header
353 int pos=BufferUtil.flipToFill(header);
356 // generate ResponseLine
357 generateResponseLine(info,header);
359 // Handle 1xx and no content responses
360 int status=info.getStatus();
361 if (status>=100 && status<200 )
365 if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
367 header.put(HttpTokens.CRLF);
368 _state=State.COMPLETING_1XX;
372 else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
377 generateHeaders(info,header,content,last);
379 // handle the content.
380 int len = BufferUtil.length(content);
383 _contentPrepared+=len;
384 if (isChunking() && !info.isHead())
385 prepareChunk(header,len);
387 _state = last?State.COMPLETING:State.COMMITTED;
391 String message= (e instanceof BufferOverflowException)?"Response header too large":e.getMessage();
392 throw new IOException(message,e);
396 BufferUtil.flipToFlush(header,pos);
404 int len = BufferUtil.length(content);
406 // handle the content.
412 return Result.NEED_CHUNK;
413 BufferUtil.clearToFill(chunk);
414 prepareChunk(chunk,len);
415 BufferUtil.flipToFlush(chunk,0);
417 _contentPrepared+=len;
422 _state=State.COMPLETING;
423 return len>0?Result.FLUSH:Result.CONTINUE;
425 return len>0?Result.FLUSH:Result.DONE;
437 if (BufferUtil.hasContent(content))
439 LOG.debug("discarding content in COMPLETING");
440 BufferUtil.clear(content);
445 // Do we need a chunk buffer?
447 return Result.NEED_CHUNK;
449 // Write the last chunk
450 BufferUtil.clearToFill(chunk);
451 prepareChunk(chunk,0);
452 BufferUtil.flipToFlush(chunk,0);
453 _endOfContent=EndOfContent.UNKNOWN_CONTENT;
459 return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
463 if (BufferUtil.hasContent(content))
465 LOG.debug("discarding content in COMPLETING");
466 BufferUtil.clear(content);
471 throw new IllegalStateException();
475 /* ------------------------------------------------------------ */
476 private void prepareChunk(ByteBuffer chunk, int remaining)
478 // if we need CRLF add this to header
480 BufferUtil.putCRLF(chunk);
482 // Add the chunk size to the header
485 BufferUtil.putHexInt(chunk, remaining);
486 BufferUtil.putCRLF(chunk);
491 chunk.put(LAST_CHUNK);
496 /* ------------------------------------------------------------ */
497 private void generateRequestLine(RequestInfo request,ByteBuffer header)
499 header.put(StringUtil.getBytes(request.getMethod()));
500 header.put((byte)' ');
501 header.put(StringUtil.getBytes(request.getUri()));
502 switch(request.getHttpVersion())
506 header.put((byte)' ');
507 header.put(request.getHttpVersion().toBytes());
510 throw new IllegalStateException();
512 header.put(HttpTokens.CRLF);
515 /* ------------------------------------------------------------ */
516 private void generateResponseLine(ResponseInfo response, ByteBuffer header)
518 // Look for prepared response line
519 int status=response.getStatus();
520 PreparedResponse preprepared = status<__preprepared.length?__preprepared[status]:null;
521 String reason=response.getReason();
522 if (preprepared!=null)
525 header.put(preprepared._responseLine);
528 header.put(preprepared._schemeCode);
529 header.put(getReasonBytes(reason));
530 header.put(HttpTokens.CRLF);
533 else // generate response line
535 header.put(HTTP_1_1_SPACE);
536 header.put((byte) ('0' + status / 100));
537 header.put((byte) ('0' + (status % 100) / 10));
538 header.put((byte) ('0' + (status % 10)));
539 header.put((byte) ' ');
542 header.put((byte) ('0' + status / 100));
543 header.put((byte) ('0' + (status % 100) / 10));
544 header.put((byte) ('0' + (status % 10)));
547 header.put(getReasonBytes(reason));
548 header.put(HttpTokens.CRLF);
552 /* ------------------------------------------------------------ */
553 private byte[] getReasonBytes(String reason)
555 if (reason.length()>1024)
556 reason=reason.substring(0,1024);
557 byte[] _bytes = StringUtil.getBytes(reason);
559 for (int i=_bytes.length;i-->0;)
560 if (_bytes[i]=='\r' || _bytes[i]=='\n')
565 /* ------------------------------------------------------------ */
566 private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
568 final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
569 final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
571 // default field values
573 HttpField transfer_encoding=null;
574 boolean keep_alive=false;
576 boolean content_type=false;
577 StringBuilder connection = null;
580 if (_info.getHttpFields() != null)
582 for (HttpField field : _info.getHttpFields())
584 HttpHeader h = field.getHeader();
586 switch (h==null?HttpHeader.UNKNOWN:h)
589 // handle specially below
590 if (_info.getContentLength()>=0)
591 _endOfContent=EndOfContent.CONTENT_LENGTH;
596 if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
597 _endOfContent=EndOfContent.SELF_DEFINING_CONTENT;
599 // write the field to the header
605 case TRANSFER_ENCODING:
607 if (_info.getHttpVersion() == HttpVersion.HTTP_1_1)
608 transfer_encoding = field;
618 // Lookup and/or split connection value field
619 HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue())?CLOSE:new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
620 String[] split = null;
624 split = field.getValue().split("\\s*,\\s*");
627 values=new HttpHeaderValue[split.length];
628 for (int i=0;i<split.length;i++)
629 values[i]=HttpHeaderValue.CACHE.get(split[i]);
633 // Handle connection values
634 for (int i=0;i<values.length;i++)
636 HttpHeaderValue value=values[i];
637 switch (value==null?HttpHeaderValue.UNKNOWN:value)
641 // special case for websocket connection ordering
642 header.put(HttpHeader.CONNECTION.getBytesColonSpace()).put(HttpHeader.UPGRADE.getBytes());
653 if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
654 _endOfContent=EndOfContent.EOF_CONTENT;
661 if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
672 if (connection==null)
673 connection=new StringBuilder();
675 connection.append(',');
676 connection.append(split==null?field.getValue():split[i]);
687 send=send&~SEND_SERVER;
699 // Calculate how to end _content and connection, _content length and transfer encoding
701 // From RFC 2616 4.4:
702 // 1. No body for 1xx, 204, 304 & HEAD response
703 // 2. Force _content-length?
704 // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk
706 // 5. multipart/byteranges
708 int status=response!=null?response.getStatus():-1;
709 switch (_endOfContent)
711 case UNKNOWN_CONTENT:
712 // It may be that we have no _content, or perhaps _content just has not been
715 // Response known not to have a body
716 if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
717 _endOfContent=EndOfContent.NO_CONTENT;
718 else if (_info.getContentLength()>0)
720 // we have been given a content length
721 _endOfContent=EndOfContent.CONTENT_LENGTH;
722 long content_length = _info.getContentLength();
723 if ((response!=null || content_length>0 || content_type ) && !_noContent)
725 // known length but not actually set.
726 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
727 BufferUtil.putDecLong(header, content_length);
728 header.put(HttpTokens.CRLF);
733 // we have seen all the _content there is, so we can be content-length limited.
734 _endOfContent=EndOfContent.CONTENT_LENGTH;
735 long content_length = _contentPrepared+BufferUtil.length(content);
737 // Do we need to tell the headers about it
738 if ((response!=null || content_length>0 || content_type ) && !_noContent)
740 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
741 BufferUtil.putDecLong(header, content_length);
742 header.put(HttpTokens.CRLF);
747 // No idea, so we must assume that a body is coming.
748 _endOfContent = EndOfContent.CHUNKED_CONTENT;
749 // HTTP 1.0 does not understand chunked content, so we must use EOF content.
750 // For a request with HTTP 1.0 & Connection: keep-alive
751 // we *must* close the connection, otherwise the client
752 // has no way to detect the end of the content.
753 if (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
754 _endOfContent = EndOfContent.EOF_CONTENT;
759 long content_length = _info.getContentLength();
760 if ((response!=null || content_length>0 || content_type ) && !_noContent)
762 // known length but not actually set.
763 header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
764 BufferUtil.putDecLong(header, content_length);
765 header.put(HttpTokens.CRLF);
770 if (response!=null && status >= 200 && status != 204 && status != 304)
771 header.put(CONTENT_LENGTH_0);
775 _persistent = request!=null;
778 case CHUNKED_CONTENT:
785 // Add transfer_encoding if needed
788 // try to use user supplied encoding as it may have other values.
789 if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue()))
791 String c = transfer_encoding.getValue();
792 if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
793 putTo(transfer_encoding,header);
795 throw new IllegalArgumentException("BAD TE");
798 header.put(TRANSFER_ENCODING_CHUNKED);
801 // Handle connection if need be
802 if (_endOfContent==EndOfContent.EOF_CONTENT)
808 // If this is a response, work out persistence
811 if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
813 if (connection==null)
814 header.put(CONNECTION_CLOSE);
817 header.put(CONNECTION_CLOSE,0,CONNECTION_CLOSE.length-2);
818 header.put((byte)',');
819 header.put(StringUtil.getBytes(connection.toString()));
825 if (connection==null)
826 header.put(CONNECTION_KEEP_ALIVE);
829 header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
830 header.put((byte)',');
831 header.put(StringUtil.getBytes(connection.toString()));
835 else if (connection!=null)
837 header.put(HttpHeader.CONNECTION.getBytesColonSpace());
838 header.put(StringUtil.getBytes(connection.toString()));
844 header.put(SEND[send]);
847 header.put(HttpTokens.CRLF);
850 /* ------------------------------------------------------------------------------- */
851 public static byte[] getReasonBuffer(int code)
853 PreparedResponse status = code<__preprepared.length?__preprepared[code]:null;
855 return status._reason;
859 /* ------------------------------------------------------------------------------- */
861 public String toString()
863 return String.format("%s{s=%s}",
864 getClass().getSimpleName(),
868 /* ------------------------------------------------------------------------------- */
869 /* ------------------------------------------------------------------------------- */
870 /* ------------------------------------------------------------------------------- */
872 private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
873 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
874 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
875 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
876 private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
877 private static final byte[] CRLF = StringUtil.getBytes("\015\012");
878 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
879 private static final byte[][] SEND = new byte[][]{
881 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
882 StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
883 StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
886 /* ------------------------------------------------------------------------------- */
887 /* ------------------------------------------------------------------------------- */
888 /* ------------------------------------------------------------------------------- */
889 // Build cache of response lines for status
890 private static class PreparedResponse
894 byte[] _responseLine;
896 private static final PreparedResponse[] __preprepared = new PreparedResponse[HttpStatus.MAX_CODE+1];
899 int versionLength=HttpVersion.HTTP_1_1.toString().length();
901 for (int i=0;i<__preprepared.length;i++)
903 HttpStatus.Code code = HttpStatus.getCode(i);
906 String reason=code.getMessage();
907 byte[] line=new byte[versionLength+5+reason.length()+2];
908 HttpVersion.HTTP_1_1.toBuffer().get(line,0,versionLength);
909 line[versionLength+0]=' ';
910 line[versionLength+1]=(byte)('0'+i/100);
911 line[versionLength+2]=(byte)('0'+(i%100)/10);
912 line[versionLength+3]=(byte)('0'+(i%10));
913 line[versionLength+4]=' ';
914 for (int j=0;j<reason.length();j++)
915 line[versionLength+5+j]=(byte)reason.charAt(j);
916 line[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN;
917 line[versionLength+6+reason.length()]=HttpTokens.LINE_FEED;
919 __preprepared[i] = new PreparedResponse();
920 __preprepared[i]._schemeCode = Arrays.copyOfRange(line, 0,versionLength+5);
921 __preprepared[i]._reason = Arrays.copyOfRange(line, versionLength+5, line.length-2);
922 __preprepared[i]._responseLine=line;
926 public static class Info
928 final HttpVersion _httpVersion;
929 final HttpFields _httpFields;
930 final long _contentLength;
932 private Info(HttpVersion httpVersion, HttpFields httpFields, long contentLength)
934 _httpVersion = httpVersion;
935 _httpFields = httpFields;
936 _contentLength = contentLength;
939 public HttpVersion getHttpVersion()
943 public HttpFields getHttpFields()
947 public long getContentLength()
949 return _contentLength;
953 public static class RequestInfo extends Info
955 private final String _method;
956 private final String _uri;
958 public RequestInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, String method, String uri)
960 super(httpVersion,httpFields,contentLength);
965 public String getMethod()
970 public String getUri()
976 public String toString()
978 return String.format("RequestInfo{%s %s %s,%d}",_method,_uri,_httpVersion,_contentLength);
982 public static class ResponseInfo extends Info
984 private final int _status;
985 private final String _reason;
986 private final boolean _head;
988 public ResponseInfo(HttpVersion httpVersion, HttpFields httpFields, long contentLength, int status, String reason, boolean head)
990 super(httpVersion,httpFields,contentLength);
996 public boolean isInformational()
998 return _status>=100 && _status<200;
1001 public int getStatus()
1006 public String getReason()
1011 public boolean isHead()
1017 public String toString()
1019 return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
1023 private static void putSanitisedName(String s,ByteBuffer buffer)
1026 for (int i=0;i<l;i++)
1030 if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
1031 buffer.put((byte)'?');
1033 buffer.put((byte)(0xff&c));
1037 private static void putSanitisedValue(String s,ByteBuffer buffer)
1040 for (int i=0;i<l;i++)
1044 if (c<0 || c>0xff || c=='\r' || c=='\n')
1045 buffer.put((byte)'?');
1047 buffer.put((byte)(0xff&c));
1051 public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
1053 if (field instanceof CachedHttpField)
1055 ((CachedHttpField)field).putTo(bufferInFillMode);
1059 HttpHeader header=field.getHeader();
1062 bufferInFillMode.put(header.getBytesColonSpace());
1063 putSanitisedValue(field.getValue(),bufferInFillMode);
1067 putSanitisedName(field.getName(),bufferInFillMode);
1068 bufferInFillMode.put(__colon_space);
1069 putSanitisedValue(field.getValue(),bufferInFillMode);
1072 BufferUtil.putCRLF(bufferInFillMode);
1076 public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
1078 for (HttpField field : fields)
1081 putTo(field,bufferInFillMode);
1083 BufferUtil.putCRLF(bufferInFillMode);
1086 public static class CachedHttpField extends HttpField
1088 private final byte[] _bytes;
1089 public CachedHttpField(HttpHeader header,String value)
1091 super(header,value);
1092 int cbl=header.getBytesColonSpace().length;
1093 _bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2);
1094 System.arraycopy(value.getBytes(StandardCharsets.ISO_8859_1),0,_bytes,cbl,value.length());
1095 _bytes[_bytes.length-2]=(byte)'\r';
1096 _bytes[_bytes.length-1]=(byte)'\n';
1099 public void putTo(ByteBuffer bufferInFillMode)
1101 bufferInFillMode.put(_bytes);