| 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 |