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: 674 754 89.4 %
Date: 2016-12-18 Branches: 598 854 70.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
181
  static int ToUnicode(std::string* input, std::string* output) {
100
362
    MaybeStackBuffer<char> buf;
101
362
    if (i18n::ToUnicode(&buf, input->c_str(), input->length()) < 0)
102
      return -1;
103
362
    output->assign(*buf, buf.length());
104
181
    return 0;
105
  }
106
107
1168
  static int ToASCII(std::string* input, std::string* output) {
108
2336
    MaybeStackBuffer<char> buf;
109
2336
    if (i18n::ToASCII(&buf, input->c_str(), input->length()) < 0)
110
      return -1;
111
2336
    output->assign(*buf, buf.length());
112
1168
    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
1170
  static int IsValidUTF8(std::string* input) {
119
1170
    const char* p = input->c_str();
120
1170
    int32_t len = input->length();
121
1170
    for (int32_t i = 0; i < len;) {
122
      UChar32 c;
123

11682
      U8_NEXT_UNSAFE(p, i, c);
124



11682
      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
15
  static url_host_type ParseIPv6Host(url_host* host,
147
                                     const char* input,
148
                                     size_t length) {
149
15
    url_host_type type = HOST_TYPE_FAILED;
150
135
    for (unsigned n = 0; n < 8; n++)
151
120
      host->value.ipv6[n] = 0;
152
15
    uint16_t* piece_pointer = &host->value.ipv6[0];
153
15
    uint16_t* last_piece = piece_pointer + 8;
154
15
    uint16_t* compress_pointer = nullptr;
155
15
    const char* pointer = input;
156
15
    const char* end = pointer + length;
157
    unsigned value, len, swaps, dots;
158
15
    char ch = pointer < end ? pointer[0] : kEOL;
159
15
    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
45
    while (ch != kEOL) {
168
33
      if (piece_pointer > last_piece)
169
        goto end;
170
33
      if (ch == ':') {
171
9
        if (compress_pointer != nullptr)
172
          goto end;
173
9
        pointer++;
174
9
        ch = pointer < end ? pointer[0] : kEOL;
175
9
        piece_pointer++;
176
9
        compress_pointer = piece_pointer;
177
9
        continue;
178
      }
179
      value = 0;
180
      len = 0;
181

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

24
      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
11
          pointer++;
230
11
          ch = pointer < end ? pointer[0] : kEOL;
231
11
          if (ch == kEOL)
232
            goto end;
233
          break;
234
        case kEOL:
235
          break;
236
        default:
237
          goto end;
238
      }
239
22
      *piece_pointer = value;
240
22
      piece_pointer++;
241
    }
242
243
12
    if (compress_pointer != nullptr) {
244
11
      swaps = piece_pointer - compress_pointer;
245
11
      piece_pointer = last_piece - 1;
246

37
      while (piece_pointer != &host->value.ipv6[0] && swaps > 0) {
247
13
        uint16_t temp = *piece_pointer;
248
13
        uint16_t* swap_piece = compress_pointer + swaps - 1;
249
13
        *piece_pointer = *swap_piece;
250
13
        *swap_piece = temp;
251
13
         piece_pointer--;
252
13
         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
15
    host->type = type;
261
15
    return type;
262
  }
263
264
1208
  static inline int ParseNumber(const char* start, const char* end) {
265
1208
    unsigned R = 10;
266

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

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

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

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

1208
        if (++parts > 4 || pointer - mark == 0)
317
          break;
318
1208
        int n = ParseNumber(mark, pointer);
319
1208
        if (n < 0) {
320
          type = HOST_TYPE_DOMAIN;
321
          goto end;
322
        }
323
82
        if (pointer - mark == 10) {
324
5
          numbers[parts - 1] = n;
325
          break;
326
        }
327
77
        if (n > 255) {
328
          type = HOST_TYPE_FAILED;
329
          goto end;
330
        }
331
73
        numbers[parts - 1] = n;
332
73
        mark = pointer + 1;
333
73
        if (ch == '.' && remaining == 0)
334
          break;
335
      }
336
6851
      pointer++;
337
    }
338
339
24
    type = HOST_TYPE_IPV4;
340
24
    if (parts > 0) {
341
24
      val = numbers[parts - 1];
342
75
      for (int n = 0; n < parts - 1; n++) {
343
51
        double b = 3-n;
344
51
        val += numbers[n] * pow(256, b);
345
      }
346
    }
347
348
24
    host->value.ipv4 = val;
349
   end:
350
1154
    host->type = type;
351
1154
    return type;
352
  }
353
354
1185
  static url_host_type ParseHost(url_host* host,
355
                                 const char* input,
356
                                 size_t length,
357
                                 bool unicode = false) {
358
1185
    url_host_type type = HOST_TYPE_FAILED;
359
1185
    const char* pointer = input;
360
2370
    std::string decoded;
361
362
1185
    if (length == 0)
363
      goto end;
364
365
1185
    if (pointer[0] == '[') {
366
15
      if (pointer[length - 1] != ']')
367
        goto end;
368
15
      return ParseIPv6Host(host, ++pointer, length - 2);
369
    }
370
371
    // First, we have to percent decode
372
1170
    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
1170
    if (IsValidUTF8(&decoded) < 0)
379
      goto end;
380
381
    // Then we have to punycode toASCII
382
1168
    if (ToASCII(&decoded, &decoded) < 0)
383
      goto end;
384
385
    // If any of the following characters are still present, we have to fail
386
24528
    for (size_t n = 0; n < decoded.size(); n++) {
387
11694
      const char ch = decoded[n];
388

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

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

11682
          ch == '?' || ch == '@' || ch == '[' || ch == '\\' ||
391
          ch == ']') {
392
        goto end;
393
      }
394
    }
395
396
    // Check to see if it's an IPv4 IP address
397
2308
    type = ParseIPv4Host(host, decoded.c_str(), decoded.length());
398
1154
    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

1126
    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
1126
    type = HOST_TYPE_DOMAIN;
407
1126
    host->value.domain = decoded;
408
409
   end:
410
1170
    host->type = type;
411
1170
    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
11
    uint16_t* start = values;
419
11
    uint16_t* end = start + len;
420
11
    uint16_t* result = nullptr;
421
422
11
    uint16_t* current = nullptr;
423
11
    unsigned counter = 0, longest = 1;
424
425
99
    while (start < end) {
426
88
      if (*start == 0) {
427
68
        if (current == nullptr)
428
11
          current = start;
429
68
        counter++;
430
      } else {
431
20
        if (counter > longest) {
432
11
          longest = counter;
433
11
          result = current;
434
        }
435
        counter = 0;
436
        current = nullptr;
437
      }
438
88
      start++;
439
    }
440
11
    if (counter > longest)
441
      result = current;
442
    return result;
443
  }
444
445
1161
  static url_host_type WriteHost(url_host* host, std::string* dest) {
446
1161
    dest->clear();
447

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

147
            while (*piece == 0 && ++n < 8)
476
68
              piece = &host->value.ipv6[n];
477
11
            if (n == 8)
478
              break;
479
          }
480
          char buf[5];
481
20
          char* buffer = buf;
482
40
          snprintf(buffer, sizeof(buf), "%x", *piece);
483
20
          *dest += buf;
484
20
          if (n < 7)
485
            *dest += ':';
486
        }
