| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 298a16a commit 223494c
22 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -50,6 +50,9 @@ const buf7 = Buffer.from('tést', 'latin1'); | |||
| 50 | 50 | ## Buffers and character encodings | |
| 51 | 51 | <!-- YAML | |
| 52 | 52 | changes: | |
| 53 | + - version: REPLACEME | ||
| 54 | + pr-url: https://github.com/nodejs/node/pull/36952 | ||
| 55 | + description: Introduced `base64url` encoding. | ||
| 53 | 56 | - version: v6.4.0 | |
| 54 | 57 | pr-url: https://github.com/nodejs/node/pull/7111 | |
| 55 | 58 | description: Introduced `latin1` as an alias for `binary`. | |
@@ -106,6 +109,11 @@ string into a `Buffer` as decoding. | |||
| 106 | 109 | specified in [RFC 4648, Section 5][]. Whitespace characters such as spaces, | |
| 107 | 110 | tabs, and new lines contained within the base64-encoded string are ignored. | |
| 108 | 111 | ||
| 112 | + * `'base64url'`: [base64url][] encoding as specified in | ||
| 113 | + [RFC 4648, Section 5][]. When creating a `Buffer` from a string, this | ||
| 114 | + encoding will also correctly accept regular base64-encoded strings. When | ||
| 115 | + encoding a `Buffer` to a string, this encoding will omit padding. | ||
| 116 | + | ||
| 109 | 117 | * `'hex'`: Encode each byte as two hexadecimal characters. Data truncation | |
| 110 | 118 | may occur when decoding strings that do exclusively contain valid hexadecimal | |
| 111 | 119 | characters. See below for an example. | |
@@ -469,9 +477,10 @@ Returns the byte length of a string when encoded using `encoding`. | |||
| 469 | 477 | This is not the same as [`String.prototype.length`][], which does not account | |
| 470 | 478 | for the encoding that is used to convert the string into bytes. | |
| 471 | 479 | ||
| 472 | - For `'base64'` and `'hex'`, this function assumes valid input. For strings that | ||
| 473 | - contain non-base64/hex-encoded data (e.g. whitespace), the return value might be | ||
| 474 | - greater than the length of a `Buffer` created from the string. | ||
| 480 | + For `'base64'`, `'base64url'`, and `'hex'`, this function assumes valid input. | ||
| 481 | + For strings that contain non-base64/hex-encoded data (e.g. whitespace), the | ||
| 482 | + return value might be greater than the length of a `Buffer` created from the | ||
| 483 | + string. | ||
| 475 | 484 | ||
| 476 | 485 | ```js | |
| 477 | 486 | const str = '\u00bd + \u00bc = \u00be'; | |
@@ -3427,6 +3436,7 @@ introducing security vulnerabilities into an application. | |||
| 3427 | 3436 | [`buffer.kMaxLength`]: #buffer_buffer_kmaxlength | |
| 3428 | 3437 | [`util.inspect()`]: util.md#util_util_inspect_object_options | |
| 3429 | 3438 | [`v8::TypedArray::kMaxLength`]: https://v8.github.io/api/head/classv8_1_1TypedArray.html#a54a48f4373da0850663c4393d843b9b0 | |
| 3439 | + [base64url]: https://tools.ietf.org/html/rfc4648#section-5 | ||
| 3430 | 3440 | [binary strings]: https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary | |
| 3431 | 3441 | [endianness]: https://en.wikipedia.org/wiki/Endianness | |
| 3432 | 3442 | [iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -659,6 +659,20 @@ const encodingOps = { | |||
| 659 | 659 | encodingsMap.base64, | |
| 660 | 660 | dir) | |
| 661 | 661 | }, | |
| 662 | + base64url: { | ||
| 663 | + encoding: 'base64url', | ||
| 664 | + encodingVal: encodingsMap.base64url, | ||
| 665 | + byteLength: (string) => base64ByteLength(string, string.length), | ||
| 666 | + write: (buf, string, offset, len) => | ||
| 667 | + buf.base64urlWrite(string, offset, len), | ||
| 668 | + slice: (buf, start, end) => buf.base64urlSlice(start, end), | ||
| 669 | + indexOf: (buf, val, byteOffset, dir) => | ||
| 670 | + indexOfBuffer(buf, | ||
| 671 | + fromStringFast(val, encodingOps.base64url), | ||
| 672 | + byteOffset, | ||
| 673 | + encodingsMap.base64url, | ||
| 674 | + dir) | ||
| 675 | + }, | ||
| 662 | 676 | hex: { | |
| 663 | 677 | encoding: 'hex', | |
| 664 | 678 | encodingVal: encodingsMap.hex, | |
@@ -715,6 +729,11 @@ function getEncodingOps(encoding) { | |||
| 715 | 729 | if (encoding === 'hex' || StringPrototypeToLowerCase(encoding) === 'hex') | |
| 716 | 730 | return encodingOps.hex; | |
| 717 | 731 | break; | |
| 732 | + case 9: | ||
| 733 | + if (encoding === 'base64url' || | ||
| 734 | + StringPrototypeToLowerCase(encoding) === 'base64url') | ||
| 735 | + return encodingOps.base64url; | ||
| 736 | + break; | ||
| 718 | 737 | } | |
| 719 | 738 | } | |
| 720 | 739 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,12 +18,14 @@ const { validateNumber } = require('internal/validators'); | |||
| 18 | 18 | const { | |
| 19 | 19 | asciiSlice, | |
| 20 | 20 | base64Slice, | |
| 21 | + base64urlSlice, | ||
| 21 | 22 | latin1Slice, | |
| 22 | 23 | hexSlice, | |
| 23 | 24 | ucs2Slice, | |
| 24 | 25 | utf8Slice, | |
| 25 | 26 | asciiWrite, | |
| 26 | 27 | base64Write, | |
| 28 | + base64urlWrite, | ||
| 27 | 29 | latin1Write, | |
| 28 | 30 | hexWrite, | |
| 29 | 31 | ucs2Write, | |
@@ -1026,12 +1028,14 @@ function addBufferPrototypeMethods(proto) { | |||
| 1026 | 1028 | ||
| 1027 | 1029 | proto.asciiSlice = asciiSlice; | |
| 1028 | 1030 | proto.base64Slice = base64Slice; | |
| 1031 | + proto.base64urlSlice = base64urlSlice; | ||
| 1029 | 1032 | proto.latin1Slice = latin1Slice; | |
| 1030 | 1033 | proto.hexSlice = hexSlice; | |
| 1031 | 1034 | proto.ucs2Slice = ucs2Slice; | |
| 1032 | 1035 | proto.utf8Slice = utf8Slice; | |
| 1033 | 1036 | proto.asciiWrite = asciiWrite; | |
| 1034 | 1037 | proto.base64Write = base64Write; | |
| 1038 | + proto.base64urlWrite = base64urlWrite; | ||
| 1035 | 1039 | proto.latin1Write = latin1Write; | |
| 1036 | 1040 | proto.hexWrite = hexWrite; | |
| 1037 | 1041 | proto.ucs2Write = ucs2Write; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -177,6 +177,11 @@ function slowCases(enc) { | |||
| 177 | 177 | `${enc}`.toLowerCase() === 'utf-16le') | |
| 178 | 178 | return 'utf16le'; | |
| 179 | 179 | break; | |
| 180 | + case 9: | ||
| 181 | + if (enc === 'base64url' || enc === 'BASE64URL' || | ||
| 182 | + `${enc}`.toLowerCase() === 'base64url') | ||
| 183 | + return 'base64url'; | ||
| 184 | + break; | ||
| 180 | 185 | default: | |
| 181 | 186 | if (enc === '') return 'utf8'; | |
| 182 | 187 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,13 +68,17 @@ enum encoding ParseEncoding(const char* encoding, | |||
| 68 | 68 | } else if (encoding[1] == 'a') { | |
| 69 | 69 | if (strncmp(encoding + 2, "se64", 5) == 0) | |
| 70 | 70 | return BASE64; | |
| 71 | + if (strncmp(encoding + 2, "se64url", 8) == 0) | ||
| 72 | + return BASE64URL; | ||
| 71 | 73 | } | |
| 72 | 74 | if (StringEqualNoCase(encoding, "binary")) | |
| 73 | 75 | return LATIN1; // BINARY is a deprecated alias of LATIN1. | |
| 74 | 76 | if (StringEqualNoCase(encoding, "buffer")) | |
| 75 | 77 | return BUFFER; | |
| 76 | 78 | if (StringEqualNoCase(encoding, "base64")) | |
| 77 | 79 | return BASE64; | |
| 80 | + if (StringEqualNoCase(encoding, "base64url")) | ||
| 81 | + return BASE64URL; | ||
| 78 | 82 | break; | |
| 79 | 83 | ||
| 80 | 84 | case 'a': | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -123,12 +123,13 @@ size_t base64_decode(char* const dst, const size_t dstlen, | |||
| 123 | 123 | inline size_t base64_encode(const char* src, | |
| 124 | 124 | size_t slen, | |
| 125 | 125 | char* dst, | |
| 126 | - size_t dlen) { | ||
| 126 | + size_t dlen, | ||
| 127 | + Base64Mode mode) { | ||
| 127 | 128 | // We know how much we'll write, just make sure that there's space. | |
| 128 | - CHECK(dlen >= base64_encoded_size(slen) && | ||
| 129 | + CHECK(dlen >= base64_encoded_size(slen, mode) && | ||
| 129 | 130 | "not enough space provided for base64 encode"); | |
| 130 | 131 | ||
| 131 | - dlen = base64_encoded_size(slen); | ||
| 132 | + dlen = base64_encoded_size(slen, mode); | ||
| 132 | 133 | ||
| 133 | 134 | unsigned a; | |
| 134 | 135 | unsigned b; | |
@@ -137,9 +138,7 @@ inline size_t base64_encode(const char* src, | |||
| 137 | 138 | unsigned k; | |
| 138 | 139 | unsigned n; | |
| 139 | 140 | ||
| 140 | - static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 141 | - "abcdefghijklmnopqrstuvwxyz" | ||
| 142 | - "0123456789+/"; | ||
| 141 | + const char* table = base64_select_table(mode); | ||
| 143 | 142 | ||
| 144 | 143 | i = 0; | |
| 145 | 144 | k = 0; | |
@@ -164,16 +163,19 @@ inline size_t base64_encode(const char* src, | |||
| 164 | 163 | a = src[i + 0] & 0xff; | |
| 165 | 164 | dst[k + 0] = table[a >> 2]; | |
| 166 | 165 | dst[k + 1] = table[(a & 3) << 4]; | |
| 167 | - dst[k + 2] = '='; | ||
| 168 | - dst[k + 3] = '='; | ||
| 166 | + if (mode == Base64Mode::NORMAL) { | ||
| 167 | + dst[k + 2] = '='; | ||
| 168 | + dst[k + 3] = '='; | ||
| 169 | + } | ||
| 169 | 170 | break; | |
| 170 | 171 | case 2: | |
| 171 | 172 | a = src[i + 0] & 0xff; | |
| 172 | 173 | b = src[i + 1] & 0xff; | |
| 173 | 174 | dst[k + 0] = table[a >> 2]; | |
| 174 | 175 | dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; | |
| 175 | 176 | dst[k + 2] = table[(b & 0x0f) << 2]; | |
| 176 | - dst[k + 3] = '='; | ||
| 177 | + if (mode == Base64Mode::NORMAL) | ||
| 178 | + dst[k + 3] = '='; | ||
| 177 | 179 | break; | |
| 178 | 180 | } | |
| 179 | 181 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,13 +5,40 @@ | |||
| 5 | 5 | ||
| 6 | 6 | #include "util.h" | |
| 7 | 7 | ||
| 8 | + #include <cmath> | ||
| 8 | 9 | #include <cstddef> | |
| 9 | 10 | #include <cstdint> | |
| 10 | 11 | ||
| 11 | 12 | namespace node { | |
| 12 | 13 | //// Base 64 //// | |
| 13 | - static inline constexpr size_t base64_encoded_size(size_t size) { | ||
| 14 | - return ((size + 2) / 3 * 4); | ||
| 14 | + | ||
| 15 | + enum class Base64Mode { | ||
| 16 | + NORMAL, | ||
| 17 | + URL | ||
| 18 | + }; | ||
| 19 | + | ||
| 20 | + static constexpr char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 21 | + "abcdefghijklmnopqrstuvwxyz" | ||
| 22 | + "0123456789+/"; | ||
| 23 | + | ||
| 24 | + static constexpr char base64_table_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 25 | + "abcdefghijklmnopqrstuvwxyz" | ||
| 26 | + "0123456789-_"; | ||
| 27 | + | ||
| 28 | + static inline const char* base64_select_table(Base64Mode mode) { | ||
| 29 | + switch (mode) { | ||
| 30 | + case Base64Mode::NORMAL: return base64_table; | ||
| 31 | + case Base64Mode::URL: return base64_table_url; | ||
| 32 | + default: UNREACHABLE(); | ||
| 33 | + } | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + static inline constexpr size_t base64_encoded_size( | ||
| 37 | + size_t size, | ||
| 38 | + Base64Mode mode = Base64Mode::NORMAL) { | ||
| 39 | + return mode == Base64Mode::NORMAL | ||
| 40 | + ? ((size + 2) / 3 * 4) | ||
| 41 | + : std::ceil(static_cast<double>(size * 4) / 3); | ||
| 15 | 42 | } | |
| 16 | 43 | ||
| 17 | 44 | // Doesn't check for padding at the end. Can be 1-2 bytes over. | |
@@ -32,7 +59,8 @@ size_t base64_decode(char* const dst, const size_t dstlen, | |||
| 32 | 59 | inline size_t base64_encode(const char* src, | |
| 33 | 60 | size_t slen, | |
| 34 | 61 | char* dst, | |
| 35 | - size_t dlen); | ||
| 62 | + size_t dlen, | ||
| 63 | + Base64Mode mode = Base64Mode::NORMAL); | ||
| 36 | 64 | } // namespace node | |
| 37 | 65 | ||
| 38 | 66 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -664,7 +664,18 @@ inline void NODE_SET_PROTOTYPE_METHOD(v8::Local<v8::FunctionTemplate> recv, | |||
| 664 | 664 | #define NODE_SET_PROTOTYPE_METHOD node::NODE_SET_PROTOTYPE_METHOD | |
| 665 | 665 | ||
| 666 | 666 | // BINARY is a deprecated alias of LATIN1. | |
| 667 | - enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER, LATIN1 = BINARY}; | ||
| 667 | + // BASE64URL is not currently exposed to the JavaScript side. | ||
| 668 | + enum encoding { | ||
| 669 | + ASCII, | ||
| 670 | + UTF8, | ||
| 671 | + BASE64, | ||
| 672 | + UCS2, | ||
| 673 | + BINARY, | ||
| 674 | + HEX, | ||
| 675 | + BUFFER, | ||
| 676 | + BASE64URL, | ||
| 677 | + LATIN1 = BINARY | ||
| 678 | + }; | ||
| 668 | 679 | ||
| 669 | 680 | NODE_EXTERN enum encoding ParseEncoding( | |
| 670 | 681 | v8::Isolate* isolate, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1163,13 +1163,15 @@ void Initialize(Local<Object> target, | |||
| 1163 | 1163 | ||
| 1164 | 1164 | env->SetMethodNoSideEffect(target, "asciiSlice", StringSlice<ASCII>); | |
| 1165 | 1165 | env->SetMethodNoSideEffect(target, "base64Slice", StringSlice<BASE64>); | |
| 1166 | + env->SetMethodNoSideEffect(target, "base64urlSlice", StringSlice<BASE64URL>); | ||
| 1166 | 1167 | env->SetMethodNoSideEffect(target, "latin1Slice", StringSlice<LATIN1>); | |
| 1167 | 1168 | env->SetMethodNoSideEffect(target, "hexSlice", StringSlice<HEX>); | |
| 1168 | 1169 | env->SetMethodNoSideEffect(target, "ucs2Slice", StringSlice<UCS2>); | |
| 1169 | 1170 | env->SetMethodNoSideEffect(target, "utf8Slice", StringSlice<UTF8>); | |
| 1170 | 1171 | ||
| 1171 | 1172 | env->SetMethod(target, "asciiWrite", StringWrite<ASCII>); | |
| 1172 | 1173 | env->SetMethod(target, "base64Write", StringWrite<BASE64>); | |
| 1174 | + env->SetMethod(target, "base64urlWrite", StringWrite<BASE64URL>); | ||
| 1173 | 1175 | env->SetMethod(target, "latin1Write", StringWrite<LATIN1>); | |
| 1174 | 1176 | env->SetMethod(target, "hexWrite", StringWrite<HEX>); | |
| 1175 | 1177 | env->SetMethod(target, "ucs2Write", StringWrite<UCS2>); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -358,6 +358,8 @@ size_t StringBytes::Write(Isolate* isolate, | |||
| 358 | 358 | break; | |
| 359 | 359 | } | |
| 360 | 360 | ||
| 361 | + case BASE64URL: | ||
| 362 | + // Fall through | ||
| 361 | 363 | case BASE64: | |
| 362 | 364 | if (str->IsExternalOneByte()) { | |
| 363 | 365 | auto ext = str->GetExternalOneByteStringResource(); | |
@@ -425,6 +427,8 @@ Maybe<size_t> StringBytes::StorageSize(Isolate* isolate, | |||
| 425 | 427 | data_size = str->Length() * sizeof(uint16_t); | |
| 426 | 428 | break; | |
| 427 | 429 | ||
| 430 | + case BASE64URL: | ||
| 431 | + // Fall through | ||
| 428 | 432 | case BASE64: | |
| 429 | 433 | data_size = base64_decoded_size_fast(str->Length()); | |
| 430 | 434 | break; | |
@@ -466,6 +470,8 @@ Maybe<size_t> StringBytes::Size(Isolate* isolate, | |||
| 466 | 470 | case UCS2: | |
| 467 | 471 | return Just(str->Length() * sizeof(uint16_t)); | |
| 468 | 472 | ||
| 473 | + case BASE64URL: | ||
| 474 | + // Fall through | ||
| 469 | 475 | case BASE64: { | |
| 470 | 476 | String::Value value(isolate, str); | |
| 471 | 477 | return Just(base64_decoded_size(*value, value.length())); | |
@@ -691,6 +697,20 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, | |||
| 691 | 697 | return ExternOneByteString::New(isolate, dst, dlen, error); | |
| 692 | 698 | } | |
| 693 | 699 | ||
| 700 | + case BASE64URL: { | ||
| 701 | + size_t dlen = base64_encoded_size(buflen, Base64Mode::URL); | ||
| 702 | + char* dst = node::UncheckedMalloc(dlen); | ||
| 703 | + if (dst == nullptr) { | ||
| 704 | + *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | ||
| 705 | + return MaybeLocal<Value>(); | ||
| 706 | + } | ||
| 707 | + | ||
| 708 | + size_t written = base64_encode(buf, buflen, dst, dlen, Base64Mode::URL); | ||
| 709 | + CHECK_EQ(written, dlen); | ||
| 710 | + | ||
| 711 | + return ExternOneByteString::New(isolate, dst, dlen, error); | ||
| 712 | + } | ||
| 713 | + | ||
| 694 | 714 | case HEX: { | |
| 695 | 715 | size_t dlen = buflen * 2; | |
| 696 | 716 | char* dst = node::UncheckedMalloc(dlen); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments