View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.handler.codec;
18  
19  
20  import java.util.Map.Entry;
21  
22  import io.netty.buffer.ByteBuf;
23  import io.netty.handler.codec.TextHeaders.EntryVisitor;
24  
25  public final class AsciiHeadersEncoder implements EntryVisitor {
26  
27      /**
28       * The separator characters to insert between a header name and a header value.
29       */
30      public enum SeparatorType {
31          /**
32           * {@code ':'}
33           */
34          COLON,
35          /**
36           * {@code ': '}
37           */
38          COLON_SPACE,
39      }
40  
41      /**
42       * The newline characters to insert between header entries.
43       */
44      public enum NewlineType {
45          /**
46           * {@code '\n'}
47           */
48          LF,
49          /**
50           * {@code '\r\n'}
51           */
52          CRLF
53      }
54  
55      private final ByteBuf buf;
56      private final SeparatorType separatorType;
57      private final NewlineType newlineType;
58  
59      public AsciiHeadersEncoder(ByteBuf buf) {
60          this(buf, SeparatorType.COLON_SPACE, NewlineType.CRLF);
61      }
62  
63      public AsciiHeadersEncoder(ByteBuf buf, SeparatorType separatorType, NewlineType newlineType) {
64          if (buf == null) {
65              throw new NullPointerException("buf");
66          }
67          if (separatorType == null) {
68              throw new NullPointerException("separatorType");
69          }
70          if (newlineType == null) {
71              throw new NullPointerException("newlineType");
72          }
73  
74          this.buf = buf;
75          this.separatorType = separatorType;
76          this.newlineType = newlineType;
77      }
78  
79      @Override
80      public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
81          final CharSequence name = entry.getKey();
82          final CharSequence value = entry.getValue();
83          final ByteBuf buf = this.buf;
84          final int nameLen = name.length();
85          final int valueLen = value.length();
86          final int entryLen = nameLen + valueLen + 4;
87          int offset = buf.writerIndex();
88          buf.ensureWritable(entryLen);
89          writeAscii(buf, offset, name, nameLen);
90          offset += nameLen;
91  
92          switch (separatorType) {
93              case COLON:
94                  buf.setByte(offset ++, ':');
95                  break;
96              case COLON_SPACE:
97                  buf.setByte(offset ++, ':');
98                  buf.setByte(offset ++, ' ');
99                  break;
100             default:
101                 throw new Error();
102         }
103 
104         writeAscii(buf, offset, value, valueLen);
105         offset += valueLen;
106 
107         switch (newlineType) {
108             case LF:
109                 buf.setByte(offset ++, '\n');
110                 break;
111             case CRLF:
112                 buf.setByte(offset ++, '\r');
113                 buf.setByte(offset ++, '\n');
114                 break;
115             default:
116                 throw new Error();
117         }
118 
119         buf.writerIndex(offset);
120         return true;
121     }
122 
123     private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
124         if (value instanceof AsciiString) {
125             writeAsciiString(buf, offset, (AsciiString) value, valueLen);
126         } else {
127             writeCharSequence(buf, offset, value, valueLen);
128         }
129     }
130 
131     private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) {
132         value.copy(0, buf, offset, valueLen);
133     }
134 
135     private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) {
136         for (int i = 0; i < valueLen; i ++) {
137             buf.setByte(offset ++, c2b(value.charAt(i)));
138         }
139     }
140 
141     private static int c2b(char ch) {
142         return ch < 256? (byte) ch : '?';
143     }
144 }