diff --git a/closed/openjdk-tag.gmk b/closed/openjdk-tag.gmk index ce2ed4a3222..cd854c37e39 100644 --- a/closed/openjdk-tag.gmk +++ b/closed/openjdk-tag.gmk @@ -1 +1 @@ -OPENJDK_TAG := jdk-11.0.17+7 +OPENJDK_TAG := jdk-11.0.17+8 diff --git a/make/autoconf/version-numbers b/make/autoconf/version-numbers index f2619eb5e7b..4e0fbd285b1 100644 --- a/make/autoconf/version-numbers +++ b/make/autoconf/version-numbers @@ -37,7 +37,7 @@ DEFAULT_VERSION_DATE=2022-10-18 DEFAULT_VERSION_CLASSFILE_MAJOR=55 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="10 11" -DEFAULT_PROMOTED_VERSION_PRE=ea +DEFAULT_PROMOTED_VERSION_PRE= LAUNCHER_NAME=openjdk PRODUCT_NAME=OpenJDK diff --git a/make/launcher/LauncherCommon.gmk b/make/launcher/LauncherCommon.gmk index c2949fc7fe5..4652051a35d 100644 --- a/make/launcher/LauncherCommon.gmk +++ b/make/launcher/LauncherCommon.gmk @@ -60,6 +60,7 @@ endif LAUNCHER_SRC := $(TOPDIR)/src/java.base/share/native/launcher LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/share/native/launcher \ + -I$(TOPDIR)/src/java.desktop/share/native/include \ -I$(TOPDIR)/src/java.base/share/native/libjli \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli \ diff --git a/make/lib/Awt2dLibraries.gmk b/make/lib/Awt2dLibraries.gmk index 8a61daa92df..6dd7da0968c 100644 --- a/make/lib/Awt2dLibraries.gmk +++ b/make/lib/Awt2dLibraries.gmk @@ -462,6 +462,7 @@ ifeq ($(findstring $(OPENJDK_TARGET_OS), windows macosx),) common/awt/debug \ common/font \ common/java2d/opengl \ + include \ # LIBAWT_HEADLESS_CFLAGS := $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) $(X_CFLAGS) \ @@ -585,6 +586,7 @@ LIBFONTMANAGER_EXTRA_HEADER_DIRS := \ libawt/java2d \ libawt/java2d/pipe \ libawt/java2d/loops \ + include \ # LIBFONTMANAGER_CFLAGS += $(LIBFREETYPE_CFLAGS) @@ -775,6 +777,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) common/awt/systemscale \ # + LIBSPLASHSCREEN_HEADER_DIRS += include + ifeq ($(USE_EXTERNAL_LIBGIF), false) LIBSPLASHSCREEN_HEADER_DIRS += libsplashscreen/giflib else diff --git a/src/java.base/share/classes/com/sun/security/ntlm/Client.java b/src/java.base/share/classes/com/sun/security/ntlm/Client.java index fe33ee89bde..9f963c12841 100644 --- a/src/java.base/share/classes/com/sun/security/ntlm/Client.java +++ b/src/java.base/share/classes/com/sun/security/ntlm/Client.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,9 +117,10 @@ public byte[] type1() { * {@code nonce} is null for NTLM v1. */ public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException { - if (type2 == null || (v != Version.NTLM && nonce == null)) { + if (type2 == null || (v != Version.NTLM && nonce == null) || + (nonce != null && nonce.length != 8)) { throw new NTLMException(NTLMException.PROTOCOL, - "type2 and nonce cannot be null"); + "type2 cannot be null, and nonce must be 8-byte long"); } debug("NTLM Client: Type 2 received\n"); debug(type2); diff --git a/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java b/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java index adc29e51b52..bcc23d6eb7e 100644 --- a/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java +++ b/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java @@ -226,23 +226,27 @@ void writeBytes(int offset, byte[] data) { System.arraycopy(data, 0, internal, offset, data.length); } - void writeSecurityBuffer(int offset, byte[] data) { + void writeSecurityBuffer(int offset, byte[] data) throws NTLMException { if (data == null) { - writeShort(offset+4, current); + writeInt(offset+4, current); } else { int len = data.length; + if (len > 65535) { + throw new NTLMException(NTLMException.INVALID_INPUT, + "Invalid data length " + len); + } if (current + len > internal.length) { internal = Arrays.copyOf(internal, current + len + 256); } writeShort(offset, len); writeShort(offset+2, len); - writeShort(offset+4, current); + writeInt(offset+4, current); System.arraycopy(data, 0, internal, current, len); current += len; } } - void writeSecurityBuffer(int offset, String str, boolean unicode) { + void writeSecurityBuffer(int offset, String str, boolean unicode) throws NTLMException { writeSecurityBuffer(offset, str == null ? null : str.getBytes( unicode ? StandardCharsets.UTF_16LE : StandardCharsets.ISO_8859_1)); diff --git a/src/java.base/share/classes/com/sun/security/ntlm/NTLMException.java b/src/java.base/share/classes/com/sun/security/ntlm/NTLMException.java index 736db459cf3..b9e0ae962cf 100644 --- a/src/java.base/share/classes/com/sun/security/ntlm/NTLMException.java +++ b/src/java.base/share/classes/com/sun/security/ntlm/NTLMException.java @@ -70,6 +70,11 @@ public final class NTLMException extends GeneralSecurityException { */ public static final int PROTOCOL = 6; + /** + * If an invalid input is provided. + */ + public static final int INVALID_INPUT = 7; + private int errorCode; /** diff --git a/src/java.base/share/classes/com/sun/security/ntlm/Server.java b/src/java.base/share/classes/com/sun/security/ntlm/Server.java index c826d82f990..f6237d8ea62 100644 --- a/src/java.base/share/classes/com/sun/security/ntlm/Server.java +++ b/src/java.base/share/classes/com/sun/security/ntlm/Server.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,9 +85,9 @@ public Server(String version, String domain) throws NTLMException { * {@code nonce} is null. */ public byte[] type2(byte[] type1, byte[] nonce) throws NTLMException { - if (nonce == null) { + if (nonce == null || nonce.length != 8) { throw new NTLMException(NTLMException.PROTOCOL, - "nonce cannot be null"); + "nonce must be 8-byte long"); } debug("NTLM Server: Type 1 received\n"); if (type1 != null) debug(type1); diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 97c10fa9214..ff9a6daf47b 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -30,6 +30,10 @@ package java.math; import static java.math.BigInteger.LONG_MASK; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.io.StreamCorruptedException; import java.util.Arrays; import java.util.Objects; @@ -1000,6 +1004,15 @@ public BigDecimal(double val, MathContext mc) { this.precision = prec; } + /** + * Accept no subclasses. + */ + private static BigInteger toStrictBigInteger(BigInteger val) { + return (val.getClass() == BigInteger.class) ? + val : + new BigInteger(val.toByteArray().clone()); + } + /** * Translates a {@code BigInteger} into a {@code BigDecimal}. * The scale of the {@code BigDecimal} is zero. @@ -1009,8 +1022,8 @@ public BigDecimal(double val, MathContext mc) { */ public BigDecimal(BigInteger val) { scale = 0; - intVal = val; - intCompact = compactValFor(val); + intVal = toStrictBigInteger(val); + intCompact = compactValFor(intVal); } /** @@ -1026,7 +1039,7 @@ public BigDecimal(BigInteger val) { * @since 1.5 */ public BigDecimal(BigInteger val, MathContext mc) { - this(val,0,mc); + this(toStrictBigInteger(val), 0, mc); } /** @@ -1040,8 +1053,8 @@ public BigDecimal(BigInteger val, MathContext mc) { */ public BigDecimal(BigInteger unscaledVal, int scale) { // Negative scales are now allowed - this.intVal = unscaledVal; - this.intCompact = compactValFor(unscaledVal); + this.intVal = toStrictBigInteger(unscaledVal); + this.intCompact = compactValFor(this.intVal); this.scale = scale; } @@ -1061,6 +1074,7 @@ public BigDecimal(BigInteger unscaledVal, int scale) { * @since 1.5 */ public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) { + unscaledVal = toStrictBigInteger(unscaledVal); long compactVal = compactValFor(unscaledVal); int mcp = mc.precision; int prec = 0; @@ -4199,9 +4213,13 @@ private static class UnsafeHolder { = unsafe.objectFieldOffset(BigDecimal.class, "intCompact"); private static final long intValOffset = unsafe.objectFieldOffset(BigDecimal.class, "intVal"); + private static final long scaleOffset + = unsafe.objectFieldOffset(BigDecimal.class, "scale"); - static void setIntCompact(BigDecimal bd, long val) { - unsafe.putLong(bd, intCompactOffset, val); + static void setIntValAndScale(BigDecimal bd, BigInteger intVal, int scale) { + unsafe.putObject(bd, intValOffset, intVal); + unsafe.putInt(bd, scaleOffset, scale); + unsafe.putLong(bd, intCompactOffset, compactValFor(intVal)); } static void setIntValVolatile(BigDecimal bd, BigInteger val) { @@ -4217,15 +4235,29 @@ static void setIntValVolatile(BigDecimal bd, BigInteger val) { */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - // Read in all fields - s.defaultReadObject(); - // validate possibly bad fields - if (intVal == null) { - String message = "BigDecimal: null intVal in stream"; - throw new java.io.StreamCorruptedException(message); - // [all values of scale are now allowed] + // prepare to read the fields + ObjectInputStream.GetField fields = s.readFields(); + BigInteger serialIntVal = (BigInteger) fields.get("intVal", null); + + // Validate field data + if (serialIntVal == null) { + throw new StreamCorruptedException("Null or missing intVal in BigDecimal stream"); } - UnsafeHolder.setIntCompact(this, compactValFor(intVal)); + // Validate provenance of serialIntVal object + serialIntVal = toStrictBigInteger(serialIntVal); + + // Any integer value is valid for scale + int serialScale = fields.get("scale", 0); + + UnsafeHolder.setIntValAndScale(this, serialIntVal, serialScale); + } + + /** + * Serialization without data not supported for this class. + */ + private void readObjectNoData() + throws ObjectStreamException { + throw new InvalidObjectException("Deserialized BigDecimal objects need data"); } /** diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 8f577595a19..56b14add8aa 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -30,9 +30,11 @@ package java.math; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.ObjectStreamException; import java.util.Arrays; import java.util.Objects; import java.util.Random; @@ -4645,17 +4647,21 @@ private void readObject(java.io.ObjectInputStream s) // prepare to read the alternate persistent fields ObjectInputStream.GetField fields = s.readFields(); - // Read the alternate persistent fields that we care about - int sign = fields.get("signum", -2); - byte[] magnitude = (byte[])fields.get("magnitude", null); + // Read and validate the alternate persistent fields that we + // care about, signum and magnitude - // Validate signum + // Read and validate signum + int sign = fields.get("signum", -2); if (sign < -1 || sign > 1) { String message = "BigInteger: Invalid signum value"; if (fields.defaulted("signum")) message = "BigInteger: Signum not present in stream"; throw new java.io.StreamCorruptedException(message); } + + // Read and validate magnitude + byte[] magnitude = (byte[])fields.get("magnitude", null); + magnitude = magnitude.clone(); // defensive copy int[] mag = stripLeadingZeroBytes(magnitude, 0, magnitude.length); if ((mag.length == 0) != (sign == 0)) { String message = "BigInteger: signum-magnitude mismatch"; @@ -4664,18 +4670,23 @@ private void readObject(java.io.ObjectInputStream s) throw new java.io.StreamCorruptedException(message); } + // Equivalent to checkRange() on mag local without assigning + // this.mag field + if (mag.length > MAX_MAG_LENGTH || + (mag.length == MAX_MAG_LENGTH && mag[0] < 0)) { + throw new java.io.StreamCorruptedException("BigInteger: Out of the supported range"); + } + // Commit final fields via Unsafe - UnsafeHolder.putSign(this, sign); + UnsafeHolder.putSignAndMag(this, sign, mag); + } - // Calculate mag field from magnitude and discard magnitude - UnsafeHolder.putMag(this, mag); - if (mag.length >= MAX_MAG_LENGTH) { - try { - checkRange(); - } catch (ArithmeticException e) { - throw new java.io.StreamCorruptedException("BigInteger: Out of the supported range"); - } - } + /** + * Serialization without data not supported for this class. + */ + private void readObjectNoData() + throws ObjectStreamException { + throw new InvalidObjectException("Deserialized BigInteger objects need data"); } // Support for resetting final fields while deserializing @@ -4687,11 +4698,8 @@ private static class UnsafeHolder { private static final long magOffset = unsafe.objectFieldOffset(BigInteger.class, "mag"); - static void putSign(BigInteger bi, int sign) { + static void putSignAndMag(BigInteger bi, int sign, int[] magnitude) { unsafe.putInt(bi, signumOffset, sign); - } - - static void putMag(BigInteger bi, int[] magnitude) { unsafe.putObject(bi, magOffset, magnitude); } } diff --git a/src/java.base/share/classes/java/security/UnresolvedPermission.java b/src/java.base/share/classes/java/security/UnresolvedPermission.java index cc60836a91b..63da0d3175a 100644 --- a/src/java.base/share/classes/java/security/UnresolvedPermission.java +++ b/src/java.base/share/classes/java/security/UnresolvedPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -152,7 +152,7 @@ public final class UnresolvedPermission extends Permission * Each chain is ordered bottom-to-top (i.e., with the signer certificate * first and the (root) certificate authority last). The signer * certificates are copied from the array. Subsequent changes to - * the array will not affect this UnsolvedPermission. + * the array will not affect this UnresolvedPermission. */ public UnresolvedPermission(String type, String name, @@ -164,59 +164,63 @@ public UnresolvedPermission(String type, if (type == null) throw new NullPointerException("type can't be null"); + // Perform a defensive copy and reassign certs if we have a non-null + // reference + if (certs != null) { + certs = certs.clone(); + } + this.type = type; this.name = name; this.actions = actions; + if (certs != null) { // Extract the signer certs from the list of certificates. - for (int i=0; i signerCerts = - new ArrayList<>(); - i = 0; - while (i < certs.length) { - signerCerts.add(certs[i]); - while (((i+1) < certs.length) && - ((X509Certificate)certs[i]).getIssuerDN().equals( - ((X509Certificate)certs[i+1]).getSubjectDN())) { - i++; - } - i++; - } - this.certs = - new java.security.cert.Certificate[signerCerts.size()]; - signerCerts.toArray(this.certs); + // extract the signer certs + ArrayList signerCerts = + new ArrayList<>(); + i = 0; + while (i < certs.length) { + signerCerts.add(certs[i]); + while (((i + 1) < certs.length) && + ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i + 1]).getSubjectDN())) { + i++; } + i++; } + this.certs = + new java.security.cert.Certificate[signerCerts.size()]; + signerCerts.toArray(this.certs); } } @@ -309,6 +313,7 @@ Permission resolve(Permission p, java.security.cert.Certificate[] certs) { * * @return false. */ + @Override public boolean implies(Permission p) { return false; } @@ -329,6 +334,7 @@ public boolean implies(Permission p) { * type (class) name, permission name, actions, and * certificates as this object. */ + @Override public boolean equals(Object obj) { if (obj == this) return true; @@ -402,7 +408,7 @@ public boolean equals(Object obj) { * * @return a hash code value for this object. */ - + @Override public int hashCode() { int hash = type.hashCode(); if (name != null) @@ -422,6 +428,7 @@ public int hashCode() { * * @return the empty string "". */ + @Override public String getActions() { return ""; @@ -489,6 +496,7 @@ public java.security.cert.Certificate[] getUnresolvedCerts() { * * @return information about this UnresolvedPermission. */ + @Override public String toString() { return "(unresolved " + type + " " + name + " " + actions + ")"; } @@ -500,7 +508,7 @@ public String toString() { * @return a new PermissionCollection object suitable for * storing UnresolvedPermissions. */ - + @Override public PermissionCollection newPermissionCollection() { return new UnresolvedPermissionCollection(); } diff --git a/src/java.base/share/classes/sun/security/util/DerInputStream.java b/src/java.base/share/classes/sun/security/util/DerInputStream.java index c39e4beb440..53acf37c62b 100644 --- a/src/java.base/share/classes/sun/security/util/DerInputStream.java +++ b/src/java.base/share/classes/sun/security/util/DerInputStream.java @@ -272,6 +272,9 @@ public byte[] getOctetString() throws IOException { throw new IOException("DER input not an octet string"); int length = getDefiniteLength(buffer); + if (length > buffer.available()) + throw new IOException("short read of an octet string"); + byte[] retval = new byte[length]; if ((length != 0) && (buffer.read(retval) != length)) throw new IOException("Short read of DER octet string"); @@ -497,6 +500,10 @@ private String readString(byte stringTag, String stringName, stringName + " string"); int length = getDefiniteLength(buffer); + if (length > buffer.available()) + throw new IOException("short read of " + + stringName + " string"); + byte[] retval = new byte[length]; if ((length != 0) && (buffer.read(retval) != length)) throw new IOException("Short read of DER " + diff --git a/src/java.base/share/classes/sun/security/util/ManifestDigester.java b/src/java.base/share/classes/sun/security/util/ManifestDigester.java index 3920d8a6b76..969df7c0eb8 100644 --- a/src/java.base/share/classes/sun/security/util/ManifestDigester.java +++ b/src/java.base/share/classes/sun/security/util/ManifestDigester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -331,8 +331,12 @@ public Entry getMainAttsEntry() { * @see #MF_MAIN_ATTRS */ public Entry getMainAttsEntry(boolean oldStyle) { - mainAttsEntry.oldStyle = oldStyle; - return mainAttsEntry; + if (mainAttsEntry != null) { + mainAttsEntry.oldStyle = oldStyle; + return mainAttsEntry; + } else { + return null; + } } public Entry get(String name) { diff --git a/src/java.base/share/native/libjava/sizecalc.h b/src/java.desktop/share/native/include/sizecalc.h similarity index 92% rename from src/java.base/share/native/libjava/sizecalc.h rename to src/java.desktop/share/native/include/sizecalc.h index 675750e8a46..8f9cc8d7bf8 100644 --- a/src/java.base/share/native/libjava/sizecalc.h +++ b/src/java.desktop/share/native/include/sizecalc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,13 @@ #define IS_SAFE_SIZE_T(x) ((x) >= 0 && (unsigned long long)(x) <= SIZE_MAX) +#define IS_MUL_OVERFLOW(m, n) \ + ((m) != 0 && (n) != 0 && (((size_t)((m)*(n))) != (((size_t)(m)) * ((size_t)(n))))) + #define IS_SAFE_SIZE_MUL(m, n) \ - (IS_SAFE_SIZE_T(m) && IS_SAFE_SIZE_T(n) && ((m) == 0 || (n) == 0 || (size_t)(n) <= (SIZE_MAX / (size_t)(m)))) + (IS_SAFE_SIZE_T(m) && IS_SAFE_SIZE_T(n) && \ + ((m) == 0 || (n) == 0 || (size_t)(n) <= (SIZE_MAX / (size_t)(m))) && \ + !IS_MUL_OVERFLOW(m, n)) #define IS_SAFE_SIZE_ADD(a, b) \ (IS_SAFE_SIZE_T(a) && IS_SAFE_SIZE_T(b) && (size_t)(b) <= (SIZE_MAX - (size_t)(a))) diff --git a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp index 6e6d5bd0210..516d77e723e 100644 --- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1010,7 +1010,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits // limit iconSize to MAX_ICON_SIZE, so that the colorBits and maskBits // arrays are big enough. // (logic: rather show bad icons than overrun the array size) - iconSize = iconSize > MAX_ICON_SIZE ? MAX_ICON_SIZE : iconSize; + iconSize = (iconSize <= 0 || iconSize > MAX_ICON_SIZE) ? MAX_ICON_SIZE : iconSize; // Set up BITMAPINFO BITMAPINFO bmi; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java index 41ee0a81614..13e105b7b0d 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java @@ -103,7 +103,7 @@ SocketChannel channel() { @Override ConnectionPool.CacheKey cacheKey() { - return ConnectionPool.cacheKey(address, null); + return ConnectionPool.cacheKey(true, address, null); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java index 00e48c965d0..49a323ecf44 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java @@ -105,7 +105,7 @@ public String toString() { @Override ConnectionPool.CacheKey cacheKey() { - return ConnectionPool.cacheKey(address, plainConnection.proxyAddr); + return ConnectionPool.cacheKey(true, address, plainConnection.proxyAddr); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java index 50a988b1fda..186d397698f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java @@ -26,7 +26,6 @@ package jdk.internal.net.http; import java.io.IOException; -import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.time.Instant; @@ -68,18 +67,21 @@ final class ConnectionPool { /** * Entries in connection pool are keyed by destination address and/or * proxy address: - * case 1: plain TCP not via proxy (destination only) + * case 1: plain TCP not via proxy (destination IP only) * case 2: plain TCP via proxy (proxy only) - * case 3: SSL not via proxy (destination only) - * case 4: SSL over tunnel (destination and proxy) + * case 3: SSL not via proxy (destination IP+hostname only) + * case 4: SSL over tunnel (destination IP+hostname and proxy) */ static class CacheKey { final InetSocketAddress proxy; final InetSocketAddress destination; + final boolean secure; - CacheKey(InetSocketAddress destination, InetSocketAddress proxy) { + private CacheKey(boolean secure, InetSocketAddress destination, + InetSocketAddress proxy) { this.proxy = proxy; this.destination = destination; + this.secure = secure; } @Override @@ -91,12 +93,26 @@ public boolean equals(Object obj) { return false; } final CacheKey other = (CacheKey) obj; + if (this.secure != other.secure) { + return false; + } if (!Objects.equals(this.proxy, other.proxy)) { return false; } if (!Objects.equals(this.destination, other.destination)) { return false; } + if (secure && destination != null) { + if (destination.getHostName() != null) { + if (!destination.getHostName().equalsIgnoreCase( + other.destination.getHostName())) { + return false; + } + } else { + if (other.destination.getHostName() != null) + return false; + } + } return true; } @@ -128,17 +144,17 @@ synchronized void start() { assert !stopped : "Already stopped"; } - static CacheKey cacheKey(InetSocketAddress destination, + static CacheKey cacheKey(boolean secure, InetSocketAddress destination, InetSocketAddress proxy) { - return new CacheKey(destination, proxy); + return new CacheKey(secure, destination, proxy); } synchronized HttpConnection getConnection(boolean secure, InetSocketAddress addr, InetSocketAddress proxy) { if (stopped) return null; - CacheKey key = new CacheKey(addr, proxy); + CacheKey key = new CacheKey(secure, addr, proxy); HttpConnection c = secure ? findConnection(key, sslPool) : findConnection(key, plainPool); //System.out.println ("getConnection returning: " + c); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java index 2ccab8e41d4..98f4333ed0a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java @@ -283,7 +283,7 @@ public void close() { @Override ConnectionPool.CacheKey cacheKey() { - return new ConnectionPool.CacheKey(address, null); + return ConnectionPool.cacheKey(false, address, null); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java index 6d8ffda7465..5cd9a1d9c8f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java @@ -35,7 +35,7 @@ class PlainProxyConnection extends PlainHttpConnection { @Override ConnectionPool.CacheKey cacheKey() { - return new ConnectionPool.CacheKey(null, address); + return ConnectionPool.cacheKey(false, null, address); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java index 85e656ba021..df573487d4f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java @@ -30,7 +30,6 @@ import java.net.http.HttpTimeoutException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.function.Function; @@ -152,7 +151,7 @@ FlowTube getConnectionFlow() { @Override ConnectionPool.CacheKey cacheKey() { - return new ConnectionPool.CacheKey(null, proxyAddr); + return ConnectionPool.cacheKey(false, null, proxyAddr); } @Override diff --git a/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp b/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp index e5ec1318d75..96f994ba43e 100644 --- a/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp +++ b/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp @@ -893,7 +893,7 @@ gss_init_sec_context(OM_uint32 *minor_status, gss_buffer_desc tn; gss_display_name(&minor, target_name, &tn, NULL); int len = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)tn.value, (int)tn.length, - outName, sizeof(outName) - 1); + outName, (sizeof(outName) / sizeof(outName[0])) - 1); if (len == 0) { goto err; } diff --git a/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c b/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c index 299135d9914..b344b9d9599 100644 --- a/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c +++ b/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or @@ -925,6 +925,12 @@ ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, } ecParams = &(key->ecParams); + + if (EC_ValidatePublicKey(ecParams, &key->publicValue, kmflag) != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_KEY); + goto cleanup; + } + flen = (ecParams->fieldID.size + 7) >> 3; olen = ecParams->order.len; if (signature->len == 0 || signature->len%2 != 0 || diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpConnection.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpConnection.java index 1107d5ef469..500edcea6b6 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpConnection.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,8 +30,6 @@ import java.nio.channels.*; import java.lang.System.Logger; import java.lang.System.Logger.Level; -import com.sun.net.httpserver.*; -import com.sun.net.httpserver.spi.*; /** * encapsulates all the connection specific state for a HTTP/S connection @@ -55,14 +53,14 @@ class HttpConnection { SocketChannel chan; SelectionKey selectionKey; String protocol; - long time; - volatile long creationTime; // time this connection was created + long idleStartTime; // absolute time in milli seconds, starting when the connection was marked idle + volatile long reqStartedTime; // time when the request was initiated volatile long rspStartedTime; // time we started writing the response int remaining; boolean closed = false; Logger logger; - public enum State {IDLE, REQUEST, RESPONSE}; + public enum State {IDLE, REQUEST, RESPONSE, NEWLY_ACCEPTED}; volatile State state; public String toString() { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/SSLStreams.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/SSLStreams.java index 04949f0adb8..bda5cceb215 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/SSLStreams.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/SSLStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,6 @@ class SSLStreams { SSLContext sslctx; SocketChannel chan; - TimeSource time; ServerImpl server; SSLEngine engine; EngineWrapper wrapper; @@ -56,7 +55,6 @@ class SSLStreams { SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException { this.server = server; - this.time= (TimeSource)server; this.sslctx= sslctx; this.chan= chan; InetSocketAddress addr = diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java index 0ff0e436828..34a50fcab3f 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,29 +36,35 @@ class ServerConfig { - private static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec. + private static final int DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS = 10000 ; // 10 sec. - /* These values must be a reasonable multiple of clockTick */ - private static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min + private static final long DEFAULT_IDLE_INTERVAL_IN_SECS = 30; + private static final int DEFAULT_MAX_CONNECTIONS = -1 ; // no limit on maximum connections private static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ; private static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever private static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever - private static final long DEFAULT_TIMER_MILLIS = 1000; + // default timer schedule, in milli seconds, for the timer task that's responsible for + // timing out request/response if max request/response time is configured + private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000; private static final int DEFAULT_MAX_REQ_HEADERS = 200; private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024; - private static int clockTick; - private static long idleInterval; + private static long idleTimerScheduleMillis; + private static long idleIntervalMillis; // The maximum number of bytes to drain from an inputstream private static long drainAmount; + // the maximum number of connections that the server will allow to be open + // after which it will no longer "accept()" any new connections, till the + // current connection count goes down due to completion of processing the requests + private static int maxConnections; private static int maxIdleConnections; // The maximum number of request headers allowable private static int maxReqHeaders; // max time a request or response is allowed to take private static long maxReqTime; private static long maxRspTime; - private static long timerMillis; + private static long reqRspTimerScheduleMillis; private static boolean debug; // the value of the TCP_NODELAY socket-level option @@ -69,11 +75,22 @@ class ServerConfig { new PrivilegedAction() { @Override public Void run () { - idleInterval = Long.getLong("sun.net.httpserver.idleInterval", - DEFAULT_IDLE_INTERVAL) * 1000; + idleIntervalMillis = Long.getLong("sun.net.httpserver.idleInterval", + DEFAULT_IDLE_INTERVAL_IN_SECS) * 1000; + if (idleIntervalMillis <= 0) { + idleIntervalMillis = DEFAULT_IDLE_INTERVAL_IN_SECS * 1000; + } + + idleTimerScheduleMillis = Long.getLong("sun.net.httpserver.clockTick", + DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS); + if (idleTimerScheduleMillis <= 0) { + // ignore zero or negative value and use the default schedule + idleTimerScheduleMillis = DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS; + } - clockTick = Integer.getInteger("sun.net.httpserver.clockTick", - DEFAULT_CLOCK_TICK); + maxConnections = Integer.getInteger( + "jdk.httpserver.maxConnections", + DEFAULT_MAX_CONNECTIONS); maxIdleConnections = Integer.getInteger( "sun.net.httpserver.maxIdleConnections", @@ -92,8 +109,13 @@ public Void run () { maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", DEFAULT_MAX_RSP_TIME); - timerMillis = Long.getLong("sun.net.httpserver.timerMillis", - DEFAULT_TIMER_MILLIS); + reqRspTimerScheduleMillis = Long.getLong("sun.net.httpserver.timerMillis", + DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS); + if (reqRspTimerScheduleMillis <= 0) { + // ignore any negative or zero value for this configuration and reset + // to default schedule + reqRspTimerScheduleMillis = DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS; + } debug = Boolean.getBoolean("sun.net.httpserver.debug"); @@ -149,14 +171,34 @@ static boolean debugEnabled() { return debug; } - static long getIdleInterval() { - return idleInterval; + /** + * {@return Returns the maximum duration, in milli seconds, a connection can be idle} + */ + static long getIdleIntervalMillis() { + return idleIntervalMillis; + } + + /** + * {@return Returns the schedule, in milli seconds, for the timer task that is responsible + * for managing the idle connections} + */ + static long getIdleTimerScheduleMillis() { + return idleTimerScheduleMillis; } - static int getClockTick() { - return clockTick; + /** + * @return Returns the maximum number of connections that can be open at any given time. + * This method can return a value of 0 or negative to represent that the limit hasn't + * been configured. + */ + static int getMaxConnections() { + return maxConnections; } + /** + * @return Returns the maximum number of connections that can be idle. This method + * can return a value of 0 or negative. + */ static int getMaxIdleConnections() { return maxIdleConnections; } @@ -169,16 +211,30 @@ static int getMaxReqHeaders() { return maxReqHeaders; } + /** + * @return Returns the maximum amount of time the server will wait for the request to be read + * completely. This method can return a value of 0 or negative to imply no maximum limit has + * been configured. + */ static long getMaxReqTime() { return maxReqTime; } + /** + * @return Returns the maximum amount of time the server will wait for the response to be generated + * for a request that is being processed. This method can return a value of 0 or negative to + * imply no maximum limit has been configured. + */ static long getMaxRspTime() { return maxRspTime; } - static long getTimerMillis() { - return timerMillis; + /** + * {@return Returns the timer schedule of the task that's responsible for timing out + * request/response that have been running longer than any configured timeout} + */ + static long getReqRspTimerScheduleMillis() { + return reqRspTimerScheduleMillis; } static boolean noDelay() { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index dc3a54c1c31..1b84ab2e681 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -25,23 +25,49 @@ package sun.net.httpserver; -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; +import com.sun.net.httpserver.Filter; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import sun.net.httpserver.HttpConnection.State; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.System.Logger; import java.lang.System.Logger.Level; -import javax.net.ssl.*; -import com.sun.net.httpserver.*; +import java.net.BindException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.net.httpserver.HttpConnection.State; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executor; /** * Provides implementation for both HTTP and HTTPS */ -class ServerImpl implements TimeSource { +class ServerImpl { private String protocol; private boolean https; @@ -53,32 +79,47 @@ class ServerImpl implements TimeSource { private ServerSocketChannel schan; private Selector selector; private SelectionKey listenerKey; - private Set idleConnections; - private Set allConnections; + private final Set idleConnections; + // connections which have been accepted() by the server but which haven't + // yet sent any byte on the connection yet + private final Set newlyAcceptedConnections; + private final Set allConnections; /* following two are used to keep track of the times * when a connection/request is first received * and when we start to send the response */ - private Set reqConnections; - private Set rspConnections; + private final Set reqConnections; + private final Set rspConnections; private List events; - private Object lolock = new Object(); + private final Object lolock = new Object(); private volatile boolean finished = false; private volatile boolean terminating = false; private boolean bound = false; private boolean started = false; - private volatile long time; /* current time */ - private volatile long subticks = 0; - private volatile long ticks; /* number of clock ticks since server started */ private HttpServer wrapper; - final static int CLOCK_TICK = ServerConfig.getClockTick(); - final static long IDLE_INTERVAL = ServerConfig.getIdleInterval(); + // schedule for the timer task that's responsible for idle connection management + static final long IDLE_TIMER_TASK_SCHEDULE = ServerConfig.getIdleTimerScheduleMillis(); + static final int MAX_CONNECTIONS = ServerConfig.getMaxConnections(); final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); - final static long TIMER_MILLIS = ServerConfig.getTimerMillis (); - final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime()); - final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime()); - final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; + // schedule for the timer task that's responsible for request/response timeout management + static final long REQ_RSP_TIMER_SCHEDULE = ServerConfig.getReqRspTimerScheduleMillis(); + static final long MAX_REQ_TIME = getTimeMillis(ServerConfig.getMaxReqTime()); + static final long MAX_RSP_TIME = getTimeMillis(ServerConfig.getMaxRspTime()); + static final boolean reqRspTimeoutEnabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; + // the maximum idle duration for a connection which is currently idle but has served + // some request in the past + static final long IDLE_INTERVAL = ServerConfig.getIdleIntervalMillis(); + // the maximum idle duration for a newly accepted connection which hasn't yet received + // the first byte of data on that connection + static final long NEWLY_ACCEPTED_CONN_IDLE_INTERVAL; + static { + // the idle duration of a newly accepted connection is considered to be the least of the + // configured idle interval and the configured max request time (if any). + NEWLY_ACCEPTED_CONN_IDLE_INTERVAL = MAX_REQ_TIME > 0 + ? Math.min(IDLE_INTERVAL, MAX_REQ_TIME) + : IDLE_INTERVAL; + } private Timer timer, timer1; private final Logger logger; @@ -109,13 +150,14 @@ class ServerImpl implements TimeSource { allConnections = Collections.synchronizedSet (new HashSet()); reqConnections = Collections.synchronizedSet (new HashSet()); rspConnections = Collections.synchronizedSet (new HashSet()); - time = System.currentTimeMillis(); - timer = new Timer ("server-timer", true); - timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK); - if (timer1Enabled) { - timer1 = new Timer ("server-timer1", true); - timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS); - logger.log (Level.DEBUG, "HttpServer timer1 enabled period in ms: ", TIMER_MILLIS); + newlyAcceptedConnections = Collections.synchronizedSet(new HashSet<>()); + timer = new Timer ("idle-timeout-task", true); + timer.schedule (new IdleTimeoutTask(), IDLE_TIMER_TASK_SCHEDULE, IDLE_TIMER_TASK_SCHEDULE); + if (reqRspTimeoutEnabled) { + timer1 = new Timer ("req-rsp-timeout-task", true); + timer1.schedule (new ReqRspTimeoutTask(), REQ_RSP_TIMER_SCHEDULE, REQ_RSP_TIMER_SCHEDULE); + logger.log(Level.DEBUG, "HttpServer request/response timeout task schedule ms: ", + REQ_RSP_TIMER_SCHEDULE); logger.log (Level.DEBUG, "MAX_REQ_TIME: "+MAX_REQ_TIME); logger.log (Level.DEBUG, "MAX_RSP_TIME: "+MAX_RSP_TIME); } @@ -206,8 +248,9 @@ public void stop (int delay) { } allConnections.clear(); idleConnections.clear(); + newlyAcceptedConnections.clear(); timer.cancel(); - if (timer1Enabled) { + if (reqRspTimeoutEnabled) { timer1.cancel(); } if (dispatcherThread != null && dispatcherThread != Thread.currentThread()) { @@ -269,10 +312,6 @@ public InetSocketAddress run() { }); } - Selector getSelector () { - return selector; - } - void addEvent (Event r) { synchronized (lolock) { events.add (r); @@ -282,6 +321,70 @@ void addEvent (Event r) { /* main server listener task */ + /** + * The Dispatcher is responsible for accepting any connections and then using those connections + * to processing any incoming requests. A connection is represented as an instance of + * sun.net.httpserver.HttpConnection. + * + * Connection states: + * An instance of HttpConnection goes through the following states: + * + * - NEWLY_ACCEPTED: A connection is marked as newly accepted as soon as the Dispatcher + * accept()s a connection. A newly accepted connection is added to a newlyAcceptedConnections + * collection. A newly accepted connection also gets added to the allConnections collection. + * The newlyAcceptedConnections isn't checked for any size limits, however, if the server is + * configured with a maximum connection limit, then the elements in the + * newlyAcceptedConnections will never exceed that configured limit (no explicit size checks + * are done on the newlyAcceptedConnections collection, since the maximum connection limit + * applies to connections across different connection states). A connection in NEWLY_ACCEPTED + * state is considered idle and is eligible for idle connection management. + * + * - REQUEST: A connection is marked to be in REQUEST state when the request processing starts + * on that connection. This typically happens when the first byte of data is received on a + * NEWLY_ACCEPTED connection or when new data arrives on a connection which was previously + * in IDLE state. When a connection is in REQUEST state, it implies that the connection is + * active and thus isn't eligible for idle connection management. If the server is configured + * with a maximum request timeout, then connections in REQUEST state are eligible + * for Request/Response timeout management. + * + * - RESPONSE: A connection is marked to be in RESPONSE state when the server has finished + * reading the request. A connection is RESPONSE state is considered active and isn't eligible + * for idle connection management. If the server is configured with a maximum response timeout, + * then connections in RESPONSE state are eligible for Request/Response timeout management. + * + * - IDLE: A connection is marked as IDLE when a request/response cycle (successfully) completes + * on that particular connection. Idle connections are held in a idleConnections collection. + * The idleConnections collection is limited in size and the size is decided by a server + * configuration. Connections in IDLE state get added to the idleConnections collection only + * if that collection hasn't reached the configured limit. If a connection has reached IDLE + * state and there's no more room in the idleConnections collection, then such a connection + * gets closed. Connections in idleConnections collection are eligible for idle connection + * management. + * + * Idle connection management: + * A timer task is responsible for closing idle connections. Each connection that is in a state + * which is eligible for idle timeout management (see above section on connection states) + * will have a corresponding idle expiration time associated with it. The idle timeout management + * task will check the expiration time of each such connection against the current time and will + * close the connection if the current time is either equal to or past the expiration time. + * + * Request/Response timeout management: + * The server can be optionally configured with a maximum request timeout and/or maximum response + * timeout. If either of these timeouts have been configured, then an additional timer task is + * run by the server. This timer task is then responsible for closing connections which have + * been in REQUEST or RESPONSE state for a period of time that exceeds the respective configured + * timeouts. + * + * Maximum connection limit management: + * The server can be optionally configured with a maximum connection limit. A value of 0 or + * negative integer is ignored and considered to represent no connection limit. In case of a + * positive integer value, any newly accepted connections will be first checked against the + * current count of established connections (held by the allConnections collection) and if the + * configured limit has reached, then the newly accepted connection will be closed immediately + * (even before setting its state to NEWLY_ACCEPTED or adding it to the newlyAcceptedConnections + * collection). + * + */ class Dispatcher implements Runnable { private void handleEvent (Event r) { @@ -335,8 +438,7 @@ void reRegister (HttpConnection c) { SelectionKey key = chan.register (selector, SelectionKey.OP_READ); key.attach (c); c.selectionKey = key; - c.time = getTime() + IDLE_INTERVAL; - idleConnections.add (c); + markIdle(c); } catch (IOException e) { dprint(e); logger.log (Level.TRACE, "Dispatcher(8)", e); @@ -379,9 +481,19 @@ public void run() { continue; } SocketChannel chan = schan.accept(); - // optimist there's a channel if (chan != null) { + if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) { + // we've hit max limit of current open connections, so we go + // ahead and close this connection without processing it + try { + chan.close(); + } catch (IOException ignore) { + } + // move on to next selected key + continue; + } + // Set TCP_NODELAY, if appropriate if (ServerConfig.noDelay()) { chan.socket().setTcpNoDelay(true); @@ -393,7 +505,7 @@ public void run() { c.selectionKey = newkey; c.setChannel (chan); newkey.attach (c); - requestStarted (c); + markNewlyAccepted(c); allConnections.add (c); } } else { @@ -404,10 +516,12 @@ public void run() { key.cancel(); chan.configureBlocking (true); - if (idleConnections.remove(conn)) { - // was an idle connection so add it - // to reqConnections set. - requestStarted (conn); + if (newlyAcceptedConnections.remove(conn) + || idleConnections.remove(conn)) { + // was either a newly accepted connection or an idle + // connection. In either case, we mark that the request + // has now started on this connection. + requestStarted(conn); } handle (chan, conn); } else { @@ -489,10 +603,14 @@ private void closeConnection(HttpConnection conn) { case IDLE: idleConnections.remove(conn); break; + case NEWLY_ACCEPTED: + newlyAcceptedConnections.remove(conn); + break; } assert !reqConnections.remove(conn); assert !rspConnections.remove(conn); assert !idleConnections.remove(conn); + assert !newlyAcceptedConnections.remove(conn); } /* per exchange task */ @@ -652,9 +770,9 @@ public void run () { rheaders.set ("Connection", "close"); } else if (chdr.equalsIgnoreCase ("keep-alive")) { rheaders.set ("Connection", "keep-alive"); - int idle=(int)(ServerConfig.getIdleInterval()/1000); + int idleSeconds = (int) (ServerConfig.getIdleIntervalMillis() / 1000); int max=ServerConfig.getMaxIdleConnections(); - String val = "timeout="+idle+", max="+max; + String val = "timeout="+idleSeconds+", max="+max; rheaders.set ("Keep-Alive", val); } } @@ -793,14 +911,6 @@ void logReply (int code, String requestStr, String text) { logger.log (Level.DEBUG, message); } - long getTicks() { - return ticks; - } - - public long getTime() { - return time; - } - void delay () { Thread.yield(); try { @@ -825,11 +935,23 @@ HttpServer getWrapper () { } void requestStarted (HttpConnection c) { - c.creationTime = getTime(); + c.reqStartedTime = System.currentTimeMillis(); c.setState (State.REQUEST); reqConnections.add (c); } + void markIdle(HttpConnection c) { + c.idleStartTime = System.currentTimeMillis(); + c.setState(State.IDLE); + idleConnections.add(c); + } + + void markNewlyAccepted(HttpConnection c) { + c.idleStartTime = System.currentTimeMillis(); + c.setState(State.NEWLY_ACCEPTED); + newlyAcceptedConnections.add(c); + } + // called after a request has been completely read // by the server. This stops the timer which would // close the connection if the request doesn't arrive @@ -841,7 +963,7 @@ void requestCompleted (HttpConnection c) { State s = c.getState(); assert s == State.REQUEST : "State is not REQUEST ("+s+")"; reqConnections.remove (c); - c.rspStartedTime = getTime(); + c.rspStartedTime = System.currentTimeMillis(); rspConnections.add (c); c.setState (State.RESPONSE); } @@ -855,38 +977,58 @@ void responseCompleted (HttpConnection c) { } /** + * Responsible for closing connections that have been idle. * TimerTask run every CLOCK_TICK ms */ - class ServerTimerTask extends TimerTask { + class IdleTimeoutTask extends TimerTask { public void run () { LinkedList toClose = new LinkedList(); - time = System.currentTimeMillis(); - ticks ++; + final long currentTime = System.currentTimeMillis(); synchronized (idleConnections) { - for (HttpConnection c : idleConnections) { - if (c.time <= time) { - toClose.add (c); + final Iterator it = idleConnections.iterator(); + while (it.hasNext()) { + final HttpConnection c = it.next(); + if (currentTime - c.idleStartTime >= IDLE_INTERVAL) { + toClose.add(c); + it.remove(); } } - for (HttpConnection c : toClose) { - idleConnections.remove (c); - allConnections.remove (c); - c.close(); + } + // if any newly accepted connection has been idle (i.e. no byte has been sent on that + // connection during the configured idle timeout period) then close it as well + synchronized (newlyAcceptedConnections) { + final Iterator it = newlyAcceptedConnections.iterator(); + while (it.hasNext()) { + final HttpConnection c = it.next(); + if (currentTime - c.idleStartTime >= NEWLY_ACCEPTED_CONN_IDLE_INTERVAL) { + toClose.add(c); + it.remove(); + } + } + } + for (HttpConnection c : toClose) { + allConnections.remove(c); + c.close(); + if (logger.isLoggable(Level.TRACE)) { + logger.log(Level.TRACE, "Closed idle connection " + c); } } } } - class ServerTimerTask1 extends TimerTask { + /** + * Responsible for closing connections which have timed out while in REQUEST or RESPONSE state + */ + class ReqRspTimeoutTask extends TimerTask { // runs every TIMER_MILLIS public void run () { LinkedList toClose = new LinkedList(); - time = System.currentTimeMillis(); + final long currentTime = System.currentTimeMillis(); synchronized (reqConnections) { if (MAX_REQ_TIME != -1) { for (HttpConnection c : reqConnections) { - if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) { + if (currentTime - c.reqStartedTime >= MAX_REQ_TIME) { toClose.add (c); } } @@ -902,7 +1044,7 @@ public void run () { synchronized (rspConnections) { if (MAX_RSP_TIME != -1) { for (HttpConnection c : rspConnections) { - if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) { + if (currentTime - c.rspStartedTime >= MAX_RSP_TIME) { toClose.add (c); } } @@ -917,22 +1059,18 @@ public void run () { } } - void logStackTrace (String s) { - logger.log (Level.TRACE, s); - StringBuilder b = new StringBuilder (); - StackTraceElement[] e = Thread.currentThread().getStackTrace(); - for (int i=0; i 0 ? milli : -1; } /* diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/TimeSource.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/TimeSource.java deleted file mode 100644 index 6a6827c3845..00000000000 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/TimeSource.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.net.httpserver; - -interface TimeSource { - public long getTime(); -} diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java index 04b983f6920..e4a15d3a30f 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.net.SocketException; import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; import java.util.Objects; import java.util.Random; @@ -50,6 +52,21 @@ private EphemeralPortRange() {} static final int RANGE = UPPER - LOWER + 1; } + private static int findFirstFreePort() { + PrivilegedExceptionAction action = () -> new DatagramSocket(0); + int port; + try { + @SuppressWarnings({"deprecated", "removal"}) + DatagramSocket ds = AccessController.doPrivileged(action); + try (DatagramSocket ds1 = ds) { + port = ds1.getLocalPort(); + } + } catch (Exception x) { + port = 0; + } + return port; + } + // Records a subset of max {@code capacity} previously used ports static final class PortHistory { final int capacity; @@ -74,7 +91,10 @@ public boolean contains(int port) { public boolean add(int port) { if (ports[index] != 0) { // at max capacity // remove one port at random and store the new port there - ports[random.nextInt(capacity)] = port; + // don't remove the last port + int remove = random.nextInt(capacity); + if ((remove +1) % capacity == index) remove = index; + ports[index = remove] = port; } else { // there's a free slot ports[index] = port; } @@ -90,7 +110,8 @@ public boolean offer(int port) { } } - int lastport = 0; + int lastport = findFirstFreePort(); + int lastSystemAllocated = lastport; int suitablePortCount; int unsuitablePortCount; final ProtocolFamily family; // null (default) means dual stack @@ -147,13 +168,16 @@ public synchronized DatagramSocket open() throws SocketException { s = openDefault(); lastport = s.getLocalPort(); if (lastseen == 0) { + lastSystemAllocated = lastport; history.offer(lastport); return s; } thresholdCrossed = suitablePortCount > thresholdCount; - boolean farEnough = Integer.bitCount(lastseen ^ lastport) > BIT_DEVIATION - && Math.abs(lastport - lastseen) > deviation; + boolean farEnough = farEnough(lastseen); + if (farEnough && lastSystemAllocated > 0) { + farEnough = farEnough(lastSystemAllocated); + } boolean recycled = history.contains(lastport); boolean suitable = (thresholdCrossed || farEnough && !recycled); if (suitable && !recycled) history.add(lastport); @@ -168,6 +192,7 @@ public synchronized DatagramSocket open() throws SocketException { // Either the underlying stack supports random UDP port allocation, // or the new port is sufficiently distant from last port to make // it look like it is. Let's use it. + lastSystemAllocated = lastport; return s; } @@ -218,24 +243,48 @@ synchronized boolean isUndecided() { && !isUsingNativePortRandomization(); } + private boolean farEnough(int port) { + return Integer.bitCount(port ^ lastport) > BIT_DEVIATION + && Math.abs(port - lastport) > deviation; + } + private DatagramSocket openRandom() { int maxtries = MAX_RANDOM_TRIES; while (maxtries-- > 0) { - int port = EphemeralPortRange.LOWER - + random.nextInt(EphemeralPortRange.RANGE); + int port; + boolean suitable; + boolean recycled; + int maxrandom = MAX_RANDOM_TRIES; + do { + port = EphemeralPortRange.LOWER + + random.nextInt(EphemeralPortRange.RANGE); + recycled = history.contains(port); + suitable = lastport == 0 || (farEnough(port) && !recycled); + } while (maxrandom-- > 0 && !suitable); + + // if no suitable port was found, try again + // this means we might call random MAX_RANDOM_TRIES x MAX_RANDOM_TRIES + // times - but that should be OK with MAX_RANDOM_TRIES = 5. + if (!suitable) continue; + try { if (family != null) { DatagramChannel c = DatagramChannel.open(family); try { DatagramSocket s = c.socket(); s.bind(new InetSocketAddress(port)); + lastport = s.getLocalPort(); + if (!recycled) history.add(port); return s; } catch (Throwable x) { c.close(); throw x; } } - return new DatagramSocket(port); + DatagramSocket s = new DatagramSocket(port); + lastport = s.getLocalPort(); + if (!recycled) history.add(port); + return s; } catch (IOException x) { // try again until maxtries == 0; } diff --git a/test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java b/test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java index 6dded4f47cb..875b3fc7dfe 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java +++ b/test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 6725892 * @library /test/lib - * @run main/othervm -Dsun.net.httpserver.maxReqTime=2 Test + * @run main/othervm -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test * @summary */ diff --git a/test/jdk/java/math/BigDecimal/ConstructorUnscaledValue.java b/test/jdk/java/math/BigDecimal/ConstructorUnscaledValue.java new file mode 100644 index 00000000000..daac661fbf1 --- /dev/null +++ b/test/jdk/java/math/BigDecimal/ConstructorUnscaledValue.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8282252 + * @summary Test constructors of BigDecimal to replace BigInteger subclasses + */ + +import java.math.*; + +public class ConstructorUnscaledValue { + public static void main(String... args) { + TestBigInteger tbi = new TestBigInteger(BigInteger.ONE); + // Create BigDecimal's using each of the three constructors + // with guards on the class of unscaledValue + BigDecimal[] values = { + new BigDecimal(tbi), + new BigDecimal(tbi, 2), + new BigDecimal(tbi, 3, MathContext.DECIMAL32), + }; + + for (var bd : values) { + BigInteger unscaledValue = bd.unscaledValue(); + if (unscaledValue.getClass() != BigInteger.class) { + throw new RuntimeException("Bad class for unscaledValue"); + } + if (!unscaledValue.equals(BigInteger.ONE)) { + throw new RuntimeException("Bad value for unscaledValue"); + } + } + } + + private static class TestBigInteger extends BigInteger { + public TestBigInteger(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return java.util.Arrays.toString(toByteArray()); + } + } +} diff --git a/test/jdk/java/math/BigDecimal/SerializationTests.java b/test/jdk/java/math/BigDecimal/SerializationTests.java index 56b391ebdee..713a4e9a353 100644 --- a/test/jdk/java/math/BigDecimal/SerializationTests.java +++ b/test/jdk/java/math/BigDecimal/SerializationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,54 +23,127 @@ /* * @test - * @bug 6177836 + * @bug 6177836 8282252 * @summary Verify BigDecimal objects with collapsed values are serialized properly. - * @author Joseph D. Darcy */ import java.math.*; import java.io.*; +import java.util.List; public class SerializationTests { - static void checkSerialForm(BigDecimal bd) throws Exception { + public static void main(String... args) throws Exception { + checkBigDecimalSerialRoundTrip(); + checkBigDecimalSubSerialRoundTrip(); + } + + private static void checkSerialForm(BigDecimal bd) throws Exception { + checkSerialForm0(bd); + checkSerialForm0(bd.negate()); + } + + private static void checkSerialForm0(BigDecimal bd) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(bd); - oos.flush(); - oos.close(); + try(ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(bd); + oos.flush(); + } + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); BigDecimal tmp = (BigDecimal)ois.readObject(); if (!bd.equals(tmp) || - bd.hashCode() != tmp.hashCode()) { + bd.hashCode() != tmp.hashCode() || + bd.getClass() != tmp.getClass() || + // Directly test equality of components + bd.scale() != tmp.scale() || + !bd.unscaledValue().equals(tmp.unscaledValue())) { System.err.print(" original : " + bd); System.err.println(" (hash: 0x" + Integer.toHexString(bd.hashCode()) + ")"); System.err.print("serialized : " + tmp); System.err.println(" (hash: 0x" + Integer.toHexString(tmp.hashCode()) + ")"); throw new RuntimeException("Bad serial roundtrip"); } + + // If the class of the deserialized number is BigDecimal, + // verify the implementation constraint on the unscaled value + // having BigInteger class + if (tmp.getClass() == BigDecimal.class) { + if (tmp.unscaledValue().getClass() != BigInteger.class) { + throw new RuntimeException("Not using genuine BigInteger as an unscaled value"); + } + } } - public static void main(String[] args) throws Exception { - BigDecimal values[] = { - BigDecimal.ZERO, - BigDecimal.ONE, - BigDecimal.TEN, - new BigDecimal(0), - new BigDecimal(1), - new BigDecimal(10), - new BigDecimal(Integer.MAX_VALUE), - new BigDecimal(Long.MAX_VALUE-1), - new BigDecimal(BigInteger.valueOf(1), 1), - new BigDecimal(BigInteger.valueOf(100), 50), - }; + private static class BigIntegerSub extends BigInteger { + public BigIntegerSub(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return java.util.Arrays.toString(toByteArray()); + } + } + private static void checkBigDecimalSerialRoundTrip() throws Exception { + var values = + List.of(BigDecimal.ZERO, + BigDecimal.ONE, + BigDecimal.TEN, + new BigDecimal(0), + new BigDecimal(1), + new BigDecimal(10), + new BigDecimal(Integer.MAX_VALUE), + new BigDecimal(Long.MAX_VALUE-1), + new BigDecimal(BigInteger.valueOf(1), 1), + new BigDecimal(BigInteger.valueOf(100), 50), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MAX_VALUE), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MIN_VALUE), + new BigDecimal(new BigIntegerSub(BigInteger.ONE), 2)); for(BigDecimal value : values) { checkSerialForm(value); - checkSerialForm(value.negate()); } + } + + private static class BigDecimalSub extends BigDecimal { + public BigDecimalSub(BigDecimal bd) { + super(bd.unscaledValue(), bd.scale()); + } + + @Override + public String toString() { + return unscaledValue() + "x10^" + (-scale()); + } + } + // Subclass defining a serialVersionUID + private static class BigDecimalSubSVUID extends BigDecimal { + private static long serialVesionUID = 0x0123_4567_89ab_cdefL; + + public BigDecimalSubSVUID(BigDecimal bd) { + super(bd.unscaledValue(), bd.scale()); + } + } + + private static void checkBigDecimalSubSerialRoundTrip() throws Exception { + var values = + List.of(BigDecimal.ZERO, + BigDecimal.ONE, + BigDecimal.TEN, + new BigDecimal(BigInteger.TEN, 1234), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MAX_VALUE), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MIN_VALUE)); + + for(var value : values) { + checkSerialForm(new BigDecimalSub(value)); + checkSerialForm(new BigDecimalSubSVUID(value)); + } } } diff --git a/test/jdk/java/math/BigInteger/SerializationTests.java b/test/jdk/java/math/BigInteger/SerializationTests.java new file mode 100644 index 00000000000..19dd99d8117 --- /dev/null +++ b/test/jdk/java/math/BigInteger/SerializationTests.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8282252 + * @summary Verify BigInteger objects are serialized properly. + */ + +import java.math.*; +import java.io.*; +import java.util.Arrays; +import java.util.List; + +public class SerializationTests { + + public static void main(String... args) throws Exception { + checkBigIntegerSerialRoundTrip(); + checkBigIntegerSubSerialRoundTrip(); + } + + private static void checkSerialForm(BigInteger bi) throws Exception { + checkSerialForm0(bi); + checkSerialForm0(bi.negate()); + } + + private static void checkSerialForm0(BigInteger bi) throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try(ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(bi); + oos.flush(); + } + + ObjectInputStream ois = new + ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); + BigInteger tmp = (BigInteger)ois.readObject(); + + if (!bi.equals(tmp) || + bi.hashCode() != tmp.hashCode() || + bi.getClass() != tmp.getClass() || + // For extra measure, directly test equality of components + bi.signum() != tmp.signum() || + !Arrays.equals(bi.toByteArray(), (tmp.toByteArray())) ) { + System.err.print(" original : " + bi); + System.err.println(" (hash: 0x" + Integer.toHexString(bi.hashCode()) + ")"); + System.err.print("serialized : " + tmp); + System.err.println(" (hash: 0x" + Integer.toHexString(tmp.hashCode()) + ")"); + throw new RuntimeException("Bad serial roundtrip"); + } + } + + private static void checkBigIntegerSerialRoundTrip() throws Exception { + var values = + List.of(BigInteger.ZERO, + BigInteger.ONE, + BigInteger.TWO, + BigInteger.TEN, + BigInteger.valueOf(100), + BigInteger.valueOf(Integer.MAX_VALUE), + BigInteger.valueOf(Long.MAX_VALUE-1), + new BigInteger("9223372036854775808")); // Long.MAX_VALUE + 1 + + for(BigInteger value : values) { + checkSerialForm(value); + } + } + + // Subclass with specialized toString output + private static class BigIntegerSub extends BigInteger { + public BigIntegerSub(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return Arrays.toString(toByteArray()); + } + } + + // Subclass defining a serialVersionUID + private static class BigIntegerSubSVUID extends BigInteger { + private static long serialVesionUID = 0x0123_4567_89ab_cdefL; + + public BigIntegerSubSVUID(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return Arrays.toString(toByteArray()); + } + } + + // Subclass defining writeReplace + private static class BigIntegerSubWR extends BigInteger { + public BigIntegerSubWR(BigInteger bi) { + super(bi.toByteArray()); + } + + // Just return this; could use a serial proxy instead + private Object writeReplace() throws ObjectStreamException { + return this; + } + } + + + private static void checkBigIntegerSubSerialRoundTrip() throws Exception { + var values = List.of(BigInteger.ZERO, + BigInteger.ONE, + BigInteger.TEN, + new BigInteger("9223372036854775808")); // Long.MAX_VALUE + 1 + + for(var value : values) { + checkSerialForm(new BigIntegerSub(value)); + checkSerialForm(new BigIntegerSubSVUID(value)); + checkSerialForm(new BigIntegerSubWR(value)); + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java index 6c97e3d831a..5caa04b24b4 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java @@ -455,7 +455,7 @@ public HttpConnectionStub( InetSocketAddress proxy, boolean secured) { super(address, impl); - this.key = ConnectionPool.cacheKey(address, proxy); + this.key = ConnectionPool.cacheKey(secured, address, proxy); this.address = address; this.proxy = proxy; this.secured = secured; diff --git a/test/jdk/sun/net/www/protocol/http/NULLTargetInfoTest.java b/test/jdk/sun/net/www/protocol/http/NULLTargetInfoTest.java index d8e88554f86..391ca50ff27 100644 --- a/test/jdk/sun/net/www/protocol/http/NULLTargetInfoTest.java +++ b/test/jdk/sun/net/www/protocol/http/NULLTargetInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8151788 + * @bug 8151788 8286526 * @summary NullPointerException from ntlm.Client.type3 * @modules java.base/com.sun.security.ntlm * @run main NULLTargetInfoTest @@ -42,7 +42,7 @@ public static void main(String[] args) throws Exception { "4E 54 4C 4D 53 53 50 00 02 00 00 00 00 00 00 00" + "00 00 00 00 05 82 89 00 0B 87 81 B6 2D 6E 8B C1" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); - byte[] nonce = new byte[10]; + byte[] nonce = new byte[8]; c.type3(type2, nonce); }