487
        *dest += ']';
488
        break;
489
      }
490
      case HOST_TYPE_FAILED:
491
        break;
492
    }
493
1161
    return host->type;
494
  }
495
496
1017
  static int ParseHost(std::string* input,
497
                       std::string* output,
498
                       bool unicode = false) {
499
1017
    if (input->length() == 0)
500
      return 0;
501
1980
    url_host host{{""}, HOST_TYPE_DOMAIN};
502
1980
    ParseHost(&host, input->c_str(), input->length(), unicode);
503
990
    if (host.type == HOST_TYPE_FAILED)
504
      return -1;
505
966
    WriteHost(&host, output);
506
966
    return 0;
507
  }
508
509
819
  static inline void Copy(Isolate* isolate,
510
                          Local<Array> ary,
511
                          std::vector<std::string>* vec) {
512
819
    const int32_t len = ary->Length();
513
819
    if (len == 0)
514
      return;  // nothing to copy
515
819
    vec->reserve(len);
516
2001
    for (int32_t n = 0; n < len; n++) {
517
1182
      Local<Value> val = ary->Get(n);
518
2364
      if (val->IsString()) {
519
3546
        Utf8Value value(isolate, val.As<String>());
520
7092
        vec->push_back(std::string(*value, value.length()));
521
      }
522
    }
523
  }
