1 /* 2 * Copyright 2015 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 /* 18 * Copyright 2014 Twitter, Inc. 19 * 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 package io.netty.handler.codec.http2; 33 34 import io.netty.handler.codec.UnsupportedValueConverter; 35 import io.netty.util.AsciiString; 36 37 import java.util.Arrays; 38 import java.util.List; 39 40 import static io.netty.handler.codec.http2.HpackUtil.equalsConstantTime; 41 42 final class HpackStaticTable { 43 44 // Appendix A: Static Table 45 // http://tools.ietf.org/html/rfc7541#appendix-A 46 private static final List<HpackHeaderField> STATIC_TABLE = Arrays.asList( 47 /* 1 */ newEmptyHeaderField(":authority"), 48 /* 2 */ newHeaderField(":method", "GET"), 49 /* 3 */ newHeaderField(":method", "POST"), 50 /* 4 */ newHeaderField(":path", "/"), 51 /* 5 */ newHeaderField(":path", "/index.html"), 52 /* 6 */ newHeaderField(":scheme", "http"), 53 /* 7 */ newHeaderField(":scheme", "https"), 54 /* 8 */ newHeaderField(":status", "200"), 55 /* 9 */ newHeaderField(":status", "204"), 56 /* 10 */ newHeaderField(":status", "206"), 57 /* 11 */ newHeaderField(":status", "304"), 58 /* 12 */ newHeaderField(":status", "400"), 59 /* 13 */ newHeaderField(":status", "404"), 60 /* 14 */ newHeaderField(":status", "500"), 61 /* 15 */ newEmptyHeaderField("accept-charset"), 62 /* 16 */ newHeaderField("accept-encoding", "gzip, deflate"), 63 /* 17 */ newEmptyHeaderField("accept-language"), 64 /* 18 */ newEmptyHeaderField("accept-ranges"), 65 /* 19 */ newEmptyHeaderField("accept"), 66 /* 20 */ newEmptyHeaderField("access-control-allow-origin"), 67 /* 21 */ newEmptyHeaderField("age"), 68 /* 22 */ newEmptyHeaderField("allow"), 69 /* 23 */ newEmptyHeaderField("authorization"), 70 /* 24 */ newEmptyHeaderField("cache-control"), 71 /* 25 */ newEmptyHeaderField("content-disposition"), 72 /* 26 */ newEmptyHeaderField("content-encoding"), 73 /* 27 */ newEmptyHeaderField("content-language"), 74 /* 28 */ newEmptyHeaderField("content-length"), 75 /* 29 */ newEmptyHeaderField("content-location"), 76 /* 30 */ newEmptyHeaderField("content-range"), 77 /* 31 */ newEmptyHeaderField("content-type"), 78 /* 32 */ newEmptyHeaderField("cookie"), 79 /* 33 */ newEmptyHeaderField("date"), 80 /* 34 */ newEmptyHeaderField("etag"), 81 /* 35 */ newEmptyHeaderField("expect"), 82 /* 36 */ newEmptyHeaderField("expires"), 83 /* 37 */ newEmptyHeaderField("from"), 84 /* 38 */ newEmptyHeaderField("host"), 85 /* 39 */ newEmptyHeaderField("if-match"), 86 /* 40 */ newEmptyHeaderField("if-modified-since"), 87 /* 41 */ newEmptyHeaderField("if-none-match"), 88 /* 42 */ newEmptyHeaderField("if-range"), 89 /* 43 */ newEmptyHeaderField("if-unmodified-since"), 90 /* 44 */ newEmptyHeaderField("last-modified"), 91 /* 45 */ newEmptyHeaderField("link"), 92 /* 46 */ newEmptyHeaderField("location"), 93 /* 47 */ newEmptyHeaderField("max-forwards"), 94 /* 48 */ newEmptyHeaderField("proxy-authenticate"), 95 /* 49 */ newEmptyHeaderField("proxy-authorization"), 96 /* 50 */ newEmptyHeaderField("range"), 97 /* 51 */ newEmptyHeaderField("referer"), 98 /* 52 */ newEmptyHeaderField("refresh"), 99 /* 53 */ newEmptyHeaderField("retry-after"), 100 /* 54 */ newEmptyHeaderField("server"), 101 /* 55 */ newEmptyHeaderField("set-cookie"), 102 /* 56 */ newEmptyHeaderField("strict-transport-security"), 103 /* 57 */ newEmptyHeaderField("transfer-encoding"), 104 /* 58 */ newEmptyHeaderField("user-agent"), 105 /* 59 */ newEmptyHeaderField("vary"), 106 /* 60 */ newEmptyHeaderField("via"), 107 /* 61 */ newEmptyHeaderField("www-authenticate") 108 ); 109 110 private static HpackHeaderField newEmptyHeaderField(String name) { 111 return new HpackHeaderField(AsciiString.cached(name), AsciiString.EMPTY_STRING); 112 } 113 114 private static HpackHeaderField newHeaderField(String name, String value) { 115 return new HpackHeaderField(AsciiString.cached(name), AsciiString.cached(value)); 116 } 117 118 private static final CharSequenceMap<Integer> STATIC_INDEX_BY_NAME = createMap(); 119 120 /** 121 * The number of header fields in the static table. 122 */ 123 static final int length = STATIC_TABLE.size(); 124 125 /** 126 * Return the header field at the given index value. 127 */ 128 static HpackHeaderField getEntry(int index) { 129 return STATIC_TABLE.get(index - 1); 130 } 131 132 /** 133 * Returns the lowest index value for the given header field name in the static table. Returns 134 * -1 if the header field name is not in the static table. 135 */ 136 static int getIndex(CharSequence name) { 137 Integer index = STATIC_INDEX_BY_NAME.get(name); 138 if (index == null) { 139 return -1; 140 } 141 return index; 142 } 143 144 /** 145 * Returns the index value for the given header field in the static table. Returns -1 if the 146 * header field is not in the static table. 147 */ 148 static int getIndex(CharSequence name, CharSequence value) { 149 int index = getIndex(name); 150 if (index == -1) { 151 return -1; 152 } 153 154 // Note this assumes all entries for a given header field are sequential. 155 while (index <= length) { 156 HpackHeaderField entry = getEntry(index); 157 if (equalsConstantTime(name, entry.name) == 0) { 158 break; 159 } 160 if (equalsConstantTime(value, entry.value) != 0) { 161 return index; 162 } 163 index++; 164 } 165 166 return -1; 167 } 168 169 // create a map CharSequenceMap header name to index value to allow quick lookup 170 private static CharSequenceMap<Integer> createMap() { 171 int length = STATIC_TABLE.size(); 172 @SuppressWarnings("unchecked") 173 CharSequenceMap<Integer> ret = new CharSequenceMap<Integer>(true, 174 UnsupportedValueConverter.<Integer>instance(), length); 175 // Iterate through the static table in reverse order to 176 // save the smallest index for a given name in the map. 177 for (int index = length; index > 0; index--) { 178 HpackHeaderField entry = getEntry(index); 179 CharSequence name = entry.name; 180 ret.set(name, index); 181 } 182 return ret; 183 } 184 185 // singleton 186 private HpackStaticTable() { 187 } 188 }