GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_url.cc Lines: 643 754 85.3 %
Date: 2016-11-30 Branches: 589 854 69.0 %

Line Branch Exec Source
1
#include "node_url.h"
2
#include "node.h"
3
#include "node_internals.h"
4
#include "env.h"
5
#include "env-inl.h"
6
#include "util.h"
7
#include "util-inl.h"
8
#include "v8.h"
9
#include "base-object.h"
10
#include "base-object-inl.h"
11
#include "node_i18n.h"
12
13
#include <string>
14
#include <vector>
15
#include <stdio.h>
16
#include <cmath>
17
18
#if defined(NODE_HAVE_I18N_SUPPORT)
19
#include <unicode/utf8.h>
20
#include <unicode/utf.h>
21
#endif
22
23
namespace node {
24
25
using v8::Array;
26
using v8::Context;
27
using v8::Function;
28
using v8::FunctionCallbackInfo;
29
using v8::HandleScope;
30
using v8::Integer;
31
using v8::Isolate;
32
using v8::Local;
33
using v8::Null;
34
using v8::Object;
35
using v8::String;
36
using v8::Undefined;
37
using v8::Value;
38
39
#define GET(env, obj, name)                                                   \
40
  obj->Get(env->context(),                                                    \
41
           OneByteString(env->isolate(), name)).ToLocalChecked()
42
43
#define GET_AND_SET(env, obj, name, data, flag)                               \
44
  {                                                                           \
45
    Local<Value> val = GET(env, obj, #name);                                  \
46
    if (val->IsString()) {                                                    \
47
      Utf8Value value(env->isolate(), val.As<String>());                      \
48
      data->name = *value;                                                    \
49
      data->flags |= flag;                                                    \
50
    }                                                                         \
51
  }
52
53
#define CANNOT_BE_BASE() url.flags |= URL_FLAGS_CANNOT_BE_BASE;
54
#define INVALID_PARSE_STATE() url.flags |= URL_FLAGS_INVALID_PARSE_STATE;
55
#define SPECIAL()                                                             \
56
  {                                                                           \
57
    url.flags |= URL_FLAGS_SPECIAL;                                           \
58
    special = true;                                                           \
59
  }
60
#define TERMINATE()                                                           \
61
  {                                                                           \
62
    url.flags |= URL_FLAGS_TERMINATED;                                        \
63
    goto done;                                                                \
64
  }
65
#define FAILED()                                                              \
66
  {                                                                           \
67
    url.flags |= URL_FLAGS_FAILED;                                            \
68
    goto done;                                                                \
69
  }
70
71
#define CHECK_FLAG(flags, name) (flags & URL_FLAGS_##name) /* NOLINT */
72
73
#define IS_CANNOT_BE_BASE(flags) CHECK_FLAG(flags, CANNOT_BE_BASE)
74
#define IS_FAILED(flags) CHECK_FLAG(flags, FAILED)
75
76
#define DOES_HAVE_SCHEME(url) CHECK_FLAG(url.flags, HAS_SCHEME)
77
#define DOES_HAVE_USERNAME(url) CHECK_FLAG(url.flags, HAS_USERNAME)
78
#define DOES_HAVE_PASSWORD(url) CHECK_FLAG(url.flags, HAS_PASSWORD)
79
#define DOES_HAVE_HOST(url) CHECK_FLAG(url.flags, HAS_HOST)
80
#define DOES_HAVE_PATH(url) CHECK_FLAG(url.flags, HAS_PATH)
81
#define DOES_HAVE_QUERY(url) CHECK_FLAG(url.flags, HAS_QUERY)
82
#define DOES_HAVE_FRAGMENT(url) CHECK_FLAG(url.flags, HAS_FRAGMENT)
83
84
#define SET_HAVE_SCHEME() url.flags |= URL_FLAGS_HAS_SCHEME;
85
#define SET_HAVE_USERNAME() url.flags |= URL_FLAGS_HAS_USERNAME;
86
#define SET_HAVE_PASSWORD() url.flags |= URL_FLAGS_HAS_PASSWORD;
87
#define SET_HAVE_HOST() url.flags |= URL_FLAGS_HAS_HOST;
88
#define SET_HAVE_PATH() url.flags |= URL_FLAGS_HAS_PATH;
89
#define SET_HAVE_QUERY() url.flags |= URL_FLAGS_HAS_QUERY;
90
#define SET_HAVE_FRAGMENT() url.flags |= URL_FLAGS_HAS_FRAGMENT;
91
92
#define UTF8STRING(isolate, str)                                              \
93
  String::NewFromUtf8(isolate, str.c_str(), v8::NewStringType::kNormal)       \
94
    .ToLocalChecked()
95
96
namespace url {
97
98
#if defined(NODE_HAVE_I18N_SUPPORT)
99
  static int ToUnicode(std::string* input, std::string* output) {
100
    MaybeStackBuffer<char> buf;
101
    if (i18n::ToUnicode(&buf, input->c_str(), input->length()) < 0)
102
      return -1;
103
    output->assign(*buf, buf.length());
104
    return 0;
105
  }
106
107
443
  static int ToASCII(std::string* input, std::string* output) {
108
886
    MaybeStackBuffer<char> buf;
109
886
    if (i18n::ToASCII(&buf, input->c_str(), input->length()) < 0)
110
      return -1;
111
886
    output->assign(*buf, buf.length());
112
443
    return 0;
113
  }
114
115
  // Unfortunately there's not really a better way to do this.
116
  // Iterate through each encoded codepoint and verify that
117
  // it is a valid unicode codepoint.
118
445
  static int IsValidUTF8(std::string* input) {
119
445
    const char* p = input->c_str();
120
445
    int32_t len = input->length();
121
445
    for (int32_t i = 0; i < len;) {
122
      UChar32 c;
123

4459
      U8_NEXT_UNSAFE(p, i, c);
124



4459
      if (!U_IS_UNICODE_CHAR(c))
125
        return -1;
126
    }
127
    return 0;
128
  }
129
#else
130
  // Intentional non-ops if ICU is not present.
131
  static int ToUnicode(std::string* input, std::string* output) {
132
    output->reserve(input->length());
133
    *output = input->c_str();
134
  }
135
136
  static int ToASCII(std::string* input, std::string* output) {
137
    output->reserve(input->length());
138
    *output = input->c_str();
139
  }
140
141
  static int IsValidUTF8(std::string* input) {
142
    return 0;
143
  }
144
#endif
145
146
9
  static url_host_type ParseIPv6Host(url_host* host,
147
                                     const char* input,
148
                                     size_t length) {
149
9
    url_host_type type = HOST_TYPE_FAILED;
150
81
    for (unsigned n = 0; n < 8; n++)
151
72
      host->value.ipv6[n] = 0;
152
9
    uint16_t* piece_pointer = &host->value.ipv6[0];
153
9
    uint16_t* last_piece = piece_pointer + 8;
154
9
    uint16_t* compress_pointer = nullptr;
155
9
    const char* pointer = input;
156
9
    const char* end = pointer + length;
157
    unsigned value, len, swaps, dots;
158
9
    char ch = pointer < end ? pointer[0] : kEOL;
159
9
    if (ch == ':') {
160

3
      if (length < 2 || pointer[1] != ':')
161
        goto end;
162
2
      pointer += 2;
163
2
      ch = pointer < end ? pointer[0] : kEOL;
164
2
      piece_pointer++;
165
2
      compress_pointer = piece_pointer;
166
    }
167
21
    while (ch != kEOL) {
168
15
      if (piece_pointer > last_piece)
169
        goto end;
170
15
      if (ch == ':') {
171
3
        if (compress_pointer != nullptr)
172
          goto end;
173
3
        pointer++;
174
3
        ch = pointer < end ? pointer[0] : kEOL;
175
3
        piece_pointer++;
176
3
        compress_pointer = piece_pointer;
177
3
        continue;
178
      }
179
      value = 0;
180
      len = 0;
181

48
      while (len < 4 && ASCII_HEX_DIGIT(ch)) {
182
36
        value = value * 0x10 + hex2bin(ch);
183
18
        pointer++;
184
18
        ch = pointer < end ? pointer[0] : kEOL;
185
18
        len++;
186
      }
187

12
      switch (ch) {
188
        case '.':
189
          if (len == 0)
190
            goto end;
191
          pointer -= len;
192
          ch = pointer < end ? pointer[0] : kEOL;
193
          if (piece_pointer > last_piece - 2)
194
            goto end;
195
          dots = 0;
196
          while (ch != kEOL) {
197
            value = 0xffffffff;
198
            if (!ASCII_DIGIT(ch))
199
              goto end;
200
            while (ASCII_DIGIT(ch)) {
201
              unsigned number = ch - '0';
202
              if (value == 0xffffffff) {
203
                value = number;
204
              } else if (value == 0) {
205
                goto end;
206
              } else {
207
                value = value * 10 + number;
208
              }
209
              if (value > 255)
210
                goto end;
211
              pointer++;
212
              ch = pointer < end ? pointer[0] : kEOL;
213
            }
214
            if (dots < 3 && ch != '.')
215
              goto end;
216
            *piece_pointer = *piece_pointer * 0x100 + value;
217
            if (dots & 0x1)
218
              piece_pointer++;
219
            if (ch != kEOL) {
220
              pointer++;
221
              ch = pointer < end ? pointer[0] : kEOL;
222
            }
223
            if (dots == 3 && ch != kEOL)
224
              goto end;
225
            dots++;
226
          }
227
          continue;
228
        case ':':
229
5
          pointer++;
230
5
          ch = pointer < end ? pointer[0] : kEOL;
231
5
          if (ch == kEOL)
232
            goto end;
233
          break;
234
        case kEOL:
235
          break;
236
        default:
237
          goto end;
238
      }
239
10
      *piece_pointer = value;
240
10
      piece_pointer++;
241
    }
242
243
6
    if (compress_pointer != nullptr) {
244
5
      swaps = piece_pointer - compress_pointer;
245
5
      piece_pointer = last_piece - 1;
246

19
      while (piece_pointer != &host->value.ipv6[0] && swaps > 0) {
247
7
        uint16_t temp = *piece_pointer;
248
7
        uint16_t* swap_piece = compress_pointer + swaps - 1;
249
7
        *piece_pointer = *swap_piece;
250
7
        *swap_piece = temp;
251
7
         piece_pointer--;
252
7
         swaps--;
253
      }
254
2
    } else if (compress_pointer == nullptr &&
255
1
               piece_pointer != last_piece) {
256
      goto end;
257
    }
258
    type = HOST_TYPE_IPV6;
259
   end:
260
9
    host->type = type;
261
9
    return type;
262
  }
263
264
445
  static inline int ParseNumber(const char* start, const char* end) {
265
445
    unsigned R = 10;
266

445
    if (end - start >= 2 && start[0] == '0' && (start[1] | 0x20) == 'x') {
267
6
      start += 2;
268
6
      R = 16;
269
    }
270
445
    if (end - start == 0) {
271
      return 0;
272

445
    } else if (R == 10 && end - start > 1 && start[0] == '0') {
273
6
      start++;
274
6
      R = 8;
275
    }
276
445
    const char* p = start;
277
278
607
    while (p < end) {
279
498
      const char ch = p[0];
280

498
      switch (R) {
281
        case 8:
282
12
          if (ch < '0' || ch > '7')
283
            return -1;
284
          break;
285
        case 10:
286
456
          if (!ASCII_DIGIT(ch))
287
            return -1;
288
          break;
289
        case 16:
290

30
          if (!ASCII_HEX_DIGIT(ch))
291
            return -1;
292
          break;
293
      }
294
81
      p++;
295
    }
296
28
    return strtol(start, NULL, R);
297
  }
298
299
429
  static url_host_type ParseIPv4Host(url_host* host,
300
                                     const char* input,
301
                                     size_t length) {
302
429
    url_host_type type = HOST_TYPE_DOMAIN;
303
429
    const char* pointer = input;
304
429
    const char* mark = input;
305
429
    const char* end = pointer + length;
306
429
    int parts = 0;
307
429
    uint32_t val = 0;
308
    unsigned numbers[4];
309
429
    if (length == 0)
310
      goto end;
311
312
3039
    while (pointer <= end) {
313
3035
      const char ch = pointer < end ? pointer[0] : kEOL;
314
3035
      const int remaining = end - pointer - 1;
315
3035
      if (ch == '.' || ch == kEOL) {
316

445
        if (++parts > 4 || pointer - mark == 0)
317
          break;
318
445
        int n = ParseNumber(mark, pointer);
319
445
        if (n < 0) {
320
          type = HOST_TYPE_DOMAIN;
321
          goto end;
322
        }
323
28
        if (pointer - mark == 10) {
324
3
          numbers[parts - 1] = n;
325
          break;
326
        }
327
25
        if (n > 255) {
328
          type = HOST_TYPE_FAILED;
329
          goto end;
330
        }
331
21
        numbers[parts - 1] = n;
332
21
        mark = pointer + 1;
333
21
        if (ch == '.' && remaining == 0)
334
          break;
335
      }
336
2610
      pointer++;
337
    }
338
339
8
    type = HOST_TYPE_IPV4;
340
8
    if (parts > 0) {
341
8
      val = numbers[parts - 1];
342
21
      for (int n = 0; n < parts - 1; n++) {
343
13
        double b = 3-n;
344
13
        val += numbers[n] * pow(256, b);
345
      }
346
    }
347
348
8
    host->value.ipv4 = val;
349
   end:
350
429
    host->type = type;
351
429
    return type;
352
  }
353
354
454
  static url_host_type ParseHost(url_host* host,
355
                                 const char* input,
356
                                 size_t length,
357
                                 bool unicode = false) {
358
454
    url_host_type type = HOST_TYPE_FAILED;
359
454
    const char* pointer = input;
360
908
    std::string decoded;
361
362
454
    if (length == 0)
363
      goto end;
364
365
454
    if (pointer[0] == '[') {
366
9
      if (pointer[length - 1] != ']')
367
        goto end;
368
9
      return ParseIPv6Host(host, ++pointer, length - 2);
369
    }
370
371
    // First, we have to percent decode
372
445
    if (PercentDecode(input, length, &decoded) < 0)
373
      goto end;
374
375
    // If there are any invalid UTF8 byte sequences, we have to fail.
376
    // Unfortunately this means iterating through the string and checking
377
    // each decoded codepoint.
378
445
    if (IsValidUTF8(&decoded) < 0)
379
      goto end;
380
381
    // Then we have to punycode toASCII
382
443
    if (ToASCII(&decoded, &decoded) < 0)
383
      goto end;
384
385
    // If any of the following characters are still present, we have to fail
386
9173
    for (size_t n = 0; n < decoded.size(); n++) {
387
4379
      const char ch = decoded[n];
388

4379
      if (ch == 0x00 || ch == 0x09 || ch == 0x0a || ch == 0x0d ||
389

4378
          ch == 0x20 || ch == '#' || ch == '%' || ch == '/' ||
390

4367
          ch == '?' || ch == '@' || ch == '[' || ch == '\\' ||
391
          ch == ']') {
392
        goto end;
393
      }
394
    }
395
396
    // Check to see if it's an IPv4 IP address
397
858
    type = ParseIPv4Host(host, decoded.c_str(), decoded.length());
398
429
    if (type == HOST_TYPE_IPV4 || type == HOST_TYPE_FAILED)
399
      goto end;
400
401
    // If the unicode flag is set, run the result through punycode ToUnicode
402

417
    if (unicode && ToUnicode(&decoded, &decoded) < 0)
403
      goto end;
404
405
    // It's not an IPv4 or IPv6 address, it must be a domain
406
417
    type = HOST_TYPE_DOMAIN;
407
417
    host->value.domain = decoded;
408
409
   end:
410
445
    host->type = type;
411
445
    return type;
412
  }
413
414
  // Locates the longest sequence of 0 segments in an IPv6 address
415
  // in order to use the :: compression when serializing
416
  static inline uint16_t* FindLongestZeroSequence(uint16_t* values,
417
                                                  size_t len) {
418
5
    uint16_t* start = values;
419
5
    uint16_t* end = start + len;
420
5
    uint16_t* result = nullptr;
421
422
5
    uint16_t* current = nullptr;
423
5
    unsigned counter = 0, longest = 1;
424
425
45
    while (start < end) {
426
40
      if (*start == 0) {
427
32
        if (current == nullptr)
428
5
          current = start;
429
32
        counter++;
430
      } else {
431
8
        if (counter > longest) {
432
5
          longest = counter;
433
5
          result = current;
434
        }
435
        counter = 0;
436
        current = nullptr;
437
      }
438
40
      start++;
439
    }
440
5
    if (counter > longest)
441
      result = current;
442
    return result;
443
  }
444
445
430
  static url_host_type WriteHost(url_host* host, std::string* dest) {
446
430
    dest->clear();
447

430
    switch (host->type) {
448
      case HOST_TYPE_DOMAIN:
449
417
        *dest = host->value.domain;
450
        break;
451
      case HOST_TYPE_IPV4: {
452
8
        dest->reserve(15);
453
8
        uint32_t value = host->value.ipv4;
454
40
        for (int n = 0; n < 4; n++) {
455
          char buf[4];
456
32
          char* buffer = buf;
457
64
          snprintf(buffer, sizeof(buf), "%d", value % 256);
458
32
          dest->insert(0, buf);
459
32
          if (n < 3)
460
24
            dest->insert(0, 1, '.');
461
32
          value /= 256;
462
        }
463
        break;
464
      }
465
      case HOST_TYPE_IPV6: {
466
5
        dest->reserve(41);
467
5
        *dest+= '[';
468
5
        uint16_t* start = &host->value.ipv6[0];
469
        uint16_t* compress_pointer =
470
5
            FindLongestZeroSequence(start, 8);
471
13
        for (int n = 0; n <= 7; n++) {
472
8
          uint16_t* piece = &host->value.ipv6[n];
473
8
          if (compress_pointer == piece) {
474
5
            *dest += n == 0 ? "::" : ":";
475

69
            while (*piece == 0 && ++n < 8)
476
32
              piece = &host->value.ipv6[n];
477
5
            if (n == 8)
478
              break;
479
          }
480
          char buf[5];
481
8
          char* buffer = buf;
482
16
          snprintf(buffer, sizeof(buf), "%x", *piece);
483
8
          *dest += buf;
484
8
          if (n < 7)
485
            *dest += ':';
486
        }
487
        *dest += ']';
488
        break;
489
      }
490
      case HOST_TYPE_FAILED:
491
        break;
492
    }
493
430
    return host->type;
494
  }
495
496
463
  static int ParseHost(std::string* input,
497
                       std::string* output,
498
                       bool unicode = false) {
499
463
    if (input->length() == 0)
500
      return 0;
501
908
    url_host host{{""}, HOST_TYPE_DOMAIN};
502
908
    ParseHost(&host, input->c_str(), input->length(), unicode);
503
454
    if (host.type == HOST_TYPE_FAILED)
504
      return -1;
505
430
    WriteHost(&host, output);
506
430
    return 0;
507
  }
508
509
315
  static inline void Copy(Isolate* isolate,
510
                          Local<Array> ary,
511
                          std::vector<std::string>* vec) {
512
315
    const int32_t len = ary->Length();
513
315
    if (len == 0)
514
      return;  // nothing to copy
515
315
    vec->reserve(len);
516
770
    for (int32_t n = 0; n < len; n++) {
517
455
      Local<Value> val = ary->Get(n);
518
910
      if (val->IsString()) {
519
1365
        Utf8Value value(isolate, val.As<String>());
520
2730
        vec->push_back(std::string(*value, value.length()));
521
      }
522
    }
523
  }
524
525
740
  static inline Local<Array> Copy(Isolate* isolate,
526
                                  std::vector<std::string> vec) {
527
1480
    Local<Array> ary = Array::New(isolate, vec.size());
528
3512
    for (size_t n = 0; n < vec.size(); n++)
529
5080
      ary->Set(n, UTF8STRING(isolate, vec[n]));
530
740
    return ary;
531
  }
532
533
315
  static inline void HarvestBase(Environment* env,
534
                                 struct url_data* base,
535
                                 Local<Object> base_obj) {
536
1260
    Local<Value> flags = GET(env, base_obj, "flags");
537
315
    if (flags->IsInt32())
538
315
      base->flags = flags->Int32Value();
539
540
2835
    GET_AND_SET(env, base_obj, scheme, base, URL_FLAGS_HAS_SCHEME);
541
1587
    GET_AND_SET(env, base_obj, username, base, URL_FLAGS_HAS_USERNAME);
542
1583
    GET_AND_SET(env, base_obj, password, base, URL_FLAGS_HAS_PASSWORD);
543
2175
    GET_AND_SET(env, base_obj, host, base, URL_FLAGS_HAS_HOST);
544
1579
    GET_AND_SET(env, base_obj, query, base, URL_FLAGS_HAS_QUERY);
545
1575
    GET_AND_SET(env, base_obj, fragment, base, URL_FLAGS_HAS_FRAGMENT);
546
1260
    Local<Value> port = GET(env, base_obj, "port");
547
315
    if (port->IsInt32())
548
2
      base->port = port->Int32Value();
549
1260
    Local<Value> path = GET(env, base_obj, "path");
550
315
    if (path->IsArray()) {
551
315
      base->flags |= URL_FLAGS_HAS_PATH;
552
630
      Copy(env->isolate(), path.As<Array>(), &(base->path));
553
    }
554
315
  }
555
556
91
  static inline void HarvestContext(Environment* env,
557
                                    struct url_data* context,
558
                                    Local<Object> context_obj) {
559
364
    Local<Value> flags = GET(env, context_obj, "flags");
560
91
    if (flags->IsInt32()) {
561
91
      int32_t _flags = flags->Int32Value();
562
91
      if (_flags & URL_FLAGS_SPECIAL)
563
69
        context->flags |= URL_FLAGS_SPECIAL;
564
91
      if (_flags & URL_FLAGS_CANNOT_BE_BASE)
565
2
        context->flags |= URL_FLAGS_CANNOT_BE_BASE;
566
    }
567
364
    Local<Value> scheme = GET(env, context_obj, "scheme");
568
182
    if (scheme->IsString()) {
569
182
      Utf8Value value(env->isolate(), scheme);
570
182
      context->scheme.assign(*value, value.length());
571
    }
572
364
    Local<Value> port = GET(env, context_obj, "port");
573
91
    if (port->IsInt32())
574
8
      context->port = port->Int32Value();
575
91
  }
576
577
  // Single dot segment can be ".", "%2e", or "%2E"
578
809
  static inline bool IsSingleDotSegment(std::string str) {
579
809
    switch (str.size()) {
580
      case 1:
581
44
        return str == ".";
582
      case 3:
583
275
        return str[0] == '%' &&
584

278
               str[1] == '2' &&
585


8
               TO_LOWER(str[2]) == 'e';
586
      default:
587
        return false;
588
    }
589
  }
590
591
  // Double dot segment can be:
592
  //   "..", ".%2e", ".%2E", "%2e.", "%2E.",
593
  //   "%2e%2e", "%2E%2E", "%2e%2E", or "%2E%2e"
594
845
  static inline bool IsDoubleDotSegment(std::string str) {
595

845
    switch (str.size()) {
596
      case 2:
597
72
        return str == "..";
598
      case 4:
599

223
        if (str[0] != '.' && str[0] != '%')
600
          return false;
601
2
        return ((str[0] == '.' &&
602
1
                 str[1] == '%' &&
603
                 str[2] == '2' &&
604




2
                 TO_LOWER(str[3]) == 'e') ||
605
1
                (str[0] == '%' &&
606
                 str[1] == '2' &&
607
                 TO_LOWER(str[2]) == 'e' &&
608
                 str[3] == '.'));
609
      case 6:
610
9
        return (str[0] == '%' &&
611
1
                str[1] == '2' &&
612


3
                TO_LOWER(str[2]) == 'e' &&
613
                str[3] == '%' &&
614


9
                str[4] == '2' &&
615
                TO_LOWER(str[5]) == 'e');
616
      default:
617
        return false;
618
    }
619
  }
620
621
846
  static void Parse(Environment* env,
622
                    Local<Value> recv,
623
                    const char* input,
624
                    const size_t len,
625
                    enum url_parse_state override,
626
                    Local<Object> base_obj,
627
                    Local<Object> context_obj,
628
                    Local<Function> cb) {
629
846
    Isolate* isolate = env->isolate();
630
846
    Local<Context> context = env->context();
631
1692
    HandleScope handle_scope(isolate);
632
1692
    Context::Scope context_scope(context);
633
634
846
    const bool has_base = base_obj->IsObject();
635
846
    bool atflag = false;
636
846
    bool sbflag = false;
637
846
    bool uflag = false;
638
846
    bool base_is_file = false;
639
846
    int wskip = 0;
640
641
1692
    struct url_data base;
642
1692
    struct url_data url;
643
846
    if (context_obj->IsObject())
644
91
      HarvestContext(env, &url, context_obj);
645
846
    if (has_base)
646
315
      HarvestBase(env, &base, base_obj);
647
648
1692
    std::string buffer;
649
846
    url.scheme.reserve(len);
650
846
    url.username.reserve(len);
651
846
    url.password.reserve(len);
652
846
    url.host.reserve(len);
653
846
    url.path.reserve(len);
654
846
    url.query.reserve(len);
655
846
    url.fragment.reserve(len);
656
846
    buffer.reserve(len);
657
658
    // Set the initial parse state.
659
846
    const bool state_override = override != kUnknownState;
660
846
    enum url_parse_state state = state_override ? override : kSchemeStart;
661
662
846
    const char* p = input;
663
846
    const char* end = input + len;
664
665
846
    if (state < kSchemeStart || state > kFragment) {
666
      INVALID_PARSE_STATE();
667
      goto done;
668
    }
669
670
22123
    while (p <= end) {
671
21359
      const char ch = p < end ? p[0] : kEOL;
672
673
21359
      if (TAB_AND_NEWLINE(ch)) {
674
42
        if (state == kAuthority) {
675
          // It's necessary to keep track of how much whitespace
676
          // is being ignored when in kAuthority state because of
677
          // how the buffer is managed. TODO: See if there's a better
678
          // way
679
9
          wskip++;
680
        }
681
42
        p++;
682
42
        continue;
683
      }
684
685
21317
      bool special = url.flags & URL_FLAGS_SPECIAL;
686
21317
      const bool special_back_slash = (special && ch == '\\');
687





21317
      switch (state) {
688
        case kSchemeStart:
689
768
          if (ASCII_ALPHA(ch)) {
690
1384
            buffer += TO_LOWER(ch);
691
692
            state = kScheme;
692
76
          } else if (!state_override) {
693
            state = kNoScheme;
694
            continue;
695
          } else {
696
3
            TERMINATE()
697
          }
698
692
          break;
699
        case kScheme:
700


2963
          if (SCHEME_CHAR(ch)) {
701
4542
            buffer += TO_LOWER(ch);
702
2271
            p++;
703
2271
            continue;
704

692
          } else if (ch == ':' || (state_override && ch == kEOL)) {
705
681
            buffer += ':';
706
681
            if (buffer.size() > 0) {
707
681
              SET_HAVE_SCHEME()
708
              url.scheme = buffer;
709
            }
710
1362
            if (IsSpecial(url.scheme)) {
711
437
              SPECIAL()
712
            } else {
713
244
              url.flags &= ~URL_FLAGS_SPECIAL;
714
            }
715
681
            if (state_override)
716
              goto done;
717
673
            buffer.clear();
718
673
            if (url.scheme == "file:") {
719
              state = kFile;
720
1276
            } else if (special &&
721
182
                       has_base &&
722

820
                       DOES_HAVE_SCHEME(base) &&
723
182
                       url.scheme == base.scheme) {
724
              state = kSpecialRelativeOrAuthority;
725
575
            } else if (special) {
726
              state = kSpecialAuthoritySlashes;
727
239
            } else if (p[1] == '/') {
728
73
              state = kPathOrAuthority;
729
73
              p++;
730
            } else {
731
166
              CANNOT_BE_BASE()
732
166
              SET_HAVE_PATH()
733
664
              url.path.push_back("");
734
166
              state = kCannotBeBase;
735
            }
736
11
          } else if (!state_override) {
737
9
            buffer.clear();
738
9
            state = kNoScheme;
739
9
            p = input;
740
9
            continue;
741
          } else {
742
2
            TERMINATE()
743
          }
744
          break;
745
        case kNoScheme:
746

82
          if (!has_base || (IS_CANNOT_BE_BASE(base.flags) && ch != '#')) {
747
8
            FAILED()
748

74
          } else if (IS_CANNOT_BE_BASE(base.flags) && ch == '#') {
749
7
            SET_HAVE_SCHEME()
750
7
            url.scheme = base.scheme;
751
14
            if (IsSpecial(url.scheme)) {
752
              SPECIAL()
753
            } else {
754
7
              url.flags &= ~URL_FLAGS_SPECIAL;
755
            }
756
7
            if (DOES_HAVE_PATH(base)) {
757
7
              SET_HAVE_PATH()
758
7
              url.path = base.path;
759
            }
760
7
            if (DOES_HAVE_QUERY(base)) {
761
1
              SET_HAVE_QUERY()
762
              url.query = base.query;
763
            }
764
7
            if (DOES_HAVE_FRAGMENT(base)) {
765
              SET_HAVE_FRAGMENT()
766
              url.fragment = base.fragment;
767
            }
768
7
            CANNOT_BE_BASE()
769
7
            state = kFragment;
770

134
          } else if (has_base &&
771

134
                     DOES_HAVE_SCHEME(base) &&
772
67
                     base.scheme != "file:") {
773
            state = kRelative;
774
            continue;
775
          } else {
776
10
            SET_HAVE_SCHEME()
777
10
            url.scheme = "file:";
778
10
            SPECIAL()
779
10
            state = kFile;
780
10
            continue;
781
          }
782
7
          break;
783
        case kSpecialRelativeOrAuthority:
784

63
          if (ch == '/' && p[1] == '/') {
785
56
            state = kSpecialAuthorityIgnoreSlashes;
786
56
            p++;
787
          } else {
788
            state = kRelative;
789
            continue;
790
          }
791
56
          break;
792
        case kPathOrAuthority:
793
73
          if (ch == '/') {
794
            state = kAuthority;
795
          } else {
796
            state = kPath;
797
            continue;
798
          }
799
          break;
800
        case kRelative:
801
64
          SET_HAVE_SCHEME()
802
64
          url.scheme = base.scheme;
803
128
          if (IsSpecial(url.scheme)) {
804
49
            SPECIAL()
805
          } else {
806
15
            url.flags &= ~URL_FLAGS_SPECIAL;
807
          }
808

64
          switch (ch) {
809
            case kEOL:
810
3
              if (DOES_HAVE_USERNAME(base)) {
811
1
                SET_HAVE_USERNAME()
812
                url.username = base.username;
813
              }
814
3
              if (DOES_HAVE_PASSWORD(base)) {
815
1
                SET_HAVE_PASSWORD()
816
                url.password = base.password;
817
              }
818
3
              if (DOES_HAVE_HOST(base)) {
819
3
                SET_HAVE_HOST()
820
                url.host = base.host;
821
              }
822
3
              if (DOES_HAVE_QUERY(base)) {
823
                SET_HAVE_QUERY()
824
                url.query = base.query;
825
              }
826
3
              if (DOES_HAVE_PATH(base)) {
827
3
                SET_HAVE_PATH()
828
3
                url.path = base.path;
829
              }
830
3
              url.port = base.port;
831
3
              break;
832
            case '/':
833
              state = kRelativeSlash;
834
              break;
835
            case '?':
836
4
              if (DOES_HAVE_USERNAME(base)) {
837
                SET_HAVE_USERNAME()
838
                url.username = base.username;
839
              }
840
4
              if (DOES_HAVE_PASSWORD(base)) {
841
                SET_HAVE_PASSWORD()
842
                url.password = base.password;
843
              }
844
4
              if (DOES_HAVE_HOST(base)) {
845
3
                SET_HAVE_HOST()
846
                url.host = base.host;
847
              }
848
4
              if (DOES_HAVE_PATH(base)) {
849
4
                SET_HAVE_PATH()
850
4
                url.path = base.path;
851
              }
852
4
              url.port = base.port;
853
4
              state = kQuery;
854
4
              break;
855
            case '#':
856
8
              if (DOES_HAVE_USERNAME(base)) {
857
                SET_HAVE_USERNAME()
858
                url.username = base.username;
859
              }
860
8
              if (DOES_HAVE_PASSWORD(base)) {
861
                SET_HAVE_PASSWORD()
862
                url.password = base.password;
863
              }
864
8
              if (DOES_HAVE_HOST(base)) {
865
7
                SET_HAVE_HOST()
866
                url.host = base.host;
867
              }
868
8
              if (DOES_HAVE_QUERY(base)) {
869
                SET_HAVE_QUERY()
870
                url.query = base.query;
871
              }
872
8
              if (DOES_HAVE_PATH(base)) {
873
8
                SET_HAVE_PATH()
874
8
                url.path = base.path;
875
              }
876
8
              url.port = base.port;
877
8
              state = kFragment;
878
8
              break;
879
            default:
880
33
              if (special_back_slash) {
881
                state = kRelativeSlash;
882
              } else {
883
31
                if (DOES_HAVE_USERNAME(base)) {
884
                  SET_HAVE_USERNAME()
885
                  url.username = base.username;
886
                }
887
31
                if (DOES_HAVE_PASSWORD(base)) {
888
                  SET_HAVE_PASSWORD()
889
                  url.password = base.password;
890
                }
891
31
                if (DOES_HAVE_HOST(base)) {
892
29
                  SET_HAVE_HOST()
893
                  url.host = base.host;
894
                }
895
31
                if (DOES_HAVE_PATH(base)) {
896
31
                  SET_HAVE_PATH()
897
31
                  url.path = base.path;
898
31
                  if (!url.path.empty())
899
31
                    url.path.pop_back();
900
                }
901
31
                url.port = base.port;
902
31
                state = kPath;
903
31
                continue;
904
              }
905
          }
906
          break;
907
        case kRelativeSlash:
908
18
          if (ch == '/' || special_back_slash) {
909
            state = kSpecialAuthorityIgnoreSlashes;
910
          } else {
911
14
            if (DOES_HAVE_USERNAME(base)) {
912
2
              SET_HAVE_USERNAME()
913
              url.username = base.username;
914
            }
915
14
            if (DOES_HAVE_PASSWORD(base)) {
916
1
              SET_HAVE_PASSWORD()
917
              url.password = base.password;
918
            }
919
14
            if (DOES_HAVE_HOST(base)) {
920
13
              SET_HAVE_HOST()
921
              url.host = base.host;
922
            }
923
14
            url.port = base.port;
924
14
            state = kPath;
925
14
            continue;
926
          }
927
          break;
928
        case kSpecialAuthoritySlashes:
929
336
          state = kSpecialAuthorityIgnoreSlashes;
930

336
          if (ch == '/' && p[1] == '/') {
931
296
            p++;
932
          } else {
933
            continue;
934
          }
935
296
          break;
936
        case kSpecialAuthorityIgnoreSlashes:
937
417
          if (ch != '/' && ch != '\\') {
938
            state = kAuthority;
939
            continue;
940
          }
941
          break;
942
        case kAuthority:
943
5342
          if (ch == '@') {
944
57
            if (atflag) {
945
5
              buffer.reserve(buffer.size() + 3);
946
5
              buffer.insert(0, "%40");
947
            }
948
57
            atflag = true;
949
57
            const size_t blen = buffer.size();
950

101
            if (blen > 0 && buffer[0] != ':') {
951
35
              SET_HAVE_USERNAME()
952
            }
953
491
            for (size_t n = 0; n < blen; n++) {
954
217
              const char bch = buffer[n];
955
217
              if (bch == ':') {
956
33
                SET_HAVE_PASSWORD()
957
33
                if (!uflag) {
958
                  uflag = true;
959
                  continue;
960
                }
961
              }
962
185
              if (uflag) {
963
83
                AppendOrEscape(&url.password, bch, UserinfoEncodeSet);
964
              } else {
965
102
                AppendOrEscape(&url.username, bch, UserinfoEncodeSet);
966
              }
967
            }
968
            buffer.clear();
969
10570
          } else if (ch == kEOL ||
970
5285
                     ch == '/' ||
971
9724
                     ch == '?' ||
972
9712
                     ch == '#' ||
973
                     special_back_slash) {
974
439
            p -= buffer.size() + 1 + wskip;
975
439
            buffer.clear();
976
439
            state = kHost;
977
          } else {
978
4846
            buffer += ch;
979
          }
980
          break;
981
        case kHost:
982
        case kHostname:
983
5181
          if (ch == ':' && !sbflag) {
984

81
            if (special && buffer.size() == 0)
985
3
              FAILED()
986
78
            SET_HAVE_HOST()
987
78
            if (ParseHost(&buffer, &url.host) < 0)
988
3
              FAILED()
989
75
            buffer.clear();
990
75
            state = kPort;
991
75
            if (override == kHostname)
992
2
              TERMINATE()
993
10200
          } else if (ch == kEOL ||
994
5100
                     ch == '/' ||
995
9460
                     ch == '?' ||
996
9444
                     ch == '#' ||
997
                     special_back_slash) {
998
392
            p--;
999

392
            if (special && buffer.size() == 0)
1000
11
              FAILED()
1001
381
            SET_HAVE_HOST()
1002
381
            if (ParseHost(&buffer, &url.host) < 0)
1003
21
              FAILED()
1004
360
            buffer.clear();
1005
360
            state = kPathStart;
1006
360
            if (state_override)
1007
14
              TERMINATE()
1008
          } else {
1009
4708
            if (ch == '[')
1010
9
              sbflag = true;
1011
4708
            if (ch == ']')
1012
9
              sbflag = false;
1013
4708
            buffer += TO_LOWER(ch);
1014
          }
1015
          break;
1016
        case kPort:
1017
364
          if (ASCII_DIGIT(ch)) {
1018
278
            buffer += ch;
1019
86
          } else if (state_override ||
1020
86
                     ch == kEOL ||
1021
96
                     ch == '/' ||
1022
48
                     ch == '?' ||
1023
6
                     ch == '#' ||
1024
                     special_back_slash) {
1025
80
            if (buffer.size() > 0) {
1026
              int port = 0;
1027
631
              for (size_t i = 0; i < buffer.size(); i++)
1028
554
                port = port * 10 + buffer[i] - '0';
1029
77
              if (port >= 0 && port <= 0xffff) {
1030
148
                url.port = NormalizePort(url.scheme, port);
1031
3
              } else if (!state_override) {
1032
1
                FAILED()
1033
              }
1034
              buffer.clear();
1035
            }
1036
            state = kPathStart;
1037
            continue;
1038
          } else {
1039
6
            FAILED();
1040
          }
1041
          break;
1042
        case kFile:
1043
          base_is_file = (
1044
24
              has_base &&
1045

69
              DOES_HAVE_SCHEME(base) &&
1046
69
              base.scheme == "file:");
1047

45
          switch (ch) {
1048
            case kEOL:
1049
              if (base_is_file) {
1050
                if (DOES_HAVE_HOST(base)) {
1051
                  SET_HAVE_HOST()
1052
                  url.host = base.host;
1053
                }
1054
                if (DOES_HAVE_PATH(base)) {
1055
                  SET_HAVE_PATH()
1056
                  url.path = base.path;
1057
                }
1058
                if (DOES_HAVE_QUERY(base)) {
1059
                  SET_HAVE_QUERY()
1060
                  url.query = base.query;
1061
                }
1062
              }
1063
              break;
1064
            case '\\':
1065
            case '/':
1066
              state = kFileSlash;
1067
              break;
1068
            case '?':
1069
              if (base_is_file) {
1070
                if (DOES_HAVE_HOST(base)) {
1071
                  SET_HAVE_HOST()
1072
                  url.host = base.host;
1073
                }
1074
                if (DOES_HAVE_PATH(base)) {
1075
                  SET_HAVE_PATH()
1076
                  url.path = base.path;
1077
                }
1078
                SET_HAVE_QUERY()
1079
                state = kQuery;
1080
              }
1081
              break;
1082
            case '#':
1083
              if (base_is_file) {
1084
                if (DOES_HAVE_HOST(base)) {
1085
                  SET_HAVE_HOST()
1086
                  url.host = base.host;
1087
                }
1088
                if (DOES_HAVE_PATH(base)) {
1089
                  SET_HAVE_PATH()
1090
                  url.path = base.path;
1091
                }
1092
                if (DOES_HAVE_QUERY(base)) {
1093
                  SET_HAVE_QUERY()
1094
                  url.query = base.query;
1095
                }
1096
                state = kFragment;
1097
              }
1098
              break;
1099
            default:
1100

13
              if (base_is_file &&
1101

13
                  (!WINDOWS_DRIVE_LETTER(ch, p[1]) ||
1102
6
                   end - p == 1 ||
1103
3
                   (p[2] != '/' &&
1104
                    p[2] != '\\' &&
1105
                    p[2] != '?' &&
1106
                    p[2] != '#'))) {
1107
2
                if (DOES_HAVE_HOST(base)) {
1108
                  SET_HAVE_HOST()
1109
                  url.host = base.host;
1110
                }
1111
2
                if (DOES_HAVE_PATH(base)) {
1112
2
                  SET_HAVE_PATH()
1113
2
                  url.path = base.path;
1114
                }
1115
2
                if (!url.path.empty())
1116
2
                  url.path.pop_back();
1117
              }
1118
              state = kPath;
1119
              continue;
1120
          }
1121
          break;
1122
        case kFileSlash:
1123
37
          if (ch == '/' || ch == '\\') {
1124
            state = kFileHost;
1125
          } else {
1126

6
            if (has_base &&
1127
6
                DOES_HAVE_SCHEME(base) &&
1128
4
                base.scheme == "file:" &&
1129
2
                DOES_HAVE_PATH(base) &&
1130

5
                base.path.size() > 0 &&
1131


1
                NORMALIZED_WINDOWS_DRIVE_LETTER(base.path[0])) {
1132
              SET_HAVE_PATH()
1133
              url.path.push_back(base.path[0]);
1134
            }
1135
            state = kPath;
1136
            continue;
1137
          }
1138
          break;
1139
        case kFileHost:
1140
170
          if (ch == kEOL ||
1141
85
              ch == '/' ||
1142
52
              ch == '\\' ||
1143
103
              ch == '?' ||
1144
              ch == '#') {
1145

69
            if (buffer.size() == 2 &&
1146


4
                WINDOWS_DRIVE_LETTER(buffer[0], buffer[1])) {
1147
              state = kPath;
1148
33
            } else if (buffer.size() == 0) {
1149
              state = kPathStart;
1150
            } else {
1151
7
              if (buffer != "localhost") {
1152
4
                SET_HAVE_HOST()
1153
4
                if (ParseHost(&buffer, &url.host) < 0)
1154
                  FAILED()
1155
              }
1156
7
              buffer.clear();
1157
7
              state = kPathStart;
1158
            }
1159
            continue;
1160
          } else {
1161
51
            buffer += ch;
1162
          }
1163
          break;
1164
        case kPathStart:
1165
465
          state = kPath;
1166
465
          if (ch != '/' && !special_back_slash)
1167
            continue;
1168
          break;
1169
        case kPath:
1170
6242
          if (ch == kEOL ||
1171
5443
              ch == '/' ||
1172
2305
              special_back_slash ||
1173
2124
              (!state_override && (ch == '?' || ch == '#'))) {
1174
1690
            if (IsDoubleDotSegment(buffer)) {
1175
36
              if (!url.path.empty())
1176
18
                url.path.pop_back();
1177
36
              if (ch != '/' && !special_back_slash) {
1178
7
                SET_HAVE_PATH()
1179
28
                url.path.push_back("");
1180
              }
1181
1618
            } else if (IsSingleDotSegment(buffer)) {
1182
10
              if (ch != '/' && !special_back_slash) {
1183
3
                SET_HAVE_PATH();
1184
12
                url.path.push_back("");
1185
              }
1186
            } else {
1187

2355
              if (DOES_HAVE_SCHEME(url) &&
1188
860
                  url.scheme == "file:" &&
1189
145
                  url.path.empty() &&
1190

846
                  buffer.size() == 2 &&
1191


23
                  WINDOWS_DRIVE_LETTER(buffer[0], buffer[1])) {
1192
5
                url.flags &= ~URL_FLAGS_HAS_HOST;
1193
5
                buffer[1] = ':';
1194
              }
1195
799
              SET_HAVE_PATH()
1196
3995
              std::string segment(buffer.c_str(), buffer.size());
1197
799
              url.path.push_back(segment);
1198
            }
1199
845
            buffer.clear();
1200
845
            if (ch == '?') {
1201
23
              SET_HAVE_QUERY()
1202
23
              state = kQuery;
1203
822
            } else if (ch == '#') {
1204
6
              state = kFragment;
1205
            }
1206
          } else {
1207


2276
            if (ch == '%' && p[1] == '2' && TO_LOWER(p[2]) == 'e') {
1208
15
              buffer += '.';
1209
15
              p += 2;
1210
            } else {
1211
2261
              AppendOrEscape(&buffer, ch, DefaultEncodeSet);
1212
            }
1213
          }
1214
          break;
1215
        case kCannotBeBase:
1216
1177
          switch (ch) {
1217
            case '?':
1218
              state = kQuery;
1219
              break;
1220
            case '#':
1221
2
              state = kFragment;
1222
2
              break;
1223
            default:
1224
2348
              if (url.path.size() == 0)
1225
                url.path.push_back("");
1226

2348
              if (url.path.size() > 0 && ch != kEOL)
1227
1011
                AppendOrEscape(&url.path[0], ch, SimpleEncodeSet);
1228
          }
1229
          break;
1230
        case kQuery:
1231

520
          if (ch == kEOL || (!state_override && ch == '#')) {
1232
46
            SET_HAVE_QUERY()
1233
46
            url.query = buffer;
1234
46
            buffer.clear();
1235
46
            if (ch == '#')
1236
14
              state = kFragment;
1237
          } else {
1238
474
            AppendOrEscape(&buffer, ch, QueryEncodeSet);
1239
          }
1240
          break;
1241
        case kFragment:
1242
196
          switch (ch) {
1243
            case kEOL:
1244
43
              SET_HAVE_FRAGMENT()
1245
              url.fragment = buffer;
1246
              break;
1247
            case 0:
1248
              break;
1249
            default:
1250
152
              buffer += ch;
1251
          }
1252
          break;
1253
        default:
1254
          INVALID_PARSE_STATE()
1255
          goto done;
1256
      }
1257
1258
18047
      p++;
1259
    }
1260
1261
   done:
1262
1263
    // Define the return value placeholders
1264
1692
    const Local<Value> undef = Undefined(isolate);
1265
    Local<Value> argv[9] = {
1266
      undef,
1267
      undef,
1268
      undef,
1269
      undef,
1270
      undef,
1271
      undef,
1272
      undef,
1273
      undef,
1274
      undef,
1275
846
    };
1276
1277
1692
    argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url.flags);
1278
846
    if (!IS_FAILED(url.flags)) {
1279
793
      if (DOES_HAVE_SCHEME(url))
1280
1424
        argv[ARG_PROTOCOL] = OneByteString(isolate, url.scheme.c_str());
1281
793
      if (DOES_HAVE_USERNAME(url))
1282
93
        argv[ARG_USERNAME] = UTF8STRING(isolate, url.username);
1283
793
      if (DOES_HAVE_PASSWORD(url))
1284
84
        argv[ARG_PASSWORD] = UTF8STRING(isolate, url.password);
1285
793
      if (DOES_HAVE_HOST(url))
1286
1461
        argv[ARG_HOST] = UTF8STRING(isolate, url.host);
1287
793
      if (DOES_HAVE_QUERY(url))
1288
141
        argv[ARG_QUERY] = UTF8STRING(isolate, url.query);
1289
793
      if (DOES_HAVE_FRAGMENT(url))
1290
129
        argv[ARG_FRAGMENT] = UTF8STRING(isolate, url.fragment);
1291
793
      if (url.port > -1)
1292
140
        argv[ARG_PORT] = Integer::New(isolate, url.port);
1293
793
      if (DOES_HAVE_PATH(url))
1294
1480
        argv[ARG_PATH] = Copy(isolate, url.path);
1295
    }
1296
1297
846
    cb->Call(context, recv, 9, argv);
1298
846
  }
1299
1300
846
  static void Parse(const FunctionCallbackInfo<Value>& args) {
1301
846
    Environment* env = Environment::GetCurrent(args);
1302
846
    CHECK_GE(args.Length(), 5);
1303
1692
    CHECK(args[0]->IsString());
1304


3665
    CHECK(args[2]->IsUndefined() ||
1305
          args[2]->IsNull() ||
1306
          args[2]->IsObject());
1307


2811
    CHECK(args[3]->IsUndefined() ||
1308
          args[3]->IsNull() ||
1309
          args[3]->IsObject());
1310
846
    CHECK(args[4]->IsFunction());
1311
1692
    Utf8Value input(env->isolate(), args[0]);
1312
846
    enum url_parse_state override = kUnknownState;
1313
846
    if (args[1]->IsNumber())
1314
846
      override = (enum url_parse_state)(args[1]->Uint32Value());
1315
1316
7614
    Parse(env, args.This(),
1317
846
          *input, input.length(),
1318
          override,
1319
          args[2].As<Object>(),
1320
          args[3].As<Object>(),
1321
846
          args[4].As<Function>());
1322
846
  }
1323
1324
8
  static void EncodeAuthSet(const FunctionCallbackInfo<Value>& args) {
1325
8
    Environment* env = Environment::GetCurrent(args);
1326
8
    CHECK_GE(args.Length(), 1);
1327
16
    CHECK(args[0]->IsString());
1328
16
    Utf8Value value(env->isolate(), args[0]);
1329
16
    std::string output;
1330
8
    const size_t len = value.length();
1331
8
    output.reserve(len);
1332
150
    for (size_t n = 0; n < len; n++) {
1333
142
      const char ch = (*value)[n];
1334
142
      AppendOrEscape(&output, ch, UserinfoEncodeSet);
1335
    }
1336
16
    args.GetReturnValue().Set(
1337
16
        String::NewFromUtf8(env->isolate(),
1338
                            output.c_str(),
1339
16
                            v8::NewStringType::kNormal).ToLocalChecked());
1340
8
  }
1341
1342
  static void DomainToASCII(const FunctionCallbackInfo<Value>& args) {
1343
    Environment* env = Environment::GetCurrent(args);
1344
    CHECK_GE(args.Length(), 1);
1345
    CHECK(args[0]->IsString());
1346
    Utf8Value value(env->isolate(), args[0]);
1347
1348
    url_host host{{""}, HOST_TYPE_DOMAIN};
1349
    ParseHost(&host, *value, value.length());
1350
    if (host.type == HOST_TYPE_FAILED) {
1351
      args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
1352
      return;
1353
    }
1354
    std::string out;
1355
    WriteHost(&host, &out);
1356
    args.GetReturnValue().Set(
1357
        String::NewFromUtf8(env->isolate(),
1358
                            out.c_str(),
1359
                            v8::NewStringType::kNormal).ToLocalChecked());
1360
  }
1361
1362
  static void DomainToUnicode(const FunctionCallbackInfo<Value>& args) {
1363
    Environment* env = Environment::GetCurrent(args);
1364
    CHECK_GE(args.Length(), 1);
1365
    CHECK(args[0]->IsString());
1366
    Utf8Value value(env->isolate(), args[0]);
1367
1368
    url_host host{{""}, HOST_TYPE_DOMAIN};
1369
    ParseHost(&host, *value, value.length(), true);
1370
    if (host.type == HOST_TYPE_FAILED) {
1371
      args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
1372
      return;
1373
    }
1374
    std::string out;
1375
    WriteHost(&host, &out);
1376
    args.GetReturnValue().Set(
1377
        String::NewFromUtf8(env->isolate(),
1378
                            out.c_str(),
1379
                            v8::NewStringType::kNormal).ToLocalChecked());
1380
  }
1381
1382
387
  static void Init(Local<Object> target,
1383
                   Local<Value> unused,
1384
                   Local<Context> context,
1385
                   void* priv) {
1386
387
    Environment* env = Environment::GetCurrent(context);
1387
387
    env->SetMethod(target, "parse", Parse);
1388
387
    env->SetMethod(target, "encodeAuth", EncodeAuthSet);
1389
387
    env->SetMethod(target, "domainToASCII", DomainToASCII);
1390
387
    env->SetMethod(target, "domainToUnicode", DomainToUnicode);
1391
1392
#define XX(name, _) NODE_DEFINE_CONSTANT(target, name);
1393
15480
    FLAGS(XX)
1394
#undef XX
1395
1396
#define XX(name) NODE_DEFINE_CONSTANT(target, name);
1397
10836
    ARGS(XX)
1398
24768
    PARSESTATES(XX)
1399
#undef XX
1400
387
  }
1401
}  // namespace url
1402
}  // namespace node
1403
1404
1705
NODE_MODULE_CONTEXT_AWARE_BUILTIN(url, node::url::Init)