524
525
1774
  static inline Local<Array> Copy(Isolate* isolate,
526
                                  std::vector<std::string> vec) {
527
3548
    Local<Array> ary = Array::New(isolate, vec.size());
528
8548
    for (size_t n = 0; n < vec.size(); n++)
529
12500
      ary->Set(n, UTF8STRING(isolate, vec[n]));
530
1774
    return ary;
531
  }
532
533
819
  static inline void HarvestBase(Environment* env,
534
                                 struct url_data* base,
535
                                 Local<Object> base_obj) {
536
3276
    Local<Value> flags = GET(env, base_obj, "flags");
537
819
    if (flags->IsInt32())
538
819
      base->flags = flags->Int32Value();
539
540
7371
    GET_AND_SET(env, base_obj, scheme, base, URL_FLAGS_HAS_SCHEME);
541
4131
    GET_AND_SET(env, base_obj, username, base, URL_FLAGS_HAS_USERNAME);
542
4119
    GET_AND_SET(env, base_obj, password, base, URL_FLAGS_HAS_PASSWORD);
543
5663
    GET_AND_SET(env, base_obj, host, base, URL_FLAGS_HAS_HOST);
544
4107
    GET_AND_SET(env, base_obj, query, base, URL_FLAGS_HAS_QUERY);
545
4095
    GET_AND_SET(env, base_obj, fragment, base, URL_FLAGS_HAS_FRAGMENT);
546
3276
    Local<Value> port = GET(env, base_obj, "port");
547
819
    if (port->IsInt32())
548
6
      base->port = port->Int32Value();
549
3276
    Local<Value> path = GET(env, base_obj, "path");
550
819
    if (path->IsArray()) {
551
819
      base->flags |= URL_FLAGS_HAS_PATH;
552
1638
      Copy(env->isolate(), path.As<Array>(), &(base->path));
553
    }
554
819
  }
555
556
83
  static inline void HarvestContext(Environment* env,
557
                                    struct url_data* context,
558
                                    Local<Object> context_obj) {
559
332
    Local<Value> flags = GET(env, context_obj, "flags");
560
83
    if (flags->IsInt32()) {
561
83
      int32_t _flags = flags->Int32Value();
562
83
      if (_flags & URL_FLAGS_SPECIAL)
563
61
        context->flags |= URL_FLAGS_SPECIAL;
564
83
      if (_flags & URL_FLAGS_CANNOT_BE_BASE)
565
2
        context->flags |= URL_FLAGS_CANNOT_BE_BASE;
566
    }
567
332
    Local<Value> scheme = GET(env, context_obj, "scheme");
568
166
    if (scheme->IsString()) {
569
166
      Utf8Value value(env->isolate(), scheme);
570
166
      context->scheme.assign(*value, value.length());
571
    }
572
332
    Local<Value> port = GET(env, context_obj, "port");
573
83
    if (port->IsInt32())
574
8
      context->port = port->Int32Value();
575
83
  }
576
577
  // Single dot segment can be ".", "%2e", or "%2E"
578
1947
  static inline bool IsSingleDotSegment(std::string str) {
579
1947
    switch (str.size()) {
580
      case 1:
581
124
        return str == ".";
582
      case 3:
583
732
        return str[0] == '%' &&
584

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


24
               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
2048
  static inline bool IsDoubleDotSegment(std::string str) {
595

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

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




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


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


19
                str[4] == '2' &&
615
                TO_LOWER(str[5]) == 'e');
616
      default:
617
        return false;
618
    }
619
  }
