OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/uri.h" | 5 #include "vm/uri.h" |
6 | 6 |
7 #include "vm/zone.h" | 7 #include "vm/zone.h" |
8 | 8 |
9 namespace dart { | 9 namespace dart { |
10 | 10 |
11 static bool IsUnreservedChar(intptr_t value) { | 11 static bool IsUnreservedChar(intptr_t value) { |
12 return ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || | 12 return ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || |
13 (value >= '0' && value <= '9') || value == '-' || value == '.' || | 13 (value >= '0' && value <= '9') || value == '-' || value == '.' || |
14 value == '_' || value == '~'); | 14 value == '_' || value == '~'); |
15 } | 15 } |
16 | 16 |
17 | |
18 static bool IsDelimiter(intptr_t value) { | 17 static bool IsDelimiter(intptr_t value) { |
19 switch (value) { | 18 switch (value) { |
20 case ':': | 19 case ':': |
21 case '/': | 20 case '/': |
22 case '?': | 21 case '?': |
23 case '#': | 22 case '#': |
24 case '[': | 23 case '[': |
25 case ']': | 24 case ']': |
26 case '@': | 25 case '@': |
27 case '!': | 26 case '!': |
28 case '$': | 27 case '$': |
29 case '&': | 28 case '&': |
30 case '\'': | 29 case '\'': |
31 case '(': | 30 case '(': |
32 case ')': | 31 case ')': |
33 case '*': | 32 case '*': |
34 case '+': | 33 case '+': |
35 case ',': | 34 case ',': |
36 case ';': | 35 case ';': |
37 case '=': | 36 case '=': |
38 return true; | 37 return true; |
39 default: | 38 default: |
40 return false; | 39 return false; |
41 } | 40 } |
42 } | 41 } |
43 | 42 |
44 | |
45 static bool IsHexDigit(char value) { | 43 static bool IsHexDigit(char value) { |
46 return ((value >= '0' && value <= '9') || (value >= 'A' && value <= 'F') || | 44 return ((value >= '0' && value <= '9') || (value >= 'A' && value <= 'F') || |
47 (value >= 'a' && value <= 'f')); | 45 (value >= 'a' && value <= 'f')); |
48 } | 46 } |
49 | 47 |
50 | |
51 static int HexValue(char digit) { | 48 static int HexValue(char digit) { |
52 if ((digit >= '0' && digit <= '9')) { | 49 if ((digit >= '0' && digit <= '9')) { |
53 return digit - '0'; | 50 return digit - '0'; |
54 } | 51 } |
55 if ((digit >= 'A' && digit <= 'F')) { | 52 if ((digit >= 'A' && digit <= 'F')) { |
56 return digit - 'A' + 10; | 53 return digit - 'A' + 10; |
57 } | 54 } |
58 if ((digit >= 'a' && digit <= 'f')) { | 55 if ((digit >= 'a' && digit <= 'f')) { |
59 return digit - 'a' + 10; | 56 return digit - 'a' + 10; |
60 } | 57 } |
61 UNREACHABLE(); | 58 UNREACHABLE(); |
62 return 0; | 59 return 0; |
63 } | 60 } |
64 | 61 |
65 | |
66 static int GetEscapedValue(const char* str, intptr_t pos, intptr_t len) { | 62 static int GetEscapedValue(const char* str, intptr_t pos, intptr_t len) { |
67 if (pos + 2 >= len) { | 63 if (pos + 2 >= len) { |
68 // Not enough room for a valid escape sequence. | 64 // Not enough room for a valid escape sequence. |
69 return -1; | 65 return -1; |
70 } | 66 } |
71 if (str[pos] != '%') { | 67 if (str[pos] != '%') { |
72 // Escape sequences start with '%'. | 68 // Escape sequences start with '%'. |
73 return -1; | 69 return -1; |
74 } | 70 } |
75 | 71 |
76 char digit1 = str[pos + 1]; | 72 char digit1 = str[pos + 1]; |
77 char digit2 = str[pos + 2]; | 73 char digit2 = str[pos + 2]; |
78 if (!IsHexDigit(digit1) || !IsHexDigit(digit2)) { | 74 if (!IsHexDigit(digit1) || !IsHexDigit(digit2)) { |
79 // Invalid escape sequence. Ignore it. | 75 // Invalid escape sequence. Ignore it. |
80 return -1; | 76 return -1; |
81 } | 77 } |
82 return HexValue(digit1) * 16 + HexValue(digit2); | 78 return HexValue(digit1) * 16 + HexValue(digit2); |
83 } | 79 } |
84 | 80 |
85 | |
86 static char* NormalizeEscapes(const char* str, intptr_t len) { | 81 static char* NormalizeEscapes(const char* str, intptr_t len) { |
87 // Allocate the buffer. | 82 // Allocate the buffer. |
88 Zone* zone = Thread::Current()->zone(); | 83 Zone* zone = Thread::Current()->zone(); |
89 // We multiply len by three because a percent-escape sequence is | 84 // We multiply len by three because a percent-escape sequence is |
90 // three characters long (e.g. ' ' -> '%20). +1 for '\0'. We could | 85 // three characters long (e.g. ' ' -> '%20). +1 for '\0'. We could |
91 // take two passes through the string and avoid the excess | 86 // take two passes through the string and avoid the excess |
92 // allocation, but it's zone-memory so it doesn't seem necessary. | 87 // allocation, but it's zone-memory so it doesn't seem necessary. |
93 char* buffer = zone->Alloc<char>(len * 3 + 1); | 88 char* buffer = zone->Alloc<char>(len * 3 + 1); |
94 | 89 |
95 // Copy the string, normalizing as we go. | 90 // Copy the string, normalizing as we go. |
(...skipping 26 matching lines...) Expand all Loading... |
122 OS::SNPrint(buffer + buffer_pos, 4, "%%%02X", c); | 117 OS::SNPrint(buffer + buffer_pos, 4, "%%%02X", c); |
123 buffer_pos += 3; | 118 buffer_pos += 3; |
124 } | 119 } |
125 pos++; | 120 pos++; |
126 } | 121 } |
127 } | 122 } |
128 buffer[buffer_pos] = '\0'; | 123 buffer[buffer_pos] = '\0'; |
129 return buffer; | 124 return buffer; |
130 } | 125 } |
131 | 126 |
132 | |
133 // Lower-case a string in place. | 127 // Lower-case a string in place. |
134 static void StringLower(char* str) { | 128 static void StringLower(char* str) { |
135 const intptr_t len = strlen(str); | 129 const intptr_t len = strlen(str); |
136 intptr_t i = 0; | 130 intptr_t i = 0; |
137 while (i < len) { | 131 while (i < len) { |
138 int escaped_value = GetEscapedValue(str, i, len); | 132 int escaped_value = GetEscapedValue(str, i, len); |
139 if (escaped_value >= 0) { | 133 if (escaped_value >= 0) { |
140 // Don't lowercase escape sequences. | 134 // Don't lowercase escape sequences. |
141 i += 3; | 135 i += 3; |
142 } else { | 136 } else { |
143 // I don't use tolower() because I don't want the locale | 137 // I don't use tolower() because I don't want the locale |
144 // transforming any non-acii characters. | 138 // transforming any non-acii characters. |
145 char c = str[i]; | 139 char c = str[i]; |
146 if (c >= 'A' && c <= 'Z') { | 140 if (c >= 'A' && c <= 'Z') { |
147 str[i] = c + ('a' - 'A'); | 141 str[i] = c + ('a' - 'A'); |
148 } | 142 } |
149 i++; | 143 i++; |
150 } | 144 } |
151 } | 145 } |
152 } | 146 } |
153 | 147 |
154 | |
155 static void ClearParsedUri(ParsedUri* parsed_uri) { | 148 static void ClearParsedUri(ParsedUri* parsed_uri) { |
156 parsed_uri->scheme = NULL; | 149 parsed_uri->scheme = NULL; |
157 parsed_uri->userinfo = NULL; | 150 parsed_uri->userinfo = NULL; |
158 parsed_uri->host = NULL; | 151 parsed_uri->host = NULL; |
159 parsed_uri->port = NULL; | 152 parsed_uri->port = NULL; |
160 parsed_uri->path = NULL; | 153 parsed_uri->path = NULL; |
161 parsed_uri->query = NULL; | 154 parsed_uri->query = NULL; |
162 parsed_uri->fragment = NULL; | 155 parsed_uri->fragment = NULL; |
163 } | 156 } |
164 | 157 |
165 | |
166 static intptr_t ParseAuthority(const char* authority, ParsedUri* parsed_uri) { | 158 static intptr_t ParseAuthority(const char* authority, ParsedUri* parsed_uri) { |
167 Zone* zone = Thread::Current()->zone(); | 159 Zone* zone = Thread::Current()->zone(); |
168 const char* current = authority; | 160 const char* current = authority; |
169 intptr_t len = 0; | 161 intptr_t len = 0; |
170 | 162 |
171 size_t userinfo_len = strcspn(current, "@/"); | 163 size_t userinfo_len = strcspn(current, "@/"); |
172 if (current[userinfo_len] == '@') { | 164 if (current[userinfo_len] == '@') { |
173 // The '@' character follows the optional userinfo string. | 165 // The '@' character follows the optional userinfo string. |
174 parsed_uri->userinfo = NormalizeEscapes(current, userinfo_len); | 166 parsed_uri->userinfo = NormalizeEscapes(current, userinfo_len); |
175 current += userinfo_len + 1; | 167 current += userinfo_len + 1; |
(...skipping 13 matching lines...) Expand all Loading... |
189 const char* port_start = current + host_len + 1; // +1 for ':' | 181 const char* port_start = current + host_len + 1; // +1 for ':' |
190 size_t port_len = strcspn(port_start, "/"); | 182 size_t port_len = strcspn(port_start, "/"); |
191 parsed_uri->port = zone->MakeCopyOfStringN(port_start, port_len); | 183 parsed_uri->port = zone->MakeCopyOfStringN(port_start, port_len); |
192 len += 1 + port_len; // +1 for ':' | 184 len += 1 + port_len; // +1 for ':' |
193 } else { | 185 } else { |
194 parsed_uri->port = NULL; | 186 parsed_uri->port = NULL; |
195 } | 187 } |
196 return len; | 188 return len; |
197 } | 189 } |
198 | 190 |
199 | |
200 // Performs a simple parse of a uri into its components. | 191 // Performs a simple parse of a uri into its components. |
201 // See RFC 3986 Section 3: Syntax. | 192 // See RFC 3986 Section 3: Syntax. |
202 bool ParseUri(const char* uri, ParsedUri* parsed_uri) { | 193 bool ParseUri(const char* uri, ParsedUri* parsed_uri) { |
203 Zone* zone = Thread::Current()->zone(); | 194 Zone* zone = Thread::Current()->zone(); |
204 | 195 |
205 // The first ':' separates the scheme from the rest of the uri. If | 196 // The first ':' separates the scheme from the rest of the uri. If |
206 // a ':' occurs after the first '/' it doesn't count. | 197 // a ':' occurs after the first '/' it doesn't count. |
207 size_t scheme_len = strcspn(uri, ":/"); | 198 size_t scheme_len = strcspn(uri, ":/"); |
208 const char* rest = uri; | 199 const char* rest = uri; |
209 if (uri[scheme_len] == ':') { | 200 if (uri[scheme_len] == ':') { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 parsed_uri->userinfo = NULL; | 243 parsed_uri->userinfo = NULL; |
253 parsed_uri->host = NULL; | 244 parsed_uri->host = NULL; |
254 parsed_uri->port = NULL; | 245 parsed_uri->port = NULL; |
255 } | 246 } |
256 | 247 |
257 // The path is the substring between the authority and the query. | 248 // The path is the substring between the authority and the query. |
258 parsed_uri->path = NormalizeEscapes(path_start, (question_pos - path_start)); | 249 parsed_uri->path = NormalizeEscapes(path_start, (question_pos - path_start)); |
259 return true; | 250 return true; |
260 } | 251 } |
261 | 252 |
262 | |
263 static char* RemoveLastSegment(char* current, char* base) { | 253 static char* RemoveLastSegment(char* current, char* base) { |
264 if (current == base) { | 254 if (current == base) { |
265 return current; | 255 return current; |
266 } | 256 } |
267 ASSERT(current > base); | 257 ASSERT(current > base); |
268 for (current--; current > base; current--) { | 258 for (current--; current > base; current--) { |
269 if (*current == '/') { | 259 if (*current == '/') { |
270 // We have found the beginning of the last segment. | 260 // We have found the beginning of the last segment. |
271 return current; | 261 return current; |
272 } | 262 } |
273 } | 263 } |
274 ASSERT(current == base); | 264 ASSERT(current == base); |
275 return current; | 265 return current; |
276 } | 266 } |
277 | 267 |
278 | |
279 static intptr_t SegmentLength(const char* input) { | 268 static intptr_t SegmentLength(const char* input) { |
280 const char* cp = input; | 269 const char* cp = input; |
281 | 270 |
282 // Include initial slash in the segment, if any. | 271 // Include initial slash in the segment, if any. |
283 if (*cp == '/') { | 272 if (*cp == '/') { |
284 cp++; | 273 cp++; |
285 } | 274 } |
286 | 275 |
287 // Don't include trailing slash in the segment. | 276 // Don't include trailing slash in the segment. |
288 cp += strcspn(cp, "/"); | 277 cp += strcspn(cp, "/"); |
289 return cp - input; | 278 return cp - input; |
290 } | 279 } |
291 | 280 |
292 | |
293 // See RFC 3986 Section 5.2.4: Remove Dot Segments. | 281 // See RFC 3986 Section 5.2.4: Remove Dot Segments. |
294 static const char* RemoveDotSegments(const char* path) { | 282 static const char* RemoveDotSegments(const char* path) { |
295 const char* input = path; | 283 const char* input = path; |
296 | 284 |
297 // The output path will always be less than or equal to the size of | 285 // The output path will always be less than or equal to the size of |
298 // the input path. | 286 // the input path. |
299 Zone* zone = Thread::Current()->zone(); | 287 Zone* zone = Thread::Current()->zone(); |
300 char* buffer = zone->Alloc<char>(strlen(path) + 1); // +1 for '\0' | 288 char* buffer = zone->Alloc<char>(strlen(path) + 1); // +1 for '\0' |
301 char* output = buffer; | 289 char* output = buffer; |
302 | 290 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 } | 333 } |
346 strncpy(output, input, segment_len); | 334 strncpy(output, input, segment_len); |
347 output += segment_len; | 335 output += segment_len; |
348 input += segment_len; | 336 input += segment_len; |
349 } | 337 } |
350 } | 338 } |
351 *output = '\0'; | 339 *output = '\0'; |
352 return buffer; | 340 return buffer; |
353 } | 341 } |
354 | 342 |
355 | |
356 // See RFC 3986 Section 5.2.3: Merge Paths. | 343 // See RFC 3986 Section 5.2.3: Merge Paths. |
357 static const char* MergePaths(const char* base_path, const char* ref_path) { | 344 static const char* MergePaths(const char* base_path, const char* ref_path) { |
358 Zone* zone = Thread::Current()->zone(); | 345 Zone* zone = Thread::Current()->zone(); |
359 if (base_path[0] == '\0') { | 346 if (base_path[0] == '\0') { |
360 // If the base_path is empty, we prepend '/'. | 347 // If the base_path is empty, we prepend '/'. |
361 return zone->PrintToString("/%s", ref_path); | 348 return zone->PrintToString("/%s", ref_path); |
362 } | 349 } |
363 | 350 |
364 // We need to find the last '/' in base_path. | 351 // We need to find the last '/' in base_path. |
365 const char* last_slash = strrchr(base_path, '/'); | 352 const char* last_slash = strrchr(base_path, '/'); |
(...skipping 17 matching lines...) Expand all Loading... |
383 | 370 |
384 // Copy the ref_path. | 371 // Copy the ref_path. |
385 strncpy((buffer + truncated_base_len + 1), ref_path, ref_path_len); | 372 strncpy((buffer + truncated_base_len + 1), ref_path, ref_path_len); |
386 | 373 |
387 // Add the trailing '\0'. | 374 // Add the trailing '\0'. |
388 buffer[len] = '\0'; | 375 buffer[len] = '\0'; |
389 | 376 |
390 return buffer; | 377 return buffer; |
391 } | 378 } |
392 | 379 |
393 | |
394 static char* BuildUri(const ParsedUri& uri) { | 380 static char* BuildUri(const ParsedUri& uri) { |
395 Zone* zone = Thread::Current()->zone(); | 381 Zone* zone = Thread::Current()->zone(); |
396 ASSERT(uri.path != NULL); | 382 ASSERT(uri.path != NULL); |
397 | 383 |
398 const char* fragment = uri.fragment == NULL ? "" : uri.fragment; | 384 const char* fragment = uri.fragment == NULL ? "" : uri.fragment; |
399 const char* fragment_separator = uri.fragment == NULL ? "" : "#"; | 385 const char* fragment_separator = uri.fragment == NULL ? "" : "#"; |
400 const char* query = uri.query == NULL ? "" : uri.query; | 386 const char* query = uri.query == NULL ? "" : uri.query; |
401 const char* query_separator = uri.query == NULL ? "" : "?"; | 387 const char* query_separator = uri.query == NULL ? "" : "?"; |
402 | 388 |
403 // If there is no scheme for this uri, just build a relative uri of | 389 // If there is no scheme for this uri, just build a relative uri of |
(...skipping 25 matching lines...) Expand all Loading... |
429 | 415 |
430 // Uri with authority: | 416 // Uri with authority: |
431 // "scheme://[userinfo@]host[:port][/]path[?query][#fragment]" | 417 // "scheme://[userinfo@]host[:port][/]path[?query][#fragment]" |
432 return zone->PrintToString( | 418 return zone->PrintToString( |
433 "%s://%s%s%s%s%s%s%s%s%s%s%s", // There is *nothing* wrong with this. | 419 "%s://%s%s%s%s%s%s%s%s%s%s%s", // There is *nothing* wrong with this. |
434 uri.scheme, user, user_separator, uri.host, port_separator, port, | 420 uri.scheme, user, user_separator, uri.host, port_separator, port, |
435 path_separator, uri.path, query_separator, query, fragment_separator, | 421 path_separator, uri.path, query_separator, query, fragment_separator, |
436 fragment); | 422 fragment); |
437 } | 423 } |
438 | 424 |
439 | |
440 // See RFC 3986 Section 5: Reference Resolution | 425 // See RFC 3986 Section 5: Reference Resolution |
441 bool ResolveUri(const char* ref_uri, | 426 bool ResolveUri(const char* ref_uri, |
442 const char* base_uri, | 427 const char* base_uri, |
443 const char** target_uri) { | 428 const char** target_uri) { |
444 // Parse the reference uri. | 429 // Parse the reference uri. |
445 ParsedUri ref; | 430 ParsedUri ref; |
446 if (!ParseUri(ref_uri, &ref)) { | 431 if (!ParseUri(ref_uri, &ref)) { |
447 *target_uri = NULL; | 432 *target_uri = NULL; |
448 return false; | 433 return false; |
449 } | 434 } |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 target.port = base.port; | 522 target.port = base.port; |
538 target.path = RemoveDotSegments(MergePaths(base.path, ref.path)); | 523 target.path = RemoveDotSegments(MergePaths(base.path, ref.path)); |
539 target.query = ref.query; | 524 target.query = ref.query; |
540 target.fragment = ref.fragment; | 525 target.fragment = ref.fragment; |
541 *target_uri = BuildUri(target); | 526 *target_uri = BuildUri(target); |
542 return true; | 527 return true; |
543 } | 528 } |
544 } | 529 } |
545 | 530 |
546 } // namespace dart | 531 } // namespace dart |
OLD | NEW |