View Javadoc

1   /*
2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3    *
4    * Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
5    *
6    * The contents of this file are subject to the terms of either the GNU
7    * General Public License Version 2 only ("GPL") or the Common Development
8    * and Distribution License("CDDL") (collectively, the "License").  You
9    * may not use this file except in compliance with the License.  You can
10   * obtain a copy of the License at
11   * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
12   * or packager/legal/LICENSE.txt.  See the License for the specific
13   * language governing permissions and limitations under the License.
14   *
15   * When distributing the software, include this License Header Notice in each
16   * file and include the License file at packager/legal/LICENSE.txt.
17   *
18   * GPL Classpath Exception:
19   * Oracle designates this particular file as subject to the "Classpath"
20   * exception as provided by Oracle in the GPL Version 2 section of the License
21   * file that accompanied this code.
22   *
23   * Modifications:
24   * If applicable, add the following below the License Header, with the fields
25   * enclosed by brackets [] replaced by your own identifying information:
26   * "Portions Copyright [year] [name of copyright owner]"
27   *
28   * Contributor(s):
29   * If you wish your version of this file to be governed by only the CDDL or
30   * only the GPL Version 2, indicate your decision by adding "[Contributor]
31   * elects to include this software in this distribution under the [CDDL or GPL
32   * Version 2] license."  If you don't indicate a single choice of license, a
33   * recipient has the option to distribute your version of this file under
34   * either the CDDL, the GPL Version 2 or to extend the choice of license to
35   * its licensees as provided above.  However, if you add GPL Version 2 code
36   * and therefore, elected the GPL Version 2 license, then the option applies
37   * only if the new code is made subject to such option by the copyright
38   * holder.
39   */
40  
41  package org.glassfish.grizzly.http;
42  
43  import java.util.ArrayList;
44  import java.util.Collections;
45  import java.util.List;
46  import org.glassfish.grizzly.Buffer;
47  import org.glassfish.grizzly.Grizzly;
48  import org.glassfish.grizzly.attributes.AttributeHolder;
49  import org.glassfish.grizzly.attributes.AttributeStorage;
50  import org.glassfish.grizzly.attributes.IndexedAttributeHolder;
51  import org.glassfish.grizzly.http.util.Ascii;
52  import org.glassfish.grizzly.http.util.Constants;
53  import org.glassfish.grizzly.http.util.DataChunk;
54  import org.glassfish.grizzly.http.util.Header;
55  import org.glassfish.grizzly.http.util.HttpUtils;
56  import org.glassfish.grizzly.http.util.MimeHeaders;
57  import org.glassfish.grizzly.utils.Charsets;
58  
59  /**
60   * {@link HttpPacket}, which represents HTTP message header. There are 2 subtypes
61   * of this class: {@link HttpRequestPacket} and {@link HttpResponsePacket}.
62   *
63   * @see HttpRequestPacket
64   * @see HttpResponsePacket
65   * 
66   * @author Alexey Stashok
67   */
68  public abstract class HttpHeader extends HttpPacket
69          implements MimeHeadersPacket, AttributeStorage {
70      
71      private final static byte[] CHUNKED_ENCODING_BYTES =
72              Constants.CHUNKED_ENCODING.getBytes(Charsets.ASCII_CHARSET);
73  
74      protected boolean isCommitted;
75      protected final MimeHeaders headers = new MimeHeaders();
76      
77      protected final DataChunk protocolC = DataChunk.newInstance();
78      protected Protocol parsedProtocol;
79  
80      protected boolean isChunked;
81      private final byte[] tmpContentLengthBuffer = new byte[20];
82      
83      protected long contentLength = -1;
84  
85      protected String characterEncoding;
86      protected String quotedCharsetValue;
87      /**
88       * Has the charset been explicitly set.
89       */
90      protected boolean charsetSet = false;
91  
92  //    private String defaultContentType;
93  
94      protected String contentType;
95  
96      protected boolean isExpectContent = true;
97  
98      protected boolean isSkipRemainder;
99      
100     /**
101      * <tt>true</tt> if HTTP message payload is broken due to inappropriate
102      * Transfer-Encoding or Content-Encoding settings.
103      */
104     protected boolean isContentBroken;
105     
106     protected boolean secure;
107 
108     protected final DataChunk upgrade = DataChunk.newInstance();
109 
110     private TransferEncoding transferEncoding;
111     private final List<ContentEncoding> contentEncodings = new ArrayList<ContentEncoding>();
112     // <tt>true</tt>, if content encodings for this headers were chosen
113     private boolean isContentEncodingsSelected;
114 
115     private final AttributeHolder attributes =
116             new IndexedAttributeHolder(Grizzly.DEFAULT_ATTRIBUTE_BUILDER);
117     private AttributeHolder activeAttributes;
118 
119     Buffer headerBuffer;
120     
121     void setHeaderBuffer(final Buffer headerBuffer) {
122         this.headerBuffer = headerBuffer;
123     }
124 
125     /**
126      * {@inheritDoc}
127      */
128     @Override
129     public AttributeHolder getAttributes() {
130         if (activeAttributes == null) {
131             activeAttributes = attributes;
132         }
133         
134         return activeAttributes;
135     }    
136 
137     /**
138      * Returns <tt>true</tt>, if the current <tt>HttpHeader</tt> represent
139      * HTTP request message, or <tt>false</tt> otherwise.
140      * 
141      * @return <tt>true</tt>, if the current <tt>HttpHeader</tt> represent
142      * HTTP request message, or <tt>false</tt> otherwise.
143      */
144     public abstract boolean isRequest();
145 
146     /**
147      * Returns <tt>true</tt>.
148      * @return <tt>true</tt>.
149      */
150     @Override
151     public final boolean isHeader() {
152         return true;
153     }
154 
155     public abstract ProcessingState getProcessingState();
156 
157     protected void addContentEncoding(ContentEncoding contentEncoding) {
158         contentEncodings.add(contentEncoding);
159     }
160 
161     protected List<ContentEncoding> getContentEncodings(final boolean isModifiable) {
162         if (isModifiable) {
163             return contentEncodings;
164         } else {
165             return Collections.unmodifiableList(contentEncodings);
166         }
167     }
168 
169     public List<ContentEncoding> getContentEncodings() {
170         return getContentEncodings(false);
171     }
172 
173     protected final boolean isContentEncodingsSelected() {
174         return isContentEncodingsSelected;
175     }
176     
177     protected final void setContentEncodingsSelected(final boolean isContentEncodingsSelected) {
178         this.isContentEncodingsSelected = isContentEncodingsSelected;
179     }
180 
181     /**
182      * Get the {@link TransferEncoding}, responsible for the parsing/serialization of the HTTP message content
183      * 
184      * @return the {@link TransferEncoding}, responsible for the parsing/serialization of the HTTP message content
185      */
186     public TransferEncoding getTransferEncoding() {
187         return transferEncoding;
188     }
189 
190     /**
191      * Set the {@link TransferEncoding}, responsible for the parsing/serialization of the HTTP message content.
192      *
193      * @param transferEncoding the {@link TransferEncoding}, responsible for the parsing/serialization of the HTTP message content.
194      */
195     protected void setTransferEncoding(TransferEncoding transferEncoding) {
196         this.transferEncoding = transferEncoding;
197     }
198 
199     /**
200      * Returns <tt>true</tt>, if this {@link HttpPacket} content will be transferred
201      * in chunking mode, or <tt>false</tt> if case of fixed-length message.
202      * 
203      * @return <tt>true</tt>, if this {@link HttpPacket} content will be transferred
204      * in chunking mode, or <tt>false</tt> if case of fixed-length message.
205      */
206     public boolean isChunked() {
207         return isChunked;
208     }
209 
210     /**
211      * Set <tt>true</tt>, if this {@link HttpPacket} content will be transferred
212      * in chunking mode, or <tt>false</tt> if case of fixed-length message.
213      *
214      * @param isChunked  <tt>true</tt>, if this {@link HttpPacket} content
215      * will be transferred in chunking mode, or <tt>false</tt> if case
216      * of fixed-length message.
217      */
218     public void setChunked(boolean isChunked) {
219         this.isChunked = isChunked;
220     }
221 
222     /**
223      * Returns <tt>true</tt>, if HTTP message, represented by this header still
224      * expects additional content basing either on content-length or chunking
225      * information. <tt>false</tt> is returned if content no additional content
226      * data is expected.
227      * Note: this method could be used only when we <b>parse</b> the HTTP message
228      * 
229      * @return <tt>true</tt>, if HTTP message, represented by this header still
230      * expects additional content basing either on content-length or chunking
231      * information. <tt>false</tt> is returned if content no additional content
232      * data is expected.
233      */
234     public boolean isExpectContent() {
235         return isExpectContent;
236     }
237 
238     protected void setExpectContent(boolean isExpectContent) {
239         this.isExpectContent = isExpectContent;
240     }
241 
242     /**
243      * Returns <tt>true</tt>, if either application or HTTP core part is not
244      * interested in parsing the rest of this HTTP message content and waits
245      * for the next HTTP message to come on this {@link org.glassfish.grizzly.Connection}.
246      * Otherwise returns <tt>false</tt>.
247      * 
248      * @return <tt>true</tt>, if either application or HTTP core part is not
249      * interested in parsing the rest of this HTTP message content and waits
250      * for the next HTTP message to come on this {@link org.glassfish.grizzly.Connection}.
251      * Otherwise returns <tt>false</tt>.
252      */
253     public boolean isSkipRemainder() {
254         return isSkipRemainder;
255     }
256 
257     /**
258      * Set flag, which is set to <tt>true</tt>, means that we're not
259      * interested in parsing the rest of this HTTP message content and wait
260      * for the next HTTP message to come on this {@link org.glassfish.grizzly.Connection}.
261      *
262      * @param isSkipRemainder <tt>true</tt> means that we're not
263      * interested in parsing the rest of this HTTP message content and wait
264      * for the next HTTP message to come on this {@link org.glassfish.grizzly.Connection}.
265      */
266     public void setSkipRemainder(boolean isSkipRemainder) {
267         this.isSkipRemainder = isSkipRemainder;
268     }
269 
270     /**
271      * Returns <tt>true</tt>, if HTTP packet payload
272      * was detected as broken due to unexpected error occurred during 
273      * Transfer-Encoding or Content-Encoding processing.
274      * Otherwise returns <tt>false</tt>.
275      * 
276      * @return <tt>true</tt>, if HTTP packet payload
277      * was detected as broken due to unexpected error occurred during 
278      * Transfer-Encoding or Content-Encoding processing.
279      * Otherwise returns <tt>false</tt>.
280      */
281     public boolean isContentBroken() {
282         return isContentBroken;
283     }
284 
285     /**
286      * Set flag, which is set to <tt>true</tt>, means that HTTP packet payload
287      * was detected as broken due to unexpected error occurred during 
288      * Transfer-Encoding or Content-Encoding processing.
289      *
290      * @param isSkipRemainder <tt>true</tt>, means that HTTP packet payload
291      * was detected as broken due to unexpected error occurred during 
292      * Transfer-Encoding or Content-Encoding processing.
293      */
294     public void setContentBroken(final boolean isBroken) {
295         this.isContentBroken = isBroken;
296     }
297     
298     public String getUpgrade() {
299         return upgrade.toString();
300     }
301 
302     public DataChunk getUpgradeDC() {
303         return upgrade;
304     }
305 
306     public void setUpgrade(String upgrade) {
307         this.upgrade.setString(upgrade);
308     }
309 
310     protected void makeUpgradeHeader() {
311         if (!upgrade.isNull()) {
312             headers.setValue(Header.Upgrade).set(upgrade);
313         }
314     }
315 
316     /**
317      * Makes sure content-length header is present.
318      * 
319      * @param defaultLength default content-length value.
320      */
321     protected void makeContentLengthHeader(final long defaultLength) {
322         if (contentLength != -1) {
323             final int start =
324                     HttpUtils.longToBuffer(contentLength, tmpContentLengthBuffer);
325             headers.setValue(Header.ContentLength).setBytes(
326                     tmpContentLengthBuffer, start,
327                     tmpContentLengthBuffer.length);
328         } else if (defaultLength != -1) {
329             final int start = HttpUtils.longToBuffer(defaultLength, tmpContentLengthBuffer);
330             final int idx = headers.indexOf(Header.ContentLength, 0);
331             if (idx == -1) {
332                 headers.addValue(Header.ContentLength).setBytes(
333                     tmpContentLengthBuffer, start,
334                     tmpContentLengthBuffer.length);
335             } else if (headers.getValue(idx).isNull()) {
336                 headers.getValue(idx).setBytes(
337                     tmpContentLengthBuffer, start,
338                     tmpContentLengthBuffer.length);
339             }
340         }
341     }
342 
343     /**
344      * Get the content-length of this {@link HttpPacket}. Applicable only in case
345      * of fixed-length HTTP message.
346      * 
347      * @return the content-length of this {@link HttpPacket}. Applicable only
348      * in case of fixed-length HTTP message.
349      */
350     public long getContentLength() {
351         if (contentLength == -1) {
352             final DataChunk contentLengthChunk =
353                     headers.getValue(Header.ContentLength);
354             if (contentLengthChunk != null) {
355                 contentLength = Ascii.parseLong(contentLengthChunk);
356             }
357         }
358 
359         return contentLength;
360     }
361 
362 
363     /**
364      * Set the length of this HTTP message.
365      *
366      * @param len the length of this HTTP message.
367      */
368     public void setContentLength(final int len) {
369         this.contentLength = len;
370         if (len < 0) {
371             headers.removeHeader(Header.ContentLength);
372         }
373     }
374 
375     /**
376      * Set the content-length of this {@link HttpPacket}. Applicable only in case
377      * of fixed-length HTTP message.
378      *
379      * @param contentLength  the content-length of this {@link HttpPacket}.
380      * Applicable only in case of fixed-length HTTP message.
381      */
382     public void setContentLengthLong(final long contentLength) {
383         this.contentLength = contentLength;
384         if (contentLength < 0) {
385             headers.removeHeader(Header.ContentLength);
386         }
387     }
388 
389     /**
390      * Is this <tt>HttpHeader</tt> written? <tt>true</tt>, if this
391      * <tt>HttpHeader</tt> has been already serialized, and only {@link HttpContent}
392      * messages might be serialized for this {@link HttpPacket}.
393      * 
394      * @return  <tt>true</tt>, if this <tt>HttpHeader</tt> has been already
395      * serialized, and only {@link HttpContent} messages might be serialized
396      * for this {@link HttpPacket}.
397      */
398     public boolean isCommitted() {
399         return isCommitted;
400     }
401 
402     /**
403      * Is this <tt>HttpHeader</tt> written? <tt>true</tt>, if this
404      * <tt>HttpHeader</tt> has been already serialized, and only {@link HttpContent}
405      * messages might be serialized for this {@link HttpPacket}.
406      *
407      * @param isCommitted   <tt>true</tt>, if this <tt>HttpHeader</tt> has been
408      * already serialized, and only {@link HttpContent} messages might be
409      * serialized for this {@link HttpPacket}.
410      */
411     public void setCommitted(final boolean isCommitted) {
412         this.isCommitted = isCommitted;
413     }
414 
415 
416     // -------------------- encoding/type --------------------
417 
418     /**
419      * Makes sure transfer-encoding header is present.
420      *
421      * @param defaultValue default transfer-encoding value.
422      */
423     protected void makeTransferEncodingHeader(final String defaultValue) {
424         final int idx = headers.indexOf(Header.TransferEncoding, 0);
425         
426         if (idx == -1) {
427             headers.addValue(Header.TransferEncoding).setBytes(
428                     CHUNKED_ENCODING_BYTES);
429         }
430     }
431 
432     /**
433      * Obtain content-encoding value and mark it as serialized.
434      *
435      * @param value container for the content-type value.
436      */
437     protected void extractContentEncoding(final DataChunk value) {
438         final int idx = headers.indexOf(Header.ContentEncoding, 0);
439 
440         if (idx != -1) {
441             headers.getAndSetSerialized(idx, true);
442             value.set(headers.getValue(idx));
443         }
444     }
445 
446     /**
447      * @return the character encoding of this HTTP message.
448      */
449     public String getCharacterEncoding() {
450         return characterEncoding;
451     }
452 
453     /**
454      * Set the character encoding of this HTTP message.
455      *
456      * @param charset the encoding.
457      */
458     public void setCharacterEncoding(final String charset) {
459 
460         if (isCommitted())
461             return;
462         if (charset == null)
463             return;
464 
465         characterEncoding = charset;
466         // START SJSAS 6316254
467         quotedCharsetValue = charset;
468         // END SJSAS 6316254
469         charsetSet = true;
470     }
471 
472     /**
473      * Serializes Content-Type header into passed Buffer
474      */
475 //    protected final Buffer serializeContentType(final MemoryManager memoryManager,
476 //            Buffer buffer) {
477 //
478 //        final int idx = headers.indexOf(Header.ContentType, 0);
479 //        final DataChunk value;
480 //        if (idx != -1 && !((value = headers.getValue(idx)).isNull())) {
481 //            if (contentType == null) {
482 //                contentType = value.toString();
483 //            }
484 //            
485 //            headers.getAndSetSerialized(idx, true);
486 //        }
487 //
488 //        if (contentType != null) {
489 //            buffer = put(memoryManager, buffer, Header.ContentType.getBytes());
490 //            buffer = put(memoryManager, buffer, HttpCodecFilter.COLON_BYTES);
491 //            buffer = put(memoryManager, buffer, contentType);
492 //
493 //            if (quotedCharsetValue != null && charsetSet) {
494 //                buffer = put(memoryManager, buffer, ";charset=");
495 //                buffer = put(memoryManager, buffer, quotedCharsetValue);
496 //            }
497 //            
498 //            buffer = put(memoryManager, buffer, HttpCodecFilter.CRLF_BYTES);
499 //        } else {
500 //            final String defaultType = getDefaultContentType();
501 //            if (defaultType != null) {
502 //                buffer = put(memoryManager, buffer, Header.ContentType.getBytes());
503 //                buffer = put(memoryManager, buffer, HttpCodecFilter.COLON_BYTES);
504 //                buffer = put(memoryManager, buffer, defaultType);
505 //                buffer = put(memoryManager, buffer, HttpCodecFilter.CRLF_BYTES);
506 //            }
507 //        }
508 //        
509 //        return buffer;
510 //    }
511 
512     /**
513      * @return <code>true</code> if a content type has been set.
514      */
515     public boolean isContentTypeSet() {
516 
517         return (contentType != null
518                     || characterEncoding != null
519                     || headers.getValue(Header.ContentType) != null);
520 
521     }
522 
523 
524     /**
525      * @return the content type of this HTTP message.
526      */
527     public String getContentType() {
528         String ret = contentType;
529 
530         if (ret != null
531                 && quotedCharsetValue != null
532                 && charsetSet) {
533 
534             ret = ret + ";charset=" + quotedCharsetValue;
535         }
536 
537         return ret;
538     }
539 
540     /**
541      * Sets the content type.
542      *
543      * This method must preserve any charset that may already have
544      * been set via a call to request/response.setContentType(),
545      * request/response.setLocale(), or request/response.setCharacterEncoding().
546      *
547      * @param type the content type
548      */
549     public void setContentType(final String type) {
550 
551         if (type == null) {
552             contentType = null;
553             return;
554         }
555 
556         /*
557          * Remove the charset param (if any) from the Content-Type, and use it
558          * to set the response encoding.
559          * The most recent response encoding setting will be appended to the
560          * response's Content-Type (as its charset param) by getContentType();
561          */
562         boolean hasCharset = false;
563         int semicolonIndex = -1;
564         int index = type.indexOf(';');
565         while (index != -1) {
566             int len = type.length();
567             semicolonIndex = index;
568             index++;
569             while (index < len && type.charAt(index) == ' ') {
570                 index++;
571             }
572             if (index+8 < len
573                     && type.charAt(index) == 'c'
574                     && type.charAt(index+1) == 'h'
575                     && type.charAt(index+2) == 'a'
576                     && type.charAt(index+3) == 'r'
577                     && type.charAt(index+4) == 's'
578                     && type.charAt(index+5) == 'e'
579                     && type.charAt(index+6) == 't'
580                     && type.charAt(index+7) == '=') {
581                 hasCharset = true;
582                 break;
583             }
584             index = type.indexOf(';', index);
585         }
586 
587         if (!hasCharset) {
588             contentType = type;
589             return;
590         }
591 
592         contentType = type.substring(0, semicolonIndex);
593         String tail = type.substring(index+8);
594         int nextParam = tail.indexOf(';');
595         String charsetValue;
596         if (nextParam != -1) {
597             contentType += tail.substring(nextParam);
598             charsetValue = tail.substring(0, nextParam);
599         } else {
600             charsetValue = tail;
601         }
602 
603         // The charset value may be quoted, but must not contain any quotes.
604         if (charsetValue != null && charsetValue.length() > 0) {
605             charsetSet=true;
606             // START SJSAS 6316254
607             quotedCharsetValue = charsetValue;
608             // END SJSAS 6316254
609             characterEncoding = charsetValue.replace('"', ' ').trim();
610         }
611     }
612     
613 
614     // -------------------- Headers --------------------
615 
616     
617     /**
618      * {@inheritDoc}
619      */
620     @Override
621     public MimeHeaders getHeaders() {
622         return headers;
623     }
624 
625     /**
626      * {@inheritDoc}
627      */
628     @Override
629     public String getHeader(String name) {
630         return headers.getHeader(name);
631     }
632 
633     /**
634      * {@inheritDoc}
635      */
636     @Override
637     public String getHeader(final Header header) {
638         return headers.getHeader(header);
639     }
640 
641     /**
642      * {@inheritDoc}
643      */
644     @Override
645     public void setHeader(String name, String value) {
646         headers.setValue(name).setString(value);
647     }
648 
649     /**
650      * {@inheritDoc}
651      */
652     @Override
653     public void setHeader(Header header, String value) {
654         headers.setValue(header).setString(value);
655     }
656 
657     /**
658      * {@inheritDoc}
659      */
660     @Override
661     public void addHeader(String name, String value) {
662         headers.addValue(name).setString(value);
663     }
664 
665     /**
666      * {@inheritDoc}
667      */
668     @Override
669     public void addHeader(Header header, String value) {
670         headers.addValue(header).setString(value);
671     }
672 
673     /**
674      * {@inheritDoc}
675      */
676     @Override
677     public boolean containsHeader(String name) {
678         return headers.getHeader(name) != null;
679     }
680 
681     /**
682      * {@inheritDoc}
683      */
684     @Override
685     public boolean containsHeader(final Header header) {
686         return (headers.getHeader(header) != null);
687     }
688 
689     /**
690      * Get the HTTP message protocol version as {@link DataChunk}
691      * (avoiding creation of a String object). The result format is "HTTP/1.x".
692      * 
693      * @return the HTTP message protocol version as {@link DataChunk}
694      * (avoiding creation of a String object). The result format is "HTTP/1.x".
695      */
696     public DataChunk getProtocolDC() {
697         // potentially the value might be changed, so we need to parse it again
698         parsedProtocol = null;
699         return protocolC;
700     }
701 
702     /**
703      * Get the HTTP message protocol version. The result format is "HTTP/1.x".
704      *
705      * @return the HTTP message protocol version. The result format is "HTTP/1.x".
706      */
707     public String getProtocolString() {
708         if (parsedProtocol == null) {
709             return getProtocolDC().toString();
710         }
711 
712         return parsedProtocol.getProtocolString();
713     }
714 
715     /**
716      * Get HTTP protocol version.
717      * @return {@link Protocol}.
718      */
719     public Protocol getProtocol() {
720         if (parsedProtocol != null) {
721             return parsedProtocol;
722         }
723 
724         parsedProtocol = Protocol.valueOf(protocolC);
725         return parsedProtocol;
726     }
727 
728     /**
729      * Set the HTTP message protocol version.
730      * @param protocol {@link Protocol}
731      */
732     public void setProtocol(Protocol protocol) {
733         parsedProtocol = protocol;
734     }
735 
736     /**
737      * @return <code>true</code> if this HTTP message is being transmitted
738      *  in a secure fashion, otherwise returns <code>false</code>.
739      */
740     public boolean isSecure() {
741         return secure;
742     }
743 
744     /**
745      * Sets the secure status of this HTTP message.
746      *
747      * @param secure <code>true</code> if secure, otherwise <code>false</code>.
748      */
749     public void setSecure(boolean secure) {
750         this.secure = secure;
751     }
752     
753     /**
754      * Get the HTTP message content builder.
755      *
756      * @return {@link HttpContent.Builder}.
757      */
758     public final HttpContent.Builder httpContentBuilder() {
759         return HttpContent.builder(this);
760     }
761 
762     /**
763      * Get the HTTP message trailer-chunk builder.
764      *
765      * @return {@link HttpTrailer.Builder}.
766      */
767     public HttpTrailer.Builder httpTrailerBuilder() {
768         return HttpTrailer.builder(this);
769     }
770 
771     /**
772      * Reset the internal state.
773      */
774     protected void reset() {
775         isContentEncodingsSelected = false;
776         secure = false;
777         isSkipRemainder = false;
778         isContentBroken = false;
779         if (activeAttributes != null) {
780             activeAttributes.recycle();
781             activeAttributes = null;
782         }
783         protocolC.recycle();
784         parsedProtocol = null;
785         contentEncodings.clear();
786         headers.clear();
787         isCommitted = false;
788         isChunked = false;
789         contentLength = -1;
790         characterEncoding = null;
791 //        defaultContentType = null;
792         quotedCharsetValue = null;
793         charsetSet = false;
794         contentType = null;
795         transferEncoding = null;
796         isExpectContent = true;
797         upgrade.recycle();
798         if (headerBuffer != null) {
799             headerBuffer.dispose();
800             headerBuffer = null;
801         }
802     }
803     
804     /**
805      * {@inheritDoc}
806      */
807     @Override
808     public void recycle() {
809         reset();
810     }
811 
812 //    protected String getDefaultContentType() {
813 //        return defaultContentType;
814 //    }
815 //
816 //    protected void setDefaultContentType(String defaultContentType) {
817 //        this.defaultContentType = defaultContentType;
818 //    }
819 
820     /**
821      * <tt>HttpHeader</tt> message builder.
822      */
823     public static abstract class Builder<T extends Builder> {
824 
825         protected HttpHeader packet;
826 
827         /**
828          * Set the HTTP message protocol version.
829          * @param protocol {@link Protocol}
830          */
831         @SuppressWarnings({"unchecked"})
832         public final T protocol(Protocol protocol) {
833             packet.setProtocol(protocol);
834             return (T) this;
835         }
836 
837         /**
838          * Set the HTTP message protocol version.
839          * @param protocol protocol version in format "HTTP/1.x".
840          */
841         @SuppressWarnings({"unchecked"})
842         public final T protocol(String protocol) {
843             packet.getProtocolDC().setString(protocol);
844             return (T) this;
845         }
846 
847         /**
848          * Set <tt>true</tt>, if this {@link HttpPacket} content will be transferred
849          * in chunking mode, or <tt>false</tt> if case of fixed-length message.
850          *
851          * @param isChunked  <tt>true</tt>, if this {@link HttpPacket} content
852          * will be transferred in chunking mode, or <tt>false</tt> if case
853          * of fixed-length message.
854          */
855         @SuppressWarnings({"unchecked"})
856         public final T chunked(boolean isChunked) {
857             packet.setChunked(isChunked);
858             return (T) this;
859         }
860 
861         /**
862          * Set the content-length of this {@link HttpPacket}. Applicable only in case
863          * of fixed-length HTTP message.
864          *
865          * @param contentLength  the content-length of this {@link HttpPacket}.
866          * Applicable only in case of fixed-length HTTP message.
867          */
868         @SuppressWarnings({"unchecked"})
869         public final T contentLength(long contentLength) {
870             packet.setContentLengthLong(contentLength);
871             return (T) this;
872         }
873 
874         /**
875          * Set the content-type of this {@link HttpPacket}.
876          *
877          * @param contentType  the content-type of this {@link HttpPacket}.
878          */
879         @SuppressWarnings({"unchecked"})
880         public final T contentType(String contentType) {
881             packet.setContentType(contentType);
882             return (T) this;
883         }
884 
885         /**
886          * Set the HTTP upgrade type.
887          *
888          * @param upgrade the type of upgrade.
889          */
890         @SuppressWarnings({"unchecked"})
891         public final T upgrade(String upgrade) {
892             packet.setUpgrade(upgrade);
893             return (T) this;
894         }
895 
896         /**
897          * Add the HTTP mime header.
898          *
899          * @param name the mime header name.
900          * @param value the mime header value.
901          */
902         @SuppressWarnings({"unchecked"})
903         public final T header(String name, String value) {
904             packet.addHeader(name, value);
905             return (T) this;
906         }
907 
908         /**
909          * Add the HTTP mime header.
910          *
911          * @param header the mime {@link Header}.
912          * @param value the mime header value.
913          */
914         @SuppressWarnings({"unchecked"})
915         public final T header(Header header, String value) {
916             packet.addHeader(header, value);
917             return (T) this;
918         }
919     }
920 }