620
621
1872
  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
1872
    Isolate* isolate = env->isolate();
630
1872
    Local<Context> context = env->context();
631
3744
    HandleScope handle_scope(isolate);
632
3744
    Context::Scope context_scope(context);
633
634
1872
    const bool has_base = base_obj->IsObject();
635
1872
    bool atflag = false;
636
1872
    bool sbflag = false;
637
1872
    bool uflag = false;
638
1872
    bool base_is_file = false;
639
1872
    int wskip = 0;
640
641
3744
    struct url_data base;
642
3744
    struct url_data url;
643
1872
    if (context_obj->IsObject())
644
83
      HarvestContext(env, &url, context_obj);
645
1872
    if (has_base)
646
819
      HarvestBase(env, &base, base_obj);
647
648
3744
    std::string buffer;
649
1872
    url.scheme.reserve(len);
650
1872
    url.username.reserve(len);
651
1872
    url.password.reserve(len);
652
1872
    url.host.reserve(len);
653
1872
    url.path.reserve(len);
654
1872
    url.query.reserve(len);
655
1872
    url.fragment.reserve(len);
656
1872
    buffer.reserve(len);
657
658
    // Set the initial parse state.
659
1872
    const bool state_override = override != kUnknownState;
660
1872
    enum url_parse_state state = state_override ? override : kSchemeStart;
661
662
1872
    const char* p = input;
663
1872
    const char* end = input + len;
664
665
1872
    if (state < kSchemeStart || state > kFragment) {
666
      INVALID_PARSE_STATE();
667
      goto done;
668
    }
669
670
49582
    while (p <= end) {
671
47792
      const char ch = p < end ? p[0] : kEOL;
672
673
47792
      if (TAB_AND_NEWLINE(ch)) {
674
108
        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
27
          wskip++;
680
        }
681
108
        p++;
682
108
        continue;
683
      }
684
685
47684
      bool special = url.flags & URL_FLAGS_SPECIAL;
686
47684
      const bool special_back_slash = (special && ch == '\\');
687





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


6850
          if (SCHEME_CHAR(ch)) {
701
10500
            buffer += TO_LOWER(ch);
702
5250
            p++;
703
5250
            continue;
704

1600
          } else if (ch == ':' || (state_override && ch == kEOL)) {
705
1577
            buffer += ':';
706
1577
            if (buffer.size() > 0) {
707
1577
              SET_HAVE_SCHEME()
708
              url.scheme = buffer;
709
            }
710
3154
            if (IsSpecial(url.scheme)) {
711
984
              SPECIAL()
712
            } else {
713
593
              url.flags &= ~URL_FLAGS_SPECIAL;
714
            }
715
1577
            if (state_override)
716
              goto done;
717
1569
            buffer.clear();
718
1569
            if (url.scheme == "file:") {
719
              state = kFile;
720
3002
            } else if (special &&
721
460
                       has_base &&
722

1961
                       DOES_HAVE_SCHEME(base) &&
723
460
                       url.scheme == base.scheme) {
724
              state = kSpecialRelativeOrAuthority;
725
1366
            } else if (special) {
726
              state = kSpecialAuthoritySlashes;
727
588
            } else if (p[1] == '/') {
728
159
              state = kPathOrAuthority;
729
159
              p++;
730
            } else {
731
429
              CANNOT_BE_BASE()
732
429
              SET_HAVE_PATH()
733
1716
              url.path.push_back("");
734
429
              state = kCannotBeBase;
735
            }
736
23
          } else if (!state_override) {
737
21
            buffer.clear();
738
21
            state = kNoScheme;
739
21
            p = input;
740
21
            continue;
741
          } else {
742
2
            TERMINATE()
743
          }
744
          break;
745
        case kNoScheme:
746

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

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

