1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  package io.netty5.handler.codec.http2;
16  
17  import io.netty5.handler.codec.CharSequenceValueConverter;
18  import io.netty5.handler.codec.DefaultHeaders;
19  import io.netty5.util.AsciiString;
20  import io.netty5.util.ByteProcessor;
21  import io.netty5.util.internal.PlatformDependent;
22  import io.netty5.util.internal.UnstableApi;
23  
24  import static io.netty5.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
25  import static io.netty5.handler.codec.http2.Http2Exception.connectionError;
26  import static io.netty5.handler.codec.http2.Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat;
27  import static io.netty5.util.AsciiString.CASE_INSENSITIVE_HASHER;
28  import static io.netty5.util.AsciiString.CASE_SENSITIVE_HASHER;
29  import static io.netty5.util.AsciiString.isUpperCase;
30  
31  @UnstableApi
32  public class DefaultHttp2Headers
33          extends DefaultHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
34      private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = value -> !isUpperCase(value);
35      static final NameValidator<CharSequence> HTTP2_NAME_VALIDATOR = name -> {
36          if (name == null || name.length() == 0) {
37              PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
38                      "empty headers are not allowed [%s]", name));
39          }
40          if (name instanceof AsciiString) {
41              int index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
42              if (index != -1) {
43                  PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
44                          "invalid header name [%s]", name));
45              }
46          } else {
47              for (int i = 0; i < name.length(); ++i) {
48                  if (isUpperCase(name.charAt(i))) {
49                      PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
50                              "invalid header name [%s]", name));
51                  }
52              }
53          }
54      };
55  
56      private HeaderEntry<CharSequence, CharSequence> firstNonPseudo = head;
57  
58      
59  
60  
61  
62  
63  
64      public DefaultHttp2Headers() {
65          this(true);
66      }
67  
68      
69  
70  
71  
72  
73      @SuppressWarnings("unchecked")
74      public DefaultHttp2Headers(boolean validate) {
75          
76          
77          super(CASE_SENSITIVE_HASHER,
78                CharSequenceValueConverter.INSTANCE,
79                validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL);
80      }
81  
82      
83  
84  
85  
86  
87  
88  
89      @SuppressWarnings("unchecked")
90      public DefaultHttp2Headers(boolean validate, int arraySizeHint) {
91          
92          
93          super(CASE_SENSITIVE_HASHER,
94                CharSequenceValueConverter.INSTANCE,
95                validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL,
96                arraySizeHint);
97      }
98  
99      @Override
100     public Http2Headers clear() {
101         firstNonPseudo = head;
102         return super.clear();
103     }
104 
105     @Override
106     public boolean equals(Object o) {
107         return o instanceof Http2Headers && equals((Http2Headers) o, CASE_SENSITIVE_HASHER);
108     }
109 
110     @Override
111     public int hashCode() {
112         return hashCode(CASE_SENSITIVE_HASHER);
113     }
114 
115     @Override
116     public Http2Headers method(CharSequence value) {
117         set(PseudoHeaderName.METHOD.value(), value);
118         return this;
119     }
120 
121     @Override
122     public Http2Headers scheme(CharSequence value) {
123         set(PseudoHeaderName.SCHEME.value(), value);
124         return this;
125     }
126 
127     @Override
128     public Http2Headers authority(CharSequence value) {
129         set(PseudoHeaderName.AUTHORITY.value(), value);
130         return this;
131     }
132 
133     @Override
134     public Http2Headers path(CharSequence value) {
135         set(PseudoHeaderName.PATH.value(), value);
136         return this;
137     }
138 
139     @Override
140     public Http2Headers status(CharSequence value) {
141         set(PseudoHeaderName.STATUS.value(), value);
142         return this;
143     }
144 
145     @Override
146     public CharSequence method() {
147         return get(PseudoHeaderName.METHOD.value());
148     }
149 
150     @Override
151     public CharSequence scheme() {
152         return get(PseudoHeaderName.SCHEME.value());
153     }
154 
155     @Override
156     public CharSequence authority() {
157         return get(PseudoHeaderName.AUTHORITY.value());
158     }
159 
160     @Override
161     public CharSequence path() {
162         return get(PseudoHeaderName.PATH.value());
163     }
164 
165     @Override
166     public CharSequence status() {
167         return get(PseudoHeaderName.STATUS.value());
168     }
169 
170     @Override
171     public boolean contains(CharSequence name, CharSequence value) {
172         return contains(name, value, false);
173     }
174 
175     @Override
176     public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) {
177         return contains(name, value, caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
178     }
179 
180     @Override
181     protected final HeaderEntry<CharSequence, CharSequence> newHeaderEntry(int h, CharSequence name, CharSequence value,
182                                                            HeaderEntry<CharSequence, CharSequence> next) {
183         return new Http2HeaderEntry(h, name, value, next);
184     }
185 
186     private final class Http2HeaderEntry extends HeaderEntry<CharSequence, CharSequence> {
187         Http2HeaderEntry(int hash, CharSequence key, CharSequence value,
188                 HeaderEntry<CharSequence, CharSequence> next) {
189             super(hash, key);
190             this.value = value;
191             this.next = next;
192 
193             
194             if (hasPseudoHeaderFormat(key)) {
195                 after = firstNonPseudo;
196                 before = firstNonPseudo.before();
197             } else {
198                 after = head;
199                 before = head.before();
200                 if (firstNonPseudo == head) {
201                     firstNonPseudo = this;
202                 }
203             }
204             pointNeighborsToThis();
205         }
206 
207         @Override
208         protected void remove() {
209             if (this == firstNonPseudo) {
210                 firstNonPseudo = firstNonPseudo.after();
211             }
212             super.remove();
213         }
214     }
215 }