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