382
          } else if (has_base &&
771

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

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

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

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

227
            if (blen > 0 && buffer[0] != ':') {
951
82
              SET_HAVE_USERNAME()
952
            }
953
1142
            for (size_t n = 0; n < blen; n++) {
954
508
              const char bch = buffer[n];
955
508
              if (bch == ':') {
956
77
                SET_HAVE_PASSWORD()
957
77
                if (!uflag) {
958
                  uflag = true;
959
                  continue;
960
                }
961
              }
962
434
              if (uflag) {
963
180
                AppendOrEscape(&url.password, bch, UserinfoEncodeSet);
964
              } else {
965
254
                AppendOrEscape(&url.username, bch, UserinfoEncodeSet);
966
              }
967
            }
968
            buffer.clear();
969
23678
          } else if (ch == kEOL ||
970
11839
                     ch == '/' ||
971
21748
                     ch == '?' ||
972
21736
                     ch == '#' ||
973
                     special_back_slash) {
974
989
            p -= buffer.size() + 1 + wskip;
975
989
            buffer.clear();
976
989
            state = kHost;
977
          } else {
978
10850
            buffer += ch;
979
          }
980
          break;
981
        case kHost:
982
        case kHostname:
983
11154
          if (ch == ':' && !sbflag) {
984

157
            if (special && buffer.size() == 0)
985
3
              FAILED()
986
154
            SET_HAVE_HOST()
987
154
            if (ParseHost(&buffer, &url.host) < 0)
988
3
              FAILED()
989
151
            buffer.clear();
990
151
            state = kPort;
991
151
            if (override == kHostname)
992
2
              TERMINATE()
993
21994
          } else if (ch == kEOL ||
994
10997
                     ch == '/' ||
995
20322
                     ch == '?' ||
996
20306
                     ch == '#' ||
997
                     special_back_slash) {
998
866
            p--;
999

866
            if (special && buffer.size() == 0)
1000
11
              FAILED()
1001
855
            SET_HAVE_HOST()
1002
855
            if (ParseHost(&buffer, &url.host) < 0)
1003
21
              FAILED()
1004
834
            buffer.clear();
1005
834
            state = kPathStart;
1006
834
            if (state_override)
1007
14
              TERMINATE()
1008
          } else {
1009
10131
            if (ch == '[')
1010
13
              sbflag = true;
1011
10131
            if (ch == ']')
1012
13
              sbflag = false;
1013
10131
            buffer += TO_LOWER(ch);
1014
          }
1015
          break;
1016
        case kPort:
1017
684
          if (ASCII_DIGIT(ch)) {
1018
522
            buffer += ch;
1019
162
          } else if (state_override ||
1020
162
                     ch == kEOL ||
1021
238
                     ch == '/' ||
1022
119
                     ch == '?' ||
1023
6
                     ch == '#' ||
1024
                     special_back_slash) {
1025
156
            if (buffer.size() > 0) {
1026
              int port = 0;
1027
1191
              for (size_t i = 0; i < buffer.size(); i++)
1028
1042
                port = port * 10 + buffer[i] - '0';
1029
149
              if (port >= 0 && port <= 0xffff) {
1030
292
                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
48
              has_base &&
1045

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

88
          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

26
              if (base_is_file &&
1101

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

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

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


2
                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
336
          if (ch == kEOL ||
1141
168
              ch == '/' ||
1142
104
              ch == '\\' ||
1143
206
              ch == '?' ||
1144
              ch == '#') {
1145

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


8
                WINDOWS_DRIVE_LETTER(buffer[0], buffer[1])) {
1147
              state = kPath;
1148
64
            } else if (buffer.size() == 0) {
1149
              state = kPathStart;
1150
            } else {
1151
14
              if (buffer != "localhost") {
1152
8
                SET_HAVE_HOST()
1153
8
                if (ParseHost(&buffer, &url.host) < 0)
1154
                  FAILED()
1155
              }
1156
14
              buffer.clear();
1157
14
              state = kPathStart;
1158
            }
1159
            continue;
1160
          } else {
1161
102
            buffer += ch;
1162
          }
1163
          break;
1164
        case kPathStart:
1165
1046
          state = kPath;
1166
1046
          if (ch != '/' && !special_back_slash)
1167
            continue;
1168
          break;
1169
        case kPath:
1170
15084
          if (ch == kEOL ||
1171
13129
              ch == '/' ||
1172
5549
              special_back_slash ||
1173
5368
              (!state_override && (ch == '?' || ch == '#'))) {
1174
4096
            if (IsDoubleDotSegment(buffer)) {
1175
101
              if (!url.path.empty())
1176
50
                url.path.pop_back();
1177
101
              if (ch != '/' && !special_back_slash) {
1178
20
                SET_HAVE_PATH()
1179
80
                url.path.push_back("");
1180
              }
1181
3894
            } else if (IsSingleDotSegment(buffer)) {
1182
28
              if (ch != '/' && !special_back_slash) {
1183
9
                SET_HAVE_PATH();
1184
36
                url.path.push_back("");
1185
              }
1186
            } else {
1187

5715
              if (DOES_HAVE_SCHEME(url) &&
1188
2077
                  url.scheme == "file:" &&
1189
282
                  url.path.empty() &&
1190

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


46
                  WINDOWS_DRIVE_LETTER(buffer[0], buffer[1])) {
1192
10
                url.flags &= ~URL_FLAGS_HAS_HOST;
1193
10
                buffer[1] = ':';
1194
              }
1195
1919
              SET_HAVE_PATH()
1196
9595
              std::string segment(buffer.c_str(), buffer.size());
1197
1919
              url.path.push_back(segment);
1198
            }
1199
2048
            buffer.clear();
1200
2048
            if (ch == '?') {
1201
45
              SET_HAVE_QUERY()
1202
45
              state = kQuery;
1203
2003
            } else if (ch == '#') {
1204
10
              state = kFragment;
1205
            }
1206
          } else {
1207


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

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

556
          if (ch == kEOL || (!state_override && ch == '#')) {
1232
70
            SET_HAVE_QUERY()
1233
70
            url.query = buffer;
1234
70
            buffer.clear();
1235
70
            if (ch == '#')
1236
26
              state = kFragment;
1237
          } else {
1238
486
            AppendOrEscape(&buffer, ch, QueryEncodeSet);
1239
          }
1240
          break;
1241
        case kFragment:
1242
328
          switch (ch) {
1243
            case kEOL:
1244
93
              SET_HAVE_FRAGMENT()
1245
              url.fragment = buffer;
1246
              break;
1247
            case 0:
1248
              break;
1249
            default:
1250
234
              buffer += ch;
1251
          }
1252
          break;
1253
        default:
1254
          INVALID_PARSE_STATE()
1255
          goto done;
1256
      }
1257
1258
40255
      p++;
1259
    }
1260
1261
   done:
1262
1263
    // Define the return value placeholders
1264
3744
    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
1872
    };
1276
1277
3744
    argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url.flags);
1278
1872
    if (!IS_FAILED(url.flags)) {
1279
1819
      if (DOES_HAVE_SCHEME(url))
1280
3492
        argv[ARG_PROTOCOL] = OneByteString(isolate, url.scheme.c_str());
1281
1819
      if (DOES_HAVE_USERNAME(url))
1282
240
        argv[ARG_USERNAME] = UTF8STRING(isolate, url.username);
1283
1819
      if (DOES_HAVE_PASSWORD(url))
1284
222
        argv[ARG_PASSWORD] = UTF8STRING(isolate, url.password);
1285
1819
      if (DOES_HAVE_HOST(url))
1286
3453
        argv[ARG_HOST] = UTF8STRING(isolate, url.host);
1287
1819
      if (DOES_HAVE_QUERY(url))
1288
219
        argv[ARG_QUERY] = UTF8STRING(isolate, url.query);
1289
1819
      if (DOES_HAVE_FRAGMENT(url))
1290
279
        argv[ARG_FRAGMENT] = UTF8STRING(isolate, url.fragment);
1291
1819
      if (url.port > -1)
1292
260
        argv[ARG_PORT] = Integer::New(isolate, url.port);
1293
1819
      if (DOES_HAVE_PATH(url))
1294
3548
        argv[ARG_PATH] = Copy(isolate, url.path);
1295
    }
