Store Ad Click Attribution requests in the network process
[WebKit-https.git] / Source / WebCore / loader / HTTPHeaderField.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "HTTPHeaderField.h"
28
29 namespace WebCore {
30
31 namespace RFC7230 {
32     
33 bool isTokenCharacter(UChar c)
34 {
35     return isASCIIAlpha(c) || isASCIIDigit(c)
36         || c == '!' || c == '#' || c == '$'
37         || c == '%' || c == '&' || c == '\''
38         || c == '*' || c == '+' || c == '-'
39         || c == '.' || c == '^' || c == '_'
40         || c == '`' || c == '|' || c == '~';
41 }
42
43 static bool isDelimiter(UChar c)
44 {
45     return c == '(' || c == ')' || c == ','
46         || c == '/' || c == ':' || c == ';'
47         || c == '<' || c == '=' || c == '>'
48         || c == '?' || c == '@' || c == '['
49         || c == '\\' || c == ']' || c == '{'
50         || c == '}' || c == '"';
51 }
52
53 static bool isVisibleCharacter(UChar c)
54 {
55     return isTokenCharacter(c) || isDelimiter(c);
56 }
57
58 bool isWhitespace(UChar c)
59 {
60     return c == ' ' || c == '\t';
61 }
62
63 template<size_t min, size_t max>
64 static bool isInRange(UChar c)
65 {
66     return c >= min && c <= max;
67 }
68
69 static bool isOBSText(UChar c)
70 {
71     return isInRange<0x80, 0xFF>(c);
72 }
73
74 static bool isQuotedTextCharacter(UChar c)
75 {
76     return isWhitespace(c)
77         || c == 0x21
78         || isInRange<0x23, 0x5B>(c)
79         || isInRange<0x5D, 0x7E>(c)
80         || isOBSText(c);
81 }
82
83 static bool isQuotedPairSecondOctet(UChar c)
84 {
85     return isWhitespace(c)
86         || isVisibleCharacter(c)
87         || isOBSText(c);
88 }
89
90 static bool isCommentText(UChar c)
91 {
92     return isWhitespace(c)
93         || isInRange<0x21, 0x27>(c)
94         || isInRange<0x2A, 0x5B>(c)
95         || isInRange<0x5D, 0x7E>(c)
96         || isOBSText(c);
97 }
98
99 static bool isValidName(StringView name)
100 {
101     if (!name.length())
102         return false;
103     for (size_t i = 0; i < name.length(); ++i) {
104         if (!isTokenCharacter(name[i]))
105             return false;
106     }
107     return true;
108 }
109
110 static bool isValidValue(StringView value)
111 {
112     enum class State {
113         OptionalWhitespace,
114         Token,
115         QuotedString,
116         Comment,
117     };
118     State state = State::OptionalWhitespace;
119     size_t commentDepth = 0;
120     bool hadNonWhitespace = false;
121     
122     for (size_t i = 0; i < value.length(); ++i) {
123         UChar c = value[i];
124         switch (state) {
125         case State::OptionalWhitespace:
126             if (isWhitespace(c))
127                 continue;
128             hadNonWhitespace = true;
129             if (isTokenCharacter(c)) {
130                 state = State::Token;
131                 continue;
132             }
133             if (c == '"') {
134                 state = State::QuotedString;
135                 continue;
136             }
137             if (c == '(') {
138                 ASSERT(!commentDepth);
139                 ++commentDepth;
140                 state = State::Comment;
141                 continue;
142             }
143             return false;
144             
145         case State::Token:
146             if (isTokenCharacter(c))
147                 continue;
148             state = State::OptionalWhitespace;
149             continue;
150         case State::QuotedString:
151             if (c == '"') {
152                 state = State::OptionalWhitespace;
153                 continue;
154             }
155             if (c == '\\') {
156                 ++i;
157                 if (i == value.length())
158                     return false;
159                 if (!isQuotedPairSecondOctet(value[i]))
160                     return false;
161                 continue;
162             }
163             if (!isQuotedTextCharacter(c))
164                 return false;
165             continue;
166         case State::Comment:
167             if (c == '(') {
168                 ++commentDepth;
169                 continue;
170             }
171             if (c == ')') {
172                 --commentDepth;
173                 if (!commentDepth)
174                     state = State::OptionalWhitespace;
175                 continue;
176             }
177             if (c == '\\') {
178                 ++i;
179                 if (i == value.length())
180                     return false;
181                 if (!isQuotedPairSecondOctet(value[i]))
182                     return false;
183                 continue;
184             }
185             if (!isCommentText(c))
186                 return false;
187             continue;
188         }
189     }
190     
191     switch (state) {
192     case State::OptionalWhitespace:
193     case State::Token:
194         return hadNonWhitespace;
195     case State::QuotedString:
196     case State::Comment:
197         // Unclosed comments or quotes are invalid values.
198         break;
199     }
200     return false;
201 }
202
203 } // namespace RFC7230
204
205 Optional<HTTPHeaderField> HTTPHeaderField::create(String&& unparsedName, String&& unparsedValue)
206 {
207     StringView strippedName = StringView(unparsedName).stripLeadingAndTrailingMatchedCharacters(RFC7230::isWhitespace);
208     StringView strippedValue = StringView(unparsedValue).stripLeadingAndTrailingMatchedCharacters(RFC7230::isWhitespace);
209     if (!RFC7230::isValidName(strippedName) || !RFC7230::isValidValue(strippedValue))
210         return WTF::nullopt;
211
212     String name = strippedName.length() == unparsedName.length() ? WTFMove(unparsedName) : strippedName.toString();
213     String value = strippedValue.length() == unparsedValue.length() ? WTFMove(unparsedValue) : strippedValue.toString();
214     return {{ WTFMove(name), WTFMove(value) }};
215 }
216
217 }