1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. 7 */ 8 9 #pragma once 10 11 #include <assert.h> 12 #include <cctype> 13 #include <folly/Range.h> 14 #include <proxygen/lib/http/HTTPMessage.h> 15 #include <proxygen/lib/http/codec/compress/Header.h> 16 #include <proxygen/lib/utils/UtilInl.h> 17 #include <stdint.h> 18 #include <string> 19 20 namespace proxygen { 21 22 /** 23 * On some mobile platforms we don't always get a chance to stop proxygen 24 * eventbase. That leads to static object being cleaned up by system while 25 * proxygen tries to access them, which is bad. The speedup from making 26 * some variable static isn't necessary on mobile clients anyway. 27 */ 28 #ifdef FOLLY_MOBILE 29 #define CODEC_STATIC 30 #else 31 #define CODEC_STATIC static 32 #endif 33 34 folly::Optional<HTTPPriority> parseHTTPPriorityString( 35 folly::StringPiece priorityString); 36 37 class CodecUtil { 38 public: 39 // If these are needed elsewhere, we can move them to a more generic 40 // namespace/class later 41 static const char http_tokens[256]; 42 validateURL(folly::ByteRange url,URLValidateMode mode)43 static bool validateURL(folly::ByteRange url, URLValidateMode mode) { 44 return proxygen::validateURL(url, mode); 45 } 46 isalpha(uint8_t c)47 static bool isalpha(uint8_t c) { 48 return ((unsigned int)(c | 32) - 97) < 26U; 49 } 50 validateMethod(folly::ByteRange method)51 static bool validateMethod(folly::ByteRange method) { 52 for (auto p = method.begin(); p != method.end(); p++) { 53 // '-' is valid except for start and end 54 if (*p == '-' && p != method.begin() && p != method.end()) { 55 continue; 56 } 57 if (!CodecUtil::isalpha(*p)) { 58 return false; 59 } 60 } 61 return true; 62 } 63 validateScheme(folly::ByteRange method)64 static bool validateScheme(folly::ByteRange method) { 65 for (auto p : method) { 66 if (!CodecUtil::isalpha(p)) { 67 // methods are all characters 68 return false; 69 } 70 } 71 return true; 72 } 73 74 enum HeaderNameValidationMode { 75 HEADER_NAME_STRICT_COMPAT, 76 HEADER_NAME_STRICT 77 }; validateHeaderName(folly::ByteRange name,HeaderNameValidationMode mode)78 static bool validateHeaderName(folly::ByteRange name, 79 HeaderNameValidationMode mode) { 80 if (name.size() == 0) { 81 return false; 82 } 83 for (uint8_t p : name) { 84 if (mode == HEADER_NAME_STRICT_COMPAT) { 85 // Allows ' ', '"', '/', '}' and high ASCII 86 if (p < 0x80 && !http_tokens[p]) { 87 return false; 88 } 89 } else { 90 if ((p < 0x80 && (http_tokens[p] != p)) || p >= 0x80) { 91 return false; 92 } 93 } 94 } 95 return true; 96 } 97 98 /** 99 * RFC2616 allows certain control chars in header values if they are 100 * quoted and escaped. 101 * When mode is COMPLIANT, then this is allowed. 102 * When mode is STRICT*, no escaped CTLs are allowed 103 * 104 * An unfortunate side effect when this function moved from signed to unsigned 105 * chars, the high-ASCII check was broken. Temporarily continue to allow this 106 * with a special mode. 107 */ 108 enum CtlEscapeMode { COMPLIANT, STRICT_COMPAT, STRICT }; 109 validateHeaderValue(folly::ByteRange value,CtlEscapeMode mode)110 static bool validateHeaderValue(folly::ByteRange value, CtlEscapeMode mode) { 111 bool escape = false; 112 bool quote = false; 113 enum { 114 lws_none, 115 lws_expect_nl, 116 lws_expect_ws1, 117 lws_expect_ws2 118 } state = lws_none; 119 120 for (auto p = std::begin(value); p != std::end(value); ++p) { 121 if (escape) { 122 escape = false; 123 if (mode == COMPLIANT) { 124 // prev char escaped. Turn off escape and go to next char 125 // COMPLIANT mode only 126 assert(quote); 127 continue; 128 } 129 } 130 switch (state) { 131 case lws_none: 132 switch (*p) { 133 case '\\': 134 if (quote) { 135 escape = true; 136 } 137 break; 138 case '\"': 139 quote = !quote; 140 break; 141 case '\r': 142 state = lws_expect_nl; 143 break; 144 default: 145 if ((*p < 0x20 && *p != '\t') || (*p == 0x7f) || 146 (*p > 0x7f && mode == STRICT)) { 147 // unexpected ctl per rfc2616, HT OK 148 return false; 149 } 150 break; 151 } 152 break; 153 case lws_expect_nl: 154 if (*p != '\n') { 155 // unescaped \r must be LWS 156 return false; 157 } 158 state = lws_expect_ws1; 159 break; 160 case lws_expect_ws1: 161 if (*p != ' ' && *p != '\t') { 162 // unescaped \r\n must be LWS 163 return false; 164 } 165 state = lws_expect_ws2; 166 break; 167 case lws_expect_ws2: 168 if (*p != ' ' && *p != '\t') { 169 // terminated LWS 170 state = lws_none; 171 // check this char again 172 p--; 173 } 174 break; 175 } 176 } 177 // Unterminated quotes are OK, since the value can be* TEXT which treats 178 // the " like any other char. 179 // Unterminated escapes are bad because it will escape the next character 180 // when converting to HTTP 181 // Unterminated LWS (dangling \r or \r\n) is bad because it could 182 // prematurely terminate the headers when converting to HTTP 183 return !escape && (state == lws_none || state == lws_expect_ws2); 184 } 185 186 static bool hasGzipAndDeflate(const std::string& value, 187 bool& hasGzip, 188 bool& hasDeflate); 189 190 static bool appendHeaders(const HTTPHeaders& inputHeaders, 191 std::vector<compress::Header>& headers, 192 HTTPHeaderCode headerToCheck); 193 194 static const std::bitset<256>& perHopHeaderCodes(); 195 }; 196 197 } // namespace proxygen 198