1296
1297
1872
    cb->Call(context, recv, 9, argv);
1298
1872
  }
1299
1300
1872
  static void Parse(const FunctionCallbackInfo<Value>& args) {
1301
1872
    Environment* env = Environment::GetCurrent(args);
1302
1872
    CHECK_GE(args.Length(), 5);
1303
3744
    CHECK(args[0]->IsString());
1304


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


5865
    CHECK(args[3]->IsUndefined() ||
1308
          args[3]->IsNull() ||
1309
          args[3]->IsObject());
1310
1872
    CHECK(args[4]->IsFunction());
1311
3744
    Utf8Value input(env->isolate(), args[0]);
1312
1872
    enum url_parse_state override = kUnknownState;
1313
1872
    if (args[1]->IsNumber())
1314
1872
      override = (enum url_parse_state)(args[1]->Uint32Value());
1315
1316
16848
    Parse(env, args.This(),
1317
1872
          *input, input.length(),
1318
          override,
1319
          args[2].As<Object>(),
1320
          args[3].As<Object>(),
1321
1872
          args[4].As<Function>());
1322
1872
  }
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
7
  static void DomainToASCII(const FunctionCallbackInfo<Value>& args) {
1343
7
    Environment* env = Environment::GetCurrent(args);
1344
7
    CHECK_GE(args.Length(), 1);
1345
14
    CHECK(args[0]->IsString());
1346
14
    Utf8Value value(env->isolate(), args[0]);
1347
1348
21
    url_host host{{""}, HOST_TYPE_DOMAIN};
1349
7
    ParseHost(&host, *value, value.length());
1350
7
    if (host.type == HOST_TYPE_FAILED) {
1351
      args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
1352
      return;
1353
    }
1354
14
    std::string out;
1355
7
    WriteHost(&host, &out);
1356
14
    args.GetReturnValue().Set(
1357
14
        String::NewFromUtf8(env->isolate(),
1358
                            out.c_str(),
1359
14
                            v8::NewStringType::kNormal).ToLocalChecked());
1360
  }
1361
1362
188
  static void DomainToUnicode(const FunctionCallbackInfo<Value>& args) {
1363
188
    Environment* env = Environment::GetCurrent(args);
1364
188
    CHECK_GE(args.Length(), 1);
1365
376
    CHECK(args[0]->IsString());
1366
376
    Utf8Value value(env->isolate(), args[0]);
1367
1368
564
    url_host host{{""}, HOST_TYPE_DOMAIN};
1369
188
    ParseHost(&host, *value, value.length(), true);
1370
188
    if (host.type == HOST_TYPE_FAILED) {
1371
      args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
1372
      return;
1373
    }
1374
376
    std::string out;
1375
188
    WriteHost(&host, &out);
1376
376
    args.GetReturnValue().Set(
1377
376
        String::NewFromUtf8(env->isolate(),
1378
                            out.c_str(),
1379
376
                            v8::NewStringType::kNormal).ToLocalChecked());
1380
  }
1381
1382
402
  static void Init(Local<Object> target,
1383
                   Local<Value> unused,
1384
                   Local<Context> context,
1385
                   void* priv) {
1386
402
    Environment* env = Environment::GetCurrent(context);
1387
402
    env->SetMethod(target, "parse", Parse);
1388
402
    env->SetMethod(target, "encodeAuth", EncodeAuthSet);
1389
402
    env->SetMethod(target, "domainToASCII", DomainToASCII);
1390
402
    env->SetMethod(target, "domainToUnicode", DomainToUnicode);
1391
1392
#define XX(name, _) NODE_DEFINE_CONSTANT(target, name);
1393
16080
    FLAGS(XX)
1394
#undef XX
1395
1396
#define XX(name) NODE_DEFINE_CONSTANT(target, name);
1397
11256
    ARGS(XX)
1398
25728
    PARSESTATES(XX)
1399
#undef XX
1400
402
  }
1401
}  // namespace url
1402
}  // namespace node
1403
1404
1734
NODE_MODULE_CONTEXT_AWARE_BUILTIN(url, node::url::Init)