diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76d5b50a934dee..0065d339f07a21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,7 +27,8 @@ release.
-8.9.2
+8.9.3
+8.9.2
8.9.1
8.9.0
8.8.1
diff --git a/deps/openssl/asm/arm-void-gas/aes/aes-armv4.S b/deps/openssl/asm/arm-void-gas/aes/aes-armv4.S
index 333a522730ba6c..bd01abddce1d55 100644
--- a/deps/openssl/asm/arm-void-gas/aes/aes-armv4.S
+++ b/deps/openssl/asm/arm-void-gas/aes/aes-armv4.S
@@ -164,7 +164,7 @@ AES_encrypt:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_encrypt
#else
- adr r3,AES_encrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
@@ -410,7 +410,7 @@ _armv4_AES_set_encrypt_key:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_set_encrypt_key
#else
- adr r3,private_AES_set_encrypt_key
+ adr r3,.
#endif
teq r0,#0
#if __ARM_ARCH__>=7
@@ -927,7 +927,7 @@ AES_decrypt:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_decrypt
#else
- adr r3,AES_decrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
diff --git a/deps/openssl/asm/arm-void-gas/aes/bsaes-armv7.S b/deps/openssl/asm/arm-void-gas/aes/bsaes-armv7.S
index b21ed7078b194a..aa15709abb403a 100644
--- a/deps/openssl/asm/arm-void-gas/aes/bsaes-armv7.S
+++ b/deps/openssl/asm/arm-void-gas/aes/bsaes-armv7.S
@@ -81,7 +81,7 @@
.type _bsaes_decrypt8,%function
.align 4
_bsaes_decrypt8:
- adr r6,_bsaes_decrypt8
+ adr r6,.
vldmia r4!, {q9} @ round 0 key
add r6,r6,#.LM0ISR-_bsaes_decrypt8
@@ -567,7 +567,7 @@ _bsaes_const:
.type _bsaes_encrypt8,%function
.align 4
_bsaes_encrypt8:
- adr r6,_bsaes_encrypt8
+ adr r6,.
vldmia r4!, {q9} @ round 0 key
sub r6,r6,#_bsaes_encrypt8-.LM0SR
@@ -998,7 +998,7 @@ _bsaes_encrypt8_bitslice:
.type _bsaes_key_convert,%function
.align 4
_bsaes_key_convert:
- adr r6,_bsaes_key_convert
+ adr r6,.
vld1.8 {q7}, [r4]! @ load round 0 key
sub r6,r6,#_bsaes_key_convert-.LM0
vld1.8 {q15}, [r4]! @ load round 1 key
diff --git a/deps/openssl/asm/arm-void-gas/sha/sha256-armv4.S b/deps/openssl/asm/arm-void-gas/sha/sha256-armv4.S
index 683f1cc0c874b5..4e1f0226e84bc0 100644
--- a/deps/openssl/asm/arm-void-gas/sha/sha256-armv4.S
+++ b/deps/openssl/asm/arm-void-gas/sha/sha256-armv4.S
@@ -88,7 +88,7 @@ sha256_block_data_order:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ sha256_block_data_order
#else
- adr r3,sha256_block_data_order
+ adr r3,.
#endif
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
ldr r12,.LOPENSSL_armcap
diff --git a/deps/openssl/asm/x64-elf-gas/bn/rsaz-avx2.s b/deps/openssl/asm/x64-elf-gas/bn/rsaz-avx2.s
index d3d84e3f7a4b78..a2cccde63604f4 100644
--- a/deps/openssl/asm/x64-elf-gas/bn/rsaz-avx2.s
+++ b/deps/openssl/asm/x64-elf-gas/bn/rsaz-avx2.s
@@ -66,7 +66,7 @@ rsaz_1024_sqr_avx2:
vmovdqu 256-128(%rsi),%ymm8
leaq 192(%rsp),%rbx
- vpbroadcastq .Land_mask(%rip),%ymm15
+ vmovdqu .Land_mask(%rip),%ymm15
jmp .LOOP_GRANDE_SQR_1024
.align 32
@@ -799,10 +799,10 @@ rsaz_1024_mul_avx2:
vpmuludq 192-128(%rcx),%ymm11,%ymm12
vpaddq %ymm12,%ymm6,%ymm6
vpmuludq 224-128(%rcx),%ymm11,%ymm13
- vpblendd $3,%ymm14,%ymm9,%ymm9
+ vpblendd $3,%ymm14,%ymm9,%ymm12
vpaddq %ymm13,%ymm7,%ymm7
vpmuludq 256-128(%rcx),%ymm11,%ymm0
- vpaddq %ymm9,%ymm3,%ymm3
+ vpaddq %ymm12,%ymm3,%ymm3
vpaddq %ymm0,%ymm8,%ymm8
movq %rbx,%rax
@@ -815,7 +815,9 @@ rsaz_1024_mul_avx2:
vmovdqu -8+64-128(%rsi),%ymm13
movq %r10,%rax
+ vpblendd $0xfc,%ymm14,%ymm9,%ymm9
imull %r8d,%eax
+ vpaddq %ymm9,%ymm4,%ymm4
andl $0x1fffffff,%eax
imulq 16-128(%rsi),%rbx
@@ -1044,7 +1046,6 @@ rsaz_1024_mul_avx2:
decl %r14d
jnz .Loop_mul_1024
- vpermq $0,%ymm15,%ymm15
vpaddq (%rsp),%ymm12,%ymm0
vpsrlq $29,%ymm0,%ymm12
@@ -1684,7 +1685,7 @@ rsaz_avx2_eligible:
.align 64
.Land_mask:
-.quad 0x1fffffff,0x1fffffff,0x1fffffff,-1
+.quad 0x1fffffff,0x1fffffff,0x1fffffff,0x1fffffff
.Lscatter_permd:
.long 0,2,4,6,7,7,7,7
.Lgather_permd:
diff --git a/deps/openssl/asm/x64-macosx-gas/bn/rsaz-avx2.s b/deps/openssl/asm/x64-macosx-gas/bn/rsaz-avx2.s
index 1dea50d90463e7..f2bc63be34eee2 100644
--- a/deps/openssl/asm/x64-macosx-gas/bn/rsaz-avx2.s
+++ b/deps/openssl/asm/x64-macosx-gas/bn/rsaz-avx2.s
@@ -66,7 +66,7 @@ L$sqr_1024_no_n_copy:
vmovdqu 256-128(%rsi),%ymm8
leaq 192(%rsp),%rbx
- vpbroadcastq L$and_mask(%rip),%ymm15
+ vmovdqu L$and_mask(%rip),%ymm15
jmp L$OOP_GRANDE_SQR_1024
.p2align 5
@@ -799,10 +799,10 @@ L$oop_mul_1024:
vpmuludq 192-128(%rcx),%ymm11,%ymm12
vpaddq %ymm12,%ymm6,%ymm6
vpmuludq 224-128(%rcx),%ymm11,%ymm13
- vpblendd $3,%ymm14,%ymm9,%ymm9
+ vpblendd $3,%ymm14,%ymm9,%ymm12
vpaddq %ymm13,%ymm7,%ymm7
vpmuludq 256-128(%rcx),%ymm11,%ymm0
- vpaddq %ymm9,%ymm3,%ymm3
+ vpaddq %ymm12,%ymm3,%ymm3
vpaddq %ymm0,%ymm8,%ymm8
movq %rbx,%rax
@@ -815,7 +815,9 @@ L$oop_mul_1024:
vmovdqu -8+64-128(%rsi),%ymm13
movq %r10,%rax
+ vpblendd $0xfc,%ymm14,%ymm9,%ymm9
imull %r8d,%eax
+ vpaddq %ymm9,%ymm4,%ymm4
andl $0x1fffffff,%eax
imulq 16-128(%rsi),%rbx
@@ -1044,7 +1046,6 @@ L$oop_mul_1024:
decl %r14d
jnz L$oop_mul_1024
- vpermq $0,%ymm15,%ymm15
vpaddq (%rsp),%ymm12,%ymm0
vpsrlq $29,%ymm0,%ymm12
@@ -1684,7 +1685,7 @@ _rsaz_avx2_eligible:
.p2align 6
L$and_mask:
-.quad 0x1fffffff,0x1fffffff,0x1fffffff,-1
+.quad 0x1fffffff,0x1fffffff,0x1fffffff,0x1fffffff
L$scatter_permd:
.long 0,2,4,6,7,7,7,7
L$gather_permd:
diff --git a/deps/openssl/asm/x64-win32-masm/bn/rsaz-avx2.asm b/deps/openssl/asm/x64-win32-masm/bn/rsaz-avx2.asm
index c24d0c5e6a3d3d..40ce01fdc61a9b 100644
--- a/deps/openssl/asm/x64-win32-masm/bn/rsaz-avx2.asm
+++ b/deps/openssl/asm/x64-win32-masm/bn/rsaz-avx2.asm
@@ -90,7 +90,7 @@ $L$sqr_1024_no_n_copy::
vmovdqu ymm8,YMMWORD PTR[((256-128))+rsi]
lea rbx,QWORD PTR[192+rsp]
- vpbroadcastq ymm15,QWORD PTR[$L$and_mask]
+ vmovdqu ymm15,YMMWORD PTR[$L$and_mask]
jmp $L$OOP_GRANDE_SQR_1024
ALIGN 32
@@ -860,10 +860,10 @@ $L$oop_mul_1024::
vpmuludq ymm12,ymm11,YMMWORD PTR[((192-128))+rcx]
vpaddq ymm6,ymm6,ymm12
vpmuludq ymm13,ymm11,YMMWORD PTR[((224-128))+rcx]
- vpblendd ymm9,ymm9,ymm14,3
+ vpblendd ymm12,ymm9,ymm14,3
vpaddq ymm7,ymm7,ymm13
vpmuludq ymm0,ymm11,YMMWORD PTR[((256-128))+rcx]
- vpaddq ymm3,ymm3,ymm9
+ vpaddq ymm3,ymm3,ymm12
vpaddq ymm8,ymm8,ymm0
mov rax,rbx
@@ -876,7 +876,9 @@ $L$oop_mul_1024::
vmovdqu ymm13,YMMWORD PTR[((-8+64-128))+rsi]
mov rax,r10
+ vpblendd ymm9,ymm9,ymm14,0fch
imul eax,r8d
+ vpaddq ymm4,ymm4,ymm9
and eax,01fffffffh
imul rbx,QWORD PTR[((16-128))+rsi]
@@ -1105,7 +1107,6 @@ $L$oop_mul_1024::
dec r14d
jnz $L$oop_mul_1024
- vpermq ymm15,ymm15,0
vpaddq ymm0,ymm12,YMMWORD PTR[rsp]
vpsrlq ymm12,ymm0,29
@@ -1783,7 +1784,7 @@ rsaz_avx2_eligible ENDP
ALIGN 64
$L$and_mask::
- DQ 01fffffffh,01fffffffh,01fffffffh,-1
+ DQ 01fffffffh,01fffffffh,01fffffffh,01fffffffh
$L$scatter_permd::
DD 0,2,4,6,7,7,7,7
$L$gather_permd::
diff --git a/deps/openssl/asm_obsolete/arm-void-gas/aes/aes-armv4.S b/deps/openssl/asm_obsolete/arm-void-gas/aes/aes-armv4.S
index 333a522730ba6c..bd01abddce1d55 100644
--- a/deps/openssl/asm_obsolete/arm-void-gas/aes/aes-armv4.S
+++ b/deps/openssl/asm_obsolete/arm-void-gas/aes/aes-armv4.S
@@ -164,7 +164,7 @@ AES_encrypt:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_encrypt
#else
- adr r3,AES_encrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
@@ -410,7 +410,7 @@ _armv4_AES_set_encrypt_key:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_set_encrypt_key
#else
- adr r3,private_AES_set_encrypt_key
+ adr r3,.
#endif
teq r0,#0
#if __ARM_ARCH__>=7
@@ -927,7 +927,7 @@ AES_decrypt:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_decrypt
#else
- adr r3,AES_decrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov r12,r0 @ inp
diff --git a/deps/openssl/asm_obsolete/arm-void-gas/aes/bsaes-armv7.S b/deps/openssl/asm_obsolete/arm-void-gas/aes/bsaes-armv7.S
index b21ed7078b194a..aa15709abb403a 100644
--- a/deps/openssl/asm_obsolete/arm-void-gas/aes/bsaes-armv7.S
+++ b/deps/openssl/asm_obsolete/arm-void-gas/aes/bsaes-armv7.S
@@ -81,7 +81,7 @@
.type _bsaes_decrypt8,%function
.align 4
_bsaes_decrypt8:
- adr r6,_bsaes_decrypt8
+ adr r6,.
vldmia r4!, {q9} @ round 0 key
add r6,r6,#.LM0ISR-_bsaes_decrypt8
@@ -567,7 +567,7 @@ _bsaes_const:
.type _bsaes_encrypt8,%function
.align 4
_bsaes_encrypt8:
- adr r6,_bsaes_encrypt8
+ adr r6,.
vldmia r4!, {q9} @ round 0 key
sub r6,r6,#_bsaes_encrypt8-.LM0SR
@@ -998,7 +998,7 @@ _bsaes_encrypt8_bitslice:
.type _bsaes_key_convert,%function
.align 4
_bsaes_key_convert:
- adr r6,_bsaes_key_convert
+ adr r6,.
vld1.8 {q7}, [r4]! @ load round 0 key
sub r6,r6,#_bsaes_key_convert-.LM0
vld1.8 {q15}, [r4]! @ load round 1 key
diff --git a/deps/openssl/asm_obsolete/arm-void-gas/sha/sha256-armv4.S b/deps/openssl/asm_obsolete/arm-void-gas/sha/sha256-armv4.S
index 683f1cc0c874b5..4e1f0226e84bc0 100644
--- a/deps/openssl/asm_obsolete/arm-void-gas/sha/sha256-armv4.S
+++ b/deps/openssl/asm_obsolete/arm-void-gas/sha/sha256-armv4.S
@@ -88,7 +88,7 @@ sha256_block_data_order:
#if __ARM_ARCH__<7
sub r3,pc,#8 @ sha256_block_data_order
#else
- adr r3,sha256_block_data_order
+ adr r3,.
#endif
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
ldr r12,.LOPENSSL_armcap
diff --git a/deps/openssl/openssl/CHANGES b/deps/openssl/openssl/CHANGES
index e3d57b328c58e3..f2fc31a25c54b1 100644
--- a/deps/openssl/openssl/CHANGES
+++ b/deps/openssl/openssl/CHANGES
@@ -7,6 +7,51 @@
https://github.com/openssl/openssl/commits/ and pick the appropriate
release branch.
+ Changes between 1.0.2m and 1.0.2n [7 Dec 2017]
+
+ *) Read/write after SSL object in error state
+
+ OpenSSL 1.0.2 (starting from version 1.0.2b) introduced an "error state"
+ mechanism. The intent was that if a fatal error occurred during a handshake
+ then OpenSSL would move into the error state and would immediately fail if
+ you attempted to continue the handshake. This works as designed for the
+ explicit handshake functions (SSL_do_handshake(), SSL_accept() and
+ SSL_connect()), however due to a bug it does not work correctly if
+ SSL_read() or SSL_write() is called directly. In that scenario, if the
+ handshake fails then a fatal error will be returned in the initial function
+ call. If SSL_read()/SSL_write() is subsequently called by the application
+ for the same SSL object then it will succeed and the data is passed without
+ being decrypted/encrypted directly from the SSL/TLS record layer.
+
+ In order to exploit this issue an application bug would have to be present
+ that resulted in a call to SSL_read()/SSL_write() being issued after having
+ already received a fatal error.
+
+ This issue was reported to OpenSSL by David Benjamin (Google).
+ (CVE-2017-3737)
+ [Matt Caswell]
+
+ *) rsaz_1024_mul_avx2 overflow bug on x86_64
+
+ There is an overflow bug in the AVX2 Montgomery multiplication procedure
+ used in exponentiation with 1024-bit moduli. No EC algorithms are affected.
+ Analysis suggests that attacks against RSA and DSA as a result of this
+ defect would be very difficult to perform and are not believed likely.
+ Attacks against DH1024 are considered just feasible, because most of the
+ work necessary to deduce information about a private key may be performed
+ offline. The amount of resources required for such an attack would be
+ significant. However, for an attack on TLS to be meaningful, the server
+ would have to share the DH1024 private key among multiple clients, which is
+ no longer an option since CVE-2016-0701.
+
+ This only affects processors that support the AVX2 but not ADX extensions
+ like Intel Haswell (4th generation).
+
+ This issue was reported to OpenSSL by David Benjamin (Google). The issue
+ was originally found via the OSS-Fuzz project.
+ (CVE-2017-3738)
+ [Andy Polyakov]
+
Changes between 1.0.2l and 1.0.2m [2 Nov 2017]
*) bn_sqrx8x_internal carry bug on x86_64
diff --git a/deps/openssl/openssl/Configure b/deps/openssl/openssl/Configure
index fd7988e2b3d93a..60386d39598758 100755
--- a/deps/openssl/openssl/Configure
+++ b/deps/openssl/openssl/Configure
@@ -592,9 +592,9 @@ my %table=(
"debug-VC-WIN64A","cl:-W3 -Gs0 -Gy -Zi -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -DUNICODE -D_UNICODE -D_CRT_SECURE_NO_DEPRECATE:::WIN64A::SIXTY_FOUR_BIT RC4_CHUNK_LL DES_INT EXPORT_VAR_AS_FN:".eval{my $asm=$x86_64_asm;$asm=~s/x86_64-gcc\.o/bn_asm.o/;$asm}.":auto:win32",
# x86 Win32 target defaults to ANSI API, if you want UNICODE, complement
# 'perl Configure VC-WIN32' with '-DUNICODE -D_UNICODE'
-"VC-WIN32","cl:-W3 -Gs0 -GF -Gy -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE:::WIN32::BN_LLONG RC4_INDEX EXPORT_VAR_AS_FN ${x86_gcc_opts}:${x86_asm}:win32n:win32",
+"VC-WIN32","cl:-W3 -WX -Gs0 -GF -Gy -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE -D_WINSOCK_DEPRECATED_NO_WARNINGS:::WIN32::BN_LLONG RC4_INDEX EXPORT_VAR_AS_FN ${x86_gcc_opts}:${x86_asm}:win32n:win32",
# Unified CE target
-"debug-VC-WIN32","cl:-W3 -Gs0 -GF -Gy -Zi -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE:::WIN32::BN_LLONG RC4_INDEX EXPORT_VAR_AS_FN ${x86_gcc_opts}:${x86_asm}:win32n:win32",
+"debug-VC-WIN32","cl:-W3 -WX -Gs0 -GF -Gy -Zi -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE -D_WINSOCK_DEPRECATED_NO_WARNINGS:::WIN32::BN_LLONG RC4_INDEX EXPORT_VAR_AS_FN ${x86_gcc_opts}:${x86_asm}:win32n:win32",
"VC-CE","cl::::WINCE::BN_LLONG RC4_INDEX EXPORT_VAR_AS_FN ${x86_gcc_opts}:${no_asm}:win32",
# Borland C++ 4.5
diff --git a/deps/openssl/openssl/Makefile b/deps/openssl/openssl/Makefile
index 484f2f45f77667..9212c288527852 100644
--- a/deps/openssl/openssl/Makefile
+++ b/deps/openssl/openssl/Makefile
@@ -4,7 +4,7 @@
## Makefile for OpenSSL
##
-VERSION=1.0.2m
+VERSION=1.0.2n
MAJOR=1
MINOR=0.2
SHLIB_VERSION_NUMBER=1.0.0
diff --git a/deps/openssl/openssl/Makefile.bak b/deps/openssl/openssl/Makefile.bak
index b545261eaeee19..e766d35764ffb7 100644
--- a/deps/openssl/openssl/Makefile.bak
+++ b/deps/openssl/openssl/Makefile.bak
@@ -4,7 +4,7 @@
## Makefile for OpenSSL
##
-VERSION=1.0.2m
+VERSION=1.0.2n
MAJOR=1
MINOR=0.2
SHLIB_VERSION_NUMBER=1.0.0
diff --git a/deps/openssl/openssl/NEWS b/deps/openssl/openssl/NEWS
index 1b72013ad18624..6f0c5c47b654bc 100644
--- a/deps/openssl/openssl/NEWS
+++ b/deps/openssl/openssl/NEWS
@@ -5,6 +5,11 @@
This file gives a brief overview of the major changes between each OpenSSL
release. For more details please read the CHANGES file.
+ Major changes between OpenSSL 1.0.2m and OpenSSL 1.0.2n [7 Dec 2017]
+
+ o Read/write after SSL object in error state (CVE-2017-3737)
+ o rsaz_1024_mul_avx2 overflow bug on x86_64 (CVE-2017-3738)
+
Major changes between OpenSSL 1.0.2l and OpenSSL 1.0.2m [2 Nov 2017]
o bn_sqrx8x_internal carry bug on x86_64 (CVE-2017-3736)
diff --git a/deps/openssl/openssl/README b/deps/openssl/openssl/README
index b5aae6260ce889..80de6886a766b0 100644
--- a/deps/openssl/openssl/README
+++ b/deps/openssl/openssl/README
@@ -1,5 +1,5 @@
- OpenSSL 1.0.2m 2 Nov 2017
+ OpenSSL 1.0.2n 7 Dec 2017
Copyright (c) 1998-2015 The OpenSSL Project
Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson
diff --git a/deps/openssl/openssl/apps/apps.c b/deps/openssl/openssl/apps/apps.c
index c487bd92db2a63..29de1b75dd600b 100644
--- a/deps/openssl/openssl/apps/apps.c
+++ b/deps/openssl/openssl/apps/apps.c
@@ -148,6 +148,10 @@
#ifdef _WIN32
static int WIN32_rename(const char *from, const char *to);
# define rename(from,to) WIN32_rename((from),(to))
+# ifdef fileno
+# undef fileno
+# endif
+# define fileno(a) (int)_fileno(a)
#endif
typedef struct {
@@ -2788,13 +2792,13 @@ unsigned char *next_protos_parse(unsigned short *outlen, const char *in)
OPENSSL_free(out);
return NULL;
}
- out[start] = i - start;
+ out[start] = (unsigned char)(i - start);
start = i + 1;
} else
out[i + 1] = in[i];
}
- *outlen = len + 1;
+ *outlen = (unsigned char)(len + 1);
return out;
}
#endif /* ndef OPENSSL_NO_TLSEXT */
diff --git a/deps/openssl/openssl/apps/dsa.c b/deps/openssl/openssl/apps/dsa.c
index 4ed21d891e8698..82a870eb291004 100644
--- a/deps/openssl/openssl/apps/dsa.c
+++ b/deps/openssl/openssl/apps/dsa.c
@@ -327,6 +327,9 @@ int MAIN(int argc, char **argv)
} else if (outformat == FORMAT_MSBLOB || outformat == FORMAT_PVK) {
EVP_PKEY *pk;
pk = EVP_PKEY_new();
+ if (pk == NULL)
+ goto end;
+
EVP_PKEY_set1_DSA(pk, dsa);
if (outformat == FORMAT_PVK)
i = i2b_PVK_bio(out, pk, pvk_encr, 0, passout);
diff --git a/deps/openssl/openssl/apps/s_client.c b/deps/openssl/openssl/apps/s_client.c
index 19914a19dae818..8f33a8d884614e 100644
--- a/deps/openssl/openssl/apps/s_client.c
+++ b/deps/openssl/openssl/apps/s_client.c
@@ -642,10 +642,11 @@ static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type,
unsigned char ext_buf[4 + 65536];
/* Reconstruct the type/len fields prior to extension data */
- ext_buf[0] = ext_type >> 8;
- ext_buf[1] = ext_type & 0xFF;
- ext_buf[2] = inlen >> 8;
- ext_buf[3] = inlen & 0xFF;
+ inlen &= 0xffff; /* for formal memcpy correctness */
+ ext_buf[0] = (unsigned char)(ext_type >> 8);
+ ext_buf[1] = (unsigned char)(ext_type);
+ ext_buf[2] = (unsigned char)(inlen >> 8);
+ ext_buf[3] = (unsigned char)(inlen);
memcpy(ext_buf + 4, in, inlen);
BIO_snprintf(pem_name, sizeof(pem_name), "SERVERINFO FOR EXTENSION %d",
diff --git a/deps/openssl/openssl/apps/speed.c b/deps/openssl/openssl/apps/speed.c
index 5259c16f1218a3..5383678b98644f 100644
--- a/deps/openssl/openssl/apps/speed.c
+++ b/deps/openssl/openssl/apps/speed.c
@@ -2829,8 +2829,8 @@ static void multiblock_speed(const EVP_CIPHER *evp_cipher)
RAND_bytes(out, 16);
len += 16;
- aad[11] = len >> 8;
- aad[12] = len;
+ aad[11] = (unsigned char)(len >> 8);
+ aad[12] = (unsigned char)(len);
pad = EVP_CIPHER_CTX_ctrl(&ctx,
EVP_CTRL_AEAD_TLS1_AAD,
EVP_AEAD_TLS1_AAD_LEN, aad);
diff --git a/deps/openssl/openssl/crypto/aes/asm/aes-armv4.pl b/deps/openssl/openssl/crypto/aes/asm/aes-armv4.pl
index 4f8917089f6c2a..c1b5e352d76ff4 100644
--- a/deps/openssl/openssl/crypto/aes/asm/aes-armv4.pl
+++ b/deps/openssl/openssl/crypto/aes/asm/aes-armv4.pl
@@ -184,7 +184,7 @@
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_encrypt
#else
- adr r3,AES_encrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov $rounds,r0 @ inp
@@ -430,7 +430,7 @@
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_set_encrypt_key
#else
- adr r3,private_AES_set_encrypt_key
+ adr r3,.
#endif
teq r0,#0
#if __ARM_ARCH__>=7
@@ -952,7 +952,7 @@
#if __ARM_ARCH__<7
sub r3,pc,#8 @ AES_decrypt
#else
- adr r3,AES_decrypt
+ adr r3,.
#endif
stmdb sp!,{r1,r4-r12,lr}
mov $rounds,r0 @ inp
diff --git a/deps/openssl/openssl/crypto/aes/asm/bsaes-armv7.pl b/deps/openssl/openssl/crypto/aes/asm/bsaes-armv7.pl
index 70b3f9656f4fa7..ec66b0502a6459 100644
--- a/deps/openssl/openssl/crypto/aes/asm/bsaes-armv7.pl
+++ b/deps/openssl/openssl/crypto/aes/asm/bsaes-armv7.pl
@@ -724,7 +724,7 @@ sub bitslice {
.type _bsaes_decrypt8,%function
.align 4
_bsaes_decrypt8:
- adr $const,_bsaes_decrypt8
+ adr $const,.
vldmia $key!, {@XMM[9]} @ round 0 key
add $const,$const,#.LM0ISR-_bsaes_decrypt8
@@ -819,7 +819,7 @@ sub bitslice {
.type _bsaes_encrypt8,%function
.align 4
_bsaes_encrypt8:
- adr $const,_bsaes_encrypt8
+ adr $const,.
vldmia $key!, {@XMM[9]} @ round 0 key
sub $const,$const,#_bsaes_encrypt8-.LM0SR
@@ -923,7 +923,7 @@ sub bitslice_key {
.type _bsaes_key_convert,%function
.align 4
_bsaes_key_convert:
- adr $const,_bsaes_key_convert
+ adr $const,.
vld1.8 {@XMM[7]}, [$inp]! @ load round 0 key
sub $const,$const,#_bsaes_key_convert-.LM0
vld1.8 {@XMM[15]}, [$inp]! @ load round 1 key
diff --git a/deps/openssl/openssl/crypto/asn1/a_i2d_fp.c b/deps/openssl/openssl/crypto/asn1/a_i2d_fp.c
index 0f56cd4e07451b..2e85e041e4cf40 100644
--- a/deps/openssl/openssl/crypto/asn1/a_i2d_fp.c
+++ b/deps/openssl/openssl/crypto/asn1/a_i2d_fp.c
@@ -87,6 +87,9 @@ int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, unsigned char *x)
int i, j = 0, n, ret = 1;
n = i2d(x, NULL);
+ if (n <= 0)
+ return 0;
+
b = (char *)OPENSSL_malloc(n);
if (b == NULL) {
ASN1err(ASN1_F_ASN1_I2D_BIO, ERR_R_MALLOC_FAILURE);
diff --git a/deps/openssl/openssl/crypto/bio/b_print.c b/deps/openssl/openssl/crypto/bio/b_print.c
index eb3ab759349cde..1c82f53d5a074d 100644
--- a/deps/openssl/openssl/crypto/bio/b_print.c
+++ b/deps/openssl/openssl/crypto/bio/b_print.c
@@ -385,7 +385,7 @@ _dopr(char **sbuffer,
if (cflags == DP_C_SHORT) {
short int *num;
num = va_arg(args, short int *);
- *num = currlen;
+ *num = (short int)currlen;
} else if (cflags == DP_C_LONG) { /* XXX */
long int *num;
num = va_arg(args, long int *);
@@ -502,7 +502,7 @@ fmtint(char **sbuffer,
if (!(flags & DP_F_UNSIGNED)) {
if (value < 0) {
signvalue = '-';
- uvalue = -(unsigned LLONG)value;
+ uvalue = 0 - (unsigned LLONG)value;
} else if (flags & DP_F_PLUS)
signvalue = '+';
else if (flags & DP_F_SPACE)
diff --git a/deps/openssl/openssl/crypto/bn/asm/rsaz-avx2.pl b/deps/openssl/openssl/crypto/bn/asm/rsaz-avx2.pl
index 712a77fe8ca3ab..2b3f8b0e21ecef 100755
--- a/deps/openssl/openssl/crypto/bn/asm/rsaz-avx2.pl
+++ b/deps/openssl/openssl/crypto/bn/asm/rsaz-avx2.pl
@@ -239,7 +239,7 @@
vmovdqu 32*8-128($ap), $ACC8
lea 192(%rsp), $tp0 # 64+128=192
- vpbroadcastq .Land_mask(%rip), $AND_MASK
+ vmovdqu .Land_mask(%rip), $AND_MASK
jmp .LOOP_GRANDE_SQR_1024
.align 32
@@ -1070,10 +1070,10 @@
vpmuludq 32*6-128($np),$Yi,$TEMP1
vpaddq $TEMP1,$ACC6,$ACC6
vpmuludq 32*7-128($np),$Yi,$TEMP2
- vpblendd \$3, $ZERO, $ACC9, $ACC9 # correct $ACC3
+ vpblendd \$3, $ZERO, $ACC9, $TEMP1 # correct $ACC3
vpaddq $TEMP2,$ACC7,$ACC7
vpmuludq 32*8-128($np),$Yi,$TEMP0
- vpaddq $ACC9, $ACC3, $ACC3 # correct $ACC3
+ vpaddq $TEMP1, $ACC3, $ACC3 # correct $ACC3
vpaddq $TEMP0,$ACC8,$ACC8
mov %rbx, %rax
@@ -1086,7 +1086,9 @@
vmovdqu -8+32*2-128($ap),$TEMP2
mov $r1, %rax
+ vpblendd \$0xfc, $ZERO, $ACC9, $ACC9 # correct $ACC3
imull $n0, %eax
+ vpaddq $ACC9,$ACC4,$ACC4 # correct $ACC3
and \$0x1fffffff, %eax
imulq 16-128($ap),%rbx
@@ -1322,15 +1324,12 @@
# But as we underutilize resources, it's possible to correct in
# each iteration with marginal performance loss. But then, as
# we do it in each iteration, we can correct less digits, and
-# avoid performance penalties completely. Also note that we
-# correct only three digits out of four. This works because
-# most significant digit is subjected to less additions.
+# avoid performance penalties completely.
$TEMP0 = $ACC9;
$TEMP3 = $Bi;
$TEMP4 = $Yi;
$code.=<<___;
- vpermq \$0, $AND_MASK, $AND_MASK
vpaddq (%rsp), $TEMP1, $ACC0
vpsrlq \$29, $ACC0, $TEMP1
@@ -1763,7 +1762,7 @@
.align 64
.Land_mask:
- .quad 0x1fffffff,0x1fffffff,0x1fffffff,-1
+ .quad 0x1fffffff,0x1fffffff,0x1fffffff,0x1fffffff
.Lscatter_permd:
.long 0,2,4,6,7,7,7,7
.Lgather_permd:
diff --git a/deps/openssl/openssl/crypto/bn/bn_exp.c b/deps/openssl/openssl/crypto/bn/bn_exp.c
index 35facd213a253d..c4b63e44ba369a 100644
--- a/deps/openssl/openssl/crypto/bn/bn_exp.c
+++ b/deps/openssl/openssl/crypto/bn/bn_exp.c
@@ -149,7 +149,7 @@ int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
|| BN_get_flags(a, BN_FLG_CONSTTIME) != 0) {
/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
BNerr(BN_F_BN_EXP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
- return -1;
+ return 0;
}
BN_CTX_start(ctx);
@@ -285,7 +285,7 @@ int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
|| BN_get_flags(m, BN_FLG_CONSTTIME) != 0) {
/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
BNerr(BN_F_BN_MOD_EXP_RECP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
- return -1;
+ return 0;
}
bits = BN_num_bits(p);
@@ -1228,7 +1228,7 @@ int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
|| BN_get_flags(m, BN_FLG_CONSTTIME) != 0) {
/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
BNerr(BN_F_BN_MOD_EXP_MONT_WORD, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
- return -1;
+ return 0;
}
bn_check_top(p);
@@ -1361,7 +1361,7 @@ int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
|| BN_get_flags(m, BN_FLG_CONSTTIME) != 0) {
/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
BNerr(BN_F_BN_MOD_EXP_SIMPLE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
- return -1;
+ return 0;
}
bits = BN_num_bits(p);
diff --git a/deps/openssl/openssl/crypto/dsa/dsa_ameth.c b/deps/openssl/openssl/crypto/dsa/dsa_ameth.c
index aac253095141a3..e22627f85152a1 100644
--- a/deps/openssl/openssl/crypto/dsa/dsa_ameth.c
+++ b/deps/openssl/openssl/crypto/dsa/dsa_ameth.c
@@ -133,6 +133,7 @@ static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
unsigned char *penc = NULL;
int penclen;
ASN1_STRING *str = NULL;
+ ASN1_OBJECT *aobj;
dsa = pkey->pkey.dsa;
if (pkey->save_parameters && dsa->p && dsa->q && dsa->g) {
@@ -159,8 +160,11 @@ static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
goto err;
}
- if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA),
- ptype, str, penc, penclen))
+ aobj = OBJ_nid2obj(EVP_PKEY_DSA);
+ if (aobj == NULL)
+ goto err;
+
+ if (X509_PUBKEY_set0_param(pk, aobj, ptype, str, penc, penclen))
return 1;
err:
diff --git a/deps/openssl/openssl/crypto/engine/eng_fat.c b/deps/openssl/openssl/crypto/engine/eng_fat.c
index 4279dd94b135b7..55d3858bb1c6df 100644
--- a/deps/openssl/openssl/crypto/engine/eng_fat.c
+++ b/deps/openssl/openssl/crypto/engine/eng_fat.c
@@ -167,6 +167,7 @@ int ENGINE_register_complete(ENGINE *e)
#endif
ENGINE_register_RAND(e);
ENGINE_register_pkey_meths(e);
+ ENGINE_register_pkey_asn1_meths(e);
return 1;
}
diff --git a/deps/openssl/openssl/crypto/lhash/lhash.c b/deps/openssl/openssl/crypto/lhash/lhash.c
index f3798872598a44..51bb258e74b8ac 100644
--- a/deps/openssl/openssl/crypto/lhash/lhash.c
+++ b/deps/openssl/openssl/crypto/lhash/lhash.c
@@ -107,7 +107,7 @@
* https://en.wikipedia.org/wiki/Linear_hashing
*
* Litwin, Witold (1980), "Linear hashing: A new tool for file and table
- * addressing", Proc. 6th Conference on Very Large Databases: 212–223
+ * addressing", Proc. 6th Conference on Very Large Databases: 212-223
* http://hackthology.com/pdfs/Litwin-1980-Linear_Hashing.pdf
*
* From the wikipedia article "Linear hashing is used in the BDB Berkeley
diff --git a/deps/openssl/openssl/crypto/opensslv.h b/deps/openssl/openssl/crypto/opensslv.h
index c944d562dae0a9..baee2d0865fd1d 100644
--- a/deps/openssl/openssl/crypto/opensslv.h
+++ b/deps/openssl/openssl/crypto/opensslv.h
@@ -30,11 +30,11 @@ extern "C" {
* (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for
* major minor fix final patch/beta)
*/
-# define OPENSSL_VERSION_NUMBER 0x100020dfL
+# define OPENSSL_VERSION_NUMBER 0x100020efL
# ifdef OPENSSL_FIPS
-# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2m-fips 2 Nov 2017"
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2n-fips 7 Dec 2017"
# else
-# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2m 2 Nov 2017"
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2n 7 Dec 2017"
# endif
# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
diff --git a/deps/openssl/openssl/crypto/rsa/rsa_gen.c b/deps/openssl/openssl/crypto/rsa/rsa_gen.c
index 082c8da2efc2a7..a85493d6097b8e 100644
--- a/deps/openssl/openssl/crypto/rsa/rsa_gen.c
+++ b/deps/openssl/openssl/crypto/rsa/rsa_gen.c
@@ -110,6 +110,16 @@ static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value,
int bitsp, bitsq, ok = -1, n = 0;
BN_CTX *ctx = NULL;
+ /*
+ * When generating ridiculously small keys, we can get stuck
+ * continually regenerating the same prime values.
+ */
+ if (bits < 16) {
+ ok = 0; /* we set our own err */
+ RSAerr(RSA_F_RSA_BUILTIN_KEYGEN, RSA_R_KEY_SIZE_TOO_SMALL);
+ goto err;
+ }
+
ctx = BN_CTX_new();
if (ctx == NULL)
goto err;
@@ -161,21 +171,10 @@ static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value,
if (!BN_GENCB_call(cb, 3, 0))
goto err;
for (;;) {
- /*
- * When generating ridiculously small keys, we can get stuck
- * continually regenerating the same prime values. Check for this and
- * bail if it happens 3 times.
- */
- unsigned int degenerate = 0;
do {
if (!BN_generate_prime_ex(rsa->q, bitsq, 0, NULL, NULL, cb))
goto err;
- } while ((BN_cmp(rsa->p, rsa->q) == 0) && (++degenerate < 3));
- if (degenerate == 3) {
- ok = 0; /* we set our own err */
- RSAerr(RSA_F_RSA_BUILTIN_KEYGEN, RSA_R_KEY_SIZE_TOO_SMALL);
- goto err;
- }
+ } while (BN_cmp(rsa->p, rsa->q) == 0);
if (!BN_sub(r2, rsa->q, BN_value_one()))
goto err;
if (!BN_gcd(r1, r2, rsa->e, ctx))
diff --git a/deps/openssl/openssl/crypto/sha/asm/sha256-armv4.pl b/deps/openssl/openssl/crypto/sha/asm/sha256-armv4.pl
index 4fee74d832d100..750216eb426704 100644
--- a/deps/openssl/openssl/crypto/sha/asm/sha256-armv4.pl
+++ b/deps/openssl/openssl/crypto/sha/asm/sha256-armv4.pl
@@ -205,7 +205,7 @@ sub BODY_16_XX {
#if __ARM_ARCH__<7
sub r3,pc,#8 @ sha256_block_data_order
#else
- adr r3,sha256_block_data_order
+ adr r3,.
#endif
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
ldr r12,.LOPENSSL_armcap
diff --git a/deps/openssl/openssl/crypto/symhacks.h b/deps/openssl/openssl/crypto/symhacks.h
index 239fa4fb1b77e7..3001957988f7d5 100644
--- a/deps/openssl/openssl/crypto/symhacks.h
+++ b/deps/openssl/openssl/crypto/symhacks.h
@@ -280,6 +280,8 @@
# define OPENSSL_add_all_algorithms_conf OPENSSL_add_all_algo_conf
# undef EVP_PKEY_meth_set_verify_recover
# define EVP_PKEY_meth_set_verify_recover EVP_PKEY_meth_set_vrfy_recover
+# undef EVP_PKEY_meth_get_verify_recover
+# define EVP_PKEY_meth_get_verify_recover EVP_PKEY_meth_get_vrfy_recover
/* Hack some long EC names */
# undef EC_GROUP_set_point_conversion_form
diff --git a/deps/openssl/openssl/crypto/x509v3/v3_lib.c b/deps/openssl/openssl/crypto/x509v3/v3_lib.c
index 8350429aafbe31..1112802483a1d6 100644
--- a/deps/openssl/openssl/crypto/x509v3/v3_lib.c
+++ b/deps/openssl/openssl/crypto/x509v3/v3_lib.c
@@ -286,9 +286,9 @@ void *X509V3_get_d2i(STACK_OF(X509_EXTENSION) *x, int nid, int *crit,
int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value,
int crit, unsigned long flags)
{
- int extidx = -1;
- int errcode;
- X509_EXTENSION *ext, *extmp;
+ int errcode, extidx = -1;
+ X509_EXTENSION *ext = NULL, *extmp;
+ STACK_OF(X509_EXTENSION) *ret = NULL;
unsigned long ext_op = flags & X509V3_ADD_OP_MASK;
/*
@@ -347,13 +347,21 @@ int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value,
return 1;
}
- if (!*x && !(*x = sk_X509_EXTENSION_new_null()))
- return -1;
- if (!sk_X509_EXTENSION_push(*x, ext))
- return -1;
+ if ((ret = *x) == NULL
+ && (ret = sk_X509_EXTENSION_new_null()) == NULL)
+ goto m_fail;
+ if (!sk_X509_EXTENSION_push(ret, ext))
+ goto m_fail;
+ *x = ret;
return 1;
+ m_fail:
+ if (ret != *x)
+ sk_X509_EXTENSION_free(ret);
+ X509_EXTENSION_free(ext);
+ return -1;
+
err:
if (!(flags & X509V3_ADD_SILENT))
X509V3err(X509V3_F_X509V3_ADD1_I2D, errcode);
diff --git a/deps/openssl/openssl/crypto/x509v3/v3_scts.c b/deps/openssl/openssl/crypto/x509v3/v3_scts.c
index 0b7c68180e7883..87a6ae1da9822b 100644
--- a/deps/openssl/openssl/crypto/x509v3/v3_scts.c
+++ b/deps/openssl/openssl/crypto/x509v3/v3_scts.c
@@ -156,7 +156,7 @@ static void timestamp_print(BIO *out, SCT_TIMESTAMP timestamp)
gen = ASN1_GENERALIZEDTIME_new();
ASN1_GENERALIZEDTIME_adj(gen, (time_t)0,
(int)(timestamp / 86400000),
- (timestamp % 86400000) / 1000);
+ (int)(timestamp % 86400000) / 1000);
/*
* Note GeneralizedTime from ASN1_GENERALIZETIME_adj is always 15
* characters long with a final Z. Update it with fractional seconds.
diff --git a/deps/openssl/openssl/doc/crypto/EVP_EncryptInit.pod b/deps/openssl/openssl/doc/crypto/EVP_EncryptInit.pod
index dc9a2d76c5f60c..4cd24d7e0169f6 100644
--- a/deps/openssl/openssl/doc/crypto/EVP_EncryptInit.pod
+++ b/deps/openssl/openssl/doc/crypto/EVP_EncryptInit.pod
@@ -40,14 +40,14 @@ EVP_aes_128_cbc_hmac_sha256, EVP_aes_256_cbc_hmac_sha256
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, unsigned char *key, unsigned char *iv);
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
- int *outl, unsigned char *in, int inl);
+ int *outl, const unsigned char *in, int inl);
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl);
int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, unsigned char *key, unsigned char *iv);
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
- int *outl, unsigned char *in, int inl);
+ int *outl, const unsigned char *in, int inl);
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,
int *outl);
diff --git a/deps/openssl/openssl/include/openssl/opensslv.h b/deps/openssl/openssl/include/openssl/opensslv.h
index c944d562dae0a9..baee2d0865fd1d 100644
--- a/deps/openssl/openssl/include/openssl/opensslv.h
+++ b/deps/openssl/openssl/include/openssl/opensslv.h
@@ -30,11 +30,11 @@ extern "C" {
* (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for
* major minor fix final patch/beta)
*/
-# define OPENSSL_VERSION_NUMBER 0x100020dfL
+# define OPENSSL_VERSION_NUMBER 0x100020efL
# ifdef OPENSSL_FIPS
-# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2m-fips 2 Nov 2017"
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2n-fips 7 Dec 2017"
# else
-# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2m 2 Nov 2017"
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2n 7 Dec 2017"
# endif
# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
diff --git a/deps/openssl/openssl/include/openssl/ssl.h b/deps/openssl/openssl/include/openssl/ssl.h
index 90aeb0ce4e1ead..3cf96a239bab44 100644
--- a/deps/openssl/openssl/include/openssl/ssl.h
+++ b/deps/openssl/openssl/include/openssl/ssl.h
@@ -1727,7 +1727,7 @@ extern "C" {
# define SSL_ST_BEFORE 0x4000
# define SSL_ST_OK 0x03
# define SSL_ST_RENEGOTIATE (0x04|SSL_ST_INIT)
-# define SSL_ST_ERR 0x05
+# define SSL_ST_ERR (0x05|SSL_ST_INIT)
# define SSL_CB_LOOP 0x01
# define SSL_CB_EXIT 0x02
diff --git a/deps/openssl/openssl/include/openssl/symhacks.h b/deps/openssl/openssl/include/openssl/symhacks.h
index 239fa4fb1b77e7..3001957988f7d5 100644
--- a/deps/openssl/openssl/include/openssl/symhacks.h
+++ b/deps/openssl/openssl/include/openssl/symhacks.h
@@ -280,6 +280,8 @@
# define OPENSSL_add_all_algorithms_conf OPENSSL_add_all_algo_conf
# undef EVP_PKEY_meth_set_verify_recover
# define EVP_PKEY_meth_set_verify_recover EVP_PKEY_meth_set_vrfy_recover
+# undef EVP_PKEY_meth_get_verify_recover
+# define EVP_PKEY_meth_get_verify_recover EVP_PKEY_meth_get_vrfy_recover
/* Hack some long EC names */
# undef EC_GROUP_set_point_conversion_form
diff --git a/deps/openssl/openssl/openssl.spec b/deps/openssl/openssl/openssl.spec
index 18e23acc3b8912..b5cb87ecec40c8 100644
--- a/deps/openssl/openssl/openssl.spec
+++ b/deps/openssl/openssl/openssl.spec
@@ -7,7 +7,7 @@ Release: 1
Summary: Secure Sockets Layer and cryptography libraries and tools
Name: openssl
-Version: 1.0.2m
+Version: 1.0.2n
Source0: ftp://ftp.openssl.org/source/%{name}-%{version}.tar.gz
License: OpenSSL
Group: System Environment/Libraries
diff --git a/deps/openssl/openssl/ssl/Makefile b/deps/openssl/openssl/ssl/Makefile
index dd1296225006ef..7866a3ccd77bab 100644
--- a/deps/openssl/openssl/ssl/Makefile
+++ b/deps/openssl/openssl/ssl/Makefile
@@ -15,7 +15,8 @@ KRB5_INCLUDES=
CFLAGS= $(INCLUDES) $(CFLAG)
GENERAL=Makefile README ssl-lib.com install.com
-TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c dtlstest.c bad_dtls_test.c
+TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c dtlstest.c \
+ bad_dtls_test.c fatalerrtest.c
APPS=
LIB=$(TOP)/libssl.a
diff --git a/deps/openssl/openssl/ssl/bad_dtls_test.c b/deps/openssl/openssl/ssl/bad_dtls_test.c
index 70d8578b588317..34af37d9a9f409 100644
--- a/deps/openssl/openssl/ssl/bad_dtls_test.c
+++ b/deps/openssl/openssl/ssl/bad_dtls_test.c
@@ -590,13 +590,13 @@ static int send_record(BIO *rbio, unsigned char type, unsigned long seqnr,
unsigned char *enc;
#ifdef SIXTY_FOUR_BIT_LONG
- seq[0] = (seqnr >> 40) & 0xff;
- seq[1] = (seqnr >> 32) & 0xff;
+ seq[0] = (unsigned char)(seqnr >> 40);
+ seq[1] = (unsigned char)(seqnr >> 32);
#endif
- seq[2] = (seqnr >> 24) & 0xff;
- seq[3] = (seqnr >> 16) & 0xff;
- seq[4] = (seqnr >> 8) & 0xff;
- seq[5] = seqnr & 0xff;
+ seq[2] = (unsigned char)(seqnr >> 24);
+ seq[3] = (unsigned char)(seqnr >> 16);
+ seq[4] = (unsigned char)(seqnr >> 8);
+ seq[5] = (unsigned char)(seqnr);
pad = 15 - ((len + SHA_DIGEST_LENGTH) % 16);
enc = OPENSSL_malloc(len + SHA_DIGEST_LENGTH + 1 + pad);
@@ -612,8 +612,8 @@ static int send_record(BIO *rbio, unsigned char type, unsigned long seqnr,
HMAC_Update(&ctx, seq, 6);
HMAC_Update(&ctx, &type, 1);
HMAC_Update(&ctx, ver, 2); /* Version */
- lenbytes[0] = len >> 8;
- lenbytes[1] = len & 0xff;
+ lenbytes[0] = (unsigned char)(len >> 8);
+ lenbytes[1] = (unsigned char)(len);
HMAC_Update(&ctx, lenbytes, 2); /* Length */
HMAC_Update(&ctx, enc, len); /* Finally the data itself */
HMAC_Final(&ctx, enc + len, NULL);
@@ -637,8 +637,8 @@ static int send_record(BIO *rbio, unsigned char type, unsigned long seqnr,
BIO_write(rbio, ver, 2);
BIO_write(rbio, epoch, 2);
BIO_write(rbio, seq, 6);
- lenbytes[0] = (len + sizeof(iv)) >> 8;
- lenbytes[1] = (len + sizeof(iv)) & 0xff;
+ lenbytes[0] = (unsigned char)((len + sizeof(iv)) >> 8);
+ lenbytes[1] = (unsigned char)(len + sizeof(iv));
BIO_write(rbio, lenbytes, 2);
BIO_write(rbio, iv, sizeof(iv));
diff --git a/deps/openssl/openssl/ssl/fatalerrtest.c b/deps/openssl/openssl/ssl/fatalerrtest.c
new file mode 100644
index 00000000000000..0288c33fa2eb9c
--- /dev/null
+++ b/deps/openssl/openssl/ssl/fatalerrtest.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include
+#include
+#include "ssltestlib.h"
+
+int main(int argc, char *argv[])
+{
+ SSL_CTX *sctx, *cctx;
+ SSL *sssl, *cssl;
+ const char *msg = "Dummy";
+ BIO *err = NULL, *wbio = NULL;
+ int ret = 1, len;
+ char buf[80];
+ unsigned char dummyrec[] = {
+ 0x17, 0x03, 0x03, 0x00, 0x05, 'D', 'u', 'm', 'm', 'y'
+ };
+
+ if (argc != 3) {
+ printf("Incorrect number of parameters\n");
+ return 1;
+ }
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+ CRYPTO_malloc_debug_init();
+ CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+
+ if (!create_ssl_ctx_pair(SSLv23_method(), SSLv23_method(), &sctx, &cctx,
+ argv[1], argv[2])) {
+ printf("Failed to create SSL_CTX pair\n");
+ goto err;
+ }
+
+ /*
+ * Deliberately set the cipher lists for client and server to be different
+ * to force a handshake failure.
+ */
+ if (!SSL_CTX_set_cipher_list(sctx, "AES128-SHA")
+ || !SSL_CTX_set_cipher_list(cctx, "AES256-SHA")) {
+ printf("Failed to set cipher lists\n");
+ goto err;
+ }
+
+ if (!create_ssl_objects(sctx, cctx, &sssl, &cssl, NULL, NULL)) {
+ printf("Failed to create SSL objectx\n");
+ goto err;
+ }
+
+ wbio = SSL_get_wbio(cssl);
+ if (wbio == NULL) {
+ printf("Unexpected NULL bio received\n");
+ goto err;
+ }
+
+ if (create_ssl_connection(sssl, cssl)) {
+ printf("Unexpected success creating a connection\n");
+ goto err;
+ }
+
+ ERR_clear_error();
+
+ /* Inject a plaintext record from client to server */
+ if (BIO_write(wbio, dummyrec, sizeof(dummyrec)) <= 0) {
+ printf("Unexpected failure injecting dummy record\n");
+ goto err;
+ }
+
+ /* SSL_read()/SSL_write should fail because of a previous fatal error */
+ if ((len = SSL_read(sssl, buf, sizeof(buf - 1))) > 0) {
+ buf[len] = '\0';
+ printf("Unexpected success reading data: %s\n", buf);
+ goto err;
+ }
+ if (SSL_write(sssl, msg, strlen(msg)) > 0) {
+ printf("Unexpected success writing data\n");
+ goto err;
+ }
+
+ ret = 0;
+ err:
+ SSL_free(sssl);
+ SSL_free(cssl);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+ ERR_print_errors_fp(stderr);
+
+ if (ret) {
+ printf("Fatal err test: FAILED\n");
+ }
+
+ ERR_free_strings();
+ ERR_remove_thread_state(NULL);
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ CRYPTO_mem_leaks(err);
+ BIO_free(err);
+
+ return ret;
+}
diff --git a/deps/openssl/openssl/ssl/s23_clnt.c b/deps/openssl/openssl/ssl/s23_clnt.c
index 92f41dd549ad64..add8c9916c8feb 100644
--- a/deps/openssl/openssl/ssl/s23_clnt.c
+++ b/deps/openssl/openssl/ssl/s23_clnt.c
@@ -757,10 +757,12 @@ static int ssl23_get_server_hello(SSL *s)
s->version = TLS1_VERSION;
s->method = TLSv1_client_method();
break;
+#ifndef OPENSSL_NO_SSL3
case SSL3_VERSION:
s->version = SSL3_VERSION;
s->method = SSLv3_client_method();
break;
+#endif
}
SSLerr(SSL_F_SSL23_GET_SERVER_HELLO, SSL_R_UNSUPPORTED_PROTOCOL);
ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
diff --git a/deps/openssl/openssl/ssl/s3_pkt.c b/deps/openssl/openssl/ssl/s3_pkt.c
index 04212c51e726d7..b9145684304176 100644
--- a/deps/openssl/openssl/ssl/s3_pkt.c
+++ b/deps/openssl/openssl/ssl/s3_pkt.c
@@ -1324,10 +1324,16 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
}
#ifndef OPENSSL_NO_HEARTBEATS
else if (rr->type == TLS1_RT_HEARTBEAT) {
- tls1_process_heartbeat(s);
+ i = tls1_process_heartbeat(s);
+
+ if (i < 0)
+ return i;
- /* Exit and notify application to read again */
rr->length = 0;
+ if (s->mode & SSL_MODE_AUTO_RETRY)
+ goto start;
+
+ /* Exit and notify application to read again */
s->rwstate = SSL_READING;
BIO_clear_retry_flags(SSL_get_rbio(s));
BIO_set_retry_read(SSL_get_rbio(s));
diff --git a/deps/openssl/openssl/ssl/ssl.h b/deps/openssl/openssl/ssl/ssl.h
index 90aeb0ce4e1ead..3cf96a239bab44 100644
--- a/deps/openssl/openssl/ssl/ssl.h
+++ b/deps/openssl/openssl/ssl/ssl.h
@@ -1727,7 +1727,7 @@ extern "C" {
# define SSL_ST_BEFORE 0x4000
# define SSL_ST_OK 0x03
# define SSL_ST_RENEGOTIATE (0x04|SSL_ST_INIT)
-# define SSL_ST_ERR 0x05
+# define SSL_ST_ERR (0x05|SSL_ST_INIT)
# define SSL_CB_LOOP 0x01
# define SSL_CB_EXIT 0x02
diff --git a/deps/openssl/openssl/ssl/ssltest.c b/deps/openssl/openssl/ssl/ssltest.c
index 2d6141cd954e00..f6a8f195eeb716 100644
--- a/deps/openssl/openssl/ssl/ssltest.c
+++ b/deps/openssl/openssl/ssl/ssltest.c
@@ -423,13 +423,13 @@ static unsigned char *next_protos_parse(unsigned short *outlen,
OPENSSL_free(out);
return NULL;
}
- out[start] = i - start;
+ out[start] = (unsigned char)(i - start);
start = i + 1;
} else
out[i + 1] = in[i];
}
- *outlen = len + 1;
+ *outlen = (unsigned char)(len + 1);
return out;
}
@@ -554,6 +554,7 @@ static int cb_ticket2(SSL* s, unsigned char* key_name, unsigned char *iv, EVP_CI
{
fprintf(stderr, "ticket callback for SNI context should never be called\n");
EXIT(1);
+ return 0;
}
#endif
diff --git a/deps/openssl/openssl/ssl/t1_lib.c b/deps/openssl/openssl/ssl/t1_lib.c
index 6587e8bb685c94..1a4387b78eb93f 100644
--- a/deps/openssl/openssl/ssl/t1_lib.c
+++ b/deps/openssl/openssl/ssl/t1_lib.c
@@ -1916,7 +1916,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf,
s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret);
s2n(3 + len, ret);
s2n(1 + len, ret);
- *ret++ = len;
+ *ret++ = (unsigned char)len;
memcpy(ret, selected, len);
ret += len;
}
diff --git a/deps/openssl/openssl/test/Makefile b/deps/openssl/openssl/test/Makefile
index a324eeb39ac317..a1f7eeb0ddeb3f 100644
--- a/deps/openssl/openssl/test/Makefile
+++ b/deps/openssl/openssl/test/Makefile
@@ -73,6 +73,7 @@ CLIENTHELLOTEST= clienthellotest
BADDTLSTEST= bad_dtls_test
SSLV2CONFTEST = sslv2conftest
DTLSTEST = dtlstest
+FATALERRTEST = fatalerrtest
TESTS= alltests
@@ -87,7 +88,7 @@ EXE= $(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT) $(ECDSATEST)$(EXE_EXT) $(ECDHTEST)
$(ASN1TEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) $(HEARTBEATTEST)$(EXE_EXT) \
$(CONSTTIMETEST)$(EXE_EXT) $(VERIFYEXTRATEST)$(EXE_EXT) \
$(CLIENTHELLOTEST)$(EXE_EXT) $(SSLV2CONFTEST)$(EXE_EXT) $(DTLSTEST)$(EXE_EXT) \
- $(BADDTLSTEST)$(EXE_EXT)
+ $(BADDTLSTEST)$(EXE_EXT) $(FATALERRTEST)$(EXE_EXT)
# $(METHTEST)$(EXE_EXT)
@@ -102,7 +103,7 @@ OBJ= $(BNTEST).o $(ECTEST).o $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \
$(EVPTEST).o $(EVPEXTRATEST).o $(IGETEST).o $(JPAKETEST).o $(ASN1TEST).o $(V3NAMETEST).o \
$(HEARTBEATTEST).o $(CONSTTIMETEST).o $(VERIFYEXTRATEST).o \
$(CLIENTHELLOTEST).o $(SSLV2CONFTEST).o $(DTLSTEST).o ssltestlib.o \
- $(BADDTLSTEST).o
+ $(BADDTLSTEST).o $(FATALERRTEST).o
SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
$(MD2TEST).c $(MD4TEST).c $(MD5TEST).c \
@@ -114,7 +115,7 @@ SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \
$(EVPTEST).c $(EVPEXTRATEST).c $(IGETEST).c $(JPAKETEST).c $(SRPTEST).c $(ASN1TEST).c \
$(V3NAMETEST).c $(HEARTBEATTEST).c $(CONSTTIMETEST).c $(VERIFYEXTRATEST).c \
$(CLIENTHELLOTEST).c $(SSLV2CONFTEST).c $(DTLSTEST).c ssltestlib.c \
- $(BADDTLSTEST).c
+ $(BADDTLSTEST).c $(FATALERRTEST).c
EXHEADER=
HEADER= testutil.h ssltestlib.h $(EXHEADER)
@@ -159,7 +160,7 @@ alltests: \
test_ss test_ca test_engine test_evp test_evp_extra test_ssl test_tsa test_ige \
test_jpake test_srp test_cms test_ocsp test_v3name test_heartbeat \
test_constant_time test_verify_extra test_clienthello test_sslv2conftest \
- test_dtls test_bad_dtls
+ test_dtls test_bad_dtls test_fatalerr
test_evp: $(EVPTEST)$(EXE_EXT) evptests.txt
../util/shlib_wrap.sh ./$(EVPTEST) evptests.txt
@@ -373,6 +374,10 @@ test_bad_dtls: $(BADDTLSTEST)$(EXE_EXT)
@echo $(START) $@
../util/shlib_wrap.sh ./$(BADDTLSTEST)
+test_fatalerr: $(FATALERRTEST)$(EXE_EXT)
+ @echo $(START) $@
+ ../util/shlib_wrap.sh ./$(FATALERRTEST) ../apps/server.pem ../apps/server.pem
+
test_sslv2conftest: $(SSLV2CONFTEST)$(EXE_EXT)
@echo $(START) $@
../util/shlib_wrap.sh ./$(SSLV2CONFTEST)
@@ -561,6 +566,9 @@ $(CLIENTHELLOTEST)$(EXE_EXT): $(CLIENTHELLOTEST).o
$(BADDTLSTEST)$(EXE_EXT): $(BADDTLSTEST).o
@target=$(BADDTLSTEST) $(BUILD_CMD)
+$(FATALERRTEST)$(EXE_EXT): $(FATALERRTEST).o ssltestlib.o $(DLIBSSL) $(DLIBCRYPTO)
+ @target=$(FATALERRTEST); exobj=ssltestlib.o; $(BUILD_CMD)
+
$(SSLV2CONFTEST)$(EXE_EXT): $(SSLV2CONFTEST).o
@target=$(SSLV2CONFTEST) $(BUILD_CMD)
@@ -776,6 +784,25 @@ exptest.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h
exptest.o: ../include/openssl/ossl_typ.h ../include/openssl/rand.h
exptest.o: ../include/openssl/safestack.h ../include/openssl/stack.h
exptest.o: ../include/openssl/symhacks.h exptest.c
+fatalerrtest.o: ../include/openssl/asn1.h ../include/openssl/bio.h
+fatalerrtest.o: ../include/openssl/buffer.h ../include/openssl/comp.h
+fatalerrtest.o: ../include/openssl/crypto.h ../include/openssl/dtls1.h
+fatalerrtest.o: ../include/openssl/e_os2.h ../include/openssl/ec.h
+fatalerrtest.o: ../include/openssl/ecdh.h ../include/openssl/ecdsa.h
+fatalerrtest.o: ../include/openssl/err.h ../include/openssl/evp.h
+fatalerrtest.o: ../include/openssl/hmac.h ../include/openssl/kssl.h
+fatalerrtest.o: ../include/openssl/lhash.h ../include/openssl/obj_mac.h
+fatalerrtest.o: ../include/openssl/objects.h ../include/openssl/opensslconf.h
+fatalerrtest.o: ../include/openssl/opensslv.h ../include/openssl/ossl_typ.h
+fatalerrtest.o: ../include/openssl/pem.h ../include/openssl/pem2.h
+fatalerrtest.o: ../include/openssl/pkcs7.h ../include/openssl/pqueue.h
+fatalerrtest.o: ../include/openssl/safestack.h ../include/openssl/sha.h
+fatalerrtest.o: ../include/openssl/srtp.h ../include/openssl/ssl.h
+fatalerrtest.o: ../include/openssl/ssl2.h ../include/openssl/ssl23.h
+fatalerrtest.o: ../include/openssl/ssl3.h ../include/openssl/stack.h
+fatalerrtest.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
+fatalerrtest.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
+fatalerrtest.o: fatalerrtest.c ssltestlib.h
heartbeat_test.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h
heartbeat_test.o: ../include/openssl/buffer.h ../include/openssl/comp.h
heartbeat_test.o: ../include/openssl/crypto.h ../include/openssl/dsa.h
diff --git a/deps/openssl/openssl/util/copy-if-different.pl b/deps/openssl/openssl/util/copy-if-different.pl
index e1245f54aff120..5420f3f2bd3041 100644
--- a/deps/openssl/openssl/util/copy-if-different.pl
+++ b/deps/openssl/openssl/util/copy-if-different.pl
@@ -12,7 +12,8 @@
foreach my $arg (@ARGV) {
$arg =~ s|\\|/|g; # compensate for bug/feature in cygwin glob...
- foreach (glob qq("$arg"))
+ $arg = qq("$arg") if ($arg =~ /\s/); # compensate for bug in 5.10...
+ foreach (glob $arg)
{
push @filelist, $_;
}
diff --git a/deps/openssl/openssl/util/copy.pl b/deps/openssl/openssl/util/copy.pl
index a6b2a54ec6f06a..9c0e68c41467ab 100644
--- a/deps/openssl/openssl/util/copy.pl
+++ b/deps/openssl/openssl/util/copy.pl
@@ -19,7 +19,8 @@
next;
}
$arg =~ s|\\|/|g; # compensate for bug/feature in cygwin glob...
- foreach (glob qq("$arg"))
+ $arg = qq("$arg") if ($arg =~ /\s/); # compensate for bug in 5.10...
+ foreach (glob $arg)
{
push @filelist, $_;
}
diff --git a/deps/openssl/openssl/util/libeay.num b/deps/openssl/openssl/util/libeay.num
index fddfe1cbb2024f..f5b4f215098ed5 100755
--- a/deps/openssl/openssl/util/libeay.num
+++ b/deps/openssl/openssl/util/libeay.num
@@ -4417,7 +4417,8 @@ EC_GROUP_get_mont_data 4772 EXIST::FUNCTION:EC
i2d_re_X509_tbs 4773 EXIST::FUNCTION:
EVP_PKEY_asn1_set_item 4774 EXIST::FUNCTION:
EVP_PKEY_meth_get_init 4775 EXIST::FUNCTION:
-EVP_PKEY_meth_get_verify_recover 4776 EXIST::FUNCTION:
+EVP_PKEY_meth_get_verify_recover 4776 EXIST:!VMS:FUNCTION:
+EVP_PKEY_meth_get_vrfy_recover 4776 EXIST:VMS:FUNCTION:
EVP_PKEY_meth_get_keygen 4777 EXIST::FUNCTION:
EVP_PKEY_meth_get_derive 4778 EXIST::FUNCTION:
EVP_PKEY_meth_get_verifyctx 4779 EXIST::FUNCTION:
diff --git a/deps/openssl/openssl/util/mk1mf.pl b/deps/openssl/openssl/util/mk1mf.pl
index 6b31496ed1e694..ee14dafea6e0ec 100755
--- a/deps/openssl/openssl/util/mk1mf.pl
+++ b/deps/openssl/openssl/util/mk1mf.pl
@@ -765,7 +765,7 @@ sub fix_asm
{
$t=&bname($_);
$tt="\$(OBJ_D)${o}$t${obj}";
- $tt.=" \$(OBJ_D)${o}ssltestlib${obj}" if $t eq "dtlstest";
+ $tt.=" \$(OBJ_D)${o}ssltestlib${obj}" if $t eq "dtlstest" or $t eq "fatalerrtest";
$rules.=&do_link_rule("\$(TEST_D)$o$t$exep",$tt,"\$(LIBS_DEP)","\$(L_LIBS) \$(EX_LIBS)");
}
diff --git a/doc/api/buffer.md b/doc/api/buffer.md
index 0dc86f1c0080fc..4a54a25bfc990e 100644
--- a/doc/api/buffer.md
+++ b/doc/api/buffer.md
@@ -510,6 +510,11 @@ console.log(buf2.toString());
### Class Method: Buffer.alloc(size[, fill[, encoding]])
* `size` {integer} The desired length of the new `Buffer`.
@@ -1253,6 +1258,19 @@ Example: Fill a `Buffer` with a two-byte character
console.log(Buffer.allocUnsafe(3).fill('\u0222'));
```
+If `value` contains invalid characters, it is truncated; if no valid
+fill data remains, no filling is performed:
+
+```js
+const buf = Buffer.allocUnsafe(5);
+// Prints:
+console.log(buf.fill('a'));
+// Prints:
+console.log(buf.fill('aazz', 'hex'));
+// Prints:
+console.log(buf.fill('zz', 'hex'));
+```
+
### buf.includes(value[, byteOffset][, encoding])
+
+* `payload` {Buffer|TypedArray|DataView} Optional ping payload.
+* `callback` {Function}
+* Returns: {boolean}
+
+Sends a `PING` frame to the connected HTTP/2 peer. A `callback` function must
+be provided. The method will return `true` if the `PING` was sent, `false`
+otherwise.
+
+The maximum number of outstanding (unacknowledged) pings is determined by the
+`maxOutstandingPings` configuration option. The default maximum is 10.
+
+If provided, the `payload` must be a `Buffer`, `TypedArray`, or `DataView`
+containing 8 bytes of data that will be transmitted with the `PING` and
+returned with the ping acknowledgement.
+
+The callback will be invoked with three arguments: an error argument that will
+be `null` if the `PING` was successfully acknowledged, a `duration` argument
+that reports the number of milliseconds elapsed since the ping was sent and the
+acknowledgement was received, and a `Buffer` containing the 8-byte `PING`
+payload.
+
+```js
+session.ping(Buffer.from('abcdefgh'), (err, duration, payload) => {
+ if (!err) {
+ console.log(`Ping acknowledged in ${duration} milliseconds`);
+ console.log(`With payload '${payload.toString()}`);
+ }
+});
+```
+
+If the `payload` argument is not specified, the default payload will be the
+64-bit timestamp (little endian) marking the start of the `PING` duration.
+
#### http2session.remoteSettings
-
-* stream {Http2Stream}
-* code {number} Unsigned 32-bit integer identifying the error code. **Default:**
- `http2.constant.NGHTTP2_NO_ERROR` (`0x00`)
-* Returns: {undefined}
-
-Sends an `RST_STREAM` frame to the connected HTTP/2 peer, causing the given
-`Http2Stream` to be closed on both sides using [error code][] `code`.
-
#### http2session.setTimeout(msecs, callback)
-
-* `stream` {Http2Stream}
-* `options` {Object}
- * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
- the given stream is made the sole direct dependency of the parent, with
- all other existing dependents made a dependent of the given stream. **Default:**
- `false`
- * `parent` {number} Specifies the numeric identifier of a stream the given
- stream is dependent on.
- * `weight` {number} Specifies the relative dependency of a stream in relation
- to other streams with the same `parent`. The value is a number between `1`
- and `256` (inclusive).
- * `silent` {boolean} When `true`, changes the priority locally without
- sending a `PRIORITY` frame to the connected peer.
-* Returns: {undefined}
-
-Updates the priority for the given `Http2Stream` instance.
-
#### http2session.settings(settings)
+
+The `'close'` event is emitted when the `Http2Stream` is destroyed. Once
+this event is emitted, the `Http2Stream` instance is no longer usable.
+
+The listener callback is passed a single argument specifying the HTTP/2 error
+code specified when closing the stream. If the code is any value other than
+`NGHTTP2_NO_ERROR` (`0`), an `'error'` event will also be emitted.
+
#### Event: 'error'
-
-The `'streamClosed'` event is emitted when the `Http2Stream` is destroyed. Once
-this event is emitted, the `Http2Stream` instance is no longer usable.
-
-The listener callback is passed a single argument specifying the HTTP/2 error
-code specified when closing the stream. If the code is any value other than
-`NGHTTP2_NO_ERROR` (`0`), an `'error'` event will also be emitted.
-
#### Event: 'timeout'
* `options` {Object}
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
+ * `maxHeaderListPairs` {number} Sets the maximum number of header entries.
+ **Default:** `128`. The minimum value is `4`.
+ * `maxOutstandingPings` {number} Sets the maximum number of outstanding,
+ unacknowledged pings. The default is `10`.
* `maxSendHeaderBlockLength` {number} Sets the maximum allowed size for a
serialized, compressed block of headers. Attempts to send headers that
exceed this limit will result in a `'frameError'` event being emitted
@@ -1525,6 +1540,15 @@ server.listen(80);
### http2.createSecureServer(options[, onRequestHandler])
* `options` {Object}
@@ -1533,6 +1557,10 @@ added: v8.4.0
`false`. See the [`'unknownProtocol'`][] event. See [ALPN negotiation][].
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
+ * `maxHeaderListPairs` {number} Sets the maximum number of header entries.
+ **Default:** `128`. The minimum value is `4`.
+ * `maxOutstandingPings` {number} Sets the maximum number of outstanding,
+ unacknowledged pings. The default is `10`.
* `maxSendHeaderBlockLength` {number} Sets the maximum allowed size for a
serialized, compressed block of headers. Attempts to send headers that
exceed this limit will result in a `'frameError'` event being emitted
@@ -1590,12 +1618,25 @@ server.listen(80);
### http2.connect(authority[, options][, listener])
* `authority` {string|URL}
* `options` {Object}
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
+ * `maxHeaderListPairs` {number} Sets the maximum number of header entries.
+ **Default:** `128`. The minimum value is `1`.
+ * `maxOutstandingPings` {number} Sets the maximum number of outstanding,
+ unacknowledged pings. The default is `10`.
* `maxReservedRemoteStreams` {number} Sets the maximum number of reserved push
streams the client will accept at any given time. Once the current number of
currently reserved push streams exceeds reaches this limit, new push streams
@@ -1747,7 +1788,13 @@ server.on('stream', (stream, headers) => {
```
### Settings Object
-
+
The `http2.getDefaultSettings()`, `http2.getPackedSettings()`,
`http2.createServer()`, `http2.createSecureServer()`,
`http2session.settings()`, `http2session.localSettings`, and
@@ -1773,8 +1820,8 @@ properties.
concurrently at any given time in an `Http2Session`. The minimum value is
0. The maximum allowed value is 231-1.
* `maxHeaderListSize` {number} Specifies the maximum size (uncompressed octets)
- of header list that will be accepted. There is no default value. The minimum
- allowed value is 0. The maximum allowed value is 232-1.
+ of header list that will be accepted. The minimum allowed value is 0. The
+ maximum allowed value is 232-1. **Default:** 65535.
All additional properties on the settings object are ignored.
diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md
index 4c4c0ae447f705..82354ee7e7a8b1 100644
--- a/doc/changelogs/CHANGELOG_V8.md
+++ b/doc/changelogs/CHANGELOG_V8.md
@@ -7,6 +7,7 @@
|
+8.9.3
8.9.2
8.9.1
8.9.0
@@ -45,6 +46,46 @@
[Node.js Long Term Support Plan](https://github.com/nodejs/LTS) and
will be supported actively until April 2019 and maintained until December 2019.
+
+## 2017-12-08, Version 8.9.3 'Carbon' (LTS), @MylesBorins
+
+This is a security release. All Node.js users should consult the security release summary at https://nodejs.org/en/blog/vulnerability/december-2017-security-releases/ for details on patched vulnerabilities.
+
+Fixes for the following CVEs are included in this release:
+
+* CVE-2017-15896
+* CVE-2017-15897
+* CVE-2017-3738 (from the openssl project)
+
+### Notable Changes
+
+* **buffer**:
+ * buffer allocated with an invalid content will now be zero filled (Anna Henningsen) [#17428](https://github.com/nodejs/node/pull/17428)
+* **deps**:
+ * openssl updated to 1.0.2n (Shigeki Ohtsu) [#17526](https://github.com/nodejs/node/pull/17526)
+
+### Commits
+
+* [[`b05ef978d3`](https://github.com/nodejs/node/commit/b05ef978d3)] - **buffer**: zero-fill buffer allocated with invalid content (Anna Henningsen) [#17428](https://github.com/nodejs/node/pull/17428)
+* [[`18652b6860`](https://github.com/nodejs/node/commit/18652b6860)] - **deps**: update openssl asm and asm_obsolete files (Shigeki Ohtsu) [#17526](https://github.com/nodejs/node/pull/17526)
+* [[`e6c308e237`](https://github.com/nodejs/node/commit/e6c308e237)] - **deps**: add -no_rand_screen to openssl s_client (Shigeki Ohtsu) [nodejs/io.js#1836](https://github.com/nodejs/io.js/pull/1836)
+* [[`a85f94bd59`](https://github.com/nodejs/node/commit/a85f94bd59)] - **deps**: fix asm build error of openssl in x86_win32 (Shigeki Ohtsu) [iojs/io.js#1389](https://github.com/iojs/io.js/pull/1389)
+* [[`b5552c854c`](https://github.com/nodejs/node/commit/b5552c854c)] - **deps**: fix openssl assembly error on ia32 win32 (Fedor Indutny) [iojs/io.js#1389](https://github.com/iojs/io.js/pull/1389)
+* [[`afad1f23a2`](https://github.com/nodejs/node/commit/afad1f23a2)] - **deps**: copy all openssl header files to include dir (Shigeki Ohtsu) [#17526](https://github.com/nodejs/node/pull/17526)
+* [[`9fdd3bddf5`](https://github.com/nodejs/node/commit/9fdd3bddf5)] - **deps**: upgrade openssl sources to 1.0.2n (Shigeki Ohtsu) [#17526](https://github.com/nodejs/node/pull/17526)
+* [[`db09f245bf`](https://github.com/nodejs/node/commit/db09f245bf)] - **doc**: warn against filling buffer with invalid data (Anna Henningsen) [#17428](https://github.com/nodejs/node/pull/17428)
+* [[`42f09ed461`](https://github.com/nodejs/node/commit/42f09ed461)] - **http2**: use correct connect event for TLS Socket (James M Snell) [#17328](https://github.com/nodejs/node/pull/17328)
+* [[`aba3544b50`](https://github.com/nodejs/node/commit/aba3544b50)] - **http2**: use 'close' event instead of 'streamClosed' (James M Snell) [#17328](https://github.com/nodejs/node/pull/17328)
+* [[`bd035d75bd`](https://github.com/nodejs/node/commit/bd035d75bd)] - **http2**: general cleanups in core.js (James M Snell) [#17209](https://github.com/nodejs/node/pull/17209)
+* [[`a5e3ba2cb3`](https://github.com/nodejs/node/commit/a5e3ba2cb3)] - **http2**: major update to internals (James M Snell) [#17105](https://github.com/nodejs/node/pull/17105)
+* [[`d7f37cebed`](https://github.com/nodejs/node/commit/d7f37cebed)] - **http2**: simplify subsequent rstStream calls (Anatoli Papirovski) [#16753](https://github.com/nodejs/node/pull/16753)
+* [[`22ee960775`](https://github.com/nodejs/node/commit/22ee960775)] - **http2**: refactor multiple internals (James M Snell) [#16676](https://github.com/nodejs/node/pull/16676)
+* [[`319beaf45b`](https://github.com/nodejs/node/commit/319beaf45b)] - **http2**: allocate on every chunk send (James M Snell) [#16669](https://github.com/nodejs/node/pull/16669)
+* [[`7d68488524`](https://github.com/nodejs/node/commit/7d68488524)] - **openssl**: fix keypress requirement in apps on win32 (Shigeki Ohtsu) [iojs/io.js#1389](https://github.com/iojs/io.js/pull/1389)
+* [[`8e8fac29de`](https://github.com/nodejs/node/commit/8e8fac29de)] - **src**: fix -Winconsistent-missing-override warning (Ben Noordhuis) [#16726](https://github.com/nodejs/node/pull/16726)
+* [[`26b43c87ee`](https://github.com/nodejs/node/commit/26b43c87ee)] - **src**: add method to compute storage in WriteWrap (Anna Henningsen) [#16727](https://github.com/nodejs/node/pull/16727)
+* [[`99d775ca07`](https://github.com/nodejs/node/commit/99d775ca07)] - **test**: fix flaky test-http2-create-client-connect (David Benjamin) [#16130](https://github.com/nodejs/node/pull/16130)
+
## 2017-12-05, Version 8.9.2 'Carbon' (LTS), @gibfahn
diff --git a/lib/buffer.js b/lib/buffer.js
index 251b24cceede42..6d28e84bc7ff3d 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -238,7 +238,9 @@ Buffer.alloc = function(size, fill, encoding) {
// be interpreted as a start offset.
if (typeof encoding !== 'string')
encoding = undefined;
- return createUnsafeBuffer(size).fill(fill, encoding);
+ const ret = createUnsafeBuffer(size);
+ if (fill_(ret, fill, encoding) > 0)
+ return ret;
}
return new FastBuffer(size);
};
@@ -796,15 +798,20 @@ Buffer.prototype.includes = function includes(val, byteOffset, encoding) {
// buffer.fill(buffer[, offset[, end]])
// buffer.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill(val, start, end, encoding) {
+ fill_(this, val, start, end, encoding);
+ return this;
+};
+
+function fill_(buf, val, start, end, encoding) {
// Handle string cases:
if (typeof val === 'string') {
if (typeof start === 'string') {
encoding = start;
start = 0;
- end = this.length;
+ end = buf.length;
} else if (typeof end === 'string') {
encoding = end;
- end = this.length;
+ end = buf.length;
}
if (encoding !== undefined && typeof encoding !== 'string') {
@@ -832,19 +839,17 @@ Buffer.prototype.fill = function fill(val, start, end, encoding) {
}
// Invalid ranges are not set to a default, so can range check early.
- if (start < 0 || end > this.length)
+ if (start < 0 || end > buf.length)
throw new RangeError('Out of range index');
if (end <= start)
- return this;
+ return 0;
start = start >>> 0;
- end = end === undefined ? this.length : end >>> 0;
+ end = end === undefined ? buf.length : end >>> 0;
- binding.fill(this, val, start, end, encoding);
-
- return this;
-};
+ return binding.fill(buf, val, start, end, encoding);
+}
Buffer.prototype.write = function(string, offset, length, encoding) {
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 86782b0762c543..0939f8f33354f0 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -204,6 +204,8 @@ E('ERR_HTTP2_OUT_OF_STREAMS',
'No stream ID is available because maximum stream ID has been reached');
E('ERR_HTTP2_PAYLOAD_FORBIDDEN',
(code) => `Responses with ${code} status must not have a payload`);
+E('ERR_HTTP2_PING_CANCEL', 'HTTP2 ping cancelled');
+E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes');
E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers');
E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams');
E('ERR_HTTP2_SEND_FILE', 'Only regular files can be sent');
diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js
index 89a22270086418..5ddde39d387785 100644
--- a/lib/internal/http2/compat.js
+++ b/lib/internal/http2/compat.js
@@ -250,7 +250,7 @@ class Http2ServerRequest extends Readable {
stream.on('close', onStreamClosedRequest);
stream.on('aborted', onStreamAbortedRequest);
const onfinish = this[kFinish].bind(this);
- stream.on('streamClosed', onfinish);
+ stream.on('close', onfinish);
stream.on('finish', onfinish);
this.on('pause', onRequestPause);
this.on('resume', onRequestResume);
@@ -383,7 +383,7 @@ class Http2ServerResponse extends Stream {
stream.on('close', onStreamClosedResponse);
stream.on('aborted', onStreamAbortedResponse);
const onfinish = this[kFinish].bind(this);
- stream.on('streamClosed', onfinish);
+ stream.on('close', onfinish);
stream.on('finish', onfinish);
}
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index a6dbd626ec882f..adfda207abf211 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -4,6 +4,7 @@
require('internal/util').assertCrypto();
+const { async_id_symbol } = process.binding('async_wrap');
const binding = process.binding('http2');
const assert = require('assert');
const { Buffer } = require('buffer');
@@ -22,11 +23,12 @@ const { onServerStream,
} = require('internal/http2/compat');
const { utcDate } = require('internal/http');
const { promisify } = require('internal/util');
-const { isUint8Array } = require('internal/util/types');
+const { isArrayBufferView } = require('internal/util/types');
const { _connectionListener: httpConnectionListener } = require('http');
const { createPromise, promiseResolve } = process.binding('util');
const debug = util.debuglog('http2');
+const kMaxStreams = (2 ** 31) - 1;
const {
assertIsObject,
@@ -53,7 +55,7 @@ const {
unenroll
} = require('timers');
-const { WriteWrap } = process.binding('stream_wrap');
+const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap');
const { constants } = binding;
const NETServer = net.Server;
@@ -77,6 +79,7 @@ const kServer = Symbol('server');
const kSession = Symbol('session');
const kState = Symbol('state');
const kType = Symbol('type');
+const kUpdateTimer = Symbol('update-timer');
const kDefaultSocketTimeout = 2 * 60 * 1000;
const kRenegTest = /TLS session renegotiation disabled for this socket/;
@@ -100,7 +103,6 @@ const {
NGHTTP2_REFUSED_STREAM,
NGHTTP2_SESSION_CLIENT,
NGHTTP2_SESSION_SERVER,
- NGHTTP2_ERR_NOMEM,
NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE,
NGHTTP2_ERR_INVALID_ARGUMENT,
NGHTTP2_ERR_STREAM_CLOSED,
@@ -145,12 +147,12 @@ function emit(self, ...args) {
// create the associated Http2Stream instance and emit the 'stream'
// event. If the stream is not new, emit the 'headers' event to pass
// the block of headers on.
-function onSessionHeaders(id, cat, flags, headers) {
+function onSessionHeaders(handle, id, cat, flags, headers) {
const owner = this[kOwner];
const type = owner[kType];
- _unrefActive(owner);
- debug(`[${sessionName(type)}] headers were received on ` +
- `stream ${id}: ${cat}`);
+ owner[kUpdateTimer]();
+ debug(`Http2Stream ${id} [Http2Session ` +
+ `${sessionName(type)}]: headers received`);
const streams = owner[kState].streams;
const endOfStream = !!(flags & NGHTTP2_FLAG_END_STREAM);
@@ -160,11 +162,10 @@ function onSessionHeaders(id, cat, flags, headers) {
const obj = toHeaderObject(headers);
if (stream === undefined) {
+ const opts = { readable: !endOfStream };
// owner[kType] can be only one of two possible values
if (type === NGHTTP2_SESSION_SERVER) {
- stream = new ServerHttp2Stream(owner, id,
- { readable: !endOfStream },
- obj);
+ stream = new ServerHttp2Stream(owner, handle, id, opts, obj);
if (obj[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) {
// For head requests, there must not be a body...
// end the writable side immediately.
@@ -172,7 +173,7 @@ function onSessionHeaders(id, cat, flags, headers) {
stream[kState].headRequest = true;
}
} else {
- stream = new ClientHttp2Stream(owner, id, { readable: !endOfStream });
+ stream = new ClientHttp2Stream(owner, handle, id, opts);
}
streams.set(id, stream);
process.nextTick(emit, owner, 'stream', stream, obj, flags, headers);
@@ -196,7 +197,8 @@ function onSessionHeaders(id, cat, flags, headers) {
} else {
event = endOfStream ? 'trailers' : 'headers';
}
- debug(`[${sessionName(type)}] emitting stream '${event}' event`);
+ debug(`Http2Stream ${id} [Http2Session ` +
+ `${sessionName(type)}]: emitting stream '${event}' event`);
process.nextTick(emit, stream, event, obj, flags, headers);
}
if (endOfStream) {
@@ -210,16 +212,8 @@ function onSessionHeaders(id, cat, flags, headers) {
// event handler returns, those are sent off for processing. Note that this
// is a necessarily synchronous operation. We need to know immediately if
// there are trailing headers to send.
-function onSessionTrailers(id) {
- const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] checking for trailers`);
- const streams = owner[kState].streams;
- const stream = streams.get(id);
- // It should not be possible for the stream not to exist at this point.
- // If it does not exist, there is something very very wrong.
- assert(stream !== undefined,
- 'Internal HTTP/2 Failure. Stream does not exist. Please ' +
- 'report this as a bug in Node.js');
+function onStreamTrailers() {
+ const stream = this[kOwner];
const trailers = Object.create(null);
stream[kState].getTrailers.call(stream, trailers);
const headersList = mapToHeaders(trailers, assertValidPseudoHeaderTrailer);
@@ -230,28 +224,17 @@ function onSessionTrailers(id) {
return headersList;
}
-// Called when the stream is closed. The streamClosed event is emitted on the
-// Http2Stream instance. Note that this event is distinctly different than the
-// require('stream') interface 'close' event which deals with the state of the
-// Readable and Writable sides of the Duplex.
-function onSessionStreamClose(id, code) {
- const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] session is closing the stream ` +
- `${id}: ${code}`);
- const stream = owner[kState].streams.get(id);
- if (stream === undefined)
- return;
- _unrefActive(owner);
- // Set the rst state for the stream
+// Called when the stream is closed. The close event is emitted on the
+// Http2Stream instance
+function onStreamClose(code) {
+ const stream = this[kOwner];
+ stream[kUpdateTimer]();
abort(stream);
const state = stream[kState];
state.rst = true;
state.rstCode = code;
-
- if (state.fd !== undefined) {
- debug(`Closing fd ${state.fd} for stream ${id}`);
+ if (state.fd !== undefined)
fs.close(state.fd, afterFDClose.bind(stream));
- }
setImmediate(stream.destroy.bind(stream));
}
@@ -264,32 +247,21 @@ function afterFDClose(err) {
// Called when an error event needs to be triggered
function onSessionError(error) {
const owner = this[kOwner];
- _unrefActive(owner);
+ owner[kUpdateTimer]();
process.nextTick(emit, owner, 'error', error);
}
// Receives a chunk of data for a given stream and forwards it on
// to the Http2Stream Duplex for processing.
-function onSessionRead(nread, buf, handle) {
- const owner = this[kOwner];
- const streams = owner[kState].streams;
- const id = handle.id;
- const stream = streams.get(id);
- // It should not be possible for the stream to not exist at this point.
- // If it does not, something is very very wrong
- assert(stream !== undefined,
- 'Internal HTTP/2 Failure. Stream does not exist. Please ' +
- 'report this as a bug in Node.js');
- _unrefActive(owner); // Reset the session timeout timer
- _unrefActive(stream); // Reset the stream timeout timer
+function onStreamRead(nread, buf, handle) {
+ const stream = handle[kOwner];
+ stream[kUpdateTimer]();
if (nread >= 0 && !stream.destroyed) {
- // prevent overflowing the buffer while pause figures out the
- // stream needs to actually pause and streamOnPause runs
- if (!stream.push(buf))
- owner[kHandle].streamReadStop(id);
+ if (!stream.push(buf)) {
+ handle.readStop();
+ }
return;
}
-
// Last chunk was received. End the readable side.
stream.push(null);
}
@@ -298,8 +270,8 @@ function onSessionRead(nread, buf, handle) {
// Resets the cached settings.
function onSettings(ack) {
const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] new settings received`);
- _unrefActive(owner);
+ debug(`Http2Session ${sessionName(owner[kType])}: new settings received`);
+ owner[kUpdateTimer]();
let event = 'remoteSettings';
if (ack) {
if (owner[kState].pendingAck > 0)
@@ -310,11 +282,8 @@ function onSettings(ack) {
owner[kRemoteSettings] = undefined;
}
// Only emit the event if there are listeners registered
- if (owner.listenerCount(event) > 0) {
- const settings = event === 'localSettings' ?
- owner.localSettings : owner.remoteSettings;
- process.nextTick(emit, owner, event, settings);
- }
+ if (owner.listenerCount(event) > 0)
+ process.nextTick(emit, owner, event, owner[event]);
}
// If the stream exists, an attempt will be made to emit an event
@@ -322,10 +291,10 @@ function onSettings(ack) {
// session (which may, in turn, forward it on to the server)
function onPriority(id, parent, weight, exclusive) {
const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] priority advisement for stream ` +
- `${id}: \n parent: ${parent},\n weight: ${weight},\n` +
- ` exclusive: ${exclusive}`);
- _unrefActive(owner);
+ debug(`Http2Stream ${id} [Http2Session ` +
+ `${sessionName(owner[kType])}]: priority [parent: ${parent}, ` +
+ `weight: ${weight}, exclusive: ${exclusive}]`);
+ owner[kUpdateTimer]();
const streams = owner[kState].streams;
const stream = streams.get(id);
const emitter = stream === undefined ? owner : stream;
@@ -344,9 +313,9 @@ function emitFrameError(self, id, type, code) {
// frame. This should be exceedingly rare.
function onFrameError(id, type, code) {
const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] error sending frame type ` +
+ debug(`Http2Session ${sessionName(owner[kType])}: error sending frame type ` +
`${type} on stream ${id}, code: ${code}`);
- _unrefActive(owner);
+ owner[kUpdateTimer]();
const streams = owner[kState].streams;
const stream = streams.get(id);
const emitter = stream !== undefined ? stream : owner;
@@ -369,7 +338,8 @@ function emitGoaway(self, code, lastStreamID, buf) {
// Called by the native layer when a goaway frame has been received
function onGoawayData(code, lastStreamID, buf) {
const owner = this[kOwner];
- debug(`[${sessionName(owner[kType])}] goaway data received`);
+ debug(`Http2Session ${sessionName(owner[kType])}: goaway ${code} received ` +
+ `[last stream id: ${lastStreamID}]`);
process.nextTick(emitGoaway, owner, code, lastStreamID, buf);
}
@@ -379,7 +349,6 @@ function onGoawayData(code, lastStreamID, buf) {
// frameLen <= n <= maxPayloadLen.
function onSelectPadding(fn) {
return function getPadding() {
- debug('fetching padding for frame');
const frameLen = paddingBuffer[PADDING_BUF_FRAME_LENGTH];
const maxFramePayloadLen = paddingBuffer[PADDING_BUF_MAX_PAYLOAD_LENGTH];
paddingBuffer[PADDING_BUF_RETURN_VALUE] =
@@ -395,7 +364,8 @@ function onSelectPadding(fn) {
// will be deferred until the socket is ready to go.
function requestOnConnect(headers, options) {
const session = this[kSession];
- debug(`[${sessionName(session[kType])}] connected.. initializing request`);
+ debug(`Http2Session ${sessionName(session[kType])}: connected, ` +
+ 'initializing request');
const streams = session[kState].streams;
validatePriorityOptions(options);
@@ -417,15 +387,14 @@ function requestOnConnect(headers, options) {
// ret will be either the reserved stream ID (if positive)
// or an error code (if negative)
- const ret = session[kHandle].submitRequest(headersList,
- streamOptions,
- options.parent | 0,
- options.weight | 0,
- !!options.exclusive);
+ const ret = session[kHandle].request(headersList,
+ streamOptions,
+ options.parent | 0,
+ options.weight | 0,
+ !!options.exclusive);
// In an error condition, one of three possible response codes will be
// possible:
- // * NGHTTP2_ERR_NOMEM - Out of memory, this should be fatal to the process.
// * NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE - Maximum stream ID is reached, this
// is fatal for the session
// * NGHTTP2_ERR_INVALID_ARGUMENT - Stream was made dependent on itself, this
@@ -433,31 +402,27 @@ function requestOnConnect(headers, options) {
// For the first two, emit the error on the session,
// For the third, emit the error on the stream, it will bubble up to the
// session if not handled.
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, session, 'error', err);
- break;
- case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
- err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
- process.nextTick(emit, session, 'error', err);
- break;
- case NGHTTP2_ERR_INVALID_ARGUMENT:
- err = new errors.Error('ERR_HTTP2_STREAM_SELF_DEPENDENCY');
- process.nextTick(emit, this, 'error', err);
- break;
- default:
- // Some other, unexpected error was returned. Emit on the session.
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, session, 'error', err);
+ if (typeof ret === 'number') {
+ let err;
+ let target = session;
+ switch (ret) {
+ case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
+ err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
+ target = this;
break;
- }
- debug(`[${sessionName(session[kType])}] stream ${ret} initialized`);
- this[kInit](ret);
- streams.set(ret, this);
+ case NGHTTP2_ERR_INVALID_ARGUMENT:
+ err = new errors.Error('ERR_HTTP2_STREAM_SELF_DEPENDENCY');
+ target = this;
+ break;
+ default:
+ err = new NghttpError(ret);
+ }
+ process.nextTick(emit, target, 'error', err);
+ return;
}
+ const id = ret.id();
+ streams.set(id, this);
+ this[kInit](id, ret);
}
function validatePriorityOptions(options) {
@@ -502,25 +467,29 @@ function validatePriorityOptions(options) {
}
}
+function onSessionInternalError(code) {
+ const owner = this[kOwner];
+ const err = new NghttpError(code);
+ process.nextTick(emit, owner, 'error', err);
+}
+
// Creates the internal binding.Http2Session handle for an Http2Session
// instance. This occurs only after the socket connection has been
// established. Note: the binding.Http2Session will take over ownership
// of the socket. No other code should read from or write to the socket.
function setupHandle(session, socket, type, options) {
return function() {
- debug(`[${sessionName(type)}] setting up session handle`);
+ debug(`Http2Session ${sessionName(type)}: setting up session handle`);
session[kState].connecting = false;
updateOptionsBuffer(options);
const handle = new binding.Http2Session(type);
handle[kOwner] = session;
+ handle.error = onSessionInternalError;
handle.onpriority = onPriority;
handle.onsettings = onSettings;
handle.onheaders = onSessionHeaders;
- handle.ontrailers = onSessionTrailers;
- handle.onstreamclose = onSessionStreamClose;
handle.onerror = onSessionError;
- handle.onread = onSessionRead;
handle.onframeerror = onFrameError;
handle.ongoawaydata = onGoawayData;
@@ -545,137 +514,97 @@ function setupHandle(session, socket, type, options) {
// Submits a SETTINGS frame to be sent to the remote peer.
function submitSettings(settings) {
const type = this[kType];
- debug(`[${sessionName(type)}] submitting actual settings`);
- _unrefActive(this);
+ debug(`Http2Session ${sessionName(type)}: submitting settings`);
+ this[kUpdateTimer]();
this[kLocalSettings] = undefined;
-
updateSettingsBuffer(settings);
- const ret = this[kHandle].submitSettings();
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, this, 'error', err);
- break;
- default:
- // Some other unexpected error was reported.
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- }
- }
- debug(`[${sessionName(type)}] settings complete`);
+ this[kHandle].settings();
}
// Submits a PRIORITY frame to be sent to the remote peer
// Note: If the silent option is true, the change will be made
// locally with no PRIORITY frame sent.
-function submitPriority(stream, options) {
- const type = this[kType];
- debug(`[${sessionName(type)}] submitting actual priority`);
- _unrefActive(this);
-
- const ret = this[kHandle].submitPriority(stream[kID],
- options.parent | 0,
- options.weight | 0,
- !!options.exclusive,
- !!options.silent);
-
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, this, 'error', err);
- break;
- default:
- // Some other unexpected error was reported.
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, stream, 'error', err);
- }
- }
- debug(`[${sessionName(type)}] priority complete`);
+function submitPriority(options) {
+ this[kUpdateTimer]();
+
+ // If the parent is the id, do nothing because a
+ // stream cannot be made to depend on itself.
+ if (options.parent === this[kID])
+ return;
+
+ this[kHandle].priority(options.parent | 0,
+ options.weight | 0,
+ !!options.exclusive,
+ !!options.silent);
}
// Submit an RST-STREAM frame to be sent to the remote peer.
// This will cause the Http2Stream to be closed.
-function submitRstStream(stream, code) {
- const type = this[kType];
- debug(`[${sessionName(type)}] submit actual rststream`);
- _unrefActive(this);
- const ret = this[kHandle].submitRstStream(stream[kID], code);
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, this, 'error', err);
- break;
- default:
- // Some other unexpected error was reported.
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, stream, 'error', err);
- break;
- }
- stream.destroy();
+function submitRstStream(code) {
+ this[kUpdateTimer]();
+
+ const state = this[kState];
+ if (state.rst) return;
+ state.rst = true;
+ state.rstCode = code;
+
+ const ret = this[kHandle].rstStream(code);
+ if (ret < 0) {
+ const err = new NghttpError(ret);
+ process.nextTick(emit, this, 'error', err);
+ return;
}
- debug(`[${sessionName(type)}] rststream complete`);
+ this.destroy();
}
-function doShutdown(self, options) {
- const handle = self[kHandle];
- const state = self[kState];
+function doShutdown(options) {
+ const handle = this[kHandle];
+ const state = this[kState];
if (handle === undefined || state.shutdown)
return; // Nothing to do, possibly because the session shutdown already.
- const ret = handle.submitGoaway(options.errorCode | 0,
- options.lastStreamID | 0,
- options.opaqueData);
+ const ret = handle.goaway(options.errorCode | 0,
+ options.lastStreamID | 0,
+ options.opaqueData);
state.shuttingDown = false;
state.shutdown = true;
if (ret < 0) {
- debug(`[${sessionName(self[kType])}] shutdown failed! code: ${ret}`);
+ debug(`Http2Session ${sessionName(this[kType])}: shutdown failed`);
const err = new NghttpError(ret);
- process.nextTick(emit, self, 'error', err);
+ process.nextTick(emit, this, 'error', err);
return;
}
- process.nextTick(emit, self, 'shutdown', options);
- debug(`[${sessionName(self[kType])}] shutdown is complete`);
+ process.nextTick(emit, this, 'shutdown', options);
}
// Submit a graceful or immediate shutdown request for the Http2Session.
function submitShutdown(options) {
const type = this[kType];
- debug(`[${sessionName(type)}] submitting actual shutdown request`);
- if (type === NGHTTP2_SESSION_SERVER &&
- options.graceful === true) {
+ debug(`Http2Session ${sessionName(type)}: submitting shutdown request`);
+ if (type === NGHTTP2_SESSION_SERVER && options.graceful === true) {
// first send a shutdown notice
- this[kHandle].submitShutdownNotice();
+ this[kHandle].shutdownNotice();
// then, on flip of the event loop, do the actual shutdown
- setImmediate(doShutdown, this, options);
+ setImmediate(doShutdown.bind(this), options);
} else {
- doShutdown(this, options);
+ doShutdown.call(this, options);
}
}
-function finishSessionDestroy(self, socket) {
- const state = self[kState];
-
+function finishSessionDestroy(socket) {
if (!socket.destroyed)
socket.destroy();
+ const state = this[kState];
state.destroying = false;
state.destroyed = true;
// Destroy the handle
- const handle = self[kHandle];
- if (handle !== undefined) {
- handle.destroy(state.skipUnconsume);
- debug(`[${sessionName(self[kType])}] nghttp2session handle destroyed`);
+ if (this[kHandle] !== undefined) {
+ this[kHandle].destroy(state.skipUnconsume);
+ this[kHandle] = undefined;
}
- self[kHandle] = undefined;
- process.nextTick(emit, self, 'close');
- debug(`[${sessionName(self[kType])}] nghttp2session destroyed`);
+ process.nextTick(emit, this, 'close');
}
const proxySocketHandler = {
@@ -720,13 +649,16 @@ const proxySocketHandler = {
}
};
+function pingCallback(cb) {
+ return function(ack, duration, payload) {
+ const err = ack ? null : new errors.Error('ERR_HTTP2_PING_CANCEL');
+ cb(err, duration, payload);
+ };
+}
+
// Upon creation, the Http2Session takes ownership of the socket. The session
// may not be ready to use immediately if the socket is not yet fully connected.
class Http2Session extends EventEmitter {
-
- // type { number } either NGHTTP2_SESSION_SERVER or NGHTTP2_SESSION_CLIENT
- // options { Object }
- // socket { net.Socket | tls.TLSSocket | stream.Duplex }
constructor(type, options, socket) {
super();
@@ -773,7 +705,9 @@ class Http2Session extends EventEmitter {
const setupFn = setupHandle(this, socket, type, options);
if (socket.connecting) {
this[kState].connecting = true;
- socket.once('connect', setupFn);
+ const connectEvent =
+ socket instanceof tls.TLSSocket ? 'secureConnect' : 'connect';
+ socket.once(connectEvent, setupFn);
} else {
setupFn();
}
@@ -785,8 +719,41 @@ class Http2Session extends EventEmitter {
// to something more reasonable because we may have any number
// of concurrent streams (2^31-1 is the upper limit on the number
// of streams)
- this.setMaxListeners((2 ** 31) - 1);
- debug(`[${sessionName(type)}] http2session created`);
+ this.setMaxListeners(kMaxStreams);
+ debug(`Http2Session ${sessionName(type)}: created`);
+ }
+
+ [kUpdateTimer]() {
+ _unrefActive(this);
+ }
+
+ setNextStreamID(id) {
+ if (typeof id !== 'number')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'id', 'number');
+ if (id <= 0 || id > kMaxStreams)
+ throw new errors.RangeError('ERR_OUT_OF_RANGE');
+ this[kHandle].setNextStreamID(id);
+ }
+
+ ping(payload, callback) {
+ const state = this[kState];
+ if (state.destroyed || state.destroying)
+ throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
+ if (typeof payload === 'function') {
+ callback = payload;
+ payload = undefined;
+ }
+ if (payload && !isArrayBufferView(payload)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
+ 'payload',
+ ['Buffer', 'TypedArray', 'DataView']);
+ }
+ if (payload && payload.length !== 8) {
+ throw new errors.RangeError('ERR_HTTP2_PING_LENGTH');
+ }
+ if (typeof callback !== 'function')
+ throw new errors.TypeError('ERR_INVALID_CALLBACK');
+ return this[kHandle].ping(payload, pingCallback(callback));
}
[kInspect](depth, opts) {
@@ -830,9 +797,7 @@ class Http2Session extends EventEmitter {
// Retrieves state information for the Http2Session
get state() {
const handle = this[kHandle];
- return handle !== undefined ?
- getSessionState(handle) :
- Object.create(null);
+ return handle === undefined ? {} : getSessionState(handle);
}
// The settings currently in effect for the local peer. These will
@@ -844,7 +809,7 @@ class Http2Session extends EventEmitter {
const handle = this[kHandle];
if (handle === undefined)
- return Object.create(null);
+ return {};
settings = getSettings(handle, false); // Local
this[kLocalSettings] = settings;
@@ -859,7 +824,7 @@ class Http2Session extends EventEmitter {
const handle = this[kHandle];
if (handle === undefined)
- return Object.create(null);
+ return {};
settings = getSettings(handle, true); // Remote
this[kRemoteSettings] = settings;
@@ -886,7 +851,7 @@ class Http2Session extends EventEmitter {
16384, 2 ** 24 - 1);
assertWithinRange('maxConcurrentStreams',
settings.maxConcurrentStreams,
- 0, 2 ** 31 - 1);
+ 0, kMaxStreams);
assertWithinRange('maxHeaderListSize',
settings.maxHeaderListSize,
0, 2 ** 32 - 1);
@@ -901,102 +866,22 @@ class Http2Session extends EventEmitter {
throw new errors.Error('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
this[kState].pendingAck);
}
- debug(`[${sessionName(this[kType])}] sending settings`);
+ debug(`Http2Session ${sessionName(this[kType])}: sending settings`);
state.pendingAck++;
if (state.connecting) {
- debug(`[${sessionName(this[kType])}] session still connecting, ` +
- 'queue settings');
this.once('connect', submitSettings.bind(this, settings));
return;
}
submitSettings.call(this, settings);
}
- // Submits a PRIORITY frame to be sent to the remote peer.
- priority(stream, options) {
- const state = this[kState];
- if (state.destroyed || state.destroying)
- throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
-
- if (!(stream instanceof Http2Stream)) {
- throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
- 'stream',
- 'Http2Stream');
- }
- assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
- validatePriorityOptions(options);
-
- const id = stream[kID];
- debug(`[${sessionName(this[kType])}] sending priority for stream ` +
- `${id}`);
-
- // A stream cannot be made to depend on itself
- if (options.parent === id) {
- throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'parent',
- options.parent);
- }
-
- if (id === undefined) {
- debug(`[${sessionName(this[kType])}] session still connecting. queue ` +
- 'priority');
- stream.once('ready', submitPriority.bind(this, stream, options));
- return;
- }
- submitPriority.call(this, stream, options);
- }
-
- // Submits an RST-STREAM frame to be sent to the remote peer. This will
- // cause the stream to be closed.
- rstStream(stream, code = NGHTTP2_NO_ERROR) {
- // Do not check destroying here, as the rstStream may be sent while
- // the session is in the middle of being destroyed.
- if (this[kState].destroyed)
- throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
-
- if (!(stream instanceof Http2Stream)) {
- throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
- 'stream',
- 'Http2Stream');
- }
-
- if (typeof code !== 'number') {
- throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
- 'code',
- 'number');
- }
-
- const state = stream[kState];
- if (state.rst) {
- // rst has already been called, do not call again,
- // skip straight to destroy
- stream.destroy();
- return;
- }
- state.rst = true;
- state.rstCode = code;
-
- const id = stream[kID];
- debug(`[${sessionName(this[kType])}] initiating rststream for stream ` +
- `${id}: ${code}`);
-
- if (id === undefined) {
- debug(`[${sessionName(this[kType])}] session still connecting, queue ` +
- 'rststream');
- stream.once('ready', submitRstStream.bind(this, stream, code));
- return;
- }
- submitRstStream.call(this, stream, code);
- }
-
// Destroy the Http2Session
destroy() {
const state = this[kState];
if (state.destroyed || state.destroying)
return;
- debug(`[${sessionName(this[kType])}] destroying nghttp2session`);
+ debug(`Http2Session ${sessionName(this[kType])}: destroying`);
state.destroying = true;
state.destroyed = false;
@@ -1017,7 +902,7 @@ class Http2Session extends EventEmitter {
if (this[kHandle] !== undefined)
this[kHandle].destroying();
- setImmediate(finishSessionDestroy, this, socket);
+ setImmediate(finishSessionDestroy.bind(this), socket);
}
// Graceful or immediate shutdown of the Http2Session. Graceful shutdown
@@ -1041,7 +926,7 @@ class Http2Session extends EventEmitter {
options = Object.assign(Object.create(null), options);
if (options.opaqueData !== undefined &&
- !isUint8Array(options.opaqueData)) {
+ !isArrayBufferView(options.opaqueData)) {
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
'opaqueData',
options.opaqueData);
@@ -1067,7 +952,7 @@ class Http2Session extends EventEmitter {
options.lastStreamID);
}
- debug(`[${sessionName(type)}] initiating shutdown`);
+ debug(`Http2Session ${sessionName(type)}: initiating shutdown`);
state.shuttingDown = true;
if (callback) {
@@ -1075,13 +960,11 @@ class Http2Session extends EventEmitter {
}
if (state.connecting) {
- debug(`[${sessionName(type)}] session still connecting, queue ` +
- 'shutdown');
this.once('connect', submitShutdown.bind(this, options));
return;
}
- debug(`[${sessionName(type)}] sending shutdown`);
+ debug(`Http2Session ${sessionName(type)}: sending shutdown`);
submitShutdown.call(this, options);
}
@@ -1097,7 +980,7 @@ class Http2Session extends EventEmitter {
handle.chunksSentSinceLastWrite : null;
if (chunksSentSinceLastWrite !== null &&
chunksSentSinceLastWrite !== handle.updateChunksSent()) {
- _unrefActive(this);
+ this[kUpdateTimer]();
return;
}
}
@@ -1120,7 +1003,6 @@ class ServerHttp2Session extends Http2Session {
class ClientHttp2Session extends Http2Session {
constructor(options, socket) {
super(NGHTTP2_SESSION_CLIENT, options, socket);
- debug(`[${sessionName(this[kType])}] clienthttp2session created`);
}
// Submits a new HTTP2 request to the connected peer. Returns the
@@ -1129,13 +1011,15 @@ class ClientHttp2Session extends Http2Session {
const state = this[kState];
if (state.destroyed || state.destroying)
throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
- debug(`[${sessionName(this[kType])}] initiating request`);
- _unrefActive(this);
+ debug(`Http2Session ${sessionName(this[kType])}: initiating request`);
+
+ this[kUpdateTimer]();
+
assertIsObject(headers, 'headers');
assertIsObject(options, 'options');
headers = Object.assign(Object.create(null), headers);
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
if (headers[HTTP2_HEADER_METHOD] === undefined)
headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET;
@@ -1178,21 +1062,16 @@ class ClientHttp2Session extends Http2Session {
options.getTrailers);
}
- const stream = new ClientHttp2Stream(this, undefined, {});
-
- const onConnect = requestOnConnect.bind(stream, headers, options);
+ const stream = new ClientHttp2Stream(this, undefined, undefined, {});
// Close the writable side of the stream if options.endStream is set.
if (options.endStream)
stream.end();
+ const onConnect = requestOnConnect.bind(stream, headers, options);
if (state.connecting) {
- debug(`[${sessionName(this[kType])}] session still connecting, queue ` +
- 'stream init');
stream.on('connect', onConnect);
} else {
- debug(`[${sessionName(this[kType])}] session connected, immediate ` +
- 'stream init');
onConnect();
}
return stream;
@@ -1229,55 +1108,40 @@ function trackWriteState(stream, bytes) {
}
function afterDoStreamWrite(status, handle, req) {
- const session = handle[kOwner];
- _unrefActive(session);
+ const stream = handle[kOwner];
+ const session = stream[kSession];
+
+ stream[kUpdateTimer]();
- const state = session[kState];
const { bytes } = req;
- state.writeQueueSize -= bytes;
+ stream[kState].writeQueueSize -= bytes;
- const stream = state.streams.get(req.stream);
- if (stream !== undefined) {
- _unrefActive(stream);
- stream[kState].writeQueueSize -= bytes;
- }
+ if (session !== undefined)
+ session[kState].writeQueueSize -= bytes;
if (typeof req.callback === 'function')
req.callback();
- this.handle = undefined;
+ req.handle = undefined;
}
function onHandleFinish() {
- const session = this[kSession];
- if (session === undefined) return;
if (this[kID] === undefined) {
this.once('ready', onHandleFinish);
} else {
- const handle = session[kHandle];
+ const handle = this[kHandle];
if (handle !== undefined) {
- // Shutdown on the next tick of the event loop just in case there is
- // still data pending in the outbound queue.
- assert(handle.shutdownStream(this[kID]) === undefined,
- `The stream ${this[kID]} does not exist. Please report this as ` +
- 'a bug in Node.js');
+ const req = new ShutdownWrap();
+ req.oncomplete = () => {};
+ req.handle = handle;
+ handle.shutdown(req);
}
}
}
function onSessionClose(hadError, code) {
abort(this);
- // Close the readable side
- this.push(null);
- // Close the writable side
- this.end();
-}
-
-function onStreamClosed(code) {
- abort(this);
- // Close the readable side
- this.push(null);
- // Close the writable side
- this.end();
+ this.push(null); // Close the readable side
+ this.end(); // Close the writable side
}
function streamOnResume() {
@@ -1285,43 +1149,24 @@ function streamOnResume() {
this.once('ready', streamOnResume);
return;
}
- const session = this[kSession];
- if (session) {
- assert(session[kHandle].streamReadStart(this[kID]) === undefined,
- `HTTP/2 Stream ${this[kID]} does not exist. Please report this as ` +
- 'a bug in Node.js');
- }
+ this[kHandle].readStart();
}
function streamOnPause() {
- const session = this[kSession];
- if (session) {
- assert(session[kHandle].streamReadStop(this[kID]) === undefined,
- `HTTP/2 Stream ${this[kID]} does not exist. Please report this as ' +
- 'a bug in Node.js`);
- }
+ this[kHandle].readStop();
}
-function handleFlushData(handle, streamID) {
- assert(handle.flushData(streamID) === undefined,
- `HTTP/2 Stream ${streamID} does not exist. Please report this as ` +
- 'a bug in Node.js');
+function handleFlushData(handle) {
+ handle.flushData();
}
function streamOnSessionConnect() {
const session = this[kSession];
- debug(`[${sessionName(session[kType])}] session connected. emiting stream ` +
- 'connect');
+ debug(`Http2Session ${sessionName(session[kType])}: session connected`);
this[kState].connecting = false;
process.nextTick(emit, this, 'connect');
}
-function streamOnceReady() {
- const session = this[kSession];
- debug(`[${sessionName(session[kType])}] stream ${this[kID]} is ready`);
- this.uncork();
-}
-
function abort(stream) {
if (!stream[kState].aborted &&
!(stream._writableState.ended || stream._writableState.ending)) {
@@ -1330,16 +1175,14 @@ function abort(stream) {
}
}
-// An Http2Stream is a Duplex stream. On the server-side, the Readable side
-// provides access to the received request data. On the client-side, the
-// Readable side provides access to the received response data. On the
-// server side, the writable side is used to transmit response data, while
-// on the client side it is used to transmit request data.
+// An Http2Stream is a Duplex stream that is backed by a
+// node::http2::Http2Stream handle implementing StreamBase.
class Http2Stream extends Duplex {
constructor(session, options) {
options.allowHalfOpen = true;
options.decodeStrings = false;
super(options);
+ this[async_id_symbol] = -1;
this.cork();
this[kSession] = session;
@@ -1353,24 +1196,32 @@ class Http2Stream extends Duplex {
writeQueueSize: 0
};
- this.once('ready', streamOnceReady);
- this.once('streamClosed', onStreamClosed);
this.once('finish', onHandleFinish);
this.on('resume', streamOnResume);
this.on('pause', streamOnPause);
session.once('close', state.closeHandler);
if (session[kState].connecting) {
- debug(`[${sessionName(session[kType])}] session is still connecting, ` +
- 'queuing stream init');
state.connecting = true;
session.once('connect', streamOnSessionConnect.bind(this));
}
- debug(`[${sessionName(session[kType])}] http2stream created`);
}
- [kInit](id) {
+ [kUpdateTimer]() {
+ _unrefActive(this);
+ if (this[kSession])
+ _unrefActive(this[kSession]);
+ }
+
+ [kInit](id, handle) {
this[kID] = id;
+ this[async_id_symbol] = handle.getAsyncId();
+ handle[kOwner] = this;
+ this[kHandle] = handle;
+ handle.ontrailers = onStreamTrailers;
+ handle.onstreamclose = onStreamClose;
+ handle.onread = onStreamRead;
+ this.uncork();
this.emit('ready');
}
@@ -1407,8 +1258,7 @@ class Http2Stream extends Duplex {
handle.chunksSentSinceLastWrite : null;
if (chunksSentSinceLastWrite !== null &&
chunksSentSinceLastWrite !== handle.updateChunksSent()) {
- _unrefActive(this);
- _unrefActive(this[kSession]);
+ this[kUpdateTimer]();
return;
}
}
@@ -1435,8 +1285,8 @@ class Http2Stream extends Duplex {
get state() {
const id = this[kID];
if (this.destroyed || id === undefined)
- return Object.create(null);
- return getStreamState(this[kSession][kHandle], id);
+ return {};
+ return getStreamState(this[kHandle], id);
}
[kProceed]() {
@@ -1450,12 +1300,13 @@ class Http2Stream extends Duplex {
this.once('ready', this._write.bind(this, data, encoding, cb));
return;
}
+
+ this[kUpdateTimer]();
+
if (!this[kState].headersSent)
this[kProceed]();
- const session = this[kSession];
- _unrefActive(this);
- _unrefActive(session);
- const handle = session[kHandle];
+
+ const handle = this[kHandle];
const req = new WriteWrap();
req.stream = this[kID];
req.handle = handle;
@@ -1473,12 +1324,13 @@ class Http2Stream extends Duplex {
this.once('ready', this._writev.bind(this, data, cb));
return;
}
+
+ this[kUpdateTimer]();
+
if (!this[kState].headersSent)
this[kProceed]();
- const session = this[kSession];
- _unrefActive(this);
- _unrefActive(session);
- const handle = session[kHandle];
+
+ const handle = this[kHandle];
const req = new WriteWrap();
req.stream = this[kID];
req.handle = handle;
@@ -1498,16 +1350,12 @@ class Http2Stream extends Duplex {
}
_read(nread) {
- if (this[kID] === undefined) {
- this.once('ready', this._read.bind(this, nread));
- return;
- }
if (this.destroyed) {
this.push(null);
return;
}
- _unrefActive(this);
- process.nextTick(handleFlushData, this[kSession][kHandle], this[kID]);
+ if (this[kHandle] !== undefined)
+ process.nextTick(handleFlushData, this[kHandle]);
}
// Submits an RST-STREAM frame to shutdown this stream.
@@ -1516,19 +1364,17 @@ class Http2Stream extends Duplex {
// After sending the rstStream, this.destroy() will be called making
// the stream object no longer usable.
rstStream(code = NGHTTP2_NO_ERROR) {
- if (this.destroyed)
- throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- const session = this[kSession];
+ if (typeof code !== 'number')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number');
+ if (code < 0 || code > 2 ** 32 - 1)
+ throw new errors.RangeError('ERR_OUT_OF_RANGE', 'code');
+
+ const fn = submitRstStream.bind(this, code);
if (this[kID] === undefined) {
- debug(
- `[${sessionName(session[kType])}] queuing rstStream for new stream`);
- this.once('ready', this.rstStream.bind(this, code));
+ this.once('ready', fn);
return;
}
- debug(`[${sessionName(session[kType])}] sending rstStream for stream ` +
- `${this[kID]}: ${code}`);
- _unrefActive(this);
- session.rstStream(this, code);
+ fn();
}
rstWithNoError() {
@@ -1551,25 +1397,20 @@ class Http2Stream extends Duplex {
this.rstStream(NGHTTP2_INTERNAL_ERROR);
}
- // Note that this (and other methods like additionalHeaders and rstStream)
- // cause nghttp to queue frames up in its internal buffer that are not
- // actually sent on the wire until the next tick of the event loop. The
- // semantics of this method then are: queue a priority frame to be sent and
- // not immediately send the priority frame. There is current no callback
- // triggered when the data is actually sent.
priority(options) {
if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- const session = this[kSession];
+
+ assertIsObject(options, 'options');
+ options = Object.assign({}, options);
+ validatePriorityOptions(options);
+
+ const fn = submitPriority.bind(this, options);
if (this[kID] === undefined) {
- debug(`[${sessionName(session[kType])}] queuing priority for new stream`);
- this.once('ready', this.priority.bind(this, options));
+ this.once('ready', fn);
return;
}
- debug(`[${sessionName(session[kType])}] sending priority for stream ` +
- `${this[kID]}`);
- _unrefActive(this);
- session.priority(this, options);
+ fn();
}
// Called by this.destroy().
@@ -1581,11 +1422,13 @@ class Http2Stream extends Duplex {
_destroy(err, callback) {
const session = this[kSession];
if (this[kID] === undefined) {
- debug(`[${sessionName(session[kType])}] queuing destroy for new stream`);
this.once('ready', this._destroy.bind(this, err, callback));
return;
}
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: destroying stream`);
+
const state = this[kState];
session[kState].writeQueueSize -= state.writeQueueSize;
state.writeQueueSize = 0;
@@ -1595,15 +1438,13 @@ class Http2Stream extends Duplex {
server.emit('streamError', err, this);
}
- process.nextTick(continueStreamDestroy, this, err, callback);
+ process.nextTick(continueStreamDestroy.bind(this), err, callback);
}
}
-function continueStreamDestroy(self, err, callback) {
- const session = self[kSession];
- const state = self[kState];
-
- debug(`[${sessionName(session[kType])}] destroying stream ${self[kID]}`);
+function continueStreamDestroy(err, callback) {
+ const session = this[kSession];
+ const state = this[kState];
// Submit RST-STREAM frame if one hasn't been sent already and the
// stream hasn't closed normally...
@@ -1611,16 +1452,16 @@ function continueStreamDestroy(self, err, callback) {
let code = state.rstCode;
if (!rst && !session.destroyed) {
code = err instanceof Error ? NGHTTP2_INTERNAL_ERROR : NGHTTP2_NO_ERROR;
- session.rstStream(self, code);
+ this.rstStream(code);
}
// Remove the close handler on the session
session.removeListener('close', state.closeHandler);
// Unenroll the timer
- self.setTimeout(0);
+ this.setTimeout(0);
- setImmediate(finishStreamDestroy, self, session[kHandle]);
+ setImmediate(finishStreamDestroy.bind(this));
// RST code 8 not emitted as an error as its used by clients to signify
// abort and is already covered by aborted event, also allows more
@@ -1629,17 +1470,22 @@ function continueStreamDestroy(self, err, callback) {
err = new errors.Error('ERR_HTTP2_STREAM_ERROR', code);
}
callback(err);
- process.nextTick(emit, self, 'streamClosed', code);
- debug(`[${sessionName(session[kType])}] stream ${self[kID]} destroyed`);
+ abort(this);
+ this.push(null); // Close the readable side
+ this.end(); // Close the writable side
+ process.nextTick(emit, this, 'close', code);
}
-function finishStreamDestroy(self, handle) {
- const id = self[kID];
- self[kSession][kState].streams.delete(id);
- delete self[kSession];
- if (handle !== undefined)
- handle.destroyStream(id);
- self.emit('destroy');
+function finishStreamDestroy() {
+ const id = this[kID];
+ this[kSession][kState].streams.delete(id);
+ this[kSession] = undefined;
+ const handle = this[kHandle];
+ if (handle !== undefined) {
+ this[kHandle] = undefined;
+ handle.destroy();
+ }
+ this.emit('destroy');
}
function processHeaders(headers) {
@@ -1665,32 +1511,25 @@ function processHeaders(headers) {
function processRespondWithFD(fd, headers, offset = 0, length = -1,
streamOptions = 0) {
- const session = this[kSession];
const state = this[kState];
state.headersSent = true;
// Close the writable side of the stream
this.end();
- const ret = session[kHandle].submitFile(this[kID], fd, headers,
- offset, length, streamOptions);
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, session, 'error', err);
- break;
- default:
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- break;
- }
- // exact length of the file doesn't matter here, since the
- // stream is closing anyway — just use 1 to signify that
- // a write does exist
- trackWriteState(this, 1);
+ const ret = this[kHandle].respondFD(fd, headers,
+ offset, length,
+ streamOptions);
+
+ if (ret < 0) {
+ const err = new NghttpError(ret);
+ process.nextTick(emit, this, 'error', err);
+ return;
}
+ // exact length of the file doesn't matter here, since the
+ // stream is closing anyway — just use 1 to signify that
+ // a write does exist
+ trackWriteState(this, 1);
}
function doSendFD(session, options, fd, headers, streamOptions, err, stat) {
@@ -1809,18 +1648,18 @@ function afterOpen(session, options, headers, streamOptions, err, fd) {
function streamOnError(err) {
// we swallow the error for parity with HTTP1
// all the errors that ends here are not critical for the project
- debug('ServerHttp2Stream errored, avoiding uncaughtException', err);
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${this[kSession][kType]}: error`, err);
}
class ServerHttp2Stream extends Http2Stream {
- constructor(session, id, options, headers) {
+ constructor(session, handle, id, options, headers) {
super(session, options);
- this[kInit](id);
+ this[kInit](id, handle);
this[kProtocol] = headers[HTTP2_HEADER_SCHEME];
this[kAuthority] = headers[HTTP2_HEADER_AUTHORITY];
this.on('error', streamOnError);
- debug(`[${sessionName(session[kType])}] created serverhttp2stream`);
}
// true if the HEADERS frame has been sent
@@ -1840,16 +1679,14 @@ class ServerHttp2Stream extends Http2Stream {
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
const session = this[kSession];
- debug(`[${sessionName(session[kType])}] initiating push stream for stream` +
- ` ${this[kID]}`);
-
- _unrefActive(this);
- const state = session[kState];
- const streams = state.streams;
-
if (!session.remoteSettings.enablePush)
throw new errors.Error('ERR_HTTP2_PUSH_DISABLED');
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating push stream`);
+
+ this[kUpdateTimer]();
+
if (typeof options === 'function') {
callback = options;
options = undefined;
@@ -1859,7 +1696,7 @@ class ServerHttp2Stream extends Http2Stream {
throw new errors.TypeError('ERR_INVALID_CALLBACK');
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
options.endStream = !!options.endStream;
assertIsObject(headers, 'headers');
@@ -1875,58 +1712,45 @@ class ServerHttp2Stream extends Http2Stream {
headers[HTTP2_HEADER_PATH] = '/';
let headRequest = false;
- if (headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) {
- headRequest = true;
- options.endStream = true;
- }
+ if (headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD)
+ headRequest = options.endStream = true;
+ options.readable = !options.endStream;
const headersList = mapToHeaders(headers);
- if (!Array.isArray(headersList)) {
- // An error occurred!
+ if (!Array.isArray(headersList))
throw headersList;
- }
const streamOptions = options.endStream ? STREAM_OPTION_EMPTY_PAYLOAD : 0;
- const ret = session[kHandle].submitPushPromise(this[kID],
- headersList,
- streamOptions);
+ const ret = this[kHandle].pushPromise(headersList, streamOptions);
let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, session, 'error', err);
- break;
- case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
- err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
- process.nextTick(emit, session, 'error', err);
- break;
- case NGHTTP2_ERR_STREAM_CLOSED:
- err = new errors.Error('ERR_HTTP2_STREAM_CLOSED');
- process.nextTick(emit, this, 'error', err);
- break;
- default:
- if (ret <= 0) {
+ if (typeof ret === 'number') {
+ switch (ret) {
+ case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
+ err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
+ break;
+ case NGHTTP2_ERR_STREAM_CLOSED:
+ err = new errors.Error('ERR_HTTP2_STREAM_CLOSED');
+ break;
+ default:
err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
break;
- }
- debug(`[${sessionName(session[kType])}] push stream ${ret} created`);
- options.readable = !options.endStream;
-
- const stream = new ServerHttp2Stream(session, ret, options, headers);
-
- // If the push stream is a head request, close the writable side of
- // the stream immediately as there won't be any data sent.
- if (headRequest) {
- stream.end();
- const state = stream[kState];
- state.headRequest = true;
- }
-
- streams.set(ret, stream);
- process.nextTick(callback, stream, headers, 0);
+ }
+ process.nextTick(emit, this, 'error', err);
+ return;
}
+
+ const id = ret.id();
+ const stream = new ServerHttp2Stream(session, ret, id, options, headers);
+ session[kState].streams.set(id, stream);
+
+ if (options.endStream)
+ stream.end();
+
+ if (headRequest)
+ stream[kState].headRequest = true;
+
+ process.nextTick(callback, stream, headers, 0);
}
// Initiate a response on this Http2Stream
@@ -1934,16 +1758,16 @@ class ServerHttp2Stream extends Http2Stream {
const session = this[kSession];
if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`[${sessionName(session[kType])}] initiating response for stream ` +
- `${this[kID]}`);
- _unrefActive(this);
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
const state = this[kState];
if (state.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
options.endStream = !!options.endStream;
let streamOptions = 0;
@@ -1974,30 +1798,19 @@ class ServerHttp2Stream extends Http2Stream {
}
const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse);
- if (!Array.isArray(headersList)) {
- // An error occurred!
+ if (!Array.isArray(headersList))
throw headersList;
- }
+
state.headersSent = true;
// Close the writable side if the endStream option is set
if (options.endStream)
this.end();
- const ret = session[kHandle].submitResponse(this[kID],
- headersList,
- streamOptions);
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, session, 'error', err);
- break;
- default:
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- }
+ const ret = this[kHandle].respond(headersList, streamOptions);
+ if (ret < 0) {
+ const err = new NghttpError(ret);
+ process.nextTick(emit, this, 'error', err);
}
}
@@ -2011,9 +1824,9 @@ class ServerHttp2Stream extends Http2Stream {
const session = this[kSession];
if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`[${sessionName(session[kType])}] initiating response for stream ` +
- `${this[kID]}`);
- _unrefActive(this);
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
const state = this[kState];
if (state.headersSent)
@@ -2094,9 +1907,9 @@ class ServerHttp2Stream extends Http2Stream {
const session = this[kSession];
if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`[${sessionName(session[kType])}] initiating response for stream ` +
- `${this[kID]}`);
- _unrefActive(this);
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
const state = this[kState];
if (state.headersSent)
@@ -2161,7 +1974,8 @@ class ServerHttp2Stream extends Http2Stream {
throw new errors.Error('ERR_HTTP2_HEADERS_AFTER_RESPOND');
const session = this[kSession];
- debug(`[${sessionName(session[kType])}] sending additional headers`);
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: sending additional headers`);
assertIsObject(headers, 'headers');
headers = Object.assign(Object.create(null), headers);
@@ -2175,7 +1989,7 @@ class ServerHttp2Stream extends Http2Stream {
}
}
- _unrefActive(this);
+ this[kUpdateTimer]();
const headersList = mapToHeaders(headers,
assertValidPseudoHeaderResponse);
@@ -2183,18 +1997,10 @@ class ServerHttp2Stream extends Http2Stream {
throw headersList;
}
- const ret = session[kHandle].sendHeaders(this[kID], headersList);
- let err;
- switch (ret) {
- case NGHTTP2_ERR_NOMEM:
- err = new errors.Error('ERR_OUTOFMEMORY');
- process.nextTick(emit, session, 'error', err);
- break;
- default:
- if (ret < 0) {
- err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- }
+ const ret = this[kHandle].info(headersList);
+ if (ret < 0) {
+ const err = new NghttpError(ret);
+ process.nextTick(emit, this, 'error', err);
}
}
}
@@ -2202,13 +2008,12 @@ class ServerHttp2Stream extends Http2Stream {
ServerHttp2Stream.prototype[kProceed] = ServerHttp2Stream.prototype.respond;
class ClientHttp2Stream extends Http2Stream {
- constructor(session, id, options) {
+ constructor(session, handle, id, options) {
super(session, options);
this[kState].headersSent = true;
if (id !== undefined)
- this[kInit](id);
+ this[kInit](id, handle);
this.on('headers', handleHeaderContinue);
- debug(`[${sessionName(session[kType])}] clienthttp2stream created`);
}
}
@@ -2237,7 +2042,7 @@ const setTimeout = {
}
} else {
enroll(this, msecs);
- _unrefActive(this);
+ this[kUpdateTimer]();
if (callback !== undefined) {
if (typeof callback !== 'function')
throw new errors.TypeError('ERR_INVALID_CALLBACK');
@@ -2257,13 +2062,12 @@ Object.defineProperty(Http2Session.prototype, 'setTimeout', setTimeout);
function socketDestroy(error) {
const session = this[kSession];
const type = session[kType];
- debug(`[${sessionName(type)}] socket destroy called`);
+ debug(`Http2Session ${sessionName(type)}: socket destroy called`);
delete this[kServer];
// destroy the session first so that it will stop trying to
// send data while we close the socket.
session.destroy();
this.destroy = this[kDestroySocket];
- debug(`[${sessionName(type)}] destroying the socket`);
this.destroy(error);
}
@@ -2272,7 +2076,8 @@ function socketDestroy(error) {
// a sessionError; failing that, destroy, remove the error listener, and
// re-emit the error event
function sessionOnError(error) {
- debug(`[${sessionName(this[kType])}] server session error: ${error.message}`);
+ debug(`Http2Session ${sessionName(this[kType])}: session error: ` +
+ `${error.message}`);
if (this[kServer] !== undefined && this[kServer].emit('sessionError', error))
return;
if (this[kSocket] !== undefined && this[kSocket].emit('sessionError', error))
@@ -2287,7 +2092,7 @@ function sessionOnError(error) {
function socketOnError(error) {
const session = this[kSession];
const type = session && session[kType];
- debug(`[${sessionName(type)}] server socket error: ${error.message}`);
+ debug(`Http2Session ${sessionName(type)}: socket error: ${error.message}`);
if (kRenegTest.test(error.message))
return this.destroy();
if (session !== undefined &&
@@ -2300,12 +2105,11 @@ function socketOnError(error) {
// Handles the on('stream') event for a session and forwards
// it on to the server object.
function sessionOnStream(stream, headers, flags, rawHeaders) {
- debug(`[${sessionName(this[kType])}] emit server stream event`);
this[kServer].emit('stream', stream, headers, flags, rawHeaders);
}
function sessionOnPriority(stream, parent, weight, exclusive) {
- debug(`[${sessionName(this[kType])}] priority change received`);
+ debug(`Http2Session ${sessionName(this[kType])}: priority change received`);
this[kServer].emit('priority', stream, parent, weight, exclusive);
}
@@ -2316,7 +2120,6 @@ function sessionOnSocketError(error, socket) {
// When the session times out on the server, attempt a graceful shutdown
function sessionOnTimeout() {
- debug('session timeout');
process.nextTick(() => {
const state = this[kState];
// if destroyed or destryoing, do nothing
@@ -2340,7 +2143,7 @@ function sessionOnTimeout() {
}
function connectionListener(socket) {
- debug('[server] received a connection');
+ debug('Http2Session server: received a connection');
const options = this[kOptions] || {};
if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') {
@@ -2409,7 +2212,6 @@ class Http2SecureServer extends TLSServer {
if (typeof requestListener === 'function')
this.on('request', requestListener);
this.on('tlsClientError', onErrorSecureServerSession);
- debug('http2secureserver created');
}
setTimeout(msecs, callback) {
@@ -2431,7 +2233,6 @@ class Http2Server extends NETServer {
this.on('newListener', setupCompat);
if (typeof requestListener === 'function')
this.on('request', requestListener);
- debug('http2server created');
}
setTimeout(msecs, callback) {
@@ -2447,13 +2248,12 @@ class Http2Server extends NETServer {
function setupCompat(ev) {
if (ev === 'request') {
- debug('setting up compatibility handler');
this.removeListener('newListener', setupCompat);
this.on('stream', onServerStream);
}
}
-function socketOnClose(hadError) {
+function socketOnClose() {
const session = this[kSession];
if (session !== undefined && !session.destroyed) {
// Skip unconsume because the socket is destroyed.
@@ -2465,7 +2265,8 @@ function socketOnClose(hadError) {
// If the session emits an error, forward it to the socket as a sessionError;
// failing that, destroy the session, remove the listener and re-emit the error
function clientSessionOnError(error) {
- debug(`client session error: ${error.message}`);
+ debug(`Http2Session ${sessionName(this[kType])}]: session error: ` +
+ `${error.message}`);
if (this[kSocket] !== undefined && this[kSocket].emit('sessionError', error))
return;
this.destroy();
@@ -2487,8 +2288,6 @@ function connect(authority, options, listener) {
assertIsObject(authority, 'authority', ['string', 'object', 'URL']);
- debug(`connecting to ${authority}`);
-
const protocol = authority.protocol || options.protocol || 'https:';
const port = '' + (authority.port !== '' ?
authority.port : (authority.protocol === 'http:' ? 80 : 443));
@@ -2542,7 +2341,6 @@ function createSecureServer(options, handler) {
'options',
'object');
}
- debug('creating http2secureserver');
return new Http2SecureServer(options, handler);
}
@@ -2551,7 +2349,6 @@ function createServer(options, handler) {
handler = options;
options = Object.create(null);
}
- debug('creating htt2pserver');
return new Http2Server(options, handler);
}
@@ -2572,7 +2369,7 @@ function getPackedSettings(settings) {
16384, 2 ** 24 - 1);
assertWithinRange('maxConcurrentStreams',
settings.maxConcurrentStreams,
- 0, 2 ** 31 - 1);
+ 0, kMaxStreams);
assertWithinRange('maxHeaderListSize',
settings.maxHeaderListSize,
0, 2 ** 32 - 1);
@@ -2588,9 +2385,9 @@ function getPackedSettings(settings) {
}
function getUnpackedSettings(buf, options = {}) {
- if (!isUint8Array(buf)) {
+ if (!isArrayBufferView(buf)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buf',
- ['Buffer', 'Uint8Array']);
+ ['Buffer', 'TypedArray', 'DataView']);
}
if (buf.length % 6 !== 0)
throw new errors.RangeError('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH');
@@ -2638,7 +2435,7 @@ function getUnpackedSettings(buf, options = {}) {
16384, 2 ** 24 - 1);
assertWithinRange('maxConcurrentStreams',
settings.maxConcurrentStreams,
- 0, 2 ** 31 - 1);
+ 0, kMaxStreams);
assertWithinRange('maxHeaderListSize',
settings.maxHeaderListSize,
0, 2 ** 32 - 1);
diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js
index 03d0639624083d..1800dc5cff33ff 100644
--- a/lib/internal/http2/util.js
+++ b/lib/internal/http2/util.js
@@ -172,7 +172,9 @@ const IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS = 1;
const IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH = 2;
const IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS = 3;
const IDX_OPTIONS_PADDING_STRATEGY = 4;
-const IDX_OPTIONS_FLAGS = 5;
+const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5;
+const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6;
+const IDX_OPTIONS_FLAGS = 7;
function updateOptionsBuffer(options) {
var flags = 0;
@@ -201,6 +203,16 @@ function updateOptionsBuffer(options) {
optionsBuffer[IDX_OPTIONS_PADDING_STRATEGY] =
options.paddingStrategy;
}
+ if (typeof options.maxHeaderListPairs === 'number') {
+ flags |= (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS);
+ optionsBuffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS] =
+ options.maxHeaderListPairs;
+ }
+ if (typeof options.maxOutstandingPings === 'number') {
+ flags |= (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS);
+ optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS] =
+ options.maxOutstandingPings;
+ }
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
}
@@ -253,25 +265,19 @@ function getDefaultSettings() {
// remote is a boolean. true to fetch remote settings, false to fetch local.
// this is only called internally
function getSettings(session, remote) {
- const holder = Object.create(null);
if (remote)
- session.refreshRemoteSettings();
+ session.remoteSettings();
else
- session.refreshLocalSettings();
-
- holder.headerTableSize =
- settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE];
- holder.enablePush =
- !!settingsBuffer[IDX_SETTINGS_ENABLE_PUSH];
- holder.initialWindowSize =
- settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE];
- holder.maxFrameSize =
- settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE];
- holder.maxConcurrentStreams =
- settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS];
- holder.maxHeaderListSize =
- settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE];
- return holder;
+ session.localSettings();
+
+ return {
+ headerTableSize: settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE],
+ enablePush: !!settingsBuffer[IDX_SETTINGS_ENABLE_PUSH],
+ initialWindowSize: settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE],
+ maxFrameSize: settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE],
+ maxConcurrentStreams: settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS],
+ maxHeaderListSize: settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]
+ };
}
function updateSettingsBuffer(settings) {
@@ -310,45 +316,39 @@ function updateSettingsBuffer(settings) {
}
function getSessionState(session) {
- const holder = Object.create(null);
- binding.refreshSessionState(session);
- holder.effectiveLocalWindowSize =
- sessionState[IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE];
- holder.effectiveRecvDataLength =
- sessionState[IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH];
- holder.nextStreamID =
- sessionState[IDX_SESSION_STATE_NEXT_STREAM_ID];
- holder.localWindowSize =
- sessionState[IDX_SESSION_STATE_LOCAL_WINDOW_SIZE];
- holder.lastProcStreamID =
- sessionState[IDX_SESSION_STATE_LAST_PROC_STREAM_ID];
- holder.remoteWindowSize =
- sessionState[IDX_SESSION_STATE_REMOTE_WINDOW_SIZE];
- holder.outboundQueueSize =
- sessionState[IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE];
- holder.deflateDynamicTableSize =
- sessionState[IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE];
- holder.inflateDynamicTableSize =
- sessionState[IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE];
- return holder;
+ session.refreshState();
+ return {
+ effectiveLocalWindowSize:
+ sessionState[IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE],
+ effectiveRecvDataLength:
+ sessionState[IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH],
+ nextStreamID:
+ sessionState[IDX_SESSION_STATE_NEXT_STREAM_ID],
+ localWindowSize:
+ sessionState[IDX_SESSION_STATE_LOCAL_WINDOW_SIZE],
+ lastProcStreamID:
+ sessionState[IDX_SESSION_STATE_LAST_PROC_STREAM_ID],
+ remoteWindowSize:
+ sessionState[IDX_SESSION_STATE_REMOTE_WINDOW_SIZE],
+ outboundQueueSize:
+ sessionState[IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE],
+ deflateDynamicTableSize:
+ sessionState[IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE],
+ inflateDynamicTableSize:
+ sessionState[IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE]
+ };
}
-function getStreamState(session, stream) {
- const holder = Object.create(null);
- binding.refreshStreamState(session, stream);
- holder.state =
- streamState[IDX_STREAM_STATE];
- holder.weight =
- streamState[IDX_STREAM_STATE_WEIGHT];
- holder.sumDependencyWeight =
- streamState[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT];
- holder.localClose =
- streamState[IDX_STREAM_STATE_LOCAL_CLOSE];
- holder.remoteClose =
- streamState[IDX_STREAM_STATE_REMOTE_CLOSE];
- holder.localWindowSize =
- streamState[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE];
- return holder;
+function getStreamState(stream) {
+ stream.refreshState();
+ return {
+ state: streamState[IDX_STREAM_STATE],
+ weight: streamState[IDX_STREAM_STATE_WEIGHT],
+ sumDependencyWeight: streamState[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT],
+ localClose: streamState[IDX_STREAM_STATE_LOCAL_CLOSE],
+ remoteClose: streamState[IDX_STREAM_STATE_REMOTE_CLOSE],
+ localWindowSize: streamState[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE]
+ };
}
function isIllegalConnectionSpecificHeader(name, value) {
diff --git a/node.gyp b/node.gyp
index 3486d5d21bc269..df531768c63300 100644
--- a/node.gyp
+++ b/node.gyp
@@ -242,8 +242,6 @@
'src/js_stream.h',
'src/module_wrap.h',
'src/node.h',
- 'src/node_http2_core.h',
- 'src/node_http2_core-inl.h',
'src/node_buffer.h',
'src/node_constants.h',
'src/node_debug_options.h',
diff --git a/src/async-wrap.h b/src/async-wrap.h
index ca87f3a3bcf147..9510e26577357d 100644
--- a/src/async-wrap.h
+++ b/src/async-wrap.h
@@ -42,7 +42,8 @@ namespace node {
V(GETADDRINFOREQWRAP) \
V(GETNAMEINFOREQWRAP) \
V(HTTP2SESSION) \
- V(HTTP2SESSIONSHUTDOWNWRAP) \
+ V(HTTP2STREAM) \
+ V(HTTP2PING) \
V(HTTPPARSER) \
V(JSSTREAM) \
V(PIPECONNECTWRAP) \
diff --git a/src/env.h b/src/env.h
index 74bc48d9b97d68..5f424a39fe5b74 100644
--- a/src/env.h
+++ b/src/env.h
@@ -311,6 +311,8 @@ class ModuleWrap;
V(context, v8::Context) \
V(domain_array, v8::Array) \
V(domains_stack_array, v8::Array) \
+ V(http2ping_constructor_template, v8::ObjectTemplate) \
+ V(http2stream_constructor_template, v8::ObjectTemplate) \
V(inspector_console_api_object, v8::Object) \
V(module_load_list_array, v8::Array) \
V(pbkdf2_constructor_template, v8::ObjectTemplate) \
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index b39bbbb5d28265..b93ec413d90b77 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -591,6 +591,8 @@ void Fill(const FunctionCallbackInfo& args) {
THROW_AND_RETURN_IF_OOB(start <= end);
THROW_AND_RETURN_IF_OOB(fill_length + start <= ts_obj_length);
+ args.GetReturnValue().Set(static_cast(fill_length));
+
// First check if Buffer has been passed.
if (Buffer::HasInstance(args[1])) {
SPREAD_BUFFER_ARG(args[1], fill_obj);
@@ -612,8 +614,10 @@ void Fill(const FunctionCallbackInfo& args) {
enc == UTF8 ? str_obj->Utf8Length() :
enc == UCS2 ? str_obj->Length() * sizeof(uint16_t) : str_obj->Length();
- if (str_length == 0)
+ if (str_length == 0) {
+ args.GetReturnValue().Set(0);
return;
+ }
// Can't use StringBytes::Write() in all cases. For example if attempting
// to write a two byte character into a one byte Buffer.
@@ -643,7 +647,7 @@ void Fill(const FunctionCallbackInfo& args) {
// TODO(trevnorris): Should this throw? Because of the string length was
// greater than 0 but couldn't be written then the string was invalid.
if (str_length == 0)
- return;
+ return args.GetReturnValue().Set(0);
}
start_fill:
diff --git a/src/node_http2.cc b/src/node_http2.cc
index c8aa9e0729d4f7..dca2dde1f3142d 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -5,6 +5,7 @@
#include "node_http2_state.h"
#include
+#include
namespace node {
@@ -13,6 +14,8 @@ using v8::Context;
using v8::Float64Array;
using v8::Function;
using v8::Integer;
+using v8::Number;
+using v8::ObjectTemplate;
using v8::String;
using v8::Uint32;
using v8::Uint32Array;
@@ -20,12 +23,11 @@ using v8::Undefined;
namespace http2 {
-Freelist stream_free_list;
-
-Nghttp2Session::Callbacks Nghttp2Session::callback_struct_saved[2] = {
+const Http2Session::Callbacks Http2Session::callback_struct_saved[2] = {
Callbacks(false),
Callbacks(true)};
+
Http2Options::Http2Options(Environment* env) {
nghttp2_option_new(&options_);
@@ -67,8 +69,17 @@ Http2Options::Http2Options(Environment* env) {
buffer.GetValue(IDX_OPTIONS_PADDING_STRATEGY));
SetPaddingStrategy(strategy);
}
+
+ if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) {
+ SetMaxHeaderPairs(buffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS]);
+ }
+
+ if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS)) {
+ SetMaxOutstandingPings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS]);
+ }
}
+
Http2Settings::Http2Settings(Environment* env) : env_(env) {
entries_.AllocateSufficientStorage(IDX_SETTINGS_COUNT);
AliasedBuffer& buffer =
@@ -79,7 +90,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
uint32_t val = buffer[IDX_SETTINGS_HEADER_TABLE_SIZE];
- DEBUG_HTTP2("Setting header table size: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting header table size: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
entries_[n].value = val;
n++;
@@ -87,7 +98,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
uint32_t val = buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS];
- DEBUG_HTTP2("Setting max concurrent streams: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting max concurrent streams: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
entries_[n].value = val;
n++;
@@ -95,7 +106,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
uint32_t val = buffer[IDX_SETTINGS_MAX_FRAME_SIZE];
- DEBUG_HTTP2("Setting max frame size: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting max frame size: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
entries_[n].value = val;
n++;
@@ -103,7 +114,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
uint32_t val = buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE];
- DEBUG_HTTP2("Setting initial window size: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting initial window size: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
entries_[n].value = val;
n++;
@@ -111,7 +122,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
uint32_t val = buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE];
- DEBUG_HTTP2("Setting max header list size: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting max header list size: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE;
entries_[n].value = val;
n++;
@@ -119,7 +130,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
if (flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) {
uint32_t val = buffer[IDX_SETTINGS_ENABLE_PUSH];
- DEBUG_HTTP2("Setting enable push: %d\n", val);
+ DEBUG_HTTP2("Http2Settings: setting enable push: %d\n", val);
entries_[n].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
entries_[n].value = val;
n++;
@@ -128,6 +139,7 @@ Http2Settings::Http2Settings(Environment* env) : env_(env) {
count_ = n;
}
+
inline Local Http2Settings::Pack() {
const size_t len = count_ * 6;
Local buf = Buffer::New(env_, len).ToLocalChecked();
@@ -141,6 +153,7 @@ inline Local Http2Settings::Pack() {
return Undefined(env_->isolate());
}
+
inline void Http2Settings::Update(Environment* env,
Http2Session* session,
get_setting fn) {
@@ -173,13 +186,17 @@ inline void Http2Settings::RefreshDefaults(Environment* env) {
DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE;
buffer[IDX_SETTINGS_MAX_FRAME_SIZE] =
DEFAULT_SETTINGS_MAX_FRAME_SIZE;
+ buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] =
+ DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
buffer[IDX_SETTINGS_COUNT] =
(1 << IDX_SETTINGS_HEADER_TABLE_SIZE) |
(1 << IDX_SETTINGS_ENABLE_PUSH) |
(1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE) |
- (1 << IDX_SETTINGS_MAX_FRAME_SIZE);
+ (1 << IDX_SETTINGS_MAX_FRAME_SIZE) |
+ (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE);
}
+
Http2Priority::Http2Priority(Environment* env,
Local parent,
Local weight,
@@ -193,24 +210,162 @@ Http2Priority::Http2Priority(Environment* env,
nghttp2_priority_spec_init(&spec, parent_, weight_, exclusive_ ? 1 : 0);
}
+
+inline const char* Http2Session::TypeName() {
+ switch (session_type_) {
+ case NGHTTP2_SESSION_SERVER: return "server";
+ case NGHTTP2_SESSION_CLIENT: return "client";
+ default:
+ // This should never happen
+ ABORT();
+ }
+}
+
+
+Headers::Headers(Isolate* isolate,
+ Local context,
+ Local headers) {
+ Local header_string = headers->Get(context, 0).ToLocalChecked();
+ Local header_count = headers->Get(context, 1).ToLocalChecked();
+ count_ = header_count.As()->Value();
+ int header_string_len = header_string.As()->Length();
+
+ if (count_ == 0) {
+ CHECK_EQ(header_string_len, 0);
+ return;
+ }
+
+ // Allocate a single buffer with count_ nghttp2_nv structs, followed
+ // by the raw header data as passed from JS. This looks like:
+ // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents |
+ buf_.AllocateSufficientStorage((alignof(nghttp2_nv) - 1) +
+ count_ * sizeof(nghttp2_nv) +
+ header_string_len);
+ // Make sure the start address is aligned appropriately for an nghttp2_nv*.
+ char* start = reinterpret_cast(
+ ROUND_UP(reinterpret_cast(*buf_), alignof(nghttp2_nv)));
+ char* header_contents = start + (count_ * sizeof(nghttp2_nv));
+ nghttp2_nv* const nva = reinterpret_cast(start);
+
+ CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length());
+ CHECK_EQ(header_string.As()
+ ->WriteOneByte(reinterpret_cast(header_contents),
+ 0, header_string_len,
+ String::NO_NULL_TERMINATION),
+ header_string_len);
+
+ size_t n = 0;
+ char* p;
+ for (p = header_contents; p < header_contents + header_string_len; n++) {
+ if (n >= count_) {
+ // This can happen if a passed header contained a null byte. In that
+ // case, just provide nghttp2 with an invalid header to make it reject
+ // the headers list.
+ static uint8_t zero = '\0';
+ nva[0].name = nva[0].value = &zero;
+ nva[0].namelen = nva[0].valuelen = 1;
+ count_ = 1;
+ return;
+ }
+
+ nva[n].flags = NGHTTP2_NV_FLAG_NONE;
+ nva[n].name = reinterpret_cast(p);
+ nva[n].namelen = strlen(p);
+ p += nva[n].namelen + 1;
+ nva[n].value = reinterpret_cast(p);
+ nva[n].valuelen = strlen(p);
+ p += nva[n].valuelen + 1;
+ }
+}
+
+
+Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) {
+ CHECK_EQ(nghttp2_session_callbacks_new(&callbacks), 0);
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ callbacks, OnBeginHeadersCallback);
+ nghttp2_session_callbacks_set_on_header_callback2(
+ callbacks, OnHeaderCallback);
+ nghttp2_session_callbacks_set_on_frame_recv_callback(
+ callbacks, OnFrameReceive);
+ nghttp2_session_callbacks_set_on_stream_close_callback(
+ callbacks, OnStreamClose);
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ callbacks, OnDataChunkReceived);
+ nghttp2_session_callbacks_set_on_frame_not_send_callback(
+ callbacks, OnFrameNotSent);
+ nghttp2_session_callbacks_set_on_invalid_header_callback2(
+ callbacks, OnInvalidHeader);
+ nghttp2_session_callbacks_set_error_callback(
+ callbacks, OnNghttpError);
+
+ if (kHasGetPaddingCallback) {
+ nghttp2_session_callbacks_set_select_padding_callback(
+ callbacks, OnSelectPadding);
+ }
+}
+
+
+Http2Session::Callbacks::~Callbacks() {
+ nghttp2_session_callbacks_del(callbacks);
+}
+
+
Http2Session::Http2Session(Environment* env,
Local |