--- a/.hgtags-top-repo Thu Feb 25 09:41:40 2016 -0800
+++ b/.hgtags-top-repo Wed Jul 05 21:23:38 2017 +0200
@@ -349,3 +349,4 @@
9a38f8b4ba220708db198d08d82fd2144a64777d jdk-9+104
be58b02c11f90b88c67e4d0e2cb5e4cf2d9b3c57 jdk-9+105
54575d8783b3a39a2d710c28cda675d44261f9d9 jdk-9+106
+4d65eba233a8730f913734a6804910b842d2cb54 jdk-9+107
--- a/common/autoconf/flags.m4 Thu Feb 25 09:41:40 2016 -0800
+++ b/common/autoconf/flags.m4 Wed Jul 05 21:23:38 2017 +0200
@@ -123,12 +123,16 @@
[
# COMPILER_TARGET_BITS_FLAG : option for selecting 32- or 64-bit output
# COMPILER_COMMAND_FILE_FLAG : option for passing a command file to the compiler
+ # COMPILER_BINDCMD_FILE_FLAG : option for specifying a file which saves the binder
+ # commands produced by the link step (currently AIX only)
if test "x$TOOLCHAIN_TYPE" = xxlc; then
COMPILER_TARGET_BITS_FLAG="-q"
COMPILER_COMMAND_FILE_FLAG="-f"
+ COMPILER_BINDCMD_FILE_FLAG="-bloadmap:"
else
COMPILER_TARGET_BITS_FLAG="-m"
COMPILER_COMMAND_FILE_FLAG="@"
+ COMPILER_BINDCMD_FILE_FLAG=""
# The solstudio linker does not support @-files.
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
@@ -152,6 +156,7 @@
fi
AC_SUBST(COMPILER_TARGET_BITS_FLAG)
AC_SUBST(COMPILER_COMMAND_FILE_FLAG)
+ AC_SUBST(COMPILER_BINDCMD_FILE_FLAG)
# FIXME: figure out if we should select AR flags depending on OS or toolchain.
if test "x$OPENJDK_TARGET_OS" = xmacosx; then
@@ -294,10 +299,23 @@
SET_SHARED_LIBRARY_NAME='-h [$]1'
SET_SHARED_LIBRARY_MAPFILE='-M[$]1'
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
- PICFLAG="-qpic=large"
+ # '-qpic' defaults to 'qpic=small'. This means that the compiler generates only
+ # one instruction for accessing the TOC. If the TOC grows larger than 64K, the linker
+ # will have to patch this single instruction with a call to some out-of-order code which
+ # does the load from the TOC. This is of course slow. But in that case we also would have
+ # to use '-bbigtoc' for linking anyway so we could also change the PICFLAG to 'qpic=large'.
+ # With 'qpic=large' the compiler will by default generate a two-instruction sequence which
+ # can be patched directly by the linker and does not require a jump to out-of-order code.
+ # Another alternative instead of using 'qpic=large -bbigtoc' may be to use '-qminimaltoc'
+ # instead. This creates a distinct TOC for every compilation unit (and thus requires two
+ # loads for accessing a global variable). But there are rumors that this may be seen as a
+ # 'performance feature' because of improved code locality of the symbols used in a
+ # compilation unit.
+ PICFLAG="-qpic"
+ JVM_CFLAGS="$JVM_CFLAGS $PICFLAG"
C_FLAG_REORDER=''
CXX_FLAG_REORDER=''
- SHARED_LIBRARY_FLAGS="-qmkshrobj"
+ SHARED_LIBRARY_FLAGS="-qmkshrobj -bM:SRE -bnoentry"
SET_EXECUTABLE_ORIGIN=""
SET_SHARED_LIBRARY_ORIGIN=''
SET_SHARED_LIBRARY_NAME=''
@@ -835,7 +853,7 @@
LDFLAGS_CXX_SOLSTUDIO="-norunpath"
LDFLAGS_CXX_JDK="$LDFLAGS_CXX_JDK $LDFLAGS_CXX_SOLSTUDIO -xnolib"
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
- LDFLAGS_XLC="-brtl -bnolibpath -bexpall -bernotok"
+ LDFLAGS_XLC="-b64 -brtl -bnolibpath -bexpall -bernotok"
LDFLAGS_JDK="${LDFLAGS_JDK} $LDFLAGS_XLC"
fi
@@ -891,6 +909,7 @@
AC_SUBST(JDKLIB_LIBS)
AC_SUBST(JDKEXE_LIBS)
AC_SUBST(LDFLAGS_CXX_JDK)
+ AC_SUBST(LDFLAGS_HASH_STYLE)
LDFLAGS_TESTLIB="$LDFLAGS_JDKLIB"
LDFLAGS_TESTEXE="$LDFLAGS_JDKEXE"
--- a/common/autoconf/generated-configure.sh Thu Feb 25 09:41:40 2016 -0800
+++ b/common/autoconf/generated-configure.sh Wed Jul 05 21:23:38 2017 +0200
@@ -701,6 +701,7 @@
ZERO_ARCHFLAG
LDFLAGS_TESTEXE
LDFLAGS_TESTLIB
+LDFLAGS_HASH_STYLE
LDFLAGS_CXX_JDK
JDKEXE_LIBS
JDKLIB_LIBS
@@ -743,6 +744,7 @@
CC_OUT_OPTION
STRIPFLAGS
ARFLAGS
+COMPILER_BINDCMD_FILE_FLAG
COMPILER_COMMAND_FILE_FLAG
COMPILER_TARGET_BITS_FLAG
JT_HOME
@@ -4230,7 +4232,7 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -4860,7 +4862,7 @@
#CUSTOM_AUTOCONF_INCLUDE
# Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1455271513
+DATE_WHEN_GENERATED=1456136781
###############################################################################
#
@@ -45391,12 +45393,16 @@
# COMPILER_TARGET_BITS_FLAG : option for selecting 32- or 64-bit output
# COMPILER_COMMAND_FILE_FLAG : option for passing a command file to the compiler
+ # COMPILER_BINDCMD_FILE_FLAG : option for specifying a file which saves the binder
+ # commands produced by the link step (currently AIX only)
if test "x$TOOLCHAIN_TYPE" = xxlc; then
COMPILER_TARGET_BITS_FLAG="-q"
COMPILER_COMMAND_FILE_FLAG="-f"
+ COMPILER_BINDCMD_FILE_FLAG="-bloadmap:"
else
COMPILER_TARGET_BITS_FLAG="-m"
COMPILER_COMMAND_FILE_FLAG="@"
+ COMPILER_BINDCMD_FILE_FLAG=""
# The solstudio linker does not support @-files.
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
@@ -45424,6 +45430,7 @@
+
# FIXME: figure out if we should select AR flags depending on OS or toolchain.
if test "x$OPENJDK_TARGET_OS" = xmacosx; then
ARFLAGS="-r"
@@ -46198,10 +46205,23 @@
SET_SHARED_LIBRARY_NAME='-h $1'
SET_SHARED_LIBRARY_MAPFILE='-M$1'
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
- PICFLAG="-qpic=large"
+ # '-qpic' defaults to 'qpic=small'. This means that the compiler generates only
+ # one instruction for accessing the TOC. If the TOC grows larger than 64K, the linker
+ # will have to patch this single instruction with a call to some out-of-order code which
+ # does the load from the TOC. This is of course slow. But in that case we also would have
+ # to use '-bbigtoc' for linking anyway so we could also change the PICFLAG to 'qpic=large'.
+ # With 'qpic=large' the compiler will by default generate a two-instruction sequence which
+ # can be patched directly by the linker and does not require a jump to out-of-order code.
+ # Another alternative instead of using 'qpic=large -bbigtoc' may be to use '-qminimaltoc'
+ # instead. This creates a distinct TOC for every compilation unit (and thus requires two
+ # loads for accessing a global variable). But there are rumors that this may be seen as a
+ # 'performance feature' because of improved code locality of the symbols used in a
+ # compilation unit.
+ PICFLAG="-qpic"
+ JVM_CFLAGS="$JVM_CFLAGS $PICFLAG"
C_FLAG_REORDER=''
CXX_FLAG_REORDER=''
- SHARED_LIBRARY_FLAGS="-qmkshrobj"
+ SHARED_LIBRARY_FLAGS="-qmkshrobj -bM:SRE -bnoentry"
SET_EXECUTABLE_ORIGIN=""
SET_SHARED_LIBRARY_ORIGIN=''
SET_SHARED_LIBRARY_NAME=''
@@ -46824,7 +46844,7 @@
LDFLAGS_CXX_SOLSTUDIO="-norunpath"
LDFLAGS_CXX_JDK="$LDFLAGS_CXX_JDK $LDFLAGS_CXX_SOLSTUDIO -xnolib"
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
- LDFLAGS_XLC="-brtl -bnolibpath -bexpall -bernotok"
+ LDFLAGS_XLC="-b64 -brtl -bnolibpath -bexpall -bernotok"
LDFLAGS_JDK="${LDFLAGS_JDK} $LDFLAGS_XLC"
fi
@@ -46881,6 +46901,7 @@
+
LDFLAGS_TESTLIB="$LDFLAGS_JDKLIB"
LDFLAGS_TESTEXE="$LDFLAGS_JDKEXE"
@@ -58630,7 +58651,8 @@
# Setup libm (the maths library)
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5
+ if test "x$OPENJDK_TARGET_OS" != "xwindows"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5
$as_echo_n "checking for cos in -lm... " >&6; }
if ${ac_cv_lib_m_cos+:} false; then :
$as_echo_n "(cached) " >&6
@@ -58675,12 +58697,15 @@
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: Maths library was not found" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Maths library was not found" >&5
$as_echo "$as_me: Maths library was not found" >&6;}
fi
- LIBM=-lm
+ LIBM="-lm"
+ else
+ LIBM=""
+ fi
# Setup libdl (for dynamic library loading)
--- a/common/autoconf/libraries.m4 Thu Feb 25 09:41:40 2016 -0800
+++ b/common/autoconf/libraries.m4 Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -160,10 +160,14 @@
AC_DEFUN_ONCE([LIB_SETUP_MISC_LIBS],
[
# Setup libm (the maths library)
- AC_CHECK_LIB(m, cos, [], [
- AC_MSG_NOTICE([Maths library was not found])
- ])
- LIBM=-lm
+ if test "x$OPENJDK_TARGET_OS" != "xwindows"; then
+ AC_CHECK_LIB(m, cos, [], [
+ AC_MSG_NOTICE([Maths library was not found])
+ ])
+ LIBM="-lm"
+ else
+ LIBM=""
+ fi
AC_SUBST(LIBM)
# Setup libdl (for dynamic library loading)
--- a/common/autoconf/spec.gmk.in Thu Feb 25 09:41:40 2016 -0800
+++ b/common/autoconf/spec.gmk.in Wed Jul 05 21:23:38 2017 +0200
@@ -314,6 +314,10 @@
# Option used to pass a command file to the compiler
COMPILER_COMMAND_FILE_FLAG:=@COMPILER_COMMAND_FILE_FLAG@
+# Option for specifying a file which saves the binder commands
+# produced by the link step (for debugging, currently AIX only)
+COMPILER_BINDCMD_FILE_FLAG:=@COMPILER_BINDCMD_FILE_FLAG@
+
CC_OUT_OPTION:=@CC_OUT_OPTION@
EXE_OUT_OPTION:=@EXE_OUT_OPTION@
LD_OUT_OPTION:=@LD_OUT_OPTION@
@@ -351,6 +355,8 @@
CFLAGS_JDKEXE:=@CFLAGS_JDKEXE@
CXXFLAGS_JDKEXE:=@CXXFLAGS_JDKEXE@
+LDFLAGS_HASH_STYLE := @LDFLAGS_HASH_STYLE@
+
CXX:=@FIXPATH@ @CCACHE@ @ICECC@ @CXX@
CPP:=@FIXPATH@ @CPP@
--- a/hotspot/.hgignore Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/.hgignore Wed Jul 05 21:23:38 2017 +0200
@@ -10,7 +10,6 @@
.igv.log
^.hgtip
.DS_Store
-\.class$
^\.mx.jvmci/env
^\.mx.jvmci/.*\.pyc
^\.mx.jvmci/eclipse-launches/.*
--- a/hotspot/.hgtags Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/.hgtags Wed Jul 05 21:23:38 2017 +0200
@@ -509,3 +509,4 @@
534c50395957c6025fb6627e93b35756f8d48a08 jdk-9+104
266fa9bb5297bf02cb2a7b038b10a109817d2b48 jdk-9+105
7232de4c17c37f60aecec4f3191090bd3d41d334 jdk-9+106
+c5146d4da417f76edfc43097d2e2ced042a65b4e jdk-9+107
--- a/hotspot/make/bsd/makefiles/arm.make Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/make/bsd/makefiles/arm.make Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2016, 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
@@ -24,8 +24,4 @@
Obj_Files += bsd_arm.o
-ifneq ($(EXT_LIBS_PATH),)
- LIBS += $(EXT_LIBS_PATH)/sflt_glibc.a
-endif
-
CFLAGS += -DVM_LITTLE_ENDIAN
--- a/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -54,6 +54,7 @@
define_pd_global(intx, InteriorEntryAlignment, 16);
define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K));
define_pd_global(intx, LoopUnrollLimit, 60);
+define_pd_global(intx, LoopPercentProfileLimit, 10);
// InitialCodeCacheSize derived from specjbb2000 run.
define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
define_pd_global(intx, CodeCacheExpansionSize, 64*K);
--- a/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/aarch64/vm/globals_aarch64.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -32,7 +32,6 @@
// Sets the default values for platform dependent flags used by the runtime system.
// (see globals.hpp)
-define_pd_global(bool, ConvertSleepToYield, true);
define_pd_global(bool, ShareVtableStubs, true);
define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this
--- a/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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,39 +30,151 @@
#include "vmreg_aarch64.inline.hpp"
jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS) {
- Unimplemented();
- return 0;
+ if (inst->is_call() || inst->is_jump() || inst->is_blr()) {
+ return pc_offset + NativeCall::instruction_size;
+ } else if (inst->is_general_jump()) {
+ return pc_offset + NativeGeneralJump::instruction_size;
+ } else {
+ JVMCI_ERROR_0("unsupported type of instruction for call site");
+ }
}
void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS) {
- Unimplemented();
+ address pc = _instructions->start() + pc_offset;
+ Handle obj = HotSpotObjectConstantImpl::object(constant);
+ jobject value = JNIHandles::make_local(obj());
+ if (HotSpotObjectConstantImpl::compressed(constant)) {
+ int oop_index = _oop_recorder->find_index(value);
+ RelocationHolder rspec = oop_Relocation::spec(oop_index);
+ _instructions->relocate(pc, rspec, 1);
+ Unimplemented();
+ } else {
+ NativeMovConstReg* move = nativeMovConstReg_at(pc);
+ move->set_data((intptr_t) value);
+ int oop_index = _oop_recorder->find_index(value);
+ RelocationHolder rspec = oop_Relocation::spec(oop_index);
+ _instructions->relocate(pc, rspec);
+ }
}
void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS) {
- Unimplemented();
+ address pc = _instructions->start() + pc_offset;
+ if (HotSpotMetaspaceConstantImpl::compressed(constant)) {
+ narrowKlass narrowOop = record_narrow_metadata_reference(constant, CHECK);
+ TRACE_jvmci_3("relocating (narrow metaspace constant) at " PTR_FORMAT "/0x%x", p2i(pc), narrowOop);
+ Unimplemented();
+ } else {
+ NativeMovConstReg* move = nativeMovConstReg_at(pc);
+ Metadata* reference = record_metadata_reference(constant, CHECK);
+ move->set_data((intptr_t) reference);
+ TRACE_jvmci_3("relocating (metaspace constant) at " PTR_FORMAT "/" PTR_FORMAT, p2i(pc), p2i(reference));
+ }
}
-void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset) {
- Unimplemented();
+void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset, TRAPS) {
+ address pc = _instructions->start() + pc_offset;
+ NativeInstruction* inst = nativeInstruction_at(pc);
+ if (inst->is_adr_aligned()) {
+ address dest = _constants->start() + data_offset;
+ _instructions->relocate(pc, section_word_Relocation::spec((address) dest, CodeBuffer::SECT_CONSTS));
+ TRACE_jvmci_3("relocating at " PTR_FORMAT " (+%d) with destination at %d", p2i(pc), pc_offset, data_offset);
+ } else {
+ JVMCI_ERROR("unknown load or move instruction at " PTR_FORMAT, p2i(pc));
+ }
}
void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS) {
- Unimplemented();
+ address pc = (address) inst;
+ if (inst->is_call()) {
+ NativeCall* call = nativeCall_at(pc);
+ call->set_destination((address) foreign_call_destination);
+ _instructions->relocate(call->instruction_address(), runtime_call_Relocation::spec());
+ } else if (inst->is_jump()) {
+ NativeJump* jump = nativeJump_at(pc);
+ jump->set_jump_destination((address) foreign_call_destination);
+ _instructions->relocate(jump->instruction_address(), runtime_call_Relocation::spec());
+ } else if (inst->is_general_jump()) {
+ NativeGeneralJump* jump = nativeGeneralJump_at(pc);
+ jump->set_jump_destination((address) foreign_call_destination);
+ _instructions->relocate(jump->instruction_address(), runtime_call_Relocation::spec());
+ } else {
+ JVMCI_ERROR("unknown call or jump instruction at " PTR_FORMAT, p2i(pc));
+ }
+ TRACE_jvmci_3("relocating (foreign call) at " PTR_FORMAT, p2i(inst));
}
void CodeInstaller::pd_relocate_JavaMethod(Handle hotspot_method, jint pc_offset, TRAPS) {
- Unimplemented();
+#ifdef ASSERT
+ Method* method = NULL;
+ // we need to check, this might also be an unresolved method
+ if (hotspot_method->is_a(HotSpotResolvedJavaMethodImpl::klass())) {
+ method = getMethodFromHotSpotMethod(hotspot_method());
+ }
+#endif
+ switch (_next_call_type) {
+ case INLINE_INVOKE:
+ break;
+ case INVOKEVIRTUAL:
+ case INVOKEINTERFACE: {
+ assert(method == NULL || !method->is_static(), "cannot call static method with invokeinterface");
+ NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
+ call->set_destination(SharedRuntime::get_resolve_virtual_call_stub());
+ _instructions->relocate(call->instruction_address(), virtual_call_Relocation::spec(_invoke_mark_pc));
+ break;
+ }
+ case INVOKESTATIC: {
+ assert(method == NULL || method->is_static(), "cannot call non-static method with invokestatic");
+ NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
+ call->set_destination(SharedRuntime::get_resolve_static_call_stub());
+ _instructions->relocate(call->instruction_address(), relocInfo::static_call_type);
+ break;
+ }
+ case INVOKESPECIAL: {
+ assert(method == NULL || !method->is_static(), "cannot call static method with invokespecial");
+ NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
+ call->set_destination(SharedRuntime::get_resolve_opt_virtual_call_stub());
+ _instructions->relocate(call->instruction_address(), relocInfo::opt_virtual_call_type);
+ break;
+ }
+ default:
+ JVMCI_ERROR("invalid _next_call_type value");
+ break;
+ }
}
void CodeInstaller::pd_relocate_poll(address pc, jint mark, TRAPS) {
- Unimplemented();
+ switch (mark) {
+ case POLL_NEAR:
+ JVMCI_ERROR("unimplemented");
+ break;
+ case POLL_FAR:
+ _instructions->relocate(pc, relocInfo::poll_type);
+ break;
+ case POLL_RETURN_NEAR:
+ JVMCI_ERROR("unimplemented");
+ break;
+ case POLL_RETURN_FAR:
+ _instructions->relocate(pc, relocInfo::poll_return_type);
+ break;
+ default:
+ JVMCI_ERROR("invalid mark value");
+ break;
+ }
}
// convert JVMCI register indices (as used in oop maps) to HotSpot registers
VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg, TRAPS) {
- return NULL;
+ if (jvmci_reg < RegisterImpl::number_of_registers) {
+ return as_Register(jvmci_reg)->as_VMReg();
+ } else {
+ jint floatRegisterNumber = jvmci_reg - RegisterImpl::number_of_registers;
+ if (floatRegisterNumber < FloatRegisterImpl::number_of_registers) {
+ return as_FloatRegister(floatRegisterNumber)->as_VMReg();
+ }
+ JVMCI_ERROR_NULL("invalid register number: %d", jvmci_reg);
+ }
}
bool CodeInstaller::is_general_purpose_reg(VMReg hotspotRegister) {
- return false;
+ return !hotspotRegister->is_FloatRegister();
}
--- a/hotspot/src/cpu/aarch64/vm/nativeInst_aarch64.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/aarch64/vm/nativeInst_aarch64.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -136,7 +136,7 @@
MacroAssembler::pd_patch_instruction(instruction_address(), (address)x);
ICache::invalidate_range(instruction_address(), instruction_size);
}
-};
+}
void NativeMovConstReg::print() {
tty->print_cr(PTR_FORMAT ": mov reg, " INTPTR_FORMAT,
@@ -208,6 +208,32 @@
//-------------------------------------------------------------------
+address NativeGeneralJump::jump_destination() const {
+ NativeMovConstReg* move = nativeMovConstReg_at(instruction_address());
+ address dest = (address) move->data();
+
+ // We use jump to self as the unresolved address which the inline
+ // cache code (and relocs) know about
+
+ // return -1 if jump to self
+ dest = (dest == (address) this) ? (address) -1 : dest;
+ return dest;
+}
+
+void NativeGeneralJump::set_jump_destination(address dest) {
+ NativeMovConstReg* move = nativeMovConstReg_at(instruction_address());
+
+ // We use jump to self as the unresolved address which the inline
+ // cache code (and relocs) know about
+ if (dest == (address) -1) {
+ dest = instruction_address();
+ }
+
+ move->set_data((uintptr_t) dest);
+};
+
+//-------------------------------------------------------------------
+
bool NativeInstruction::is_safepoint_poll() {
// a safepoint_poll is implemented in two steps as either
//
@@ -249,6 +275,22 @@
Instruction_aarch64::extract(insn, 4, 0) == 0b11111);
}
+bool NativeInstruction::is_general_jump() {
+ if (is_movz()) {
+ NativeInstruction* inst1 = nativeInstruction_at(addr_at(instruction_size * 1));
+ if (inst1->is_movk()) {
+ NativeInstruction* inst2 = nativeInstruction_at(addr_at(instruction_size * 2));
+ if (inst2->is_movk()) {
+ NativeInstruction* inst3 = nativeInstruction_at(addr_at(instruction_size * 3));
+ if (inst3->is_blr()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
bool NativeInstruction::is_movz() {
return Instruction_aarch64::extract(int_at(0), 30, 23) == 0b10100101;
}
--- a/hotspot/src/cpu/aarch64/vm/nativeInst_aarch64.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/aarch64/vm/nativeInst_aarch64.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -54,11 +54,22 @@
friend class Relocation;
friend bool is_NativeCallTrampolineStub_at(address);
public:
- enum { instruction_size = 4 };
+ enum {
+ instruction_size = 4
+ };
+
+ juint encoding() const {
+ return uint_at(0);
+ }
+
+ bool is_blr() const { return (encoding() & 0xfffffc1f) == 0xd63f0000; }
+ bool is_adr_aligned() const { return (encoding() & 0xff000000) == 0x10000000; } // adr Xn, <label>, where label is aligned to 4 bytes (address of instruction).
+
inline bool is_nop();
inline bool is_illegal();
inline bool is_return();
bool is_jump();
+ bool is_general_jump();
inline bool is_jump_or_nop();
inline bool is_cond_jump();
bool is_safepoint_poll();
@@ -341,11 +352,15 @@
// An interface for accessing/manipulating native leal instruction of form:
// leal reg, [reg + offset]
-class NativeLoadAddress: public NativeMovRegMem {
- static const bool has_rex = true;
- static const int rex_size = 1;
+class NativeLoadAddress: public NativeInstruction {
+ enum AArch64_specific_constants {
+ instruction_size = 4,
+ instruction_offset = 0,
+ data_offset = 0,
+ next_instruction_offset = 4
+ };
+
public:
-
void verify();
void print ();
@@ -398,6 +413,10 @@
data_offset = 0,
next_instruction_offset = 4 * 4
};
+
+ address jump_destination() const;
+ void set_jump_destination(address dest);
+
static void insert_unconditional(address code_pos, address entry);
static void replace_mt_safe(address instr_addr, address code_buffer);
static void verify();
--- a/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -54,6 +54,7 @@
define_pd_global(bool, UseTLAB, true);
define_pd_global(bool, ResizeTLAB, true);
define_pd_global(intx, LoopUnrollLimit, 60);
+define_pd_global(intx, LoopPercentProfileLimit, 10);
// Peephole and CISC spilling both break the graph, and so make the
// scheduler sick.
--- a/hotspot/src/cpu/ppc/vm/globals_ppc.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/ppc/vm/globals_ppc.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -32,7 +32,6 @@
// Sets the default values for platform dependent flags used by the runtime system.
// (see globals.hpp)
-define_pd_global(bool, ConvertSleepToYield, true);
define_pd_global(bool, ShareVtableStubs, false); // Improves performance markedly for mtrt and compress.
define_pd_global(bool, NeedsDeoptSuspend, false); // Only register window machines need this.
--- a/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -42,7 +42,7 @@
Unimplemented();
}
-void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset) {
+void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset, TRAPS) {
Unimplemented();
}
--- a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -52,6 +52,7 @@
define_pd_global(bool, UseTLAB, true);
define_pd_global(bool, ResizeTLAB, true);
define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1
+define_pd_global(intx, LoopPercentProfileLimit, 10);
define_pd_global(intx, MinJumpTableSize, 5);
// Peephole and CISC spilling both break the graph, and so makes the
--- a/hotspot/src/cpu/sparc/vm/globals_sparc.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -37,7 +37,6 @@
// the load of the dispatch address and hence the jmp would still go to the location
// according to the prior table. So, we let the thread continue and let it block by itself.
define_pd_global(bool, DontYieldALot, true); // yield no more than 100 times per second
-define_pd_global(bool, ConvertSleepToYield, false); // do not convert sleep(0) to yield. Helps GUI
define_pd_global(bool, ShareVtableStubs, false); // improves performance markedly for mtrt and compress
define_pd_global(bool, NeedsDeoptSuspend, true); // register window machines need this
--- a/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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,7 +85,7 @@
}
}
-void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset) {
+void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset, TRAPS) {
address pc = _instructions->start() + pc_offset;
NativeInstruction* inst = nativeInstruction_at(pc);
NativeInstruction* inst1 = nativeInstruction_at(pc + 4);
--- a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2015,23 +2015,33 @@
int vep_offset = ((intptr_t)__ pc()) - start;
#ifdef COMPILER1
- if (InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) {
- // Object.hashCode can pull the hashCode from the header word
- // instead of doing a full VM transition once it's been computed.
- // Since hashCode is usually polymorphic at call sites we can't do
- // this optimization at the call site without a lot of work.
+ if ((InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) || (method->intrinsic_id() == vmIntrinsics::_identityHashCode)) {
+ // Object.hashCode, System.identityHashCode can pull the hashCode from the
+ // header word instead of doing a full VM transition once it's been computed.
+ // Since hashCode is usually polymorphic at call sites we can't do this
+ // optimization at the call site without a lot of work.
Label slowCase;
- Register receiver = O0;
+ Label done;
+ Register obj_reg = O0;
Register result = O0;
Register header = G3_scratch;
Register hash = G3_scratch; // overwrite header value with hash value
Register mask = G1; // to get hash field from header
+ // Unlike for Object.hashCode, System.identityHashCode is static method and
+ // gets object as argument instead of the receiver.
+ if (method->intrinsic_id() == vmIntrinsics::_identityHashCode) {
+ assert(method->is_static(), "method should be static");
+ // return 0 for null reference input
+ __ br_null(obj_reg, false, Assembler::pn, done);
+ __ delayed()->mov(obj_reg, hash);
+ }
+
// Read the header and build a mask to get its hash field. Give up if the object is not unlocked.
// We depend on hash_mask being at most 32 bits and avoid the use of
// hash_mask_in_place because it could be larger than 32 bits in a 64-bit
// vm: see markOop.hpp.
- __ ld_ptr(receiver, oopDesc::mark_offset_in_bytes(), header);
+ __ ld_ptr(obj_reg, oopDesc::mark_offset_in_bytes(), header);
__ sethi(markOopDesc::hash_mask, mask);
__ btst(markOopDesc::unlocked_value, header);
__ br(Assembler::zero, false, Assembler::pn, slowCase);
@@ -2054,6 +2064,7 @@
__ delayed()->nop();
// leaf return.
+ __ bind(done);
__ retl();
__ delayed()->mov(hash, result);
__ bind(slowCase);
--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2361,7 +2361,7 @@
void Assembler::movdqu(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit);
simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x6F);
@@ -2398,7 +2398,7 @@
void Assembler::vmovdqu(XMMRegister dst, Address src) {
assert(UseAVX > 0, "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit);
vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x6F);
@@ -2486,7 +2486,7 @@
void Assembler::evmovdqul(XMMRegister dst, Address src, int vector_len) {
assert(VM_Version::supports_evex(), "");
InstructionMark im(this);
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false , /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit);
vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x6F);
@@ -2515,7 +2515,7 @@
void Assembler::evmovdquq(XMMRegister dst, Address src, int vector_len) {
assert(VM_Version::supports_evex(), "");
InstructionMark im(this);
- InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit);
vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x6F);
@@ -2640,7 +2640,7 @@
void Assembler::movsd(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x10);
emit_int8((unsigned char)(0xC0 | encode));
@@ -2649,7 +2649,7 @@
void Assembler::movsd(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x10);
@@ -2668,7 +2668,7 @@
void Assembler::movss(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x10);
emit_int8((unsigned char)(0xC0 | encode));
@@ -2677,7 +2677,7 @@
void Assembler::movss(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x10);
@@ -2782,7 +2782,7 @@
void Assembler::mulsd(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
@@ -2791,7 +2791,7 @@
void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
emit_int8((unsigned char)(0xC0 | encode));
@@ -2800,7 +2800,7 @@
void Assembler::mulss(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
@@ -2809,7 +2809,7 @@
void Assembler::mulss(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
emit_int8((unsigned char)(0xC0 | encode));
@@ -3993,7 +3993,7 @@
void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x51);
emit_int8((unsigned char)(0xC0 | encode));
@@ -4002,7 +4002,7 @@
void Assembler::sqrtsd(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x51);
@@ -4011,7 +4011,7 @@
void Assembler::sqrtss(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x51);
emit_int8((unsigned char)(0xC0 | encode));
@@ -4024,7 +4024,7 @@
void Assembler::sqrtss(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x51);
@@ -4078,7 +4078,7 @@
void Assembler::subsd(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
emit_int8((unsigned char)(0xC0 | encode));
@@ -4087,7 +4087,7 @@
void Assembler::subsd(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
@@ -4096,7 +4096,7 @@
void Assembler::subss(XMMRegister dst, XMMRegister src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false , /* uses_vl */ false);
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
emit_int8((unsigned char)(0xC0 | encode));
@@ -4105,7 +4105,7 @@
void Assembler::subss(XMMRegister dst, Address src) {
NOT_LP64(assert(VM_Version::supports_sse(), ""));
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
@@ -4293,7 +4293,7 @@
void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4303,7 +4303,7 @@
void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x58);
@@ -4313,7 +4313,7 @@
void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
@@ -4323,7 +4323,7 @@
void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x58);
@@ -4333,7 +4333,7 @@
void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4343,7 +4343,7 @@
void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x5E);
@@ -4353,7 +4353,7 @@
void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
@@ -4363,7 +4363,7 @@
void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x5E);
@@ -4373,7 +4373,7 @@
void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4383,7 +4383,7 @@
void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
@@ -4393,7 +4393,7 @@
void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
@@ -4403,7 +4403,7 @@
void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x59);
@@ -4413,7 +4413,7 @@
void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4423,7 +4423,7 @@
void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
@@ -4433,7 +4433,7 @@
void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) {
assert(VM_Version::supports_avx(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
@@ -4443,7 +4443,7 @@
void Assembler::vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
int nds_enc = nds->is_valid() ? nds->encoding() : 0;
int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
emit_int8(0x5C);
@@ -5901,7 +5901,7 @@
// duplicate 1-byte integer data from src into 16||32|64 locations in dest : requires AVX512BW and AVX512VL
void Assembler::evpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len) {
assert(VM_Version::supports_evex(), "");
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8(0x78);
emit_int8((unsigned char)(0xC0 | encode));
@@ -5911,7 +5911,7 @@
assert(VM_Version::supports_evex(), "");
assert(dst != xnoreg, "sanity");
InstructionMark im(this);
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_8bit);
// swap src<->dst for encoding
vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
@@ -5922,7 +5922,7 @@
// duplicate 2-byte integer data from src into 8|16||32 locations in dest : requires AVX512BW and AVX512VL
void Assembler::evpbroadcastw(XMMRegister dst, XMMRegister src, int vector_len) {
assert(VM_Version::supports_evex(), "");
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8(0x79);
emit_int8((unsigned char)(0xC0 | encode));
@@ -5932,7 +5932,7 @@
assert(VM_Version::supports_evex(), "");
assert(dst != xnoreg, "sanity");
InstructionMark im(this);
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit);
// swap src<->dst for encoding
vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
@@ -6027,7 +6027,7 @@
// duplicate 1-byte integer data from src into 16||32|64 locations in dest : requires AVX512BW and AVX512VL
void Assembler::evpbroadcastb(XMMRegister dst, Register src, int vector_len) {
assert(VM_Version::supports_evex(), "");
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8(0x7A);
emit_int8((unsigned char)(0xC0 | encode));
@@ -6036,7 +6036,7 @@
// duplicate 2-byte integer data from src into 8|16||32 locations in dest : requires AVX512BW and AVX512VL
void Assembler::evpbroadcastw(XMMRegister dst, Register src, int vector_len) {
assert(VM_Version::supports_evex(), "");
- InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8(0x7B);
emit_int8((unsigned char)(0xC0 | encode));
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -2044,11 +2044,11 @@
class InstructionAttr {
public:
InstructionAttr(
- int vector_len,
- bool rex_vex_w,
- bool legacy_mode,
- bool no_reg_mask,
- bool uses_vl)
+ int vector_len, // The length of vector to be applied in encoding - for both AVX and EVEX
+ bool rex_vex_w, // Width of data: if 32-bits or less, false, else if 64-bit or specially defined, true
+ bool legacy_mode, // Details if either this instruction is conditionally encoded to AVX or earlier if true else possibly EVEX
+ bool no_reg_mask, // when true, k0 is used when EVEX encoding is chosen, else k1 is used under the same condition
+ bool uses_vl) // This instruction may have legacy constraints based on vector length for EVEX
:
_avx_vector_len(vector_len),
_rex_vex_w(rex_vex_w),
--- a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -46,6 +46,7 @@
define_pd_global(intx, ConditionalMoveLimit, 3);
define_pd_global(intx, FreqInlineSize, 325);
define_pd_global(intx, MinJumpTableSize, 10);
+define_pd_global(intx, LoopPercentProfileLimit, 30);
#ifdef AMD64
define_pd_global(intx, INTPRESSURE, 13);
define_pd_global(intx, FLOATPRESSURE, 14);
--- a/hotspot/src/cpu/x86/vm/globals_x86.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -31,7 +31,6 @@
// Sets the default values for platform dependent flags used by the runtime system.
// (see globals.hpp)
-define_pd_global(bool, ConvertSleepToYield, true);
define_pd_global(bool, ShareVtableStubs, true);
define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this
--- a/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -101,7 +101,7 @@
}
}
-void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset) {
+void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset, TRAPS) {
address pc = _instructions->start() + pc_offset;
address operand = Assembler::locate_operand(pc, Assembler::disp32_operand);
--- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -7840,7 +7840,9 @@
Label COMPARE_WIDE_VECTORS_LOOP_FAILED; // used only _LP64 && AVX3
int stride, stride2, adr_stride, adr_stride1, adr_stride2;
int stride2x2 = 0x40;
- Address::ScaleFactor scale, scale1, scale2;
+ Address::ScaleFactor scale = Address::no_scale;
+ Address::ScaleFactor scale1 = Address::no_scale;
+ Address::ScaleFactor scale2 = Address::no_scale;
if (ae != StrIntrinsicNode::LL) {
stride2x2 = 0x20;
@@ -7894,9 +7896,9 @@
stride = 8;
}
} else {
- scale = Address::no_scale; // not used
scale1 = Address::times_1;
scale2 = Address::times_2;
+ // scale not used
stride = 8;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "asm/macroAssembler.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "vmreg_x86.inline.hpp"
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif //COMPILER1
+
+#define __ masm->
+
+#ifdef COMPILER1
+// ---------------------------------------------------------------------------
+// Object.hashCode, System.identityHashCode can pull the hashCode from the
+// header word instead of doing a full VM transition once it's been computed.
+// Since hashCode is usually polymorphic at call sites we can't do this
+// optimization at the call site without a lot of work.
+void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* masm,
+ methodHandle method,
+ Register obj_reg,
+ Register result) {
+ Label slowCase;
+
+ // Unlike for Object.hashCode, System.identityHashCode is static method and
+ // gets object as argument instead of the receiver.
+ if (method->intrinsic_id() == vmIntrinsics::_identityHashCode) {
+ Label Continue;
+ // return 0 for null reference input
+ __ cmpptr(obj_reg, (int32_t)NULL_WORD);
+ __ jcc(Assembler::notEqual, Continue);
+ __ xorptr(result, result);
+ __ ret(0);
+ __ bind(Continue);
+ }
+
+ __ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
+
+ // check if locked
+ __ testptr(result, markOopDesc::unlocked_value);
+ __ jcc(Assembler::zero, slowCase);
+
+ if (UseBiasedLocking) {
+ // Check if biased and fall through to runtime if so
+ __ testptr(result, markOopDesc::biased_lock_bit_in_place);
+ __ jcc(Assembler::notZero, slowCase);
+ }
+
+ // get hash
+#ifdef _LP64
+ // Read the header and build a mask to get its hash field.
+ // Depend on hash_mask being at most 32 bits and avoid the use of hash_mask_in_place
+ // because it could be larger than 32 bits in a 64-bit vm. See markOop.hpp.
+ __ shrptr(result, markOopDesc::hash_shift);
+ __ andptr(result, markOopDesc::hash_mask);
+#else
+ __ andptr(result, markOopDesc::hash_mask_in_place);
+#endif //_LP64
+
+ // test if hashCode exists
+ __ jcc(Assembler::zero, slowCase);
+#ifndef _LP64
+ __ shrptr(result, markOopDesc::hash_shift);
+#endif
+ __ ret(0);
+ __ bind(slowCase);
+}
+#endif //COMPILER1
+
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1754,34 +1754,10 @@
int vep_offset = ((intptr_t)__ pc()) - start;
#ifdef COMPILER1
- if (InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) {
- // Object.hashCode can pull the hashCode from the header word
- // instead of doing a full VM transition once it's been computed.
- // Since hashCode is usually polymorphic at call sites we can't do
- // this optimization at the call site without a lot of work.
- Label slowCase;
- Register receiver = rcx;
- Register result = rax;
- __ movptr(result, Address(receiver, oopDesc::mark_offset_in_bytes()));
-
- // check if locked
- __ testptr(result, markOopDesc::unlocked_value);
- __ jcc (Assembler::zero, slowCase);
-
- if (UseBiasedLocking) {
- // Check if biased and fall through to runtime if so
- __ testptr(result, markOopDesc::biased_lock_bit_in_place);
- __ jcc (Assembler::notZero, slowCase);
- }
-
- // get hash
- __ andptr(result, markOopDesc::hash_mask_in_place);
- // test if hashCode exists
- __ jcc (Assembler::zero, slowCase);
- __ shrptr(result, markOopDesc::hash_shift);
- __ ret(0);
- __ bind (slowCase);
- }
+ // For Object.hashCode, System.identityHashCode try to pull hashCode from object header if available.
+ if ((InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) || (method->intrinsic_id() == vmIntrinsics::_identityHashCode)) {
+ inline_check_hashcode_from_object_header(masm, method, rcx /*obj_reg*/, rax /*result*/);
+ }
#endif // COMPILER1
// The instruction at the verified entry point must be 5 bytes or longer
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2058,6 +2058,13 @@
int vep_offset = ((intptr_t)__ pc()) - start;
+#ifdef COMPILER1
+ // For Object.hashCode, System.identityHashCode try to pull hashCode from object header if available.
+ if ((InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) || (method->intrinsic_id() == vmIntrinsics::_identityHashCode)) {
+ inline_check_hashcode_from_object_header(masm, method, j_rarg0 /*obj_reg*/, rax /*result*/);
+ }
+#endif // COMPILER1
+
// The instruction at the verified entry point must be 5 bytes or longer
// because it can be patched on the fly by make_non_entrant. The stack bang
// instruction fits that requirement.
--- a/hotspot/src/cpu/zero/vm/globals_zero.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/cpu/zero/vm/globals_zero.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -32,7 +32,6 @@
// Set the default values for platform dependent flags used by the
// runtime system. See globals.hpp for details of what they do.
-define_pd_global(bool, ConvertSleepToYield, true);
define_pd_global(bool, ShareVtableStubs, true);
define_pd_global(bool, NeedsDeoptSuspend, false);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/G1HeapRegionType.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+package sun.jvm.hotspot.gc.shared;
+
+//These definitions should be kept in sync with the definitions in the HotSpot code.
+
+public enum G1HeapRegionType {
+ Free ("Free"),
+ Eden ("Eden"),
+ Survivor ("Survivor"),
+ StartsHumongous ("Starts Humongous"),
+ ContinuesHumongous ("Continues Humongous"),
+ Old ("Old"),
+ Archive ("Archive"),
+ G1HeapRegionTypeEndSentinel ("G1HeapRegionTypeEndSentinel");
+
+ private final String value;
+
+ G1HeapRegionType(String val) {
+ this.value = val;
+ }
+ public String value() {
+ return value;
+ }
+}
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.aarch64/src/jdk/vm/ci/aarch64/AArch64.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.aarch64/src/jdk/vm/ci/aarch64/AArch64.java Wed Jul 05 21:23:38 2017 +0200
@@ -70,56 +70,64 @@
public static final Register r28 = new Register(28, 28, "r28", CPU);
public static final Register r29 = new Register(29, 29, "r29", CPU);
public static final Register r30 = new Register(30, 30, "r30", CPU);
+
+ /*
+ * r31 is not a general purpose register, but represents either the stackpointer or the
+ * zero/discard register depending on the instruction. So we represent those two uses as two
+ * different registers. The register numbers are kept in sync with register_aarch64.hpp and have
+ * to be sequential, hence we also need a general r31 register here, which is never used.
+ */
public static final Register r31 = new Register(31, 31, "r31", CPU);
+ public static final Register zr = new Register(32, 31, "zr", CPU);
+ public static final Register sp = new Register(33, 31, "sp", CPU);
public static final Register lr = r30;
- public static final Register zr = r31;
- public static final Register sp = r31;
// @formatter:off
public static final Register[] cpuRegisters = {
r0, r1, r2, r3, r4, r5, r6, r7,
r8, r9, r10, r11, r12, r13, r14, r15,
r16, r17, r18, r19, r20, r21, r22, r23,
- r24, r25, r26, r27, r28, r29, r30, r31
+ r24, r25, r26, r27, r28, r29, r30, r31,
+ zr, sp
};
// @formatter:on
public static final RegisterCategory SIMD = new RegisterCategory("SIMD");
// Simd registers
- public static final Register v0 = new Register(32, 0, "v0", SIMD);
- public static final Register v1 = new Register(33, 1, "v1", SIMD);
- public static final Register v2 = new Register(34, 2, "v2", SIMD);
- public static final Register v3 = new Register(35, 3, "v3", SIMD);
- public static final Register v4 = new Register(36, 4, "v4", SIMD);
- public static final Register v5 = new Register(37, 5, "v5", SIMD);
- public static final Register v6 = new Register(38, 6, "v6", SIMD);
- public static final Register v7 = new Register(39, 7, "v7", SIMD);
- public static final Register v8 = new Register(40, 8, "v8", SIMD);
- public static final Register v9 = new Register(41, 9, "v9", SIMD);
- public static final Register v10 = new Register(42, 10, "v10", SIMD);
- public static final Register v11 = new Register(43, 11, "v11", SIMD);
- public static final Register v12 = new Register(44, 12, "v12", SIMD);
- public static final Register v13 = new Register(45, 13, "v13", SIMD);
- public static final Register v14 = new Register(46, 14, "v14", SIMD);
- public static final Register v15 = new Register(47, 15, "v15", SIMD);
- public static final Register v16 = new Register(48, 16, "v16", SIMD);
- public static final Register v17 = new Register(49, 17, "v17", SIMD);
- public static final Register v18 = new Register(50, 18, "v18", SIMD);
- public static final Register v19 = new Register(51, 19, "v19", SIMD);
- public static final Register v20 = new Register(52, 20, "v20", SIMD);
- public static final Register v21 = new Register(53, 21, "v21", SIMD);
- public static final Register v22 = new Register(54, 22, "v22", SIMD);
- public static final Register v23 = new Register(55, 23, "v23", SIMD);
- public static final Register v24 = new Register(56, 24, "v24", SIMD);
- public static final Register v25 = new Register(57, 25, "v25", SIMD);
- public static final Register v26 = new Register(58, 26, "v26", SIMD);
- public static final Register v27 = new Register(59, 27, "v27", SIMD);
- public static final Register v28 = new Register(60, 28, "v28", SIMD);
- public static final Register v29 = new Register(61, 29, "v29", SIMD);
- public static final Register v30 = new Register(62, 30, "v30", SIMD);
- public static final Register v31 = new Register(63, 31, "v31", SIMD);
+ public static final Register v0 = new Register(34, 0, "v0", SIMD);
+ public static final Register v1 = new Register(35, 1, "v1", SIMD);
+ public static final Register v2 = new Register(36, 2, "v2", SIMD);
+ public static final Register v3 = new Register(37, 3, "v3", SIMD);
+ public static final Register v4 = new Register(38, 4, "v4", SIMD);
+ public static final Register v5 = new Register(39, 5, "v5", SIMD);
+ public static final Register v6 = new Register(40, 6, "v6", SIMD);
+ public static final Register v7 = new Register(41, 7, "v7", SIMD);
+ public static final Register v8 = new Register(42, 8, "v8", SIMD);
+ public static final Register v9 = new Register(43, 9, "v9", SIMD);
+ public static final Register v10 = new Register(44, 10, "v10", SIMD);
+ public static final Register v11 = new Register(45, 11, "v11", SIMD);
+ public static final Register v12 = new Register(46, 12, "v12", SIMD);
+ public static final Register v13 = new Register(47, 13, "v13", SIMD);
+ public static final Register v14 = new Register(48, 14, "v14", SIMD);
+ public static final Register v15 = new Register(49, 15, "v15", SIMD);
+ public static final Register v16 = new Register(50, 16, "v16", SIMD);
+ public static final Register v17 = new Register(51, 17, "v17", SIMD);
+ public static final Register v18 = new Register(52, 18, "v18", SIMD);
+ public static final Register v19 = new Register(53, 19, "v19", SIMD);
+ public static final Register v20 = new Register(54, 20, "v20", SIMD);
+ public static final Register v21 = new Register(55, 21, "v21", SIMD);
+ public static final Register v22 = new Register(56, 22, "v22", SIMD);
+ public static final Register v23 = new Register(57, 23, "v23", SIMD);
+ public static final Register v24 = new Register(58, 24, "v24", SIMD);
+ public static final Register v25 = new Register(59, 25, "v25", SIMD);
+ public static final Register v26 = new Register(60, 26, "v26", SIMD);
+ public static final Register v27 = new Register(61, 27, "v27", SIMD);
+ public static final Register v28 = new Register(62, 28, "v28", SIMD);
+ public static final Register v29 = new Register(63, 29, "v29", SIMD);
+ public static final Register v30 = new Register(64, 30, "v30", SIMD);
+ public static final Register v31 = new Register(65, 31, "v31", SIMD);
// @formatter:off
public static final Register[] simdRegisters = {
@@ -136,6 +144,7 @@
r8, r9, r10, r11, r12, r13, r14, r15,
r16, r17, r18, r19, r20, r21, r22, r23,
r24, r25, r26, r27, r28, r29, r30, r31,
+ zr, sp,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14, v15,
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java Wed Jul 05 21:23:38 2017 +0200
@@ -31,6 +31,7 @@
import static jdk.vm.ci.aarch64.AArch64.r28;
import static jdk.vm.ci.aarch64.AArch64.r29;
import static jdk.vm.ci.aarch64.AArch64.r3;
+import static jdk.vm.ci.aarch64.AArch64.r31;
import static jdk.vm.ci.aarch64.AArch64.r4;
import static jdk.vm.ci.aarch64.AArch64.r5;
import static jdk.vm.ci.aarch64.AArch64.r6;
@@ -45,11 +46,13 @@
import static jdk.vm.ci.aarch64.AArch64.v5;
import static jdk.vm.ci.aarch64.AArch64.v6;
import static jdk.vm.ci.aarch64.AArch64.v7;
+import static jdk.vm.ci.aarch64.AArch64.zr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import jdk.vm.ci.aarch64.AArch64;
@@ -130,16 +133,20 @@
public static final Register threadRegister = r28;
public static final Register fp = r29;
+ private static final Register[] reservedRegisters = {threadRegister, fp, lr, r31, zr, sp};
+
private static Register[] initAllocatable(Architecture arch, boolean reserveForHeapBase) {
Register[] allRegisters = arch.getAvailableValueRegisters();
- Register[] registers = new Register[allRegisters.length - (reserveForHeapBase ? 5 : 4)];
+ Register[] registers = new Register[allRegisters.length - reservedRegisters.length - (reserveForHeapBase ? 1 : 0)];
+ List<Register> reservedRegistersList = Arrays.asList(reservedRegisters);
int idx = 0;
for (Register reg : allRegisters) {
- if (reg.equals(threadRegister) || reg.equals(fp) || reg.equals(lr) || reg.equals(sp)) {
- // skip thread register, frame pointer, link register and stack pointer
+ if (reservedRegistersList.contains(reg)) {
+ // skip reserved registers
continue;
}
+ assert !(reg.equals(threadRegister) || reg.equals(fp) || reg.equals(lr) || reg.equals(r31) || reg.equals(zr) || reg.equals(sp));
if (reserveForHeapBase && reg.equals(heapBaseRegister)) {
// skip heap base register
continue;
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java Wed Jul 05 21:23:38 2017 +0200
@@ -45,6 +45,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import jdk.vm.ci.code.Architecture;
@@ -119,14 +120,17 @@
*/
private final boolean needsNativeStackHomeSpace;
+ private static final Register[] reservedRegisters = {rsp, r15};
+
private static Register[] initAllocatable(Architecture arch, boolean reserveForHeapBase) {
Register[] allRegisters = arch.getAvailableValueRegisters();
- Register[] registers = new Register[allRegisters.length - (reserveForHeapBase ? 3 : 2)];
+ Register[] registers = new Register[allRegisters.length - reservedRegisters.length - (reserveForHeapBase ? 1 : 0)];
+ List<Register> reservedRegistersList = Arrays.asList(reservedRegisters);
int idx = 0;
for (Register reg : allRegisters) {
- if (reg.equals(rsp) || reg.equals(r15)) {
- // skip stack pointer and thread register
+ if (reservedRegistersList.contains(reg)) {
+ // skip reserved registers
continue;
}
if (reserveForHeapBase && reg.equals(r12)) {
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java Wed Jul 05 21:23:38 2017 +0200
@@ -68,6 +68,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.CallingConvention;
@@ -140,14 +141,17 @@
i0, i1, i2, i3, i4, i5, i6, i7};
// @formatter:on
+ private static final Register[] reservedRegisters = {sp, g0, g2};
+
private static Register[] initAllocatable(Architecture arch, boolean reserveForHeapBase) {
Register[] allRegisters = arch.getAvailableValueRegisters();
- Register[] registers = new Register[allRegisters.length - (reserveForHeapBase ? 4 : 3)];
+ Register[] registers = new Register[allRegisters.length - reservedRegisters.length - (reserveForHeapBase ? 1 : 0)];
+ List<Register> reservedRegistersList = Arrays.asList(reservedRegisters);
int idx = 0;
for (Register reg : allRegisters) {
- if (reg.equals(sp) || reg.equals(g2) || reg.equals(g0)) {
- // skip g0, stack pointer and thread register
+ if (reservedRegistersList.contains(reg)) {
+ // skip reserved registers
continue;
}
if (reserveForHeapBase && reg.equals(g6)) {
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, 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
@@ -26,6 +26,7 @@
import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
import java.lang.reflect.Array;
+import java.util.Objects;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
@@ -70,13 +71,13 @@
} else if (x instanceof HotSpotObjectConstantImpl) {
return y instanceof HotSpotObjectConstantImpl && ((HotSpotObjectConstantImpl) x).object() == ((HotSpotObjectConstantImpl) y).object();
} else {
- return x.equals(y);
+ return Objects.equals(x, y);
}
}
@Override
public Integer readArrayLength(JavaConstant array) {
- if (array.getJavaKind() != JavaKind.Object || array.isNull()) {
+ if (array == null || array.getJavaKind() != JavaKind.Object || array.isNull()) {
return null;
}
@@ -133,12 +134,12 @@
@Override
public JavaConstant readArrayElement(JavaConstant array, int index) {
- if (array.getJavaKind() != JavaKind.Object || array.isNull()) {
+ if (array == null || array.getJavaKind() != JavaKind.Object || array.isNull()) {
return null;
}
Object a = ((HotSpotObjectConstantImpl) array).object();
- if (index < 0 || index >= Array.getLength(a)) {
+ if (!a.getClass().isArray() || index < 0 || index >= Array.getLength(a)) {
return null;
}
@@ -184,7 +185,7 @@
@Override
public JavaConstant boxPrimitive(JavaConstant source) {
- if (!source.getJavaKind().isPrimitive() || !isBoxCached(source)) {
+ if (source == null || !source.getJavaKind().isPrimitive() || !isBoxCached(source)) {
return null;
}
return HotSpotObjectConstantImpl.forObject(source.asBoxedPrimitive());
@@ -192,7 +193,7 @@
@Override
public JavaConstant unboxPrimitive(JavaConstant source) {
- if (!source.getJavaKind().isObject()) {
+ if (source == null || !source.getJavaKind().isObject()) {
return null;
}
if (source.isNull()) {
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethod.java Wed Jul 05 21:23:38 2017 +0200
@@ -57,6 +57,13 @@
boolean isDontInline();
/**
+ * Returns true if this method has a {@code ReservedStackAccess} annotation.
+ *
+ * @return true if ReservedStackAccess annotation present, false otherwise
+ */
+ boolean hasReservedStackAccess();
+
+ /**
* Manually adds a DontInline annotation to this method.
*/
void setNotInlineable();
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -309,6 +309,15 @@
}
/**
+ * Returns true if this method has a {@code ReservedStackAccess} annotation.
+ *
+ * @return true if ReservedStackAccess annotation present, false otherwise
+ */
+ public boolean hasReservedStackAccess() {
+ return (getFlags() & config().methodFlagsReservedStackAccess) != 0;
+ }
+
+ /**
* Manually adds a DontInline annotation to this method.
*/
public void setNotInlineable() {
--- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java Wed Jul 05 21:23:38 2017 +0200
@@ -850,6 +850,7 @@
@HotSpotVMFlag(name = "DontCompileHugeMethods") @Stable public boolean dontCompileHugeMethods;
@HotSpotVMFlag(name = "HugeMethodLimit") @Stable public int hugeMethodLimit;
@HotSpotVMFlag(name = "PrintInlining") @Stable public boolean printInlining;
+ @HotSpotVMFlag(name = "Inline") @Stable public boolean inline;
@HotSpotVMFlag(name = "JVMCIUseFastLocking") @Stable public boolean useFastLocking;
@HotSpotVMFlag(name = "ForceUnreachable") @Stable public boolean forceUnreachable;
@HotSpotVMFlag(name = "CodeCacheSegmentSize") @Stable public int codeSegmentSize;
@@ -974,6 +975,7 @@
@HotSpotVMFlag(name = "BlockZeroingLowLimit", archs = {"sparc"}) @Stable public int blockZeroingLowLimit;
@HotSpotVMFlag(name = "StackShadowPages") @Stable public int stackShadowPages;
+ @HotSpotVMFlag(name = "StackReservedPages") @Stable public int stackReservedPages;
@HotSpotVMFlag(name = "UseStackBanging") @Stable public boolean useStackBanging;
@HotSpotVMConstant(name = "STACK_BIAS") @Stable public int stackBias;
@HotSpotVMField(name = "CompilerToVM::Data::vm_page_size", type = "int", get = HotSpotVMField.Type.VALUE) @Stable public int vmPageSize;
@@ -1092,6 +1094,7 @@
@HotSpotVMField(name = "JavaThread::_satb_mark_queue", type = "SATBMarkQueue", get = HotSpotVMField.Type.OFFSET) @Stable public int javaThreadSatbMarkQueueOffset;
@HotSpotVMField(name = "JavaThread::_vm_result", type = "oop", get = HotSpotVMField.Type.OFFSET) @Stable public int threadObjectResultOffset;
@HotSpotVMField(name = "JavaThread::_jvmci_counters", type = "jlong*", get = HotSpotVMField.Type.OFFSET) @Stable public int jvmciCountersThreadOffset;
+ @HotSpotVMField(name = "JavaThread::_reserved_stack_activation", type = "address", get = HotSpotVMField.Type.OFFSET) @Stable public int javaThreadReservedStackActivationOffset;
/**
* An invalid value for {@link #rtldDefault}.
@@ -1235,6 +1238,7 @@
@HotSpotVMConstant(name = "Method::_force_inline") @Stable public int methodFlagsForceInline;
@HotSpotVMConstant(name = "Method::_dont_inline") @Stable public int methodFlagsDontInline;
@HotSpotVMConstant(name = "Method::_hidden") @Stable public int methodFlagsHidden;
+ @HotSpotVMConstant(name = "Method::_reserved_stack_access") @Stable public int methodFlagsReservedStackAccess;
@HotSpotVMConstant(name = "Method::nonvirtual_vtable_index") @Stable public int nonvirtualVtableIndex;
@HotSpotVMConstant(name = "Method::invalid_vtable_index") @Stable public int invalidVtableIndex;
@@ -1491,6 +1495,8 @@
@HotSpotVMField(name = "StubRoutines::_updateBytesCRC32", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long updateBytesCRC32Stub;
@HotSpotVMField(name = "StubRoutines::_crc_table_adr", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long crcTableAddress;
+ @HotSpotVMField(name = "StubRoutines::_throw_delayed_StackOverflowError_entry", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long throwDelayedStackOverflowErrorEntry;
+
@HotSpotVMField(name = "StubRoutines::_jbyte_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long jbyteArraycopy;
@HotSpotVMField(name = "StubRoutines::_jshort_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long jshortArraycopy;
@HotSpotVMField(name = "StubRoutines::_jint_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long jintArraycopy;
@@ -1548,6 +1554,7 @@
@HotSpotVMAddress(name = "SharedRuntime::register_finalizer") @Stable public long registerFinalizerAddress;
@HotSpotVMAddress(name = "SharedRuntime::exception_handler_for_return_address") @Stable public long exceptionHandlerForReturnAddressAddress;
@HotSpotVMAddress(name = "SharedRuntime::OSR_migration_end") @Stable public long osrMigrationEndAddress;
+ @HotSpotVMAddress(name = "SharedRuntime::enable_stack_reserved_zone") @Stable public long enableStackReservedZoneAddress;
@HotSpotVMAddress(name = "os::javaTimeMillis") @Stable public long javaTimeMillisAddress;
@HotSpotVMAddress(name = "os::javaTimeNanos") @Stable public long javaTimeNanosAddress;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.services/.checkstyle_checks.xml Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+ Checkstyle-Configuration: Checks
+ Description: none
+-->
+<module name="Checker">
+ <property name="severity" value="error"/>
+ <module name="TreeWalker">
+ <property name="tabWidth" value="4"/>
+ <module name="FileContentsHolder"/>
+ <module name="JavadocStyle">
+ <property name="checkHtml" value="false"/>
+ </module>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName">
+ <property name="format" value="^(([a-z][a-zA-Z0-9]*$)|(_[A-Z][a-zA-Z0-9]*_[a-z][a-zA-Z0-9]*$))"/>
+ </module>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="TypeName">
+ <property name="format" value="^[A-Z][_a-zA-Z0-9]*$"/>
+ </module>
+ <module name="RedundantImport"/>
+ <module name="LineLength">
+ <property name="max" value="250"/>
+ </module>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter">
+ <property name="tokens" value="ARRAY_INIT,BNOT,DEC,DOT,INC,LNOT,UNARY_MINUS,UNARY_PLUS"/>
+ </module>
+ <module name="AvoidStarImport">
+ <property name="allowClassImports" value="false"/>
+ <property name="allowStaticMemberImports" value="false"/>
+ </module>
+ <module name="NoWhitespaceBefore">
+ <property name="tokens" value="SEMI,DOT,POST_DEC,POST_INC"/>
+ </module>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad">
+ <property name="tokens" value="RPAREN,TYPECAST"/>
+ </module>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround">
+ <property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,EQUAL,GE,GT,LAND,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND"/>
+ </module>
+ <module name="RedundantModifier"/>
+ <module name="AvoidNestedBlocks">
+ <property name="allowInSwitchCase" value="true"/>
+ </module>
+ <module name="EmptyBlock">
+ <property name="option" value="text"/>
+ <property name="tokens" value="LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_IF,LITERAL_TRY,LITERAL_WHILE,STATIC_INIT"/>
+ </module>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+ <module name="EmptyStatement"/>
+ <module name="HiddenField">
+ <property name="severity" value="ignore"/>
+ <property name="ignoreConstructorParameter" value="true"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="ArrayTypeStyle"/>
+ <module name="UpperEll"/>
+ <module name="FallThrough"/>
+ <module name="FinalLocalVariable">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="StringLiteralEquality">
+ <property name="severity" value="error"/>
+ </module>
+ <module name="SuperFinalize"/>
+ <module name="UnnecessaryParentheses">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="Indentation">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="StaticVariableName">
+ <property name="format" value="^[A-Za-z][a-zA-Z0-9]*$"/>
+ </module>
+ <module name="EmptyForInitializerPad"/>
+ <module name="EmptyForIteratorPad"/>
+ <module name="ModifierOrder"/>
+ <module name="DefaultComesLast"/>
+ <module name="InnerAssignment">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="ModifiedControlVariable"/>
+ <module name="MutableException">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="ParameterAssignment">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="RegexpSinglelineJava">
+ <metadata name="net.sf.eclipsecs.core.comment" value="Illegal trailing whitespace(s) at the end of the line."/>
+ <property name="format" value="\s$"/>
+ <property name="message" value="Illegal trailing whitespace(s) at the end of the line."/>
+ <property name="ignoreComments" value="true"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Checks for trailing spaces at the end of a line"/>
+ </module>
+ <module name="RegexpSinglelineJava">
+ <metadata name="net.sf.eclipsecs.core.comment" value="illegal space before a comma"/>
+ <property name="format" value=" ,"/>
+ <property name="message" value="illegal space before a comma"/>
+ <property name="ignoreComments" value="true"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Checks for whitespace before a comma."/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.customMessage" value="Illegal whitespace before a comma."/>
+ </module>
+ <module name="RegexpSinglelineJava">
+ <property name="format" value="[^\x00-\x7F]"/>
+ <property name="message" value="Only use ASCII characters."/>
+ </module>
+ <module name="RegexpSinglelineJava">
+ <property name="format" value="new (Hashtable|Vector|Stack|StringBuffer)[^\w]"/>
+ <property name="message" value="Don't use old synchronized collection classes"/>
+ </module>
+ </module>
+ <module name="RegexpHeader">
+ <property name="header" value="/\*\n \* Copyright \(c\) (20[0-9][0-9], )?20[0-9][0-9], Oracle and/or its affiliates. All rights reserved.\n \* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n \*\n \* This code is free software; you can redistribute it and/or modify it\n \* under the terms of the GNU General Public License version 2 only, as\n \* published by the Free Software Foundation.\n \*\n \* This code is distributed in the hope that it will be useful, but WITHOUT\n \* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n \* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n \* version 2 for more details \(a copy is included in the LICENSE file that\n \* accompanied this code\).\n \*\n \* You should have received a copy of the GNU General Public License version\n \* 2 along with this work; if not, write to the Free Software Foundation,\n \* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n \*\n \* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n \* or visit www.oracle.com if you need additional information or have any\n \* questions.\n \*/\n"/>
+ <property name="fileExtensions" value="java"/>
+ </module>
+ <module name="FileTabCharacter">
+ <property name="severity" value="error"/>
+ <property name="fileExtensions" value="java"/>
+ </module>
+ <module name="NewlineAtEndOfFile">
+ <property name="lineSeparator" value="lf"/>
+ </module>
+ <module name="Translation"/>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="Checkstyle: stop constant name check"/>
+ <property name="onCommentFormat" value="Checkstyle: resume constant name check"/>
+ <property name="checkFormat" value="ConstantNameCheck"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Allow non-conforming constant names"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="Checkstyle: stop method name check"/>
+ <property name="onCommentFormat" value="Checkstyle: resume method name check"/>
+ <property name="checkFormat" value="MethodName"/>
+ <property name="checkC" value="false"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable method name checks"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle: stop parameter assignment check"/>
+ <property name="onCommentFormat" value="CheckStyle: resume parameter assignment check"/>
+ <property name="checkFormat" value="ParameterAssignment"/>
+ <property name="checkC" value="false"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable Parameter Assignment"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="Checkstyle: stop final variable check"/>
+ <property name="onCommentFormat" value="Checkstyle: resume final variable check"/>
+ <property name="checkFormat" value="FinalLocalVariable"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable final variable checks"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="Checkstyle: stop"/>
+ <property name="onCommentFormat" value="Checkstyle: resume"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable all checks"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle: stop inner assignment check"/>
+ <property name="onCommentFormat" value="CheckStyle: resume inner assignment check"/>
+ <property name="checkFormat" value="InnerAssignment"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable inner assignment checks"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="Checkstyle: stop field name check"/>
+ <property name="onCommentFormat" value="Checkstyle: resume field name check"/>
+ <property name="checkFormat" value="MemberName"/>
+ <property name="checkC" value="false"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable field name checks"/>
+ </module>
+ <module name="RegexpMultiline">
+ <metadata name="net.sf.eclipsecs.core.comment" value="illegal Windows line ending"/>
+ <property name="format" value="\r\n"/>
+ <property name="message" value="illegal Windows line ending"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle: stop header check"/>
+ <property name="onCommentFormat" value="CheckStyle: resume header check"/>
+ <property name="checkFormat" value=".*Header"/>
+ <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Disable header checks"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle: stop line length check"/>
+ <property name="onCommentFormat" value="CheckStyle: resume line length check"/>
+ <property name="checkFormat" value="LineLength"/>
+ </module>
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle: start generated"/>
+ <property name="onCommentFormat" value="CheckStyle: stop generated"/>
+ <property name="checkFormat" value=".*Name|.*LineLength|.*Header"/>
+ </module>
+</module>
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, 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
@@ -4042,61 +4042,6 @@
// could have been signaled after a wait started
// 1 : signaled - thread is running or ready
//
-// Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can
-// hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable.
-// For specifics regarding the bug see GLIBC BUGID 261237 :
-// http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html.
-// Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future
-// will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar
-// is used. (The simple C test-case provided in the GLIBC bug report manifests the
-// hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos()
-// and monitorenter when we're using 1-0 locking. All those operations may result in
-// calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version
-// of libpthread avoids the problem, but isn't practical.
-//
-// Possible remedies:
-//
-// 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work.
-// This is palliative and probabilistic, however. If the thread is preempted
-// between the call to compute_abstime() and pthread_cond_timedwait(), more
-// than the minimum period may have passed, and the abstime may be stale (in the
-// past) resultin in a hang. Using this technique reduces the odds of a hang
-// but the JVM is still vulnerable, particularly on heavily loaded systems.
-//
-// 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead
-// of the usual flag-condvar-mutex idiom. The write side of the pipe is set
-// NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo)
-// reduces to poll()+read(). This works well, but consumes 2 FDs per extant
-// thread.
-//
-// 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread
-// that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing
-// a timeout request to the chron thread and then blocking via pthread_cond_wait().
-// This also works well. In fact it avoids kernel-level scalability impediments
-// on certain platforms that don't handle lots of active pthread_cond_timedwait()
-// timers in a graceful fashion.
-//
-// 4. When the abstime value is in the past it appears that control returns
-// correctly from pthread_cond_timedwait(), but the condvar is left corrupt.
-// Subsequent timedwait/wait calls may hang indefinitely. Given that, we
-// can avoid the problem by reinitializing the condvar -- by cond_destroy()
-// followed by cond_init() -- after all calls to pthread_cond_timedwait().
-// It may be possible to avoid reinitialization by checking the return
-// value from pthread_cond_timedwait(). In addition to reinitializing the
-// condvar we must establish the invariant that cond_signal() is only called
-// within critical sections protected by the adjunct mutex. This prevents
-// cond_signal() from "seeing" a condvar that's in the midst of being
-// reinitialized or that is corrupt. Sadly, this invariant obviates the
-// desirable signal-after-unlock optimization that avoids futile context switching.
-//
-// I'm also concerned that some versions of NTPL might allocate an auxilliary
-// structure when a condvar is used or initialized. cond_destroy() would
-// release the helper structure. Our reinitialize-after-timedwait fix
-// put excessive stress on malloc/free and locks protecting the c-heap.
-//
-// We currently use (4). See the WorkAroundNTPLTimedWaitHang flag.
-// It may be possible to refine (4) by checking the kernel and NTPL verisons
-// and only enabling the work-around for vulnerable environments.
// utility to compute the abstime argument to timedwait:
// millis is the relative timeout time
@@ -4208,10 +4153,6 @@
while (_Event < 0) {
status = pthread_cond_timedwait(_cond, _mutex, &abst);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(_cond);
- pthread_cond_init(_cond, NULL);
- }
assert_status(status == 0 || status == EINTR ||
status == ETIMEDOUT,
status, "cond_timedwait");
@@ -4255,10 +4196,6 @@
assert_status(status == 0, status, "mutex_lock");
int AnyWaiters = _nParked;
assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
- if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
- AnyWaiters = 0;
- pthread_cond_signal(_cond);
- }
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
if (AnyWaiters != 0) {
@@ -4391,7 +4328,7 @@
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
+ assert_status(status == 0, status, "invariant");
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
@@ -4414,10 +4351,6 @@
status = pthread_cond_wait(_cond, _mutex);
} else {
status = pthread_cond_timedwait(_cond, _mutex, &absTime);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(_cond);
- pthread_cond_init(_cond, NULL);
- }
}
assert_status(status == 0 || status == EINTR ||
status == ETIMEDOUT,
@@ -4442,24 +4375,14 @@
void Parker::unpark() {
int status = pthread_mutex_lock(_mutex);
- assert(status == 0, "invariant");
+ assert_status(status == 0, status, "invariant");
const int s = _counter;
_counter = 1;
+ status = pthread_mutex_unlock(_mutex);
+ assert_status(status == 0, status, "invariant");
if (s < 1) {
- if (WorkAroundNPTLTimedWaitHang) {
- status = pthread_cond_signal(_cond);
- assert(status == 0, "invariant");
- status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- } else {
- status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- status = pthread_cond_signal(_cond);
- assert(status == 0, "invariant");
- }
- } else {
- pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
+ status = pthread_cond_signal(_cond);
+ assert_status(status == 0, status, "invariant");
}
}
--- a/hotspot/src/os/linux/vm/os_linux.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -4771,6 +4771,25 @@
}
}
+// older glibc versions don't have this macro (which expands to
+// an optimized bit-counting function) so we have to roll our own
+#ifndef CPU_COUNT
+
+static int _cpu_count(const cpu_set_t* cpus) {
+ int count = 0;
+ // only look up to the number of configured processors
+ for (int i = 0; i < os::processor_count(); i++) {
+ if (CPU_ISSET(i, cpus)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+#define CPU_COUNT(cpus) _cpu_count(cpus)
+
+#endif // CPU_COUNT
+
// Get the current number of available processors for this process.
// This value can change at any time during a process's lifetime.
// sched_getaffinity gives an accurate answer as it accounts for cpusets.
@@ -4786,6 +4805,9 @@
int configured_cpus = processor_count(); // upper bound on available cpus
int cpu_count = 0;
+// old build platforms may not support dynamic cpu sets
+#ifdef CPU_ALLOC
+
// To enable easy testing of the dynamic path on different platforms we
// introduce a diagnostic flag: UseCpuAllocPath
if (configured_cpus >= CPU_SETSIZE || UseCpuAllocPath) {
@@ -4814,10 +4836,18 @@
log_trace(os)("active_processor_count: using static path - configured processors: %d",
configured_cpus);
}
+#else // CPU_ALLOC
+// these stubs won't be executed
+#define CPU_COUNT_S(size, cpus) -1
+#define CPU_FREE(cpus)
+
+ log_trace(os)("active_processor_count: only static path available - configured processors: %d",
+ configured_cpus);
+#endif // CPU_ALLOC
// pid 0 means the current thread - which we have to assume represents the process
if (sched_getaffinity(0, cpus_size, cpus_p) == 0) {
- if (cpus_p != &cpus) {
+ if (cpus_p != &cpus) { // can only be true when CPU_ALLOC used
cpu_count = CPU_COUNT_S(cpus_size, cpus_p);
}
else {
@@ -4831,7 +4861,7 @@
"which may exceed available processors", strerror(errno), cpu_count);
}
- if (cpus_p != &cpus) {
+ if (cpus_p != &cpus) { // can only be true when CPU_ALLOC used
CPU_FREE(cpus_p);
}
@@ -5349,61 +5379,6 @@
// could have been signaled after a wait started
// 1 : signaled - thread is running or ready
//
-// Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can
-// hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable.
-// For specifics regarding the bug see GLIBC BUGID 261237 :
-// http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html.
-// Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future
-// will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar
-// is used. (The simple C test-case provided in the GLIBC bug report manifests the
-// hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos()
-// and monitorenter when we're using 1-0 locking. All those operations may result in
-// calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version
-// of libpthread avoids the problem, but isn't practical.
-//
-// Possible remedies:
-//
-// 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work.
-// This is palliative and probabilistic, however. If the thread is preempted
-// between the call to compute_abstime() and pthread_cond_timedwait(), more
-// than the minimum period may have passed, and the abstime may be stale (in the
-// past) resultin in a hang. Using this technique reduces the odds of a hang
-// but the JVM is still vulnerable, particularly on heavily loaded systems.
-//
-// 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead
-// of the usual flag-condvar-mutex idiom. The write side of the pipe is set
-// NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo)
-// reduces to poll()+read(). This works well, but consumes 2 FDs per extant
-// thread.
-//
-// 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread
-// that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing
-// a timeout request to the chron thread and then blocking via pthread_cond_wait().
-// This also works well. In fact it avoids kernel-level scalability impediments
-// on certain platforms that don't handle lots of active pthread_cond_timedwait()
-// timers in a graceful fashion.
-//
-// 4. When the abstime value is in the past it appears that control returns
-// correctly from pthread_cond_timedwait(), but the condvar is left corrupt.
-// Subsequent timedwait/wait calls may hang indefinitely. Given that, we
-// can avoid the problem by reinitializing the condvar -- by cond_destroy()
-// followed by cond_init() -- after all calls to pthread_cond_timedwait().
-// It may be possible to avoid reinitialization by checking the return
-// value from pthread_cond_timedwait(). In addition to reinitializing the
-// condvar we must establish the invariant that cond_signal() is only called
-// within critical sections protected by the adjunct mutex. This prevents
-// cond_signal() from "seeing" a condvar that's in the midst of being
-// reinitialized or that is corrupt. Sadly, this invariant obviates the
-// desirable signal-after-unlock optimization that avoids futile context switching.
-//
-// I'm also concerned that some versions of NTPL might allocate an auxilliary
-// structure when a condvar is used or initialized. cond_destroy() would
-// release the helper structure. Our reinitialize-after-timedwait fix
-// put excessive stress on malloc/free and locks protecting the c-heap.
-//
-// We currently use (4). See the WorkAroundNTPLTimedWaitHang flag.
-// It may be possible to refine (4) by checking the kernel and NTPL verisons
-// and only enabling the work-around for vulnerable environments.
// utility to compute the abstime argument to timedwait:
// millis is the relative timeout time
@@ -5529,10 +5504,6 @@
while (_Event < 0) {
status = pthread_cond_timedwait(_cond, _mutex, &abst);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(_cond);
- pthread_cond_init(_cond, os::Linux::condAttr());
- }
assert_status(status == 0 || status == EINTR ||
status == ETIME || status == ETIMEDOUT,
status, "cond_timedwait");
@@ -5576,10 +5547,6 @@
assert_status(status == 0, status, "mutex_lock");
int AnyWaiters = _nParked;
assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
- if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
- AnyWaiters = 0;
- pthread_cond_signal(_cond);
- }
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
if (AnyWaiters != 0) {
@@ -5731,7 +5698,7 @@
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
+ assert_status(status == 0, status, "invariant");
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
@@ -5757,10 +5724,6 @@
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
status = pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(&_cond[_cur_index]);
- pthread_cond_init(&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());
- }
}
_cur_index = -1;
assert_status(status == 0 || status == EINTR ||
@@ -5786,33 +5749,17 @@
void Parker::unpark() {
int status = pthread_mutex_lock(_mutex);
- assert(status == 0, "invariant");
+ assert_status(status == 0, status, "invariant");
const int s = _counter;
_counter = 1;
- if (s < 1) {
- // thread might be parked
- if (_cur_index != -1) {
- // thread is definitely parked
- if (WorkAroundNPTLTimedWaitHang) {
- status = pthread_cond_signal(&_cond[_cur_index]);
- assert(status == 0, "invariant");
- status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- } else {
- // must capture correct index before unlocking
- int index = _cur_index;
- status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- status = pthread_cond_signal(&_cond[index]);
- assert(status == 0, "invariant");
- }
- } else {
- pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- }
- } else {
- pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
+ // must capture correct index before unlocking
+ int index = _cur_index;
+ status = pthread_mutex_unlock(_mutex);
+ assert_status(status == 0, status, "invariant");
+ if (s < 1 && index != -1) {
+ // thread is definitely parked
+ status = pthread_cond_signal(&_cond[index]);
+ assert_status(status == 0, status, "invariant");
}
}
--- a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -246,7 +246,6 @@
bool PICL::open_library() {
_dl_handle = dlopen("libpicl.so.1", RTLD_LAZY);
if (_dl_handle == NULL) {
- warning("PICL (libpicl.so.1) is missing. Performance will not be optimal.");
return false;
}
if (!bind_library_functions()) {
--- a/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -730,7 +730,7 @@
}
} else if (rt == objectNull &&
(l->as_NewInstance() || l->as_NewArray() ||
- (UseNewCode && l->as_Local() && l->as_Local()->is_receiver()))) {
+ (l->as_Local() && l->as_Local()->is_receiver()))) {
if (x->cond() == Instruction::eql) {
BlockBegin* sux = x->fsux();
set_canonical(new Goto(sux, x->state_before(), is_safepoint(x, sux)));
--- a/hotspot/src/share/vm/ci/ciField.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/ci/ciField.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -211,6 +211,12 @@
// so there is no hacking of finals going on with them.
if (holder->is_anonymous())
return true;
+ // Trust final fields in all boxed classes
+ if (holder->is_box_klass())
+ return true;
+ // Trust final fields in String
+ if (holder->name() == ciSymbol::java_lang_String())
+ return true;
// Trust Atomic*FieldUpdaters: they are very important for performance, and make up one
// more reason not to use Unsafe, if their final fields are trusted. See more in JDK-8140483.
if (holder->name() == ciSymbol::java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl() ||
--- a/hotspot/src/share/vm/code/codeBlob.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/code/codeBlob.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -291,6 +291,9 @@
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
blob = new (size) MethodHandlesAdapterBlob(size);
+ if (blob == NULL) {
+ vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "CodeCache: no room for method handle adapter blob");
+ }
}
// Track memory usage statistic after releasing CodeCache_lock
MemoryService::track_code_cache_memory_usage();
--- a/hotspot/src/share/vm/code/nmethod.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/code/nmethod.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -33,6 +33,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compilerDirectives.hpp"
+#include "compiler/directivesParser.hpp"
#include "compiler/disassembler.hpp"
#include "interpreter/bytecode.hpp"
#include "oops/methodData.hpp"
@@ -965,6 +966,12 @@
}
}
+void nmethod::maybe_print_nmethod(DirectiveSet* directive) {
+ bool printnmethods = directive->PrintAssemblyOption || directive->PrintNMethodsOption;
+ if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
+ print_nmethod(printnmethods);
+ }
+}
void nmethod::print_nmethod(bool printmethod) {
ttyLocker ttyl; // keep the following output all in one block
--- a/hotspot/src/share/vm/code/nmethod.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/code/nmethod.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -29,6 +29,8 @@
#include "code/pcDesc.hpp"
#include "oops/metadata.hpp"
+class DirectiveSet;
+
// This class is used internally by nmethods, to cache
// exception/pc/handler information.
@@ -714,6 +716,8 @@
void print_nul_chk_table() PRODUCT_RETURN;
void print_recorded_oops() PRODUCT_RETURN;
void print_recorded_metadata() PRODUCT_RETURN;
+
+ void maybe_print_nmethod(DirectiveSet* directive);
void print_nmethod(bool print_code);
// need to re-define this from CodeBlob else the overload hides it
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1919,12 +1919,9 @@
collect_statistics(thread, time, task);
- bool printnmethods = directive->PrintAssemblyOption || directive->PrintNMethodsOption;
- if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
- nmethod* nm = task->code();
- if (nm != NULL) {
- nm->print_nmethod(printnmethods);
- }
+ nmethod* nm = task->code();
+ if (nm != NULL) {
+ nm->maybe_print_nmethod(directive);
}
DirectivesStack::release(directive);
--- a/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2206,13 +2206,13 @@
}
if (res == 0) {
LogHandle(gc, verify) log;
- log.info("Livelock: no rank reduction!");
- log.info(" Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n"
- " Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n",
+ log.error("Livelock: no rank reduction!");
+ log.error(" Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n"
+ " Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n",
p2i(addr), res, was_obj ?"true":"false", was_live ?"true":"false",
p2i(_last_addr), _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false");
ResourceMark rm;
- _sp->print_on(log.info_stream());
+ _sp->print_on(log.error_stream());
guarantee(false, "Verification failed.");
}
_last_addr = addr;
--- a/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2224,8 +2224,8 @@
if (!_marks->isMarked(addr)) {
LogHandle(gc, verify) log;
ResourceMark rm;
- oop(addr)->print_on(log.info_stream());
- log.info(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
+ oop(addr)->print_on(log.error_stream());
+ log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
_failed = true;
}
return true;
@@ -2350,9 +2350,9 @@
verification_mark_bm()->iterate(&vcl);
if (vcl.failed()) {
LogHandle(gc, verify) log;
- log.info("Verification failed");
+ log.error("Failed marking verification after remark");
ResourceMark rm;
- gch->print_on(log.info_stream());
+ gch->print_on(log.error_stream());
fatal("CMS: failed marking verification after remark");
}
}
@@ -2923,7 +2923,7 @@
CMSTokenSyncWithLocks ts(true, bitMapLock());
GCTraceCPUTime tcpu;
- CMSPhaseAccounting pa(this, "Concrurrent Mark");
+ CMSPhaseAccounting pa(this, "Concurrent Mark");
bool res = markFromRootsWork();
if (res) {
_collectorState = Precleaning;
@@ -5880,8 +5880,8 @@
if (!_cms_bm->isMarked(addr)) {
LogHandle(gc, verify) log;
ResourceMark rm;
- oop(addr)->print_on(log.info_stream());
- log.info(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
+ oop(addr)->print_on(log.error_stream());
+ log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
fatal("... aborting");
}
}
@@ -6661,8 +6661,8 @@
if (!_cms_bm->isMarked(addr)) {
LogHandle(gc, verify) log;
ResourceMark rm;
- oop(addr)->print_on(log.info_stream());
- log.info(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
+ oop(addr)->print_on(log.error_stream());
+ log.error(" (" INTPTR_FORMAT " should have been marked)", p2i(addr));
fatal("... aborting");
}
--- a/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/concurrentMarkThread.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "classfile/classLoaderData.hpp"
#include "gc/g1/concurrentMarkThread.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectorPolicy.hpp"
@@ -123,6 +124,7 @@
// wait until started is set.
sleepBeforeNextCycle();
if (_should_terminate) {
+ _cm->root_regions()->cancel_scan();
break;
}
@@ -132,6 +134,11 @@
HandleMark hm;
double cycle_start = os::elapsedVTime();
+ {
+ GCConcPhaseTimer(_cm, "Concurrent Clearing of Claimed Marks");
+ ClassLoaderDataGraph::clear_claimed_marks();
+ }
+
// We have to ensure that we finish scanning the root regions
// before the next GC takes place. To ensure this we have to
// make sure that we do not join the STS until the root regions
@@ -140,7 +147,7 @@
// without the root regions have been scanned which would be a
// correctness issue.
- if (!cm()->has_aborted()) {
+ {
GCConcPhaseTimer(_cm, "Concurrent Root Region Scanning");
_cm->scanRootRegions();
}
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1290,8 +1290,7 @@
ref_processor_cm()->verify_no_references_recorded();
// Abandon current iterations of concurrent marking and concurrent
- // refinement, if any are in progress. We have to do this before
- // wait_until_scan_finished() below.
+ // refinement, if any are in progress.
concurrent_mark()->abort();
// Make sure we'll choose a new allocation region afterwards.
@@ -2148,8 +2147,8 @@
virtual bool doHeapRegion(HeapRegion* hr) {
unsigned region_gc_time_stamp = hr->get_gc_time_stamp();
if (_gc_time_stamp != region_gc_time_stamp) {
- log_info(gc, verify)("Region " HR_FORMAT " has GC time stamp = %d, expected %d", HR_FORMAT_PARAMS(hr),
- region_gc_time_stamp, _gc_time_stamp);
+ log_error(gc, verify)("Region " HR_FORMAT " has GC time stamp = %d, expected %d", HR_FORMAT_PARAMS(hr),
+ region_gc_time_stamp, _gc_time_stamp);
_failures = true;
}
return false;
@@ -2848,7 +2847,7 @@
(g1_policy()->young_list_target_length() * HeapRegion::GrainBytes) - survivor_used_bytes;
VirtualSpaceSummary heap_summary = create_heap_space_summary();
- return G1HeapSummary(heap_summary, used(), eden_used_bytes, eden_capacity_bytes, survivor_used_bytes);
+ return G1HeapSummary(heap_summary, used(), eden_used_bytes, eden_capacity_bytes, survivor_used_bytes, num_regions());
}
G1EvacSummary G1CollectedHeap::create_g1_evac_summary(G1EvacStats* stats) {
@@ -5186,8 +5185,8 @@
NoYoungRegionsClosure() : _success(true) { }
bool doHeapRegion(HeapRegion* r) {
if (r->is_young()) {
- log_info(gc, verify)("Region [" PTR_FORMAT ", " PTR_FORMAT ") tagged as young",
- p2i(r->bottom()), p2i(r->end()));
+ log_error(gc, verify)("Region [" PTR_FORMAT ", " PTR_FORMAT ") tagged as young",
+ p2i(r->bottom()), p2i(r->end()));
_success = false;
}
return false;
--- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -293,29 +293,83 @@
_heap_alignment = MAX3(card_table_alignment, _space_alignment, page_size);
}
-void G1CollectorPolicy::initialize_flags() {
- if (G1HeapRegionSize != HeapRegion::GrainBytes) {
- FLAG_SET_ERGO(size_t, G1HeapRegionSize, HeapRegion::GrainBytes);
+G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); }
+
+// There are three command line options related to the young gen size:
+// NewSize, MaxNewSize and NewRatio (There is also -Xmn, but that is
+// just a short form for NewSize==MaxNewSize). G1 will use its internal
+// heuristics to calculate the actual young gen size, so these options
+// basically only limit the range within which G1 can pick a young gen
+// size. Also, these are general options taking byte sizes. G1 will
+// internally work with a number of regions instead. So, some rounding
+// will occur.
+//
+// If nothing related to the the young gen size is set on the command
+// line we should allow the young gen to be between G1NewSizePercent
+// and G1MaxNewSizePercent of the heap size. This means that every time
+// the heap size changes, the limits for the young gen size will be
+// recalculated.
+//
+// If only -XX:NewSize is set we should use the specified value as the
+// minimum size for young gen. Still using G1MaxNewSizePercent of the
+// heap as maximum.
+//
+// If only -XX:MaxNewSize is set we should use the specified value as the
+// maximum size for young gen. Still using G1NewSizePercent of the heap
+// as minimum.
+//
+// If -XX:NewSize and -XX:MaxNewSize are both specified we use these values.
+// No updates when the heap size changes. There is a special case when
+// NewSize==MaxNewSize. This is interpreted as "fixed" and will use a
+// different heuristic for calculating the collection set when we do mixed
+// collection.
+//
+// If only -XX:NewRatio is set we should use the specified ratio of the heap
+// as both min and max. This will be interpreted as "fixed" just like the
+// NewSize==MaxNewSize case above. But we will update the min and max
+// every time the heap size changes.
+//
+// NewSize and MaxNewSize override NewRatio. So, NewRatio is ignored if it is
+// combined with either NewSize or MaxNewSize. (A warning message is printed.)
+class G1YoungGenSizer : public CHeapObj<mtGC> {
+private:
+ enum SizerKind {
+ SizerDefaults,
+ SizerNewSizeOnly,
+ SizerMaxNewSizeOnly,
+ SizerMaxAndNewSize,
+ SizerNewRatio
+ };
+ SizerKind _sizer_kind;
+ uint _min_desired_young_length;
+ uint _max_desired_young_length;
+ bool _adaptive_size;
+ uint calculate_default_min_length(uint new_number_of_heap_regions);
+ uint calculate_default_max_length(uint new_number_of_heap_regions);
+
+ // Update the given values for minimum and maximum young gen length in regions
+ // given the number of heap regions depending on the kind of sizing algorithm.
+ void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length);
+
+public:
+ G1YoungGenSizer();
+ // Calculate the maximum length of the young gen given the number of regions
+ // depending on the sizing algorithm.
+ uint max_young_length(uint number_of_heap_regions);
+
+ void heap_size_changed(uint new_number_of_heap_regions);
+ uint min_desired_young_length() {
+ return _min_desired_young_length;
+ }
+ uint max_desired_young_length() {
+ return _max_desired_young_length;
}
- if (SurvivorRatio < 1) {
- vm_exit_during_initialization("Invalid survivor ratio specified");
+ bool adaptive_young_list_length() const {
+ return _adaptive_size;
}
- CollectorPolicy::initialize_flags();
- _young_gen_sizer = new G1YoungGenSizer(); // Must be after call to initialize_flags
-}
+};
-void G1CollectorPolicy::post_heap_initialize() {
- uintx max_regions = G1CollectedHeap::heap()->max_regions();
- size_t max_young_size = (size_t)_young_gen_sizer->max_young_length(max_regions) * HeapRegion::GrainBytes;
- if (max_young_size != MaxNewSize) {
- FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size);
- }
-
- _ihop_control = create_ihop_control();
-}
-
-G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); }
G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), _adaptive_size(true),
_min_desired_young_length(0), _max_desired_young_length(0) {
@@ -412,6 +466,29 @@
&_max_desired_young_length);
}
+void G1CollectorPolicy::post_heap_initialize() {
+ uintx max_regions = G1CollectedHeap::heap()->max_regions();
+ size_t max_young_size = (size_t)_young_gen_sizer->max_young_length(max_regions) * HeapRegion::GrainBytes;
+ if (max_young_size != MaxNewSize) {
+ FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size);
+ }
+
+ _ihop_control = create_ihop_control();
+}
+
+void G1CollectorPolicy::initialize_flags() {
+ if (G1HeapRegionSize != HeapRegion::GrainBytes) {
+ FLAG_SET_ERGO(size_t, G1HeapRegionSize, HeapRegion::GrainBytes);
+ }
+
+ if (SurvivorRatio < 1) {
+ vm_exit_during_initialization("Invalid survivor ratio specified");
+ }
+ CollectorPolicy::initialize_flags();
+ _young_gen_sizer = new G1YoungGenSizer(); // Must be after call to initialize_flags
+}
+
+
void G1CollectorPolicy::init() {
// Set aside an initial future to_space.
_g1 = G1CollectedHeap::heap();
@@ -758,7 +835,7 @@
curr = curr->get_next_young_region()) {
SurvRateGroup* group = curr->surv_rate_group();
if (group == NULL && !curr->is_survivor()) {
- log_info(gc, verify)("## %s: encountered NULL surv_rate_group", name);
+ log_error(gc, verify)("## %s: encountered NULL surv_rate_group", name);
ret = false;
}
@@ -766,12 +843,12 @@
int age = curr->age_in_surv_rate_group();
if (age < 0) {
- log_info(gc, verify)("## %s: encountered negative age", name);
+ log_error(gc, verify)("## %s: encountered negative age", name);
ret = false;
}
if (age <= prev_age) {
- log_info(gc, verify)("## %s: region ages are not strictly increasing (%d, %d)", name, age, prev_age);
+ log_error(gc, verify)("## %s: region ages are not strictly increasing (%d, %d)", name, age, prev_age);
ret = false;
}
prev_age = age;
@@ -1601,6 +1678,10 @@
return young_list_length < young_list_max_length;
}
+bool G1CollectorPolicy::adaptive_young_list_length() const {
+ return _young_gen_sizer->adaptive_young_list_length();
+}
+
void G1CollectorPolicy::update_max_gc_locker_expansion() {
uint expansion_region_num = 0;
if (GCLockerEdenExpansionPercent > 0) {
--- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -43,6 +43,7 @@
class HeapRegion;
class CollectionSetChooser;
class G1IHOPControl;
+class G1YoungGenSizer;
// TraceYoungGenTime collects data on _both_ young and mixed evacuation pauses
// (the latter may contain non-young regions - i.e. regions that are
@@ -90,81 +91,6 @@
void print() const;
};
-// There are three command line options related to the young gen size:
-// NewSize, MaxNewSize and NewRatio (There is also -Xmn, but that is
-// just a short form for NewSize==MaxNewSize). G1 will use its internal
-// heuristics to calculate the actual young gen size, so these options
-// basically only limit the range within which G1 can pick a young gen
-// size. Also, these are general options taking byte sizes. G1 will
-// internally work with a number of regions instead. So, some rounding
-// will occur.
-//
-// If nothing related to the the young gen size is set on the command
-// line we should allow the young gen to be between G1NewSizePercent
-// and G1MaxNewSizePercent of the heap size. This means that every time
-// the heap size changes, the limits for the young gen size will be
-// recalculated.
-//
-// If only -XX:NewSize is set we should use the specified value as the
-// minimum size for young gen. Still using G1MaxNewSizePercent of the
-// heap as maximum.
-//
-// If only -XX:MaxNewSize is set we should use the specified value as the
-// maximum size for young gen. Still using G1NewSizePercent of the heap
-// as minimum.
-//
-// If -XX:NewSize and -XX:MaxNewSize are both specified we use these values.
-// No updates when the heap size changes. There is a special case when
-// NewSize==MaxNewSize. This is interpreted as "fixed" and will use a
-// different heuristic for calculating the collection set when we do mixed
-// collection.
-//
-// If only -XX:NewRatio is set we should use the specified ratio of the heap
-// as both min and max. This will be interpreted as "fixed" just like the
-// NewSize==MaxNewSize case above. But we will update the min and max
-// every time the heap size changes.
-//
-// NewSize and MaxNewSize override NewRatio. So, NewRatio is ignored if it is
-// combined with either NewSize or MaxNewSize. (A warning message is printed.)
-class G1YoungGenSizer : public CHeapObj<mtGC> {
-private:
- enum SizerKind {
- SizerDefaults,
- SizerNewSizeOnly,
- SizerMaxNewSizeOnly,
- SizerMaxAndNewSize,
- SizerNewRatio
- };
- SizerKind _sizer_kind;
- uint _min_desired_young_length;
- uint _max_desired_young_length;
- bool _adaptive_size;
- uint calculate_default_min_length(uint new_number_of_heap_regions);
- uint calculate_default_max_length(uint new_number_of_heap_regions);
-
- // Update the given values for minimum and maximum young gen length in regions
- // given the number of heap regions depending on the kind of sizing algorithm.
- void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length);
-
-public:
- G1YoungGenSizer();
- // Calculate the maximum length of the young gen given the number of regions
- // depending on the sizing algorithm.
- uint max_young_length(uint number_of_heap_regions);
-
- void heap_size_changed(uint new_number_of_heap_regions);
- uint min_desired_young_length() {
- return _min_desired_young_length;
- }
- uint max_desired_young_length() {
- return _max_desired_young_length;
- }
-
- bool adaptive_young_list_length() const {
- return _adaptive_size;
- }
-};
-
class G1CollectorPolicy: public CollectorPolicy {
private:
G1IHOPControl* _ihop_control;
@@ -784,9 +710,7 @@
return _young_list_max_length;
}
- bool adaptive_young_list_length() const {
- return _young_gen_sizer->adaptive_young_list_length();
- }
+ bool adaptive_young_list_length() const;
virtual bool should_process_references() const {
return true;
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -372,6 +372,16 @@
return res;
}
+void G1CMRootRegions::notify_scan_done() {
+ MutexLockerEx x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag);
+ _scan_in_progress = false;
+ RootRegionScan_lock->notify_all();
+}
+
+void G1CMRootRegions::cancel_scan() {
+ notify_scan_done();
+}
+
void G1CMRootRegions::scan_finished() {
assert(scan_in_progress(), "pre-condition");
@@ -381,11 +391,7 @@
}
_next_survivor = NULL;
- {
- MutexLockerEx x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag);
- _scan_in_progress = false;
- RootRegionScan_lock->notify_all();
- }
+ notify_scan_done();
}
bool G1CMRootRegions::wait_until_scan_finished() {
@@ -978,13 +984,11 @@
};
void G1ConcurrentMark::scanRootRegions() {
- // Start of concurrent marking.
- ClassLoaderDataGraph::clear_claimed_marks();
-
// scan_in_progress() will have been set to true only if there was
// at least one root region to scan. So, if it's false, we
// should not attempt to do any further work.
if (root_regions()->scan_in_progress()) {
+ assert(!has_aborted(), "Aborting before root region scanning is finished not supported.");
GCTraceConcTime(Info, gc) tt("Concurrent Root Region Scan");
_parallel_marking_threads = calc_parallel_marking_threads();
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -229,6 +229,8 @@
volatile bool _should_abort;
HeapRegion* volatile _next_survivor;
+ void notify_scan_done();
+
public:
G1CMRootRegions();
// We actually do most of the initialization in this method.
@@ -248,6 +250,8 @@
// all have been claimed.
HeapRegion* claim_next();
+ void cancel_scan();
+
// Flag that we're done with root region scanning and notify anyone
// who's waiting on it. If aborted is false, assume that all regions
// have been claimed.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1HeapRegionTraceType.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_G1_G1HEAPREGIONTRACETYPE_HPP
+#define SHARE_VM_GC_G1_G1HEAPREGIONTRACETYPE_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+
+class G1HeapRegionTraceType : AllStatic {
+ public:
+ enum Type {
+ Free,
+ Eden,
+ Survivor,
+ StartsHumongous,
+ ContinuesHumongous,
+ Old,
+ Archive,
+ G1HeapRegionTypeEndSentinel
+ };
+
+ static const char* to_string(G1HeapRegionTraceType::Type type) {
+ switch (type) {
+ case Free: return "Free";
+ case Eden: return "Eden";
+ case Survivor: return "Survivor";
+ case StartsHumongous: return "Starts Humongous";
+ case ContinuesHumongous: return "Continues Humongous";
+ case Old: return "Old";
+ case Archive: return "Archive";
+ default: ShouldNotReachHere(); return NULL;
+ }
+ }
+};
+
+#endif // SHARE_VM_GC_G1_G1HEAPREGIONTRACETYPE_HPP
--- a/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/g1HeapVerifier.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -63,10 +63,10 @@
LogHandle(gc, verify) log;
log.info("Root location " PTR_FORMAT " points to dead obj " PTR_FORMAT, p2i(p), p2i(obj));
if (_vo == VerifyOption_G1UseMarkWord) {
- log.info(" Mark word: " PTR_FORMAT, p2i(obj->mark()));
+ log.error(" Mark word: " PTR_FORMAT, p2i(obj->mark()));
}
ResourceMark rm;
- obj->print_on(log.info_stream());
+ obj->print_on(log.error_stream());
_failures = true;
}
}
@@ -111,10 +111,10 @@
// Verify that the strong code root list for this region
// contains the nmethod
if (!hrrs->strong_code_roots_list_contains(_nm)) {
- log_info(gc, verify)("Code root location " PTR_FORMAT " "
- "from nmethod " PTR_FORMAT " not in strong "
- "code roots for region [" PTR_FORMAT "," PTR_FORMAT ")",
- p2i(p), p2i(_nm), p2i(hr->bottom()), p2i(hr->end()));
+ log_error(gc, verify)("Code root location " PTR_FORMAT " "
+ "from nmethod " PTR_FORMAT " not in strong "
+ "code roots for region [" PTR_FORMAT "," PTR_FORMAT ")",
+ p2i(p), p2i(_nm), p2i(hr->bottom()), p2i(hr->end()));
_failures = true;
}
}
@@ -292,8 +292,8 @@
r->object_iterate(¬_dead_yet_cl);
if (_vo != VerifyOption_G1UseNextMarking) {
if (r->max_live_bytes() < not_dead_yet_cl.live_bytes()) {
- log_info(gc, verify)("[" PTR_FORMAT "," PTR_FORMAT "] max_live_bytes " SIZE_FORMAT " < calculated " SIZE_FORMAT,
- p2i(r->bottom()), p2i(r->end()), r->max_live_bytes(), not_dead_yet_cl.live_bytes());
+ log_error(gc, verify)("[" PTR_FORMAT "," PTR_FORMAT "] max_live_bytes " SIZE_FORMAT " < calculated " SIZE_FORMAT,
+ p2i(r->bottom()), p2i(r->end()), r->max_live_bytes(), not_dead_yet_cl.live_bytes());
_failures = true;
}
} else {
@@ -402,13 +402,13 @@
}
if (failures) {
- log_info(gc, verify)("Heap after failed verification:");
+ log_error(gc, verify)("Heap after failed verification:");
// It helps to have the per-region information in the output to
// help us track down what went wrong. This is why we call
// print_extended_on() instead of print_on().
LogHandle(gc, verify) log;
ResourceMark rm;
- _g1h->print_extended_on(log.info_stream());
+ _g1h->print_extended_on(log.error_stream());
}
guarantee(!failures, "there should not have been any failures");
}
@@ -597,8 +597,8 @@
"tams: " PTR_FORMAT " end: " PTR_FORMAT, p2i(tams), p2i(end));
HeapWord* result = bitmap->getNextMarkedWordAddress(tams, end);
if (result < end) {
- log_info(gc, verify)("## wrong marked address on %s bitmap: " PTR_FORMAT, bitmap_name, p2i(result));
- log_info(gc, verify)("## %s tams: " PTR_FORMAT " end: " PTR_FORMAT, bitmap_name, p2i(tams), p2i(end));
+ log_error(gc, verify)("## wrong marked address on %s bitmap: " PTR_FORMAT, bitmap_name, p2i(result));
+ log_error(gc, verify)("## %s tams: " PTR_FORMAT " end: " PTR_FORMAT, bitmap_name, p2i(tams), p2i(end));
return false;
}
return true;
@@ -623,8 +623,8 @@
res_n = verify_no_bits_over_tams("next", next_bitmap, ntams, end);
}
if (!res_p || !res_n) {
- log_info(gc, verify)("#### Bitmap verification failed for " HR_FORMAT, HR_FORMAT_PARAMS(hr));
- log_info(gc, verify)("#### Caller: %s", caller);
+ log_error(gc, verify)("#### Bitmap verification failed for " HR_FORMAT, HR_FORMAT_PARAMS(hr));
+ log_error(gc, verify)("#### Caller: %s", caller);
return false;
}
return true;
@@ -676,41 +676,41 @@
InCSetState cset_state = (InCSetState) G1CollectedHeap::heap()->_in_cset_fast_test.get_by_index(i);
if (hr->is_humongous()) {
if (hr->in_collection_set()) {
- log_info(gc, verify)("## humongous region %u in CSet", i);
+ log_error(gc, verify)("## humongous region %u in CSet", i);
_failures = true;
return true;
}
if (cset_state.is_in_cset()) {
- log_info(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for humongous region %u", cset_state.value(), i);
+ log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for humongous region %u", cset_state.value(), i);
_failures = true;
return true;
}
if (hr->is_continues_humongous() && cset_state.is_humongous()) {
- log_info(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for continues humongous region %u", cset_state.value(), i);
+ log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for continues humongous region %u", cset_state.value(), i);
_failures = true;
return true;
}
} else {
if (cset_state.is_humongous()) {
- log_info(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for non-humongous region %u", cset_state.value(), i);
+ log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for non-humongous region %u", cset_state.value(), i);
_failures = true;
return true;
}
if (hr->in_collection_set() != cset_state.is_in_cset()) {
- log_info(gc, verify)("## in CSet %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
+ log_error(gc, verify)("## in CSet %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
hr->in_collection_set(), cset_state.value(), i);
_failures = true;
return true;
}
if (cset_state.is_in_cset()) {
if (hr->is_young() != (cset_state.is_young())) {
- log_info(gc, verify)("## is_young %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
+ log_error(gc, verify)("## is_young %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
hr->is_young(), cset_state.value(), i);
_failures = true;
return true;
}
if (hr->is_old() != (cset_state.is_old())) {
- log_info(gc, verify)("## is_old %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
+ log_error(gc, verify)("## is_old %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
hr->is_old(), cset_state.value(), i);
_failures = true;
return true;
--- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -26,11 +26,13 @@
#include "code/nmethod.hpp"
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
+#include "gc/g1/g1HeapRegionTraceType.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionBounds.inline.hpp"
#include "gc/g1/heapRegionManager.inline.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
+#include "gc/g1/heapRegionTracer.hpp"
#include "gc/shared/genOopClosures.inline.hpp"
#include "gc/shared/liveRange.hpp"
#include "gc/shared/space.inline.hpp"
@@ -212,10 +214,41 @@
_gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms;
}
+void HeapRegion::set_free() {
+ report_region_type_change(G1HeapRegionTraceType::Free);
+ _type.set_free();
+}
+
+void HeapRegion::set_eden() {
+ report_region_type_change(G1HeapRegionTraceType::Eden);
+ _type.set_eden();
+}
+
+void HeapRegion::set_eden_pre_gc() {
+ report_region_type_change(G1HeapRegionTraceType::Eden);
+ _type.set_eden_pre_gc();
+}
+
+void HeapRegion::set_survivor() {
+ report_region_type_change(G1HeapRegionTraceType::Survivor);
+ _type.set_survivor();
+}
+
+void HeapRegion::set_old() {
+ report_region_type_change(G1HeapRegionTraceType::Old);
+ _type.set_old();
+}
+
+void HeapRegion::set_archive() {
+ report_region_type_change(G1HeapRegionTraceType::Archive);
+ _type.set_archive();
+}
+
void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) {
assert(!is_humongous(), "sanity / pre-condition");
assert(top() == bottom(), "should be empty");
+ report_region_type_change(G1HeapRegionTraceType::StartsHumongous);
_type.set_starts_humongous();
_humongous_start_region = this;
@@ -227,6 +260,7 @@
assert(top() == bottom(), "should be empty");
assert(first_hr->is_starts_humongous(), "pre-condition");
+ report_region_type_change(G1HeapRegionTraceType::ContinuesHumongous);
_type.set_continues_humongous();
_humongous_start_region = first_hr;
}
@@ -272,6 +306,15 @@
record_timestamp();
}
+void HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) {
+ HeapRegionTracer::send_region_type_change(_hrm_index,
+ get_trace_type(),
+ to,
+ (uintptr_t)bottom(),
+ used(),
+ (uint)allocation_context());
+}
+
CompactibleSpace* HeapRegion::next_compaction_space() const {
return G1CollectedHeap::heap()->next_compaction_region(this);
}
@@ -479,7 +522,7 @@
// Object is in the region. Check that its less than top
if (_hr->top() <= (HeapWord*)obj) {
// Object is above top
- log_info(gc, verify)("Object " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ") is above top " PTR_FORMAT,
+ log_error(gc, verify)("Object " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ") is above top " PTR_FORMAT,
p2i(obj), p2i(_hr->bottom()), p2i(_hr->end()), p2i(_hr->top()));
_failures = true;
return;
@@ -513,19 +556,19 @@
if (nm != NULL) {
// Verify that the nemthod is live
if (!nm->is_alive()) {
- log_info(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has dead nmethod " PTR_FORMAT " in its strong code roots",
- p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
+ log_error(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has dead nmethod " PTR_FORMAT " in its strong code roots",
+ p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
_failures = true;
} else {
VerifyStrongCodeRootOopClosure oop_cl(_hr, nm);
nm->oops_do(&oop_cl);
if (!oop_cl.has_oops_in_region()) {
- log_info(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has nmethod " PTR_FORMAT " in its strong code roots with no pointers into region",
- p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
+ log_error(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has nmethod " PTR_FORMAT " in its strong code roots with no pointers into region",
+ p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
_failures = true;
} else if (oop_cl.failures()) {
- log_info(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has other failures for nmethod " PTR_FORMAT,
- p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
+ log_error(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] has other failures for nmethod " PTR_FORMAT,
+ p2i(_hr->bottom()), p2i(_hr->end()), p2i(nm));
_failures = true;
}
}
@@ -558,8 +601,8 @@
// on its strong code root list
if (is_empty()) {
if (strong_code_roots_length > 0) {
- log_info(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] is empty but has " SIZE_FORMAT " code root entries",
- p2i(bottom()), p2i(end()), strong_code_roots_length);
+ log_error(gc, verify)("region [" PTR_FORMAT "," PTR_FORMAT "] is empty but has " SIZE_FORMAT " code root entries",
+ p2i(bottom()), p2i(end()), strong_code_roots_length);
*failures = true;
}
return;
@@ -567,8 +610,8 @@
if (is_continues_humongous()) {
if (strong_code_roots_length > 0) {
- log_info(gc, verify)("region " HR_FORMAT " is a continuation of a humongous region but has " SIZE_FORMAT " code root entries",
- HR_FORMAT_PARAMS(this), strong_code_roots_length);
+ log_error(gc, verify)("region " HR_FORMAT " is a continuation of a humongous region but has " SIZE_FORMAT " code root entries",
+ HR_FORMAT_PARAMS(this), strong_code_roots_length);
*failures = true;
}
return;
@@ -661,26 +704,26 @@
Mutex::_no_safepoint_check_flag);
if (!_failures) {
- log.info("----------");
+ log.error("----------");
}
ResourceMark rm;
if (!_g1h->is_in_closed_subset(obj)) {
HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
- log.info("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
+ log.error("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
p2i(p), p2i(_containing_obj), p2i(from->bottom()), p2i(from->end()));
- print_object(log.info_stream(), _containing_obj);
- log.info("points to obj " PTR_FORMAT " not in the heap", p2i(obj));
+ print_object(log.error_stream(), _containing_obj);
+ log.error("points to obj " PTR_FORMAT " not in the heap", p2i(obj));
} else {
HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p);
HeapRegion* to = _g1h->heap_region_containing((HeapWord*)obj);
- log.info("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
+ log.error("Field " PTR_FORMAT " of live obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
p2i(p), p2i(_containing_obj), p2i(from->bottom()), p2i(from->end()));
- print_object(log.info_stream(), _containing_obj);
- log.info("points to dead obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
+ print_object(log.error_stream(), _containing_obj);
+ log.error("points to dead obj " PTR_FORMAT " in region [" PTR_FORMAT ", " PTR_FORMAT ")",
p2i(obj), p2i(to->bottom()), p2i(to->end()));
- print_object(log.info_stream(), obj);
+ print_object(log.error_stream(), obj);
}
- log.info("----------");
+ log.error("----------");
_failures = true;
failed = true;
_n_failures++;
@@ -730,17 +773,17 @@
Mutex::_no_safepoint_check_flag);
if (!_failures) {
- log.info("----------");
+ log.error("----------");
}
- log.info("Missing rem set entry:");
- log.info("Field " PTR_FORMAT " of obj " PTR_FORMAT ", in region " HR_FORMAT,
+ log.error("Missing rem set entry:");
+ log.error("Field " PTR_FORMAT " of obj " PTR_FORMAT ", in region " HR_FORMAT,
p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from));
ResourceMark rm;
- _containing_obj->print_on(log.info_stream());
- log.info("points to obj " PTR_FORMAT " in region " HR_FORMAT, p2i(obj), HR_FORMAT_PARAMS(to));
- obj->print_on(log.info_stream());
- log.info("Obj head CTE = %d, field CTE = %d.", cv_obj, cv_field);
- log.info("----------");
+ _containing_obj->print_on(log.error_stream());
+ log.error("points to obj " PTR_FORMAT " in region " HR_FORMAT, p2i(obj), HR_FORMAT_PARAMS(to));
+ obj->print_on(log.error_stream());
+ log.error("Obj head CTE = %d, field CTE = %d.", cv_obj, cv_field);
+ log.error("----------");
_failures = true;
if (!failed) _n_failures++;
}
@@ -774,13 +817,13 @@
(vo == VerifyOption_G1UsePrevMarking &&
ClassLoaderDataGraph::unload_list_contains(klass));
if (!is_metaspace_object) {
- log_info(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " "
- "not metadata", p2i(klass), p2i(obj));
+ log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " "
+ "not metadata", p2i(klass), p2i(obj));
*failures = true;
return;
} else if (!klass->is_klass()) {
- log_info(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " "
- "not a klass", p2i(klass), p2i(obj));
+ log_error(gc, verify)("klass " PTR_FORMAT " of object " PTR_FORMAT " "
+ "not a klass", p2i(klass), p2i(obj));
*failures = true;
return;
} else {
@@ -811,7 +854,7 @@
}
}
} else {
- log_info(gc, verify)(PTR_FORMAT " not an oop", p2i(obj));
+ log_error(gc, verify)(PTR_FORMAT " not an oop", p2i(obj));
*failures = true;
return;
}
@@ -827,13 +870,15 @@
if (is_region_humongous) {
oop obj = oop(this->humongous_start_region()->bottom());
if ((HeapWord*)obj > bottom() || (HeapWord*)obj + obj->size() < bottom()) {
- log_info(gc, verify)("this humongous region is not part of its' humongous object " PTR_FORMAT, p2i(obj));
+ log_error(gc, verify)("this humongous region is not part of its' humongous object " PTR_FORMAT, p2i(obj));
+ *failures = true;
+ return;
}
}
if (!is_region_humongous && p != top()) {
- log_info(gc, verify)("end of last object " PTR_FORMAT " "
- "does not match top " PTR_FORMAT, p2i(p), p2i(top()));
+ log_error(gc, verify)("end of last object " PTR_FORMAT " "
+ "does not match top " PTR_FORMAT, p2i(p), p2i(top()));
*failures = true;
return;
}
@@ -847,9 +892,9 @@
HeapWord* addr_1 = p;
HeapWord* b_start_1 = _bot_part.block_start_const(addr_1);
if (b_start_1 != p) {
- log_info(gc, verify)("BOT look up for top: " PTR_FORMAT " "
- " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
- p2i(addr_1), p2i(b_start_1), p2i(p));
+ log_error(gc, verify)("BOT look up for top: " PTR_FORMAT " "
+ " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
+ p2i(addr_1), p2i(b_start_1), p2i(p));
*failures = true;
return;
}
@@ -859,9 +904,9 @@
if (addr_2 < the_end) {
HeapWord* b_start_2 = _bot_part.block_start_const(addr_2);
if (b_start_2 != p) {
- log_info(gc, verify)("BOT look up for top + 1: " PTR_FORMAT " "
- " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
- p2i(addr_2), p2i(b_start_2), p2i(p));
+ log_error(gc, verify)("BOT look up for top + 1: " PTR_FORMAT " "
+ " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
+ p2i(addr_2), p2i(b_start_2), p2i(p));
*failures = true;
return;
}
@@ -873,9 +918,9 @@
if (addr_3 < the_end) {
HeapWord* b_start_3 = _bot_part.block_start_const(addr_3);
if (b_start_3 != p) {
- log_info(gc, verify)("BOT look up for top + diff: " PTR_FORMAT " "
- " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
- p2i(addr_3), p2i(b_start_3), p2i(p));
+ log_error(gc, verify)("BOT look up for top + diff: " PTR_FORMAT " "
+ " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
+ p2i(addr_3), p2i(b_start_3), p2i(p));
*failures = true;
return;
}
@@ -885,9 +930,9 @@
HeapWord* addr_4 = the_end - 1;
HeapWord* b_start_4 = _bot_part.block_start_const(addr_4);
if (b_start_4 != p) {
- log_info(gc, verify)("BOT look up for end - 1: " PTR_FORMAT " "
- " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
- p2i(addr_4), p2i(b_start_4), p2i(p));
+ log_error(gc, verify)("BOT look up for end - 1: " PTR_FORMAT " "
+ " yielded " PTR_FORMAT ", expecting " PTR_FORMAT,
+ p2i(addr_4), p2i(b_start_4), p2i(p));
*failures = true;
return;
}
@@ -924,7 +969,7 @@
return;
}
} else {
- log_info(gc, verify)(PTR_FORMAT " not an oop", p2i(obj));
+ log_error(gc, verify)(PTR_FORMAT " not an oop", p2i(obj));
*failures = true;
return;
}
--- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -27,6 +27,8 @@
#include "gc/g1/g1AllocationContext.hpp"
#include "gc/g1/g1BlockOffsetTable.hpp"
+#include "gc/g1/g1HeapRegionTraceType.hpp"
+#include "gc/g1/heapRegionTracer.hpp"
#include "gc/g1/heapRegionType.hpp"
#include "gc/g1/survRateGroup.hpp"
#include "gc/shared/ageTable.hpp"
@@ -243,6 +245,8 @@
return HeapRegion::block_size(addr); // Avoid virtual call
}
+ void report_region_type_change(G1HeapRegionTraceType::Type to);
+
protected:
// The index of this region in the heap region sequence.
uint _hrm_index;
@@ -427,6 +431,7 @@
const char* get_type_str() const { return _type.get_str(); }
const char* get_short_type_str() const { return _type.get_short_str(); }
+ G1HeapRegionTraceType::Type get_trace_type() { return _type.get_trace_type(); }
bool is_free() const { return _type.is_free(); }
@@ -637,15 +642,15 @@
}
}
- void set_free() { _type.set_free(); }
+ void set_free();
- void set_eden() { _type.set_eden(); }
- void set_eden_pre_gc() { _type.set_eden_pre_gc(); }
- void set_survivor() { _type.set_survivor(); }
+ void set_eden();
+ void set_eden_pre_gc();
+ void set_survivor();
- void set_old() { _type.set_old(); }
+ void set_old();
- void set_archive() { _type.set_archive(); }
+ void set_archive();
// Determine if an object has been allocated since the last
// mark performed by the collector. This returns true iff the object
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegionTracer.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2015, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/g1/heapRegionTracer.hpp"
+#include "trace/tracing.hpp"
+
+void HeapRegionTracer::send_region_type_change(uint index,
+ G1HeapRegionTraceType::Type from,
+ G1HeapRegionTraceType::Type to,
+ uintptr_t start,
+ size_t used,
+ uint allocationContext) {
+ EventG1HeapRegionTypeChange e;
+ if (e.should_commit()) {
+ e.set_index(index);
+ e.set_from(from);
+ e.set_to(to);
+ e.set_start(start);
+ e.set_used(used);
+ e.set_allocContext(allocationContext);
+ e.commit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/g1/heapRegionTracer.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_G1_HEAPREGIONTRACER_HPP
+#define SHARE_VM_GC_G1_HEAPREGIONTRACER_HPP
+
+#include "gc/g1/g1HeapRegionTraceType.hpp"
+#include "memory/allocation.hpp"
+
+class HeapRegionTracer : AllStatic {
+ public:
+ static void send_region_type_change(uint index,
+ G1HeapRegionTraceType::Type from,
+ G1HeapRegionTraceType::Type to,
+ uintptr_t start,
+ size_t used,
+ uint allocationContext);
+};
+
+#endif // SHARE_VM_GC_G1_HEAPREGIONTRACER_HPP
--- a/hotspot/src/share/vm/gc/g1/heapRegionType.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/heapRegionType.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "gc/g1/g1HeapRegionTraceType.hpp"
#include "gc/g1/heapRegionType.hpp"
bool HeapRegionType::is_valid(Tag tag) {
@@ -70,3 +71,19 @@
// keep some compilers happy
return NULL;
}
+
+G1HeapRegionTraceType::Type HeapRegionType::get_trace_type() {
+ hrt_assert_is_valid(_tag);
+ switch (_tag) {
+ case FreeTag: return G1HeapRegionTraceType::Free;
+ case EdenTag: return G1HeapRegionTraceType::Eden;
+ case SurvTag: return G1HeapRegionTraceType::Survivor;
+ case StartsHumongousTag: return G1HeapRegionTraceType::StartsHumongous;
+ case ContinuesHumongousTag: return G1HeapRegionTraceType::ContinuesHumongous;
+ case OldTag: return G1HeapRegionTraceType::Old;
+ case ArchiveTag: return G1HeapRegionTraceType::Archive;
+ }
+ ShouldNotReachHere();
+ // keep some compilers happy
+ return G1HeapRegionTraceType::Free;
+}
--- a/hotspot/src/share/vm/gc/g1/heapRegionType.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/heapRegionType.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -25,6 +25,7 @@
#ifndef SHARE_VM_GC_G1_HEAPREGIONTYPE_HPP
#define SHARE_VM_GC_G1_HEAPREGIONTYPE_HPP
+#include "gc/g1/g1HeapRegionTraceType.hpp"
#include "memory/allocation.hpp"
#define hrt_assert_is_valid(tag) \
@@ -141,6 +142,7 @@
const char* get_str() const;
const char* get_short_str() const;
+ G1HeapRegionTraceType::Type get_trace_type();
HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); }
};
--- a/hotspot/src/share/vm/gc/g1/satbMarkQueue.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/satbMarkQueue.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -221,13 +221,13 @@
#ifdef ASSERT
void SATBMarkQueueSet::dump_active_states(bool expected_active) {
- log_info(gc, verify)("Expected SATB active state: %s", expected_active ? "ACTIVE" : "INACTIVE");
- log_info(gc, verify)("Actual SATB active states:");
- log_info(gc, verify)(" Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE");
+ log_error(gc, verify)("Expected SATB active state: %s", expected_active ? "ACTIVE" : "INACTIVE");
+ log_error(gc, verify)("Actual SATB active states:");
+ log_error(gc, verify)(" Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE");
for (JavaThread* t = Threads::first(); t; t = t->next()) {
- log_info(gc, verify)(" Thread \"%s\" queue: %s", t->name(), t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE");
+ log_error(gc, verify)(" Thread \"%s\" queue: %s", t->name(), t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE");
}
- log_info(gc, verify)(" Shared queue: %s", shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE");
+ log_error(gc, verify)(" Shared queue: %s", shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE");
}
void SATBMarkQueueSet::verify_active_states(bool expected_active) {
--- a/hotspot/src/share/vm/gc/g1/youngList.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/g1/youngList.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -99,10 +99,10 @@
HeapRegion* last = NULL;
while (curr != NULL) {
if (!curr->is_young()) {
- log_info(gc, verify)("### YOUNG REGION " PTR_FORMAT "-" PTR_FORMAT " "
- "incorrectly tagged (y: %d, surv: %d)",
- p2i(curr->bottom()), p2i(curr->end()),
- curr->is_young(), curr->is_survivor());
+ log_error(gc, verify)("### YOUNG REGION " PTR_FORMAT "-" PTR_FORMAT " "
+ "incorrectly tagged (y: %d, surv: %d)",
+ p2i(curr->bottom()), p2i(curr->end()),
+ curr->is_young(), curr->is_survivor());
ret = false;
}
++length;
@@ -112,8 +112,8 @@
ret = ret && (length == _length);
if (!ret) {
- log_info(gc, verify)("### YOUNG LIST seems not well formed!");
- log_info(gc, verify)("### list has %u entries, _length is %u", length, _length);
+ log_error(gc, verify)("### YOUNG LIST seems not well formed!");
+ log_error(gc, verify)("### list has %u entries, _length is %u", length, _length);
}
return ret;
@@ -123,19 +123,19 @@
bool ret = true;
if (_length != 0) {
- log_info(gc, verify)("### YOUNG LIST should have 0 length, not %u", _length);
+ log_error(gc, verify)("### YOUNG LIST should have 0 length, not %u", _length);
ret = false;
}
if (check_sample && _last_sampled_rs_lengths != 0) {
- log_info(gc, verify)("### YOUNG LIST has non-zero last sampled RS lengths");
+ log_error(gc, verify)("### YOUNG LIST has non-zero last sampled RS lengths");
ret = false;
}
if (_head != NULL) {
- log_info(gc, verify)("### YOUNG LIST does not have a NULL head");
+ log_error(gc, verify)("### YOUNG LIST does not have a NULL head");
ret = false;
}
if (!ret) {
- log_info(gc, verify)("### YOUNG LIST does not seem empty");
+ log_error(gc, verify)("### YOUNG LIST does not seem empty");
}
return ret;
--- a/hotspot/src/share/vm/gc/shared/ageTable.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/ageTable.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc/shared/ageTable.inline.hpp"
+#include "gc/shared/ageTableTracer.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/collectorPolicy.hpp"
#include "gc/shared/gcPolicyCounters.hpp"
@@ -100,17 +101,19 @@
log_debug(gc, age)("Desired survivor size " SIZE_FORMAT " bytes, new threshold " UINTX_FORMAT " (max threshold " UINTX_FORMAT ")",
desired_survivor_size*oopSize, (uintx) result, MaxTenuringThreshold);
- if (log_is_enabled(Trace, gc, age) || UsePerfData) {
+ if (log_is_enabled(Trace, gc, age) || UsePerfData || AgeTableTracer::is_tenuring_distribution_event_enabled()) {
size_t total = 0;
uint age = 1;
while (age < table_size) {
- total += sizes[age];
- if (sizes[age] > 0) {
+ size_t wordSize = sizes[age];
+ total += wordSize;
+ if (wordSize > 0) {
log_trace(gc, age)("- age %3u: " SIZE_FORMAT_W(10) " bytes, " SIZE_FORMAT_W(10) " total",
- age, sizes[age]*oopSize, total*oopSize);
+ age, wordSize*oopSize, total*oopSize);
}
+ AgeTableTracer::send_tenuring_distribution_event(age, wordSize*oopSize);
if (UsePerfData) {
- _perf_sizes[age]->set_value(sizes[age]*oopSize);
+ _perf_sizes[age]->set_value(wordSize*oopSize);
}
age++;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/shared/ageTableTracer.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/shared/ageTableTracer.hpp"
+#include "gc/shared/gcId.hpp"
+#include "trace/tracing.hpp"
+
+void AgeTableTracer::send_tenuring_distribution_event(uint age, size_t size) {
+ EventTenuringDistribution e;
+ if (e.should_commit()) {
+ e.set_gcId(GCId::current());
+ e.set_age(age);
+ e.set_size(size);
+ e.commit();
+ }
+}
+
+bool AgeTableTracer::is_tenuring_distribution_event_enabled() {
+ return EventTenuringDistribution::is_enabled();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/shared/ageTableTracer.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_AGETABLETRACER_HPP
+#define SHARE_VM_GC_SHARED_AGETABLETRACER_HPP
+
+#include "memory/allocation.hpp"
+
+class AgeTableTracer : AllStatic {
+ public:
+ static void send_tenuring_distribution_event(uint age, size_t size);
+ static bool is_tenuring_distribution_event_enabled();
+};
+
+#endif // SHARE_VM_GC_SHARED_AGETABLETRACER_HPP
--- a/hotspot/src/share/vm/gc/shared/concurrentGCThread.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/concurrentGCThread.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -60,7 +60,7 @@
void ConcurrentGCThread::wait_for_universe_init() {
MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag);
while (!is_init_completed() && !_should_terminate) {
- CGC_lock->wait(Mutex::_no_safepoint_check_flag, 200);
+ CGC_lock->wait(Mutex::_no_safepoint_check_flag, 1);
}
}
--- a/hotspot/src/share/vm/gc/shared/concurrentGCThread.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/concurrentGCThread.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -32,7 +32,7 @@
friend class VMStructs;
protected:
- bool _should_terminate;
+ bool volatile _should_terminate;
bool _has_terminated;
// Create and start the thread (setting it's priority high.)
--- a/hotspot/src/share/vm/gc/shared/gcHeapSummary.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/gcHeapSummary.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -131,12 +131,14 @@
size_t _edenUsed;
size_t _edenCapacity;
size_t _survivorUsed;
+ uint _numberOfRegions;
public:
- G1HeapSummary(VirtualSpaceSummary& heap_space, size_t heap_used, size_t edenUsed, size_t edenCapacity, size_t survivorUsed) :
- GCHeapSummary(heap_space, heap_used), _edenUsed(edenUsed), _edenCapacity(edenCapacity), _survivorUsed(survivorUsed) { }
+ G1HeapSummary(VirtualSpaceSummary& heap_space, size_t heap_used, size_t edenUsed, size_t edenCapacity, size_t survivorUsed, uint numberOfRegions) :
+ GCHeapSummary(heap_space, heap_used), _edenUsed(edenUsed), _edenCapacity(edenCapacity), _survivorUsed(survivorUsed), _numberOfRegions(numberOfRegions) { }
const size_t edenUsed() const { return _edenUsed; }
const size_t edenCapacity() const { return _edenCapacity; }
const size_t survivorUsed() const { return _survivorUsed; }
+ const uint numberOfRegions() const { return _numberOfRegions; }
virtual void accept(GCHeapSummaryVisitor* visitor) const {
visitor->visit(this);
--- a/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -359,6 +359,7 @@
e.set_edenUsedSize(g1_heap_summary->edenUsed());
e.set_edenTotalSize(g1_heap_summary->edenCapacity());
e.set_survivorUsedSize(g1_heap_summary->survivorUsed());
+ e.set_numberOfRegions(g1_heap_summary->numberOfRegions());
e.commit();
}
}
--- a/hotspot/src/share/vm/gc/shared/memset_with_concurrent_readers.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/gc/shared/memset_with_concurrent_readers.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -34,7 +34,7 @@
#if INCLUDE_ALL_GCS
// Unit test
-#ifdef ASSERT
+#ifndef PRODUCT
static unsigned line_byte(const char* line, size_t i) {
return unsigned(line[i]) & 0xFF;
--- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1091,7 +1091,7 @@
} else if (reference->is_a(site_DataSectionReference::klass())) {
int data_offset = site_DataSectionReference::offset(reference);
if (0 <= data_offset && data_offset < _constants_size) {
- pd_patch_DataSectionReference(pc_offset, data_offset);
+ pd_patch_DataSectionReference(pc_offset, data_offset, CHECK);
} else {
JVMCI_ERROR("data offset 0x%X points outside data section (size 0x%X)", data_offset, _constants_size);
}
--- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -156,7 +156,7 @@
jint pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS);
void pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS);
void pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS);
- void pd_patch_DataSectionReference(int pc_offset, int data_offset);
+ void pd_patch_DataSectionReference(int pc_offset, int data_offset, TRAPS);
void pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS);
void pd_relocate_JavaMethod(Handle method, jint pc_offset, TRAPS);
void pd_relocate_poll(address pc, jint mark, TRAPS);
--- a/hotspot/src/share/vm/jvmci/jvmciEnv.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/jvmci/jvmciEnv.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -591,6 +591,13 @@
// JVMTI -- compiled method notification (must be done outside lock)
if (nm != NULL) {
nm->post_compiled_method_load_event();
+
+ if (env == NULL) {
+ // This compile didn't come through the CompileBroker so perform the printing here
+ DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, compiler);
+ nm->maybe_print_nmethod(directive);
+ DirectivesStack::release(directive);
+ }
}
return result;
--- a/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -151,6 +151,7 @@
nonstatic_field(JavaThread, _pending_failed_speculation, oop) \
nonstatic_field(JavaThread, _pending_transfer_to_interpreter, bool) \
nonstatic_field(JavaThread, _jvmci_counters, jlong*) \
+ nonstatic_field(JavaThread, _reserved_stack_activation, address) \
\
static_field(java_lang_Class, _klass_offset, int) \
static_field(java_lang_Class, _array_klass_offset, int) \
@@ -210,6 +211,8 @@
\
static_field(StubRoutines, _verify_oop_count, jint) \
\
+ static_field(StubRoutines, _throw_delayed_StackOverflowError_entry, address) \
+ \
static_field(StubRoutines, _jbyte_arraycopy, address) \
static_field(StubRoutines, _jshort_arraycopy, address) \
static_field(StubRoutines, _jint_arraycopy, address) \
@@ -471,6 +474,7 @@
declare_constant(Method::_force_inline) \
declare_constant(Method::_dont_inline) \
declare_constant(Method::_hidden) \
+ declare_constant(Method::_reserved_stack_access) \
\
declare_constant(Method::nonvirtual_vtable_index) \
declare_constant(Method::invalid_vtable_index) \
@@ -517,6 +521,7 @@
declare_function(SharedRuntime::register_finalizer) \
declare_function(SharedRuntime::exception_handler_for_return_address) \
declare_function(SharedRuntime::OSR_migration_end) \
+ declare_function(SharedRuntime::enable_stack_reserved_zone) \
declare_function(SharedRuntime::dsin) \
declare_function(SharedRuntime::dcos) \
declare_function(SharedRuntime::dtan) \
--- a/hotspot/src/share/vm/opto/c2_globals.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/c2_globals.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -182,6 +182,10 @@
"Unroll loop bodies with node count less than this") \
range(0, max_jint / 4) \
\
+ product_pd(intx, LoopPercentProfileLimit, \
+ "Unroll loop bodies with % node count of profile limit") \
+ range(10, 100) \
+ \
product(intx, LoopMaxUnroll, 16, \
"Maximum number of unrolls for main loop") \
range(0, max_jint) \
--- a/hotspot/src/share/vm/opto/callGenerator.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/callGenerator.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -825,10 +825,12 @@
input_not_const = false;
const TypeOopPtr* oop_ptr = receiver->bottom_type()->is_oopptr();
ciMethod* target = oop_ptr->const_oop()->as_method_handle()->get_vmtarget();
- guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove
const int vtable_index = Method::invalid_vtable_index;
- CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS, NULL, true, true);
- assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here");
+ CallGenerator* cg = C->call_generator(target, vtable_index,
+ false /* call_does_dispatch */,
+ jvms,
+ true /* allow_inline */,
+ PROB_ALWAYS);
return cg;
} else {
const char* msg = "receiver not constant";
@@ -899,13 +901,15 @@
target = C->optimize_virtual_call(caller, jvms->bci(), klass, klass,
target, receiver_type, is_virtual,
call_does_dispatch, vtable_index, // out-parameters
- /*check_access=*/false);
+ false /* check_access */);
// We lack profiling at this call but type speculation may
// provide us with a type
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
}
- CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, /*allow_inline=*/true, PROB_ALWAYS, speculative_receiver_type, true, true);
- assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here");
+ CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
+ true /* allow_inline */,
+ PROB_ALWAYS,
+ speculative_receiver_type);
return cg;
} else {
const char* msg = "member_name not constant";
--- a/hotspot/src/share/vm/opto/castnode.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/castnode.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -37,7 +37,6 @@
Node* ConstraintCastNode::Identity(PhaseGVN* phase) {
Node* dom = dominating_cast(phase);
if (dom != NULL) {
- assert(_carry_dependency, "only for casts that carry a dependency");
return dom;
}
if (_carry_dependency) {
@@ -110,18 +109,22 @@
}
TypeNode* ConstraintCastNode::dominating_cast(PhaseTransform *phase) const {
- if (!carry_dependency()) {
- return NULL;
- }
Node* val = in(1);
Node* ctl = in(0);
int opc = Opcode();
if (ctl == NULL) {
return NULL;
}
+ // Range check CastIIs may all end up under a single range check and
+ // in that case only the narrower CastII would be kept by the code
+ // below which would be incorrect.
+ if (is_CastII() && as_CastII()->has_range_check()) {
+ return NULL;
+ }
for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) {
Node* u = val->fast_out(i);
if (u != this &&
+ u->outcnt() > 0 &&
u->Opcode() == opc &&
u->in(0) != NULL &&
u->bottom_type()->higher_equal(type())) {
@@ -300,7 +303,6 @@
Node* CheckCastPPNode::Identity(PhaseGVN* phase) {
Node* dom = dominating_cast(phase);
if (dom != NULL) {
- assert(_carry_dependency, "only for casts that carry a dependency");
return dom;
}
if (_carry_dependency) {
--- a/hotspot/src/share/vm/opto/loopTransform.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/loopTransform.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -666,7 +666,8 @@
if (future_unroll_ct > LoopMaxUnroll) return false;
} else {
// obey user constraints on vector mapped loops with additional unrolling applied
- if ((future_unroll_ct / cl->slp_max_unroll()) > LoopMaxUnroll) return false;
+ int unroll_constraint = (cl->slp_max_unroll()) ? cl->slp_max_unroll() : 1;
+ if ((future_unroll_ct / unroll_constraint) > LoopMaxUnroll) return false;
}
// Check for initial stride being a small enough constant
@@ -689,7 +690,7 @@
// Progress defined as current size less than 20% larger than previous size.
if (UseSuperWord && cl->node_count_before_unroll() > 0 &&
future_unroll_ct > LoopUnrollMin &&
- (future_unroll_ct - 1) * 10.0 > cl->profile_trip_cnt() &&
+ (future_unroll_ct - 1) * (100 / LoopPercentProfileLimit) > cl->profile_trip_cnt() &&
1.2 * cl->node_count_before_unroll() < (double)_body.size()) {
return false;
}
@@ -1260,6 +1261,146 @@
loop->record_for_igvn();
}
+//------------------------------insert_vector_post_loop------------------------
+// Insert a copy of the atomic unrolled vectorized main loop as a post loop,
+// unroll_policy has already informed us that more unrolling is about to happen to
+// the main loop. The resultant post loop will serve as a vectorized drain loop.
+void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new) {
+ if (!loop->_head->is_CountedLoop()) return;
+
+ CountedLoopNode *cl = loop->_head->as_CountedLoop();
+
+ // only process vectorized main loops
+ if (!cl->is_vectorized_loop() || !cl->is_main_loop()) return;
+
+ int slp_max_unroll_factor = cl->slp_max_unroll();
+ int cur_unroll = cl->unrolled_count();
+
+ if (slp_max_unroll_factor == 0) return;
+
+ // only process atomic unroll vector loops (not super unrolled after vectorization)
+ if (cur_unroll != slp_max_unroll_factor) return;
+
+ // we only ever process this one time
+ if (cl->has_atomic_post_loop()) return;
+
+#ifndef PRODUCT
+ if (TraceLoopOpts) {
+ tty->print("PostVector ");
+ loop->dump_head();
+ }
+#endif
+ C->set_major_progress();
+
+ // Find common pieces of the loop being guarded with pre & post loops
+ CountedLoopNode *main_head = loop->_head->as_CountedLoop();
+ CountedLoopEndNode *main_end = main_head->loopexit();
+ guarantee(main_end != NULL, "no loop exit node");
+ // diagnostic to show loop end is not properly formed
+ assert(main_end->outcnt() == 2, "1 true, 1 false path only");
+ uint dd_main_head = dom_depth(main_head);
+ uint max = main_head->outcnt();
+
+ // mark this loop as processed
+ main_head->mark_has_atomic_post_loop();
+
+ Node *pre_header = main_head->in(LoopNode::EntryControl);
+ Node *init = main_head->init_trip();
+ Node *incr = main_end->incr();
+ Node *limit = main_end->limit();
+ Node *stride = main_end->stride();
+ Node *cmp = main_end->cmp_node();
+ BoolTest::mask b_test = main_end->test_trip();
+
+ //------------------------------
+ // Step A: Create a new post-Loop.
+ Node* main_exit = main_end->proj_out(false);
+ assert(main_exit->Opcode() == Op_IfFalse, "");
+ int dd_main_exit = dom_depth(main_exit);
+
+ // Step A1: Clone the loop body of main. The clone becomes the vector post-loop.
+ // The main loop pre-header illegally has 2 control users (old & new loops).
+ clone_loop(loop, old_new, dd_main_exit);
+ assert(old_new[main_end->_idx]->Opcode() == Op_CountedLoopEnd, "");
+ CountedLoopNode *post_head = old_new[main_head->_idx]->as_CountedLoop();
+ post_head->set_normal_loop();
+ post_head->set_post_loop(main_head);
+
+ // Reduce the post-loop trip count.
+ CountedLoopEndNode* post_end = old_new[main_end->_idx]->as_CountedLoopEnd();
+ post_end->_prob = PROB_FAIR;
+
+ // Build the main-loop normal exit.
+ IfFalseNode *new_main_exit = new IfFalseNode(main_end);
+ _igvn.register_new_node_with_optimizer(new_main_exit);
+ set_idom(new_main_exit, main_end, dd_main_exit);
+ set_loop(new_main_exit, loop->_parent);
+
+ // Step A2: Build a zero-trip guard for the vector post-loop. After leaving the
+ // main-loop, the vector post-loop may not execute at all. We 'opaque' the incr
+ // (the vectorized main-loop trip-counter exit value) because we will be changing
+ // the exit value (via additional unrolling) so we cannot constant-fold away the zero
+ // trip guard until all unrolling is done.
+ Node *zer_opaq = new Opaque1Node(C, incr);
+ Node *zer_cmp = new CmpINode(zer_opaq, limit);
+ Node *zer_bol = new BoolNode(zer_cmp, b_test);
+ register_new_node(zer_opaq, new_main_exit);
+ register_new_node(zer_cmp, new_main_exit);
+ register_new_node(zer_bol, new_main_exit);
+
+ // Build the IfNode
+ IfNode *zer_iff = new IfNode(new_main_exit, zer_bol, PROB_FAIR, COUNT_UNKNOWN);
+ _igvn.register_new_node_with_optimizer(zer_iff);
+ set_idom(zer_iff, new_main_exit, dd_main_exit);
+ set_loop(zer_iff, loop->_parent);
+
+ // Plug in the false-path, taken if we need to skip vector post-loop
+ _igvn.replace_input_of(main_exit, 0, zer_iff);
+ set_idom(main_exit, zer_iff, dd_main_exit);
+ set_idom(main_exit->unique_out(), zer_iff, dd_main_exit);
+ // Make the true-path, must enter the vector post loop
+ Node *zer_taken = new IfTrueNode(zer_iff);
+ _igvn.register_new_node_with_optimizer(zer_taken);
+ set_idom(zer_taken, zer_iff, dd_main_exit);
+ set_loop(zer_taken, loop->_parent);
+ // Plug in the true path
+ _igvn.hash_delete(post_head);
+ post_head->set_req(LoopNode::EntryControl, zer_taken);
+ set_idom(post_head, zer_taken, dd_main_exit);
+
+ Arena *a = Thread::current()->resource_area();
+ VectorSet visited(a);
+ Node_Stack clones(a, main_head->back_control()->outcnt());
+ // Step A3: Make the fall-in values to the vector post-loop come from the
+ // fall-out values of the main-loop.
+ for (DUIterator_Fast imax, i = main_head->fast_outs(imax); i < imax; i++) {
+ Node* main_phi = main_head->fast_out(i);
+ if (main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() >0) {
+ Node *cur_phi = old_new[main_phi->_idx];
+ Node *fallnew = clone_up_backedge_goo(main_head->back_control(),
+ post_head->init_control(),
+ main_phi->in(LoopNode::LoopBackControl),
+ visited, clones);
+ _igvn.hash_delete(cur_phi);
+ cur_phi->set_req(LoopNode::EntryControl, fallnew);
+ }
+ }
+
+ // CastII for the new post loop:
+ bool inserted = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head);
+ assert(inserted, "no castII inserted");
+
+ // It's difficult to be precise about the trip-counts
+ // for post loops. They are usually very short,
+ // so guess that unit vector trips is a reasonable value.
+ post_head->set_profile_trip_cnt((float)slp_max_unroll_factor);
+
+ // Now force out all loop-invariant dominating tests. The optimizer
+ // finds some, but we _know_ they are all useless.
+ peeled_dom_test_elim(loop, old_new);
+ loop->record_for_igvn();
+}
+
//------------------------------is_invariant-----------------------------
// Return true if n is invariant
bool IdealLoopTree::is_invariant(Node* n) const {
@@ -2608,6 +2749,9 @@
// and we'd rather unroll the post-RCE'd loop SO... do not unroll if
// peeling.
if (should_unroll && !should_peel) {
+ if (SuperWordLoopUnrollAnalysis) {
+ phase->insert_vector_post_loop(this, old_new);
+ }
phase->do_unroll(this, old_new, true);
}
--- a/hotspot/src/share/vm/opto/loopnode.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/loopnode.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -67,7 +67,9 @@
HasReductions=128,
WasSlpAnalyzed=256,
PassedSlpAnalysis=512,
- DoUnrollOnly=1024 };
+ DoUnrollOnly=1024,
+ VectorizedLoop=2048,
+ HasAtomicPostLoop=4096 };
char _unswitch_count;
enum { _unswitch_max=3 };
@@ -86,6 +88,8 @@
void mark_was_slp() { _loop_flags |= WasSlpAnalyzed; }
void mark_passed_slp() { _loop_flags |= PassedSlpAnalysis; }
void mark_do_unroll_only() { _loop_flags |= DoUnrollOnly; }
+ void mark_loop_vectorized() { _loop_flags |= VectorizedLoop; }
+ void mark_has_atomic_post_loop() { _loop_flags |= HasAtomicPostLoop; }
int unswitch_max() { return _unswitch_max; }
int unswitch_count() { return _unswitch_count; }
@@ -221,6 +225,8 @@
int has_passed_slp () const { return (_loop_flags&PassedSlpAnalysis) == PassedSlpAnalysis; }
int do_unroll_only () const { return (_loop_flags&DoUnrollOnly) == DoUnrollOnly; }
int is_main_no_pre_loop() const { return _loop_flags & MainHasNoPreLoop; }
+ int is_vectorized_loop () const { return (_loop_flags & VectorizedLoop) == VectorizedLoop; }
+ int has_atomic_post_loop () const { return (_loop_flags & HasAtomicPostLoop) == HasAtomicPostLoop; }
void set_main_no_pre_loop() { _loop_flags |= MainHasNoPreLoop; }
int main_idx() const { return _main_idx; }
@@ -893,6 +899,8 @@
// Add pre and post loops around the given loop. These loops are used
// during RCE, unrolling and aligning loops.
void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only );
+ // Add a vector post loop between a vector main loop and the current post loop
+ void insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new);
// If Node n lives in the back_ctrl block, we clone a private version of n
// in preheader_ctrl block and return that, otherwise return n.
Node *clone_up_backedge_goo( Node *back_ctrl, Node *preheader_ctrl, Node *n, VectorSet &visited, Node_Stack &clones );
@@ -1105,6 +1113,8 @@
Node *place_near_use( Node *useblock ) const;
Node* try_move_store_before_loop(Node* n, Node *n_ctrl);
void try_move_store_after_loop(Node* n);
+ bool identical_backtoback_ifs(Node *n);
+ bool can_split_if(Node *n_ctrl);
bool _created_loop_node;
public:
--- a/hotspot/src/share/vm/opto/loopopts.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/loopopts.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -47,6 +47,14 @@
return NULL;
}
+ // Splitting range check CastIIs through a loop induction Phi can
+ // cause new Phis to be created that are left unrelated to the loop
+ // induction Phi and prevent optimizations (vectorization)
+ if (n->Opcode() == Op_CastII && n->as_CastII()->has_range_check() &&
+ region->is_CountedLoop() && n->in(1) == region->as_CountedLoop()->phi()) {
+ return NULL;
+ }
+
int wins = 0;
assert(!n->is_CFG(), "");
assert(region->is_Region(), "");
@@ -1020,108 +1028,193 @@
}
+bool PhaseIdealLoop::identical_backtoback_ifs(Node *n) {
+ if (!n->is_If()) {
+ return false;
+ }
+ if (!n->in(0)->is_Region()) {
+ return false;
+ }
+ Node* region = n->in(0);
+ Node* dom = idom(region);
+ if (!dom->is_If() || dom->in(1) != n->in(1)) {
+ return false;
+ }
+ IfNode* dom_if = dom->as_If();
+ Node* proj_true = dom_if->proj_out(1);
+ Node* proj_false = dom_if->proj_out(0);
+
+ for (uint i = 1; i < region->req(); i++) {
+ if (is_dominator(proj_true, region->in(i))) {
+ continue;
+ }
+ if (is_dominator(proj_false, region->in(i))) {
+ continue;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool PhaseIdealLoop::can_split_if(Node *n_ctrl) {
+ if (C->live_nodes() > 35000) {
+ return false; // Method too big
+ }
+
+ // Do not do 'split-if' if irreducible loops are present.
+ if (_has_irreducible_loops) {
+ return false;
+ }
+
+ if (merge_point_too_heavy(C, n_ctrl)) {
+ return false;
+ }
+
+ // Do not do 'split-if' if some paths are dead. First do dead code
+ // elimination and then see if its still profitable.
+ for (uint i = 1; i < n_ctrl->req(); i++) {
+ if (n_ctrl->in(i) == C->top()) {
+ return false;
+ }
+ }
+
+ // If trying to do a 'Split-If' at the loop head, it is only
+ // profitable if the cmp folds up on BOTH paths. Otherwise we
+ // risk peeling a loop forever.
+
+ // CNC - Disabled for now. Requires careful handling of loop
+ // body selection for the cloned code. Also, make sure we check
+ // for any input path not being in the same loop as n_ctrl. For
+ // irreducible loops we cannot check for 'n_ctrl->is_Loop()'
+ // because the alternative loop entry points won't be converted
+ // into LoopNodes.
+ IdealLoopTree *n_loop = get_loop(n_ctrl);
+ for (uint j = 1; j < n_ctrl->req(); j++) {
+ if (get_loop(n_ctrl->in(j)) != n_loop) {
+ return false;
+ }
+ }
+
+ // Check for safety of the merge point.
+ if (!merge_point_safe(n_ctrl)) {
+ return false;
+ }
+
+ return true;
+}
+
//------------------------------split_if_with_blocks_post----------------------
// Do the real work in a non-recursive function. CFG hackery wants to be
// in the post-order, so it can dirty the I-DOM info and not use the dirtied
// info.
-void PhaseIdealLoop::split_if_with_blocks_post( Node *n ) {
+void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
// Cloning Cmp through Phi's involves the split-if transform.
// FastLock is not used by an If
- if( n->is_Cmp() && !n->is_FastLock() ) {
- if( C->live_nodes() > 35000 ) return; // Method too big
-
- // Do not do 'split-if' if irreducible loops are present.
- if( _has_irreducible_loops )
- return;
-
+ if (n->is_Cmp() && !n->is_FastLock()) {
Node *n_ctrl = get_ctrl(n);
// Determine if the Node has inputs from some local Phi.
// Returns the block to clone thru.
- Node *n_blk = has_local_phi_input( n );
- if( n_blk != n_ctrl ) return;
+ Node *n_blk = has_local_phi_input(n);
+ if (n_blk != n_ctrl) {
+ return;
+ }
- if( merge_point_too_heavy(C, n_ctrl) )
+ if (!can_split_if(n_ctrl)) {
return;
+ }
- if( n->outcnt() != 1 ) return; // Multiple bool's from 1 compare?
+ if (n->outcnt() != 1) {
+ return; // Multiple bool's from 1 compare?
+ }
Node *bol = n->unique_out();
- assert( bol->is_Bool(), "expect a bool here" );
- if( bol->outcnt() != 1 ) return;// Multiple branches from 1 compare?
+ assert(bol->is_Bool(), "expect a bool here");
+ if (bol->outcnt() != 1) {
+ return;// Multiple branches from 1 compare?
+ }
Node *iff = bol->unique_out();
// Check some safety conditions
- if( iff->is_If() ) { // Classic split-if?
- if( iff->in(0) != n_ctrl ) return; // Compare must be in same blk as if
+ if (iff->is_If()) { // Classic split-if?
+ if (iff->in(0) != n_ctrl) {
+ return; // Compare must be in same blk as if
+ }
} else if (iff->is_CMove()) { // Trying to split-up a CMOVE
// Can't split CMove with different control edge.
- if (iff->in(0) != NULL && iff->in(0) != n_ctrl ) return;
- if( get_ctrl(iff->in(2)) == n_ctrl ||
- get_ctrl(iff->in(3)) == n_ctrl )
+ if (iff->in(0) != NULL && iff->in(0) != n_ctrl ) {
+ return;
+ }
+ if (get_ctrl(iff->in(2)) == n_ctrl ||
+ get_ctrl(iff->in(3)) == n_ctrl) {
return; // Inputs not yet split-up
- if ( get_loop(n_ctrl) != get_loop(get_ctrl(iff)) ) {
+ }
+ if (get_loop(n_ctrl) != get_loop(get_ctrl(iff))) {
return; // Loop-invar test gates loop-varying CMOVE
}
} else {
return; // some other kind of node, such as an Allocate
}
- // Do not do 'split-if' if some paths are dead. First do dead code
- // elimination and then see if its still profitable.
- for( uint i = 1; i < n_ctrl->req(); i++ )
- if( n_ctrl->in(i) == C->top() )
- return;
-
// When is split-if profitable? Every 'win' on means some control flow
// goes dead, so it's almost always a win.
int policy = 0;
- // If trying to do a 'Split-If' at the loop head, it is only
- // profitable if the cmp folds up on BOTH paths. Otherwise we
- // risk peeling a loop forever.
-
- // CNC - Disabled for now. Requires careful handling of loop
- // body selection for the cloned code. Also, make sure we check
- // for any input path not being in the same loop as n_ctrl. For
- // irreducible loops we cannot check for 'n_ctrl->is_Loop()'
- // because the alternative loop entry points won't be converted
- // into LoopNodes.
- IdealLoopTree *n_loop = get_loop(n_ctrl);
- for( uint j = 1; j < n_ctrl->req(); j++ )
- if( get_loop(n_ctrl->in(j)) != n_loop )
- return;
-
- // Check for safety of the merge point.
- if( !merge_point_safe(n_ctrl) ) {
+ // Split compare 'n' through the merge point if it is profitable
+ Node *phi = split_thru_phi( n, n_ctrl, policy);
+ if (!phi) {
return;
}
- // Split compare 'n' through the merge point if it is profitable
- Node *phi = split_thru_phi( n, n_ctrl, policy );
- if( !phi ) return;
-
// Found a Phi to split thru!
// Replace 'n' with the new phi
- _igvn.replace_node( n, phi );
+ _igvn.replace_node(n, phi);
// Now split the bool up thru the phi
- Node *bolphi = split_thru_phi( bol, n_ctrl, -1 );
+ Node *bolphi = split_thru_phi(bol, n_ctrl, -1);
guarantee(bolphi != NULL, "null boolean phi node");
- _igvn.replace_node( bol, bolphi );
- assert( iff->in(1) == bolphi, "" );
+ _igvn.replace_node(bol, bolphi);
+ assert(iff->in(1) == bolphi, "");
- if( bolphi->Value(&_igvn)->singleton() )
+ if (bolphi->Value(&_igvn)->singleton()) {
return;
+ }
// Conditional-move? Must split up now
- if( !iff->is_If() ) {
- Node *cmovphi = split_thru_phi( iff, n_ctrl, -1 );
- _igvn.replace_node( iff, cmovphi );
+ if (!iff->is_If()) {
+ Node *cmovphi = split_thru_phi(iff, n_ctrl, -1);
+ _igvn.replace_node(iff, cmovphi);
return;
}
// Now split the IF
- do_split_if( iff );
+ do_split_if(iff);
+ return;
+ }
+
+ // Two identical ifs back to back can be merged
+ if (identical_backtoback_ifs(n) && can_split_if(n->in(0))) {
+ Node *n_ctrl = n->in(0);
+ PhiNode* bolphi = PhiNode::make_blank(n_ctrl, n->in(1));
+ IfNode* dom_if = idom(n_ctrl)->as_If();
+ Node* proj_true = dom_if->proj_out(1);
+ Node* proj_false = dom_if->proj_out(0);
+ Node* con_true = _igvn.makecon(TypeInt::ONE);
+ Node* con_false = _igvn.makecon(TypeInt::ZERO);
+
+ for (uint i = 1; i < n_ctrl->req(); i++) {
+ if (is_dominator(proj_true, n_ctrl->in(i))) {
+ bolphi->init_req(i, con_true);
+ } else {
+ assert(is_dominator(proj_false, n_ctrl->in(i)), "bad if");
+ bolphi->init_req(i, con_false);
+ }
+ }
+ register_new_node(bolphi, n_ctrl);
+ _igvn.replace_input_of(n, 1, bolphi);
+
+ // Now split the IF
+ do_split_if(n);
return;
}
--- a/hotspot/src/share/vm/opto/memnode.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/memnode.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1709,38 +1709,10 @@
// unsafe field access may not have a constant offset
C->has_unsafe_access(),
"Field accesses must be precise" );
- // For oop loads, we expect the _type to be precise
- if (klass == env->String_klass() &&
- adr->is_AddP() && off != Type::OffsetBot) {
- // For constant Strings treat the final fields as compile time constants.
- // While we can list what field types java.lang.String has, it is more
- // future-proof to handle all possible field types, anticipating future
- // changes and experiments in String code.
- Node* base = adr->in(AddPNode::Base);
- const TypeOopPtr* t = phase->type(base)->isa_oopptr();
- if (t != NULL && t->singleton()) {
- ciField* field = env->String_klass()->get_field_by_offset(off, false);
- if (field != NULL && field->is_final()) {
- ciObject* string = t->const_oop();
- ciConstant constant = string->as_instance()->field_value(field);
- // Type::make_from_constant does not handle narrow oops, so handle it here.
- // Everything else can use the factory method.
- if ((constant.basic_type() == T_ARRAY || constant.basic_type() == T_OBJECT)
- && adr->bottom_type()->is_ptr_to_narrowoop()) {
- return TypeNarrowOop::make_from_constant(constant.as_object(), true);
- } else {
- return Type::make_from_constant(constant, true);
- }
- }
- }
- }
+ // For oop loads, we expect the _type to be precise.
// Optimizations for constant objects
ciObject* const_oop = tinst->const_oop();
if (const_oop != NULL) {
- // For constant Boxed value treat the target field as a compile time constant.
- if (tinst->is_ptr_to_boxed_value()) {
- return tinst->get_const_boxed_value();
- } else
// For constant CallSites treat the target field as a compile time constant.
if (const_oop->is_call_site()) {
ciCallSite* call_site = const_oop->as_call_site();
--- a/hotspot/src/share/vm/opto/stringopts.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/stringopts.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1552,8 +1552,7 @@
if (str->is_Con()) {
// Constant source string
- const TypeOopPtr* t = kit.gvn().type(src_array)->isa_oopptr();
- ciTypeArray* src_array_type = t->const_oop()->as_type_array();
+ ciTypeArray* src_array_type = get_constant_value(kit, str);
// Check encoding of constant string
bool src_is_byte = (get_constant_coder(kit, str) == java_lang_String::CODER_LATIN1);
@@ -1673,9 +1672,15 @@
int PhaseStringOpts::get_constant_length(GraphKit& kit, Node* str) {
assert(str->is_Con(), "String must be constant");
- Node* src_array = kit.load_String_value(kit.control(), str);
- const TypeOopPtr* t = kit.gvn().type(src_array)->isa_oopptr();
- return t->const_oop()->as_type_array()->length();
+ return get_constant_value(kit, str)->length();
+}
+
+ciTypeArray* PhaseStringOpts::get_constant_value(GraphKit& kit, Node* str) {
+ assert(str->is_Con(), "String must be constant");
+ const TypeOopPtr* str_type = kit.gvn().type(str)->isa_oopptr();
+ ciInstance* str_instance = str_type->const_oop()->as_instance();
+ ciObject* src_array = str_instance->field_value_by_offset(java_lang_String::value_offset_in_bytes()).as_object();
+ return src_array->as_type_array();
}
void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
--- a/hotspot/src/share/vm/opto/stringopts.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/stringopts.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -97,6 +97,9 @@
// Returns the length of a constant string
int get_constant_length(GraphKit& kit, Node* str);
+ // Returns the value array of a constant string
+ ciTypeArray* get_constant_value(GraphKit& kit, Node* str);
+
// Clean up any leftover nodes
void record_dead_node(Node* node);
void remove_dead_nodes();
--- a/hotspot/src/share/vm/opto/superword.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/opto/superword.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -2253,6 +2253,9 @@
C->set_major_progress();
}
cl->mark_do_unroll_only();
+ if (do_reserve_copy()) {
+ cl->mark_loop_vectorized();
+ }
}
}
}
--- a/hotspot/src/share/vm/prims/methodComparator.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/prims/methodComparator.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -34,9 +34,6 @@
BytecodeStream *MethodComparator::_s_new;
ConstantPool* MethodComparator::_old_cp;
ConstantPool* MethodComparator::_new_cp;
-BciMap *MethodComparator::_bci_map;
-bool MethodComparator::_switchable_test;
-GrowableArray<int> *MethodComparator::_fwd_jmps;
bool MethodComparator::methods_EMCP(Method* old_method, Method* new_method) {
if (old_method->code_size() != new_method->code_size())
@@ -55,7 +52,6 @@
BytecodeStream s_new(new_method);
_s_old = &s_old;
_s_new = &s_new;
- _switchable_test = false;
Bytecodes::Code c_old, c_new;
while ((c_old = s_old.next()) >= 0) {
@@ -68,64 +64,6 @@
return true;
}
-
-bool MethodComparator::methods_switchable(Method* old_method, Method* new_method,
- BciMap &bci_map) {
- if (old_method->code_size() > new_method->code_size())
- // Something has definitely been deleted in the new method, compared to the old one.
- return false;
-
- if (! check_stack_and_locals_size(old_method, new_method))
- return false;
-
- _old_cp = old_method->constants();
- _new_cp = new_method->constants();
- BytecodeStream s_old(old_method);
- BytecodeStream s_new(new_method);
- _s_old = &s_old;
- _s_new = &s_new;
- _bci_map = &bci_map;
- _switchable_test = true;
- GrowableArray<int> fwd_jmps(16);
- _fwd_jmps = &fwd_jmps;
- Bytecodes::Code c_old, c_new;
-
- while ((c_old = s_old.next()) >= 0) {
- if ((c_new = s_new.next()) < 0)
- return false;
- if (! (c_old == c_new && args_same(c_old, c_new))) {
- int old_bci = s_old.bci();
- int new_st_bci = s_new.bci();
- bool found_match = false;
- do {
- c_new = s_new.next();
- if (c_new == c_old && args_same(c_old, c_new)) {
- found_match = true;
- break;
- }
- } while (c_new >= 0);
- if (! found_match)
- return false;
- int new_end_bci = s_new.bci();
- bci_map.store_fragment_location(old_bci, new_st_bci, new_end_bci);
- }
- }
-
- // Now we can test all forward jumps
- for (int i = 0; i < fwd_jmps.length() / 2; i++) {
- if (! bci_map.old_and_new_locations_same(fwd_jmps.at(i*2), fwd_jmps.at(i*2+1))) {
- RC_TRACE(0x00800000,
- ("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d",
- fwd_jmps.at(i*2), bci_map.new_bci_for_old(fwd_jmps.at(i*2)),
- fwd_jmps.at(i*2+1)));
- return false;
- }
- }
-
- return true;
-}
-
-
bool MethodComparator::args_same(Bytecodes::Code c_old, Bytecodes::Code c_new) {
// BytecodeStream returns the correct standard Java bytecodes for various "fast"
// bytecode versions, so we don't have to bother about them here..
@@ -275,22 +213,8 @@
case Bytecodes::_jsr : {
int old_ofs = _s_old->bytecode().get_offset_s2(c_old);
int new_ofs = _s_new->bytecode().get_offset_s2(c_new);
- if (_switchable_test) {
- int old_dest = _s_old->bci() + old_ofs;
- int new_dest = _s_new->bci() + new_ofs;
- if (old_ofs < 0 && new_ofs < 0) {
- if (! _bci_map->old_and_new_locations_same(old_dest, new_dest))
- return false;
- } else if (old_ofs > 0 && new_ofs > 0) {
- _fwd_jmps->append(old_dest);
- _fwd_jmps->append(new_dest);
- } else {
- return false;
- }
- } else {
- if (old_ofs != new_ofs)
- return false;
- }
+ if (old_ofs != new_ofs)
+ return false;
break;
}
@@ -312,73 +236,19 @@
case Bytecodes::_jsr_w : {
int old_ofs = _s_old->bytecode().get_offset_s4(c_old);
int new_ofs = _s_new->bytecode().get_offset_s4(c_new);
- if (_switchable_test) {
- int old_dest = _s_old->bci() + old_ofs;
- int new_dest = _s_new->bci() + new_ofs;
- if (old_ofs < 0 && new_ofs < 0) {
- if (! _bci_map->old_and_new_locations_same(old_dest, new_dest))
- return false;
- } else if (old_ofs > 0 && new_ofs > 0) {
- _fwd_jmps->append(old_dest);
- _fwd_jmps->append(new_dest);
- } else {
- return false;
- }
- } else {
- if (old_ofs != new_ofs)
- return false;
- }
+ if (old_ofs != new_ofs)
+ return false;
break;
}
case Bytecodes::_lookupswitch : // fall through
case Bytecodes::_tableswitch : {
- if (_switchable_test) {
- address aligned_bcp_old = (address) round_to((intptr_t)_s_old->bcp() + 1, jintSize);
- address aligned_bcp_new = (address) round_to((intptr_t)_s_new->bcp() + 1, jintSize);
- int default_old = (int) Bytes::get_Java_u4(aligned_bcp_old);
- int default_new = (int) Bytes::get_Java_u4(aligned_bcp_new);
- _fwd_jmps->append(_s_old->bci() + default_old);
- _fwd_jmps->append(_s_new->bci() + default_new);
- if (c_old == Bytecodes::_lookupswitch) {
- int npairs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + jintSize);
- int npairs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + jintSize);
- if (npairs_old != npairs_new)
- return false;
- for (int i = 0; i < npairs_old; i++) {
- int match_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (2+2*i)*jintSize);
- int match_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (2+2*i)*jintSize);
- if (match_old != match_new)
- return false;
- int ofs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (2+2*i+1)*jintSize);
- int ofs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (2+2*i+1)*jintSize);
- _fwd_jmps->append(_s_old->bci() + ofs_old);
- _fwd_jmps->append(_s_new->bci() + ofs_new);
- }
- } else if (c_old == Bytecodes::_tableswitch) {
- int lo_old = (int) Bytes::get_Java_u4(aligned_bcp_old + jintSize);
- int lo_new = (int) Bytes::get_Java_u4(aligned_bcp_new + jintSize);
- if (lo_old != lo_new)
- return false;
- int hi_old = (int) Bytes::get_Java_u4(aligned_bcp_old + 2*jintSize);
- int hi_new = (int) Bytes::get_Java_u4(aligned_bcp_new + 2*jintSize);
- if (hi_old != hi_new)
- return false;
- for (int i = 0; i < hi_old - lo_old + 1; i++) {
- int ofs_old = (int) Bytes::get_Java_u4(aligned_bcp_old + (3+i)*jintSize);
- int ofs_new = (int) Bytes::get_Java_u4(aligned_bcp_new + (3+i)*jintSize);
- _fwd_jmps->append(_s_old->bci() + ofs_old);
- _fwd_jmps->append(_s_new->bci() + ofs_new);
- }
- }
- } else { // !_switchable_test, can use fast rough compare
- int len_old = _s_old->instruction_size();
- int len_new = _s_new->instruction_size();
- if (len_old != len_new)
- return false;
- if (memcmp(_s_old->bcp(), _s_new->bcp(), len_old) != 0)
- return false;
- }
+ int len_old = _s_old->instruction_size();
+ int len_new = _s_new->instruction_size();
+ if (len_old != len_new)
+ return false;
+ if (memcmp(_s_old->bcp(), _s_new->bcp(), len_old) != 0)
+ return false;
break;
}
}
--- a/hotspot/src/share/vm/prims/methodComparator.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/prims/methodComparator.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -29,8 +29,6 @@
#include "oops/constantPool.hpp"
#include "oops/method.hpp"
-class BciMap;
-
// methodComparator provides an interface for determining if methods of
// different versions of classes are equivalent or switchable
@@ -39,9 +37,6 @@
static BytecodeStream *_s_old, *_s_new;
static ConstantPool* _old_cp;
static ConstantPool* _new_cp;
- static BciMap *_bci_map;
- static bool _switchable_test;
- static GrowableArray<int> *_fwd_jmps;
static bool args_same(Bytecodes::Code c_old, Bytecodes::Code c_new);
static bool pool_constants_same(int cpi_old, int cpi_new);
@@ -55,79 +50,6 @@
// these indices eventually point to the same constants for both method versions.
static bool methods_EMCP(Method* old_method, Method* new_method);
- static bool methods_switchable(Method* old_method, Method* new_method, BciMap &bci_map);
-};
-
-
-// ByteCode Index Map. For two versions of the same method, where the new version may contain
-// fragments not found in the old version, provides a mapping from an index of a bytecode in
-// the old method to the index of the same bytecode in the new method.
-
-class BciMap {
- private:
- int *_old_bci, *_new_st_bci, *_new_end_bci;
- int _cur_size, _cur_pos;
- int _pos;
-
- public:
- BciMap() {
- _cur_size = 50;
- _old_bci = (int*) malloc(sizeof(int) * _cur_size);
- _new_st_bci = (int*) malloc(sizeof(int) * _cur_size);
- _new_end_bci = (int*) malloc(sizeof(int) * _cur_size);
- _cur_pos = 0;
- }
-
- ~BciMap() {
- free(_old_bci);
- free(_new_st_bci);
- free(_new_end_bci);
- }
-
- // Store the position of an added fragment, e.g.
- //
- // |<- old_bci
- // -----------------------------------------
- // Old method |invokevirtual 5|aload 1|...
- // -----------------------------------------
- //
- // |<- new_st_bci |<- new_end_bci
- // --------------------------------------------------------------------
- // New method |invokevirual 5|aload 2|invokevirtual 6|aload 1|...
- // --------------------------------------------------------------------
- // ^^^^^^^^^^^^^^^^^^^^^^^^
- // Added fragment
-
- void store_fragment_location(int old_bci, int new_st_bci, int new_end_bci) {
- if (_cur_pos == _cur_size) {
- _cur_size += 10;
- _old_bci = (int*) realloc(_old_bci, sizeof(int) * _cur_size);
- _new_st_bci = (int*) realloc(_new_st_bci, sizeof(int) * _cur_size);
- _new_end_bci = (int*) realloc(_new_end_bci, sizeof(int) * _cur_size);
- }
- _old_bci[_cur_pos] = old_bci;
- _new_st_bci[_cur_pos] = new_st_bci;
- _new_end_bci[_cur_pos] = new_end_bci;
- _cur_pos++;
- }
-
- int new_bci_for_old(int old_bci) {
- if (_cur_pos == 0 || old_bci < _old_bci[0]) return old_bci;
- _pos = 1;
- while (_pos < _cur_pos && old_bci >= _old_bci[_pos])
- _pos++;
- return _new_end_bci[_pos-1] + (old_bci - _old_bci[_pos-1]);
- }
-
- // Test if two indexes - one in the old method and another in the new one - correspond
- // to the same bytecode
- bool old_and_new_locations_same(int old_dest_bci, int new_dest_bci) {
- if (new_bci_for_old(old_dest_bci) == new_dest_bci)
- return true;
- else if (_old_bci[_pos-1] == old_dest_bci)
- return (new_dest_bci == _new_st_bci[_pos-1]);
- else return false;
- }
};
#endif // SHARE_VM_PRIMS_METHODCOMPARATOR_HPP
--- a/hotspot/src/share/vm/prims/methodHandles.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -63,30 +63,21 @@
bool MethodHandles::_enabled = false; // set true after successful native linkage
MethodHandlesAdapterBlob* MethodHandles::_adapter_code = NULL;
-
/**
* Generates method handle adapters. Returns 'false' if memory allocation
* failed and true otherwise.
*/
-bool MethodHandles::generate_adapters() {
- if (SystemDictionary::MethodHandle_klass() == NULL) {
- return true;
- }
-
+void MethodHandles::generate_adapters() {
+ assert(SystemDictionary::MethodHandle_klass() != NULL, "should be present");
assert(_adapter_code == NULL, "generate only once");
ResourceMark rm;
TraceTime timer("MethodHandles adapters generation", TraceStartupTime);
_adapter_code = MethodHandlesAdapterBlob::create(adapter_code_size);
- if (_adapter_code == NULL) {
- return false;
- }
-
CodeBuffer code(_adapter_code);
MethodHandlesAdapterGenerator g(&code);
g.generate();
code.log_section_sizes("MethodHandlesAdapterBlob");
- return true;
}
//------------------------------------------------------------------------------
@@ -1436,53 +1427,31 @@
};
/**
- * Helper method to register native methods.
- */
-static bool register_natives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods, jint nMethods) {
- int status = env->RegisterNatives(clazz, methods, nMethods);
- if (status != JNI_OK || env->ExceptionOccurred()) {
- warning("JSR 292 method handle code is mismatched to this JVM. Disabling support.");
- env->ExceptionClear();
- return false;
- }
- return true;
-}
-
-/**
* This one function is exported, used by NativeLookup.
*/
JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
assert(!MethodHandles::enabled(), "must not be enabled");
- bool enable_MH = true;
+ assert(SystemDictionary::MethodHandle_klass() != NULL, "should be present");
- jclass MH_class = NULL;
- if (SystemDictionary::MethodHandle_klass() == NULL) {
- enable_MH = false;
- } else {
- oop mirror = SystemDictionary::MethodHandle_klass()->java_mirror();
- MH_class = (jclass) JNIHandles::make_local(env, mirror);
- }
+ oop mirror = SystemDictionary::MethodHandle_klass()->java_mirror();
+ jclass MH_class = (jclass) JNIHandles::make_local(env, mirror);
- if (enable_MH) {
+ {
ThreadToNativeFromVM ttnfv(thread);
- if (enable_MH) {
- enable_MH = register_natives(env, MHN_class, MHN_methods, sizeof(MHN_methods)/sizeof(JNINativeMethod));
- }
- if (enable_MH) {
- enable_MH = register_natives(env, MH_class, MH_methods, sizeof(MH_methods)/sizeof(JNINativeMethod));
- }
+ int status = env->RegisterNatives(MHN_class, MHN_methods, sizeof(MHN_methods)/sizeof(JNINativeMethod));
+ guarantee(status == JNI_OK && !env->ExceptionOccurred(),
+ "register java.lang.invoke.MethodHandleNative natives");
+
+ status = env->RegisterNatives(MH_class, MH_methods, sizeof(MH_methods)/sizeof(JNINativeMethod));
+ guarantee(status == JNI_OK && !env->ExceptionOccurred(),
+ "register java.lang.invoke.MethodHandle natives");
}
if (TraceInvokeDynamic) {
tty->print_cr("MethodHandle support loaded (using LambdaForms)");
}
- if (enable_MH) {
- if (MethodHandles::generate_adapters() == false) {
- THROW_MSG(vmSymbols::java_lang_VirtualMachineError(), "Out of space in CodeCache for method handle adapters");
- }
- MethodHandles::set_enabled(true);
- }
+ MethodHandles::set_enabled(true);
}
JVM_END
--- a/hotspot/src/share/vm/prims/methodHandles.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -81,7 +81,7 @@
static void flush_dependent_nmethods(Handle call_site, Handle target);
// Generate MethodHandles adapters.
- static bool generate_adapters();
+ static void generate_adapters();
// Called from MethodHandlesAdapterGenerator.
static address generate_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid);
--- a/hotspot/src/share/vm/prims/unsafe.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/prims/unsafe.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -660,6 +660,36 @@
Copy::conjoint_memory_atomic(src, dst, sz);
UNSAFE_END
+// This function is a leaf since if the source and destination are both in native memory
+// the copy may potentially be very large, and we don't want to disable GC if we can avoid it.
+// If either source or destination (or both) are on the heap, the function will enter VM using
+// JVM_ENTRY_FROM_LEAF
+JVM_LEAF(void, Unsafe_CopySwapMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size, jlong elemSize)) {
+ UnsafeWrapper("Unsafe_CopySwapMemory0");
+
+ size_t sz = (size_t)size;
+ size_t esz = (size_t)elemSize;
+
+ if (srcObj == NULL && dstObj == NULL) {
+ // Both src & dst are in native memory
+ address src = (address)srcOffset;
+ address dst = (address)dstOffset;
+
+ Copy::conjoint_swap(src, dst, sz, esz);
+ } else {
+ // At least one of src/dst are on heap, transition to VM to access raw pointers
+
+ JVM_ENTRY_FROM_LEAF(env, void, Unsafe_CopySwapMemory0) {
+ oop srcp = JNIHandles::resolve(srcObj);
+ oop dstp = JNIHandles::resolve(dstObj);
+
+ address src = (address)index_oop_from_field_offset_long(srcp, srcOffset);
+ address dst = (address)index_oop_from_field_offset_long(dstp, dstOffset);
+
+ Copy::conjoint_swap(src, dst, sz, esz);
+ } JVM_END
+ }
+} JVM_END
////// Random queries
@@ -1363,6 +1393,7 @@
{CC "getLoadAverage", CC "([DI)I", FN_PTR(Unsafe_Loadavg)},
{CC "copyMemory", CC "(" OBJ "J" OBJ "JJ)V", FN_PTR(Unsafe_CopyMemory)},
+ {CC "copySwapMemory0", CC "(" OBJ "J" OBJ "JJJ)V", FN_PTR(Unsafe_CopySwapMemory0)},
{CC "setMemory", CC "(" OBJ "JJB)V", FN_PTR(Unsafe_SetMemory)},
{CC "defineAnonymousClass", CC "(" DAC_Args ")" CLS, FN_PTR(Unsafe_DefineAnonymousClass)},
--- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -333,6 +333,8 @@
// --- Non-alias flags - sorted by obsolete_in then expired_in:
{ "MaxGCMinorPauseMillis", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() },
{ "UseParNewGC", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::jdk(10) },
+ { "ConvertSleepToYield", JDK_Version::jdk(9), JDK_Version::jdk(10), JDK_Version::jdk(11) },
+ { "ConvertYieldToSleep", JDK_Version::jdk(9), JDK_Version::jdk(10), JDK_Version::jdk(11) },
// --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in:
{ "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() },
--- a/hotspot/src/share/vm/runtime/globals.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1239,9 +1239,8 @@
product_pd(bool, DontYieldALot, \
"Throw away obvious excess yield calls") \
\
- product_pd(bool, ConvertSleepToYield, \
- "Convert sleep(0) to thread yield " \
- "(may be off for Solaris to improve GUI)") \
+ product(bool, ConvertSleepToYield, true, \
+ "Convert sleep(0) to thread yield ") \
\
product(bool, ConvertYieldToSleep, false, \
"Convert yield to a sleep of MinSleepInterval to simulate Win32 " \
@@ -1279,10 +1278,6 @@
experimental(intx, hashCode, 5, \
"(Unstable) select hashCode generation algorithm") \
\
- experimental(intx, WorkAroundNPTLTimedWaitHang, 0, \
- "(Unstable, Linux-specific) " \
- "avoid NPTL-FUTEX hang pthread_cond_timedwait") \
- \
product(bool, FilterSpuriousWakeups, true, \
"When true prevents OS-level spurious, or premature, wakeups " \
"from Object.wait (Ignored for Windows)") \
@@ -2012,11 +2007,15 @@
range(min_intx, 100) \
\
product(uintx, InitiatingHeapOccupancyPercent, 45, \
- "Percentage of the (entire) heap occupancy to start a " \
- "concurrent GC cycle. It is used by GCs that trigger a " \
- "concurrent GC cycle based on the occupancy of the entire heap, " \
- "not just one of the generations (e.g., G1). A value of 0 " \
- "denotes 'do constant GC cycles'.") \
+ "The percent occupancy (IHOP) of the current old generation " \
+ "capacity above which a concurrent mark cycle will be initiated " \
+ "Its value may change over time if adaptive IHOP is enabled, " \
+ "otherwise the value remains constant. " \
+ "In the latter case a value of 0 will result as frequent as " \
+ "possible concurrent marking cycles. A value of 100 disables " \
+ "concurrent marking. " \
+ "Fragmentation waste in the old generation is not considered " \
+ "free space in this calculation. (G1 collector only)") \
range(0, 100) \
\
manageable(intx, CMSTriggerInterval, -1, \
--- a/hotspot/src/share/vm/runtime/init.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/init.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -145,6 +145,7 @@
}
javaClasses_init(); // must happen after vtable initialization
stubRoutines_init2(); // note: StubRoutines need 2-phase init
+ MethodHandles::generate_adapters();
CodeCacheExtensions::complete_step(CodeCacheExtensionsSteps::StubRoutines2);
#if INCLUDE_NMT
@@ -181,8 +182,7 @@
}
}
-
-static bool _init_completed = false;
+static volatile bool _init_completed = false;
bool is_init_completed() {
return _init_completed;
--- a/hotspot/src/share/vm/runtime/interfaceSupport.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/interfaceSupport.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -417,6 +417,14 @@
os::verify_stack_alignment(); \
/* begin of body */
+#define VM_ENTRY_BASE_FROM_LEAF(result_type, header, thread) \
+ TRACE_CALL(result_type, header) \
+ debug_only(ResetNoHandleMark __rnhm;) \
+ HandleMarkCleaner __hm(thread); \
+ Thread* THREAD = thread; \
+ os::verify_stack_alignment(); \
+ /* begin of body */
+
// ENTRY routines may lock, GC and throw exceptions
@@ -584,6 +592,14 @@
VM_LEAF_BASE(result_type, header)
+#define JVM_ENTRY_FROM_LEAF(env, result_type, header) \
+ { { \
+ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
+ ThreadInVMfromNative __tiv(thread); \
+ debug_only(VMNativeEntryWrapper __vew;) \
+ VM_ENTRY_BASE_FROM_LEAF(result_type, header, thread)
+
+
#define JVM_END } }
#endif // SHARE_VM_RUNTIME_INTERFACESUPPORT_HPP
--- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -359,6 +359,11 @@
static address clean_opt_virtual_call_entry();
static address clean_static_call_entry();
+#if defined(X86) && defined(COMPILER1)
+ // For Object.hashCode, System.identityHashCode try to pull hashCode from object header if available.
+ static void inline_check_hashcode_from_object_header(MacroAssembler* masm, methodHandle method, Register obj_reg, Register result);
+#endif // X86 && COMPILER1
+
public:
// Read the array of BasicTypes from a Java signature, and compute where
--- a/hotspot/src/share/vm/runtime/stubCodeGenerator.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/stubCodeGenerator.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -37,7 +37,7 @@
StubCodeDesc* StubCodeDesc::_list = NULL;
int StubCodeDesc::_count = 0;
-
+bool StubCodeDesc::_frozen = false;
StubCodeDesc* StubCodeDesc::desc_for(address pc) {
StubCodeDesc* p = _list;
@@ -46,20 +46,23 @@
return p;
}
-
StubCodeDesc* StubCodeDesc::desc_for_index(int index) {
StubCodeDesc* p = _list;
while (p != NULL && p->index() != index) p = p->_next;
return p;
}
-
const char* StubCodeDesc::name_for(address pc) {
StubCodeDesc* p = desc_for(pc);
return p == NULL ? NULL : p->name();
}
+void StubCodeDesc::freeze() {
+ assert(!_frozen, "repeated freeze operation");
+ _frozen = true;
+}
+
void StubCodeDesc::print_on(outputStream* st) const {
st->print("%s", group());
st->print("::");
@@ -110,12 +113,10 @@
}
}
-
void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) {
// default implementation - do nothing
}
-
void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) {
// default implementation - record the cdesc
if (_first_stub == NULL) _first_stub = cdesc;
--- a/hotspot/src/share/vm/runtime/stubCodeGenerator.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/stubCodeGenerator.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -28,7 +28,7 @@
#include "asm/assembler.hpp"
#include "memory/allocation.hpp"
-// All the basic framework for stubcode generation/debugging/printing.
+// All the basic framework for stub code generation/debugging/printing.
// A StubCodeDesc describes a piece of generated code (usually stubs).
@@ -37,9 +37,10 @@
// this may have to change if searching becomes too slow.
class StubCodeDesc: public CHeapObj<mtCode> {
- protected:
+ private:
static StubCodeDesc* _list; // the list of all descriptors
static int _count; // length of list
+ static bool _frozen; // determines whether _list modifications are allowed
StubCodeDesc* _next; // the next element in the linked list
const char* _group; // the group to which the stub code belongs
@@ -68,6 +69,7 @@
static const char* name_for(address pc); // returns the name of the code containing pc or NULL
StubCodeDesc(const char* group, const char* name, address begin, address end = NULL) {
+ assert(!_frozen, "no modifications allowed");
assert(name != NULL, "no name specified");
_next = _list;
_group = group;
@@ -78,6 +80,8 @@
_list = this;
};
+ static void freeze();
+
const char* group() const { return _group; }
const char* name() const { return _name; }
int index() const { return _index; }
@@ -117,7 +121,7 @@
// later via an address pointing into it.
class StubCodeMark: public StackObj {
- protected:
+ private:
StubCodeGenerator* _cgen;
StubCodeDesc* _cdesc;
--- a/hotspot/src/share/vm/runtime/thread.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/runtime/thread.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -3600,6 +3600,9 @@
vm_exit_during_initialization("Failed to initialize tracing backend");
}
+ // No more stub generation allowed after that point.
+ StubCodeDesc::freeze();
+
// Set flag that basic initialization has completed. Used by exceptions and various
// debug stuff, that does not work until all basic classes have been initialized.
set_init_completed();
--- a/hotspot/src/share/vm/trace/trace.xml Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/trace/trace.xml Wed Jul 05 21:23:38 2017 +0200
@@ -283,6 +283,7 @@
<value type="BYTES64" field="edenUsedSize" label="Eden Used Size" />
<value type="BYTES64" field="edenTotalSize" label="Eden Total Size" />
<value type="BYTES64" field="survivorUsedSize" label="Survivor Used Size" />
+ <value type="UINT" field="numberOfRegions" label="Number of Regions" />
</event>
<event id="GCGarbageCollection" path="vm/gc/collector/garbage_collection" label="Garbage Collection"
@@ -478,6 +479,23 @@
<value type="BYTES64" field="size" label="Allocation Size" />
</event>
+ <event id="TenuringDistribution" path="vm/gc/detailed/tenuring_distribution" label="Tenuring Distribution"
+ is_instant="true">
+ <value type="UINT" field="gcId" label="GC ID" relation="GC_ID"/>
+ <value type="UINT" field="age" label="Age" />
+ <value type="BYTES64" field="size" label="Size" />
+ </event>
+
+ <event id="G1HeapRegionTypeChange" path="vm/gc/detailed/g1_heap_region_type_change" label="G1 Heap Region Type Change"
+ description="Information about a G1 heap region type change." is_instant="true">
+ <value type="UINT" field="index" label="Index" />
+ <value type="G1HEAPREGIONTYPE" field="from" label="From Type" />
+ <value type="G1HEAPREGIONTYPE" field="to" label="To Type" />
+ <value type="ADDRESS" field="start" label="Start" />
+ <value type="BYTES64" field="used" label="Used" />
+ <value type="UINT" field="allocContext" label="Allocation Context" />
+ </event>
+
<!-- Compiler events -->
<event id="Compilation" path="vm/compiler/compilation" label="Compilation"
--- a/hotspot/src/share/vm/trace/tracetypes.xml Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/trace/tracetypes.xml Wed Jul 05 21:23:38 2017 +0200
@@ -126,6 +126,11 @@
<value type="UTF8" field="when" label="when" />
</content_type>
+ <content_type id="G1HeapRegionType" hr_name="G1 Heap Region Type"
+ type="U1" jvm_type="G1HEAPREGIONTYPE">
+ <value type="UTF8" field="type" label="type" />
+ </content_type>
+
<content_type id="G1YCType" hr_name="G1 YC Type"
type="U1" jvm_type="G1YCTYPE">
<value type="UTF8" field="type" label="type" />
@@ -345,6 +350,10 @@
<primary_type symbol="GCWHEN" datatype="U1" contenttype="GCWHEN"
type="u1" sizeop="sizeof(u1)" />
+ <!-- G1HEAPREGIONTYPE -->
+ <primary_type symbol="G1HEAPREGIONTYPE" datatype="U1" contenttype="G1HEAPREGIONTYPE"
+ type="u1" sizeop="sizeof(u1)" />
+
<!-- G1YCType -->
<primary_type symbol="G1YCTYPE" datatype="U1" contenttype="G1YCTYPE"
type="u1" sizeop="sizeof(u1)" />
--- a/hotspot/src/share/vm/utilities/copy.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/utilities/copy.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -53,6 +53,175 @@
}
}
+class CopySwap : AllStatic {
+public:
+ /**
+ * Copy and byte swap elements
+ *
+ * @param src address of source
+ * @param dst address of destination
+ * @param byte_count number of bytes to copy
+ * @param elem_size size of the elements to copy-swap
+ */
+ static void conjoint_swap(address src, address dst, size_t byte_count, size_t elem_size) {
+ assert(src != NULL, "address must not be NULL");
+ assert(dst != NULL, "address must not be NULL");
+ assert(elem_size == 2 || elem_size == 4 || elem_size == 8,
+ "incorrect element size: " SIZE_FORMAT, elem_size);
+ assert(is_size_aligned(byte_count, elem_size),
+ "byte_count " SIZE_FORMAT " must be multiple of element size " SIZE_FORMAT, byte_count, elem_size);
+
+ address src_end = src + byte_count;
+
+ if (dst <= src || dst >= src_end) {
+ do_conjoint_swap<RIGHT>(src, dst, byte_count, elem_size);
+ } else {
+ do_conjoint_swap<LEFT>(src, dst, byte_count, elem_size);
+ }
+ }
+
+private:
+ /**
+ * Byte swap a 16-bit value
+ */
+ static uint16_t byte_swap(uint16_t x) {
+ return (x << 8) | (x >> 8);
+ }
+
+ /**
+ * Byte swap a 32-bit value
+ */
+ static uint32_t byte_swap(uint32_t x) {
+ uint16_t lo = (uint16_t)x;
+ uint16_t hi = (uint16_t)(x >> 16);
+
+ return ((uint32_t)byte_swap(lo) << 16) | (uint32_t)byte_swap(hi);
+ }
+
+ /**
+ * Byte swap a 64-bit value
+ */
+ static uint64_t byte_swap(uint64_t x) {
+ uint32_t lo = (uint32_t)x;
+ uint32_t hi = (uint32_t)(x >> 32);
+
+ return ((uint64_t)byte_swap(lo) << 32) | (uint64_t)byte_swap(hi);
+ }
+
+ enum CopyDirection {
+ RIGHT, // lower -> higher address
+ LEFT // higher -> lower address
+ };
+
+ /**
+ * Copy and byte swap elements
+ *
+ * <T> - type of element to copy
+ * <D> - copy direction
+ * <is_src_aligned> - true if src argument is aligned to element size
+ * <is_dst_aligned> - true if dst argument is aligned to element size
+ *
+ * @param src address of source
+ * @param dst address of destination
+ * @param byte_count number of bytes to copy
+ */
+ template <typename T, CopyDirection D, bool is_src_aligned, bool is_dst_aligned>
+ static void do_conjoint_swap(address src, address dst, size_t byte_count) {
+ address cur_src, cur_dst;
+
+ switch (D) {
+ case RIGHT:
+ cur_src = src;
+ cur_dst = dst;
+ break;
+ case LEFT:
+ cur_src = src + byte_count - sizeof(T);
+ cur_dst = dst + byte_count - sizeof(T);
+ break;
+ }
+
+ for (size_t i = 0; i < byte_count / sizeof(T); i++) {
+ T tmp;
+
+ if (is_src_aligned) {
+ tmp = *(T*)cur_src;
+ } else {
+ memcpy(&tmp, cur_src, sizeof(T));
+ }
+
+ tmp = byte_swap(tmp);
+
+ if (is_dst_aligned) {
+ *(T*)cur_dst = tmp;
+ } else {
+ memcpy(cur_dst, &tmp, sizeof(T));
+ }
+
+ switch (D) {
+ case RIGHT:
+ cur_src += sizeof(T);
+ cur_dst += sizeof(T);
+ break;
+ case LEFT:
+ cur_src -= sizeof(T);
+ cur_dst -= sizeof(T);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Copy and byte swap elements
+ *
+ * <T> - type of element to copy
+ * <D> - copy direction
+ *
+ * @param src address of source
+ * @param dst address of destination
+ * @param byte_count number of bytes to copy
+ */
+ template <typename T, CopyDirection direction>
+ static void do_conjoint_swap(address src, address dst, size_t byte_count) {
+ if (is_ptr_aligned(src, sizeof(T))) {
+ if (is_ptr_aligned(dst, sizeof(T))) {
+ do_conjoint_swap<T,direction,true,true>(src, dst, byte_count);
+ } else {
+ do_conjoint_swap<T,direction,true,false>(src, dst, byte_count);
+ }
+ } else {
+ if (is_ptr_aligned(dst, sizeof(T))) {
+ do_conjoint_swap<T,direction,false,true>(src, dst, byte_count);
+ } else {
+ do_conjoint_swap<T,direction,false,false>(src, dst, byte_count);
+ }
+ }
+ }
+
+
+ /**
+ * Copy and byte swap elements
+ *
+ * <D> - copy direction
+ *
+ * @param src address of source
+ * @param dst address of destination
+ * @param byte_count number of bytes to copy
+ * @param elem_size size of the elements to copy-swap
+ */
+ template <CopyDirection D>
+ static void do_conjoint_swap(address src, address dst, size_t byte_count, size_t elem_size) {
+ switch (elem_size) {
+ case 2: do_conjoint_swap<uint16_t,D>(src, dst, byte_count); break;
+ case 4: do_conjoint_swap<uint32_t,D>(src, dst, byte_count); break;
+ case 8: do_conjoint_swap<uint64_t,D>(src, dst, byte_count); break;
+ default: guarantee(false, "do_conjoint_swap: Invalid elem_size %zd\n", elem_size);
+ }
+ }
+};
+
+void Copy::conjoint_swap(address src, address dst, size_t byte_count, size_t elem_size) {
+ CopySwap::conjoint_swap(src, dst, byte_count, elem_size);
+}
// Fill bytes; larger units are filled atomically if everything is aligned.
void Copy::fill_to_memory_atomic(void* to, size_t size, jubyte value) {
--- a/hotspot/src/share/vm/utilities/copy.hpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/utilities/copy.hpp Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -227,6 +227,16 @@
}
}
+ /**
+ * Copy and *unconditionally* byte swap elements
+ *
+ * @param src address of source
+ * @param dst address of destination
+ * @param byte_count number of bytes to copy
+ * @param elem_size size of the elements to copy-swap
+ */
+ static void conjoint_swap(address src, address dst, size_t byte_count, size_t elem_size);
+
// Fill methods
// Fill word-aligned words, not atomic on each word
--- a/hotspot/src/share/vm/utilities/quickSort.cpp Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/src/share/vm/utilities/quickSort.cpp Wed Jul 05 21:23:38 2017 +0200
@@ -44,6 +44,31 @@
}
return 1;
}
+
+static void print_array(const char* prefix, int* array, int length) {
+ tty->print("%s:", prefix);
+ for (int i = 0; i < length; i++) {
+ tty->print(" %d", array[i]);
+ }
+ tty->cr();
+}
+
+static bool compare_arrays(int* actual, int* expected, int length) {
+ for (int i = 0; i < length; i++) {
+ if (actual[i] != expected[i]) {
+ print_array("Sorted array ", actual, length);
+ print_array("Expected array", expected, length);
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class C>
+static bool sort_and_compare(int* arrayToSort, int* expectedResult, int length, C comparator, bool idempotent = false) {
+ QuickSort::sort<int, C>(arrayToSort, length, comparator, idempotent);
+ return compare_arrays(arrayToSort, expectedResult, length);
+}
#endif // ASSERT
static int test_even_odd_comparator(int a, int b) {
@@ -72,31 +97,6 @@
}
}
-static void print_array(const char* prefix, int* array, int length) {
- tty->print("%s:", prefix);
- for (int i = 0; i < length; i++) {
- tty->print(" %d", array[i]);
- }
- tty->cr();
-}
-
-static bool compare_arrays(int* actual, int* expected, int length) {
- for (int i = 0; i < length; i++) {
- if (actual[i] != expected[i]) {
- print_array("Sorted array ", actual, length);
- print_array("Expected array", expected, length);
- return false;
- }
- }
- return true;
-}
-
-template <class C>
-static bool sort_and_compare(int* arrayToSort, int* expectedResult, int length, C comparator, bool idempotent = false) {
- QuickSort::sort<int, C>(arrayToSort, length, comparator, idempotent);
- return compare_arrays(arrayToSort, expectedResult, length);
-}
-
void QuickSort_test() {
{
int* test_array = NULL;
--- a/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics2.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics2.java Wed Jul 05 21:23:38 2017 +0200
@@ -33,9 +33,10 @@
*
* @run main/othervm
* -Xbootclasspath/a:.
+ * -Xmixed
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
- * -XX:MaxInlineSize=100
+ * -XX:MaxInlineSize=70
* -XX:MinInliningThreshold=0
* TestStringIntrinsics2
*/
--- a/hotspot/test/compiler/loopopts/superword/ProdRed_Double.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/ProdRed_Double.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar product reduction optimizations : float test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Double
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Double
--- a/hotspot/test/compiler/loopopts/superword/ProdRed_Float.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/ProdRed_Float.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar product reduction optimizations : float test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Float
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Float
--- a/hotspot/test/compiler/loopopts/superword/ProdRed_Int.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/ProdRed_Int.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar product reduction optimizations : int test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Int
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 ProdRed_Int
--- a/hotspot/test/compiler/loopopts/superword/ReductionPerf.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/ReductionPerf.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar product reduction optimizations : int test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:CompileThresholdScaling=0.1 -XX:CompileCommand=exclude,ReductionPerf::main ReductionPerf
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:CompileThresholdScaling=0.1 -XX:CompileCommand=exclude,ReductionPerf::main ReductionPerf
--- a/hotspot/test/compiler/loopopts/superword/SumRedSqrt_Double.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/SumRedSqrt_Double.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,7 +26,7 @@
* @test
* @bug 8135028
* @summary Add C2 x86 Superword support for scalar sum reduction optimizations : double sqrt test
-* @requires os.arch=="x86" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
+* @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRedSqrt_Double
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRedSqrt_Double
--- a/hotspot/test/compiler/loopopts/superword/SumRed_Double.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/SumRed_Double.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar sum reduction optimizations : double test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Double
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Double
--- a/hotspot/test/compiler/loopopts/superword/SumRed_Float.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/SumRed_Float.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar sum reduction optimizations : float test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Float
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Float
--- a/hotspot/test/compiler/loopopts/superword/SumRed_Int.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/SumRed_Int.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8074981
* @summary Add C2 x86 Superword support for scalar sum reduction optimizations : int test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Int
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=2 -XX:CompileThresholdScaling=0.1 SumRed_Int
--- a/hotspot/test/compiler/loopopts/superword/SumRed_Long.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/compiler/loopopts/superword/SumRed_Long.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
* @test
* @bug 8076276
* @summary Add C2 x86 Superword support for scalar sum reduction optimizations : long test
+ * @requires os.arch=="x86" | os.arch=="i386" | os.arch=="amd64" | os.arch=="x86_64"
*
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=4 -XX:CompileThresholdScaling=0.1 SumRed_Long
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-SuperWordReductions -XX:LoopUnrollLimit=250 -XX:LoopMaxUnroll=4 -XX:CompileThresholdScaling=0.1 SumRed_Long
--- a/hotspot/test/runtime/CommandLine/VMDeprecatedOptions.java Thu Feb 25 09:41:40 2016 -0800
+++ b/hotspot/test/runtime/CommandLine/VMDeprecatedOptions.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -40,6 +40,8 @@
// deprecated non-alias flags:
{"MaxGCMinorPauseMillis", "1032"},
{"UseParNewGC", "false"},
+ {"ConvertSleepToYield", "false" },
+ {"ConvertYieldToSleep", "false" },
// deprecated alias flags (see also aliased_jvm_flags):
{"DefaultMaxRAMFraction", "4"},
--- a/jdk/.hgtags Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/.hgtags Wed Jul 05 21:23:38 2017 +0200
@@ -349,3 +349,4 @@
8faf1aec77a9517c69d2f4d8dd146429852ace7f jdk-9+104
55518739e399a1066c8613e19100d51b38d9f223 jdk-9+105
6e9ecae50b4e0d37483fb2719202eea5dca026a4 jdk-9+106
+8701b2bb1d2e1b9abc2a9be0933993c7150a9dbe jdk-9+107
--- a/jdk/make/gensrc/GensrcMisc.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/gensrc/GensrcMisc.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -53,6 +53,8 @@
$(eval $(call SetupNativeCompilation, BUILD_GENSRC_SOR_EXE, \
SRC := $(GENSRC_SOR_SRC), \
INCLUDE_FILES := $(GENSRC_SOR_SRC_FILE), \
+ CFLAGS_windows := -nologo, \
+ LDFLAGS_windows := -nologo, \
TOOLCHAIN := TOOLCHAIN_BUILD, \
OBJECT_DIR := $(GENSRC_SOR_BIN), \
OUTPUT_DIR := $(GENSRC_SOR_BIN), \
@@ -61,12 +63,12 @@
SOR_PREGEN_FILE := $(JDK_TOPDIR)/src/closed/java.base/$(OPENJDK_TARGET_OS)/classes/sun/nio/ch/SocketOptionRegistry-$(OPENJDK_TARGET_OS)-$(OPENJDK_TARGET_CPU_ARCH).java.template
ifeq ($(wildcard $(SOR_PREGEN_FILE)), )
- $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/ch/SocketOptionRegistry.java: $(BUILD_GENSRC_SOR_EXE)
+ $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/ch/SocketOptionRegistry.java: $(BUILD_GENSRC_SOR_EXE_TARGET)
$(MKDIR) -p $(@D)
$(RM) $@ $@.tmp
NAWK="$(NAWK)" SH="$(SH)" $(SH) -e \
$(JDK_TOPDIR)/make/scripts/addNotices.sh "$(SOR_COPYRIGHT_YEARS)" > $@.tmp
- $(BUILD_GENSRC_SOR_EXE) >> $@.tmp
+ $(BUILD_GENSRC_SOR_EXE_TARGET) >> $@.tmp
$(MV) $@.tmp $@
else
$(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/ch/SocketOptionRegistry.java: $(SOR_PREGEN_FILE)
@@ -97,12 +99,12 @@
UC_PREGEN_FILE := $(JDK_TOPDIR)/src/closed/java.base/$(OPENJDK_TARGET_OS)/classes/sun/nio/fs/UnixConstants-$(OPENJDK_TARGET_OS)-$(OPENJDK_TARGET_CPU_ARCH).java.template
ifeq ($(wildcard $(UC_PREGEN_FILE)), )
- $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/fs/UnixConstants.java: $(BUILD_GENSRC_UC_EXE)
+ $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/fs/UnixConstants.java: $(BUILD_GENSRC_UC_EXE_TARGET)
$(MKDIR) -p $(@D)
$(RM) $@ $@.tmp
NAWK="$(NAWK)" SH="$(SH)" $(SH) -e \
$(JDK_TOPDIR)/make/scripts/addNotices.sh "$(UC_COPYRIGHT_YEARS)" > $@.tmp
- $(BUILD_GENSRC_UC_EXE) >> $@.tmp
+ $(BUILD_GENSRC_UC_EXE_TARGET) >> $@.tmp
$(MV) $@.tmp $@
else
$(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/fs/UnixConstants.java: $(UC_PREGEN_FILE)
@@ -132,12 +134,12 @@
OUTPUT_DIR := $(GENSRC_SOL_BIN), \
PROGRAM := genSolarisConstants))
- $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/fs/SolarisConstants.java: $(BUILD_GENSRC_SOL_EXE)
+ $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/nio/fs/SolarisConstants.java: $(BUILD_GENSRC_SOL_EXE_TARGET)
$(MKDIR) -p $(@D)
$(RM) $@ $@.tmp
NAWK="$(NAWK)" SH="$(SH)" $(SH) -e \
$(JDK_TOPDIR)/make/scripts/addNotices.sh "$(SOL_COPYRIGHT_YEARS)" > $@.tmp
- $(BUILD_GENSRC_SOL_EXE) >> $@.tmp
+ $(BUILD_GENSRC_SOL_EXE_TARGET) >> $@.tmp
$(MV) $@.tmp $@
--- a/jdk/make/lib/Awt2dLibraries.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/lib/Awt2dLibraries.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -593,8 +593,8 @@
LDFLAGS_solaris := $(call SET_SHARED_LIBRARY_ORIGIN,/..), \
REORDER := $(LIBAWT_HEADLESS_REORDER), \
LIBS_unix := -lawt -ljvm -ljava, \
- LIBS_linux := -lm $(LIBDL), \
- LIBS_solaris := -lm $(LIBDL) $(LIBCXX) -lc, \
+ LIBS_linux := $(LIBM) $(LIBDL), \
+ LIBS_solaris := $(LIBM) $(LIBDL) $(LIBCXX) -lc, \
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libawt_headless, \
))
--- a/jdk/make/lib/CoreLibraries.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/lib/CoreLibraries.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -137,12 +137,6 @@
endif
endif
-ifeq ($(OPENJDK_TARGET_OS), linux)
- ifeq ($(OPENJDK_TARGET_CPU), x86_64)
- BUILD_LIBJAVA_Bits.c_CFLAGS := $(C_O_FLAG_NORM)
- endif
-endif
-
$(eval $(call SetupNativeCompilation,BUILD_LIBJAVA, \
LIBRARY := java, \
OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
@@ -163,7 +157,7 @@
LIBS_unix := -ljvm -lverify, \
LIBS_linux := $(LIBDL) $(BUILD_LIBFDLIBM), \
LIBS_solaris := -lsocket -lnsl -lscf $(LIBDL) $(BUILD_LIBFDLIBM) -lc, \
- LIBS_aix := $(LIBDL) $(BUILD_LIBFDLIBM) -lm,\
+ LIBS_aix := $(LIBDL) $(BUILD_LIBFDLIBM) $(LIBM),\
LIBS_macosx := -lfdlibm \
-framework CoreFoundation \
-framework Foundation \
--- a/jdk/make/lib/Lib-java.instrument.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/lib/Lib-java.instrument.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -67,7 +67,6 @@
-L$(call FindLibDirForModule, java.base)/jli, \
LDFLAGS_macosx := -Wl$(COMMA)-all_load, \
LDFLAGS_aix := -L$(SUPPORT_OUTPUTDIR)/native/java.base, \
- LDFLAGS_windows := -export:Agent_OnAttach, \
LIBS := $(JDKLIB_LIBS), \
LIBS_unix := -ljava $(LIBZ), \
LIBS_linux := -ljli $(LIBDL), \
--- a/jdk/make/lib/Lib-jdk.jdi.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/lib/Lib-jdk.jdi.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
--- a/jdk/make/lib/Lib-jdk.jdwp.agent.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/lib/Lib-jdk.jdwp.agent.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
--- a/jdk/make/mapfiles/libjava/mapfile-vers Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Wed Jul 05 21:23:38 2017 +0200
@@ -229,12 +229,6 @@
Java_java_lang_Throwable_fillInStackTrace;
Java_java_lang_Throwable_getStackTraceDepth;
Java_java_lang_Throwable_getStackTraceElement;
- Java_java_nio_Bits_copyFromShortArray;
- Java_java_nio_Bits_copyToShortArray;
- Java_java_nio_Bits_copyFromIntArray;
- Java_java_nio_Bits_copyToIntArray;
- Java_java_nio_Bits_copyFromLongArray;
- Java_java_nio_Bits_copyToLongArray;
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedAction_2;
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedAction_2Ljava_security_AccessControlContext_2;
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2;
--- a/jdk/make/mapfiles/libnet/mapfile-vers Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/mapfiles/libnet/mapfile-vers Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1997, 2016, 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
@@ -87,6 +87,9 @@
Java_java_net_PlainSocketImpl_socketConnect;
Java_java_net_PlainDatagramSocketImpl_getTimeToLive;
Java_java_net_PlainDatagramSocketImpl_setTimeToLive;
+ Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0;
+ Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0;
+ Java_jdk_net_Sockets_isReusePortAvailable0;
Java_sun_net_PortConfig_getUpper0;
Java_sun_net_PortConfig_getLower0;
Java_sun_net_dns_ResolverConfigurationImpl_localDomain0;
@@ -112,6 +115,7 @@
NET_EnableFastTcpLoopback;
NET_ThrowNew;
ipv6_available;
+ reuseport_available;
initInetAddressIDs;
local:
--- a/jdk/make/mapfiles/libnio/mapfile-linux Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/mapfiles/libnio/mapfile-linux Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2001, 2016, 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
@@ -109,6 +109,7 @@
Java_sun_nio_ch_Net_setIntOption0;
Java_sun_nio_ch_Net_initIDs;
Java_sun_nio_ch_Net_isIPv6Available0;
+ Java_sun_nio_ch_Net_isReusePortAvailable0;
Java_sun_nio_ch_Net_joinOrDrop4;
Java_sun_nio_ch_Net_blockOrUnblock4;
Java_sun_nio_ch_Net_joinOrDrop6;
--- a/jdk/make/mapfiles/libnio/mapfile-macosx Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/mapfiles/libnio/mapfile-macosx Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2001, 2016, 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
@@ -102,6 +102,7 @@
Java_sun_nio_ch_Net_setIntOption0;
Java_sun_nio_ch_Net_initIDs;
Java_sun_nio_ch_Net_isIPv6Available0;
+ Java_sun_nio_ch_Net_isReusePortAvailable0;
Java_sun_nio_ch_Net_joinOrDrop4;
Java_sun_nio_ch_Net_blockOrUnblock4;
Java_sun_nio_ch_Net_joinOrDrop6;
--- a/jdk/make/mapfiles/libnio/mapfile-solaris Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/mapfiles/libnio/mapfile-solaris Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2001, 2016, 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
@@ -97,6 +97,7 @@
Java_sun_nio_ch_Net_setIntOption0;
Java_sun_nio_ch_Net_initIDs;
Java_sun_nio_ch_Net_isIPv6Available0;
+ Java_sun_nio_ch_Net_isReusePortAvailable0;
Java_sun_nio_ch_Net_joinOrDrop4;
Java_sun_nio_ch_Net_blockOrUnblock4;
Java_sun_nio_ch_Net_joinOrDrop6;
--- a/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java Wed Jul 05 21:23:38 2017 +0200
@@ -248,10 +248,7 @@
dir.mkdirs();
}
String className =
- (CLDRConverter.isBaseModule ? "CLDRBaseLocaleDataMetaInfo" :
- "CLDRLocaleDataMetaInfo_" +
- CLDRConverter.DESTINATION_DIR.substring(CLDRConverter.DESTINATION_DIR.lastIndexOf('/')+1)
- .replaceAll("\\.", "_"));
+ (CLDRConverter.isBaseModule ? "CLDRBaseLocaleDataMetaInfo" : "CLDRLocaleDataMetaInfo");
File file = new File(dir, className + ".java");
if (!file.exists()) {
file.createNewFile();
--- a/jdk/make/src/classes/build/tools/dtdbuilder/DTDParser.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/src/classes/build/tools/dtdbuilder/DTDParser.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -908,16 +908,12 @@
this.dtd = dtd;
this.in = new DTDInputStream(in, dtd);
- long tm = System.currentTimeMillis();
ch = this.in.read();
parseSection();
if (ch != -1) {
error("premature");
}
-
- tm = System.currentTimeMillis() - tm;
- System.err.println("[Parsed DTD " + dtd + " in " + tm + "ms]");
} catch (IOException e) {
error("ioexception");
} catch (Exception e) {
--- a/jdk/make/src/classes/build/tools/module/boot.modules Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/src/classes/build/tools/module/boot.modules Wed Jul 05 21:23:38 2017 +0200
@@ -2,6 +2,7 @@
java.compiler
java.datatransfer
java.desktop
+java.httpclient
java.instrument
java.logging
java.management
--- a/jdk/make/src/native/genconstants/ch/genSocketOptionRegistry.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/make/src/native/genconstants/ch/genSocketOptionRegistry.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -34,6 +34,21 @@
#include <netinet/tcp.h>
#endif
+/* Defines SO_REUSEPORT */
+#if !defined(SO_REUSEPORT)
+#ifdef _WIN32
+#define SO_REUSEPORT 0
+#elif __linux__
+#define SO_REUSEPORT 15
+#elif __solaris__
+#define SO_REUSEPORT 0x100e
+#elif defined(AIX) || defined(MACOSX)
+#define SO_REUSEPORT 0x0200
+#else
+#define SO_REUSEPORT 0
+#endif
+#endif
+
/**
* Generates sun.nio.ch.SocketOptionRegistry, a class that maps Java-level
* socket options to the platform specific level and option.
@@ -102,6 +117,7 @@
emit_unspec("StandardSocketOptions.SO_SNDBUF", SOL_SOCKET, SO_SNDBUF);
emit_unspec("StandardSocketOptions.SO_RCVBUF", SOL_SOCKET, SO_RCVBUF);
emit_unspec("StandardSocketOptions.SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR);
+ emit_unspec("StandardSocketOptions.SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT);
emit_unspec("StandardSocketOptions.TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY);
emit_inet("StandardSocketOptions.IP_TOS", IPPROTO_IP, IP_TOS);
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -1026,6 +1026,9 @@
.asCollector(long[].class, 1);
assertEquals("[123]", (String) longsToString.invokeExact((long)123));
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
* @param arrayLength the number of arguments to collect into a new array argument
* @return a new method handle which collects some trailing argument
@@ -1067,6 +1070,9 @@
swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
assertEquals("BCPQRSZ", swr.toString());
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param collectArgPos the zero-based position in the parameter list at which to start collecting.
* @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
* @param arrayLength the number of arguments to collect into a new array argument
@@ -1356,8 +1362,11 @@
* The reference {@code x} must be convertible to the first parameter
* type of the target.
* <p>
- * (<em>Note:</em> Because method handles are immutable, the target method handle
- * retains its original type and behavior.)
+ * <em>Note:</em> Because method handles are immutable, the target method handle
+ * retains its original type and behavior.
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param x the value to bind to the first argument of the target
* @return a new method handle which prepends the given value to the incoming
* argument list, before calling the original method handle
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -778,8 +778,21 @@
return (asTypeCache = wrapper);
}
+ // Customize target if counting happens for too long.
+ private int invocations = CUSTOMIZE_THRESHOLD;
+ private void maybeCustomizeTarget() {
+ int c = invocations;
+ if (c >= 0) {
+ if (c == 1) {
+ target.customize();
+ }
+ invocations = c - 1;
+ }
+ }
+
boolean countDown() {
int c = count;
+ maybeCustomizeTarget();
if (c <= 1) {
// Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility.
if (isCounting) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Jul 05 21:23:38 2017 +0200
@@ -2139,7 +2139,7 @@
* if its index does not appear in the array.
* As in the case of {@link #dropArguments(MethodHandle,int,List) dropArguments},
* incoming arguments which are not mentioned in the reordering array
- * are may be any type, as determined only by {@code newType}.
+ * may be of any type, as determined only by {@code newType}.
* <blockquote><pre>{@code
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
@@ -2157,6 +2157,9 @@
assert(twice.type().equals(intfn1));
assert((int)twice.invokeExact(21) == 42);
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param target the method handle to invoke after arguments are reordered
* @param newType the expected type of the new method handle
* @param reorder an index array which controls the reordering
@@ -2421,6 +2424,9 @@
* It may range between zero and <i>N-L</i> (inclusively),
* where <i>N</i> is the arity of the target method handle
* and <i>L</i> is the length of the values array.
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param target the method handle to invoke after the argument is inserted
* @param pos where to insert the argument (zero for the first)
* @param values the series of arguments to insert
@@ -2639,14 +2645,25 @@
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
* }</pre></blockquote>
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * denotes the return type of both the {@code target} and resulting adapter.
+ * {@code P}/{@code p} and {@code B}/{@code b} represent the types and values
+ * of the parameters and arguments that precede and follow the filter position
+ * {@code pos}, respectively. {@code A[i]}/{@code a[i]} stand for the types and
+ * values of the filtered parameters and arguments; they also represent the
+ * return types of the {@code filter[i]} handles. The latter accept arguments
+ * {@code v[i]} of type {@code V[i]}, which also appear in the signature of
+ * the resulting adapter.
* <blockquote><pre>{@code
- * V target(P... p, A[i]... a[i], B... b);
+ * T target(P... p, A[i]... a[i], B... b);
* A[i] filter[i](V[i]);
* T adapter(P... p, V[i]... v[i], B... b) {
- * return target(p..., f[i](v[i])..., b...);
+ * return target(p..., filter[i](v[i])..., b...);
* }
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
*
* @param target the method handle to invoke after arguments are filtered
* @param pos the position of the first argument to filter
@@ -2753,7 +2770,17 @@
assertEquals("[top, [[up, down, strange], charm], bottom]",
(String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
* }</pre></blockquote>
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the return type of the {@code target} and resulting adapter.
+ * {@code V}/{@code v} stand for the return type and value of the
+ * {@code filter}, which are also found in the signature and arguments of
+ * the {@code target}, respectively, unless {@code V} is {@code void}.
+ * {@code A}/{@code a} and {@code C}/{@code c} represent the parameter types
+ * and values preceding and following the collection position, {@code pos},
+ * in the {@code target}'s signature. They also turn up in the resulting
+ * adapter's signature and arguments, where they surround
+ * {@code B}/{@code b}, which represent the parameter types and arguments
+ * to the {@code filter} (if any).
* <blockquote><pre>{@code
* T target(A...,V,C...);
* V filter(B...);
@@ -2771,7 +2798,7 @@
* // and if the filter has a void return:
* T target3(A...,C...);
* void filter3(B...);
- * void adapter3(A... a,B... b,C... c) {
+ * T adapter3(A... a,B... b,C... c) {
* filter3(b...);
* return target3(a...,c...);
* }
@@ -2791,6 +2818,9 @@
* a non-void result, then {@code collectArguments(mh, N, coll)}
* is equivalent to {@code filterArguments(mh, N, coll)}.
* Other equivalences are possible but would require argument permutation.
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
*
* @param target the method handle to invoke after filtering the subsequence of arguments
* @param pos the position of the first adapter argument to pass to the filter,
@@ -2864,29 +2894,36 @@
MethodHandle f0 = filterReturnValue(cat, length);
System.out.println((int) f0.invokeExact("x", "y")); // 2
* }</pre></blockquote>
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>Here is pseudocode for the resulting adapter. In the code,
+ * {@code T}/{@code t} represent the result type and value of the
+ * {@code target}; {@code V}, the result type of the {@code filter}; and
+ * {@code A}/{@code a}, the types and values of the parameters and arguments
+ * of the {@code target} as well as the resulting adapter.
* <blockquote><pre>{@code
- * V target(A...);
- * T filter(V);
- * T adapter(A... a) {
- * V v = target(a...);
- * return filter(v);
+ * T target(A...);
+ * V filter(T);
+ * V adapter(A... a) {
+ * T t = target(a...);
+ * return filter(t);
* }
* // and if the target has a void return:
* void target2(A...);
- * T filter2();
- * T adapter2(A... a) {
+ * V filter2();
+ * V adapter2(A... a) {
* target2(a...);
* return filter2();
* }
* // and if the filter has a void return:
- * V target3(A...);
+ * T target3(A...);
* void filter3(V);
* void adapter3(A... a) {
- * V v = target3(a...);
- * filter3(v);
+ * T t = target3(a...);
+ * filter3(t);
* }
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param target the method handle to invoke before filtering the return value
* @param filter method handle to call on the return value
* @return method handle which incorporates the specified return value filtering logic
@@ -2964,7 +3001,15 @@
// also prints "boo":
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }</pre></blockquote>
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the result type of the {@code target} and resulting adapter.
+ * {@code V}/{@code v} represent the type and value of the parameter and argument
+ * of {@code target} that precedes the folding position; {@code V} also is
+ * the result type of the {@code combiner}. {@code A}/{@code a} denote the
+ * types and values of the {@code N} parameters and arguments at the folding
+ * position. {@code B}/{@code b} represent the types and values of the
+ * {@code target} parameters and arguments that follow the folded parameters
+ * and arguments.
* <blockquote><pre>{@code
* // there are N arguments in A...
* T target(V, A[N]..., B...);
@@ -2981,6 +3026,9 @@
* return target2(a..., b...);
* }
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
* @param target the method handle to invoke after arguments are combined
* @param combiner method handle to call initially on the incoming arguments
* @return method handle which incorporates the specified argument folding logic
@@ -3022,7 +3070,13 @@
* argument and return types, except that the return type
* of the test must be boolean, and the test is allowed
* to have fewer arguments than the other two method handles.
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>
+ * Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the uniform result type of the three involved handles;
+ * {@code A}/{@code a}, the types and values of the {@code target}
+ * parameters and arguments that are consumed by the {@code test}; and
+ * {@code B}/{@code b}, those types and values of the {@code target}
+ * parameters and arguments that are not consumed by the {@code test}.
* <blockquote><pre>{@code
* boolean test(A...);
* T target(A...,B...);
@@ -3084,7 +3138,13 @@
* argument and return types, except that handler may omit trailing arguments
* (similarly to the predicate in {@link #guardWithTest guardWithTest}).
* Also, the handler must have an extra leading parameter of {@code exType} or a supertype.
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>
+ * Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the return type of the {@code target} and {@code handler},
+ * and correspondingly that of the resulting adapter; {@code A}/{@code a},
+ * the types and values of arguments to the resulting handle consumed by
+ * {@code handler}; and {@code B}/{@code b}, those of arguments to the
+ * resulting handle discarded by {@code handler}.
* <blockquote><pre>{@code
* T target(A..., B...);
* T handler(ExType, A...);
@@ -3828,8 +3888,9 @@
* (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists
* by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.)
* <p>
- * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also
- * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:<ul>
+ * The {@code target} and {@code cleanup} handles must have the same corresponding argument and return types, except
+ * that the {@code cleanup} handle may omit trailing arguments. Also, the {@code cleanup} handle must have one or
+ * two extra leading parameters:<ul>
* <li>a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and
* <li>a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry
* the result from the execution of the {@code target} handle.
@@ -3932,7 +3993,16 @@
// also prints "jum":
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* }</pre></blockquote>
- * <p> Here is pseudocode for the resulting adapter:
+ * <p>Here is pseudocode for the resulting adapter. In the code, {@code T}
+ * represents the result type of the {@code target} and resulting adapter.
+ * {@code V}/{@code v} represent the type and value of the parameter and argument
+ * of {@code target} that precedes the folding position; {@code V} also is
+ * the result type of the {@code combiner}. {@code A}/{@code a} denote the
+ * types and values of the {@code N} parameters and arguments at the folding
+ * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types
+ * and values of the {@code target} parameters and arguments that precede and
+ * follow the folded parameters and arguments starting at {@code pos},
+ * respectively.
* <blockquote><pre>{@code
* // there are N arguments in A...
* T target(Z..., V, A[N]..., B...);
@@ -3949,6 +4019,9 @@
* return target2(z..., a..., b...);
* }
* }</pre></blockquote>
+ * <p>
+ * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector
+ * variable-arity method handle}, even if the original target method handle was.
*
* @param target the method handle to invoke after arguments are combined
* @param pos the position at which to start folding and at which to insert the folding result; if this is {@code
@@ -4064,8 +4137,10 @@
// The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
// target parameter list.
int cleanupArgIndex = rtype == void.class ? 1 : 2;
- if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()).
- equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
+ List<Class<?>> cleanupArgSuffix = cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size());
+ List<Class<?>> targetParamTypes = target.type().parameterList();
+ if (targetParamTypes.size() < cleanupArgSuffix.size() ||
+ !cleanupArgSuffix.equals(targetParamTypes.subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix",
cleanup.type(), target.type());
}
--- a/jdk/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -28,6 +28,9 @@
import java.io.IOException;
import java.security.AccessController;
import sun.net.ResourceManager;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
/**
* Abstract datagram and multicast socket implementation base class.
@@ -70,6 +73,45 @@
});
}
+ private static volatile boolean checkedReusePort;
+ private static volatile boolean isReusePortAvailable;
+
+ /**
+ * Tells whether SO_REUSEPORT is supported.
+ */
+ static boolean isReusePortAvailable() {
+ if (!checkedReusePort) {
+ isReusePortAvailable = isReusePortAvailable0();
+ checkedReusePort = true;
+ }
+ return isReusePortAvailable;
+ }
+
+ private static volatile Set<SocketOption<?>> socketOptions;
+
+ /**
+ * Returns a set of SocketOptions supported by this impl
+ * and by this impl's socket (Socket or ServerSocket)
+ *
+ * @return a Set of SocketOptions
+ */
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ Set<SocketOption<?>> options = socketOptions;
+ if (options == null) {
+ if (isReusePortAvailable()) {
+ options = new HashSet<>();
+ options.addAll(super.supportedOptions());
+ options.add(StandardSocketOptions.SO_REUSEPORT);
+ options = Collections.unmodifiableSet(options);
+ } else {
+ options = super.supportedOptions();
+ }
+ socketOptions = options;
+ }
+ return options;
+ }
+
/**
* Creates a datagram socket
*/
@@ -303,6 +345,14 @@
if (o == null || !(o instanceof Boolean))
throw new SocketException("bad argument for IP_MULTICAST_LOOP");
break;
+ case SO_REUSEPORT:
+ if (o == null || !(o instanceof Boolean)) {
+ throw new SocketException("bad argument for SO_REUSEPORT");
+ }
+ if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ break;
default:
throw new SocketException("invalid option: " + optID);
}
@@ -343,6 +393,13 @@
result = socketGetOption(optID);
break;
+ case SO_REUSEPORT:
+ if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ result = socketGetOption(optID);
+ break;
+
default:
throw new SocketException("invalid option: " + optID);
}
@@ -364,4 +421,5 @@
}
abstract int dataAvailable();
+ private static native boolean isReusePortAvailable0();
}
--- a/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, 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
@@ -33,6 +33,9 @@
import sun.net.ConnectionResetException;
import sun.net.NetHooks;
import sun.net.ResourceManager;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
/**
* Default Socket Implementation. This implementation does
@@ -87,6 +90,45 @@
});
}
+ private static volatile boolean checkedReusePort;
+ private static volatile boolean isReusePortAvailable;
+
+ /**
+ * Tells whether SO_REUSEPORT is supported.
+ */
+ static boolean isReusePortAvailable() {
+ if (!checkedReusePort) {
+ isReusePortAvailable = isReusePortAvailable0();
+ checkedReusePort = true;
+ }
+ return isReusePortAvailable;
+ }
+
+ private static volatile Set<SocketOption<?>> socketOptions;
+
+ /**
+ * Returns a set of SocketOptions supported by this impl
+ * and by this impl's socket (Socket or ServerSocket)
+ *
+ * @return a Set of SocketOptions
+ */
+ @Override
+ protected Set<SocketOption<?>> supportedOptions() {
+ Set<SocketOption<?>> options = socketOptions;
+ if (options == null) {
+ if (isReusePortAvailable()) {
+ options = new HashSet<>();
+ options.addAll(super.supportedOptions());
+ options.add(StandardSocketOptions.SO_REUSEPORT);
+ options = Collections.unmodifiableSet(options);
+ } else {
+ options = super.supportedOptions();
+ }
+ socketOptions = options;
+ }
+ return options;
+ }
+
/**
* Creates a socket with a boolean that specifies whether this
* is a stream socket (true) or an unconnected UDP socket (false).
@@ -269,6 +311,13 @@
throw new SocketException("bad parameter for SO_REUSEADDR");
on = ((Boolean)val).booleanValue();
break;
+ case SO_REUSEPORT:
+ if (val == null || !(val instanceof Boolean))
+ throw new SocketException("bad parameter for SO_REUSEPORT");
+ if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT))
+ throw new UnsupportedOperationException("unsupported option");
+ on = ((Boolean)val).booleanValue();
+ break;
default:
throw new SocketException("unrecognized TCP option: " + opt);
}
@@ -326,6 +375,12 @@
case SO_KEEPALIVE:
ret = socketGetOption(opt, null);
return Boolean.valueOf(ret != -1);
+ case SO_REUSEPORT:
+ if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ ret = socketGetOption(opt, null);
+ return Boolean.valueOf(ret != -1);
// should never get here
default:
return null;
@@ -723,4 +778,6 @@
public static final int SHUT_RD = 0;
public static final int SHUT_WR = 1;
+
+ private static native boolean isReusePortAvailable0();
}
--- a/jdk/src/java.base/share/classes/java/net/Authenticator.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/Authenticator.java Wed Jul 05 21:23:38 2017 +0200
@@ -320,6 +320,48 @@
}
/**
+ * Ask this authenticator for a password.
+ *
+ * @param host The hostname of the site requesting authentication.
+ * @param addr The InetAddress of the site requesting authorization,
+ * or null if not known.
+ * @param port the port for the requested connection
+ * @param protocol The protocol that's requesting the connection
+ * ({@link java.net.Authenticator#getRequestingProtocol()})
+ * @param prompt A prompt string for the user
+ * @param scheme The authentication scheme
+ * @param url The requesting URL that caused the authentication
+ * @param reqType The type (server or proxy) of the entity requesting
+ * authentication.
+ *
+ * @return The username/password, or null if one can't be gotten
+ *
+ * @since 9
+ */
+ public PasswordAuthentication
+ requestPasswordAuthenticationInstance(String host,
+ InetAddress addr,
+ int port,
+ String protocol,
+ String prompt,
+ String scheme,
+ URL url,
+ RequestorType reqType) {
+ synchronized (this) {
+ this.reset();
+ this.requestingHost = host;
+ this.requestingSite = addr;
+ this.requestingPort = port;
+ this.requestingProtocol = protocol;
+ this.requestingPrompt = prompt;
+ this.requestingScheme = scheme;
+ this.requestingURL = url;
+ this.requestingAuthType = reqType;
+ return this.getPasswordAuthentication();
+ }
+ }
+
+ /**
* Gets the {@code hostname} of the
* site or proxy requesting authentication, or {@code null}
* if not available.
--- a/jdk/src/java.base/share/classes/java/net/DatagramSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/DatagramSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -287,6 +287,9 @@
setOption(SocketOptions.SO_RCVBUF, value);
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
setOption(SocketOptions.SO_REUSEADDR, value);
+ } else if (name == StandardSocketOptions.SO_REUSEPORT &&
+ supportedOptions().contains(name)) {
+ setOption(SocketOptions.SO_REUSEPORT, value);
} else if (name == StandardSocketOptions.IP_TOS) {
setOption(SocketOptions.IP_TOS, value);
} else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
@@ -329,6 +332,9 @@
return (T) getOption(SocketOptions.SO_RCVBUF);
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
return (T) getOption(SocketOptions.SO_REUSEADDR);
+ } else if (name == StandardSocketOptions.SO_REUSEPORT &&
+ supportedOptions().contains(name)) {
+ return (T) getOption(SocketOptions.SO_REUSEPORT);
} else if (name == StandardSocketOptions.IP_TOS) {
return (T) getOption(SocketOptions.IP_TOS);
} else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
--- a/jdk/src/java.base/share/classes/java/net/JarURLConnection.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/JarURLConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -173,6 +173,14 @@
}
jarFileURL = new URL(spec.substring(0, separator++));
+ /*
+ * The url argument may have had a runtime fragment appended, so
+ * we need to add a runtime fragment to the jarFileURL to enable
+ * runtime versioning when the underlying jar file is opened.
+ */
+ if ("runtime".equals(url.getRef())) {
+ jarFileURL = new URL(jarFileURL, "#runtime");
+ }
entryName = null;
/* if ! is the last letter of the innerURL, entryName is null */
--- a/jdk/src/java.base/share/classes/java/net/MulticastSocket.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/MulticastSocket.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, 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
@@ -98,7 +98,11 @@
* <p>
* When the socket is created the
* {@link DatagramSocket#setReuseAddress(boolean)} method is
- * called to enable the SO_REUSEADDR socket option.
+ * called to enable the SO_REUSEADDR socket option. When
+ * {@link StandardSocketOptions#SO_REUSEPORT SO_REUSEPORT} is
+ * supported then
+ * {@link DatagramSocketImpl#setOption(SocketOption, Object)}
+ * is called to enable the socket option.
*
* @exception IOException if an I/O exception occurs
* while creating the MulticastSocket
@@ -106,6 +110,7 @@
* {@code checkListen} method doesn't allow the operation.
* @see SecurityManager#checkListen
* @see java.net.DatagramSocket#setReuseAddress(boolean)
+ * @see java.net.DatagramSocketImpl#setOption(SocketOption, Object)
*/
public MulticastSocket() throws IOException {
this(new InetSocketAddress(0));
@@ -167,6 +172,11 @@
// Enable SO_REUSEADDR before binding
setReuseAddress(true);
+ // Enable SO_REUSEPORT if supported before binding
+ if (supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ this.setOption(StandardSocketOptions.SO_REUSEPORT, true);
+ }
+
if (bindaddr != null) {
try {
bind(bindaddr);
--- a/jdk/src/java.base/share/classes/java/net/ProxySelector.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/ProxySelector.java Wed Jul 05 21:23:38 2017 +0200
@@ -162,4 +162,49 @@
* @throws IllegalArgumentException if either argument is null
*/
public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe);
+
+ /**
+ * Returns a ProxySelector which uses the given proxy address for all HTTP
+ * and HTTPS requests. If proxy is {@code null} then proxying is disabled.
+ *
+ * @param proxyAddress
+ * The address of the proxy
+ *
+ * @return a ProxySelector
+ *
+ * @since 9
+ */
+ public static ProxySelector of(InetSocketAddress proxyAddress) {
+ return new StaticProxySelector(proxyAddress);
+ }
+
+ static class StaticProxySelector extends ProxySelector {
+ private static final List<Proxy> NO_PROXY_LIST = List.of(Proxy.NO_PROXY);
+ final List<Proxy> list;
+
+ StaticProxySelector(InetSocketAddress address){
+ Proxy p;
+ if (address == null) {
+ p = Proxy.NO_PROXY;
+ } else {
+ p = new Proxy(Proxy.Type.HTTP, address);
+ }
+ list = List.of(p);
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException e) {
+ /* ignore */
+ }
+
+ @Override
+ public synchronized List<Proxy> select(URI uri) {
+ String scheme = uri.getScheme().toLowerCase();
+ if (scheme.equals("http") || scheme.equals("https")) {
+ return list;
+ } else {
+ return NO_PROXY_LIST;
+ }
+ }
+ }
}
--- a/jdk/src/java.base/share/classes/java/net/SocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/SocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, 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
@@ -386,6 +386,9 @@
setOption(SocketOptions.SO_RCVBUF, value);
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
setOption(SocketOptions.SO_REUSEADDR, value);
+ } else if (name == StandardSocketOptions.SO_REUSEPORT &&
+ supportedOptions().contains(name)) {
+ setOption(SocketOptions.SO_REUSEPORT, value);
} else if (name == StandardSocketOptions.SO_LINGER &&
(getSocket() != null)) {
setOption(SocketOptions.SO_LINGER, value);
@@ -426,6 +429,9 @@
return (T)getOption(SocketOptions.SO_RCVBUF);
} else if (name == StandardSocketOptions.SO_REUSEADDR) {
return (T)getOption(SocketOptions.SO_REUSEADDR);
+ } else if (name == StandardSocketOptions.SO_REUSEPORT &&
+ supportedOptions().contains(name)) {
+ return (T)getOption(SocketOptions.SO_REUSEPORT);
} else if (name == StandardSocketOptions.SO_LINGER &&
(getSocket() != null)) {
return (T)getOption(SocketOptions.SO_LINGER);
--- a/jdk/src/java.base/share/classes/java/net/SocketOptions.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/SocketOptions.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -170,6 +170,17 @@
@Native public static final int SO_REUSEADDR = 0x04;
+ /** Sets SO_REUSEPORT for a socket. This option enables and disables
+ * the ability to have multiple sockets listen to the same address
+ * and port.
+ * <P>
+ * Valid for: SocketImpl, DatagramSocketImpl
+ *
+ * @since 9
+ * @see StandardSocketOptions#SO_REUSEPORT
+ */
+ @Native public static final int SO_REUSEPORT = 0x0E;
+
/**
* Sets SO_BROADCAST for a socket. This option enables and disables
* the ability of the process to send broadcast messages. It is supported
--- a/jdk/src/java.base/share/classes/java/net/StandardSocketOptions.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/StandardSocketOptions.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -187,6 +187,29 @@
new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
/**
+ * Re-use port.
+ *
+ * <p> The value of this socket option is a {@code Boolean} that represents
+ * whether the option is enabled or disabled. The exact semantics of this
+ * socket option are socket type and system dependent.
+ *
+ * <p> In the case of stream-oriented sockets, this socket option usually allows
+ * multiple listening sockets to be bound to both same address
+ * and same port.
+ *
+ * <p> For datagram-oriented sockets the socket option usually allows
+ * multiple UDP sockets to be bound to the same address and port.
+ *
+ * <p> An implementation allows this socket option to be set before the
+ * socket is bound or connected. Changing the value of this socket option
+ * after the socket is bound has no effect.
+ *
+ * @since 9
+ */
+ public static final SocketOption<Boolean> SO_REUSEPORT =
+ new StdSocketOption<Boolean>("SO_REUSEPORT", Boolean.class);
+
+ /**
* Linger on close if data is present.
*
* <p> The value of this socket option is an {@code Integer} that controls
--- a/jdk/src/java.base/share/classes/java/net/package-info.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/package-info.java Wed Jul 05 21:23:38 2017 +0200
@@ -121,7 +121,8 @@
* underlying protocol handlers like http or https.</li>
* <li>{@link java.net.HttpURLConnection} is a subclass of URLConnection
* and provides some additional functionalities specific to the
- * HTTP protocol.</li>
+ * HTTP protocol. This API has been superceded by the newer
+ HTTP client API described in the previous section.</li>
* </ul>
* <p>The recommended usage is to use {@link java.net.URI} to identify
* resources, then convert it into a {@link java.net.URL} when it is time to
--- a/jdk/src/java.base/share/classes/java/nio/Bits.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/Bits.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -807,31 +807,131 @@
}
}
- static void copyFromCharArray(Object src, long srcPos, long dstAddr,
- long length)
- {
- copyFromShortArray(src, srcPos, dstAddr, length);
+ /**
+ * Copy and unconditionally byte swap 16 bit elements from a heap array to off-heap memory
+ *
+ * @param src
+ * the source array, must be a 16-bit primitive array type
+ * @param srcPos
+ * byte offset within source array of the first element to read
+ * @param dstAddr
+ * destination address
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyFromCharArray(Object src, long srcPos, long dstAddr, long length) {
+ unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 2);
}
- static void copyToCharArray(long srcAddr, Object dst, long dstPos,
- long length)
- {
- copyToShortArray(srcAddr, dst, dstPos, length);
+ /**
+ * Copy and unconditionally byte swap 16 bit elements from off-heap memory to a heap array
+ *
+ * @param srcAddr
+ * source address
+ * @param dst
+ * destination array, must be a 16-bit primitive array type
+ * @param dstPos
+ * byte offset within the destination array of the first element to write
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyToCharArray(long srcAddr, Object dst, long dstPos, long length) {
+ unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 2);
+ }
+
+ /**
+ * Copy and unconditionally byte swap 16 bit elements from a heap array to off-heap memory
+ *
+ * @param src
+ * the source array, must be a 16-bit primitive array type
+ * @param srcPos
+ * byte offset within source array of the first element to read
+ * @param dstAddr
+ * destination address
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyFromShortArray(Object src, long srcPos, long dstAddr, long length) {
+ unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 2);
+ }
+
+ /**
+ * Copy and unconditionally byte swap 16 bit elements from off-heap memory to a heap array
+ *
+ * @param srcAddr
+ * source address
+ * @param dst
+ * destination array, must be a 16-bit primitive array type
+ * @param dstPos
+ * byte offset within the destination array of the first element to write
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyToShortArray(long srcAddr, Object dst, long dstPos, long length) {
+ unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 2);
}
- static native void copyFromShortArray(Object src, long srcPos, long dstAddr,
- long length);
- static native void copyToShortArray(long srcAddr, Object dst, long dstPos,
- long length);
+ /**
+ * Copy and unconditionally byte swap 32 bit elements from a heap array to off-heap memory
+ *
+ * @param src
+ * the source array, must be a 32-bit primitive array type
+ * @param srcPos
+ * byte offset within source array of the first element to read
+ * @param dstAddr
+ * destination address
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyFromIntArray(Object src, long srcPos, long dstAddr, long length) {
+ unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 4);
+ }
+
+ /**
+ * Copy and unconditionally byte swap 32 bit elements from off-heap memory to a heap array
+ *
+ * @param srcAddr
+ * source address
+ * @param dst
+ * destination array, must be a 32-bit primitive array type
+ * @param dstPos
+ * byte offset within the destination array of the first element to write
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyToIntArray(long srcAddr, Object dst, long dstPos, long length) {
+ unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 4);
+ }
- static native void copyFromIntArray(Object src, long srcPos, long dstAddr,
- long length);
- static native void copyToIntArray(long srcAddr, Object dst, long dstPos,
- long length);
+ /**
+ * Copy and unconditionally byte swap 64 bit elements from a heap array to off-heap memory
+ *
+ * @param src
+ * the source array, must be a 64-bit primitive array type
+ * @param srcPos
+ * byte offset within source array of the first element to read
+ * @param dstAddr
+ * destination address
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyFromLongArray(Object src, long srcPos, long dstAddr, long length) {
+ unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 8);
+ }
- static native void copyFromLongArray(Object src, long srcPos, long dstAddr,
- long length);
- static native void copyToLongArray(long srcAddr, Object dst, long dstPos,
- long length);
-
+ /**
+ * Copy and unconditionally byte swap 64 bit elements from off-heap memory to a heap array
+ *
+ * @param srcAddr
+ * source address
+ * @param dst
+ * destination array, must be a 64-bit primitive array type
+ * @param dstPos
+ * byte offset within the destination array of the first element to write
+ * @param length
+ * number of bytes to copy
+ */
+ static void copyToLongArray(long srcAddr, Object dst, long dstPos, long length) {
+ unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 8);
+ }
}
--- a/jdk/src/java.base/share/classes/java/util/Currency.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Currency.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -92,7 +92,12 @@
* and the remainder of entries in file are processed. For instances where duplicate
* country code entries exist, the behavior of the Currency information for that
* {@code Currency} is undefined and the remainder of entries in file are processed.
+ * <p>
+ * It is recommended to use {@link java.math.BigDecimal} class while dealing
+ * with {@code Currency} or monetary values as it provides better handling of floating
+ * point numbers and their operations.
*
+ * @see java.math.BigDecimal
* @since 1.4
*/
public final class Currency implements Serializable {
@@ -516,14 +521,16 @@
}
/**
- * Gets the default number of fraction digits used with this currency.
- * For example, the default number of fraction digits for the Euro is 2,
- * while for the Japanese Yen it's 0.
- * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
- * -1 is returned.
- *
- * @return the default number of fraction digits used with this currency
- */
+ * Gets the default number of fraction digits used with this currency.
+ * Note that the number of fraction digits is the same as ISO 4217's
+ * minor unit for the currency.
+ * For example, the default number of fraction digits for the Euro is 2,
+ * while for the Japanese Yen it's 0.
+ * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
+ * -1 is returned.
+ *
+ * @return the default number of fraction digits used with this currency
+ */
public int getDefaultFractionDigits() {
return defaultFractionDigits;
}
--- a/jdk/src/java.base/share/classes/java/util/jar/Attributes.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/jar/Attributes.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -646,5 +646,13 @@
* manifest attribute used for package versioning.
*/
public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
+
+ /**
+ * {@code Name} object for {@code Multi-Release}
+ * manifest attribute that indicates this is a multi-release JAR file.
+ *
+ * @since 9
+ */
+ public static final Name MULTI_RELEASE = new Name("Multi-Release");
}
}
--- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Wed Jul 05 21:23:38 2017 +0200
@@ -28,6 +28,7 @@
import java.io.*;
import java.lang.ref.SoftReference;
import java.net.URL;
+import java.security.PrivilegedAction;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -37,28 +38,91 @@
import java.security.AccessController;
import java.security.CodeSource;
import jdk.internal.misc.SharedSecrets;
-import sun.security.action.GetPropertyAction;
import sun.security.util.ManifestEntryVerifier;
import sun.security.util.SignatureFileVerifier;
+import static java.util.jar.Attributes.Name.MULTI_RELEASE;
+
/**
* The {@code JarFile} class is used to read the contents of a jar file
* from any file that can be opened with {@code java.io.RandomAccessFile}.
* It extends the class {@code java.util.zip.ZipFile} with support
- * for reading an optional {@code Manifest} entry. The
- * {@code Manifest} can be used to specify meta-information about the
- * jar file and its entries.
+ * for reading an optional {@code Manifest} entry, and support for
+ * processing multi-release jar files. The {@code Manifest} can be used
+ * to specify meta-information about the jar file and its entries.
+ *
+ * <p>A multi-release jar file is a jar file that contains
+ * a manifest with a main attribute named "Multi-Release",
+ * a set of "base" entries, some of which are public classes with public
+ * or protected methods that comprise the public interface of the jar file,
+ * and a set of "versioned" entries contained in subdirectories of the
+ * "META-INF/versions" directory. The versioned entries are partitioned by the
+ * major version of the Java platform. A versioned entry, with a version
+ * {@code n}, {@code 8 < n}, in the "META-INF/versions/{n}" directory overrides
+ * the base entry as well as any entry with a version number {@code i} where
+ * {@code 8 < i < n}.
+ *
+ * <p>By default, a {@code JarFile} for a multi-release jar file is configured
+ * to process the multi-release jar file as if it were a plain (unversioned) jar
+ * file, and as such an entry name is associated with at most one base entry.
+ * The {@code JarFile} may be configured to process a multi-release jar file by
+ * creating the {@code JarFile} with the
+ * {@link JarFile#JarFile(File, boolean, int, Release)} constructor. The
+ * {@code Release} object sets a maximum version used when searching for
+ * versioned entries. When so configured, an entry name
+ * can correspond with at most one base entry and zero or more versioned
+ * entries. A search is required to associate the entry name with the latest
+ * versioned entry whose version is less than or equal to the maximum version
+ * (see {@link #getEntry(String)}).
+ *
+ * <p>Class loaders that utilize {@code JarFile} to load classes from the
+ * contents of {@code JarFile} entries should construct the {@code JarFile}
+ * by invoking the {@link JarFile#JarFile(File, boolean, int, Release)}
+ * constructor with the value {@code Release.RUNTIME} assigned to the last
+ * argument. This assures that classes compatible with the major
+ * version of the running JVM are loaded from multi-release jar files.
+ *
+ * <p>If the verify flag is on when opening a signed jar file, the content of
+ * the file is verified against its signature embedded inside the file. Please
+ * note that the verification process does not include validating the signer's
+ * certificate. A caller should inspect the return value of
+ * {@link JarEntry#getCodeSigners()} to further determine if the signature
+ * can be trusted.
*
* <p> Unless otherwise noted, passing a {@code null} argument to a constructor
* or method in this class will cause a {@link NullPointerException} to be
* thrown.
*
- * If the verify flag is on when opening a signed jar file, the content of the
- * file is verified against its signature embedded inside the file. Please note
- * that the verification process does not include validating the signer's
- * certificate. A caller should inspect the return value of
- * {@link JarEntry#getCodeSigners()} to further determine if the signature
- * can be trusted.
+ * @implNote
+ * <div class="block">
+ * If the API can not be used to configure a {@code JarFile} (e.g. to override
+ * the configuration of a compiled application or library), two {@code System}
+ * properties are available.
+ * <ul>
+ * <li>
+ * {@code jdk.util.jar.version} can be assigned a value that is the
+ * {@code String} representation of a non-negative integer
+ * {@code <= Version.current().major()}. The value is used to set the effective
+ * runtime version to something other than the default value obtained by
+ * evaluating {@code Version.current().major()}. The effective runtime version
+ * is the version that the {@link JarFile#JarFile(File, boolean, int, Release)}
+ * constructor uses when the value of the last argument is
+ * {@code Release.RUNTIME}.
+ * </li>
+ * <li>
+ * {@code jdk.util.jar.enableMultiRelease} can be assigned one of the three
+ * {@code String} values <em>true</em>, <em>false</em>, or <em>force</em>. The
+ * value <em>true</em>, the default value, enables multi-release jar file
+ * processing. The value <em>false</em> disables multi-release jar processing,
+ * ignoring the "Multi-Release" manifest attribute, and the versioned
+ * directories in a multi-release jar file if they exist. Furthermore,
+ * the method {@link JarFile#isMultiRelease()} returns <em>false</em>. The value
+ * <em>force</em> causes the {@code JarFile} to be initialized to runtime
+ * versioning after construction. It effectively does the same as this code:
+ * {@code (new JarFile(File, boolean, int, Release.RUNTIME)}.
+ * </li>
+ * </ul>
+ * </div>
*
* @author David Connelly
* @see Manifest
@@ -68,26 +132,126 @@
*/
public
class JarFile extends ZipFile {
+ private final static int BASE_VERSION;
+ private final static int RUNTIME_VERSION;
+ private final static boolean MULTI_RELEASE_ENABLED;
+ private final static boolean MULTI_RELEASE_FORCED;
private SoftReference<Manifest> manRef;
private JarEntry manEntry;
private JarVerifier jv;
private boolean jvInitialized;
private boolean verify;
+ private final int version;
+ private boolean notVersioned;
+ private final boolean runtimeVersioned;
// indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
private boolean hasClassPathAttribute;
// true if manifest checked for special attributes
private volatile boolean hasCheckedSpecialAttributes;
- // Set up JavaUtilJarAccess in SharedSecrets
static {
+ // Set up JavaUtilJarAccess in SharedSecrets
SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
+
+ BASE_VERSION = 8; // one less than lowest version for versioned entries
+ RUNTIME_VERSION = AccessController.doPrivileged(
+ new PrivilegedAction<Integer>() {
+ public Integer run() {
+ Integer v = sun.misc.Version.jdkMajorVersion(); // fixme when JEP 223 Version integrated
+ Integer i = Integer.getInteger("jdk.util.jar.version", v);
+ i = i < 0 ? 0 : i;
+ return i > v ? v : i;
+ }
+ }
+ );
+ String multi_release = AccessController.doPrivileged(
+ new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("jdk.util.jar.enableMultiRelease", "true");
+ }
+ }
+ );
+ switch (multi_release) {
+ case "true":
+ default:
+ MULTI_RELEASE_ENABLED = true;
+ MULTI_RELEASE_FORCED = false;
+ break;
+ case "false":
+ MULTI_RELEASE_ENABLED = false;
+ MULTI_RELEASE_FORCED = false;
+ break;
+ case "force":
+ MULTI_RELEASE_ENABLED = true;
+ MULTI_RELEASE_FORCED = true;
+ break;
+ }
}
/**
+ * A set of constants that represent the entries in either the base directory
+ * or one of the versioned directories in a multi-release jar file. It's
+ * possible for a multi-release jar file to contain versioned directories
+ * that are not represented by the constants of the {@code Release} enum.
+ * In those cases, the entries will not be located by this {@code JarFile}
+ * through the aliasing mechanism, but they can be directly accessed by
+ * specifying the full path name of the entry.
+ *
+ * @since 9
+ */
+ public enum Release {
+ /**
+ * Represents unversioned entries, or entries in "regular", as opposed
+ * to multi-release jar files.
+ */
+ BASE(BASE_VERSION),
+
+ /**
+ * Represents entries found in the META-INF/versions/9 directory of a
+ * multi-release jar file.
+ */
+ VERSION_9(9),
+
+ // fill in the "blanks" for future releases
+
+ /**
+ * Represents entries found in the META-INF/versions/{n} directory of a
+ * multi-release jar file, where {@code n} is the effective runtime
+ * version of the jar file.
+ *
+ * @implNote
+ * <div class="block">
+ * The effective runtime version is determined
+ * by evaluating {@code Version.current().major()} or by using the value
+ * of the {@code jdk.util.jar.version} System property if it exists.
+ * </div>
+ */
+ RUNTIME(RUNTIME_VERSION);
+
+ Release(int version) {
+ this.version = version;
+ }
+
+ private static Release valueOf(int version) {
+ return version <= BASE.value() ? BASE : valueOf("VERSION_" + version);
+ }
+
+ private final int version;
+
+ private int value() {
+ return this.version;
+ }
+ }
+
+ private static final String META_INF = "META-INF/";
+
+ private static final String META_INF_VERSIONS = META_INF + "versions/";
+
+ /**
* The JAR manifest file name.
*/
- public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+ public static final String MANIFEST_NAME = META_INF + "MANIFEST.MF";
/**
* Creates a new {@code JarFile} to read from the specified
@@ -129,7 +293,6 @@
this(file, true, ZipFile.OPEN_READ);
}
-
/**
* Creates a new {@code JarFile} to read from the specified
* {@code File} object.
@@ -144,7 +307,6 @@
this(file, verify, ZipFile.OPEN_READ);
}
-
/**
* Creates a new {@code JarFile} to read from the specified
* {@code File} object in the specified mode. The mode argument
@@ -162,10 +324,104 @@
* @since 1.3
*/
public JarFile(File file, boolean verify, int mode) throws IOException {
+ this(file, verify, mode, Release.BASE);
+ this.notVersioned = true;
+ }
+
+ /**
+ * Creates a new {@code JarFile} to read from the specified
+ * {@code File} object in the specified mode. The mode argument
+ * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
+ * The version argument configures the {@code JarFile} for processing
+ * multi-release jar files.
+ *
+ * @param file the jar file to be opened for reading
+ * @param verify whether or not to verify the jar file if
+ * it is signed.
+ * @param mode the mode in which the file is to be opened
+ * @param version specifies the release version for a multi-release jar file
+ * @throws IOException if an I/O error has occurred
+ * @throws IllegalArgumentException
+ * if the {@code mode} argument is invalid
+ * @throws SecurityException if access to the file is denied
+ * by the SecurityManager
+ * @throws NullPointerException if {@code version} is {@code null}
+ * @since 9
+ */
+ public JarFile(File file, boolean verify, int mode, Release version) throws IOException {
super(file, mode);
+ Objects.requireNonNull(version);
this.verify = verify;
+ // version applies to multi-release jar files, ignored for regular jar files
+ this.version = MULTI_RELEASE_FORCED ? RUNTIME_VERSION : version.value();
+ this.runtimeVersioned = version == Release.RUNTIME;
+ assert runtimeVersionExists();
}
+ private boolean runtimeVersionExists() {
+ int version = sun.misc.Version.jdkMajorVersion(); // fixme when JEP 223 integrated
+ try {
+ Release.valueOf(version);
+ return true;
+ } catch (IllegalArgumentException x) {
+ System.err.println("No JarFile.Release object for release " + version);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the maximum version used when searching for versioned entries.
+ *
+ * @return the maximum version, or {@code Release.BASE} if this jar file is
+ * processed as if it is an unversioned jar file or is not a
+ * multi-release jar file
+ * @since 9
+ */
+ public final Release getVersion() {
+ if (isMultiRelease()) {
+ return runtimeVersioned ? Release.RUNTIME : Release.valueOf(version);
+ } else {
+ return Release.BASE;
+ }
+ }
+
+ /**
+ * Indicates whether or not this jar file is a multi-release jar file.
+ *
+ * @return true if this JarFile is a multi-release jar file
+ * @since 9
+ */
+ public final boolean isMultiRelease() {
+ // do not call this code in a constructor because some subclasses use
+ // lazy loading of manifest so it won't be available at construction time
+ if (MULTI_RELEASE_ENABLED) {
+ // Doubled-checked locking pattern
+ Boolean result = isMultiRelease;
+ if (result == null) {
+ synchronized (this) {
+ result = isMultiRelease;
+ if (result == null) {
+ Manifest man = null;
+ try {
+ man = getManifest();
+ } catch (IOException e) {
+ //Ignored, manifest cannot be read
+ }
+ isMultiRelease = result = (man != null)
+ && man.getMainAttributes().containsKey(MULTI_RELEASE)
+ ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+ }
+ return result == Boolean.TRUE;
+ } else {
+ return false;
+ }
+ }
+ // the following field, isMultiRelease, should only be used in the method
+ // isMultiRelease(), like a static local
+ private volatile Boolean isMultiRelease; // is jar multi-release?
+
/**
* Returns the jar file manifest, or {@code null} if none.
*
@@ -209,40 +465,87 @@
}
/**
- * Returns the {@code JarEntry} for the given entry name or
+ * Returns the {@code JarEntry} for the given base entry name or
* {@code null} if not found.
*
+ * <p>If this {@code JarFile} is a multi-release jar file and is configured
+ * to be processed as such, then a search is performed to find and return
+ * a {@code JarEntry} that is the latest versioned entry associated with the
+ * given entry name. The returned {@code JarEntry} is the versioned entry
+ * corresponding to the given base entry name prefixed with the string
+ * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
+ * which an entry exists. If such a versioned entry does not exist, then
+ * the {@code JarEntry} for the base entry is returned, otherwise
+ * {@code null} is returned if no entries are found. The initial value for
+ * the version {@code n} is the maximum version as returned by the method
+ * {@link JarFile#getVersion()}.
+ *
* @param name the jar file entry name
- * @return the {@code JarEntry} for the given entry name or
- * {@code null} if not found.
+ * @return the {@code JarEntry} for the given entry name, or
+ * the versioned entry name, or {@code null} if not found
*
* @throws IllegalStateException
* may be thrown if the jar file has been closed
*
* @see java.util.jar.JarEntry
+ *
+ * @implSpec
+ * <div class="block">
+ * This implementation invokes {@link JarFile#getEntry(String)}.
+ * </div>
*/
public JarEntry getJarEntry(String name) {
return (JarEntry)getEntry(name);
}
/**
- * Returns the {@code ZipEntry} for the given entry name or
+ * Returns the {@code ZipEntry} for the given base entry name or
* {@code null} if not found.
*
+ * <p>If this {@code JarFile} is a multi-release jar file and is configured
+ * to be processed as such, then a search is performed to find and return
+ * a {@code ZipEntry} that is the latest versioned entry associated with the
+ * given entry name. The returned {@code ZipEntry} is the versioned entry
+ * corresponding to the given base entry name prefixed with the string
+ * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
+ * which an entry exists. If such a versioned entry does not exist, then
+ * the {@code ZipEntry} for the base entry is returned, otherwise
+ * {@code null} is returned if no entries are found. The initial value for
+ * the version {@code n} is the maximum version as returned by the method
+ * {@link JarFile#getVersion()}.
+ *
* @param name the jar file entry name
* @return the {@code ZipEntry} for the given entry name or
- * {@code null} if not found
+ * the versioned entry name or {@code null} if not found
*
* @throws IllegalStateException
* may be thrown if the jar file has been closed
*
* @see java.util.zip.ZipEntry
+ *
+ * @implSpec
+ * <div class="block">
+ * This implementation may return a versioned entry for the requested name
+ * even if there is not a corresponding base entry. This can occur
+ * if there is a private or package-private versioned entry that matches.
+ * If a subclass overrides this method, assure that the override method
+ * invokes {@code super.getEntry(name)} to obtain all versioned entries.
+ * </div>
*/
public ZipEntry getEntry(String name) {
ZipEntry ze = super.getEntry(name);
if (ze != null) {
return new JarFileEntry(ze);
}
+ // no matching base entry, but maybe there is a versioned entry,
+ // like a new private class
+ if (isMultiRelease()) {
+ ze = new ZipEntry(name);
+ ZipEntry vze = getVersionedEntry(ze);
+ if (ze != vze) {
+ return new JarFileEntry(name, vze);
+ }
+ }
return null;
}
@@ -250,14 +553,42 @@
Iterator<JarEntry>
{
final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
+ ZipEntry ze;
public boolean hasNext() {
- return e.hasMoreElements();
+ if (notVersioned) {
+ return e.hasMoreElements();
+ }
+ if (ze != null) {
+ return true;
+ }
+ return findNext();
+ }
+
+ private boolean findNext() {
+ while (e.hasMoreElements()) {
+ ZipEntry ze2 = e.nextElement();
+ if (!ze2.getName().startsWith(META_INF_VERSIONS)) {
+ ze = ze2;
+ return true;
+ }
+ }
+ return false;
}
public JarEntry next() {
- ZipEntry ze = e.nextElement();
- return new JarFileEntry(ze);
+ ZipEntry ze2;
+
+ if (notVersioned) {
+ ze2 = e.nextElement();
+ return new JarFileEntry(ze2.getName(), ze2);
+ }
+ if (ze != null || findNext()) {
+ ze2 = ze;
+ ze = null;
+ return new JarFileEntry(ze2);
+ }
+ throw new NoSuchElementException();
}
public boolean hasMoreElements() {
@@ -274,7 +605,19 @@
}
/**
- * Returns an enumeration of the jar file entries.
+ * Returns an enumeration of the jar file entries. The set of entries
+ * returned depends on whether or not the jar file is a multi-release jar
+ * file, and on the constructor used to create the {@code JarFile}. If the
+ * jar file is not a multi-release jar file, all entries are returned,
+ * regardless of how the {@code JarFile} is created. If the constructor
+ * does not take a {@code Release} argument, all entries are returned.
+ * If the jar file is a multi-release jar file and the constructor takes a
+ * {@code Release} argument, then the set of entries returned is equivalent
+ * to the set of entries that would be returned if the set was built by
+ * invoking {@link JarFile#getEntry(String)} or
+ * {@link JarFile#getJarEntry(String)} with the name of each base entry in
+ * the jar file. A base entry is an entry whose path name does not start
+ * with "META-INF/versions/".
*
* @return an enumeration of the jar file entries
* @throws IllegalStateException
@@ -285,10 +628,21 @@
}
/**
- * Returns an ordered {@code Stream} over the jar file entries.
+ * Returns an ordered {@code Stream} over all the jar file entries.
* Entries appear in the {@code Stream} in the order they appear in
- * the central directory of the jar file.
- *
+ * the central directory of the jar file. The set of entries
+ * returned depends on whether or not the jar file is a multi-release jar
+ * file, and on the constructor used to create the {@code JarFile}. If the
+ * jar file is not a multi-release jar file, all entries are returned,
+ * regardless of how the {@code JarFile} is created. If the constructor
+ * does not take a {@code Release} argument, all entries are returned.
+ * If the jar file is a multi-release jar file and the constructor takes a
+ * {@code Release} argument, then the set of entries returned is equivalent
+ * to the set of entries that would be returned if the set was built by
+ * invoking {@link JarFile#getEntry(String)} or
+ * {@link JarFile#getJarEntry(String)} with the name of each base entry in
+ * the jar file. A base entry is an entry whose path name does not start
+ * with "META-INF/versions/".
* @return an ordered {@code Stream} of entries in this jar file
* @throws IllegalStateException if the jar file has been closed
* @since 1.8
@@ -300,14 +654,44 @@
Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
}
+ private ZipEntry searchForVersionedEntry(final int version, String name) {
+ ZipEntry vze = null;
+ String sname = "/" + name;
+ int i = version;
+ while (i > BASE_VERSION) {
+ vze = super.getEntry(META_INF_VERSIONS + i + sname);
+ if (vze != null) break;
+ i--;
+ }
+ return vze;
+ }
+
+ private ZipEntry getVersionedEntry(ZipEntry ze) {
+ ZipEntry vze = null;
+ if (version > BASE_VERSION && !ze.isDirectory()) {
+ String name = ze.getName();
+ if (!name.startsWith(META_INF)) {
+ vze = searchForVersionedEntry(version, name);
+ }
+ }
+ return vze == null ? ze : vze;
+ }
+
private class JarFileEntry extends JarEntry {
+ final private String name;
+
JarFileEntry(ZipEntry ze) {
- super(ze);
+ super(isMultiRelease() ? getVersionedEntry(ze) : ze);
+ this.name = ze.getName();
+ }
+ JarFileEntry(String name, ZipEntry vze) {
+ super(vze);
+ this.name = name;
}
public Attributes getAttributes() throws IOException {
Manifest man = JarFile.this.getManifest();
if (man != null) {
- return man.getAttributes(getName());
+ return man.getAttributes(super.getName());
} else {
return null;
}
@@ -319,7 +703,7 @@
throw new RuntimeException(e);
}
if (certs == null && jv != null) {
- certs = jv.getCerts(JarFile.this, this);
+ certs = jv.getCerts(JarFile.this, reifiedEntry());
}
return certs == null ? null : certs.clone();
}
@@ -330,10 +714,22 @@
throw new RuntimeException(e);
}
if (signers == null && jv != null) {
- signers = jv.getCodeSigners(JarFile.this, this);
+ signers = jv.getCodeSigners(JarFile.this, reifiedEntry());
}
return signers == null ? null : signers.clone();
}
+ JarFileEntry reifiedEntry() {
+ if (isMultiRelease()) {
+ String entryName = super.getName();
+ return entryName.equals(this.name) ? this : new JarFileEntry(entryName, this);
+ }
+ return this;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
}
/*
@@ -491,12 +887,19 @@
// wrap a verifier stream around the real stream
return new JarVerifier.VerifierStream(
getManifestFromReference(),
- ze instanceof JarFileEntry ?
- (JarEntry) ze : getJarEntry(ze.getName()),
+ verifiableEntry(ze),
super.getInputStream(ze),
jv);
}
+ private JarEntry verifiableEntry(ZipEntry ze) {
+ if (!(ze instanceof JarFileEntry)) {
+ ze = getJarEntry(ze.getName());
+ }
+ // assure the name and entry match for verification
+ return ze == null ? null : ((JarFileEntry)ze).reifiedEntry();
+ }
+
// Statics for hand-coded Boyer-Moore search
private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
// The bad character shift for "class-path"
@@ -523,7 +926,7 @@
private JarEntry getManEntry() {
if (manEntry == null) {
// First look up manifest entry using standard name
- manEntry = getJarEntry(MANIFEST_NAME);
+ ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
if (manEntry == null) {
// If not found, then iterate through all the "META-INF/"
// entries to find a match.
@@ -531,12 +934,15 @@
if (names != null) {
for (String name : names) {
if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
- manEntry = getJarEntry(name);
+ manEntry = super.getEntry(name);
break;
}
}
}
}
+ this.manEntry = (manEntry == null)
+ ? null
+ : new JarFileEntry(manEntry.getName(), manEntry);
}
return manEntry;
}
--- a/jdk/src/java.base/share/classes/javax/crypto/JceSecurity.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/javax/crypto/JceSecurity.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -64,8 +64,7 @@
private static final Map<Provider, Object> verifyingProviders =
new IdentityHashMap<>();
- // Set the default value. May be changed in the static initializer.
- private static boolean isRestricted = true;
+ private static final boolean isRestricted;
/*
* Don't let anyone instantiate this.
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/Signal.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/Signal.java Wed Jul 05 21:23:38 2017 +0200
@@ -279,6 +279,11 @@
public void handle(Signal sig) {
throw new UnsupportedOperationException("invoking native signal handle not supported");
}
+
+ public String toString() {
+ return this == SIG_DFL ? "SIG_DFL" :
+ (this == SIG_IGN ? "SIG_IGN" : super.toString());
+ }
}
}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -458,6 +458,78 @@
copyMemory(null, srcAddress, null, destAddress, bytes);
}
+ private boolean isPrimitiveArray(Class<?> c) {
+ Class<?> componentType = c.getComponentType();
+ return componentType != null && componentType.isPrimitive();
+ }
+
+ private native void copySwapMemory0(Object srcBase, long srcOffset,
+ Object destBase, long destOffset,
+ long bytes, long elemSize);
+
+ /**
+ * Copies all elements from one block of memory to another block,
+ * *unconditionally* byte swapping the elements on the fly.
+ *
+ * <p>This method determines each block's base address by means of two parameters,
+ * and so it provides (in effect) a <em>double-register</em> addressing mode,
+ * as discussed in {@link #getInt(Object,long)}. When the object reference is null,
+ * the offset supplies an absolute base address.
+ *
+ * @since 9
+ */
+ public void copySwapMemory(Object srcBase, long srcOffset,
+ Object destBase, long destOffset,
+ long bytes, long elemSize) {
+ if (bytes < 0) {
+ throw new IllegalArgumentException();
+ }
+ if (elemSize != 2 && elemSize != 4 && elemSize != 8) {
+ throw new IllegalArgumentException();
+ }
+ if (bytes % elemSize != 0) {
+ throw new IllegalArgumentException();
+ }
+ if ((srcBase == null && srcOffset == 0) ||
+ (destBase == null && destOffset == 0)) {
+ throw new NullPointerException();
+ }
+
+ // Must be off-heap, or primitive heap arrays
+ if (srcBase != null && (srcOffset < 0 || !isPrimitiveArray(srcBase.getClass()))) {
+ throw new IllegalArgumentException();
+ }
+ if (destBase != null && (destOffset < 0 || !isPrimitiveArray(destBase.getClass()))) {
+ throw new IllegalArgumentException();
+ }
+
+ // Sanity check size and offsets on 32-bit platforms. Most
+ // significant 32 bits must be zero.
+ if (ADDRESS_SIZE == 4 &&
+ (bytes >>> 32 != 0 || srcOffset >>> 32 != 0 || destOffset >>> 32 != 0)) {
+ throw new IllegalArgumentException();
+ }
+
+ if (bytes == 0) {
+ return;
+ }
+
+ copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
+ }
+
+ /**
+ * Copies all elements from one block of memory to another block, byte swapping the
+ * elements on the fly.
+ *
+ * This provides a <em>single-register</em> addressing mode, as
+ * discussed in {@link #getInt(Object,long)}.
+ *
+ * Equivalent to {@code copySwapMemory(null, srcAddress, null, destAddress, bytes, elemSize)}.
+ */
+ public void copySwapMemory(long srcAddress, long destAddress, long bytes, long elemSize) {
+ copySwapMemory(null, srcAddress, null, destAddress, bytes, elemSize);
+ }
+
/**
* Disposes of a block of native memory, as obtained from {@link
* #allocateMemory} or {@link #reallocateMemory}. The address passed to
@@ -1159,7 +1231,12 @@
/** @see #getLongUnaligned(Object, long) */
@HotSpotIntrinsicCandidate
public final char getCharUnaligned(Object o, long offset) {
- return (char)getShortUnaligned(o, offset);
+ if ((offset & 1) == 0) {
+ return getChar(o, offset);
+ } else {
+ return (char)makeShort(getByte(o, offset),
+ getByte(o, offset + 1));
+ }
}
/** @see #getLongUnaligned(Object, long, boolean) */
--- a/jdk/src/java.base/share/classes/jdk/net/Sockets.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/jdk/net/Sockets.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -251,9 +251,23 @@
}
}
+ private static volatile boolean checkedReusePort;
+ private static volatile boolean isReusePortAvailable;
+
+ /**
+ * Tells whether SO_REUSEPORT is supported.
+ */
+ static boolean isReusePortAvailable() {
+ if (!checkedReusePort) {
+ isReusePortAvailable = isReusePortAvailable0();
+ checkedReusePort = true;
+ }
+ return isReusePortAvailable;
+ }
+
private static void initOptionSets() {
boolean flowsupported = ExtendedOptionsImpl.flowSupported();
-
+ boolean reuseportsupported = isReusePortAvailable();
// Socket
Set<SocketOption<?>> set = new HashSet<>();
@@ -261,6 +275,9 @@
set.add(StandardSocketOptions.SO_SNDBUF);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (reuseportsupported) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.SO_LINGER);
set.add(StandardSocketOptions.IP_TOS);
set.add(StandardSocketOptions.TCP_NODELAY);
@@ -275,6 +292,9 @@
set = new HashSet<>();
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (reuseportsupported) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.IP_TOS);
set = Collections.unmodifiableSet(set);
options.put(ServerSocket.class, set);
@@ -285,6 +305,9 @@
set.add(StandardSocketOptions.SO_SNDBUF);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (reuseportsupported) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.IP_TOS);
if (flowsupported) {
set.add(ExtendedSocketOptions.SO_FLOW_SLA);
@@ -298,6 +321,9 @@
set.add(StandardSocketOptions.SO_SNDBUF);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (reuseportsupported) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.IP_TOS);
set.add(StandardSocketOptions.IP_MULTICAST_IF);
set.add(StandardSocketOptions.IP_MULTICAST_TTL);
@@ -308,4 +334,6 @@
set = Collections.unmodifiableSet(set);
options.put(MulticastSocket.class, set);
}
+
+ private static native boolean isReusePortAvailable0();
}
--- a/jdk/src/java.base/share/classes/sun/misc/URLClassPath.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/misc/URLClassPath.java Wed Jul 05 21:23:38 2017 +0200
@@ -63,6 +63,7 @@
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
+import java.util.zip.ZipFile;
import jdk.internal.jimage.ImageLocation;
import jdk.internal.jimage.ImageReader;
@@ -727,9 +728,10 @@
if (!p.exists()) {
throw new FileNotFoundException(p.getPath());
}
- return checkJar(new JarFile(p.getPath()));
+ return checkJar(new JarFile(new File(p.getPath()), true, ZipFile.OPEN_READ,
+ JarFile.Release.RUNTIME));
}
- URLConnection uc = getBaseURL().openConnection();
+ URLConnection uc = (new URL(getBaseURL(), "#runtime")).openConnection();
uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION);
JarFile jarFile = ((JarURLConnection)uc).getJarFile();
return checkJar(jarFile);
@@ -756,7 +758,9 @@
final URL url;
try {
- url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
+ // add #runtime fragment to tell JarURLConnection to use
+ // runtime versioning if the underlying jar file is multi-release
+ url = new URL(getBaseURL(), ParseUtil.encodePath(name, false) + "#runtime");
if (check) {
URLClassPath.check(url);
}
--- a/jdk/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java Wed Jul 05 21:23:38 2017 +0200
@@ -65,9 +65,10 @@
}
static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
- if (isFileURL(url))
- return new URLJarFile(url, closeController);
- else {
+ if (isFileURL(url)) {
+ Release version = "runtime".equals(url.getRef()) ? Release.RUNTIME : Release.BASE;
+ return new URLJarFile(url, closeController, version);
+ } else {
return retrieve(url, closeController);
}
}
@@ -89,8 +90,13 @@
this.closeController = closeController;
}
- private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
- super(ParseUtil.decode(url.getFile()));
+ private URLJarFile(File file, URLJarFileCloseController closeController, Release version) throws IOException {
+ super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE, version);
+ this.closeController = closeController;
+ }
+
+ private URLJarFile(URL url, URLJarFileCloseController closeController, Release version) throws IOException {
+ super(new File(ParseUtil.decode(url.getFile())), true, ZipFile.OPEN_READ, version);
this.closeController = closeController;
}
@@ -179,14 +185,6 @@
* Given a URL, retrieves a JAR file, caches it to disk, and creates a
* cached JAR file object.
*/
- private static JarFile retrieve(final URL url) throws IOException {
- return retrieve(url, null);
- }
-
- /**
- * Given a URL, retrieves a JAR file, caches it to disk, and creates a
- * cached JAR file object.
- */
private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException {
/*
* See if interface is set, then call retrieve function of the class
@@ -202,6 +200,7 @@
{
JarFile result = null;
+ Release version = "runtime".equals(url.getRef()) ? Release.RUNTIME : Release.BASE;
/* get the stream before asserting privileges */
try (final InputStream in = url.openConnection().getInputStream()) {
@@ -211,7 +210,7 @@
Path tmpFile = Files.createTempFile("jar_cache", null);
try {
Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
- JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController);
+ JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController, version);
tmpFile.toFile().deleteOnExit();
return jarFile;
} catch (Throwable thr) {
--- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -231,6 +231,9 @@
HashSet<SocketOption<?>> set = new HashSet<>(2);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (Net.isReusePortAvailable()) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
return Collections.unmodifiableSet(set);
}
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -508,6 +508,9 @@
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_KEEPALIVE);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (Net.isReusePortAvailable()) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.TCP_NODELAY);
if (ExtendedOptionsImpl.flowSupported()) {
set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA);
--- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -298,6 +298,9 @@
set.add(StandardSocketOptions.SO_SNDBUF);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (Net.isReusePortAvailable()) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.SO_BROADCAST);
set.add(StandardSocketOptions.IP_TOS);
set.add(StandardSocketOptions.IP_MULTICAST_IF);
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -56,6 +56,8 @@
private static volatile boolean checkedIPv6;
private static volatile boolean isIPv6Available;
+ private static volatile boolean checkedReusePort;
+ private static volatile boolean isReusePortAvailable;
/**
* Tells whether dual-IPv4/IPv6 sockets should be used.
@@ -69,6 +71,17 @@
}
/**
+ * Tells whether SO_REUSEPORT is supported.
+ */
+ static boolean isReusePortAvailable() {
+ if (!checkedReusePort) {
+ isReusePortAvailable = isReusePortAvailable0();
+ checkedReusePort = true;
+ }
+ return isReusePortAvailable;
+ }
+
+ /**
* Returns true if exclusive binding is on
*/
static boolean useExclusiveBind() {
@@ -389,6 +402,8 @@
private static native boolean isIPv6Available0();
+ private static native boolean isReusePortAvailable0();
+
/*
* Returns 1 for Windows and -1 for Solaris/Linux/Mac OS
*/
--- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -33,7 +33,6 @@
import java.util.*;
import sun.net.NetHooks;
-
/**
* An implementation of ServerSocketChannels
*/
@@ -185,6 +184,9 @@
HashSet<SocketOption<?>> set = new HashSet<>(2);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (Net.isReusePortAvailable()) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.IP_TOS);
return Collections.unmodifiableSet(set);
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -234,6 +234,9 @@
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_KEEPALIVE);
set.add(StandardSocketOptions.SO_REUSEADDR);
+ if (Net.isReusePortAvailable()) {
+ set.add(StandardSocketOptions.SO_REUSEPORT);
+ }
set.add(StandardSocketOptions.SO_LINGER);
set.add(StandardSocketOptions.TCP_NODELAY);
// additional options required by socket adaptor
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -81,9 +81,6 @@
private boolean serverKeyExchangeReceived;
- private final boolean enableStatusRequestExtension =
- Debug.getBooleanProperty(
- "jdk.tls.client.enableStatusRequestExtension", true);
private boolean staplingActive = false;
private X509Certificate[] deferredCerts;
@@ -761,7 +758,7 @@
type == ExtensionType.EXT_STATUS_REQUEST_V2) {
// Only enable the stapling feature if the client asserted
// these extensions.
- if (enableStatusRequestExtension) {
+ if (sslContext.isStaplingEnabled(true)) {
staplingActive = true;
} else {
fatalSE(Alerts.alert_unexpected_message, "Server set " +
@@ -1562,7 +1559,7 @@
}
// Add status_request and status_request_v2 extensions
- if (enableStatusRequestExtension) {
+ if (sslContext.isStaplingEnabled(true)) {
clientHelloMessage.addCertStatusReqListV2Extension();
clientHelloMessage.addCertStatusRequestExtension();
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, 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
@@ -55,7 +55,11 @@
// DTLS cookie exchange manager
private volatile HelloCookieManager helloCookieManager;
- private StatusResponseManager statusResponseManager;
+ private final boolean clientEnableStapling = Debug.getBooleanProperty(
+ "jdk.tls.client.enableStatusRequestExtension", true);
+ private final boolean serverEnableStapling = Debug.getBooleanProperty(
+ "jdk.tls.server.enableStatusRequestExtension", false);
+ private volatile StatusResponseManager statusResponseManager;
SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
@@ -80,7 +84,6 @@
}
}
trustManager = chooseTrustManager(tm);
- statusResponseManager = new StatusResponseManager();
if (sr == null) {
secureRandom = JsseJce.getSecureRandom();
@@ -258,6 +261,18 @@
}
StatusResponseManager getStatusResponseManager() {
+ if (serverEnableStapling && statusResponseManager == null) {
+ synchronized (this) {
+ if (statusResponseManager == null) {
+ if (debug != null && Debug.isOn("sslctx")) {
+ System.out.println(
+ "Initializing StatusResponseManager");
+ }
+ statusResponseManager = new StatusResponseManager();
+ }
+ }
+ }
+
return statusResponseManager;
}
@@ -309,6 +324,18 @@
(cipherSuites == getClientDefaultCipherSuiteList());
}
+ /**
+ * Return whether client or server side stapling has been enabled
+ * for this SSLContextImpl
+ * @param isClient true if the caller is operating in a client side role,
+ * false if acting as a server.
+ * @return true if stapling has been enabled for the specified role, false
+ * otherwise.
+ */
+ boolean isStaplingEnabled(boolean isClient) {
+ return isClient ? clientEnableStapling : serverEnableStapling;
+ }
+
/*
* Return the list of all available CipherSuites with a priority of
* minPriority or above.
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -118,10 +118,6 @@
LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS,
new SSLAlgorithmDecomposer());
- // To switch off the status_request[_v2] extensions
- private static final boolean enableStatusRequestExtension =
- Debug.getBooleanProperty(
- "jdk.tls.server.enableStatusRequestExtension", false);
private boolean staplingActive = false;
private long statusRespTimeout;
@@ -589,7 +585,7 @@
(CertStatusReqListV2Extension)mesg.extensions.get(
ExtensionType.EXT_STATUS_REQUEST_V2);
// Keep stapling active if at least one of the extensions has been set
- staplingActive = enableStatusRequestExtension &&
+ staplingActive = sslContext.isStaplingEnabled(false) &&
(statReqExt != null || statReqExtV2 != null);
/*
@@ -932,19 +928,32 @@
}
if (statReqType != null && statReqData != null) {
- // Next, attempt to obtain status responses
StatusResponseManager statRespMgr =
sslContext.getStatusResponseManager();
- responseMap = statRespMgr.get(statReqType, statReqData, certs,
- statusRespTimeout, TimeUnit.MILLISECONDS);
- if (!responseMap.isEmpty()) {
- // We now can safely assert status_request[_v2] in our
- // ServerHello, and know for certain that we can provide
- // responses back to this client for this connection.
- if (statusRespExt == ExtensionType.EXT_STATUS_REQUEST) {
- m1.extensions.add(new CertStatusReqExtension());
- } else if (statusRespExt == ExtensionType.EXT_STATUS_REQUEST_V2) {
- m1.extensions.add(new CertStatusReqListV2Extension());
+ if (statRespMgr != null) {
+ responseMap = statRespMgr.get(statReqType, statReqData,
+ certs, statusRespTimeout, TimeUnit.MILLISECONDS);
+ if (!responseMap.isEmpty()) {
+ // We now can safely assert status_request[_v2] in our
+ // ServerHello, and know for certain that we can provide
+ // responses back to this client for this connection.
+ if (statusRespExt == ExtensionType.EXT_STATUS_REQUEST) {
+ m1.extensions.add(new CertStatusReqExtension());
+ } else if (statusRespExt ==
+ ExtensionType.EXT_STATUS_REQUEST_V2) {
+ m1.extensions.add(
+ new CertStatusReqListV2Extension());
+ }
+ }
+ } else {
+ // This should not happen if stapling is active, but
+ // if lazy initialization of the StatusResponseManager
+ // doesn't occur we should turn off stapling.
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Warning: lazy initialization " +
+ "of the StatusResponseManager failed. " +
+ "Stapling has been disabled.");
+ staplingActive = false;
}
}
}
--- a/jdk/src/java.base/share/native/libjava/Bits.c Thu Feb 25 09:41:40 2016 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,233 +0,0 @@
-/*
- * Copyright (c) 2002, 2010, 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.
- */
-
-/*
- */
-
-#include "jni.h"
-#include "jni_util.h"
-#include "jlong.h"
-#include <string.h>
-
-#define MBYTE 1048576
-
-#define GETCRITICAL_OR_RETURN(bytes, env, obj) { \
- bytes = (*env)->GetPrimitiveArrayCritical(env, obj, NULL); \
- if (bytes == NULL) { \
- if ((*env)->ExceptionOccurred(env) == NULL) \
- JNU_ThrowInternalError(env, "Unable to get array"); \
- return; \
- } \
-}
-
-#define RELEASECRITICAL(bytes, env, obj, mode) { \
- (*env)->ReleasePrimitiveArrayCritical(env, obj, bytes, mode); \
-}
-
-#define SWAPSHORT(x) ((jshort)(((x) << 8) | (((x) >> 8) & 0xff)))
-#define SWAPINT(x) ((jint)((SWAPSHORT((jshort)(x)) << 16) | \
- (SWAPSHORT((jshort)((x) >> 16)) & 0xffff)))
-#define SWAPLONG(x) ((jlong)(((jlong)SWAPINT((jint)(x)) << 32) | \
- ((jlong)SWAPINT((jint)((x) >> 32)) & 0xffffffff)))
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyFromShortArray(JNIEnv *env, jclass clazz, jobject src,
- jlong srcPos, jlong dstAddr, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jshort *srcShort, *dstShort, *endShort;
- jshort tmpShort;
-
- dstShort = (jshort *)jlong_to_ptr(dstAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, src);
-
- srcShort = (jshort *)(bytes + srcPos);
- endShort = srcShort + (size / sizeof(jshort));
- while (srcShort < endShort) {
- tmpShort = *srcShort++;
- *dstShort++ = SWAPSHORT(tmpShort);
- }
-
- RELEASECRITICAL(bytes, env, src, JNI_ABORT);
-
- length -= size;
- srcPos += size;
- }
-}
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyToShortArray(JNIEnv *env, jclass clazz, jlong srcAddr,
- jobject dst, jlong dstPos, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jshort *srcShort, *dstShort, *endShort;
- jshort tmpShort;
-
- srcShort = (jshort *)jlong_to_ptr(srcAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, dst);
-
- dstShort = (jshort *)(bytes + dstPos);
- endShort = srcShort + (size / sizeof(jshort));
- while (srcShort < endShort) {
- tmpShort = *srcShort++;
- *dstShort++ = SWAPSHORT(tmpShort);
- }
-
- RELEASECRITICAL(bytes, env, dst, 0);
-
- length -= size;
- dstPos += size;
- }
-}
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyFromIntArray(JNIEnv *env, jclass clazz, jobject src,
- jlong srcPos, jlong dstAddr, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jint *srcInt, *dstInt, *endInt;
- jint tmpInt;
-
- dstInt = (jint *)jlong_to_ptr(dstAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, src);
-
- srcInt = (jint *)(bytes + srcPos);
- endInt = srcInt + (size / sizeof(jint));
- while (srcInt < endInt) {
- tmpInt = *srcInt++;
- *dstInt++ = SWAPINT(tmpInt);
- }
-
- RELEASECRITICAL(bytes, env, src, JNI_ABORT);
-
- length -= size;
- srcPos += size;
- }
-}
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyToIntArray(JNIEnv *env, jclass clazz, jlong srcAddr,
- jobject dst, jlong dstPos, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jint *srcInt, *dstInt, *endInt;
- jint tmpInt;
-
- srcInt = (jint *)jlong_to_ptr(srcAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, dst);
-
- dstInt = (jint *)(bytes + dstPos);
- endInt = srcInt + (size / sizeof(jint));
- while (srcInt < endInt) {
- tmpInt = *srcInt++;
- *dstInt++ = SWAPINT(tmpInt);
- }
-
- RELEASECRITICAL(bytes, env, dst, 0);
-
- length -= size;
- dstPos += size;
- }
-}
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyFromLongArray(JNIEnv *env, jclass clazz, jobject src,
- jlong srcPos, jlong dstAddr, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jlong *srcLong, *dstLong, *endLong;
- jlong tmpLong;
-
- dstLong = (jlong *)jlong_to_ptr(dstAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, src);
-
- srcLong = (jlong *)(bytes + srcPos);
- endLong = srcLong + (size / sizeof(jlong));
- while (srcLong < endLong) {
- tmpLong = *srcLong++;
- *dstLong++ = SWAPLONG(tmpLong);
- }
-
- RELEASECRITICAL(bytes, env, src, JNI_ABORT);
-
- length -= size;
- srcPos += size;
- }
-}
-
-JNIEXPORT void JNICALL
-Java_java_nio_Bits_copyToLongArray(JNIEnv *env, jclass clazz, jlong srcAddr,
- jobject dst, jlong dstPos, jlong length)
-{
- jbyte *bytes;
- size_t size;
- jlong *srcLong, *dstLong, *endLong;
- jlong tmpLong;
-
- srcLong = (jlong *)jlong_to_ptr(srcAddr);
-
- while (length > 0) {
- size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
-
- GETCRITICAL_OR_RETURN(bytes, env, dst);
-
- dstLong = (jlong *)(bytes + dstPos);
- endLong = srcLong + (size / sizeof(jlong));
- while (srcLong < endLong) {
- tmpLong = *srcLong++;
- *dstLong++ = SWAPLONG(tmpLong);
- }
-
- RELEASECRITICAL(bytes, env, dst, 0);
-
- length -= size;
- dstPos += size;
- }
-}
--- a/jdk/src/java.base/share/native/libnet/net_util.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/native/libnet/net_util.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -29,14 +29,21 @@
#include "net_util.h"
int IPv6_supported() ;
+int reuseport_supported() ;
static int IPv6_available;
+static int REUSEPORT_available;
JNIEXPORT jint JNICALL ipv6_available()
{
return IPv6_available ;
}
+JNIEXPORT jint JNICALL reuseport_available()
+{
+ return REUSEPORT_available;
+}
+
JNIEXPORT jint JNICALL
DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
{
@@ -45,7 +52,6 @@
jmethodID mid;
jstring s;
jint preferIPv4Stack;
-
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) {
return JNI_EVERSION; /* JNI version not supported */
}
@@ -64,6 +70,9 @@
supporting socket APIs are available
*/
IPv6_available = IPv6_supported() & (!preferIPv4Stack);
+
+ /* check if SO_REUSEPORT is supported on this platform */
+ REUSEPORT_available = reuseport_supported();
platformInit();
parseExclusiveBindProperty(env);
--- a/jdk/src/java.base/share/native/libnet/net_util.h Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/native/libnet/net_util.h Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -131,6 +131,8 @@
JNIEXPORT jint JNICALL ipv6_available() ;
+JNIEXPORT jint JNICALL reuseport_available() ;
+
void
NET_AllocSockaddr(struct sockaddr **him, int *len);
--- a/jdk/src/java.base/share/native/libzip/CRC32.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/share/native/libzip/CRC32.c Wed Jul 05 21:23:38 2017 +0200
@@ -54,7 +54,7 @@
return crc;
}
-JNIEXPORT jint JNICALL
+jint JNICALL
ZIP_CRC32(jint crc, const jbyte *buf, jint len)
{
return crc32(crc, (Bytef*)buf, len);
--- a/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -45,7 +45,15 @@
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
- super.setOption(name, value);
+ if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
+ super.setOption(name, value);
+ } else {
+ if (supportedOptions().contains(name)) {
+ super.setOption(name, value);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
} else {
if (!flowSupported()) {
throw new UnsupportedOperationException("unsupported option");
@@ -62,7 +70,15 @@
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
- return super.getOption(name);
+ if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
+ return super.getOption(name);
+ } else {
+ if (supportedOptions().contains(name)) {
+ return super.getOption(name);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
}
if (!flowSupported()) {
throw new UnsupportedOperationException("unsupported option");
@@ -87,6 +103,9 @@
}
protected void socketSetOption(int opt, Object val) throws SocketException {
+ if (opt == SocketOptions.SO_REUSEPORT && !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
try {
socketSetOption0(opt, val);
} catch (SocketException se) {
--- a/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -59,7 +59,15 @@
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
- super.setOption(name, value);
+ if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
+ super.setOption(name, value);
+ } else {
+ if (supportedOptions().contains(name)) {
+ super.setOption(name, value);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
} else {
if (getSocket() == null || !flowSupported()) {
throw new UnsupportedOperationException("unsupported option");
@@ -76,7 +84,15 @@
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
- return super.getOption(name);
+ if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
+ return super.getOption(name);
+ } else {
+ if (supportedOptions().contains(name)) {
+ return super.getOption(name);
+ } else {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+ }
}
if (getSocket() == null || !flowSupported()) {
throw new UnsupportedOperationException("unsupported option");
@@ -101,6 +117,9 @@
}
protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
+ if (opt == SocketOptions.SO_REUSEPORT && !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
try {
socketSetOption0(opt, b, val);
} catch (SocketException se) {
--- a/jdk/src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/classes/sun/net/www/protocol/jar/JarFileFactory.java Wed Jul 05 21:23:38 2017 +0200
@@ -85,7 +85,7 @@
synchronized (instance) {
result = getCachedJarFile(url);
if (result == null) {
- fileCache.put(URLUtil.urlNoFragString(url), local_result);
+ fileCache.put(urlKey(url), local_result);
urlCache.put(local_result, url);
result = local_result;
} else {
@@ -113,13 +113,13 @@
synchronized (instance) {
URL urlRemoved = urlCache.remove(jarFile);
if (urlRemoved != null)
- fileCache.remove(URLUtil.urlNoFragString(urlRemoved));
+ fileCache.remove(urlKey(urlRemoved));
}
}
private JarFile getCachedJarFile(URL url) {
assert Thread.holdsLock(instance);
- JarFile result = fileCache.get(URLUtil.urlNoFragString(url));
+ JarFile result = fileCache.get(urlKey(url));
/* if the JAR file is cached, the permission will always be there */
if (result != null) {
@@ -149,6 +149,12 @@
return result;
}
+ private String urlKey(URL url) {
+ String urlstr = URLUtil.urlNoFragString(url);
+ if ("runtime".equals(url.getRef())) urlstr += "#runtime";
+ return urlstr;
+ }
+
private Permission getPermission(JarFile jarFile) {
try {
URLConnection uc = getConnection(jarFile);
--- a/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -1392,6 +1392,7 @@
}
case java_net_SocketOptions_SO_REUSEADDR:
+ case java_net_SocketOptions_SO_REUSEPORT:
case java_net_SocketOptions_SO_BROADCAST:
{
jclass cls;
@@ -1769,6 +1770,9 @@
case java_net_SocketOptions_SO_REUSEADDR:
return createBoolean(env, optval.i);
+ case java_net_SocketOptions_SO_REUSEPORT:
+ return createBoolean(env, optval.i);
+
case java_net_SocketOptions_SO_SNDBUF:
case java_net_SocketOptions_SO_RCVBUF:
case java_net_SocketOptions_IP_TOS:
--- a/jdk/src/java.base/unix/native/libnet/SdpSupport.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnet/SdpSupport.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, 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
@@ -108,6 +108,11 @@
len = sizeof(arg);
if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, &len) == 0)
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, len);
+#ifdef SO_REUSEPORT
+ len = sizeof(arg);
+ if (getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char*)&arg, &len) == 0)
+ setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char*)&arg, len);
+#endif
len = sizeof(arg);
if (getsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char*)&arg, &len) == 0)
setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char*)&arg, len);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/unix/native/libnet/SocketImpl.c Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+#include <jni.h>
+#include <string.h>
+
+#include "net_util.h"
+
+JNIEXPORT jboolean JNICALL
+Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_net_Sockets_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
+}
--- a/jdk/src/java.base/unix/native/libnet/net_util_md.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnet/net_util_md.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -439,6 +439,25 @@
}
#endif /* DONT_ENABLE_IPV6 */
+jint reuseport_supported()
+{
+ /* Do a simple dummy call, and try to figure out from that */
+ int one = 1;
+ int rv, s;
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ return JNI_FALSE;
+ }
+ rv = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(one));
+ if (rv != 0) {
+ rv = JNI_FALSE;
+ } else {
+ rv = JNI_TRUE;
+ }
+ close(s);
+ return rv;
+}
+
void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
const char* hostname,
int gai_error)
@@ -1014,6 +1033,7 @@
{ java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF },
{ java_net_SocketOptions_SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE },
{ java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR },
+ { java_net_SocketOptions_SO_REUSEPORT, SOL_SOCKET, SO_REUSEPORT },
{ java_net_SocketOptions_SO_BROADCAST, SOL_SOCKET, SO_BROADCAST },
{ java_net_SocketOptions_IP_TOS, IPPROTO_IP, IP_TOS },
{ java_net_SocketOptions_IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF },
--- a/jdk/src/java.base/unix/native/libnet/net_util_md.h Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnet/net_util_md.h Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -60,6 +60,19 @@
#define NET_WAIT_WRITE 0x02
#define NET_WAIT_CONNECT 0x04
+/* Defines SO_REUSEPORT */
+#ifndef SO_REUSEPORT
+#ifdef __linux__
+#define SO_REUSEPORT 15
+#elif __solaris__
+#define SO_REUSEPORT 0x100e
+#elif defined(AIX) || defined(MACOSX)
+#define SO_REUSEPORT 0x0200
+#else
+#define SO_REUSEPORT 0
+#endif
+#endif
+
jint NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout);
/************************************************************************
--- a/jdk/src/java.base/unix/native/libnio/ch/Net.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnio/ch/Net.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -161,6 +161,12 @@
return (ipv6_available()) ? JNI_TRUE : JNI_FALSE;
}
+JNIEXPORT jboolean JNICALL
+Java_sun_nio_ch_Net_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
+}
+
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
return -1;
--- a/jdk/src/java.base/unix/native/libnio/ch/nio_util.h Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/unix/native/libnio/ch/nio_util.h Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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,6 +36,18 @@
} while((_result == -1) && (errno == EINTR)); \
} while(0)
+/* Defines SO_REUSEPORT */
+#ifndef SO_REUSEPORT
+#ifdef __linux__
+#define SO_REUSEPORT 15
+#elif __solaris__
+#define SO_REUSEPORT 0x100e
+#elif defined(AIX) || defined(MACOSX)
+#define SO_REUSEPORT 0x0200
+#else
+#define SO_REUSEPORT 0
+#endif
+#endif
/* NIO utility procedures */
--- a/jdk/src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -167,6 +167,11 @@
int optionValue = 0;
+ // SO_REUSEPORT is not supported on Windows.
+ if (opt == SO_REUSEPORT) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
+
switch(opt) {
case IP_TOS :
case SO_RCVBUF :
@@ -200,6 +205,9 @@
}
if (opt == SO_REUSEADDR && reuseAddressEmulated)
return isReuseAddress;
+ // SO_REUSEPORT is not supported on Windows.
+ if (opt == SO_REUSEPORT)
+ throw new UnsupportedOperationException("unsupported option");
int value = socketGetIntOption(nativefd, opt);
Object returnValue = null;
--- a/jdk/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/java/net/DualStackPlainSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -181,6 +181,10 @@
if (opt == SO_TIMEOUT) { // timeout implemented through select.
return;
}
+ // SO_REUSEPORT is not supported on Windows.
+ if (opt == SO_REUSEPORT) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
int optionValue = 0;
@@ -224,6 +228,10 @@
localAddress(nativefd, (InetAddressContainer)iaContainerObj);
return 0; // return value doesn't matter.
}
+ // SO_REUSEPORT is not supported on Windows.
+ if (opt == SO_REUSEPORT) {
+ throw new UnsupportedOperationException("unsupported option");
+ }
// SO_REUSEADDR emulated when using exclusive bind
if (opt == SO_REUSEADDR && exclusiveBind)
--- a/jdk/src/java.base/windows/classes/java/net/PlainSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/java/net/PlainSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -173,10 +173,18 @@
}
public void setOption(int opt, Object val) throws SocketException {
+ if (opt == SocketOptions.SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
+ }
impl.setOption(opt, val);
}
public Object getOption(int opt) throws SocketException {
+ if (opt == SocketOptions.SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
+ }
return impl.getOption(opt);
}
@@ -332,14 +340,27 @@
void socketSetOption(int cmd, boolean on, Object value)
throws SocketException {
+ if (cmd == SocketOptions.SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
+ }
impl.socketSetOption(cmd, on, value);
}
int socketGetOption(int opt, Object iaContainerObj) throws SocketException {
+ if (opt == SocketOptions.SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
+ }
return impl.socketGetOption(opt, iaContainerObj);
}
void socketSendUrgentData(int data) throws IOException {
impl.socketSendUrgentData(data);
}
+
+ static boolean isReusePortAvailable() {
+ // SO_REUSEPORT is not supported on Windows.
+ return false;
+ }
}
--- a/jdk/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -130,6 +130,9 @@
return socketLocalAddress(family);
} else if (optID == SO_REUSEADDR && reuseAddressEmulated) {
return isReuseAddress;
+ } else if (optID == SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
} else {
return super.getOption(optID);
}
@@ -142,6 +145,9 @@
// socket already bound, emulate
reuseAddressEmulated = true;
isReuseAddress = (Boolean)val;
+ } else if (opt == SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
} else {
socketNativeSetOption(opt, val);
}
--- a/jdk/src/java.base/windows/classes/java/net/TwoStacksPlainSocketImpl.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/java/net/TwoStacksPlainSocketImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -128,6 +128,9 @@
} else if (opt == SO_REUSEADDR && exclusiveBind) {
// SO_REUSEADDR emulated when using exclusive bind
return isReuseAddress;
+ } else if (opt == SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
} else
return super.getOption(opt);
}
@@ -144,6 +147,10 @@
// SO_REUSEADDR emulated when using exclusive bind
if (opt == SO_REUSEADDR && exclusiveBind)
isReuseAddress = on;
+ else if (opt == SO_REUSEPORT) {
+ // SO_REUSEPORT is not supported on Windows.
+ throw new UnsupportedOperationException("unsupported option");
+ }
else
socketNativeSetOption(opt, on, value);
}
--- a/jdk/src/java.base/windows/classes/sun/net/www/protocol/jar/JarFileFactory.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/classes/sun/net/www/protocol/jar/JarFileFactory.java Wed Jul 05 21:23:38 2017 +0200
@@ -95,7 +95,7 @@
synchronized (instance) {
result = getCachedJarFile(url);
if (result == null) {
- fileCache.put(URLUtil.urlNoFragString(url), local_result);
+ fileCache.put(urlKey(url), local_result);
urlCache.put(local_result, url);
result = local_result;
} else {
@@ -123,13 +123,13 @@
synchronized (instance) {
URL urlRemoved = urlCache.remove(jarFile);
if (urlRemoved != null)
- fileCache.remove(URLUtil.urlNoFragString(urlRemoved));
+ fileCache.remove(urlKey(urlRemoved));
}
}
private JarFile getCachedJarFile(URL url) {
assert Thread.holdsLock(instance);
- JarFile result = fileCache.get(URLUtil.urlNoFragString(url));
+ JarFile result = fileCache.get(urlKey(url));
/* if the JAR file is cached, the permission will always be there */
if (result != null) {
@@ -159,6 +159,12 @@
return result;
}
+ private String urlKey(URL url) {
+ String urlstr = URLUtil.urlNoFragString(url);
+ if ("runtime".equals(url.getRef())) urlstr += "#runtime";
+ return urlstr;
+ }
+
private Permission getPermission(JarFile jarFile) {
try {
URLConnection uc = getConnection(jarFile);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libnet/SocketImpl.c Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+#include <jni.h>
+
+JNIEXPORT jboolean JNICALL
+Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ // SO_REUSEPORT is not supported on Windows
+ return JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ // SO_REUSEPORT is not supported on Windows
+ return JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_net_Sockets_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ // SO_REUSEPORT is not supported on Windows
+ return JNI_FALSE;
+}
--- a/jdk/src/java.base/windows/native/libnet/net_util_md.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/native/libnet/net_util_md.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -242,6 +242,11 @@
return JNI_TRUE;
}
+jint reuseport_supported()
+{
+ /* SO_REUSEPORT is not supported onn Windows */
+ return JNI_FALSE;
+}
/*
* Return the default TOS value
*/
--- a/jdk/src/java.base/windows/native/libnet/net_util_md.h Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/native/libnet/net_util_md.h Wed Jul 05 21:23:38 2017 +0200
@@ -54,6 +54,9 @@
#else
+/*SO_REUSEPORT is not supported on Windows, define it to 0*/
+#define SO_REUSEPORT 0
+
/* Retain this code a little longer to support building in
* old environments. _MSC_VER is defined as:
* 1200 for MSVC++ 6.0
@@ -353,3 +356,4 @@
JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isLoopback0_XP
(JNIEnv *env, jclass cls, jstring name, jint index);
+
--- a/jdk/src/java.base/windows/native/libnio/ch/Net.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/java.base/windows/native/libnio/ch/Net.c Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -93,6 +93,13 @@
return ipv6_available() ? JNI_TRUE : JNI_FALSE;
}
+JNIEXPORT jboolean JNICALL
+Java_sun_nio_ch_Net_isReusePortAvailable0(JNIEnv* env, jclass c1)
+{
+ // SO_REUSEPORT is not supported on Windows
+ return JNI_FALSE;
+}
+
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
return 1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.nio.channels.SelectableChannel;
+
+/**
+ * Event handling interface from HttpClientImpl's selector.
+ *
+ * <p> If blockingChannel is true, then the channel will be put in blocking
+ * mode prior to handle() being called. If false, then it remains non-blocking.
+ */
+abstract class AsyncEvent {
+
+ /**
+ * Implement this if channel should be made blocking before calling handle()
+ */
+ public interface Blocking { }
+
+ /**
+ * Implement this if channel should remain non-blocking before calling handle()
+ */
+ public interface NonBlocking { }
+
+ /** Returns the channel */
+ public abstract SelectableChannel channel();
+
+ /** Returns the selector interest op flags OR'd */
+ public abstract int interestOps();
+
+ /** Called when event occurs */
+ public abstract void handle();
+
+ /** Called when selector is shutting down. Abort all exchanges. */
+ public abstract void abort();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import static java.net.Authenticator.RequestorType.PROXY;
+import static java.net.Authenticator.RequestorType.SERVER;
+import java.net.PasswordAuthentication;
+import java.net.URI;
+import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.LinkedList;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
+/**
+ * Implementation of Http Basic authentication.
+ */
+class AuthenticationFilter implements HeaderFilter {
+
+ static private final Base64.Encoder encoder = Base64.getEncoder();
+
+ static final int DEFAULT_RETRY_LIMIT = 3;
+
+ static final int retry_limit = Utils.getIntegerNetProperty(
+ "sun.net.httpclient.auth.retrylimit", DEFAULT_RETRY_LIMIT);
+
+ static final int UNAUTHORIZED = 401;
+ static final int PROXY_UNAUTHORIZED = 407;
+
+ private PasswordAuthentication getCredentials(String header,
+ boolean proxy,
+ HttpRequestImpl req)
+ throws IOException
+ {
+ HttpClientImpl client = req.client();
+ java.net.Authenticator auth =
+ client.authenticator()
+ .orElseThrow(() -> new IOException("No authenticator set"));
+ URI uri = req.uri();
+ HeaderParser parser = new HeaderParser(header);
+ String authscheme = parser.findKey(0);
+
+ String realm = parser.findValue("realm");
+ java.net.Authenticator.RequestorType rtype = proxy ? PROXY : SERVER;
+
+ // needs to be instance method in Authenticator
+ return auth.requestPasswordAuthenticationInstance(uri.getHost(),
+ null,
+ uri.getPort(),
+ uri.getScheme(),
+ realm,
+ authscheme,
+ uri.toURL(),
+ rtype
+ );
+ }
+
+ private URI getProxyURI(HttpRequestImpl r) {
+ InetSocketAddress proxy = r.proxy();
+ if (proxy == null) {
+ return null;
+ }
+
+ // our own private scheme for proxy URLs
+ // eg. proxy.http://host:port/
+ String scheme = "proxy." + r.uri().getScheme();
+ try {
+ return new URI(scheme,
+ null,
+ proxy.getHostString(),
+ proxy.getPort(),
+ null,
+ null,
+ null);
+ } catch (URISyntaxException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ @Override
+ public void request(HttpRequestImpl r) throws IOException {
+ // use preemptive authentication if an entry exists.
+ Cache cache = getCache(r);
+
+ // Proxy
+ if (r.exchange.proxyauth == null) {
+ URI proxyURI = getProxyURI(r);
+ if (proxyURI != null) {
+ CacheEntry ca = cache.get(proxyURI, true);
+ if (ca != null) {
+ r.exchange.proxyauth = new AuthInfo(true, ca.scheme, null, ca);
+ addBasicCredentials(r, true, ca.value);
+ }
+ }
+ }
+
+ // Server
+ if (r.exchange.serverauth == null) {
+ CacheEntry ca = cache.get(r.uri(), false);
+ if (ca != null) {
+ r.exchange.serverauth = new AuthInfo(true, ca.scheme, null, ca);
+ addBasicCredentials(r, false, ca.value);
+ }
+ }
+ }
+
+ // TODO: refactor into per auth scheme class
+ static private void addBasicCredentials(HttpRequestImpl r,
+ boolean proxy,
+ PasswordAuthentication pw) {
+ String hdrname = proxy ? "Proxy-Authorization" : "Authorization";
+ StringBuilder sb = new StringBuilder(128);
+ sb.append(pw.getUserName()).append(':').append(pw.getPassword());
+ String s = encoder.encodeToString(sb.toString().getBytes(ISO_8859_1));
+ String value = "Basic " + s;
+ r.setSystemHeader(hdrname, value);
+ }
+
+ // Information attached to a HttpRequestImpl relating to authentication
+ static class AuthInfo {
+ final boolean fromcache;
+ final String scheme;
+ int retries;
+ PasswordAuthentication credentials; // used in request
+ CacheEntry cacheEntry; // if used
+
+ AuthInfo(boolean fromcache,
+ String scheme,
+ PasswordAuthentication credentials) {
+ this.fromcache = fromcache;
+ this.scheme = scheme;
+ this.credentials = credentials;
+ this.retries = 1;
+ }
+
+ AuthInfo(boolean fromcache,
+ String scheme,
+ PasswordAuthentication credentials,
+ CacheEntry ca) {
+ this(fromcache, scheme, credentials);
+ assert credentials == null || (ca != null && ca.value == null);
+ cacheEntry = ca;
+ }
+ }
+
+ @Override
+ public HttpRequestImpl response(HttpResponseImpl r) throws IOException {
+ Cache cache = getCache(r.request);
+ int status = r.statusCode();
+ HttpHeaders hdrs = r.headers();
+ HttpRequestImpl req = r.request();
+
+ if (status != UNAUTHORIZED && status != PROXY_UNAUTHORIZED) {
+ // check if any authentication succeeded for first time
+ if (req.exchange.serverauth != null && !req.exchange.serverauth.fromcache) {
+ AuthInfo au = req.exchange.serverauth;
+ cache.store(au.scheme, req.uri(), false, au.credentials);
+ }
+ if (req.exchange.proxyauth != null && !req.exchange.proxyauth.fromcache) {
+ AuthInfo au = req.exchange.proxyauth;
+ cache.store(au.scheme, req.uri(), false, au.credentials);
+ }
+ return null;
+ }
+
+ boolean proxy = status == PROXY_UNAUTHORIZED;
+ String authname = proxy ? "Proxy-Authentication" : "WWW-Authenticate";
+ String authval = hdrs.firstValue(authname).orElseThrow(() -> {
+ return new IOException("Invalid auth header");
+ });
+ HeaderParser parser = new HeaderParser(authval);
+ String scheme = parser.findKey(0);
+
+ // TODO: Need to generalise from Basic only. Delegate to a provider class etc.
+
+ if (!scheme.equalsIgnoreCase("Basic")) {
+ return null; // error gets returned to app
+ }
+
+ String realm = parser.findValue("realm");
+ AuthInfo au = proxy ? req.exchange.proxyauth : req.exchange.serverauth;
+ if (au == null) {
+ PasswordAuthentication pw = getCredentials(authval, proxy, req);
+ if (pw == null) {
+ throw new IOException("No credentials provided");
+ }
+ // No authentication in request. Get credentials from user
+ au = new AuthInfo(false, "Basic", pw);
+ if (proxy)
+ req.exchange.proxyauth = au;
+ else
+ req.exchange.serverauth = au;
+ addBasicCredentials(req, proxy, pw);
+ return req;
+ } else if (au.retries > retry_limit) {
+ throw new IOException("too many authentication attempts");
+ } else {
+ // we sent credentials, but they were rejected
+ if (au.fromcache) {
+ cache.remove(au.cacheEntry);
+ }
+ // try again
+ au.credentials = getCredentials(authval, proxy, req);
+ addBasicCredentials(req, proxy, au.credentials);
+ au.retries++;
+ return req;
+ }
+ }
+
+ static final HashMap<HttpClientImpl,Cache> caches = new HashMap<>();
+
+ static synchronized Cache getCache(HttpRequestImpl req) {
+ HttpClientImpl client = req.client();
+ Cache c = caches.get(client);
+ if (c == null) {
+ c = new Cache();
+ caches.put(client, c);
+ }
+ return c;
+ }
+
+ static class Cache {
+ final LinkedList<CacheEntry> entries = new LinkedList<>();
+
+ synchronized CacheEntry get(URI uri, boolean proxy) {
+ for (CacheEntry entry : entries) {
+ if (entry.equalsKey(uri, proxy)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ synchronized void remove(String authscheme, URI domain, boolean proxy) {
+ for (CacheEntry entry : entries) {
+ if (entry.equalsKey(domain, proxy)) {
+ entries.remove(entry);
+ }
+ }
+ }
+
+ synchronized void remove(CacheEntry entry) {
+ entries.remove(entry);
+ }
+
+ synchronized void store(String authscheme,
+ URI domain,
+ boolean proxy,
+ PasswordAuthentication value) {
+ remove(authscheme, domain, proxy);
+ entries.add(new CacheEntry(authscheme, domain, proxy, value));
+ }
+ }
+
+ static class CacheEntry {
+ final String root;
+ final String scheme;
+ final boolean proxy;
+ final PasswordAuthentication value;
+
+ CacheEntry(String authscheme,
+ URI uri,
+ boolean proxy,
+ PasswordAuthentication value) {
+ this.scheme = authscheme;
+ this.root = uri.resolve(".").toString(); // remove extraneous components
+ this.proxy = proxy;
+ this.value = value;
+ }
+
+ public PasswordAuthentication value() {
+ return value;
+ }
+
+ public boolean equalsKey(URI uri, boolean proxy) {
+ if (this.proxy != proxy) {
+ return false;
+ }
+ String other = uri.toString();
+ return other.startsWith(root);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implemented by buffer pools.
+ */
+interface BufferHandler {
+
+ ByteBuffer getBuffer();
+
+ void returnBuffer(ByteBuffer buffer);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Objects;
+
+/**
+ * Http 1.1 connection pool.
+ */
+class ConnectionPool {
+
+ static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
+ "sun.net.httpclient.keepalive.timeout", 1200); // seconds
+
+ // Pools of idle connections
+
+ final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
+ final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool;
+ CacheCleaner cleaner;
+
+ /**
+ * Entries in connection pool are keyed by destination address and/or
+ * proxy address:
+ * case 1: plain TCP not via proxy (destination 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)
+ */
+ static class CacheKey {
+ final InetSocketAddress proxy;
+ final InetSocketAddress destination;
+
+ CacheKey(InetSocketAddress destination, InetSocketAddress proxy) {
+ this.proxy = proxy;
+ this.destination = destination;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final CacheKey other = (CacheKey) obj;
+ if (!Objects.equals(this.proxy, other.proxy)) {
+ return false;
+ }
+ if (!Objects.equals(this.destination, other.destination)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(proxy, destination);
+ }
+ }
+
+ static class ExpiryEntry {
+ final HttpConnection connection;
+ final long expiry; // absolute time in seconds of expiry time
+ ExpiryEntry(HttpConnection connection, long expiry) {
+ this.connection = connection;
+ this.expiry = expiry;
+ }
+ }
+
+ final LinkedList<ExpiryEntry> expiryList;
+
+ /**
+ * There should be one of these per HttpClient.
+ */
+ ConnectionPool() {
+ plainPool = new HashMap<>();
+ sslPool = new HashMap<>();
+ expiryList = new LinkedList<>();
+ cleaner = new CacheCleaner();
+ }
+
+ void start() {
+ cleaner.start();
+ }
+
+ static CacheKey cacheKey(InetSocketAddress destination,
+ InetSocketAddress proxy) {
+ return new CacheKey(destination, proxy);
+ }
+
+ synchronized HttpConnection getConnection(boolean secure,
+ InetSocketAddress addr,
+ InetSocketAddress proxy) {
+ CacheKey key = new CacheKey(addr, proxy);
+ HttpConnection c = secure ? findConnection(key, sslPool)
+ : findConnection(key, plainPool);
+ //System.out.println ("getConnection returning: " + c);
+ return c;
+ }
+
+ /**
+ * Returns the connection to the pool.
+ *
+ * @param conn
+ */
+ synchronized void returnToPool(HttpConnection conn) {
+ if (conn instanceof PlainHttpConnection) {
+ putConnection(conn, plainPool);
+ } else {
+ putConnection(conn, sslPool);
+ }
+ addToExpiryList(conn);
+ //System.out.println("Return to pool: " + conn);
+ }
+
+ private HttpConnection
+ findConnection(CacheKey key,
+ HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
+ LinkedList<HttpConnection> l = pool.get(key);
+ if (l == null || l.size() == 0) {
+ return null;
+ } else {
+ HttpConnection c = l.removeFirst();
+ removeFromExpiryList(c);
+ return c;
+ }
+ }
+
+ /* called from cache cleaner only */
+ private void
+ removeFromPool(HttpConnection c,
+ HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
+ //System.out.println("cacheCleaner removing: " + c);
+ LinkedList<HttpConnection> l = pool.get(c.cacheKey());
+ assert l != null;
+ boolean wasPresent = l.remove(c);
+ assert wasPresent;
+ }
+
+ private void
+ putConnection(HttpConnection c,
+ HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
+ CacheKey key = c.cacheKey();
+ LinkedList<HttpConnection> l = pool.get(key);
+ if (l == null) {
+ l = new LinkedList<>();
+ pool.put(key, l);
+ }
+ l.add(c);
+ }
+
+ // only runs while entries exist in cache
+
+ class CacheCleaner extends Thread {
+
+ volatile boolean stopping;
+
+ CacheCleaner() {
+ super(null, null, "HTTP-Cache-cleaner", 0, false);
+ setDaemon(true);
+ }
+
+ synchronized boolean stopping() {
+ return stopping;
+ }
+
+ synchronized void stopCleaner() {
+ stopping = true;
+ }
+
+ @Override
+ public void run() {
+ while (!stopping()) {
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {}
+ cleanCache();
+ }
+ }
+ }
+
+ synchronized void removeFromExpiryList(HttpConnection c) {
+ if (c == null) {
+ return;
+ }
+ ListIterator<ExpiryEntry> li = expiryList.listIterator();
+ while (li.hasNext()) {
+ ExpiryEntry e = li.next();
+ if (e.connection.equals(c)) {
+ li.remove();
+ return;
+ }
+ }
+ if (expiryList.isEmpty()) {
+ cleaner.stopCleaner();
+ }
+ }
+
+ private void cleanCache() {
+ long now = System.currentTimeMillis() / 1000;
+ LinkedList<HttpConnection> closelist = new LinkedList<>();
+
+ synchronized (this) {
+ ListIterator<ExpiryEntry> li = expiryList.listIterator();
+ while (li.hasNext()) {
+ ExpiryEntry entry = li.next();
+ if (entry.expiry <= now) {
+ li.remove();
+ HttpConnection c = entry.connection;
+ closelist.add(c);
+ if (c instanceof PlainHttpConnection) {
+ removeFromPool(c, plainPool);
+ } else {
+ removeFromPool(c, sslPool);
+ }
+ }
+ }
+ }
+ for (HttpConnection c : closelist) {
+ //System.out.println ("KAC: closing " + c);
+ c.close();
+ }
+ }
+
+ private synchronized void addToExpiryList(HttpConnection conn) {
+ long now = System.currentTimeMillis() / 1000;
+ long then = now + KEEP_ALIVE;
+
+ if (expiryList.isEmpty())
+ cleaner = new CacheCleaner();
+
+ ListIterator<ExpiryEntry> li = expiryList.listIterator();
+ while (li.hasNext()) {
+ ExpiryEntry entry = li.next();
+
+ if (then > entry.expiry) {
+ li.previous();
+ // insert here
+ li.add(new ExpiryEntry(conn, then));
+ return;
+ }
+ }
+ // first element of list
+ expiryList.add(new ExpiryEntry(conn, then));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+import java.net.CookieManager;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class CookieFilter implements HeaderFilter {
+
+ final HttpClientImpl client;
+ final CookieManager cookieMan;
+
+ CookieFilter(HttpClientImpl client) {
+ this.client = client;
+ this.cookieMan = client.cookieManager().orElseThrow(
+ () -> new IllegalArgumentException("no cookie manager"));
+ }
+
+ @Override
+ public void request(HttpRequestImpl r) throws IOException {
+ Map<String,List<String>> userheaders, cookies;
+ userheaders = r.getUserHeaders().directMap();
+ cookies = cookieMan.get(r.uri(), userheaders);
+ // add the returned cookies
+ HttpHeadersImpl systemHeaders = r.getSystemHeaders();
+ Set<String> keys = cookies.keySet();
+ for (String hdrname : keys) {
+ List<String> vals = cookies.get(hdrname);
+ for (String val : vals) {
+ systemHeaders.addHeader(hdrname, val);
+ }
+ }
+ }
+
+ @Override
+ public HttpRequestImpl response(HttpResponseImpl r) throws IOException {
+ HttpHeaders hdrs = r.headers();
+ cookieMan.put(r.uri(), hdrs.map());
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketPermission;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLPermission;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * One request/response exchange (handles 100/101 intermediate response also).
+ * depth field used to track number of times a new request is being sent
+ * for a given API request. If limit exceeded exception is thrown.
+ *
+ * Security check is performed here:
+ * - uses AccessControlContext captured at API level
+ * - checks for appropriate URLPermission for request
+ * - if permission allowed, grants equivalent SocketPermission to call
+ * - in case of direct HTTP proxy, checks additionally for access to proxy
+ * (CONNECT proxying uses its own Exchange, so check done there)
+ *
+ */
+class Exchange {
+
+ final HttpRequestImpl request;
+ final HttpClientImpl client;
+ ExchangeImpl exchImpl;
+ HttpResponseImpl response;
+ final List<SocketPermission> permissions = new LinkedList<>();
+ AccessControlContext acc;
+ boolean upgrading; // to HTTP/2
+
+ Exchange(HttpRequestImpl request) {
+ this.request = request;
+ this.upgrading = false;
+ this.client = request.client();
+ }
+
+ /* If different AccessControlContext to be used */
+ Exchange(HttpRequestImpl request, AccessControlContext acc) {
+ this.request = request;
+ this.acc = acc;
+ this.upgrading = false;
+ this.client = request.client();
+ }
+
+ public HttpRequestImpl request() {
+ return request;
+ }
+
+ public HttpResponseImpl response() throws IOException, InterruptedException {
+ response = responseImpl(null);
+ return response;
+ }
+
+ public void cancel() {
+ if (exchImpl != null)
+ exchImpl.cancel();
+ }
+
+ public void h2Upgrade() {
+ upgrading = true;
+ request.setH2Upgrade();
+ }
+
+ static final SocketPermission[] SOCKET_ARRAY = new SocketPermission[0];
+
+ HttpResponseImpl responseImpl(HttpConnection connection)
+ throws IOException, InterruptedException
+ {
+ if (acc == null) {
+ acc = request.getAccessControlContext();
+ }
+ SecurityException e = securityCheck(acc);
+ if (e != null)
+ throw e;
+
+ if (permissions.size() > 0) {
+ try {
+ return AccessController.doPrivileged(
+ (PrivilegedExceptionAction<HttpResponseImpl>)() ->
+ responseImpl0(connection),
+ null,
+ permissions.toArray(SOCKET_ARRAY));
+ } catch (Throwable ee) {
+ if (ee instanceof PrivilegedActionException) {
+ ee = ee.getCause();
+ }
+ if (ee instanceof IOException)
+ throw (IOException)ee;
+ else
+ throw new RuntimeException(ee); // TODO: fix
+ }
+ } else {
+ return responseImpl0(connection);
+ }
+ }
+
+ HttpResponseImpl responseImpl0(HttpConnection connection)
+ throws IOException, InterruptedException
+ {
+ exchImpl = ExchangeImpl.get(this, connection);
+ if (request.expectContinue()) {
+ request.addSystemHeader("Expect", "100-Continue");
+ exchImpl.sendHeadersOnly();
+ HttpResponseImpl resp = exchImpl.getResponse();
+ logResponse(resp);
+ if (resp.statusCode() != 100) {
+ return resp;
+ }
+ exchImpl.sendBody();
+ return exchImpl.getResponse();
+ } else {
+ exchImpl.sendRequest();
+ HttpResponseImpl resp = exchImpl.getResponse();
+ logResponse(resp);
+ return checkForUpgrade(resp, exchImpl);
+ }
+ }
+
+ // Completed HttpResponse will be null if response succeeded
+ // will be a non null responseAsync if expect continue returns an error
+
+ public CompletableFuture<HttpResponseImpl> responseAsync(Void v) {
+ return responseAsyncImpl(null);
+ }
+
+ CompletableFuture<HttpResponseImpl> responseAsyncImpl(HttpConnection connection) {
+ if (acc == null) {
+ acc = request.getAccessControlContext();
+ }
+ SecurityException e = securityCheck(acc);
+ if (e != null) {
+ CompletableFuture<HttpResponseImpl> cf = new CompletableFuture<>();
+ cf.completeExceptionally(e);
+ return cf;
+ }
+ if (permissions.size() > 0) {
+ return AccessController.doPrivileged(
+ (PrivilegedAction<CompletableFuture<HttpResponseImpl>>)() ->
+ responseAsyncImpl0(connection),
+ null,
+ permissions.toArray(SOCKET_ARRAY));
+ } else {
+ return responseAsyncImpl0(connection);
+ }
+ }
+
+ CompletableFuture<HttpResponseImpl> responseAsyncImpl0(HttpConnection connection) {
+ try {
+ exchImpl = ExchangeImpl.get(this, connection);
+ } catch (IOException | InterruptedException e) {
+ CompletableFuture<HttpResponseImpl> cf = new CompletableFuture<>();
+ cf.completeExceptionally(e);
+ return cf;
+ }
+ if (request.expectContinue()) {
+ request.addSystemHeader("Expect", "100-Continue");
+ return exchImpl.sendHeadersAsync()
+ .thenCompose(exchImpl::getResponseAsync)
+ .thenCompose((HttpResponseImpl r1) -> {
+ int rcode = r1.statusCode();
+ CompletableFuture<HttpResponseImpl> cf =
+ checkForUpgradeAsync(r1, exchImpl);
+ if (cf != null)
+ return cf;
+ if (rcode == 100) {
+ return exchImpl.sendBodyAsync()
+ .thenCompose(exchImpl::getResponseAsync)
+ .thenApply((r) -> {
+ logResponse(r);
+ return r;
+ });
+ } else {
+ Exchange.this.response = r1;
+ logResponse(r1);
+ return CompletableFuture.completedFuture(r1);
+ }
+ });
+ } else {
+ return exchImpl
+ .sendHeadersAsync()
+ .thenCompose((Void v) -> {
+ // send body and get response at same time
+ exchImpl.sendBodyAsync();
+ return exchImpl.getResponseAsync(null);
+ })
+ .thenCompose((HttpResponseImpl r1) -> {
+ int rcode = r1.statusCode();
+ CompletableFuture<HttpResponseImpl> cf =
+ checkForUpgradeAsync(r1, exchImpl);
+ if (cf != null) {
+ return cf;
+ } else {
+ Exchange.this.response = r1;
+ logResponse(r1);
+ return CompletableFuture.completedFuture(r1);
+ }
+ })
+ .thenApply((HttpResponseImpl response) -> {
+ this.response = response;
+ logResponse(response);
+ return response;
+ });
+ }
+ }
+
+ // if this response was received in reply to an upgrade
+ // then create the Http2Connection from the HttpConnection
+ // initialize it and wait for the real response on a newly created Stream
+
+ private CompletableFuture<HttpResponseImpl>
+ checkForUpgradeAsync(HttpResponseImpl resp,
+ ExchangeImpl ex) {
+ int rcode = resp.statusCode();
+ if (upgrading && (rcode == 101)) {
+ Http1Exchange e = (Http1Exchange)ex;
+ // check for 101 switching protocols
+ return e.responseBodyAsync(HttpResponse.ignoreBody())
+ .thenCompose((Void v) ->
+ Http2Connection.createAsync(e.connection(),
+ client.client2(),
+ this)
+ .thenCompose((Http2Connection c) -> {
+ Stream s = c.getStream(1);
+ exchImpl = s;
+ c.putConnection();
+ return s.getResponseAsync(null);
+ })
+ );
+ }
+ return CompletableFuture.completedFuture(resp);
+ }
+
+ private HttpResponseImpl checkForUpgrade(HttpResponseImpl resp,
+ ExchangeImpl ex)
+ throws IOException, InterruptedException
+ {
+ int rcode = resp.statusCode();
+ if (upgrading && (rcode == 101)) {
+ Http1Exchange e = (Http1Exchange) ex;
+ // must get connection from Http1Exchange
+ e.responseBody(HttpResponse.ignoreBody(), false);
+ Http2Connection h2con = new Http2Connection(e.connection(),
+ client.client2(),
+ this);
+ h2con.putConnection();
+ Stream s = h2con.getStream(1);
+ exchImpl = s;
+ return s.getResponse();
+ }
+ return resp;
+ }
+
+
+ <T> T responseBody(HttpResponse.BodyProcessor<T> processor) {
+ try {
+ return exchImpl.responseBody(processor);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+
+ private void logResponse(HttpResponseImpl r) {
+ if (!Log.requests())
+ return;
+ StringBuilder sb = new StringBuilder();
+ String method = r.request().method();
+ URI uri = r.uri();
+ String uristring = uri == null ? "" : uri.toString();
+ sb.append('(')
+ .append(method)
+ .append(" ")
+ .append(uristring)
+ .append(") ")
+ .append(Integer.toString(r.statusCode()));
+ Log.logResponse(sb.toString());
+ }
+
+ <T> CompletableFuture<T> responseBodyAsync(HttpResponse.BodyProcessor<T> processor) {
+ return exchImpl.responseBodyAsync(processor);
+ }
+
+ private URI getURIForSecurityCheck() {
+ URI u;
+ String method = request.method();
+ InetSocketAddress authority = request.authority();
+ URI uri = request.uri();
+
+ // CONNECT should be restricted at API level
+ if (method.equalsIgnoreCase("CONNECT")) {
+ try {
+ u = new URI("socket",
+ null,
+ authority.getHostString(),
+ authority.getPort(),
+ null,
+ null,
+ null);
+ } catch (URISyntaxException e) {
+ throw new InternalError(e); // shouldn't happen
+ }
+ } else {
+ u = uri;
+ }
+ return u;
+ }
+
+ /**
+ * Do the security check and return any exception.
+ * Return null if no check needed or passes.
+ *
+ * Also adds any generated permissions to the "permissions" list.
+ */
+ private SecurityException securityCheck(AccessControlContext acc) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null) {
+ return null;
+ }
+
+ String method = request.method();
+ HttpHeadersImpl userHeaders = request.getUserHeaders();
+ URI u = getURIForSecurityCheck();
+ URLPermission p = Utils.getPermission(u, method, userHeaders.directMap());
+
+ try {
+ assert acc != null;
+ sm.checkPermission(p, acc);
+ permissions.add(getSocketPermissionFor(u));
+ } catch (SecurityException e) {
+ return e;
+ }
+ InetSocketAddress proxy = request.proxy();
+ if (proxy != null) {
+ // may need additional check
+ if (!method.equals("CONNECT")) {
+ // a direct http proxy. Need to check access to proxy
+ try {
+ u = new URI("socket", null, proxy.getHostString(),
+ proxy.getPort(), null, null, null);
+ } catch (URISyntaxException e) {
+ throw new InternalError(e); // shouldn't happen
+ }
+ p = new URLPermission(u.toString(), "CONNECT");
+ try {
+ sm.checkPermission(p, acc);
+ } catch (SecurityException e) {
+ permissions.clear();
+ return e;
+ }
+ String sockperm = proxy.getHostString() +
+ ":" + Integer.toString(proxy.getPort());
+
+ permissions.add(new SocketPermission(sockperm, "connect,resolve"));
+ }
+ }
+ return null;
+ }
+
+ private static SocketPermission getSocketPermissionFor(URI url) {
+ if (System.getSecurityManager() == null)
+ return null;
+
+ StringBuilder sb = new StringBuilder();
+ String host = url.getHost();
+ sb.append(host);
+ int port = url.getPort();
+ if (port == -1) {
+ String scheme = url.getScheme();
+ if ("http".equals(scheme)) {
+ sb.append(":80");
+ } else { // scheme must be https
+ sb.append(":443");
+ }
+ } else {
+ sb.append(':')
+ .append(Integer.toString(port));
+ }
+ String target = sb.toString();
+ return new SocketPermission(target, "connect");
+ }
+
+ AccessControlContext getAccessControlContext() {
+ return acc;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import static java.net.http.HttpClient.Version.HTTP_1_1;
+
+/**
+ * Splits request so that headers and body can be sent separately with optional
+ * (multiple) responses in between (e.g. 100 Continue). Also request and
+ * response always sent/received in different calls.
+ *
+ * Synchronous and asynchronous versions of each method are provided.
+ *
+ * Separate implementations of this class exist for HTTP/1.1 and HTTP/2
+ * Http1Exchange (HTTP/1.1)
+ * Stream (HTTP/2)
+ *
+ * These implementation classes are where work is allocated to threads.
+ */
+abstract class ExchangeImpl {
+
+ final Exchange exchange;
+
+ ExchangeImpl(Exchange e) {
+ this.exchange = e;
+ }
+
+ /**
+ * Initiates a new exchange and assigns it to a connection if one exists
+ * already. connection usually null.
+ */
+ static ExchangeImpl get(Exchange exchange, HttpConnection connection)
+ throws IOException, InterruptedException
+ {
+ HttpRequestImpl req = exchange.request();
+ if (req.version() == HTTP_1_1) {
+ return new Http1Exchange(exchange, connection);
+ } else {
+ Http2ClientImpl c2 = exchange.request().client().client2(); // TODO: improve
+ HttpRequestImpl request = exchange.request();
+ Http2Connection c = c2.getConnectionFor(request);
+ if (c == null) {
+ // no existing connection. Send request with HTTP 1 and then
+ // upgrade if successful
+ ExchangeImpl ex = new Http1Exchange(exchange, connection);
+ exchange.h2Upgrade();
+ return ex;
+ }
+ return c.createStream(exchange);
+ }
+ }
+
+ /* The following methods have separate HTTP/1.1 and HTTP/2 implementations */
+
+ /**
+ * Sends the request headers only. May block until all sent.
+ */
+ abstract void sendHeadersOnly() throws IOException, InterruptedException;
+
+ /**
+ * Gets response headers by blocking if necessary. This may be an
+ * intermediate response (like 101) or a final response 200 etc.
+ */
+ abstract HttpResponseImpl getResponse() throws IOException;
+
+ /**
+ * Sends a request body after request headers.
+ */
+ abstract void sendBody() throws IOException, InterruptedException;
+
+ /**
+ * Sends the entire request (headers and body) blocking.
+ */
+ abstract void sendRequest() throws IOException, InterruptedException;
+
+ /**
+ * Asynchronous version of sendHeaders().
+ */
+ abstract CompletableFuture<Void> sendHeadersAsync();
+
+ /**
+ * Asynchronous version of getResponse(). Requires void parameter for
+ * CompletableFuture chaining.
+ */
+ abstract CompletableFuture<HttpResponseImpl> getResponseAsync(Void v);
+
+ /**
+ * Asynchronous version of sendBody().
+ */
+ abstract CompletableFuture<Void> sendBodyAsync();
+
+ /**
+ * Cancels a request. Not currently exposed through API.
+ */
+ abstract void cancel();
+
+ /**
+ * Asynchronous version of sendRequest().
+ */
+ abstract CompletableFuture<Void> sendRequestAsync();
+
+ abstract <T> T responseBody(HttpResponse.BodyProcessor<T> processor)
+ throws IOException;
+
+ /**
+ * Asynchronous version of responseBody().
+ */
+ abstract <T> CompletableFuture<T>
+ responseBodyAsync(HttpResponse.BodyProcessor<T> processor);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Supplier;
+
+/**
+ * Wraps the supplied user ExecutorService.
+ *
+ * 1) when a Security manager set, the correct access control context
+ * is used to execute task
+ *
+ * 2) memory fence implemented
+ */
+class ExecutorWrapper {
+
+ final ExecutorService userExecutor; // the actual executor service used
+ final Executor executor;
+
+ public static ExecutorWrapper wrap(ExecutorService userExecutor) {
+ return new ExecutorWrapper(userExecutor);
+ }
+
+ /**
+ * Returns a dummy ExecutorWrapper which uses the calling thread
+ */
+ public static ExecutorWrapper callingThread() {
+ return new ExecutorWrapper();
+ }
+
+ private ExecutorWrapper(ExecutorService userExecutor) {
+ // used for executing in calling thread
+ this.userExecutor = userExecutor;
+ this.executor = userExecutor;
+ }
+
+ private ExecutorWrapper() {
+ this.userExecutor = null;
+ this.executor = (Runnable command) -> {
+ command.run();
+ };
+ }
+
+ public ExecutorService userExecutor() {
+ return userExecutor;
+ }
+
+ public synchronized void synchronize() {}
+
+ public void execute(Runnable r, Supplier<AccessControlContext> ctxSupplier) {
+ synchronize();
+ Runnable r1 = () -> {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ Log.logError(t);
+ }
+ };
+
+ if (ctxSupplier != null && System.getSecurityManager() != null) {
+ AccessControlContext acc = ctxSupplier.get();
+ if (acc == null) {
+ throw new InternalError();
+ }
+ AccessController.doPrivilegedWithCombiner(
+ (PrivilegedAction<Void>)() -> {
+ executor.execute(r1); // all throwables must be caught
+ return null;
+ }, acc);
+ } else {
+ executor.execute(r1); // all throwables must be caught
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.util.LinkedList;
+import java.util.List;
+
+class FilterFactory {
+
+ final LinkedList<Class<? extends HeaderFilter>> filterClasses = new LinkedList<>();
+
+ public void addFilter(Class<? extends HeaderFilter> type) {
+ filterClasses.add(type);
+ }
+
+ List<HeaderFilter> getFilterChain() {
+ List<HeaderFilter> l = new LinkedList<>();
+ for (Class<? extends HeaderFilter> clazz : filterClasses) {
+ try {
+ l.add(clazz.newInstance());
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ }
+ return l;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+
+/**
+ * A header filter that can examine or modify, typically system headers for
+ * requests before they are sent, and responses before they are returned to the
+ * user. Some ability to resend requests is provided.
+ *
+ */
+interface HeaderFilter {
+
+ void request(HttpRequestImpl r) throws IOException;
+
+ /**
+ * Returns null if response ok to be given to user. Non null is a request
+ * that must be resent and its response given to user. If impl throws an
+ * exception that is returned to user instead.
+ */
+ HttpRequestImpl response(HttpResponseImpl r) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+/* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers
+ * sensibly:
+ * From a String like: 'timeout=15, max=5'
+ * create an array of Strings:
+ * { {"timeout", "15"},
+ * {"max", "5"}
+ * }
+ * From one like: 'Basic Realm="FuzzFace" Foo="Biz Bar Baz"'
+ * create one like (no quotes in literal):
+ * { {"basic", null},
+ * {"realm", "FuzzFace"}
+ * {"foo", "Biz Bar Baz"}
+ * }
+ * keys are converted to lower case, vals are left as is....
+ */
+class HeaderParser {
+
+ /* table of key/val pairs */
+ String raw;
+ String[][] tab;
+ int nkeys;
+ int asize = 10; // initial size of array is 10
+
+ public HeaderParser(String raw) {
+ this.raw = raw;
+ tab = new String[asize][2];
+ parse();
+ }
+
+ private HeaderParser () { }
+
+ /**
+ * Creates a new HeaderParser from this, whose keys (and corresponding
+ * values) range from "start" to "end-1"
+ */
+ public HeaderParser subsequence(int start, int end) {
+ if (start == 0 && end == nkeys) {
+ return this;
+ }
+ if (start < 0 || start >= end || end > nkeys)
+ throw new IllegalArgumentException("invalid start or end");
+ HeaderParser n = new HeaderParser();
+ n.tab = new String [asize][2];
+ n.asize = asize;
+ System.arraycopy (tab, start, n.tab, 0, (end-start));
+ n.nkeys= (end-start);
+ return n;
+ }
+
+ private void parse() {
+
+ if (raw != null) {
+ raw = raw.trim();
+ char[] ca = raw.toCharArray();
+ int beg = 0, end = 0, i = 0;
+ boolean inKey = true;
+ boolean inQuote = false;
+ int len = ca.length;
+ while (end < len) {
+ char c = ca[end];
+ if ((c == '=') && !inQuote) { // end of a key
+ tab[i][0] = new String(ca, beg, end-beg).toLowerCase(Locale.US);
+ inKey = false;
+ end++;
+ beg = end;
+ } else if (c == '\"') {
+ if (inQuote) {
+ tab[i++][1]= new String(ca, beg, end-beg);
+ inQuote=false;
+ do {
+ end++;
+ } while (end < len && (ca[end] == ' ' || ca[end] == ','));
+ inKey=true;
+ beg=end;
+ } else {
+ inQuote=true;
+ end++;
+ beg=end;
+ }
+ } else if (c == ' ' || c == ',') { // end key/val, of whatever we're in
+ if (inQuote) {
+ end++;
+ continue;
+ } else if (inKey) {
+ tab[i++][0] = (new String(ca, beg, end-beg)).toLowerCase(Locale.US);
+ } else {
+ tab[i++][1] = (new String(ca, beg, end-beg));
+ }
+ while (end < len && (ca[end] == ' ' || ca[end] == ',')) {
+ end++;
+ }
+ inKey = true;
+ beg = end;
+ } else {
+ end++;
+ }
+ if (i == asize) {
+ asize = asize * 2;
+ String[][] ntab = new String[asize][2];
+ System.arraycopy (tab, 0, ntab, 0, tab.length);
+ tab = ntab;
+ }
+ }
+ // get last key/val, if any
+ if (--end > beg) {
+ if (!inKey) {
+ if (ca[end] == '\"') {
+ tab[i++][1] = (new String(ca, beg, end-beg));
+ } else {
+ tab[i++][1] = (new String(ca, beg, end-beg+1));
+ }
+ } else {
+ tab[i++][0] = (new String(ca, beg, end-beg+1)).toLowerCase();
+ }
+ } else if (end == beg) {
+ if (!inKey) {
+ if (ca[end] == '\"') {
+ tab[i++][1] = String.valueOf(ca[end-1]);
+ } else {
+ tab[i++][1] = String.valueOf(ca[end]);
+ }
+ } else {
+ tab[i++][0] = String.valueOf(ca[end]).toLowerCase();
+ }
+ }
+ nkeys=i;
+ }
+ }
+
+ public String findKey(int i) {
+ if (i < 0 || i > asize)
+ return null;
+ return tab[i][0];
+ }
+
+ public String findValue(int i) {
+ if (i < 0 || i > asize)
+ return null;
+ return tab[i][1];
+ }
+
+ public String findValue(String key) {
+ return findValue(key, null);
+ }
+
+ public String findValue(String k, String Default) {
+ if (k == null)
+ return Default;
+ k = k.toLowerCase(Locale.US);
+ for (int i = 0; i < asize; ++i) {
+ if (tab[i][0] == null) {
+ return Default;
+ } else if (k.equals(tab[i][0])) {
+ return tab[i][1];
+ }
+ }
+ return Default;
+ }
+
+ class ParserIterator implements Iterator<String> {
+ int index;
+ boolean returnsValue; // or key
+
+ ParserIterator (boolean returnValue) {
+ returnsValue = returnValue;
+ }
+ @Override
+ public boolean hasNext () {
+ return index<nkeys;
+ }
+ @Override
+ public String next () {
+ if (index >= nkeys)
+ throw new NoSuchElementException();
+ return tab[index++][returnsValue?1:0];
+ }
+ }
+
+ public Iterator<String> keys () {
+ return new ParserIterator (false);
+ }
+
+ public Iterator<String> values () {
+ return new ParserIterator (true);
+ }
+
+ @Override
+ public String toString () {
+ Iterator<String> k = keys();
+ StringBuilder sb = new StringBuilder();
+ sb.append("{size=").append(asize).append(" nkeys=").append(nkeys)
+ .append(' ');
+ for (int i=0; k.hasNext(); i++) {
+ String key = k.next();
+ String val = findValue (i);
+ if (val != null && "".equals (val)) {
+ val = null;
+ }
+ sb.append(" {").append(key).append(val == null ? "" : "," + val)
+ .append('}');
+ if (k.hasNext()) {
+ sb.append (',');
+ }
+ }
+ sb.append (" }");
+ return sb.toString();
+ }
+
+ public int findInt(String k, int Default) {
+ try {
+ return Integer.parseInt(findValue(k, String.valueOf(Default)));
+ } catch (Throwable t) {
+ return Default;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Encapsulates one HTTP/1.1 request/responseAsync exchange.
+ */
+class Http1Exchange extends ExchangeImpl {
+
+ final HttpRequestImpl request; // main request
+ final List<CompletableFuture<?>> operations; // used for cancel
+ final Http1Request requestAction;
+ volatile Http1Response response;
+ final HttpConnection connection;
+ final HttpClientImpl client;
+ final ExecutorWrapper executor;
+
+ @Override
+ public String toString() {
+ return request.toString();
+ }
+
+ HttpRequestImpl request() {
+ return request;
+ }
+
+ Http1Exchange(Exchange exchange, HttpConnection connection)
+ throws IOException
+ {
+ super(exchange);
+ this.request = exchange.request();
+ this.client = request.client();
+ this.executor = client.executorWrapper();
+ this.operations = Collections.synchronizedList(new LinkedList<>());
+ if (connection != null) {
+ this.connection = connection;
+ } else {
+ InetSocketAddress addr = getAddress(request);
+ this.connection = HttpConnection.getConnection(addr, request);
+ }
+ this.requestAction = new Http1Request(request, this.connection);
+ }
+
+ private static InetSocketAddress getAddress(HttpRequestImpl req) {
+ URI uri = req.uri();
+ if (uri == null) {
+ return req.authority();
+ }
+ int port = uri.getPort();
+ if (port == -1) {
+ if (uri.getScheme().equalsIgnoreCase("https")) {
+ port = 443;
+ } else {
+ port = 80;
+ }
+ }
+ String host = uri.getHost();
+ if (req.proxy() == null) {
+ return new InetSocketAddress(host, port);
+ } else {
+ return InetSocketAddress.createUnresolved(host, port);
+ }
+ }
+
+ HttpConnection connection() {
+ return connection;
+ }
+
+ @Override
+ <T> T responseBody(HttpResponse.BodyProcessor<T> processor)
+ throws IOException
+ {
+ return responseBody(processor, true);
+ }
+
+ <T> T responseBody(HttpResponse.BodyProcessor<T> processor,
+ boolean return2Cache)
+ throws IOException
+ {
+ try {
+ T body = response.readBody(processor, return2Cache);
+ return body;
+ } catch (Throwable t) {
+ connection.close();
+ throw t;
+ }
+ }
+
+ @Override
+ <T> CompletableFuture<T> responseBodyAsync(HttpResponse.BodyProcessor<T> processor) {
+ CompletableFuture<T> cf = new CompletableFuture<>();
+ request.client()
+ .executorWrapper()
+ .execute(() -> {
+ try {
+ T body = responseBody(processor);
+ cf.complete(body);
+ } catch (Throwable e) {
+ cf.completeExceptionally(e);
+ }
+ },
+ () -> response.response.getAccessControlContext()); // TODO: fix
+ return cf;
+ }
+
+ @Override
+ void sendHeadersOnly() throws IOException, InterruptedException {
+ try {
+ if (!connection.connected()) {
+ connection.connect();
+ }
+ requestAction.sendHeadersOnly();
+ } catch (Throwable e) {
+ connection.close();
+ throw e;
+ }
+ }
+
+ @Override
+ void sendBody() throws IOException {
+ try {
+ requestAction.continueRequest();
+ } catch (Throwable e) {
+ connection.close();
+ throw e;
+ }
+ }
+
+ @Override
+ HttpResponseImpl getResponse() throws IOException {
+ try {
+ response = new Http1Response(connection, this);
+ response.readHeaders();
+ return response.response();
+ } catch (Throwable t) {
+ connection.close();
+ throw t;
+ }
+ }
+
+ @Override
+ void sendRequest() throws IOException, InterruptedException {
+ try {
+ if (!connection.connected()) {
+ connection.connect();
+ }
+ requestAction.sendRequest();
+ } catch (Throwable t) {
+ connection.close();
+ throw t;
+ }
+ }
+
+ private void closeConnection() {
+ connection.close();
+ }
+
+ @Override
+ CompletableFuture<Void> sendHeadersAsync() {
+ if (!connection.connected()) {
+ CompletableFuture<Void> op = connection.connectAsync()
+ .thenCompose(this::sendHdrsAsyncImpl)
+ .whenComplete((Void b, Throwable t) -> {
+ if (t != null)
+ closeConnection();
+ });
+ operations.add(op);
+ return op;
+ } else {
+ return sendHdrsAsyncImpl(null);
+ }
+ }
+
+ private CompletableFuture<Void> sendHdrsAsyncImpl(Void v) {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ executor.execute(() -> {
+ try {
+ requestAction.sendHeadersOnly();
+ cf.complete(null);
+ } catch (Throwable e) {
+ cf.completeExceptionally(e);
+ connection.close();
+ }
+ },
+ () -> request.getAccessControlContext());
+ operations.add(cf);
+ return cf;
+ }
+
+ /**
+ * Cancel checks to see if request and responseAsync finished already.
+ * If not it closes the connection and completes all pending operations
+ */
+ @Override
+ synchronized void cancel() {
+ if (requestAction != null && requestAction.finished()
+ && response != null && response.finished()) {
+ return;
+ }
+ connection.close();
+ IOException e = new IOException("Request cancelled");
+ int count = 0;
+ for (CompletableFuture<?> cf : operations) {
+ cf.completeExceptionally(e);
+ count++;
+ }
+ Log.logError("Http1Exchange.cancel: count=" + count);
+ }
+
+ CompletableFuture<HttpResponseImpl> getResponseAsyncImpl(Void v) {
+ CompletableFuture<HttpResponseImpl> cf = new CompletableFuture<>();
+ try {
+ response = new Http1Response(connection, Http1Exchange.this);
+ response.readHeaders();
+ cf.complete(response.response());
+ } catch (IOException e) {
+ cf.completeExceptionally(e);
+ }
+ return cf;
+ }
+
+ @Override
+ CompletableFuture<HttpResponseImpl> getResponseAsync(Void v) {
+ CompletableFuture<HttpResponseImpl> cf =
+ connection.whenReceivingResponse()
+ .thenCompose(this::getResponseAsyncImpl);
+
+ operations.add(cf);
+ return cf;
+ }
+
+ @Override
+ CompletableFuture<Void> sendBodyAsync() {
+ final CompletableFuture<Void> cf = new CompletableFuture<>();
+ executor.execute(() -> {
+ try {
+ requestAction.continueRequest();
+ cf.complete(null);
+ } catch (Throwable e) {
+ cf.completeExceptionally(e);
+ connection.close();
+ }
+ }, () -> request.getAccessControlContext());
+ operations.add(cf);
+ return cf;
+ }
+
+ @Override
+ CompletableFuture<Void> sendRequestAsync() {
+ CompletableFuture<Void> op;
+ if (!connection.connected()) {
+ op = connection.connectAsync()
+ .thenCompose(this::sendRequestAsyncImpl)
+ .whenComplete((Void v, Throwable t) -> {
+ if (t != null) {
+ closeConnection();
+ }
+ });
+ } else {
+ op = sendRequestAsyncImpl(null);
+ }
+ operations.add(op);
+ return op;
+ }
+
+ CompletableFuture<Void> sendRequestAsyncImpl(Void v) {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ executor.execute(() -> {
+ try {
+ requestAction.sendRequest();
+ cf.complete(null);
+ } catch (Throwable e) {
+ cf.completeExceptionally(e);
+ connection.close();
+ }
+ }, () -> request.getAccessControlContext());
+ operations.add(cf);
+ return cf;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.function.LongConsumer;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+/**
+ * A HTTP/1.1 request.
+ *
+ * send() -> Writes the request + body to the given channel, in one blocking
+ * operation.
+ */
+class Http1Request {
+
+ final HttpRequestImpl request;
+ final HttpConnection chan;
+ // Multiple buffers are used to hold different parts of request
+ // See line 206 and below for description
+ final ByteBuffer[] buffers;
+ final HttpRequest.BodyProcessor requestProc;
+ final HttpHeadersImpl userHeaders, systemHeaders;
+ final LongConsumer flowController;
+ boolean streaming;
+ long contentLength;
+
+ Http1Request(HttpRequestImpl request, HttpConnection connection)
+ throws IOException
+ {
+ this.request = request;
+ this.chan = connection;
+ buffers = new ByteBuffer[5]; // TODO: check
+ this.requestProc = request.requestProcessor();
+ this.userHeaders = request.getUserHeaders();
+ this.systemHeaders = request.getSystemHeaders();
+ this.flowController = this::dummy;
+ }
+
+ private void logHeaders() throws IOException {
+ StringBuilder sb = new StringBuilder(256);
+ sb.append("REQUEST HEADERS:\r\n");
+ collectHeaders1(sb, request, systemHeaders);
+ collectHeaders1(sb, request, userHeaders);
+ Log.logHeaders(sb.toString());
+ }
+
+ private void dummy(long x) {
+ // not used in this class
+ }
+
+ private void collectHeaders0() throws IOException {
+ if (Log.headers()) {
+ logHeaders();
+ }
+ StringBuilder sb = new StringBuilder(256);
+ collectHeaders1(sb, request, systemHeaders);
+ collectHeaders1(sb, request, userHeaders);
+ sb.append("\r\n");
+ String headers = sb.toString();
+ buffers[1] = ByteBuffer.wrap(headers.getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private void collectHeaders1(StringBuilder sb,
+ HttpRequestImpl request,
+ HttpHeadersImpl headers)
+ throws IOException
+ {
+ Map<String,List<String>> h = headers.directMap();
+ Set<Map.Entry<String,List<String>>> entries = h.entrySet();
+
+ for (Map.Entry<String,List<String>> entry : entries) {
+ String key = entry.getKey();
+ sb.append(key).append(": ");
+ List<String> values = entry.getValue();
+ int num = values.size();
+ for (String value : values) {
+ sb.append(value);
+ if (--num > 0) {
+ sb.append(',');
+ }
+ }
+ sb.append("\r\n");
+ }
+ }
+
+ private static final int BUFSIZE = 64 * 1024; // TODO: configurable?
+
+ private String getPathAndQuery(URI uri) {
+ String path = uri.getPath();
+ String query = uri.getQuery();
+ if (path == null || path.equals("")) {
+ path = "/";
+ }
+ if (query == null) {
+ query = "";
+ }
+ if (query.equals("")) {
+ return path;
+ } else {
+ return path + "?" + query;
+ }
+ }
+
+ private String authorityString(InetSocketAddress addr) {
+ return addr.getHostString() + ":" + addr.getPort();
+ }
+
+ private String requestURI() {
+ URI uri = request.uri();
+ String method = request.method();
+
+ if ((request.proxy() == null && !method.equals("CONNECT"))
+ || request.isWebSocket()) {
+ return getPathAndQuery(uri);
+ }
+ if (request.secure()) {
+ if (request.method().equals("CONNECT")) {
+ // use authority for connect itself
+ return authorityString(request.authority());
+ } else {
+ // requests over tunnel do not require full URL
+ return getPathAndQuery(uri);
+ }
+ }
+ return uri == null? authorityString(request.authority()) : uri.toString();
+ }
+
+ void sendHeadersOnly() throws IOException {
+ collectHeaders();
+ chan.write(buffers, 0, 2);
+ }
+
+ void sendRequest() throws IOException {
+ collectHeaders();
+ if (contentLength == 0) {
+ chan.write(buffers, 0, 2);
+ } else if (contentLength > 0) {
+ writeFixedContent(true);
+ } else {
+ writeStreamedContent(true);
+ }
+ setFinished();
+ }
+
+ private boolean finished;
+
+ synchronized boolean finished() {
+ return finished;
+ }
+
+ synchronized void setFinished() {
+ finished = true;
+ }
+
+ private void collectHeaders() throws IOException {
+ if (Log.requests() && request != null) {
+ Log.logRequest(request.toString());
+ }
+ String uriString = requestURI();
+ StringBuilder sb = new StringBuilder(64);
+ sb.append(request.method())
+ .append(' ')
+ .append(uriString)
+ .append(" HTTP/1.1\r\n");
+ String cmd = sb.toString();
+
+ buffers[0] = ByteBuffer.wrap(cmd.getBytes(StandardCharsets.US_ASCII));
+ URI uri = request.uri();
+ if (uri != null) {
+ systemHeaders.setHeader("Host", uri.getHost());
+ }
+ if (request == null) {
+ // this is not a user request. No content
+ contentLength = 0;
+ } else {
+ contentLength = requestProc.onRequestStart(request, flowController);
+ }
+
+ if (contentLength == 0) {
+ systemHeaders.setHeader("Content-Length", "0");
+ collectHeaders0();
+ } else if (contentLength > 0) {
+ /* [0] request line [1] headers [2] body */
+ systemHeaders.setHeader("Content-Length",
+ Integer.toString((int) contentLength));
+ streaming = false;
+ collectHeaders0();
+ buffers[2] = chan.getBuffer();
+ } else {
+ /* Chunked:
+ *
+ * [0] request line [1] headers [2] chunk header [3] chunk data [4]
+ * final chunk header and trailing CRLF of previous chunks
+ *
+ * 2,3,4 used repeatedly */
+ streaming = true;
+ systemHeaders.setHeader("Transfer-encoding", "chunked");
+ collectHeaders0();
+ buffers[3] = chan.getBuffer();
+ }
+ }
+
+ // The following two methods used by Http1Exchange to handle expect continue
+
+ void continueRequest() throws IOException {
+ if (streaming) {
+ writeStreamedContent(false);
+ } else {
+ writeFixedContent(false);
+ }
+ setFinished();
+ }
+
+ /* Entire request is sent, or just body only */
+ private void writeStreamedContent(boolean includeHeaders)
+ throws IOException
+ {
+ if (requestProc instanceof HttpRequest.BodyProcessor) {
+ HttpRequest.BodyProcessor pullproc = requestProc;
+ int startbuf, nbufs;
+
+ if (includeHeaders) {
+ startbuf = 0;
+ nbufs = 5;
+ } else {
+ startbuf = 2;
+ nbufs = 3;
+ }
+ try {
+ // TODO: currently each write goes out as one chunk
+ // TODO: should be collecting data and buffer it.
+
+ buffers[3].clear();
+ boolean done = pullproc.onRequestBodyChunk(buffers[3]);
+ int chunklen = buffers[3].position();
+ buffers[2] = getHeader(chunklen);
+ buffers[3].flip();
+ buffers[4] = CRLF_BUFFER();
+ chan.write(buffers, startbuf, nbufs);
+ while (!done) {
+ buffers[3].clear();
+ done = pullproc.onRequestBodyChunk(buffers[3]);
+ if (done)
+ break;
+ buffers[3].flip();
+ chunklen = buffers[3].remaining();
+ buffers[2] = getHeader(chunklen);
+ buffers[4] = CRLF_BUFFER();
+ chan.write(buffers, 2, 3);
+ }
+ buffers[3] = EMPTY_CHUNK_HEADER();
+ buffers[4] = CRLF_BUFFER();
+ chan.write(buffers, 3, 2);
+ } catch (IOException e) {
+ requestProc.onRequestError(e);
+ throw e;
+ }
+ }
+ }
+ /* Entire request is sent, or just body only */
+ private void writeFixedContent(boolean includeHeaders)
+ throws IOException
+ {
+ try {
+ int startbuf, nbufs;
+
+ if (contentLength == 0) {
+ return;
+ }
+ if (includeHeaders) {
+ startbuf = 0;
+ nbufs = 3;
+ } else {
+ startbuf = 2;
+ nbufs = 1;
+ buffers[0].clear().flip();
+ buffers[1].clear().flip();
+ }
+ buffers[2] = chan.getBuffer();
+ if (requestProc instanceof HttpRequest.BodyProcessor) {
+ HttpRequest.BodyProcessor pullproc = requestProc;
+
+ boolean done = pullproc.onRequestBodyChunk(buffers[2]);
+ buffers[2].flip();
+ long headersLength = buffers[0].remaining() + buffers[1].remaining();
+ long contentWritten = buffers[2].remaining();
+ chan.checkWrite(headersLength + contentWritten,
+ buffers,
+ startbuf,
+ nbufs);
+ while (!done) {
+ buffers[2].clear();
+ done = pullproc.onRequestBodyChunk(buffers[2]);
+ buffers[2].flip();
+ long len = buffers[2].remaining();
+ if (contentWritten + len > contentLength) {
+ break;
+ }
+ chan.checkWrite(len, buffers[2]);
+ contentWritten += len;
+ }
+ if (contentWritten != contentLength) {
+ throw new IOException("wrong content length");
+ }
+ }
+ } catch (IOException e) {
+ requestProc.onRequestError(e);
+ throw e;
+ }
+ }
+
+ private static final byte[] CRLF = {'\r', '\n'};
+ private static final byte[] EMPTY_CHUNK_BYTES = {'0', '\r', '\n'};
+
+ private ByteBuffer CRLF_BUFFER() {
+ return ByteBuffer.wrap(CRLF);
+ }
+
+ private ByteBuffer EMPTY_CHUNK_HEADER() {
+ return ByteBuffer.wrap(EMPTY_CHUNK_BYTES);
+ }
+
+ /* Returns a header for a particular chunk size */
+ private static ByteBuffer getHeader(int size){
+ String hexStr = Integer.toHexString(size);
+ byte[] hexBytes = hexStr.getBytes(US_ASCII);
+ byte[] header = new byte[hexStr.length()+2];
+ System.arraycopy(hexBytes, 0, header, 0, hexBytes.length);
+ header[hexBytes.length] = CRLF[0];
+ header[hexBytes.length+1] = CRLF[1];
+ return ByteBuffer.wrap(header);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.LongConsumer;
+import static java.net.http.HttpClient.Version.HTTP_1_1;
+
+/**
+ * Handles a HTTP/1.1 response in two blocking calls. readHeaders() and
+ * readBody(). There can be more than one of these per Http exchange.
+ */
+class Http1Response {
+
+ private ResponseContent content;
+ private final HttpRequestImpl request;
+ HttpResponseImpl response;
+ private final HttpConnection connection;
+ private ResponseHeaders headers;
+ private int responseCode;
+ private ByteBuffer buffer; // same buffer used for reading status line and headers
+ private final Http1Exchange exchange;
+ private final boolean redirecting; // redirecting
+ private boolean return2Cache; // return connection to cache when finished
+
+ Http1Response(HttpConnection conn, Http1Exchange exchange) {
+ this.request = exchange.request();
+ this.exchange = exchange;
+ this.connection = conn;
+ this.redirecting = false;
+ buffer = connection.getRemaining();
+ }
+
+ // called when the initial read should come from a buffer left
+ // over from a previous response.
+ void setBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void readHeaders() throws IOException {
+ String statusline = readStatusLine();
+ if (statusline == null) {
+ if (Log.errors()) {
+ Log.logError("Connection closed. Retry");
+ }
+ connection.close();
+ // connection was closed
+ throw new IOException("Connection closed");
+ }
+ if (!statusline.startsWith("HTTP/1.")) {
+ throw new IOException("Invalid status line: " + statusline);
+ }
+ char c = statusline.charAt(7);
+ responseCode = Integer.parseInt(statusline.substring(9, 12));
+
+ headers = new ResponseHeaders(connection, buffer);
+ headers.initHeaders();
+ if (Log.headers()) {
+ logHeaders(headers);
+ }
+ response = new HttpResponseImpl(responseCode,
+ exchange.exchange,
+ headers,
+ null,
+ connection.sslParameters(),
+ HTTP_1_1,
+ connection);
+ }
+
+ private boolean finished;
+
+ synchronized void completed() {
+ finished = true;
+ }
+
+ synchronized boolean finished() {
+ return finished;
+ }
+
+ // Blocking flow controller implementation. Only works when a
+ // thread is dedicated to reading response body
+
+ static class FlowController implements LongConsumer {
+ long window ;
+
+ @Override
+ public synchronized void accept(long value) {
+ window += value;
+ notifyAll();
+ }
+
+ public synchronized void request(long value) throws InterruptedException {
+ while (window < value) {
+ wait();
+ }
+ window -= value;
+ }
+ }
+
+ FlowController flowController;
+
+ int fixupContentLen(int clen) {
+ if (request.method().equalsIgnoreCase("HEAD")) {
+ return 0;
+ }
+ if (clen == -1) {
+ if (headers.firstValue("Transfer-encoding").orElse("")
+ .equalsIgnoreCase("chunked")) {
+ return -1;
+ }
+ return 0;
+ }
+ return clen;
+ }
+
+ private void returnBuffer(ByteBuffer buf) {
+ // not currently used, but will be when we change SSL to use fixed
+ // sized buffers and a single buffer pool for HttpClientImpl
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readBody(java.net.http.HttpResponse.BodyProcessor<T> p,
+ boolean return2Cache)
+ throws IOException
+ {
+ T body = null; // TODO: check null case below
+ this.return2Cache = return2Cache;
+ final java.net.http.HttpResponse.BodyProcessor<T> pusher = p;
+
+ int clen0 = headers.getContentLength();
+ final int clen = fixupContentLen(clen0);
+
+ flowController = new FlowController();
+
+ body = pusher.onResponseBodyStart(clen, headers, flowController);
+
+ ExecutorWrapper executor;
+ if (body == null) {
+ executor = ExecutorWrapper.callingThread();
+ } else {
+ executor = request.client().executorWrapper();
+ }
+
+ final ResponseHeaders h = headers;
+ if (body == null) {
+ content = new ResponseContent(connection,
+ clen,
+ h,
+ pusher,
+ flowController);
+ content.pushBody(headers.getResidue());
+ body = pusher.onResponseComplete();
+ completed();
+ onFinished();
+ return body;
+ } else {
+ executor.execute(() -> {
+ try {
+ content = new ResponseContent(connection,
+ clen,
+ h,
+ pusher,
+ flowController);
+ content.pushBody(headers.getResidue());
+ pusher.onResponseComplete();
+ completed();
+ onFinished();
+ } catch (Throwable e) {
+ pusher.onResponseError(e);
+ }
+ },
+ () -> response.getAccessControlContext());
+ }
+ return body;
+ }
+
+ private void onFinished() {
+ connection.buffer = content.getResidue();
+ if (return2Cache) {
+ connection.returnToCache(headers);
+ }
+ }
+
+ private void logHeaders(ResponseHeaders headers) {
+ Map<String, List<String>> h = headers.mapInternal();
+ Set<String> keys = h.keySet();
+ Set<Map.Entry<String, List<String>>> entries = h.entrySet();
+ for (Map.Entry<String, List<String>> entry : entries) {
+ String key = entry.getKey();
+ StringBuilder sb = new StringBuilder();
+ sb.append(key).append(": ");
+ List<String> values = entry.getValue();
+ if (values != null) {
+ for (String value : values) {
+ sb.append(value).append(' ');
+ }
+ }
+ Log.logHeaders(sb.toString());
+ }
+ }
+
+ HttpResponseImpl response() {
+ return response;
+ }
+
+ boolean redirecting() {
+ return redirecting;
+ }
+
+ HttpHeaders responseHeaders() {
+ return headers;
+ }
+
+ int responseCode() {
+ return responseCode;
+ }
+
+ static final char CR = '\r';
+ static final char LF = '\n';
+
+ private ByteBuffer getBuffer() throws IOException {
+ if (buffer == null || !buffer.hasRemaining()) {
+ buffer = connection.read();
+ }
+ return buffer;
+ }
+
+ ByteBuffer buffer() {
+ return buffer;
+ }
+
+ String readStatusLine() throws IOException {
+ boolean cr = false;
+ StringBuilder statusLine = new StringBuilder(128);
+ ByteBuffer b;
+ while ((b = getBuffer()) != null) {
+ byte[] buf = b.array();
+ int offset = b.position();
+ int len = b.limit() - offset;
+
+ for (int i = 0; i < len; i++) {
+ char c = (char) buf[i+offset];
+
+ if (cr) {
+ if (c == LF) {
+ b.position(i + 1 + offset);
+ return statusLine.toString();
+ } else {
+ throw new IOException("invalid status line");
+ }
+ }
+ if (c == CR) {
+ cr = true;
+ } else {
+ statusLine.append(c);
+ }
+ }
+ // unlikely, but possible, that multiple reads required
+ b.position(b.limit());
+ }
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015, 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
+ */
+package java.net.http;
+
+class Http2ClientImpl {
+ Http2ClientImpl(HttpClientImpl t) {}
+ String getSettingsString() {return "";}
+ void debugPrint() {}
+ Http2Connection getConnectionFor(HttpRequestImpl r) {
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.Authenticator;
+import java.net.CookieManager;
+import java.net.ProxySelector;
+import java.net.URI;
+import static java.net.http.Utils.BUFSIZE;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
+import java.nio.channels.Selector;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.*;
+import java.security.NoSuchAlgorithmException;
+import java.util.ListIterator;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+class Http2Connection {
+ static CompletableFuture<Http2Connection> createAsync(
+ HttpConnection connection, Http2ClientImpl client2, Exchange exchange) {
+ return null;
+ }
+
+ Http2Connection(HttpConnection connection, Http2ClientImpl client2,
+ Exchange exchange) throws IOException, InterruptedException {
+ }
+
+ Stream getStream(int i) {return null;}
+ Stream createStream(Exchange ex) {return null;}
+ void putConnection() {}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClient.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.net.Authenticator;
+import java.net.CookieManager;
+import java.net.InetSocketAddress;
+import java.net.NetPermission;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * A container for configuration information common to multiple {@link
+ * HttpRequest}s. All requests are associated with, and created from a {@code
+ * HttpClient}.
+ *
+ * <p> {@code HttpClient}s are immutable and created from a builder returned
+ * from {@link HttpClient#create()}. Request builders that are associated with
+ * an application created client, are created by calling {@link #request(URI) }.
+ * It is also possible to create a request builder directly which is associated
+ * with the <i>default</i> {@code HttpClient} by calling {@link
+ * HttpRequest#create(URI)}.
+ *
+ * <p> The HTTP API functions asynchronously (using {@link
+ * java.util.concurrent.CompletableFuture}) and also in a simple synchronous
+ * mode, where all work may be done on the calling thread. In asynchronous mode,
+ * work is done on the threads supplied by the client's {@link
+ * java.util.concurrent.ExecutorService}.
+ *
+ * <p> <a name="defaultclient"></a> The <i>default</i> {@code HttpClient} is
+ * used whenever a request is created without specifying a client explicitly
+ * (by calling {@link HttpRequest#create(java.net.URI) HttpRequest.create}).
+ * There is only one static instance of this {@code HttpClient}. A reference to
+ * the default client can be obtained by calling {@link #getDefault() }. If a
+ * security manager is set, then a permission is required for this.
+ *
+ * <p> See {@link HttpRequest} for examples of usage of this API.
+ *
+ * @since 9
+ */
+public abstract class HttpClient {
+
+ HttpClient() {}
+
+ private static HttpClient defaultClient;
+
+ /**
+ * Creates a new {@code HttpClient} builder.
+ *
+ * @return a {@code HttpClient.Builder}
+ */
+ public static Builder create() {
+ return new HttpClientBuilderImpl();
+ }
+
+ //public abstract void debugPrint();
+
+ /**
+ * Returns the default {@code HttpClient} that is used when a {@link
+ * HttpRequest} is created without specifying a client. If a security
+ * manager is set, then its {@code checkPermission} method is called with a
+ * {@link java.net.NetPermission} specifying the name "getDefaultHttpClient".
+ * If the caller does not possess this permission a {@code SecurityException}
+ * is thrown.
+ *
+ * @implNote Code running under a security manager can avoid the security
+ * manager check by creating a {@code HttpClient} explicitly.
+ *
+ * @return the default {@code HttpClient}
+ * @throws SecurityException if the caller does not have the required
+ * permission
+ */
+ public synchronized static HttpClient getDefault() {
+ Utils.checkNetPermission("getDefaultHttpClient");
+ if (defaultClient == null) {
+ Builder b = create();
+ defaultClient = b.executorService(Executors.newCachedThreadPool())
+ .build();
+ }
+ return defaultClient;
+ }
+
+ /**
+ * Creates a {@code HttpRequest} builder associated with this client.
+ *
+ * @return a new builder
+ */
+ public abstract HttpRequest.Builder request();
+
+ /**
+ * Creates a {@code HttpRequest} builder associated with this client and
+ * using the given request URI.
+ *
+ * @param uri the request URI
+ * @return a new builder
+ */
+ public abstract HttpRequest.Builder request(URI uri);
+
+ /**
+ * A builder of immutable {@link HttpClient}s. {@code HttpClient.Builder}s
+ * are created by calling {@link HttpClient#create()}.
+ *
+ * <p> Each of the setter methods in this class modifies the state of the
+ * builder and returns <i>this</i> (ie. the same instance). The methods are
+ * not synchronized and should not be called from multiple threads without
+ * external synchronization.
+ *
+ * <p> {@link #build() } returns a new {@code HttpClient} each time it is
+ * called.
+ *
+ * @since 9
+ */
+ public abstract static class Builder {
+
+ Builder() {}
+
+ /**
+ * Sets a cookie manager.
+ *
+ * @param manager the CookieManager
+ * @return this builder
+ * @throws NullPointerException if {@code manager} is null
+ */
+ public abstract Builder cookieManager(CookieManager manager);
+
+ /**
+ * Sets an SSLContext. If a security manager is set, then the caller
+ * must have the {@link java.net.NetPermission NetPermission}
+ * ("setSSLContext")
+ *
+ * <p> The effect of not calling this method, is that a default {@link
+ * javax.net.ssl.SSLContext} is used, which is normally adequate for
+ * client applications that do not need to specify protocols, or require
+ * client authentication.
+ *
+ * @param sslContext the SSLContext
+ * @return this builder
+ * @throws NullPointerException if {@code sslContext} is null
+ * @throws SecurityException if a security manager is set and the
+ * caller does not have any required permission
+ */
+ public abstract Builder sslContext(SSLContext sslContext);
+
+ /**
+ * Sets an SSLParameters. If this method is not called, then a default
+ * set of parameters are used. The contents of the given object are
+ * copied. Some parameters which are used internally by the HTTP protocol
+ * implementation (such as application protocol list) should not be set
+ * by callers, as they are ignored.
+ *
+ * @param sslParameters the SSLParameters
+ * @return this builder
+ * @throws NullPointerException if {@code sslParameters} is null
+ */
+ public abstract Builder sslParameters(SSLParameters sslParameters);
+
+ /**
+ * Sets the ExecutorService to be used for sending and receiving
+ * asynchronous requests. If this method is not called, a default
+ * executor service is set, which is the one returned from {@link
+ * java.util.concurrent.Executors#newCachedThreadPool()
+ * Executors.newCachedThreadPool}.
+ *
+ * @param s the ExecutorService
+ * @return this builder
+ * @throws NullPointerException if {@code s} is null
+ */
+ public abstract Builder executorService(ExecutorService s);
+
+ /**
+ * Specifies whether requests will automatically follow redirects issued
+ * by the server. This setting can be overridden on each request. The
+ * default value for this setting is {@link Redirect#NEVER NEVER}
+ *
+ * @param policy the redirection policy
+ * @return this builder
+ * @throws NullPointerException if {@code policy} is null
+ */
+ public abstract Builder followRedirects(Redirect policy);
+
+ /**
+ * Requests a specific HTTP protocol version where possible. If not set,
+ * the version defaults to {@link HttpClient.Version#HTTP_1_1}. If
+ * {@link HttpClient.Version#HTTP_2} is set, then each request will
+ * attempt to upgrade to HTTP/2. If the upgrade succeeds, then the
+ * response to this request will use HTTP/2 and all subsequent requests
+ * and responses to the same
+ * <a href="https://tools.ietf.org/html/rfc6454#section-4">origin server</a>
+ * will use HTTP/2. If the upgrade fails, then the response will be
+ * handled using HTTP/1.1
+ *
+ * <p>This setting can be over-ridden per request.
+ *
+ * @param version the requested HTTP protocol version
+ * @return this builder
+ * @throws NullPointerException if {@code version} is null
+ */
+ public abstract Builder version(HttpClient.Version version);
+
+ /**
+ * Sets the default priority for any HTTP/2 requests sent from this
+ * client. The value provided must be between {@code 1} and {@code 255}.
+ *
+ * @param priority the priority weighting
+ * @return this builder
+ * @throws IllegalArgumentException if the given priority is out of range
+ */
+ public abstract Builder priority(int priority);
+
+ /**
+ * Enables pipelining mode for HTTP/1.1 requests sent through this
+ * client. When pipelining is enabled requests to the same destination
+ * are sent over existing TCP connections that may already have requests
+ * outstanding. This reduces the number of connections, but may have
+ * a performance impact since responses must be delivered in the same
+ * order that they were sent. By default, pipelining is disabled.
+ *
+ * @param enable {@code true} enables pipelining
+ * @return this builder
+ * @throws UnsupportedOperationException if pipelining mode is not
+ * supported by this implementation
+ */
+ public abstract Builder pipelining(boolean enable);
+
+ /**
+ * Sets a {@link java.net.ProxySelector} for this client. If no selector
+ * is set, then no proxies are used. If a {@code null} parameter is
+ * given then the system wide default proxy selector is used.
+ *
+ * @implNote {@link java.net.ProxySelector#of(InetSocketAddress)}
+ * provides a ProxySelector which uses one proxy for all requests.
+ *
+ * @param selector the ProxySelector
+ * @return this builder
+ */
+ public abstract Builder proxy(ProxySelector selector);
+
+ /**
+ * Sets an authenticator to use for HTTP authentication.
+ *
+ * @param a the Authenticator
+ * @return this builder
+ */
+ public abstract Builder authenticator(Authenticator a);
+
+ /**
+ * Returns a {@link HttpClient} built from the current state of this
+ * builder.
+ *
+ * @return this builder
+ */
+ public abstract HttpClient build();
+ }
+
+
+ /**
+ * Returns an {@code Optional} which contains this client's {@link
+ * CookieManager}. If no CookieManager was set in this client's builder,
+ * then the {@code Optional} is empty.
+ *
+ * @return an {@code Optional} containing this client's CookieManager
+ */
+ public abstract Optional<CookieManager> cookieManager();
+
+ /**
+ * Returns the follow-redirects setting for this client. The default value
+ * for this setting is {@link HttpClient.Redirect#NEVER}
+ *
+ * @return this client's follow redirects setting
+ */
+ public abstract Redirect followRedirects();
+
+ /**
+ * Returns an {@code Optional} containing the ProxySelector for this client.
+ * If no proxy is set then the {@code Optional} is empty.
+ *
+ * @return an {@code Optional} containing this client's proxy selector
+ */
+ public abstract Optional<ProxySelector> proxy();
+
+ /**
+ * Returns the SSLContext, if one was set on this client. If a security
+ * manager is set then then caller must then the caller must have the
+ * {@link java.net.NetPermission NetPermission}("getSSLContext") permission.
+ * If no SSLContext was set, then the default context is returned.
+ *
+ * @return this client's SSLContext
+ */
+ public abstract SSLContext sslContext();
+
+ /**
+ * Returns an {@code Optional} containing the {@link SSLParameters} set on
+ * this client. If no {@code SSLParameters} were set in the client's builder,
+ * then the {@code Optional} is empty.
+ *
+ * @return an {@code Optional} containing this client's SSLParameters
+ */
+ public abstract Optional<SSLParameters> sslParameters();
+
+ /**
+ * Returns an {@code Optional} containing the {@link Authenticator} set on
+ * this client. If no {@code Authenticator} was set in the client's builder,
+ * then the {@code Optional} is empty.
+ *
+ * @return an {@code Optional} containing this client's Authenticator
+ */
+ public abstract Optional<Authenticator> authenticator();
+
+ /**
+ * Returns the HTTP protocol version requested for this client. The default
+ * value is {@link HttpClient.Version#HTTP_1_1}
+ *
+ * @return the HTTP protocol version requested
+ */
+ public abstract HttpClient.Version version();
+
+ /**
+ * Returns whether this client supports HTTP/1.1 pipelining.
+ *
+ * @return whether pipelining allowed
+ */
+ public abstract boolean pipelining();
+
+ /**
+ * Returns the {@code ExecutorService} set on this client. If an {@code
+ * ExecutorService} was not set on the client's builder, then a default
+ * object is returned. The default ExecutorService is created independently
+ * for each client.
+ *
+ * @return this client's ExecutorService
+ */
+ public abstract ExecutorService executorService();
+
+ /**
+ * The HTTP protocol version.
+ *
+ * @since 9
+ */
+ public static enum Version {
+
+ /**
+ * HTTP version 1.1
+ */
+ HTTP_1_1,
+
+ /**
+ * HTTP version 2
+ */
+ HTTP_2
+ }
+
+ /**
+ * Defines automatic redirection policy. This is checked whenever a 3XX
+ * response code is received. If redirection does not happen automatically
+ * then the response is returned to the user, where it can be handled
+ * manually.
+ *
+ * <p> {@code Redirect} policy is set via the {@link
+ * HttpClient.Builder#followRedirects(HttpClient.Redirect)} method.
+ *
+ * @since 9
+ */
+ public static enum Redirect {
+
+ /**
+ * Never redirect.
+ */
+ NEVER,
+
+ /**
+ * Always redirect.
+ */
+ ALWAYS,
+
+ /**
+ * Redirect to same protocol only. Redirection may occur from HTTP URLs
+ * to other HTTP URLs, and from HTTPS URLs to other HTTPS URLs.
+ */
+ SAME_PROTOCOL,
+
+ /**
+ * Redirect always except from HTTPS URLs to HTTP URLs.
+ */
+ SECURE
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.net.Authenticator;
+import java.net.CookieManager;
+import java.net.ProxySelector;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+class HttpClientBuilderImpl extends HttpClient.Builder {
+
+ CookieManager cookieManager;
+ HttpClient.Redirect followRedirects;
+ ProxySelector proxy;
+ Authenticator authenticator;
+ HttpClient.Version version = HttpClient.Version.HTTP_1_1;
+ ExecutorService executor;
+ // Security parameters
+ SSLContext sslContext;
+ SSLParameters sslParams;
+ int priority = -1;
+
+ @Override
+ public HttpClientBuilderImpl cookieManager(CookieManager manager) {
+ Objects.requireNonNull(manager);
+ this.cookieManager = manager;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl sslContext(SSLContext sslContext) {
+ Objects.requireNonNull(sslContext);
+ Utils.checkNetPermission("setSSLContext");
+ this.sslContext = sslContext;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl sslParameters(SSLParameters sslParameters) {
+ Objects.requireNonNull(sslParameters);
+ this.sslParams = sslParameters;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl executorService(ExecutorService s) {
+ Objects.requireNonNull(s);
+ this.executor = s;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl followRedirects(HttpClient.Redirect policy) {
+ Objects.requireNonNull(policy);
+ this.followRedirects = policy;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl version(HttpClient.Version version) {
+ Objects.requireNonNull(version);
+ this.version = version;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl priority(int priority) {
+ if (priority < 1 || priority > 255) {
+ throw new IllegalArgumentException("priority must be between 1 and 255");
+ }
+ this.priority = priority;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl pipelining(boolean enable) {
+ //To change body of generated methods, choose Tools | Templates.
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl proxy(ProxySelector proxy) {
+ Objects.requireNonNull(proxy);
+ this.proxy = proxy;
+ return this;
+ }
+
+
+ @Override
+ public HttpClientBuilderImpl authenticator(Authenticator a) {
+ Objects.requireNonNull(a);
+ this.authenticator = a;
+ return this;
+ }
+
+ @Override
+ public HttpClient build() {
+ return HttpClientImpl.create(this);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.Authenticator;
+import java.net.CookieManager;
+import java.net.ProxySelector;
+import java.net.URI;
+import static java.net.http.Utils.BUFSIZE;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
+import java.nio.channels.Selector;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.security.NoSuchAlgorithmException;
+import java.util.ListIterator;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Client implementation. Contains all configuration information and also
+ * the selector manager thread which allows async events to be registered
+ * and delivered when they occur. See AsyncEvent.
+ */
+class HttpClientImpl extends HttpClient implements BufferHandler {
+
+ private final CookieManager cookieManager;
+ private final Redirect followRedirects;
+ private final ProxySelector proxySelector;
+ private final Authenticator authenticator;
+ private final Version version;
+ private boolean pipelining = false;
+ private final ConnectionPool connections;
+ private final ExecutorWrapper executor;
+ // Security parameters
+ private final SSLContext sslContext;
+ private final SSLParameters sslParams;
+ private final SelectorManager selmgr;
+ private final FilterFactory filters;
+ private final Http2ClientImpl client2;
+ private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
+ private final LinkedList<TimeoutEvent> timeouts;
+
+ //@Override
+ void debugPrint() {
+ selmgr.debugPrint();
+ client2.debugPrint();
+ }
+
+ public static HttpClientImpl create(HttpClientBuilderImpl builder) {
+ HttpClientImpl impl = new HttpClientImpl(builder);
+ impl.start();
+ return impl;
+ }
+
+ private HttpClientImpl(HttpClientBuilderImpl builder) {
+ if (builder.sslContext == null) {
+ try {
+ sslContext = SSLContext.getDefault();
+ } catch (NoSuchAlgorithmException ex) {
+ throw new InternalError(ex);
+ }
+ } else {
+ sslContext = builder.sslContext;
+ }
+ ExecutorService ex = builder.executor;
+ if (ex == null) {
+ ex = Executors.newCachedThreadPool((r) -> {
+ Thread t = defaultFactory.newThread(r);
+ t.setDaemon(true);
+ return t;
+ });
+ } else {
+ ex = builder.executor;
+ }
+ client2 = new Http2ClientImpl(this);
+ executor = ExecutorWrapper.wrap(ex);
+ cookieManager = builder.cookieManager;
+ followRedirects = builder.followRedirects == null ?
+ Redirect.NEVER : builder.followRedirects;
+ this.proxySelector = builder.proxy;
+ authenticator = builder.authenticator;
+ version = builder.version;
+ sslParams = builder.sslParams;
+ connections = new ConnectionPool();
+ connections.start();
+ timeouts = new LinkedList<>();
+ try {
+ selmgr = new SelectorManager();
+ } catch (IOException e) {
+ // unlikely
+ throw new InternalError(e);
+ }
+ selmgr.setDaemon(true);
+ selmgr.setName("HttpSelector");
+ filters = new FilterFactory();
+ initFilters();
+ }
+
+ private void start() {
+ selmgr.start();
+ }
+
+ /**
+ * Wait for activity on given exchange (assuming blocking = false).
+ * It's a no-op if blocking = true. In particular, the following occurs
+ * in the SelectorManager thread.
+ *
+ * 1) mark the connection non-blocking
+ * 2) add to selector
+ * 3) If selector fires for this exchange then
+ * 4) - mark connection as blocking
+ * 5) - call AsyncEvent.handle()
+ *
+ * If exchange needs to block again, then call registerEvent() again
+ */
+ void registerEvent(AsyncEvent exchange) throws IOException {
+ selmgr.register(exchange);
+ }
+
+ Http2ClientImpl client2() {
+ return client2;
+ }
+
+ LinkedList<ByteBuffer> freelist = new LinkedList<>();
+
+ @Override
+ public synchronized ByteBuffer getBuffer() {
+ if (freelist.isEmpty()) {
+ return ByteBuffer.allocate(BUFSIZE);
+ }
+ return freelist.removeFirst();
+ }
+
+ @Override
+ public synchronized void returnBuffer(ByteBuffer buffer) {
+ buffer.clear();
+ freelist.add(buffer);
+ }
+
+
+ // Main loop for this client's selector
+
+ class SelectorManager extends Thread {
+
+ final Selector selector;
+ boolean closed;
+
+ final List<AsyncEvent> readyList;
+ final List<AsyncEvent> registrations;
+
+ List<AsyncEvent> debugList;
+
+ SelectorManager() throws IOException {
+ readyList = new LinkedList<>();
+ registrations = new LinkedList<>();
+ debugList = new LinkedList<>();
+ selector = Selector.open();
+ }
+
+ // This returns immediately. So caller not allowed to send/receive
+ // on connection.
+
+ synchronized void register(AsyncEvent e) throws IOException {
+ registrations.add(e);
+ selector.wakeup();
+ }
+
+ void wakeupSelector() {
+ selector.wakeup();
+ }
+
+ synchronized void shutdown() {
+ closed = true;
+ try {
+ selector.close();
+ } catch (IOException e) {}
+ }
+
+ private List<AsyncEvent> copy(List<AsyncEvent> list) {
+ LinkedList<AsyncEvent> c = new LinkedList<>();
+ for (AsyncEvent e : list) {
+ c.add(e);
+ }
+ return c;
+ }
+
+ synchronized void debugPrint() {
+ System.err.println("Selecting on:");
+ for (AsyncEvent e : debugList) {
+ System.err.println(opvals(e.interestOps()));
+ }
+ }
+
+ String opvals(int i) {
+ StringBuilder sb = new StringBuilder();
+ if ((i & OP_READ) != 0)
+ sb.append("OP_READ ");
+ if ((i & OP_CONNECT) != 0)
+ sb.append("OP_CONNECT ");
+ if ((i & OP_WRITE) != 0)
+ sb.append("OP_WRITE ");
+ return sb.toString();
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ synchronized (this) {
+ debugList = copy(registrations);
+ for (AsyncEvent exchange : registrations) {
+ SelectableChannel c = exchange.channel();
+ try {
+ c.configureBlocking(false);
+ c.register(selector,
+ exchange.interestOps(),
+ exchange);
+ } catch (IOException e) {
+ Log.logError("HttpClientImpl: " + e);
+ c.close();
+ // let the exchange deal with it
+ handleEvent(exchange);
+ }
+ }
+ registrations.clear();
+ }
+ long timeval = getTimeoutValue();
+ long now = System.currentTimeMillis();
+ int n = selector.select(timeval);
+ if (n == 0) {
+ signalTimeouts(now);
+ continue;
+ }
+ Set<SelectionKey> keys = selector.selectedKeys();
+
+ for (SelectionKey key : keys) {
+ if (key.isReadable() || key.isConnectable() || key.isWritable()) {
+ key.cancel();
+ AsyncEvent exchange = (AsyncEvent) key.attachment();
+ readyList.add(exchange);
+ }
+ }
+ selector.selectNow(); // complete cancellation
+ selector.selectedKeys().clear();
+
+ for (AsyncEvent exchange : readyList) {
+ if (exchange instanceof AsyncEvent.Blocking) {
+ exchange.channel().configureBlocking(true);
+ } else {
+ assert exchange instanceof AsyncEvent.NonBlocking;
+ }
+ executor.synchronize();
+ handleEvent(exchange); // will be delegated to executor
+ }
+ readyList.clear();
+ }
+ } catch (Throwable e) {
+ if (!closed) {
+ System.err.println("HttpClientImpl terminating on error");
+ // This terminates thread. So, better just print stack trace
+ String err = Utils.stackTrace(e);
+ Log.logError("HttpClientImpl: fatal error: " + err);
+ }
+ }
+ }
+
+ void handleEvent(AsyncEvent e) {
+ if (closed) {
+ e.abort();
+ } else {
+ e.handle();
+ }
+ }
+ }
+
+ /**
+ * Creates a HttpRequest associated with this group.
+ *
+ * @throws IllegalStateException if the group has been stopped
+ */
+ @Override
+ public HttpRequestBuilderImpl request() {
+ return new HttpRequestBuilderImpl(this, null);
+ }
+
+ /**
+ * Creates a HttpRequest associated with this group.
+ *
+ * @throws IllegalStateException if the group has been stopped
+ */
+ @Override
+ public HttpRequestBuilderImpl request(URI uri) {
+ return new HttpRequestBuilderImpl(this, uri);
+ }
+
+ @Override
+ public SSLContext sslContext() {
+ Utils.checkNetPermission("getSSLContext");
+ return sslContext;
+ }
+
+ @Override
+ public Optional<SSLParameters> sslParameters() {
+ return Optional.ofNullable(sslParams);
+ }
+
+ @Override
+ public Optional<Authenticator> authenticator() {
+ return Optional.ofNullable(authenticator);
+ }
+
+ @Override
+ public ExecutorService executorService() {
+ return executor.userExecutor();
+ }
+
+ ExecutorWrapper executorWrapper() {
+ return executor;
+ }
+
+ @Override
+ public boolean pipelining() {
+ return this.pipelining;
+ }
+
+ ConnectionPool connectionPool() {
+ return connections;
+ }
+
+ @Override
+ public Redirect followRedirects() {
+ return followRedirects;
+ }
+
+
+ @Override
+ public Optional<CookieManager> cookieManager() {
+ return Optional.ofNullable(cookieManager);
+ }
+
+ @Override
+ public Optional<ProxySelector> proxy() {
+ return Optional.ofNullable(this.proxySelector);
+ }
+
+ @Override
+ public Version version() {
+ return version;
+ }
+
+ //private final HashMap<String, Boolean> http2NotSupported = new HashMap<>();
+
+ boolean getHttp2Allowed() {
+ return version.equals(Version.HTTP_2);
+ }
+
+ //void setHttp2NotSupported(String host) {
+ //http2NotSupported.put(host, false);
+ //}
+
+ final void initFilters() {
+ addFilter(AuthenticationFilter.class);
+ addFilter(RedirectFilter.class);
+ }
+
+ final void addFilter(Class<? extends HeaderFilter> f) {
+ filters.addFilter(f);
+ }
+
+ final List<HeaderFilter> filterChain() {
+ return filters.getFilterChain();
+ }
+
+ // Timer controls. Timers are implemented through timed Selector.select()
+ // calls.
+ synchronized void registerTimer(TimeoutEvent event) {
+ long elapse = event.timevalMillis();
+ ListIterator<TimeoutEvent> iter = timeouts.listIterator();
+ long listval = 0;
+ event.delta = event.timeval; // in case list empty
+ TimeoutEvent next;
+ while (iter.hasNext()) {
+ next = iter.next();
+ listval += next.delta;
+ if (elapse < listval) {
+ listval -= next.delta;
+ event.delta = elapse - listval;
+ next.delta -= event.delta;
+ iter.previous();
+ break;
+ } else if (!iter.hasNext()) {
+ event.delta = event.timeval - listval ;
+ }
+ }
+ iter.add(event);
+ //debugPrintList("register");
+ selmgr.wakeupSelector();
+ }
+
+ void debugPrintList(String s) {
+ System.err.printf("%s: {", s);
+ for (TimeoutEvent e : timeouts) {
+ System.err.printf("(%d,%d) ", e.delta, e.timeval);
+ }
+ System.err.println("}");
+ }
+
+ synchronized void signalTimeouts(long then) {
+ if (timeouts.isEmpty()) {
+ return;
+ }
+ long now = System.currentTimeMillis();
+ long duration = now - then;
+ ListIterator<TimeoutEvent> iter = timeouts.listIterator();
+ TimeoutEvent event = iter.next();
+ long delta = event.delta;
+ if (duration < delta) {
+ event.delta -= duration;
+ return;
+ }
+ event.handle();
+ iter.remove();
+ while (iter.hasNext()) {
+ event = iter.next();
+ if (event.delta == 0) {
+ event.handle();
+ iter.remove();
+ } else {
+ event.delta += delta;
+ break;
+ }
+ }
+ //debugPrintList("signalTimeouts");
+ }
+
+ synchronized void cancelTimer(TimeoutEvent event) {
+ ListIterator<TimeoutEvent> iter = timeouts.listIterator();
+ while (iter.hasNext()) {
+ TimeoutEvent ev = iter.next();
+ if (event == ev) {
+ if (iter.hasNext()) {
+ // adjust
+ TimeoutEvent next = iter.next();
+ next.delta += ev.delta;
+ iter.previous();
+ }
+ iter.remove();
+ }
+ }
+ }
+
+ // used for the connection window
+ int getReceiveBufferSize() {
+ return Utils.getIntegerNetProperty(
+ "sun.net.httpclient.connectionWindowSize", 256 * 1024
+ );
+ }
+
+ // returns 0 meaning block forever, or a number of millis to block for
+ synchronized long getTimeoutValue() {
+ if (timeouts.isEmpty()) {
+ return 0;
+ } else {
+ return timeouts.get(0).delta;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.CompletableFuture;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Wraps socket channel layer and takes care of SSL also.
+ *
+ * Subtypes are:
+ * PlainHttpConnection: regular direct TCP connection to server
+ * PlainProxyConnection: plain text proxy connection
+ * PlainTunnelingConnection: opens plain text (CONNECT) tunnel to server
+ * SSLConnection: TLS channel direct to server
+ * SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel
+ */
+abstract class HttpConnection implements BufferHandler {
+
+ // address we are connected to. Could be a server or a proxy
+ final InetSocketAddress address;
+ final HttpClientImpl client;
+ protected volatile ByteBuffer buffer;
+
+ HttpConnection(InetSocketAddress address, HttpClientImpl client) {
+ this.address = address;
+ this.client = client;
+ }
+
+ /**
+ * Public API to this class. addr is the ultimate destination. Any proxies
+ * etc are figured out from the request. Returns an instance of one of the
+ * following
+ * PlainHttpConnection
+ * PlainTunnelingConnection
+ * SSLConnection
+ * SSLTunnelConnection
+ *
+ * When object returned, connect() or connectAsync() must be called, which
+ * when it returns/completes, the connection is usable for requests.
+ */
+ public static HttpConnection getConnection(InetSocketAddress addr,
+ HttpRequestImpl request) {
+ return getConnectionImpl(addr, request);
+ }
+
+ public abstract void connect() throws IOException, InterruptedException;
+
+ public abstract CompletableFuture<Void> connectAsync();
+
+ /**
+ * Returns whether this connection is connected to its destination
+ */
+ abstract boolean connected();
+
+ abstract boolean isSecure();
+
+ abstract boolean isProxied();
+
+ /**
+ * Completes when the first byte of the response is available to be read.
+ */
+ abstract CompletableFuture<Void> whenReceivingResponse();
+
+ // must be called before reading any data off connection
+ // at beginning of response.
+ ByteBuffer getRemaining() {
+ ByteBuffer b = buffer;
+ buffer = null;
+ return b;
+ }
+
+ final boolean isOpen() {
+ return channel().isOpen();
+ }
+
+ /* Returns either a plain HTTP connection or a plain tunnelling connection
+ * for proxied websockets */
+ private static HttpConnection getPlainConnection(InetSocketAddress addr,
+ InetSocketAddress proxy,
+ HttpRequestImpl request) {
+ HttpClientImpl client = request.client();
+
+ if (request.isWebSocket() && proxy != null) {
+ return new PlainTunnelingConnection(addr,
+ proxy,
+ client,
+ request.getAccessControlContext());
+ } else {
+ if (proxy == null) {
+ return new PlainHttpConnection(addr, client);
+ } else {
+ return new PlainProxyConnection(proxy, client);
+ }
+ }
+ }
+
+ private static HttpConnection getSSLConnection(InetSocketAddress addr,
+ InetSocketAddress proxy,
+ HttpRequestImpl request,
+ String[] alpn) {
+ HttpClientImpl client = request.client();
+ if (proxy != null) {
+ return new SSLTunnelConnection(addr,
+ client,
+ proxy,
+ request.getAccessControlContext());
+ } else {
+ return new SSLConnection(addr, client, alpn);
+ }
+ }
+
+ /**
+ * Main factory method. Gets a HttpConnection, either cached or new if
+ * none available.
+ */
+ private static HttpConnection getConnectionImpl(InetSocketAddress addr,
+ HttpRequestImpl request) {
+ HttpConnection c;
+ HttpClientImpl client = request.client();
+ InetSocketAddress proxy = request.proxy();
+ boolean secure = request.secure();
+ ConnectionPool pool = client.connectionPool();
+ String[] alpn = null;
+
+ if (secure && request.requestHttp2()) {
+ alpn = new String[1];
+ alpn[0] = "h2";
+ }
+
+ if (!secure) {
+ c = pool.getConnection(false, addr, proxy);
+ if (c != null) {
+ return c;
+ } else {
+ return getPlainConnection(addr, proxy, request);
+ }
+ } else {
+ c = pool.getConnection(true, addr, proxy);
+ if (c != null) {
+ return c;
+ } else {
+ return getSSLConnection(addr, proxy, request, alpn);
+ }
+ }
+ }
+
+ void returnToCache(HttpHeaders hdrs) {
+ if (hdrs == null) {
+ // the connection was closed by server
+ close();
+ return;
+ }
+ if (!isOpen()) {
+ return;
+ }
+ ConnectionPool pool = client.connectionPool();
+ boolean keepAlive = hdrs.firstValue("Connection")
+ .map((s) -> !s.equalsIgnoreCase("close"))
+ .orElse(true);
+
+ if (keepAlive) {
+ pool.returnToPool(this);
+ } else {
+ close();
+ }
+ }
+
+ /**
+ * Also check that the number of bytes written is what was expected. This
+ * could be different if the buffer is user-supplied and its internal
+ * pointers were manipulated in a race condition.
+ */
+ final void checkWrite(long expected, ByteBuffer buffer) throws IOException {
+ long written = write(buffer);
+ if (written != expected) {
+ throw new IOException("incorrect number of bytes written");
+ }
+ }
+
+ final void checkWrite(long expected,
+ ByteBuffer[] buffers,
+ int start,
+ int length)
+ throws IOException
+ {
+ long written = write(buffers, start, length);
+ if (written != expected) {
+ throw new IOException("incorrect number of bytes written");
+ }
+ }
+
+ abstract SocketChannel channel();
+
+ final InetSocketAddress address() {
+ return address;
+ }
+
+ void configureBlocking(boolean mode) throws IOException {
+ channel().configureBlocking(mode);
+ }
+
+ abstract ConnectionPool.CacheKey cacheKey();
+
+ /*
+ static PrintStream ps;
+
+ static {
+ try {
+ String propval = Utils.getNetProperty("java.net.httpclient.showData");
+ if (propval != null && propval.equalsIgnoreCase("true")) {
+ ps = new PrintStream(new FileOutputStream("/tmp/httplog.txt"), false);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ synchronized final void debugPrint(String s, ByteBuffer b) {
+ ByteBuffer[] bufs = new ByteBuffer[1];
+ bufs[0] = b;
+ debugPrint(s, bufs, 0, 1);
+ }
+
+ synchronized final void debugPrint(String s,
+ ByteBuffer[] bufs,
+ int start,
+ int number) {
+ if (ps == null) {
+ return;
+ }
+
+ ps.printf("\n%s:\n", s);
+
+ for (int i=start; i<start+number; i++) {
+ ByteBuffer b = bufs[i].duplicate();
+ while (b.hasRemaining()) {
+ int c = b.get();
+ if (c == 10) {
+ ps.printf("LF \n");
+ } else if (c == 13) {
+ ps.printf(" CR ");
+ } else if (c == 0x20) {
+ ps.printf("_");
+ } else if (c > 0x20 && c <= 0x7F) {
+ ps.printf("%c", (char)c);
+ } else {
+ ps.printf("0x%02x ", c);
+ }
+ }
+ }
+ ps.printf("\n---------------------\n");
+ }
+
+ */
+
+ // overridden in SSL only
+ SSLParameters sslParameters() {
+ return null;
+ }
+
+ // Methods to be implemented for Plain TCP and SSL
+
+ abstract long write(ByteBuffer[] buffers, int start, int number)
+ throws IOException;
+
+ abstract long write(ByteBuffer buffer) throws IOException;
+
+ /**
+ * Closes this connection, by returning the socket to its connection pool.
+ */
+ abstract void close();
+
+ /**
+ * Returns a ByteBuffer with data, or null if EOF.
+ */
+ final ByteBuffer read() throws IOException {
+ return read(-1);
+ }
+
+ /**
+ * Puts position to limit and limit to capacity so we can resume reading
+ * into this buffer, but if required > 0 then limit may be reduced so that
+ * no more than required bytes are read next time.
+ */
+ static void resumeChannelRead(ByteBuffer buf, int required) {
+ int limit = buf.limit();
+ buf.position(limit);
+ int capacity = buf.capacity() - limit;
+ if (required > 0 && required < capacity) {
+ buf.limit(limit + required);
+ } else {
+ buf.limit(buf.capacity());
+ }
+ }
+
+ /**
+ * Blocks ands return requested amount.
+ */
+ final ByteBuffer read(int length) throws IOException {
+ if (length <= 0) {
+ buffer = readImpl(length);
+ return buffer;
+ }
+ buffer = readImpl(length);
+ int required = length - buffer.remaining();
+ while (buffer.remaining() < length) {
+ resumeChannelRead(buffer, required);
+ int n = readImpl(buffer);
+ required -= n;
+ }
+ return buffer;
+ }
+
+ final int read(ByteBuffer buffer) throws IOException {
+ int n = readImpl(buffer);
+ return n;
+ }
+
+ /** Reads up to length bytes. */
+ protected abstract ByteBuffer readImpl(int length) throws IOException;
+
+ /** Reads as much as possible into given buffer and returns amount read. */
+ protected abstract int readImpl(ByteBuffer buffer) throws IOException;
+
+ @Override
+ public String toString() {
+ return "HttpConnection: " + channel().toString();
+ }
+
+ @Override
+ public final ByteBuffer getBuffer() {
+ return client.getBuffer();
+ }
+
+ @Override
+ public final void returnBuffer(ByteBuffer buffer) {
+ client.returnBuffer(buffer);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeaders.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A read-only view of a set of received HTTP headers.
+ *
+ * @since 9
+ */
+public interface HttpHeaders {
+
+ /**
+ * Returns an {@link java.util.Optional} containing the first value of the
+ * given named (and possibly multi-valued) header. If the header is not
+ * present, then the returned {@code Optional} is empty.
+ *
+ * @param name the header name
+ * @return an {@code Optional<String>} for the first named value
+ */
+ public Optional<String> firstValue(String name);
+
+ /**
+ * Returns an {@link java.util.Optional} containing the first value of the
+ * named header field as an {@literal Optional<Long>}. If the header is not
+ * present, then the Optional is empty. If the header is present but
+ * contains a value that does not parse as a {@code Long} value, then an
+ * exception is thrown.
+ *
+ * @param name the header name
+ * @return an {@code Optional<Long>}
+ * @throws NumberFormatException if a value is found, but does not parse as
+ * a Long
+ */
+ public Optional<Long> firstValueAsLong(String name);
+
+ /**
+ * Returns an unmodifiable List of all of the values of the given named
+ * header. Always returns a List, which may be empty if the header is not
+ * present.
+ *
+ * @param name the header name
+ * @return a List of String values
+ */
+ public List<String> allValues(String name);
+
+ /**
+ * Returns an unmodifiable multi Map view of this HttpHeaders. This
+ * interface should only be used when it is required to iterate over the
+ * entire set of headers.
+ *
+ * @return the Map
+ */
+ public Map<String,List<String>> map();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeaders1.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+public interface HttpHeaders1 extends HttpHeaders {
+ public void makeUnmodifiable();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Implementation of HttpHeaders.
+ */
+class HttpHeadersImpl implements HttpHeaders1 {
+
+ private final HashMap<String,List<String>> headers;
+ private boolean isUnmodifiable = false;
+
+ public HttpHeadersImpl() {
+ headers = new HashMap<>();
+ }
+
+ /**
+ * Replace all List<String> in headers with unmodifiable Lists. Call
+ * this only after all headers are added. The headers HashMap
+ * is wrapped with an unmodifiable HashMap in map()
+ */
+ @Override
+ public void makeUnmodifiable() {
+ if (isUnmodifiable)
+ return;
+
+ Set<String> keys = new HashSet<>(headers.keySet());
+ for (String key : keys) {
+ List<String> values = headers.remove(key);
+ if (values != null) {
+ headers.put(key, Collections.unmodifiableList(values));
+ }
+ }
+ isUnmodifiable = true;
+ }
+
+ @Override
+ public Optional<String> firstValue(String name) {
+ List<String> l = headers.get(name);
+ return Optional.ofNullable(l == null ? null : l.get(0));
+ }
+
+ @Override
+ public List<String> allValues(String name) {
+ return headers.get(name);
+ }
+
+ @Override
+ public Map<String, List<String>> map() {
+ return Collections.unmodifiableMap(headers);
+ }
+
+ Map<String, List<String>> directMap() {
+ return headers;
+ }
+
+ // package private mutators
+
+ public HttpHeadersImpl deepCopy() {
+ HttpHeadersImpl h1 = new HttpHeadersImpl();
+ HashMap<String,List<String>> headers1 = h1.headers;
+ Set<String> keys = headers.keySet();
+ for (String key : keys) {
+ List<String> vals = headers.get(key);
+ LinkedList<String> vals1 = new LinkedList<>(vals);
+ headers1.put(key, vals1);
+ }
+ return h1;
+ }
+
+ private List<String> getOrCreate(String name) {
+ List<String> l = headers.get(name);
+ if (l == null) {
+ l = new LinkedList<>();
+ headers.put(name, l);
+ }
+ return l;
+ }
+
+ void addHeader(String name, String value) {
+ List<String> l = getOrCreate(name);
+ l.add(value);
+ }
+
+ void setHeader(String name, String value) {
+ List<String> l = getOrCreate(name);
+ l.clear();
+ l.add(value);
+ }
+
+ @Override
+ public Optional<Long> firstValueAsLong(String name) {
+ List<String> l = headers.get(name);
+ if (l == null) {
+ return Optional.ofNullable(null);
+ } else {
+ String v = l.get(0);
+ Long lv = Long.parseLong(v);
+ return Optional.of(lv);
+ }
+ }
+
+ void clear() {
+ headers.clear();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRedirectImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.net.*;
+
+interface HttpRedirectImpl {
+
+ static HttpRedirectImpl getRedirects(java.net.http.HttpClient.Redirect redir) {
+ switch (redir) {
+ case NEVER:
+ return HttpRedirectImpl.NEVER;
+ case ALWAYS:
+ return HttpRedirectImpl.ALWAYS;
+ case SECURE:
+ return HttpRedirectImpl.SECURE;
+ case SAME_PROTOCOL:
+ return HttpRedirectImpl.SAME_PROTOCOL;
+ }
+ return HttpRedirectImpl.NEVER;
+ }
+
+ static HttpClient.Redirect getRedirects(HttpRedirectImpl redir) {
+ if (redir == HttpRedirectImpl.NEVER) {
+ return HttpClient.Redirect.NEVER;
+ } else if (redir == HttpRedirectImpl.ALWAYS) {
+ return HttpClient.Redirect.ALWAYS;
+ } else if (redir == HttpRedirectImpl.SECURE) {
+ return HttpClient.Redirect.SECURE;
+ } else {
+ return HttpClient.Redirect.SAME_PROTOCOL;
+ }
+ }
+
+ /**
+ * Called to determine whether the given intermediate response
+ * with a redirection response code should be redirected. The target URI
+ * can be obtained from the "Location" header in the given response object.
+ *
+ * @param rsp the response from the redirected resource
+ * @return {@code true} if the redirect should be attempted automatically
+ * or {@code false} if not.
+ */
+ boolean redirect(HttpResponse rsp);
+
+ /**
+ * Never redirect.
+ */
+ static HttpRedirectImpl NEVER = (HttpResponse rsp) -> false;
+
+ /**
+ * Always redirect.
+ */
+ static HttpRedirectImpl ALWAYS = (HttpResponse rsp) -> true;
+
+ /**
+ * Redirect to same protocol only. Redirection may occur from HTTP URLs to
+ * other THHP URLs and from HTTPS URLs to other HTTPS URLs.
+ */
+ static HttpRedirectImpl SAME_PROTOCOL = (HttpResponse rsp) -> {
+ String orig = rsp.request().uri().getScheme().toLowerCase();
+ String redirect = URI.create(
+ rsp.headers().firstValue("Location").orElse(""))
+ .getScheme().toLowerCase();
+ return orig.equals(redirect);
+ };
+
+ /**
+ * Redirect always except from HTTPS URLs to HTTP URLs.
+ */
+ static HttpRedirectImpl SECURE = (HttpResponse rsp) -> {
+ String orig = rsp.request().uri().getScheme().toLowerCase();
+ String redirect = URI.create(
+ rsp.headers().firstValue("Location").orElse(""))
+ .getScheme().toLowerCase();
+ if (orig.equals("https")) {
+ return redirect.equals("https");
+ }
+ return true;
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,871 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.ProxySelector;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.*;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.LongConsumer;
+
+/**
+ * Represents one HTTP request which can be sent to a server. {@code
+ * HttpRequest}s are built from {@code HttpRequest} {@link HttpRequest.Builder
+ * builder}s. {@code HttpRequest} builders are obtained from a {@link HttpClient}
+ * by calling {@link HttpClient#request(java.net.URI) HttpClient.request}, or
+ * by calling {@link #create(java.net.URI) HttpRequest.create} which returns a
+ * builder on the <a href="HttpClient.html#defaultclient">default</a> client.
+ * A request's {@link java.net.URI}, headers and body can be set. Request bodies
+ * are provided through a {@link BodyProcessor} object. Once all required
+ * parameters have been set in the builder, one of the builder methods should be
+ * called, which sets the request method and returns a {@code HttpRequest}.
+ * These methods are {@link Builder#GET() GET}, {@link HttpRequest.Builder#POST()
+ * POST} and {@link HttpRequest.Builder#PUT() PUT} which return a GET, POST or
+ * PUT request respectively. Alternatively, {@link
+ * HttpRequest.Builder#method(String) method} can be called to set an arbitrary
+ * method type (and return a {@code HttpRequest}). Builders can also be copied
+ * and modified multiple times in order to build multiple related requests that
+ * differ in some parameters.
+ *
+ * <p> Two simple, example HTTP interactions are shown below:
+ * <pre>
+ * {@code
+ * // GET
+ * HttpResponse response = HttpRequest
+ * .create(new URI("http://www.foo.com"))
+ * .headers("Foo", "foovalue", "Bar", "barvalue")
+ * .GET()
+ * .response();
+ *
+ * int statusCode = response.statusCode();
+ * String responseBody = response.body(asString());
+ *
+ * // POST
+ * response = HttpRequest
+ * .create(new URI("http://www.foo.com"))
+ * .body(fromString("param1=foo,param2=bar"))
+ * .POST()
+ * .response();}
+ * </pre>
+ *
+ * <p> The request is sent and the response obtained by calling one of the
+ * following methods.
+ * <ul><li>{@link #response() response} blocks until the entire request has been
+ * sent and the response status code and headers have been received.</li>
+ * <li>{@link #responseAsync() responseAsync} sends the request and receives the
+ * response asynchronously. Returns immediately with a
+ * {@link java.util.concurrent.CompletableFuture CompletableFuture}<{@link
+ * HttpResponse}>.</li>
+ * <li>{@link #multiResponseAsync(HttpResponse.MultiProcessor) multiResponseAsync}
+ * sends the request asynchronously, expecting multiple responses. This
+ * capability is of most relevance to HTTP/2 server push, but can be used for
+ * single responses (HTTP/1.1 or HTTP/2) also.</li>
+ * </ul>
+ *
+ * <p> Once a request has been sent, it is an error to try and send it again.
+ *
+ * <p> Once a {@code HttpResponse} is received, the headers and response code are
+ * available. The body can then be received by calling one of the body methods
+ * on {@code HttpResponse}.
+ *
+ * <p> See below for discussion of synchronous versus asynchronous usage.
+ *
+ * <p> <b>Request bodies</b>
+ *
+ * <p> Request bodies are sent using one of the request processor implementations
+ * below provided in {@code HttpRequest}, or else a custom implementation can be
+ * used.
+ * <ul>
+ * <li>{@link #fromByteArray(byte[]) } from byte array</li>
+ * <li>{@link #fromByteArrays(java.util.Iterator) fromByteArrays(Iterator)}
+ * from an iterator of byte arrays</li>
+ * <li>{@link #fromFile(java.nio.file.Path) fromFile(Path)} from the file located
+ * at the given Path</li>
+ * <li>{@link #fromString(java.lang.String) fromString(String)} from a String </li>
+ * <li>{@link #fromInputStream(java.io.InputStream) fromInputStream(InputStream)}
+ * request body from InputStream</li>
+ * <li>{@link #noBody() } no request body is sent</li>
+ * </ul>
+ *
+ * <p> <b>Response bodies</b>
+ *
+ * <p> Responses bodies are handled by the {@link HttpResponse.BodyProcessor}
+ * {@code <T>} supplied to the {@link HttpResponse#body(HttpResponse.BodyProcessor)
+ * HttpResponse.body} and {@link HttpResponse#bodyAsync(HttpResponse.BodyProcessor)
+ * HttpResponse.bodyAsync} methods. Some implementations of {@code
+ * HttpResponse.BodyProcessor} are provided in {@link HttpResponse}:
+ * <ul>
+ * <li>{@link HttpResponse#asByteArray() } stores the body in a byte array</li>
+ * <li>{@link HttpResponse#asString()} stores the body as a String </li>
+ * <li>{@link HttpResponse#asFile(java.nio.file.Path) } stores the body in a
+ * named file</li>
+ * <li>{@link HttpResponse#ignoreBody() } ignores any received response body</li>
+ * </ul>
+ *
+ * <p> The output of a response processor is the response body, and its
+ * parameterized type {@code T} determines the type of the body object returned
+ * from {@code HttpResponse.body} and {@code HttpResponse.bodyAsync}. Therefore,
+ * as an example, the second response processor in the list above has the type
+ * {@code HttpResponse.BodyProcessor<String>} which means the type returned by
+ * {@code HttpResponse.body()} is a String. Response processors can be defined
+ * to return potentially any type as body.
+ *
+ * <p> <b>Multi responses</b>
+ *
+ * <p> With HTTP/2 it is possible for a server to return a main response and zero
+ * or more additional responses (known as server pushes) to a client-initiated
+ * request. These are handled using a special response processor called {@link
+ * HttpResponse.MultiProcessor}.
+ *
+ * <p> <b>Blocking/asynchronous behavior and thread usage</b>
+ *
+ * <p> There are two styles of request sending: <i>synchronous</i> and
+ * <i>asynchronous</i>. {@link #response() response} blocks the calling thread
+ * until the request has been sent and the response received.
+ *
+ * <p> {@link #responseAsync() responseAsync} is asynchronous and returns
+ * immediately with a {@link java.util.concurrent.CompletableFuture}<{@link
+ * HttpResponse}> and when this object completes (in a background thread) the
+ * response has been received.
+ *
+ * <p> {@link #multiResponseAsync(HttpResponse.MultiProcessor) multiResponseAsync}
+ * is the variant for multi responses and is also asynchronous.
+ *
+ * <p> CompletableFutures can be combined in different ways to declare the
+ * dependencies among several asynchronous tasks, while allowing for the maximum
+ * level of parallelism to be utilized.
+ *
+ * <p> <b>Security checks</b>
+ *
+ * <p> If a security manager is present then security checks are performed by
+ * the {@link #response() } and {@link #responseAsync() } methods. A {@link
+ * java.net.URLPermission} or {@link java.net.SocketPermission} is required to
+ * access any destination origin server and proxy server utilised. URLPermissions
+ * should be preferred in policy files over SocketPermissions given the more
+ * limited scope of URLPermission. Permission is always implicitly granted to a
+ * system's default proxies. The URLPermission form used to access proxies uses
+ * a method parameter of "CONNECT" (for all kinds of proxying) and a url string
+ * of the form "socket://host:port" where host and port specify the proxy's
+ * address.
+ *
+ * <p> <b>Examples</b>
+ * <pre>
+ * import static java.net.http.HttpRequest.*;
+ * import static java.net.http.HttpResponse.*;
+ *
+ * //Simple blocking
+ *
+ * HttpResponse r1 = HttpRequest.create(new URI("http://www.foo.com/"))
+ * .GET()
+ * .response();
+ * int responseCode = r1.statusCode());
+ * String body = r1.body(asString());
+ *
+ * HttpResponse r2 = HttpRequest.create(new URI("http://www.foo.com/"))
+ * .GET()
+ * .response();
+ *
+ * System.out.println("Response was " + r1.statusCode());
+ * Path body1 = r2.body(asFile(Paths.get("/tmp/response.txt")));
+ * // Content stored in /tmp/response.txt
+ *
+ * HttpResponse r3 = HttpRequest.create(new URI("http://www.foo.com/"))
+ * .body(fromString("param1=1, param2=2"))
+ * .POST()
+ * .response();
+ *
+ * Void body2 = r3.body(ignoreBody()); // body is Void in this case
+ * </pre>
+ *
+ * <p><b>Asynchronous Example</b>
+ *
+ * <p> All of the above examples will work asynchronously, if {@link
+ * #responseAsync()} is used instead of {@link #response()} in which case the
+ * returned object is a {@code CompletableFuture<HttpResponse>} instead of
+ * {@code HttpResponse}. The following example shows how multiple requests can
+ * be sent asynchronously. It also shows how dependent asynchronous operations
+ * (receiving response, and receiving response body) can be chained easily using
+ * one of the many methods in {@code CompletableFuture}.
+ * <pre>
+ * {@code
+ * // fetch a list of target URIs asynchronously and store them in Files.
+ *
+ * List<URI> targets = ...
+ *
+ * List<CompletableFuture<File>> futures = targets
+ * .stream()
+ * .map(target -> {
+ * return HttpRequest
+ * .create(target)
+ * .GET()
+ * .responseAsync()
+ * .thenCompose(response -> {
+ * Path dest = Paths.get("base", target.getPath());
+ * if (response.statusCode() == 200) {
+ * return response.bodyAsync(asFile(dest));
+ * } else {
+ * return CompletableFuture.completedFuture(dest);
+ * }
+ * })
+ * // convert Path -> File
+ * .thenApply((Path dest) -> {
+ * return dest.toFile();
+ * });
+ * })
+ * .collect(Collectors.toList());
+ *
+ * // all async operations waited for here
+ *
+ * CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]))
+ * .join();
+ *
+ * // all elements of futures have completed and can be examined.
+ * // Use File.exists() to check whether file was successfully downloaded
+ * }
+ * </pre>
+ *
+ * @since 9
+ */
+public abstract class HttpRequest {
+
+ HttpRequest() {}
+
+ /**
+ * A builder of {@link HttpRequest}s. {@code HttpRequest.Builder}s are
+ * created by calling {@link HttpRequest#create(URI)} or {@link
+ * HttpClient#request(URI)}.
+ *
+ * <p> Each of the setter methods in this class modifies the state of the
+ * builder and returns <i>this</i> (ie. the same instance). The methods are
+ * not synchronized and should not be called from multiple threads without
+ * external synchronization.
+ *
+ * <p> The build methods return a new {@code HttpRequest} each time they are
+ * called.
+ *
+ * @since 9
+ */
+ public abstract static class Builder {
+
+ Builder() {}
+
+ /**
+ * Sets this HttpRequest's request URI.
+ *
+ * @param uri the request URI
+ * @return this request builder
+ */
+ public abstract Builder uri(URI uri);
+
+ /**
+ * Specifies whether this request will automatically follow redirects
+ * issued by the server. The default value for this setting is the value
+ * of {@link HttpClient#followRedirects() }
+ *
+ * @param policy the redirection policy
+ * @return this request builder
+ */
+ public abstract Builder followRedirects(HttpClient.Redirect policy);
+
+ /**
+ * Request server to acknowledge request before sending request
+ * body. This is disabled by default. If enabled, the server is requested
+ * to send an error response or a 100-Continue response before the client
+ * sends the request body. This means the request processor for the
+ * request will not be invoked until this interim response is received.
+ *
+ * @param enable {@code true} if Expect continue to be sent
+ * @return this request builder
+ */
+ public abstract Builder expectContinue(boolean enable);
+
+ /**
+ * Overrides the {@link HttpClient#version() } setting for this
+ * request.
+ *
+ * @param version the HTTP protocol version requested
+ * @return this request builder
+ */
+ public abstract Builder version(HttpClient.Version version);
+
+ /**
+ * Adds the given name value pair to the set of headers for this request.
+ *
+ * @param name the header name
+ * @param value the header value
+ * @return this request builder
+ */
+ public abstract Builder header(String name, String value);
+
+ /**
+ * Overrides the ProxySelector set on the request's client for this
+ * request.
+ *
+ * @param proxy the ProxySelector to use
+ * @return this request builder
+ */
+ public abstract Builder proxy(ProxySelector proxy);
+
+ /**
+ * Adds the given name value pairs to the set of headers for this
+ * request. The supplied Strings must alternate as names and values.
+ *
+ * @param headers the list of String name value pairs
+ * @return this request builder
+ * @throws IllegalArgumentException if there is an odd number of
+ * parameters
+ */
+ public abstract Builder headers(String... headers);
+
+ /**
+ * Sets a timeout for this request. If the response is not received
+ * within the specified timeout then a {@link HttpTimeoutException} is
+ * thrown from {@link #response() } or {@link #responseAsync() }
+ * completes exceptionally with a {@code HttpTimeoutException}.
+ *
+ * @param unit the timeout units
+ * @param timeval the number of units to wait for
+ * @return this request builder
+ */
+ public abstract Builder timeout(TimeUnit unit, long timeval);
+
+ /**
+ * Sets the given name value pair to the set of headers for this
+ * request. This overwrites any previously set values for name.
+ *
+ * @param name the header name
+ * @param value the header value
+ * @return this request builder
+ */
+ public abstract Builder setHeader(String name, String value);
+
+ /**
+ * Sets a request body for this builder. See {@link HttpRequest}
+ * for example {@code BodyProcessor} implementations.
+ * If no body is specified, then no body is sent with the request.
+ *
+ * @param reqproc the request body processor
+ * @return this request builder
+ */
+ public abstract Builder body(BodyProcessor reqproc);
+
+ /**
+ * Builds and returns a GET {@link HttpRequest} from this builder.
+ *
+ * @return a {@code HttpRequest}
+ */
+ public abstract HttpRequest GET();
+
+ /**
+ * Builds and returns a POST {@link HttpRequest} from this builder.
+ *
+ * @return a {@code HttpRequest}
+ */
+ public abstract HttpRequest POST();
+
+ /**
+ * Builds and returns a PUT {@link HttpRequest} from this builder.
+ *
+ * @return a {@code HttpRequest}
+ */
+ public abstract HttpRequest PUT();
+
+ /**
+ * Builds and returns a {@link HttpRequest} from this builder using
+ * the given method String. The method string is case-sensitive, and
+ * may be rejected if an upper-case string is not used.
+ *
+ * @param method the method to use
+ * @return a {@code HttpRequest}
+ * @throws IllegalArgumentException if an unrecognised method is used
+ */
+ public abstract HttpRequest method(String method);
+
+ /**
+ * Returns an exact duplicate copy of this Builder based on current
+ * state. The new builder can then be modified independently of this
+ * builder.
+ *
+ * @return an exact copy of this Builder
+ */
+ public abstract Builder copy();
+ }
+
+ /**
+ * Creates a HttpRequest builder from the <i>default</i> HttpClient.
+ *
+ * @param uri the request URI
+ * @return a new request builder
+ */
+ public static HttpRequest.Builder create(URI uri) {
+ return HttpClient.getDefault().request(uri);
+ }
+
+ /**
+ * Returns the follow-redirects setting for this request.
+ *
+ * @return follow redirects setting
+ */
+ public abstract HttpClient.Redirect followRedirects();
+
+ /**
+ * Returns the response to this request, by sending it and blocking if
+ * necessary to get the response. The {@link HttpResponse} contains the
+ * response status and headers.
+ *
+ * @return a HttpResponse for this request
+ * @throws IOException if an I/O error occurs
+ * @throws InterruptedException if the operation was interrupted
+ * @throws SecurityException if the caller does not have the required
+ * permission
+ * @throws IllegalStateException if called more than once or if
+ * responseAsync() called previously
+ */
+ public abstract HttpResponse response()
+ throws IOException, InterruptedException;
+
+ /**
+ * Sends the request and returns the response asynchronously. This method
+ * returns immediately with a {@link CompletableFuture}<{@link
+ * HttpResponse}>
+ *
+ * @return a {@code CompletableFuture<HttpResponse>}
+ * @throws IllegalStateException if called more than once or if response()
+ * called previously.
+ */
+ public abstract CompletableFuture<HttpResponse> responseAsync();
+
+ /**
+ * Sends the request asynchronously expecting multiple responses.
+ *
+ * <p> This method must be given a {@link HttpResponse.MultiProcessor} to
+ * handle the multiple responses.
+ *
+ * <p> If a security manager is set, the caller must possess a {@link
+ * java.net.URLPermission} for the request's URI, method and any user set
+ * headers. The security manager is also checked for each incoming
+ * additional server generated request/response. Any request that fails the
+ * security check, is canceled and ignored.
+ *
+ * <p> This method can be used for both HTTP/1.1 and HTTP/2, but in cases
+ * where multiple responses are not supported, the MultiProcessor
+ * only receives the main response.
+ *
+ * <p> The aggregate {@code CompletableFuture} returned from this method
+ * returns a {@code <U>} defined by the {@link HttpResponse.MultiProcessor}
+ * implementation supplied. This will typically be a Collection of
+ * HttpResponses or of some response body type.
+ *
+ * @param <U> the aggregate response type
+ * @param rspproc the MultiProcessor for the request
+ * @return a {@code CompletableFuture<U>}
+ * @throws IllegalStateException if the request has already been sent.
+ */
+ public abstract <U> CompletableFuture<U>
+ multiResponseAsync(HttpResponse.MultiProcessor<U> rspproc);
+
+ /**
+ * Returns the request method for this request. If not set explicitly,
+ * the default method for any request is "GET".
+ *
+ * @return this request's method
+ */
+ public abstract String method();
+
+ /**
+ * Returns this request's {@link HttpRequest.Builder#expectContinue(boolean)
+ * expect continue } setting.
+ *
+ * @return this request's expect continue setting
+ */
+ public abstract boolean expectContinue();
+
+ /**
+ * Returns this request's request URI.
+ *
+ * @return this request's URI
+ */
+ public abstract URI uri();
+
+ /**
+ * Returns this request's {@link HttpClient}.
+ *
+ * @return this request's HttpClient
+ */
+ public abstract HttpClient client();
+
+ /**
+ * Returns the HTTP protocol version that this request will use or used.
+ *
+ * @return HTTP protocol version
+ */
+ public abstract HttpClient.Version version();
+
+ /**
+ * The (user-accessible) request headers that this request was (or will be)
+ * sent with.
+ *
+ * @return this request's HttpHeaders
+ */
+ public abstract HttpHeaders headers();
+
+ /**
+ * Returns a request processor whose body is the given String, converted
+ * using the {@link java.nio.charset.StandardCharsets#ISO_8859_1 ISO_8859_1}
+ * character set.
+ *
+ * @param body the String containing the body
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromString(String body) {
+ return fromString(body, StandardCharsets.ISO_8859_1);
+ }
+
+ /**
+ * A request processor that takes data from the contents of a File.
+ *
+ * @param path the path to the file containing the body
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromFile(Path path) {
+ FileChannel fc;
+ long size;
+
+ try {
+ fc = FileChannel.open(path);
+ size = fc.size();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ return new BodyProcessor() {
+ LongConsumer flow;
+
+ @Override
+ public long onRequestStart(HttpRequest hr, LongConsumer flow) {
+ // could return exact file length, but for now -1
+ this.flow = flow;
+ flow.accept(1);
+ if (size != 0) {
+ return size;
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public boolean onRequestBodyChunk(ByteBuffer buffer) throws IOException {
+ int n = fc.read(buffer);
+ if (n == -1) {
+ fc.close();
+ return true;
+ }
+ flow.accept(1);
+ return false;
+ }
+
+ @Override
+ public void onRequestError(Throwable t) {
+ try {
+ fc.close();
+ } catch (IOException ex) {
+ Log.logError(ex.toString());
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns a request processor whose body is the given String, converted
+ * using the given character set.
+ *
+ * @param s the String containing the body
+ * @param charset the character set to convert the string to bytes
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromString(String s, Charset charset) {
+ return fromByteArray(s.getBytes(charset));
+ }
+
+ /**
+ * Returns a request processor whose body is the given byte array.
+ *
+ * @param buf the byte array containing the body
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromByteArray(byte[] buf) {
+ return fromByteArray(buf, 0, buf.length);
+ }
+
+ /**
+ * Returns a request processor whose body is the content of the given byte
+ * array length bytes starting from the specified offset.
+ *
+ * @param buf the byte array containing the body
+ * @param offset the offset of the first byte
+ * @param length the number of bytes to use
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromByteArray(byte[] buf, int offset, int length) {
+
+ return new BodyProcessor() {
+ LongConsumer flow;
+ byte[] barray;
+ int index;
+ int sent;
+
+ @Override
+ public long onRequestStart(HttpRequest hr, LongConsumer flow) {
+ this.flow = flow;
+ flow.accept(1);
+ barray = buf;
+ index = offset;
+ return length;
+ }
+
+ @Override
+ public boolean onRequestBodyChunk(ByteBuffer buffer)
+ throws IOException
+ {
+ if (sent == length) {
+ return true;
+ }
+
+ int remaining = buffer.remaining();
+ int left = length - sent;
+ int n = remaining > left ? left : remaining;
+ buffer.put(barray, index, n);
+ index += n;
+ sent += n;
+ flow.accept(1);
+ return sent == length;
+ }
+
+ @Override
+ public void onRequestError(Throwable t) {
+ Log.logError(t.toString());
+ }
+ };
+ }
+
+ /**
+ * A request processor that takes data from an Iterator of byte arrays.
+ *
+ * @param iter an Iterator of byte arrays
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromByteArrays(Iterator<byte[]> iter) {
+
+ return new BodyProcessor() {
+ LongConsumer flow;
+ byte[] current;
+ int curIndex;
+
+ @Override
+ public long onRequestStart(HttpRequest hr, LongConsumer flow) {
+ this.flow = flow;
+ flow.accept(1);
+ return -1;
+ }
+
+ @Override
+ public boolean onRequestBodyChunk(ByteBuffer buffer)
+ throws IOException
+ {
+ int remaining;
+
+ while ((remaining = buffer.remaining()) > 0) {
+ if (current == null) {
+ if (!iter.hasNext()) {
+ return true;
+ }
+ current = iter.next();
+ curIndex = 0;
+ }
+ int n = Math.min(remaining, current.length - curIndex);
+ buffer.put(current, curIndex, n);
+ curIndex += n;
+
+ if (curIndex == current.length) {
+ current = null;
+ flow.accept(1);
+ return false;
+ }
+ }
+ flow.accept(1);
+ return false;
+ }
+
+ @Override
+ public void onRequestError(Throwable t) {
+ Log.logError(t.toString());
+ }
+ };
+ }
+
+ /**
+ * A request processor that reads its data from an InputStream.
+ *
+ * @param stream an InputStream
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor fromInputStream(InputStream stream) {
+ // for now, this blocks. It could be offloaded to a separate thread
+ // to do reading and guarantee that onRequestBodyChunk() won't block
+ return new BodyProcessor() {
+ LongConsumer flow;
+
+ @Override
+ public long onRequestStart(HttpRequest hr, LongConsumer flow) {
+ this.flow = flow;
+ flow.accept(1);
+ return -1;
+ }
+
+ @Override
+ public boolean onRequestBodyChunk(ByteBuffer buffer)
+ throws IOException
+ {
+ int remaining = buffer.remaining();
+ int n = stream.read(buffer.array(), buffer.arrayOffset(), remaining);
+ if (n == -1) {
+ stream.close();
+ return true;
+ }
+ buffer.position(buffer.position() + n);
+ flow.accept(1);
+ return false;
+ }
+
+ @Override
+ public void onRequestError(Throwable t) {
+ Log.logError(t.toString());
+ }
+ };
+ }
+
+ /**
+ * A request processor which sends no request body.
+ *
+ * @return a BodyProcessor
+ */
+ public static BodyProcessor noBody() {
+ return new BodyProcessor() {
+
+ @Override
+ public long onRequestStart(HttpRequest hr, LongConsumer flow) {
+ return 0;
+ }
+
+ @Override
+ public boolean onRequestBodyChunk(ByteBuffer buffer)
+ throws IOException
+ {
+ throw new InternalError("should never reach here");
+ }
+
+ @Override
+ public void onRequestError(Throwable t) {
+ Log.logError(t.toString());
+ }
+ };
+ }
+
+ /**
+ * A request processor which obtains the request body from some source.
+ * Implementations of this interface are provided which allow request bodies
+ * to be supplied from standard types, such as {@code String, byte[], File,
+ * InputStream}. Other implementations can be provided.
+ *
+ * <p> The methods of this interface may be called from multiple threads,
+ * but only one method is invoked at a time, and behaves as if called from
+ * one thread.
+ *
+ * <p> See {@link HttpRequest} for implementations that take request bodies
+ * from {@code byte arrays, Strings, Paths} etc.
+ *
+ * @since 9
+ */
+ public interface BodyProcessor {
+
+ /**
+ * Called before a request is sent. Is expected to return the content
+ * length of the request body. Zero means no content. Less than zero
+ * means an unknown positive content-length, and the body will be
+ * streamed.
+ *
+ * <p> The flowController object must be used to manage the flow of
+ * calls to {@link #onRequestBodyChunk(ByteBuffer)}. The typical usage
+ * for a non-blocking processor is to call it once inside
+ * onRequestStart() and once during each call to onRequestBodyChunk().
+ *
+ * @param hr the request
+ * @param flowController the HttpFlowController
+ * @return the content length
+ * @throws IOException if an I/O error occurs
+ */
+ long onRequestStart(HttpRequest hr, LongConsumer flowController)
+ throws IOException;
+
+ /**
+ * Called if sending a request body fails.
+ *
+ * @implSpec The default implementation does nothing.
+ *
+ * @param t the Throwable that caused the failure
+ */
+ default void onRequestError(Throwable t) { }
+
+ /**
+ * Called to obtain a buffer of data to send. The data must be placed
+ * in the provided buffer. The implementation should not block. The
+ * boolean return code notifies the protocol implementation if the
+ * supplied buffer is the final one (or not).
+ *
+ * @param buffer a ByteBuffer to write data into
+ * @return whether or not this is the last buffer
+ * @throws IOException if an I/O error occurs
+ */
+ boolean onRequestBodyChunk(ByteBuffer buffer) throws IOException;
+
+ /**
+ * Called when the request body has been completely sent.
+ *
+ * @implSpec The default implementation does nothing
+ */
+ default void onComplete() {
+ // TODO: need to call this
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.net.URI;
+import java.net.ProxySelector;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+class HttpRequestBuilderImpl extends HttpRequest.Builder {
+
+ private HttpHeadersImpl userHeaders;
+ private URI uri;
+ private String method;
+ private HttpClient.Redirect followRedirects;
+ private boolean expectContinue;
+ private HttpRequest.BodyProcessor body;
+ private HttpClient.Version version;
+ private final HttpClientImpl client;
+ private ProxySelector proxy;
+ private long timeval = 0;
+
+ public HttpRequestBuilderImpl(HttpClientImpl client, URI uri) {
+ this.client = client;
+ this.uri = uri;
+ this.version = client.version();
+ this.userHeaders = new HttpHeadersImpl();
+ }
+
+ @Override
+ public HttpRequestBuilderImpl body(HttpRequest.BodyProcessor reqproc) {
+ Objects.requireNonNull(reqproc);
+ this.body = reqproc;
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl uri(URI uri) {
+ Objects.requireNonNull(uri);
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl followRedirects(HttpClient.Redirect follow) {
+ Objects.requireNonNull(follow);
+ this.followRedirects = follow;
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl header(String name, String value) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(value);
+ Utils.validateToken(name, "invalid header name");
+ userHeaders.addHeader(name, value);
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl headers(String... params) {
+ Objects.requireNonNull(params);
+ if (params.length % 2 != 0) {
+ throw new IllegalArgumentException("wrong number of parameters");
+ }
+ for (int i=0; i<params.length; ) {
+ String name = params[i];
+ String value = params[i+1];
+ header(name, value);
+ i+=2;
+ }
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl proxy(ProxySelector proxy) {
+ Objects.requireNonNull(proxy);
+ this.proxy = proxy;
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl copy() {
+ HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(this.client, this.uri);
+ b.userHeaders = this.userHeaders.deepCopy();
+ b.method = this.method;
+ b.followRedirects = this.followRedirects;
+ b.expectContinue = this.expectContinue;
+ b.body = body;
+ b.uri = uri;
+ b.proxy = proxy;
+ return b;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl setHeader(String name, String value) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(value);
+ userHeaders.setHeader(name, value);
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl expectContinue(boolean enable) {
+ expectContinue = enable;
+ return this;
+ }
+
+ @Override
+ public HttpRequestBuilderImpl version(HttpClient.Version version) {
+ Objects.requireNonNull(version);
+ this.version = version;
+ return this;
+ }
+
+ HttpHeadersImpl headers() { return userHeaders; }
+
+ URI uri() { return uri; }
+
+ String method() { return method; }
+
+ HttpClient.Redirect followRedirects() { return followRedirects; }
+
+ ProxySelector proxy() { return proxy; }
+
+ boolean expectContinue() { return expectContinue; }
+
+ HttpRequest.BodyProcessor body() { return body; }
+
+ HttpClient.Version version() { return version; }
+
+ @Override
+ public HttpRequest GET() { return method("GET"); }
+
+ @Override
+ public HttpRequest POST() { return method("POST"); }
+
+ @Override
+ public HttpRequest PUT() { return method("PUT"); }
+
+ @Override
+ public HttpRequest method(String method) {
+ Objects.requireNonNull(method);
+ this.method = method;
+ return new HttpRequestImpl(client, method, this);
+ }
+
+ @Override
+ public HttpRequest.Builder timeout(TimeUnit timeunit, long timeval) {
+ Objects.requireNonNull(timeunit);
+ this.timeval = timeunit.toMillis(timeval);
+ return this;
+ }
+
+ long timeval() { return timeval; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.http.HttpClient.Version;
+import java.net.http.HttpResponse.MultiProcessor;
+import java.util.concurrent.CompletableFuture;
+import java.net.SocketPermission;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.util.Set;
+import static java.net.http.HttpRedirectImpl.getRedirects;
+import java.util.Locale;
+
+class HttpRequestImpl extends HttpRequest {
+
+ private final HttpHeadersImpl userHeaders;
+ private final HttpHeadersImpl systemHeaders;
+ private final URI uri;
+ private InetSocketAddress authority; // only used when URI not specified
+ private final String method;
+ private final HttpClientImpl client;
+ private final HttpRedirectImpl followRedirects;
+ private final ProxySelector proxy;
+ final BodyProcessor requestProcessor;
+ final boolean secure;
+ final boolean expectContinue;
+ private final java.net.http.HttpClient.Version version;
+ private boolean isWebSocket;
+ final MultiExchange exchange;
+ private boolean receiving;
+ private AccessControlContext acc;
+ private final long timeval;
+
+ public HttpRequestImpl(HttpClientImpl client,
+ String method,
+ HttpRequestBuilderImpl builder) {
+ this.client = client;
+ this.method = method == null? "GET" : method;
+ this.userHeaders = builder.headers() == null ?
+ new HttpHeadersImpl() : builder.headers();
+ dropDisallowedHeaders();
+ this.followRedirects = getRedirects(builder.followRedirects() == null ?
+ client.followRedirects() : builder.followRedirects());
+ this.systemHeaders = new HttpHeadersImpl();
+ this.uri = builder.uri();
+ this.proxy = builder.proxy();
+ this.expectContinue = builder.expectContinue();
+ this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
+ this.version = builder.version();
+ if (builder.body() == null) {
+ this.requestProcessor = HttpRequest.noBody();
+ } else {
+ this.requestProcessor = builder.body();
+ }
+ this.exchange = new MultiExchange(this);
+ this.timeval = builder.timeval();
+ }
+
+ /** Creates a HttpRequestImpl using fields of an existing request impl. */
+ public HttpRequestImpl(URI uri,
+ HttpRequest request,
+ HttpClientImpl client,
+ String method,
+ HttpRequestImpl other) {
+ this.client = client;
+ this.method = method == null? "GET" : method;
+ this.userHeaders = other.userHeaders == null ?
+ new HttpHeadersImpl() : other.userHeaders;
+ dropDisallowedHeaders();
+ this.followRedirects = getRedirects(other.followRedirects() == null ?
+ client.followRedirects() : other.followRedirects());
+ this.systemHeaders = other.systemHeaders;
+ this.uri = uri;
+ this.expectContinue = other.expectContinue;
+ this.secure = other.secure;
+ this.requestProcessor = other.requestProcessor;
+ this.proxy = other.proxy;
+ this.version = other.version;
+ this.acc = other.acc;
+ this.exchange = new MultiExchange(this);
+ this.timeval = other.timeval;
+ }
+
+ /* used for creating CONNECT requests */
+ HttpRequestImpl(HttpClientImpl client,
+ String method,
+ InetSocketAddress authority) {
+ this.client = client;
+ this.method = method;
+ this.followRedirects = getRedirects(client.followRedirects());
+ this.systemHeaders = new HttpHeadersImpl();
+ this.userHeaders = new HttpHeadersImpl();
+ this.uri = null;
+ this.proxy = null;
+ this.requestProcessor = HttpRequest.noBody();
+ this.version = java.net.http.HttpClient.Version.HTTP_1_1;
+ this.authority = authority;
+ this.secure = false;
+ this.expectContinue = false;
+ this.exchange = new MultiExchange(this);
+ this.timeval = 0; // block TODO: fix
+ }
+
+ @Override
+ public HttpClientImpl client() {
+ return client;
+ }
+
+
+ @Override
+ public String toString() {
+ return (uri == null ? "" : uri.toString()) + "/" + method + "("
+ + hashCode() + ")";
+ }
+
+ @Override
+ public HttpHeaders headers() {
+ userHeaders.makeUnmodifiable();
+ return userHeaders;
+ }
+
+ InetSocketAddress authority() { return authority; }
+
+ void setH2Upgrade() {
+ Http2ClientImpl h2client = client.client2();
+ systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings");
+ systemHeaders.setHeader("Upgrade", "h2c");
+ systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString());
+ }
+
+ private static final Set<String> DISALLOWED_HEADERS_SET = Set.of(
+ "authorization", "connection", "cookie", "content-length",
+ "date", "expect", "from", "host", "origin", "proxy-authorization",
+ "referer", "user-agent", "upgrade", "via", "warning");
+
+
+ // we silently drop headers that are disallowed
+ private void dropDisallowedHeaders() {
+ Set<String> hdrnames = userHeaders.directMap().keySet();
+
+ hdrnames.removeIf((s) ->
+ DISALLOWED_HEADERS_SET.contains(s.toLowerCase())
+ );
+ }
+
+ private synchronized void receiving() {
+ if (receiving) {
+ throw new IllegalStateException("already receiving response");
+ }
+ receiving = true;
+ }
+
+ /*
+ * Response filters may result in a new HttpRequestImpl being created
+ * (but still associated with the same API HttpRequest) and the process
+ * is repeated.
+ */
+ @Override
+ public HttpResponse response() throws IOException, InterruptedException {
+ receiving(); // TODO: update docs
+ if (System.getSecurityManager() != null) {
+ acc = AccessController.getContext();
+ }
+ return exchange.response();
+ }
+
+ @Override
+ public synchronized CompletableFuture<HttpResponse> responseAsync() {
+ receiving(); // TODO: update docs
+ if (System.getSecurityManager() != null) {
+ acc = AccessController.getContext();
+ }
+ return exchange.responseAsync(null)
+ .thenApply((r) -> (HttpResponse)r);
+ }
+
+ public <U> CompletableFuture<U>
+ sendAsyncMulti(HttpResponse.MultiProcessor<U> rspproc) {
+ // To change body of generated methods, choose Tools | Templates.
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public boolean expectContinue() { return expectContinue; }
+
+ public boolean requestHttp2() {
+ return version.equals(HttpClient.Version.HTTP_2);
+ //return client.getHttp2Allowed();
+ }
+
+ AccessControlContext getAccessControlContext() { return acc; }
+
+ InetSocketAddress proxy() {
+ ProxySelector ps = this.proxy;
+ if (ps == null) {
+ ps = client.proxy().orElse(null);
+ }
+ if (ps == null || method.equalsIgnoreCase("CONNECT")) {
+ return null;
+ }
+ return (InetSocketAddress)ps.select(uri).get(0).address();
+ }
+
+ boolean secure() { return secure; }
+
+ void isWebSocket(boolean is) {
+ isWebSocket = is;
+ }
+
+ boolean isWebSocket() {
+ return isWebSocket;
+ }
+
+ /** Returns the follow-redirects setting for this request. */
+ @Override
+ public java.net.http.HttpClient.Redirect followRedirects() {
+ return getRedirects(followRedirects);
+ }
+
+ HttpRedirectImpl followRedirectsImpl() { return followRedirects; }
+
+ /**
+ * Returns the request method for this request. If not set explicitly,
+ * the default method for any request is "GET".
+ */
+ @Override
+ public String method() { return method; }
+
+ @Override
+ public URI uri() { return uri; }
+
+ HttpHeadersImpl getUserHeaders() { return userHeaders; }
+
+ HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
+
+ HttpClientImpl getClient() { return client; }
+
+ BodyProcessor requestProcessor() { return requestProcessor; }
+
+ @Override
+ public Version version() { return version; }
+
+ void addSystemHeader(String name, String value) {
+ systemHeaders.addHeader(name, value);
+ }
+
+ void setSystemHeader(String name, String value) {
+ systemHeaders.setHeader(name, value);
+ }
+
+ long timeval() { return timeval; }
+
+ @Override
+ public <U> CompletableFuture<U>
+ multiResponseAsync(MultiProcessor<U> rspproc) {
+ //To change body of generated methods, choose Tools | Templates.
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Represents a response to a {@link HttpRequest}. A {@code HttpResponse} is
+ * available when the response status code and headers have been received, but
+ * before the response body is received.
+ *
+ * <p> Methods are provided in this class for accessing the response headers,
+ * and status code immediately and also methods for retrieving the response body.
+ * Static methods are provided which implement {@link BodyProcessor} for
+ * standard body types such as {@code String, byte arrays, files}.
+ *
+ * <p> The {@link #body(BodyProcessor) body} or {@link #bodyAsync(BodyProcessor)
+ * bodyAsync} which retrieve any response body must be called to ensure that the
+ * TCP connection can be re-used subsequently, and any response trailers
+ * accessed, if they exist, unless it is known that no response body was received.
+ *
+ * @since 9
+ */
+public abstract class HttpResponse {
+
+ HttpResponse() { }
+
+ /**
+ * Returns the status code for this response.
+ *
+ * @return the response code
+ */
+ public abstract int statusCode();
+
+ /**
+ * Returns the {@link HttpRequest} for this response.
+ *
+ * @return the request
+ */
+ public abstract HttpRequest request();
+
+ /**
+ * Returns the received response headers.
+ *
+ * @return the response headers
+ */
+ public abstract HttpHeaders headers();
+
+ /**
+ * Returns the received response trailers, if there are any. This must only
+ * be called after the response body has been received.
+ *
+ * @return the response trailers (may be empty)
+ * @throws IllegalStateException if the response body has not been received
+ * yet
+ */
+ public abstract HttpHeaders trailers();
+
+ /**
+ * Returns the body, blocking if necessary. The type T is determined by the
+ * {@link BodyProcessor} implementation supplied. The body object will be
+ * returned immediately if it is a type (such as {@link java.io.InputStream}
+ * which reads the data itself. If the body object represents the fully read
+ * body then it blocks until it is fully read.
+ *
+ * @param <T> the type of the returned body object
+ * @param processor the processor to handle the response body
+ * @return the body
+ * @throws java.io.UncheckedIOException if an I/O error occurs reading the
+ * response
+ */
+ public abstract <T> T body(BodyProcessor<T> processor);
+
+ /**
+ * Returns a {@link java.util.concurrent.CompletableFuture} of type T. This
+ * always returns immediately and the future completes when the body object
+ * is available. The body will be available immediately if it is a type
+ * (such as {@link java.io.InputStream} which reads the data itself. If the
+ * body object represents the fully read body then it will not be available
+ * until it is fully read.
+ *
+ * @param <T> the type of the returned body object
+ * @param processor the processor to handle the response body
+ * @return a CompletableFuture
+ */
+ public abstract <T> CompletableFuture<T> bodyAsync(BodyProcessor<T> processor);
+
+ /**
+ * Returns the {@link javax.net.ssl.SSLParameters} in effect for this
+ * response. Returns {@code null} if this is not a https response.
+ *
+ * @return the SSLParameters associated with the response
+ */
+ public abstract SSLParameters sslParameters();
+
+ /**
+ * Returns the URI that the response was received from. This may be
+ * different from the request URI if redirection occurred.
+ *
+ * @return the URI of the response
+ */
+ public abstract URI uri();
+
+ /**
+ * Returns the HTTP protocol version that was used for this response.
+ *
+ * @return HTTP protocol version
+ */
+ public abstract HttpClient.Version version();
+
+ /**
+ * Returns a {@link BodyProcessor}<{@link java.nio.file.Path}> where
+ * the file is created if it does not already exist. When the Path object is
+ * returned, the body has been completely written to the file.
+ *
+ * @param file the file to store the body in
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<Path> asFile(Path file) {
+ return asFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ }
+
+ /**
+ * Returns a {@link BodyProcessor}<{@link java.nio.file.Path}> where
+ * the download directory is specified, but the filename is obtained from
+ * the Content-Disposition response header. The Content-Disposition header
+ * must specify the <i>attachment</i> type and must also contain a
+ * <i>filename</i> parameter. If the filename specifies multiple path
+ * components only the final component is used as the filename (with the
+ * given directory name). When the Path object is returned, the body has
+ * been completely written to the file. The returned Path is the combination
+ * of the supplied directory name and the file name supplied by the server.
+ * If the destination directory does not exist or cannot be written to, then
+ * the response will fail with an IOException.
+ *
+ * @param directory the directory to store the file in
+ * @param openOptions open options
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<Path> asFileDownload(Path directory,
+ OpenOption... openOptions) {
+ return new AbstractResponseProcessor<Path>() {
+
+ FileChannel fc;
+ Path file;
+
+ @Override
+ public Path onResponseBodyStartImpl(long contentLength,
+ HttpHeaders headers)
+ throws IOException
+ {
+ String dispoHeader = headers.firstValue("Content-Disposition")
+ .orElseThrow(() -> new IOException("No Content-Disposition"));
+ if (!dispoHeader.startsWith("attachment;")) {
+ throw new IOException("Unknown Content-Disposition type");
+ }
+ int n = dispoHeader.indexOf("filename=");
+ if (n == -1) {
+ throw new IOException("Bad Content-Disposition type");
+ }
+ String disposition = dispoHeader.substring(n + 9,
+ dispoHeader.lastIndexOf(';'));
+ file = Paths.get(directory.toString(), disposition);
+ fc = FileChannel.open(file, openOptions);
+ return null;
+ }
+
+ @Override
+ public void onResponseBodyChunkImpl(ByteBuffer b) throws IOException {
+ fc.write(b);
+ }
+
+ @Override
+ public Path onResponseComplete() throws IOException {
+ fc.close();
+ return file;
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ try {
+ if (fc != null) {
+ fc.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link BodyProcessor}<{@link java.nio.file.Path}>.
+ *
+ * <p> {@link HttpResponse}s returned using this response processor complete
+ * after the entire response, including body has been read.
+ *
+ * @param file the filename to store the body in
+ * @param openOptions any options to use when opening/creating the file
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<Path> asFile(Path file,
+ OpenOption... openOptions) {
+ return new AbstractResponseProcessor<Path>() {
+
+ FileChannel fc;
+
+ @Override
+ public Path onResponseBodyStartImpl(long contentLength,
+ HttpHeaders headers)
+ throws IOException
+ {
+ fc = FileChannel.open(file, openOptions);
+ return null;
+ }
+
+ @Override
+ public void onResponseBodyChunkImpl(ByteBuffer b)
+ throws IOException
+ {
+ fc.write(b);
+ }
+
+ @Override
+ public Path onResponseComplete() throws IOException {
+ fc.close();
+ return file;
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ try {
+ if (fc != null) {
+ fc.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ };
+ }
+
+ static class ByteArrayResponseProcessor {
+
+ static final int INITIAL_BUFLEN = 1024;
+
+ byte[] buffer;
+ int capacity;
+ boolean knownLength;
+ int position;
+
+ ByteArrayResponseProcessor() { }
+
+ public byte[] onStart(long contentLength) throws IOException {
+ if (contentLength > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException(
+ "byte array response limited to MAX_INT size");
+ }
+ capacity = (int) contentLength;
+ if (capacity != -1) {
+ buffer = new byte[capacity];
+ knownLength = true;
+ } else {
+ buffer = new byte[INITIAL_BUFLEN];
+ capacity = INITIAL_BUFLEN;
+ knownLength = false;
+ }
+ position = 0;
+ return null;
+ }
+
+ public void onBodyContent(ByteBuffer b) throws IOException {
+ int toCopy = b.remaining();
+ int size = capacity;
+ if (toCopy > capacity - position) {
+ // resize
+ size += toCopy * 2;
+ }
+ if (size != capacity) {
+ if (knownLength) {
+ // capacity should have been right from start
+ throw new IOException("Inconsistent content length");
+ }
+ byte[] newbuf = new byte[size];
+ System.arraycopy(buffer, 0, newbuf, 0, position);
+ buffer = newbuf;
+ capacity = size;
+ }
+ int srcposition = b.arrayOffset() + b.position();
+ System.arraycopy(b.array(), srcposition, buffer, position, toCopy);
+ b.position(b.limit());
+ position += toCopy;
+ }
+
+ public byte[] onComplete() throws IOException {
+ if (knownLength) {
+ if (position != capacity) {
+ throw new IOException("Wrong number of bytes received");
+ }
+ return buffer;
+ }
+ byte[] buf1 = new byte[position];
+ System.arraycopy(buffer, 0, buf1, 0, position);
+ return buf1;
+ }
+
+ public void onError(Throwable t) {
+ // TODO:
+ }
+ }
+
+ static final byte[] EMPTY = new byte[0];
+
+ /**
+ * Returns a response processor which supplies the response body to the
+ * given Consumer. Each time data is received the consumer is invoked with a
+ * byte[] containing at least one byte of data. After the final buffer is
+ * received, the consumer is invoked one last time, with an empty byte
+ * array.
+ *
+ * @param consumer a Consumer to accept the response body
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<Void> asByteArrayConsumer(Consumer<byte[]> consumer) {
+ return new AbstractResponseProcessor<Void>() {
+ @Override
+ public Void onResponseBodyStartImpl(long clen,
+ HttpHeaders h)
+ throws IOException
+ {
+ return null;
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ }
+
+ @Override
+ public void onResponseBodyChunkImpl(ByteBuffer b) throws IOException {
+ if (!b.hasRemaining()) {
+ return;
+ }
+ byte[] buf = new byte[b.remaining()];
+ b.get(buf);
+ consumer.accept(buf);
+ }
+
+ @Override
+ public Void onResponseComplete() throws IOException {
+ consumer.accept(EMPTY);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Returns a BodyProcessor which delivers the response data to a
+ * {@link java.util.concurrent.Flow.Subscriber}{@code ByteBuffer}.
+ * <p>
+ * The given {@code Supplier<U>} is invoked when the Flow is completed in
+ * order to convert the flow data into the U object that is returned as the
+ * response body.
+ *
+ * @param <U> the response body type
+ * @param subscriber the Flow.Subscriber
+ * @param bufferSize the maximum number of bytes of data to be supplied in
+ * each ByteBuffer
+ * @param bodySupplier an object that converts the received data to the body
+ * type U.
+ * @return a BodyProcessor
+ *
+ * public static <U> BodyProcessor<Flow.Subscriber<ByteBuffer>>
+ * asFlowSubscriber() {
+ *
+ * return new BodyProcessor<U>() { Flow.Subscriber<ByteBuffer> subscriber;
+ * LongConsumer flowController; FlowSubscription subscription; Supplier<U>
+ * bodySupplier; int bufferSize; // down-stream Flow window. long
+ * buffersWindow; // upstream window long bytesWindow;
+ * LinkedList<ByteBuffer> buffers = new LinkedList<>();
+ *
+ * class FlowSubscription implements Subscription { int recurseLevel = 0;
+ * @Override public void request(long n) { boolean goodToGo = recurseLevel++
+ * == 0;
+ *
+ * while (goodToGo && buffers.size() > 0 && n > 0) { ByteBuffer buf =
+ * buffers.get(0); subscriber.onNext(buf); n--; } buffersWindow += n;
+ * flowController.accept(n * bufferSize); recurseLevel--; }
+ *
+ * @Override public void cancel() { // ?? set flag and throw exception on
+ * next receipt of buffer } }
+ *
+ * @Override public U onResponseBodyStart(long contentLength, HttpHeaders
+ * responseHeaders, LongConsumer flowController) throws IOException {
+ * this.subscriber = subscriber; this.flowController = flowController;
+ * this.subscription = new FlowSubscription(); this.bufferSize = bufferSize;
+ * subscriber.onSubscribe(subscription); return null; }
+ *
+ * @Override public void onResponseError(Throwable t) {
+ * subscriber.onError(t); }
+ *
+ * @Override public void onResponseBodyChunk(ByteBuffer b) throws
+ * IOException { if (buffersWindow > 0) { buffersWindow --;
+ * subscriber.onNext(b); } else { buffers.add(b); // or could combine
+ * buffers? } }
+ *
+ * @Override public U onResponseComplete() throws IOException {
+ * subscriber.onComplete(); return bodySupplier.get(); } }; }
+ */
+ private static final ByteBuffer EOF = ByteBuffer.allocate(0);
+ private static final ByteBuffer CLOSED = ByteBuffer.allocate(0);
+
+ // prototype using ByteBuffer based flow control. InputStream feeds off a
+ // BlockingQueue. Size of Q is determined from the the bufsize (bytes) and
+ // the default ByteBuffer size. bufsize should be a reasonable multiple of
+ // ByteBuffer size to prevent underflow/starvation. The InputStream updates
+ // the flowControl window by one as each ByteBuffer is fully consumed.
+ // Special sentinels are used to indicate stream closed and EOF.
+ /**
+ * Returns a response body processor which provides an InputStream to read
+ * the body.
+ *
+ * @implNote This mechanism is provided primarily for backwards
+ * compatibility for code that expects InputStream. It is recommended for
+ * better performance to use one of the other response processor
+ * implementations.
+ *
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<InputStream> asInputStream() {
+ return new BodyProcessor<InputStream>() {
+ int queueSize = 2;
+ private volatile Throwable throwable;
+
+ BlockingQueue<ByteBuffer> queue = new LinkedBlockingQueue<>();
+
+ private void closeImpl() {
+ try {
+ queue.put(CLOSED);
+ } catch (InterruptedException e) { }
+ }
+
+ @Override
+ public InputStream onResponseBodyStart(long contentLength,
+ HttpHeaders responseHeaders,
+ LongConsumer flowController)
+ throws IOException
+ {
+ flowController.accept(queueSize);
+
+ return new InputStream() {
+ ByteBuffer buffer;
+
+ @Override
+ public int read() throws IOException {
+ byte[] bb = new byte[1];
+ int n = read(bb, 0, 1);
+ if (n == -1) {
+ return -1;
+ } else {
+ return bb[0];
+ }
+ }
+
+ @Override
+ public int read(byte[] bb) throws IOException {
+ return read(bb, 0, bb.length);
+ }
+
+ @Override
+ public int read(byte[] bb, int offset, int length)
+ throws IOException
+ {
+ int n;
+ if (getBuffer()) {
+ return -1; // EOF
+ } else {
+ int remaining = buffer.remaining();
+ if (length >= remaining) {
+ buffer.get(bb, offset, remaining);
+ return remaining;
+ } else {
+ buffer.get(bb, offset, length);
+ return length;
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ closeImpl();
+ }
+
+ private boolean getBuffer() throws IOException {
+ while (buffer == null || (buffer != EOF &&
+ buffer != CLOSED && !buffer.hasRemaining())) {
+ try {
+ buffer = queue.take();
+ flowController.accept(1);
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+ if (buffer == CLOSED) {
+ if (throwable != null) {
+ if (throwable instanceof IOException) {
+ throw (IOException) throwable;
+ } else {
+ throw new IOException(throwable);
+ }
+ }
+ throw new IOException("Closed");
+ }
+
+ if (buffer == EOF) {
+ return true; // EOF
+ }
+ return false; // not EOF
+ }
+
+ };
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ throwable = t;
+ closeImpl();
+ }
+
+ @Override
+ public void onResponseBodyChunk(ByteBuffer b) throws IOException {
+ try {
+ queue.put(Utils.copy(b));
+ } catch (InterruptedException e) {
+ // shouldn't happen as queue should never block
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public InputStream onResponseComplete() throws IOException {
+ try {
+ queue.put(EOF);
+ } catch (InterruptedException e) {
+ throw new IOException(e); // can't happen
+ }
+ return null;
+ }
+
+ };
+ }
+
+ /**
+ * Common super class that takes care of flow control
+ *
+ * @param <T>
+ */
+ private static abstract class AbstractResponseProcessor<T>
+ implements BodyProcessor<T>
+ {
+ LongConsumer flowController;
+
+ @Override
+ public final T onResponseBodyStart(long contentLength,
+ HttpHeaders responseHeaders,
+ LongConsumer flowController)
+ throws IOException
+ {
+ this.flowController = flowController;
+ flowController.accept(1);
+ return onResponseBodyStartImpl(contentLength, responseHeaders);
+ }
+
+ public abstract T onResponseBodyStartImpl(long contentLength,
+ HttpHeaders responseHeaders)
+ throws IOException;
+
+ public abstract void onResponseBodyChunkImpl(ByteBuffer b)
+ throws IOException;
+
+ @Override
+ public final void onResponseBodyChunk(ByteBuffer b) throws IOException {
+ onResponseBodyChunkImpl(b);
+ flowController.accept(1);
+ }
+ }
+
+ /**
+ * Returns a {@link BodyProcessor}<byte[]> which returns the response
+ * body as a {@code byte array}.
+ *
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<byte[]> asByteArray() {
+ ByteArrayResponseProcessor brp = new ByteArrayResponseProcessor();
+
+ return new AbstractResponseProcessor<byte[]>() {
+
+ @Override
+ public byte[] onResponseBodyStartImpl(long contentLength,
+ HttpHeaders h)
+ throws IOException
+ {
+ brp.onStart(contentLength);
+ return null;
+ }
+
+ @Override
+ public void onResponseBodyChunkImpl(ByteBuffer b)
+ throws IOException
+ {
+ brp.onBodyContent(b);
+ }
+
+ @Override
+ public byte[] onResponseComplete() throws IOException {
+ return brp.onComplete();
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ brp.onError(t);
+ }
+ };
+ }
+
+ /**
+ * Returns a response processor which decodes the body using the character
+ * set specified in the {@code Content-encoding} response header. If there
+ * is no such header, or the character set is not supported, then
+ * {@link java.nio.charset.StandardCharsets#ISO_8859_1 ISO_8859_1} is used.
+ *
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<String> asString() {
+ return asString(null);
+ }
+
+ /**
+ * Returns a MultiProcessor that handles multiple responses, writes the
+ * response bodies to files and which returns an aggregate response object
+ * that is a {@code Map<URI,Path>}. The keyset of the Map represents the
+ * URIs of the original request and any additional requests generated by the
+ * server. The values are the paths of the destination files. Each path uses
+ * the URI path of the request offset from the destination parent directory
+ * provided.
+ *
+ * <p> All incoming additional requests (push promises) are accepted by this
+ * multi response processor. Errors are effectively ignored and any failed
+ * responses are simply omitted from the result Map. Other implementations
+ * of MultiProcessor can handle these situations
+ *
+ * <p><b>Example usage</b>
+ * <pre>
+ * {@code
+ * CompletableFuture<Map<URI,Path>> cf =
+ * HttpRequest.create(new URI("https://www.foo.com/"))
+ * .version(Version.HTTP2)
+ * .GET()
+ * .sendAsyncMulti(HttpResponse.multiFile("/usr/destination"));
+ *
+ * Map<URI,Path> results = cf.join();
+ * }
+ * </pre>
+ *
+ * @param destination the destination parent directory of all response
+ * bodies
+ * @return a MultiProcessor
+ */
+ public static MultiProcessor<Map<URI, Path>> multiFile(Path destination) {
+
+ return new MultiProcessor<Map<URI, Path>>() {
+ Map<URI, CompletableFuture<Path>> bodyCFs = new HashMap<>();
+
+ Map<URI, Path> results = new HashMap<>();
+
+ @Override
+ public BiFunction<HttpRequest, CompletableFuture<HttpResponse>, Boolean>
+ onStart(HttpRequest mainRequest,
+ CompletableFuture<HttpResponse> response) {
+ bodyCFs.put(mainRequest.uri(), getBody(mainRequest, response));
+ return (HttpRequest additional, CompletableFuture<HttpResponse> cf) -> {
+ CompletableFuture<Path> bcf = getBody(additional, cf);
+ bodyCFs.put(additional.uri(), bcf);
+ // we accept all comers
+ return true;
+ };
+ }
+
+ private CompletableFuture<Path> getBody(HttpRequest req,
+ CompletableFuture<HttpResponse> cf) {
+ URI u = req.uri();
+ String path = u.getPath();
+ return cf.thenCompose((HttpResponse resp) -> {
+ return resp.bodyAsync(HttpResponse.asFile(destination.resolve(path)));
+ });
+ }
+
+ @Override
+ public Map<URI, Path> onComplete() {
+ // all CFs have completed normally or in error.
+ Set<Map.Entry<URI, CompletableFuture<Path>>> entries = bodyCFs.entrySet();
+ for (Map.Entry<URI, CompletableFuture<Path>> entry : entries) {
+ CompletableFuture<Path> v = entry.getValue();
+ URI uri = entry.getKey();
+ if (v.isDone() && !v.isCompletedExceptionally()) {
+ results.put(uri, v.join());
+ }
+ }
+ return results;
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link BodyProcessor}<{@link String}>.
+ *
+ * @param charset the name of the charset to interpret the body as. If
+ * {@code null} then the processor tries to determine the character set from
+ * the {@code Content-encoding} header. If that charset is not supported
+ * then {@link java.nio.charset.StandardCharsets#ISO_8859_1 ISO_8859_1} is
+ * used.
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<String> asString(Charset charset) {
+
+ ByteArrayResponseProcessor brp = new ByteArrayResponseProcessor();
+
+ return new AbstractResponseProcessor<String>() {
+ Charset cs = charset;
+ HttpHeaders headers;
+
+ @Override
+ public String onResponseBodyStartImpl(long contentLength,
+ HttpHeaders h)
+ throws IOException
+ {
+ headers = h;
+ brp.onStart(contentLength);
+ return null;
+ }
+
+ @Override
+ public void onResponseBodyChunkImpl(ByteBuffer b) throws IOException {
+ brp.onBodyContent(b);
+ }
+
+ @Override
+ public String onResponseComplete() throws IOException {
+ byte[] buf = brp.onComplete();
+ if (cs == null) {
+ cs = headers.firstValue("Content-encoding")
+ .map((String s) -> Charset.forName(s))
+ .orElse(StandardCharsets.ISO_8859_1);
+ }
+ return new String(buf, cs);
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ brp.onError(t);
+ }
+
+ };
+ }
+
+ /**
+ * Returns a response processor which ignores the response body.
+ *
+ * @return a {@code BodyProcessor}
+ */
+ public static BodyProcessor<Void> ignoreBody() {
+ return asByteArrayConsumer((byte[] buf) -> { /* ignore */ });
+ }
+
+ /**
+ * A processor for response bodies, which determines the type of the
+ * response body returned from {@link HttpResponse}. Response processors can
+ * either return an object that represents the body itself (after it has
+ * been read) or else an object that is used to read the body (such as an
+ * {@code InputStream}). The parameterized type {@code <T>} is the type of
+ * the returned body object from
+ * {@link HttpResponse#body(BodyProcessor) HttpResponse.body} and
+ * (indirectly) from {@link HttpResponse#bodyAsync(BodyProcessor)
+ * HttpResponse.bodyAsync}.
+ *
+ * <p> Implementations of this interface are provided in {@link HttpResponse}
+ * which write responses to {@code String, byte[], File, Consumer<byte[]>}.
+ * Custom implementations can also be used.
+ *
+ * <p> The methods of this interface may be called from multiple threads,
+ * but only one method is invoked at a time, and behaves as if called from
+ * one thread.
+ *
+ * @param <T> the type of the response body
+ *
+ * @since 9
+ */
+ public interface BodyProcessor<T> {
+
+ /**
+ * Called immediately before the response body is read. If {@code <T>}
+ * is an object used to read or accept the response body, such as a
+ * {@code Consumer} or {@code InputStream} then it should be returned
+ * from this method, and the body object will be returned before any
+ * data is read. If {@code <T>} represents the body itself after being
+ * read, then this method must return {@code null} and the body will be
+ * returned from {@link #onResponseComplete()}. In both cases, the
+ * actual body data is provided by the
+ * {@link #onResponseBodyChunk(ByteBuffer) onResponseBodyChunk} method
+ * in exactly the same way.
+ *
+ * <p> flowController is a consumer of long values and is used for
+ * updating a flow control window as follows. The window represents the
+ * number of times
+ * {@link #onResponseBodyChunk(java.nio.ByteBuffer) onResponseBodyChunk}
+ * may be called before receiving further updates to the window. Each
+ * time it is called, the window is reduced by {@code 1}. When the
+ * window reaches zero {@code onResponseBodyChunk()} will not be called
+ * again until the window has opened again with further calls to
+ * flowController.accept().
+ * {@link java.util.function.LongConsumer#accept(long) flowcontroller.accept()}
+ * must be called to open (increase) the window by the specified amount.
+ * The initial value is zero. This implies that if {@code
+ * onResponseBodyStart()} does not call {@code flowController.accept()}
+ * with a positive value no data will ever be delivered.
+ *
+ * @param contentLength {@code -1} signifies unknown content length.
+ * Otherwise, a positive integer, or zero.
+ * @param responseHeaders the response headers
+ * @param flowController a LongConsumer used to update the flow control
+ * window
+ * @return {@code null} or an object that can be used to read the
+ * response body.
+ * @throws IOException if an exception occurs starting the response
+ * body receive
+ */
+ T onResponseBodyStart(long contentLength,
+ HttpHeaders responseHeaders,
+ LongConsumer flowController)
+ throws IOException;
+
+ /**
+ * Called if an error occurs while reading the response body. This
+ * terminates the operation and no further calls will occur after this.
+ *
+ * @param t the Throwable
+ */
+ void onResponseError(Throwable t);
+
+ /**
+ * Called for each buffer of data received for this response.
+ * ByteBuffers can be reused as soon as this method returns.
+ *
+ * @param b a ByteBuffer whose position is at the first byte that can be
+ * read, and whose limit is after the last byte that can be read
+ * @throws IOException in case of I/O error
+ */
+ void onResponseBodyChunk(ByteBuffer b) throws IOException;
+
+ /**
+ * Called after the last time
+ * {@link #onResponseBodyChunk(java.nio.ByteBuffer)} has been called and
+ * returned indicating that the entire content has been read. This
+ * method must return an object that represents or contains the response
+ * body just received, but only if an object was not returned from
+ * {@link #onResponseBodyStart(long, HttpHeaders, LongConsumer)
+ * onResponseBodyStart}.
+ *
+ * @return a T, or {@code null} if an object was already returned
+ * @throws IOException in case of I/O error
+ */
+ T onResponseComplete() throws IOException;
+ }
+
+ /**
+ * A response processor for a HTTP/2 multi response. A multi response
+ * comprises a main response, and zero or more additional responses. Each
+ * additional response is sent by the server in response to requests that
+ * the server also generates. Additional responses are typically resources
+ * that the server guesses the client will need which are related to the
+ * initial request.
+ *
+ * <p>The server generated requests are also known as <i>push promises</i>.
+ * The server is permitted to send any number of these requests up to the
+ * point where the main response is fully received. Therefore, after
+ * completion of the main response body, the final number of additional
+ * responses is known. Additional responses may be cancelled, but given that
+ * the server does not wait for any acknowledgment before sending the
+ * response, this must be done quickly to avoid unnecessary data transmission.
+ *
+ * <p> {@code MultiProcessor}s are parameterised with a type {@code T} which
+ * represents some meaningful aggregate of the responses received. This
+ * would typically be a Collection of response or response body objects. One
+ * example implementation can be found at {@link
+ * HttpResponse#multiFile(java.nio.file.Path)}.
+ *
+ * @param <T> a type representing the aggregated results
+ *
+ * @since 9
+ */
+ public interface MultiProcessor<T> {
+
+ /**
+ * Called before or soon after a multi request is sent. The request that
+ * initiated the multi response is supplied, as well as a
+ * CompletableFuture for the main response. The implementation of this
+ * method must return a BiFunction which is called once for each push
+ * promise received.
+ *
+ * <p> The parameters to the {@code BiFunction} are the {@code HttpRequest}
+ * for the push promise and a {@code CompletableFuture} for its
+ * response. The function must return a Boolean indicating whether the
+ * push promise has been accepted (true) or should be canceled (false).
+ * The CompletableFutures for any canceled pushes are themselves
+ * completed exceptionally soon after the function returns.
+ *
+ * @param mainRequest the main request
+ * @param response a CompletableFuture for the main response
+ * @return a BiFunction that is called for each push promise
+ */
+ BiFunction<HttpRequest, CompletableFuture<HttpResponse>, Boolean>
+ onStart(HttpRequest mainRequest,
+ CompletableFuture<HttpResponse> response);
+
+ /**
+ * Called after all responses associated with the multi response have
+ * been fully processed, including response bodies.
+ *
+ * <p> Example types for {@code T} could be Collections of response body
+ * types or {@code Map}s from request {@code URI} to a response body
+ * type.
+ *
+ * @return the aggregate response object
+ */
+ T onComplete();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.LongConsumer;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * The implementation class for HttpResponse
+ */
+class HttpResponseImpl extends HttpResponse {
+
+ int responseCode;
+ Exchange exchange;
+ HttpRequestImpl request;
+ HttpHeaders1 headers;
+ HttpHeaders1 trailers;
+ SSLParameters sslParameters;
+ URI uri;
+ HttpClient.Version version;
+ AccessControlContext acc;
+ RawChannel rawchan;
+ HttpConnection connection;
+
+ public HttpResponseImpl(int responseCode, Exchange exch, HttpHeaders1 headers,
+ HttpHeaders1 trailers, SSLParameters sslParameters,
+ HttpClient.Version version, HttpConnection connection) {
+ this.responseCode = responseCode;
+ this.exchange = exch;
+ this.request = exchange.request();
+ this.headers = headers;
+ this.trailers = trailers;
+ this.sslParameters = sslParameters;
+ this.uri = request.uri();
+ this.version = version;
+ this.connection = connection;
+ }
+
+ @Override
+ public int statusCode() {
+ return responseCode;
+ }
+
+ @Override
+ public HttpRequestImpl request() {
+ return request;
+ }
+
+ @Override
+ public HttpHeaders headers() {
+ headers.makeUnmodifiable();
+ return headers;
+ }
+
+ @Override
+ public HttpHeaders trailers() {
+ trailers.makeUnmodifiable();
+ return trailers;
+ }
+
+
+ @Override
+ public <T> T body(java.net.http.HttpResponse.BodyProcessor<T> processor) {
+ return exchange.responseBody(processor);
+ }
+
+ @Override
+ public <T> CompletableFuture<T> bodyAsync(java.net.http.HttpResponse.BodyProcessor<T> processor) {
+ acc = AccessController.getContext();
+ return exchange.responseBodyAsync(processor);
+ }
+
+ @Override
+ public SSLParameters sslParameters() {
+ return sslParameters;
+ }
+
+ public AccessControlContext getAccessControlContext() {
+ return acc;
+ }
+
+ @Override
+ public URI uri() {
+ return uri;
+ }
+
+ @Override
+ public HttpClient.Version version() {
+ return version;
+ }
+ // keepalive flag determines whether connection is closed or kept alive
+ // by reading/skipping data
+
+ public static java.net.http.HttpResponse.BodyProcessor<Void> ignoreBody(boolean keepalive) {
+ return new java.net.http.HttpResponse.BodyProcessor<Void>() {
+
+ @Override
+ public Void onResponseBodyStart(long clen, HttpHeaders h,
+ LongConsumer flowController) throws IOException {
+ return null;
+ }
+
+ @Override
+ public void onResponseBodyChunk(ByteBuffer b) throws IOException {
+ }
+
+ @Override
+ public Void onResponseComplete() throws IOException {
+ return null;
+ }
+
+ @Override
+ public void onResponseError(Throwable t) {
+ }
+ };
+ }
+
+ /**
+ *
+ * @return
+ */
+ RawChannel rawChannel() {
+ if (rawchan == null) {
+ rawchan = new RawChannel(request.client(), connection);
+ }
+ return rawchan;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpTimeoutException.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a response is not received within a specified time period.
+ */
+public class HttpTimeoutException extends IOException {
+
+ private static final long serialVersionUID = 981344271622632951L;
+
+ public HttpTimeoutException(String message) {
+ super(message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.util.Locale;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * -Djava.net.HttpClient.log=errors,requests,headers,frames[:type:type2:..],content
+ *
+ * Any of errors, requests, headers or content are optional.
+ *
+ * Other handlers may be added. All logging is at level INFO
+ *
+ * Logger name is "java.net.http.HttpClient"
+ */
+class Log {
+
+ final static String logProp = "java.net.http.HttpClient.log";
+
+ public static final int OFF = 0;
+ public static final int ERRORS = 0x1;
+ public static final int REQUESTS = 0x2;
+ public static final int HEADERS = 0x4;
+ public static final int CONTENT = 0x8;
+ public static final int FRAMES = 0x10;
+ public static final int SSL = 0x20;
+ static int logging;
+
+ // Frame types: "control", "data", "window", "all"
+ public static final int CONTROL = 1; // all except DATA and WINDOW_UPDATES
+ public static final int DATA = 2;
+ public static final int WINDOW_UPDATES = 4;
+ public static final int ALL = CONTROL| DATA | WINDOW_UPDATES;
+ static int frametypes;
+
+ static sun.util.logging.PlatformLogger logger;
+
+ static {
+ String s = Utils.getNetProperty(logProp);
+ if (s == null) {
+ logging = OFF;
+ } else {
+ String[] vals = s.split(",");
+ for (String val : vals) {
+ switch (val.toLowerCase(Locale.US)) {
+ case "errors":
+ logging |= ERRORS;
+ break;
+ case "requests":
+ logging |= REQUESTS;
+ break;
+ case "headers":
+ logging |= HEADERS;
+ break;
+ case "content":
+ logging |= CONTENT;
+ break;
+ case "ssl":
+ logging |= SSL;
+ break;
+ case "all":
+ logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS;
+ break;
+ }
+ if (val.startsWith("frames")) {
+ logging |= FRAMES;
+ String[] types = val.split(":");
+ if (types.length == 1) {
+ frametypes = CONTROL | DATA | WINDOW_UPDATES;
+ } else {
+ for (String type : types) {
+ switch (type.toLowerCase()) {
+ case "control":
+ frametypes |= CONTROL;
+ break;
+ case "data":
+ frametypes |= DATA;
+ break;
+ case "window":
+ frametypes |= WINDOW_UPDATES;
+ break;
+ case "all":
+ frametypes = ALL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (logging != OFF) {
+ logger = PlatformLogger.getLogger("java.net.http.HttpClient");
+ }
+ }
+
+ static boolean errors() {
+ return (logging & ERRORS) != 0;
+ }
+
+ static boolean requests() {
+ return (logging & REQUESTS) != 0;
+ }
+
+ static boolean headers() {
+ return (logging & HEADERS) != 0;
+ }
+
+ static boolean ssl() {
+ return (logging & SSL) != 0;
+ }
+
+ static boolean frames() {
+ return (logging & FRAMES) != 0;
+ }
+
+ static void logError(String s) {
+ if (errors())
+ logger.info("ERROR: " + s);
+ }
+
+ static void logError(Throwable t) {
+ if (errors()) {
+ String s = Utils.stackTrace(t);
+ logger.info("ERROR: " + s);
+ }
+ }
+
+ static void logSSL(String s) {
+ if (ssl())
+ logger.info("SSL: " + s);
+ }
+
+ static void logRequest(String s) {
+ if (requests())
+ logger.info("REQUEST: " + s);
+ }
+
+ static void logResponse(String s) {
+ if (requests())
+ logger.info("RESPONSE: " + s);
+ }
+
+ static void logHeaders(String s) {
+ if (headers())
+ logger.info("HEADERS: " + s);
+ }
+// END HTTP2
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
+
+import static java.net.http.Pair.pair;
+
+/**
+ * Encapsulates multiple Exchanges belonging to one HttpRequestImpl.
+ * - manages filters
+ * - retries due to filters.
+ * - I/O errors and most other exceptions get returned directly to user
+ *
+ * Creates a new Exchange for each request/response interaction
+ */
+class MultiExchange {
+
+ final HttpRequestImpl request; // the user request
+ final HttpClientImpl client;
+ HttpRequestImpl currentreq; // used for async only
+ Exchange exchange; // the current exchange
+ Exchange previous;
+ int attempts;
+ // Maximum number of times a request will be retried/redirected
+ // for any reason
+
+ final static int DEFAULT_MAX_ATTEMPTS = 5;
+ final static int max_attempts = Utils.getIntegerNetProperty(
+ "sun.net.httpclient.redirects.retrylimit", DEFAULT_MAX_ATTEMPTS
+ );
+
+ private final List<HeaderFilter> filters;
+ TimedEvent td;
+ boolean cancelled = false;
+
+ /**
+ * Filter fields. These are attached as required by filters
+ * and only used by the filter implementations. This could be
+ * generalised into Objects that are passed explicitly to the filters
+ * (one per MultiExchange object, and one per Exchange object possibly)
+ */
+ volatile AuthenticationFilter.AuthInfo serverauth, proxyauth;
+ // RedirectHandler
+ volatile int numberOfRedirects = 0;
+
+ /**
+ */
+ MultiExchange(HttpRequestImpl request) {
+ this.exchange = new Exchange(request);
+ this.previous = null;
+ this.request = request;
+ this.currentreq = request;
+ this.attempts = 0;
+ this.client = request.client();
+ this.filters = client.filterChain();
+ }
+
+ public HttpResponseImpl response() throws IOException, InterruptedException {
+ HttpRequestImpl r = request;
+ if (r.timeval() != 0) {
+ // set timer
+ td = new TimedEvent(r.timeval());
+ client.registerTimer(td);
+ }
+ while (attempts < max_attempts) {
+ try {
+ attempts++;
+ Exchange currExchange = getExchange();
+ requestFilters(r);
+ HttpResponseImpl response = currExchange.response();
+ Pair<HttpResponse, HttpRequestImpl> filterResult = responseFilters(response);
+ HttpRequestImpl newreq = filterResult.second;
+ if (newreq == null) {
+ if (attempts > 1) {
+ Log.logError("Succeeded on attempt: " + attempts);
+ }
+ cancelTimer();
+ return response;
+ }
+ response.body(HttpResponse.ignoreBody());
+ setExchange(new Exchange(newreq, currExchange.getAccessControlContext() ));
+ r = newreq;
+ } catch (IOException e) {
+ if (cancelled) {
+ throw new HttpTimeoutException("Request timed out");
+ }
+ throw e;
+ }
+ }
+ cancelTimer();
+ throw new IOException("Retry limit exceeded");
+ }
+
+ private synchronized Exchange getExchange() {
+ return exchange;
+ }
+
+ private synchronized void setExchange(Exchange exchange) {
+ this.exchange = exchange;
+ }
+
+ private void cancelTimer() {
+ if (td != null) {
+ client.cancelTimer(td);
+ }
+ }
+
+ private void requestFilters(HttpRequestImpl r) throws IOException {
+ for (HeaderFilter filter : filters) {
+ filter.request(r);
+ }
+ }
+
+ // Filters are assumed to be non-blocking so the async
+ // versions of these methods just call the blocking ones
+
+ private CompletableFuture<Void> requestFiltersAsync(HttpRequestImpl r) {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ try {
+ requestFilters(r);
+ cf.complete(null);
+ } catch(Throwable e) {
+ cf.completeExceptionally(e);
+ }
+ return cf;
+ }
+
+
+ private Pair<HttpResponse,HttpRequestImpl>
+ responseFilters(HttpResponse response) throws IOException
+ {
+ for (HeaderFilter filter : filters) {
+ HttpRequestImpl newreq = filter.response((HttpResponseImpl)response);
+ if (newreq != null) {
+ return pair(null, newreq);
+ }
+ }
+ return pair(response, null);
+ }
+
+ private CompletableFuture<Pair<HttpResponse,HttpRequestImpl>>
+ responseFiltersAsync(HttpResponse response)
+ {
+ CompletableFuture<Pair<HttpResponse,HttpRequestImpl>> cf = new CompletableFuture<>();
+ try {
+ Pair<HttpResponse,HttpRequestImpl> n = responseFilters(response); // assumed to be fast
+ cf.complete(n);
+ } catch (Throwable e) {
+ cf.completeExceptionally(e);
+ }
+ return cf;
+ }
+
+ public void cancel() {
+ cancelled = true;
+ getExchange().cancel();
+ }
+
+ public CompletableFuture<HttpResponseImpl> responseAsync(Void v) {
+ CompletableFuture<HttpResponseImpl> cf;
+ if (++attempts > max_attempts) {
+ cf = new CompletableFuture<>();
+ cf.completeExceptionally(new IOException("Too many retries"));
+ } else {
+ if (currentreq.timeval() != 0) {
+ // set timer
+ td = new TimedEvent(currentreq.timeval());
+ client.registerTimer(td);
+ }
+ Exchange exch = getExchange();
+ cf = requestFiltersAsync(currentreq)
+ .thenCompose(exch::responseAsync)
+ .thenCompose(this::responseFiltersAsync)
+ .thenCompose((Pair<HttpResponse,HttpRequestImpl> pair) -> {
+ HttpResponseImpl resp = (HttpResponseImpl)pair.first;
+ if (resp != null) {
+ if (attempts > 1) {
+ Log.logError("Succeeded on attempt: " + attempts);
+ }
+ return CompletableFuture.completedFuture(resp);
+ } else {
+ currentreq = pair.second;
+ Exchange previous = exch;
+ setExchange(new Exchange(currentreq,
+ currentreq.getAccessControlContext()));
+ //reads body off previous, and then waits for next response
+ return previous
+ .responseBodyAsync(HttpResponse.ignoreBody())
+ .thenCompose(this::responseAsync);
+ }
+ })
+ .handle((BiFunction<HttpResponse, Throwable, Pair<HttpResponse, Throwable>>) Pair::new)
+ .thenCompose((Pair<HttpResponse,Throwable> obj) -> {
+ HttpResponseImpl response = (HttpResponseImpl)obj.first;
+ if (response != null) {
+ return CompletableFuture.completedFuture(response);
+ }
+ // all exceptions thrown are handled here
+ CompletableFuture<HttpResponseImpl> error = getExceptionalCF(obj.second);
+ if (error == null) {
+ cancelTimer();
+ return responseAsync(null);
+ } else {
+ return error;
+ }
+ });
+ }
+ return cf;
+ }
+
+ /**
+ * Take a Throwable and return a suitable CompletableFuture that is
+ * completed exceptionally.
+ */
+ private CompletableFuture<HttpResponseImpl> getExceptionalCF(Throwable t) {
+ CompletableFuture<HttpResponseImpl> error = new CompletableFuture<>();
+ if ((t instanceof CompletionException) || (t instanceof ExecutionException)) {
+ if (t.getCause() != null) {
+ t = t.getCause();
+ }
+ }
+ if (cancelled && t instanceof IOException) {
+ t = new HttpTimeoutException("request timed out");
+ }
+ error.completeExceptionally(t);
+ return error;
+ }
+
+ <T> T responseBody(HttpResponse.BodyProcessor<T> processor) {
+ return getExchange().responseBody(processor);
+ }
+
+ <T> CompletableFuture<T> responseBodyAsync(HttpResponse.BodyProcessor<T> processor) {
+ return getExchange().responseBodyAsync(processor);
+ }
+
+ class TimedEvent extends TimeoutEvent {
+ TimedEvent(long timeval) {
+ super(timeval);
+ }
+ @Override
+ public void handle() {
+ cancel();
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 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 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 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 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 java.net.http;
+
+/**
+ * A simple paired value class
+ */
+final class Pair<T, U> {
+
+ Pair(T first, U second) {
+ this.second = second;
+ this.first = first;
+ }
+
+ final T first;
+ final U second;
+
+ // Because 'pair()' is shorter than 'new Pair<>()'.
+ // Sometimes this difference might be very significant (especially in a
+ // 80-ish characters boundary). Sorry diamond operator.
+ static <T, U> Pair<T, U> pair(T first, U second) {
+ return new Pair<>(first, second);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.StandardSocketOptions;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Plain raw TCP connection direct to destination
+ */
+class PlainHttpConnection extends HttpConnection {
+
+ protected SocketChannel chan;
+ private volatile boolean connected;
+ private boolean closed;
+
+ class ConnectEvent extends AsyncEvent implements AsyncEvent.Blocking {
+ CompletableFuture<Void> cf;
+
+ ConnectEvent(CompletableFuture<Void> cf) {
+ this.cf = cf;
+ }
+
+ @Override
+ public SelectableChannel channel() {
+ return chan;
+ }
+
+ @Override
+ public int interestOps() {
+ return SelectionKey.OP_CONNECT;
+ }
+
+ @Override
+ public void handle() {
+ try {
+ chan.finishConnect();
+ } catch (IOException e) {
+ cf.completeExceptionally(e);
+ }
+ connected = true;
+ cf.complete(null);
+ }
+
+ @Override
+ public void abort() {
+ close();
+ }
+ }
+
+ @Override
+ public CompletableFuture<Void> connectAsync() {
+ CompletableFuture<Void> plainFuture = new CompletableFuture<>();
+ try {
+ chan.configureBlocking(false);
+ chan.connect(address);
+ client.registerEvent(new ConnectEvent(plainFuture));
+ } catch (IOException e) {
+ plainFuture.completeExceptionally(e);
+ }
+ return plainFuture;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ chan.connect(address);
+ connected = true;
+ }
+
+ @Override
+ SocketChannel channel() {
+ return chan;
+ }
+
+ PlainHttpConnection(InetSocketAddress addr, HttpClientImpl client) {
+ super(addr, client);
+ try {
+ this.chan = SocketChannel.open();
+ int bufsize = client.getReceiveBufferSize();
+ chan.setOption(StandardSocketOptions.SO_RCVBUF, bufsize);
+ } catch (IOException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ @Override
+ long write(ByteBuffer[] buffers, int start, int number) throws IOException {
+ //debugPrint("Send", buffers, start, number);
+ return chan.write(buffers, start, number);
+ }
+
+ @Override
+ long write(ByteBuffer buffer) throws IOException {
+ //debugPrint("Send", buffer);
+ return chan.write(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "PlainHttpConnection: " + super.toString();
+ }
+
+ /**
+ * Close this connection
+ */
+ @Override
+ synchronized void close() {
+ if (closed)
+ return;
+ closed = true;
+ try {
+ Log.logError("Closing: " + toString());
+ //System.out.println("Closing: " + this);
+ chan.close();
+ } catch (IOException e) {}
+ }
+
+ @Override
+ protected ByteBuffer readImpl(int length) throws IOException {
+ ByteBuffer buf = getBuffer(); // TODO not using length
+ int n = chan.read(buf);
+ if (n == -1) {
+ return null;
+ }
+ buf.flip();
+ String s = "Receive (" + n + " bytes) ";
+ //debugPrint(s, buf);
+ return buf;
+ }
+
+ @Override
+ protected int readImpl(ByteBuffer buf) throws IOException {
+ int mark = buf.position();
+ int n = chan.read(buf);
+ if (n == -1) {
+ return -1;
+ }
+ Utils.flipToMark(buffer, mark);
+ String s = "Receive (" + n + " bytes) ";
+ //debugPrint(s, buf);
+ return n;
+ }
+
+ @Override
+ ConnectionPool.CacheKey cacheKey() {
+ return new ConnectionPool.CacheKey(address, null);
+ }
+
+ @Override
+ synchronized boolean connected() {
+ return connected;
+ }
+
+ class ReceiveResponseEvent extends AsyncEvent implements AsyncEvent.Blocking {
+ CompletableFuture<Void> cf;
+
+ ReceiveResponseEvent(CompletableFuture<Void> cf) {
+ this.cf = cf;
+ }
+ @Override
+ public SelectableChannel channel() {
+ return chan;
+ }
+
+ @Override
+ public void handle() {
+ cf.complete(null);
+ }
+
+ @Override
+ public int interestOps() {
+ return SelectionKey.OP_READ;
+ }
+
+ @Override
+ public void abort() {
+ close();
+ }
+ }
+
+ @Override
+ boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ boolean isProxied() {
+ return false;
+ }
+
+ @Override
+ CompletableFuture<Void> whenReceivingResponse() {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ try {
+ client.registerEvent(new ReceiveResponseEvent(cf));
+ } catch (IOException e) {
+ cf.completeExceptionally(e);
+ }
+ return cf;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.net.InetSocketAddress;
+
+class PlainProxyConnection extends PlainHttpConnection {
+
+ PlainProxyConnection(InetSocketAddress proxy, HttpClientImpl client) {
+ super(proxy, client);
+ }
+
+ @Override
+ ConnectionPool.CacheKey cacheKey() {
+ return new ConnectionPool.CacheKey(null, address);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.security.AccessControlContext;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not
+ * encrypt. Used by WebSockets. Subclassed in SSLTunnelConnection for encryption.
+ */
+class PlainTunnelingConnection extends HttpConnection {
+
+ final PlainHttpConnection delegate;
+ protected final InetSocketAddress proxyAddr;
+ private volatile boolean connected;
+ private final AccessControlContext acc;
+
+ @Override
+ public CompletableFuture<Void> connectAsync() {
+ return delegate.connectAsync()
+ .thenCompose((Void v) -> {
+ HttpRequestImpl req = new HttpRequestImpl(client, "CONNECT", address);
+ Exchange connectExchange = new Exchange(req, acc);
+ return connectExchange
+ .responseAsyncImpl(delegate)
+ .thenCompose((HttpResponse r) -> {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ if (r.statusCode() != 200) {
+ cf.completeExceptionally(new IOException("Tunnel failed"));
+ } else {
+ connected = true;
+ cf.complete(null);
+ }
+ return cf;
+ });
+ });
+ }
+
+ @Override
+ public void connect() throws IOException, InterruptedException {
+ delegate.connect();
+ HttpRequestImpl req = new HttpRequestImpl(client, "CONNECT", address);
+ Exchange connectExchange = new Exchange(req, acc);
+ HttpResponse r = connectExchange.responseImpl(delegate);
+ if (r.statusCode() != 200) {
+ throw new IOException("Tunnel failed");
+ }
+ connected = true;
+ }
+
+ @Override
+ boolean connected() {
+ return connected;
+ }
+
+ protected PlainTunnelingConnection(InetSocketAddress addr,
+ InetSocketAddress proxy,
+ HttpClientImpl client,
+ AccessControlContext acc) {
+ super(addr, client);
+ this.proxyAddr = proxy;
+ this.acc = acc;
+ delegate = new PlainHttpConnection(proxy, client);
+ }
+
+ @Override
+ SocketChannel channel() {
+ return delegate.channel();
+ }
+
+ @Override
+ ConnectionPool.CacheKey cacheKey() {
+ return new ConnectionPool.CacheKey(null, proxyAddr);
+ }
+
+ @Override
+ long write(ByteBuffer[] buffers, int start, int number) throws IOException {
+ return delegate.write(buffers, start, number);
+ }
+
+ @Override
+ long write(ByteBuffer buffer) throws IOException {
+ return delegate.write(buffer);
+ }
+
+ @Override
+ void close() {
+ delegate.close();
+ connected = false;
+ }
+
+ @Override
+ protected ByteBuffer readImpl(int length) throws IOException {
+ return delegate.readImpl(length);
+ }
+
+ @Override
+ CompletableFuture<Void> whenReceivingResponse() {
+ return delegate.whenReceivingResponse();
+ }
+
+ @Override
+ protected int readImpl(ByteBuffer buffer) throws IOException {
+ return delegate.readImpl(buffer);
+ }
+
+ @Override
+ boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ boolean isProxied() {
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.SelectableChannel;
+
+/**
+ * Used to implement WebSocket. Each RawChannel corresponds to
+ * a TCP connection (SocketChannel) but is connected to a Selector
+ * and an ExecutorService for invoking the send and receive callbacks
+ * Also includes SSL processing.
+ */
+class RawChannel implements ByteChannel, GatheringByteChannel {
+
+ private final HttpClientImpl client;
+ private final HttpConnection connection;
+ private boolean closed;
+
+ private interface RawEvent {
+
+ /** must return the selector interest op flags OR'd. */
+ int interestOps();
+
+ /** called when event occurs. */
+ void handle();
+ }
+
+ interface BlockingEvent extends RawEvent { }
+
+ interface NonBlockingEvent extends RawEvent { }
+
+ RawChannel(HttpClientImpl client, HttpConnection connection) {
+ this.client = client;
+ this.connection = connection;
+ }
+
+ private class RawAsyncEvent extends AsyncEvent {
+
+ private final RawEvent re;
+
+ RawAsyncEvent(RawEvent re) {
+ this.re = re;
+ }
+
+ public SelectableChannel channel() {
+ return connection.channel();
+ }
+
+ // must return the selector interest op flags OR'd
+ public int interestOps() {
+ return re.interestOps();
+ }
+
+ // called when event occurs
+ public void handle() {
+ re.handle();
+ }
+
+ public void abort() {}
+ }
+
+ private class BlockingRawAsyncEvent extends RawAsyncEvent
+ implements AsyncEvent.Blocking {
+
+ BlockingRawAsyncEvent(RawEvent re) {
+ super(re);
+ }
+ }
+
+ private class NonBlockingRawAsyncEvent extends RawAsyncEvent
+ implements AsyncEvent.NonBlocking {
+
+ NonBlockingRawAsyncEvent(RawEvent re) {
+ super(re);
+ }
+ }
+
+ /*
+ * Register given event whose callback will be called once only.
+ * (i.e. register new event for each callback)
+ */
+ public void registerEvent(RawEvent event) throws IOException {
+ if (event instanceof BlockingEvent) {
+ client.registerEvent(new BlockingRawAsyncEvent(event));
+ } else if (event instanceof NonBlockingEvent) {
+ client.registerEvent(new NonBlockingRawAsyncEvent(event));
+ } else {
+ throw new InternalError();
+ }
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ return connection.read(dst);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ connection.close();
+ }
+
+ @Override
+ public long write(ByteBuffer[] src) throws IOException {
+ return connection.write(src, 0, src.length);
+ }
+
+ @Override
+ public long write(ByteBuffer[] src, int offset, int len)
+ throws IOException {
+ return connection.write(src, offset, len);
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ return (int) connection.write(src);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+
+class RedirectFilter implements HeaderFilter {
+
+ HttpRequestImpl requestImpl;
+ HttpRequest request;
+ HttpClientImpl client;
+ String method;
+ final static int DEFAULT_MAX_REDIRECTS = 5;
+ URI uri;
+
+ final static int max_redirects = Utils.getIntegerNetProperty(
+ "sun.net.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS
+ );
+
+ @Override
+ public void request(HttpRequestImpl r) throws IOException {
+ this.request = r;
+ this.client = r.getClient();
+ this.method = r.method();
+ this.requestImpl = r;
+ this.uri = r.uri();
+ }
+
+ @Override
+ public HttpRequestImpl response(HttpResponseImpl r) throws IOException {
+ return handleResponse(r);
+ }
+
+ /**
+ * checks to see if new request needed and returns it.
+ * Null means response is ok to return to user.
+ */
+ private HttpRequestImpl handleResponse(HttpResponseImpl r) {
+ int rcode = r.statusCode();
+ if (rcode == 200) {
+ return null;
+ }
+ if (rcode >= 300 && rcode <= 399) {
+ URI redir = getRedirectedURI(r.headers());
+ if (canRedirect(r) && ++r.request.exchange.numberOfRedirects < max_redirects) {
+ //System.out.println("Redirecting to: " + redir);
+ return new HttpRequestImpl(redir, request, client, method, requestImpl);
+ } else {
+ //System.out.println("Redirect: giving up");
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private URI getRedirectedURI(HttpHeaders headers) {
+ URI redirectedURI;
+ redirectedURI = headers.firstValue("Location")
+ .map((s) -> URI.create(s))
+ .orElseThrow(() -> new UncheckedIOException(
+ new IOException("Invalid redirection")));
+
+ // redirect could be relative to original URL, but if not
+ // then redirect is used.
+ redirectedURI = uri.resolve(redirectedURI);
+ return redirectedURI;
+ }
+
+ private boolean canRedirect(HttpResponse r) {
+ return requestImpl.followRedirectsImpl().redirect(r);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Implements chunked/fixed transfer encodings of HTTP/1.1 responses.
+ */
+class ResponseContent {
+
+ final HttpResponse.BodyProcessor<?> userProcessor;
+ final HttpResponse.BodyProcessor<?> pusher;
+ final HttpConnection connection;
+ final int contentLength;
+ ByteBuffer buffer;
+ ByteBuffer lastBufferUsed;
+ final ResponseHeaders headers;
+ final Http1Response.FlowController flowController;
+
+ ResponseContent(HttpConnection connection,
+ int contentLength,
+ ResponseHeaders h,
+ HttpResponse.BodyProcessor<?> userProcessor,
+ Http1Response.FlowController flowController) {
+ this.userProcessor = userProcessor;
+ this.pusher = (HttpResponse.BodyProcessor)userProcessor;
+ this.connection = connection;
+ this.contentLength = contentLength;
+ this.headers = h;
+ this.flowController = flowController;
+ }
+
+ static final int LF = 10;
+ static final int CR = 13;
+ static final int SP = 0x20;
+ static final int BUF_SIZE = 1024;
+
+ boolean chunkedContent, chunkedContentInitialized;
+
+ private boolean contentChunked() throws IOException {
+ if (chunkedContentInitialized) {
+ return chunkedContent;
+ }
+ if (contentLength == -1) {
+ String tc = headers.firstValue("Transfer-Encoding")
+ .orElse("");
+ if (!tc.equals("")) {
+ if (tc.equalsIgnoreCase("chunked")) {
+ chunkedContent = true;
+ } else {
+ throw new IOException("invalid content");
+ }
+ } else {
+ chunkedContent = false;
+ }
+ }
+ chunkedContentInitialized = true;
+ return chunkedContent;
+ }
+
+ /**
+ * Entry point for pusher. b is an initial ByteBuffer that may
+ * have some data in it. When this method returns, the body
+ * has been fully processed.
+ */
+ void pushBody(ByteBuffer b) throws IOException {
+ // TODO: check status
+ if (contentChunked()) {
+ pushBodyChunked(b);
+ } else {
+ pushBodyFixed(b);
+ }
+ }
+
+ // reads and returns chunklen. Position of chunkbuf is first byte
+ // of chunk on return. chunklen includes the CR LF at end of chunk
+ int readChunkLen() throws IOException {
+ chunklen = 0;
+ boolean cr = false;
+ while (true) {
+ getHunk();
+ int c = chunkbuf.get();
+ if (cr) {
+ if (c == LF) {
+ return chunklen + 2;
+ } else {
+ throw new IOException("invalid chunk header");
+ }
+ }
+ if (c == CR) {
+ cr = true;
+ } else {
+ int digit = toDigit(c);
+ chunklen = chunklen * 16 + digit;
+ }
+ }
+ }
+
+ int chunklen = -1; // number of bytes in chunk (fixed)
+ int bytesremaining; // number of bytes in chunk left to be read incl CRLF
+ int bytesread;
+ ByteBuffer chunkbuf; // initialise
+
+ // make sure we have at least 1 byte to look at
+ private void getHunk() throws IOException {
+ while (chunkbuf == null || !chunkbuf.hasRemaining()) {
+
+ if (chunkbuf != null) {
+ connection.returnBuffer(chunkbuf);
+ }
+ chunkbuf = connection.read();
+ }
+ }
+
+ private void consumeBytes(int n) throws IOException {
+ getHunk();
+ while (n > 0) {
+ int e = Math.min(chunkbuf.remaining(), n);
+ chunkbuf.position(chunkbuf.position() + e);
+ n -= e;
+ if (n > 0)
+ getHunk();
+ }
+ }
+
+ /**
+ * Returns a ByteBuffer containing a chunk of data or a "hunk" of data
+ * (a chunk of a chunk if the chunk size is larger than our ByteBuffers).
+ */
+ ByteBuffer readChunkedBuffer() throws IOException {
+ if (chunklen == -1) {
+ // new chunk
+ bytesremaining = readChunkLen();
+ chunklen = bytesremaining - 2;
+ if (chunklen == 0) {
+ consumeBytes(2);
+ return null;
+ }
+ }
+
+ getHunk();
+ bytesread = chunkbuf.remaining();
+ ByteBuffer returnBuffer;
+
+ /**
+ * Cases. Always at least one byte is read by getHunk()
+ *
+ * 1) one read contains exactly 1 chunk. Strip off CRLF and pass buffer on
+ * 2) one read contains a hunk. If at end of chunk, consume CRLF.Pass buffer up.
+ * 3) read contains rest of chunk and more data. Copy buffer.
+ */
+ if (bytesread == bytesremaining) {
+ // common case: 1 read = 1 chunk (or final hunk of chunk)
+ chunkbuf.limit(chunkbuf.limit() - 2); // remove trailing CRLF
+ bytesremaining = 0;
+ returnBuffer = chunkbuf;
+ chunkbuf = null;
+ chunklen = -1;
+ } else if (bytesread < bytesremaining) {
+ // read a hunk, maybe including CR or LF or both
+ bytesremaining -= bytesread;
+ if (bytesremaining <= 2) {
+ // remove any trailing CR LF already read, and then read the rest
+ chunkbuf.limit(chunkbuf.limit() - (2 - bytesremaining));
+ consumeBytes(bytesremaining);
+ chunklen = -1;
+ }
+ returnBuffer = chunkbuf;
+ chunkbuf = null;
+ } else {
+ // bytesread > bytesremaining
+ returnBuffer = splitChunkedBuffer(bytesremaining-2);
+ bytesremaining = 0;
+ chunklen = -1;
+ consumeBytes(2);
+ }
+ return returnBuffer;
+ }
+
+ ByteBuffer initialBuffer;
+ int fixedBytesReturned;
+
+ ByteBuffer getResidue() {
+ return lastBufferUsed;
+ }
+
+ private void compactBuffer(ByteBuffer buf) {
+ buf.compact()
+ .flip();
+ }
+
+ /**
+ * Copies inbuf (numBytes from its position) to new buffer. The returned
+ * buffer's position is zero and limit is at end (numBytes)
+ */
+ private ByteBuffer copyBuffer(ByteBuffer inbuf, int numBytes) {
+ ByteBuffer b1 = connection.getBuffer();
+ assert b1.remaining() >= numBytes;
+ byte[] b = b1.array();
+ inbuf.get(b, 0, numBytes);
+ b1.limit(numBytes);
+ return b1;
+ }
+
+ /**
+ * Split numBytes of data out of chunkbuf from the remainder,
+ * copying whichever part is smaller. chunkbuf points to second part
+ * of buffer on return. The returned buffer is the data from position
+ * to position + numBytes. Both buffers positions are reset so same
+ * data can be re-read.
+ */
+ private ByteBuffer splitChunkedBuffer(int numBytes) {
+ ByteBuffer newbuf = connection.getBuffer();
+ byte[] b = newbuf.array();
+ int midpoint = chunkbuf.position() + numBytes;
+ int remainder = chunkbuf.limit() - midpoint;
+
+ if (numBytes < remainder) {
+ // copy first part of chunkbuf to new buf
+ chunkbuf.get(b, 0, numBytes);
+ newbuf.limit(numBytes);
+ return newbuf;
+ } else {
+ // copy remainder of chunkbuf to newbuf and make newbuf chunkbuf
+ chunkbuf.mark();
+ chunkbuf.position(midpoint);
+ chunkbuf.get(b, 0, remainder);
+ chunkbuf.reset();
+ chunkbuf.limit(midpoint);
+ newbuf.limit(remainder);
+ newbuf.position(0);
+ ByteBuffer tmp = chunkbuf;
+ chunkbuf = newbuf;
+ return tmp;
+ }
+ }
+
+ private void pushBodyChunked(ByteBuffer b) throws IOException {
+ chunkbuf = b;
+ while (true) {
+ ByteBuffer b1 = readChunkedBuffer();
+ if (b1 != null) {
+ if (b1.hasRemaining()) {
+ request(1); // wait till we can send
+ pusher.onResponseBodyChunk(b1);
+ lastBufferUsed = b1;
+ }
+ } else {
+ return;
+ }
+ }
+ }
+
+ private int toDigit(int b) throws IOException {
+ if (b >= 0x30 && b <= 0x39) {
+ return b - 0x30;
+ }
+ if (b >= 0x41 && b <= 0x46) {
+ return b - 0x41 + 10;
+ }
+ if (b >= 0x61 && b <= 0x66) {
+ return b - 0x61 + 10;
+ }
+ throw new IOException("Invalid chunk header byte " + b);
+ }
+
+ private void request(long value) throws IOException {
+ try {
+ flowController.request(value);
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void pushBodyFixed(ByteBuffer b) throws IOException {
+ lastBufferUsed = b;
+ for (int remaining = contentLength; remaining > 0;) {
+ int bufsize = b.remaining();
+ if (bufsize > remaining) {
+ // more data available than required, must copy
+ lastBufferUsed = b;
+ b = copyBuffer(b, remaining);
+ remaining = 0;
+ } else {
+ // pass entire buffer up to user
+ remaining -= bufsize;
+ compactBuffer(b);
+ }
+ request(1); // wait till we can send
+ pusher.onResponseBodyChunk(b);
+ if (remaining > 0) {
+ b = connection.read();
+ if (b == null) {
+ throw new IOException("Error reading response");
+ }
+ lastBufferUsed = b;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Reads response headers off channel, in blocking mode. Entire header
+ * block is collected in a byte[]. The offset location of the start of
+ * each header name is recorded in an array to facilitate later searching.
+ *
+ * The location of "Content-length" is recorded explicitly. Similar approach
+ * could be taken for other common headers.
+ *
+ * This class is not thread-safe
+ */
+class ResponseHeaders implements HttpHeaders1 {
+
+ static final int DATA_SIZE = 16 * 1024; // initial space for headers
+ static final int NUM_HEADERS = 50; // initial expected max number of headers
+
+ final HttpConnection connection;
+ byte[] data;
+ int contentlen = -2; // means not initialized
+ ByteBuffer buffer;
+
+ /**
+ * Following used for scanning the array looking for:
+ * - well known headers
+ * - end of header block
+ */
+ int[] headerOffsets; // index into data
+ int numHeaders;
+ int count;
+
+ ByteBuffer residue; // after headers processed, data may be here
+
+ ResponseHeaders(HttpConnection connection, ByteBuffer buffer) {
+ this.connection = connection;
+ initOffsets();
+ this.buffer = buffer;
+ data = new byte[DATA_SIZE];
+ }
+
+ int getContentLength() throws IOException {
+ if (contentlen != -2) {
+ return contentlen;
+ }
+ int[] search = findHeaderValue("Content-length");
+ if (search[0] == -1) {
+ contentlen = -1;
+ return -1;
+ }
+
+ int i = search[0];
+
+ while (data[i] == ' ' || data[i] == '\t') {
+ i++;
+ if (i == data.length || data[i] == CR || data[i] == LF) {
+ throw new IOException("Bad header");
+ }
+ }
+ contentlen = 0;
+ int digit = data[i++] - 0x30;
+ while (digit >= 0 && digit <= 9) {
+ contentlen = contentlen * 10 + digit;
+ digit = data[i++] - 0x30;
+ }
+ return contentlen;
+ }
+
+ void log() {
+ populateMap(false);
+ }
+
+ void populateMap(boolean clearOffsets) {
+ StringBuilder sb;
+
+ for (int i = 0; i < numHeaders; i++) {
+ sb = new StringBuilder(32);
+ int offset = headerOffsets[i];
+ if (offset == -1) {
+ continue;
+ }
+ int j;
+ for (j=0; data[offset+j] != ':'; j++) {
+ // byte to char promotion ok for US-ASCII
+ sb.append((char)data[offset+j]);
+ }
+ String name = sb.toString();
+ List<String> l = getOrCreate(name);
+ addEntry(l, name, offset + j + 1);
+ // clear the offset
+ if (clearOffsets)
+ headerOffsets[i] = -1;
+ }
+ }
+
+ void addEntry(List<String> l, String name, int j) {
+
+ while (data[j] == ' ' || data[j] == '\t') {
+ j++;
+ }
+
+ int vstart = j;
+ // TODO: back slash ??
+
+ while (data[j] != CR) {
+ j++;
+ }
+ try {
+ String value = new String(data, vstart, j - vstart, "US-ASCII");
+ l.add(value);
+ } catch (UnsupportedEncodingException e) {
+ // can't happen
+ throw new InternalError(e);
+ }
+ }
+
+ // returns an int[2]: [0] = offset of value in data[]
+ // [1] = offset in headerOffsets. Both are -1 in error
+
+ private int[] findHeaderValue(String name) {
+ int[] result = new int[2];
+ byte[] namebytes = getBytes(name);
+
+ outer: for (int i = 0; i < numHeaders; i++) {
+ int offset = headerOffsets[i];
+ if (offset == -1) {
+ continue;
+ }
+
+ for (int j=0; j<namebytes.length; j++) {
+ if (namebytes[j] != lowerCase(data[offset+j])) {
+ continue outer;
+ }
+ }
+ // next char must be ':'
+ if (data[offset+namebytes.length] != ':') {
+ continue;
+ }
+ result[0] = offset+namebytes.length + 1;
+ result[1] = i;
+ return result;
+ }
+ result[0] = -1;
+ result[1] = -1;
+ return result;
+ }
+
+ /**
+ * Populates the map for header values with the given name.
+ * The offsets are cleared for any that are found, so they don't
+ * get repeatedly searched.
+ */
+ List<String> populateMapEntry(String name) {
+ List<String> l = getOrCreate(name);
+ int[] search = findHeaderValue(name);
+ if (search[0] != -1) {
+ addEntry(l, name, search[0]);
+ // clear the offset
+ headerOffsets[search[1]] = -1;
+ }
+ return l;
+ }
+
+ static final Locale usLocale = Locale.US;
+ static final Charset ascii = StandardCharsets.US_ASCII;
+
+ private byte[] getBytes(String name) {
+ return name.toLowerCase(usLocale).getBytes(ascii);
+ }
+
+ /*
+ * We read buffers in a loop until we detect end of headers
+ * CRLFCRLF. Each byte received is copied into the byte[] data
+ * The position of the first byte of each header (after a CRLF)
+ * is recorded in a separate array showing the location of
+ * each header name.
+ */
+ void initHeaders() throws IOException {
+
+ inHeaderName = true;
+ endOfHeader = true;
+
+ for (int numBuffers = 0; true; numBuffers++) {
+
+ if (numBuffers > 0) {
+ buffer = connection.read();
+ }
+
+ if (buffer == null) {
+ throw new IOException("Error reading headers");
+ }
+
+ if (!buffer.hasRemaining()) {
+ continue;
+ }
+
+ // Position set to first byte
+ int start = buffer.position();
+ byte[] backing = buffer.array();
+ int len = buffer.limit() - start;
+
+ for (int i = 0; i < len; i++) {
+ byte b = backing[i + start];
+ if (inHeaderName) {
+ b = lowerCase(b);
+ }
+ if (b == ':') {
+ inHeaderName = false;
+ }
+ data[count++] = b;
+ checkByte(b);
+ if (firstChar) {
+ recordHeaderOffset(count-1);
+ firstChar = false;
+ }
+ if (endOfHeader && numHeaders == 0) {
+ // empty headers
+ endOfAllHeaders = true;
+ }
+ if (endOfAllHeaders) {
+ int newposition = i + 1 + start;
+ if (newposition <= buffer.limit()) {
+ buffer.position(newposition);
+ residue = buffer;
+ } else {
+ residue = null;
+ }
+ return;
+ }
+
+ if (count == data.length) {
+ resizeData();
+ }
+ }
+ }
+ }
+
+ static final int CR = 13;
+ static final int LF = 10;
+ int crlfCount = 0;
+
+ // results of checkByte()
+ boolean endOfHeader; // just seen LF after CR before
+ boolean endOfAllHeaders; // just seen LF after CRLFCR before
+ boolean firstChar; //
+ boolean inHeaderName; // examining header name
+
+ void checkByte(byte b) throws IOException {
+ if (endOfHeader && b != CR && b != LF)
+ firstChar = true;
+ endOfHeader = false;
+ endOfAllHeaders = false;
+ switch (crlfCount) {
+ case 0:
+ crlfCount = b == CR ? 1 : 0;
+ break;
+ case 1:
+ crlfCount = b == LF ? 2 : 0;
+ endOfHeader = true;
+ inHeaderName = true;
+ break;
+ case 2:
+ crlfCount = b == CR ? 3 : 0;
+ break;
+ case 3:
+ if (b != LF) {
+ throw new IOException("Bad header block termination");
+ }
+ endOfAllHeaders = true;
+ break;
+ }
+ }
+
+ byte lowerCase(byte b) {
+ if (b >= 0x41 && b <= 0x5A)
+ b = (byte)(b + 32);
+ return b;
+ }
+
+ void resizeData() {
+ int oldlen = data.length;
+ int newlen = oldlen * 2;
+ byte[] newdata = new byte[newlen];
+ System.arraycopy(data, 0, newdata, 0, oldlen);
+ data = newdata;
+ }
+
+ final void initOffsets() {
+ headerOffsets = new int[NUM_HEADERS];
+ numHeaders = 0;
+ }
+
+ ByteBuffer getResidue() {
+ return residue;
+ }
+
+ void recordHeaderOffset(int index) {
+ if (numHeaders >= headerOffsets.length) {
+ int oldlen = headerOffsets.length;
+ int newlen = oldlen * 2;
+ int[] new1 = new int[newlen];
+ System.arraycopy(headerOffsets, 0, new1, 0, oldlen);
+ headerOffsets = new1;
+ }
+ headerOffsets[numHeaders++] = index;
+ }
+
+ /**
+ * As entries are read from the byte[] they are placed in here
+ * So we always check this map first
+ */
+ Map<String,List<String>> headers = new HashMap<>();
+
+ @Override
+ public Optional<String> firstValue(String name) {
+ List<String> l = allValues(name);
+ if (l == null || l.isEmpty()) {
+ return Optional.ofNullable(null);
+ } else {
+ return Optional.of(l.get(0));
+ }
+ }
+
+ @Override
+ public List<String> allValues(String name) {
+ name = name.toLowerCase(usLocale);
+ List<String> l = headers.get(name);
+ if (l == null) {
+ l = populateMapEntry(name);
+ }
+ return Collections.unmodifiableList(l);
+ }
+
+ @Override
+ public void makeUnmodifiable() {
+ }
+
+ // Delegates map to HashMap but converts keys to lower case
+
+ static class HeaderMap implements Map<String,List<String>> {
+ Map<String,List<String>> inner;
+
+ HeaderMap(Map<String,List<String>> inner) {
+ this.inner = inner;
+ }
+ @Override
+ public int size() {
+ return inner.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return inner.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ if (!(key instanceof String)) {
+ return false;
+ }
+ String s = ((String)key).toLowerCase(usLocale);
+ return inner.containsKey(s);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return inner.containsValue(value);
+ }
+
+ @Override
+ public List<String> get(Object key) {
+ String s = ((String)key).toLowerCase(usLocale);
+ return inner.get(s);
+ }
+
+ @Override
+ public List<String> put(String key, List<String> value) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public List<String> remove(Object key) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ? extends List<String>> m) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return inner.keySet();
+ }
+
+ @Override
+ public Collection<List<String>> values() {
+ return inner.values();
+ }
+
+ @Override
+ public Set<Entry<String, List<String>>> entrySet() {
+ return inner.entrySet();
+ }
+ }
+
+ @Override
+ public Map<String, List<String>> map() {
+ populateMap(true);
+ return new HeaderMap(headers);
+ }
+
+ Map<String, List<String>> mapInternal() {
+ populateMap(false);
+ return new HeaderMap(headers);
+ }
+
+ private List<String> getOrCreate(String name) {
+ List<String> l = headers.get(name);
+ if (l == null) {
+ l = new LinkedList<>();
+ headers.put(name, l);
+ }
+ return l;
+ }
+
+ @Override
+ public Optional<Long> firstValueAsLong(String name) {
+ List<String> l = allValues(name);
+ if (l == null) {
+ return Optional.ofNullable(null);
+ } else {
+ String v = l.get(0);
+ Long lv = Long.parseLong(v);
+ return Optional.of(lv);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.CompletableFuture;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLParameters;
+import java.net.http.SSLDelegate.BufType;
+import java.net.http.SSLDelegate.WrapperResult;
+
+/**
+ * An SSL connection built on a Plain TCP connection.
+ */
+class SSLConnection extends HttpConnection {
+
+ PlainHttpConnection delegate;
+ SSLDelegate sslDelegate;
+ final String[] alpn;
+
+ @Override
+ public CompletableFuture<Void> connectAsync() {
+ return delegate.connectAsync()
+ .thenCompose((Void v) -> {
+ CompletableFuture<Void> cf = new CompletableFuture<>();
+ try {
+ this.sslDelegate = new SSLDelegate(delegate.channel(),
+ client,
+ alpn);
+ cf.complete(null);
+ } catch (IOException e) {
+ cf.completeExceptionally(e);
+ }
+ return cf;
+ });
+ }
+
+ @Override
+ public void connect() throws IOException {
+ delegate.connect();
+ this.sslDelegate = new SSLDelegate(delegate.channel(), client, alpn);
+ }
+
+ SSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) {
+ super(addr, client);
+ this.alpn = ap;
+ delegate = new PlainHttpConnection(addr, client);
+ }
+
+ @Override
+ SSLParameters sslParameters() {
+ return sslDelegate.getSSLParameters();
+ }
+
+ @Override
+ public String toString() {
+ return "SSLConnection: " + super.toString();
+ }
+
+ private static long countBytes(ByteBuffer[] buffers, int start, int length) {
+ long c = 0;
+ for (int i=0; i<length; i++) {
+ c+= buffers[start+i].remaining();
+ }
+ return c;
+ }
+
+ @Override
+ ConnectionPool.CacheKey cacheKey() {
+ return ConnectionPool.cacheKey(address, null);
+ }
+
+ @Override
+ long write(ByteBuffer[] buffers, int start, int number) throws IOException {
+ //debugPrint("Send", buffers, start, number);
+ long l = countBytes(buffers, start, number);
+ WrapperResult r = sslDelegate.sendData(buffers, start, number);
+ if (r.result.getStatus() == Status.CLOSED) {
+ if (l > 0) {
+ throw new IOException("SSLHttpConnection closed");
+ }
+ }
+ return l;
+ }
+
+ @Override
+ long write(ByteBuffer buffer) throws IOException {
+ //debugPrint("Send", buffer);
+ long l = buffer.remaining();
+ WrapperResult r = sslDelegate.sendData(buffer);
+ if (r.result.getStatus() == Status.CLOSED) {
+ if (l > 0) {
+ throw new IOException("SSLHttpConnection closed");
+ }
+ }
+ return l;
+ }
+
+ @Override
+ void close() {
+ try {
+ //System.err.println ("Closing: " + this);
+ delegate.channel().close(); // TODO: proper close
+ } catch (IOException ex) {
+ Log.logError(ex.toString());
+ }
+ }
+
+ @Override
+ protected ByteBuffer readImpl(int length) throws IOException {
+ ByteBuffer buf = sslDelegate.allocate(BufType.PACKET, length);
+ WrapperResult r = sslDelegate.recvData(buf);
+ // TODO: check for closure
+ String s = "Receive) ";
+ //debugPrint(s, r.buf);
+ return r.buf;
+ }
+
+ @Override
+ protected int readImpl(ByteBuffer buf) throws IOException {
+ // TODO: need to ensure that buf is big enough for application data
+ WrapperResult r = sslDelegate.recvData(buf);
+ // TODO: check for closure
+ String s = "Receive) ";
+ //debugPrint(s, r.buf);
+ return r.result.bytesProduced();
+ }
+
+ @Override
+ boolean connected() {
+ return delegate.connected();
+ }
+
+ @Override
+ SocketChannel channel() {
+ return delegate.channel();
+ }
+
+ @Override
+ CompletableFuture<Void> whenReceivingResponse() {
+ return delegate.whenReceivingResponse();
+ }
+
+ @Override
+ boolean isSecure() {
+ return true;
+ }
+
+ @Override
+ boolean isProxied() {
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
+
+/**
+ * Implements the mechanics of SSL by managing an SSLEngine object.
+ * One of these is associated with each SSLConnection.
+ */
+class SSLDelegate {
+
+ final SSLEngine engine;
+ final EngineWrapper wrapper;
+ final Lock handshaking = new ReentrantLock();
+ final SSLParameters sslParameters;
+ final SocketChannel chan;
+ final HttpClientImpl client;
+
+ // alpn[] may be null
+ SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn)
+ throws IOException
+ {
+ SSLContext context = client.sslContext();
+ engine = context.createSSLEngine();
+ engine.setUseClientMode(true);
+ SSLParameters sslp = client.sslParameters().orElse(null);
+ if (sslp == null) {
+ sslp = context.getDefaultSSLParameters();
+ }
+ sslParameters = Utils.copySSLParameters(sslp);
+ if (alpn != null) {
+ sslParameters.setApplicationProtocols(alpn);
+ Log.logSSL("Setting application protocols: " + Arrays.toString(alpn));
+ } else {
+ Log.logSSL("Warning no application protocols proposed!");
+ }
+ engine.setSSLParameters(sslParameters);
+ wrapper = new EngineWrapper(chan, engine);
+ this.chan = chan;
+ this.client = client;
+ }
+
+ SSLParameters getSSLParameters() {
+ return sslParameters;
+ }
+
+ private static long countBytes(ByteBuffer[] buffers, int start, int number) {
+ long c = 0;
+ for (int i=0; i<number; i++) {
+ c+= buffers[start+i].remaining();
+ }
+ return c;
+ }
+
+
+ static class WrapperResult {
+ static WrapperResult createOK() {
+ WrapperResult r = new WrapperResult();
+ r.buf = null;
+ r.result = new SSLEngineResult(Status.OK, NOT_HANDSHAKING, 0, 0);
+ return r;
+ }
+ SSLEngineResult result;
+
+ /* if passed in buffer was not big enough then the a reallocated buffer
+ * is returned here */
+ ByteBuffer buf;
+ }
+
+ int app_buf_size;
+ int packet_buf_size;
+
+ enum BufType {
+ PACKET,
+ APPLICATION
+ };
+
+ ByteBuffer allocate (BufType type) {
+ return allocate (type, -1);
+ }
+
+ // TODO: Use buffer pool for this
+ ByteBuffer allocate (BufType type, int len) {
+ assert engine != null;
+ synchronized (this) {
+ int size;
+ if (type == BufType.PACKET) {
+ if (packet_buf_size == 0) {
+ SSLSession sess = engine.getSession();
+ packet_buf_size = sess.getPacketBufferSize();
+ }
+ if (len > packet_buf_size) {
+ packet_buf_size = len;
+ }
+ size = packet_buf_size;
+ } else {
+ if (app_buf_size == 0) {
+ SSLSession sess = engine.getSession();
+ app_buf_size = sess.getApplicationBufferSize();
+ }
+ if (len > app_buf_size) {
+ app_buf_size = len;
+ }
+ size = app_buf_size;
+ }
+ return ByteBuffer.allocate (size);
+ }
+ }
+
+ /* reallocates the buffer by :-
+ * 1. creating a new buffer double the size of the old one
+ * 2. putting the contents of the old buffer into the new one
+ * 3. set xx_buf_size to the new size if it was smaller than new size
+ *
+ * flip is set to true if the old buffer needs to be flipped
+ * before it is copied.
+ */
+ private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
+ synchronized (this) {
+ int nsize = 2 * b.capacity();
+ ByteBuffer n = allocate (type, nsize);
+ if (flip) {
+ b.flip();
+ }
+ n.put(b);
+ b = n;
+ }
+ return b;
+ }
+
+ /**
+ * This is a thin wrapper over SSLEngine and the SocketChannel, which
+ * guarantees the ordering of wraps/unwraps with respect to the underlying
+ * channel read/writes. It handles the UNDER/OVERFLOW status codes
+ * It does not handle the handshaking status codes, or the CLOSED status code
+ * though once the engine is closed, any attempt to read/write to it
+ * will get an exception. The overall result is returned.
+ * It functions synchronously/blocking
+ */
+ class EngineWrapper {
+
+ SocketChannel chan;
+ SSLEngine engine;
+ Object wrapLock, unwrapLock;
+ ByteBuffer unwrap_src, wrap_dst;
+ boolean closed = false;
+ int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
+
+ EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {
+ this.chan = chan;
+ this.engine = engine;
+ wrapLock = new Object();
+ unwrapLock = new Object();
+ unwrap_src = allocate(BufType.PACKET);
+ wrap_dst = allocate(BufType.PACKET);
+ }
+
+ void close () throws IOException {
+ }
+
+ WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose)
+ throws IOException
+ {
+ ByteBuffer[] buffers = new ByteBuffer[1];
+ buffers[0] = src;
+ return wrapAndSend(buffers, 0, 1, ignoreClose);
+ }
+
+ /* try to wrap and send the data in src. Handles OVERFLOW.
+ * Might block if there is an outbound blockage or if another
+ * thread is calling wrap(). Also, might not send any data
+ * if an unwrap is needed.
+ */
+ WrapperResult wrapAndSend(ByteBuffer[] src,
+ int offset,
+ int len,
+ boolean ignoreClose)
+ throws IOException
+ {
+ if (closed && !ignoreClose) {
+ throw new IOException ("Engine is closed");
+ }
+ Status status;
+ WrapperResult r = new WrapperResult();
+ synchronized (wrapLock) {
+ wrap_dst.clear();
+ do {
+ r.result = engine.wrap (src, offset, len, wrap_dst);
+ status = r.result.getStatus();
+ if (status == Status.BUFFER_OVERFLOW) {
+ wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
+ }
+ } while (status == Status.BUFFER_OVERFLOW);
+ if (status == Status.CLOSED && !ignoreClose) {
+ closed = true;
+ return r;
+ }
+ if (r.result.bytesProduced() > 0) {
+ wrap_dst.flip();
+ int l = wrap_dst.remaining();
+ assert l == r.result.bytesProduced();
+ while (l>0) {
+ l -= chan.write (wrap_dst);
+ }
+ }
+ }
+ return r;
+ }
+
+ /* block until a complete message is available and return it
+ * in dst, together with the Result. dst may have been re-allocated
+ * so caller should check the returned value in Result
+ * If handshaking is in progress then, possibly no data is returned
+ */
+ WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
+ Status status;
+ WrapperResult r = new WrapperResult();
+ r.buf = dst;
+ if (closed) {
+ throw new IOException ("Engine is closed");
+ }
+ boolean needData;
+ if (u_remaining > 0) {
+ unwrap_src.compact();
+ unwrap_src.flip();
+ needData = false;
+ } else {
+ unwrap_src.clear();
+ needData = true;
+ }
+ synchronized (unwrapLock) {
+ int x;
+ do {
+ if (needData) {
+ do {
+ x = chan.read (unwrap_src);
+ } while (x == 0);
+ if (x == -1) {
+ throw new IOException ("connection closed for reading");
+ }
+ unwrap_src.flip();
+ }
+ r.result = engine.unwrap (unwrap_src, r.buf);
+ status = r.result.getStatus();
+ if (status == Status.BUFFER_UNDERFLOW) {
+ if (unwrap_src.limit() == unwrap_src.capacity()) {
+ /* buffer not big enough */
+ unwrap_src = realloc (
+ unwrap_src, false, BufType.PACKET
+ );
+ } else {
+ /* Buffer not full, just need to read more
+ * data off the channel. Reset pointers
+ * for reading off SocketChannel
+ */
+ unwrap_src.position (unwrap_src.limit());
+ unwrap_src.limit (unwrap_src.capacity());
+ }
+ needData = true;
+ } else if (status == Status.BUFFER_OVERFLOW) {
+ r.buf = realloc (r.buf, true, BufType.APPLICATION);
+ needData = false;
+ } else if (status == Status.CLOSED) {
+ closed = true;
+ r.buf.flip();
+ return r;
+ }
+ } while (status != Status.OK);
+ }
+ u_remaining = unwrap_src.remaining();
+ return r;
+ }
+ }
+
+ WrapperResult sendData (ByteBuffer src) throws IOException {
+ ByteBuffer[] buffers = new ByteBuffer[1];
+ buffers[0] = src;
+ return sendData(buffers, 0, 1);
+ }
+
+ /**
+ * send the data in the given ByteBuffer. If a handshake is needed
+ * then this is handled within this method. When this call returns,
+ * all of the given user data has been sent and any handshake has been
+ * completed. Caller should check if engine has been closed.
+ */
+ WrapperResult sendData (ByteBuffer[] src, int offset, int len) throws IOException {
+ WrapperResult r = WrapperResult.createOK();
+ while (countBytes(src, offset, len) > 0) {
+ r = wrapper.wrapAndSend(src, offset, len, false);
+ Status status = r.result.getStatus();
+ if (status == Status.CLOSED) {
+ doClosure ();
+ return r;
+ }
+ HandshakeStatus hs_status = r.result.getHandshakeStatus();
+ if (hs_status != HandshakeStatus.FINISHED &&
+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
+ {
+ doHandshake(hs_status);
+ }
+ }
+ return r;
+ }
+
+ /**
+ * read data thru the engine into the given ByteBuffer. If the
+ * given buffer was not large enough, a new one is allocated
+ * and returned. This call handles handshaking automatically.
+ * Caller should check if engine has been closed.
+ */
+ WrapperResult recvData (ByteBuffer dst) throws IOException {
+ /* we wait until some user data arrives */
+ int mark = dst.position();
+ WrapperResult r = null;
+ assert dst.position() == 0;
+ while (dst.position() == 0) {
+ r = wrapper.recvAndUnwrap (dst);
+ dst = (r.buf != dst) ? r.buf: dst;
+ Status status = r.result.getStatus();
+ if (status == Status.CLOSED) {
+ doClosure ();
+ return r;
+ }
+
+ HandshakeStatus hs_status = r.result.getHandshakeStatus();
+ if (hs_status != HandshakeStatus.FINISHED &&
+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
+ {
+ doHandshake (hs_status);
+ }
+ }
+ Utils.flipToMark(dst, mark);
+ return r;
+ }
+
+ /* we've received a close notify. Need to call wrap to send
+ * the response
+ */
+ void doClosure () throws IOException {
+ try {
+ handshaking.lock();
+ ByteBuffer tmp = allocate(BufType.APPLICATION);
+ WrapperResult r;
+ do {
+ tmp.clear();
+ tmp.flip ();
+ r = wrapper.wrapAndSend(tmp, true);
+ } while (r.result.getStatus() != Status.CLOSED);
+ } finally {
+ handshaking.unlock();
+ }
+ }
+
+ /* do the (complete) handshake after acquiring the handshake lock.
+ * If two threads call this at the same time, then we depend
+ * on the wrapper methods being idempotent. eg. if wrapAndSend()
+ * is called with no data to send then there must be no problem
+ */
+ @SuppressWarnings("fallthrough")
+ void doHandshake (HandshakeStatus hs_status) throws IOException {
+ boolean wasBlocking = false;
+ try {
+ wasBlocking = chan.isBlocking();
+ handshaking.lock();
+ chan.configureBlocking(true);
+ ByteBuffer tmp = allocate(BufType.APPLICATION);
+ while (hs_status != HandshakeStatus.FINISHED &&
+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
+ {
+ WrapperResult r = null;
+ switch (hs_status) {
+ case NEED_TASK:
+ Runnable task;
+ while ((task = engine.getDelegatedTask()) != null) {
+ /* run in current thread, because we are already
+ * running an external Executor
+ */
+ task.run();
+ }
+ /* fall thru - call wrap again */
+ case NEED_WRAP:
+ tmp.clear();
+ tmp.flip();
+ r = wrapper.wrapAndSend(tmp, false);
+ break;
+
+ case NEED_UNWRAP:
+ tmp.clear();
+ r = wrapper.recvAndUnwrap (tmp);
+ if (r.buf != tmp) {
+ tmp = r.buf;
+ }
+ assert tmp.position() == 0;
+ break;
+ }
+ hs_status = r.result.getHandshakeStatus();
+ }
+ Log.logSSL(getSessionInfo());
+ if (!wasBlocking) {
+ chan.configureBlocking(false);
+ }
+ } finally {
+ handshaking.unlock();
+ }
+ }
+
+ String getSessionInfo() {
+ StringBuilder sb = new StringBuilder();
+ String application = engine.getApplicationProtocol();
+ SSLSession sess = engine.getSession();
+ String cipher = sess.getCipherSuite();
+ String protocol = sess.getProtocol();
+ sb.append("Handshake complete alpn: ")
+ .append(application)
+ .append(", Cipher: ")
+ .append(cipher)
+ .append(", Protocol: ")
+ .append(protocol);
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+package java.net.http;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.security.AccessControlContext;
+import java.util.concurrent.CompletableFuture;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLParameters;
+import java.net.http.SSLDelegate.BufType;
+import java.net.http.SSLDelegate.WrapperResult;
+
+/**
+ * An SSL tunnel built on a Plain (CONNECT) TCP tunnel.
+ */
+class SSLTunnelConnection extends HttpConnection {
+
+ final PlainTunnelingConnection delegate;
+ protected SSLDelegate sslDelegate;
+ private volatile boolean connected;
+
+ @Override
+ public void connect() throws IOException, InterruptedException {
+ delegate.connect();
+ this.sslDelegate = new SSLDelegate(delegate.channel(), client, null);
+ connected = true;
+ }
+
+ @Override
+ boolean connected() {
+ return connected && delegate.connected();
+ }
+
+ @Override
+ public CompletableFuture<Void> connectAsync() {
+ return delegate.connectAsync()
+ .thenAccept((Void v) -> {
+ try {
+ // can this block?
+ this.sslDelegate = new SSLDelegate(delegate.channel(),
+ client,
+ null);
+ connected = true;
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ }
+
+ SSLTunnelConnection(InetSocketAddress addr,
+ HttpClientImpl client,
+ InetSocketAddress proxy,
+ AccessControlContext acc) {
+ super(addr, client);
+ delegate = new PlainTunnelingConnection(addr, proxy, client, acc);
+ }
+
+ @Override
+ SSLParameters sslParameters() {
+ return sslDelegate.getSSLParameters();
+ }
+
+ @Override
+ public String toString() {
+ return "SSLTunnelConnection: " + super.toString();
+ }
+
+ private static long countBytes(ByteBuffer[] buffers, int start, int number) {
+ long c = 0;
+ for (int i=0; i<number; i++) {
+ c+= buffers[start+i].remaining();
+ }
+ return c;
+ }
+
+ @Override
+ ConnectionPool.CacheKey cacheKey() {
+ return ConnectionPool.cacheKey(address, delegate.proxyAddr);
+ }
+
+ @Override
+ long write(ByteBuffer[] buffers, int start, int number) throws IOException {
+ //debugPrint("Send", buffers, start, number);
+ long l = countBytes(buffers, start, number);
+ WrapperResult r = sslDelegate.sendData(buffers, start, number);
+ if (r.result.getStatus() == Status.CLOSED) {
+ if (l > 0) {
+ throw new IOException("SSLHttpConnection closed");
+ }
+ }
+ return l;
+ }
+
+ @Override
+ long write(ByteBuffer buffer) throws IOException {
+ //debugPrint("Send", buffer);
+ long l = buffer.remaining();
+ WrapperResult r = sslDelegate.sendData(buffer);
+ if (r.result.getStatus() == Status.CLOSED) {
+ if (l > 0) {
+ throw new IOException("SSLHttpConnection closed");
+ }
+ }
+ return l;
+ }
+
+ @Override
+ void close() {
+ try {
+ //System.err.println ("Closing: " + this);
+ delegate.channel().close(); // TODO: proper close
+ } catch (IOException ex) {
+ }
+ }
+
+ @Override
+ protected ByteBuffer readImpl(int length) throws IOException {
+ ByteBuffer buf = sslDelegate.allocate(BufType.PACKET, length);
+ WrapperResult r = sslDelegate.recvData(buf);
+ // TODO: check for closure
+ String s = "Receive) ";
+ //debugPrint(s, r.buf);
+ return r.buf;
+ }
+
+ @Override
+ protected int readImpl(ByteBuffer buf) throws IOException {
+ WrapperResult r = sslDelegate.recvData(buf);
+ // TODO: check for closure
+ String s = "Receive) ";
+ //debugPrint(s, r.buf);
+ return r.result.bytesProduced();
+ }
+
+ @Override
+ SocketChannel channel() {
+ return delegate.channel();
+ }
+
+ @Override
+ CompletableFuture<Void> whenReceivingResponse() {
+ return delegate.whenReceivingResponse();
+ }
+
+ @Override
+ boolean isSecure() {
+ return true;
+ }
+
+ @Override
+ boolean isProxied() {
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, 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
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.function.LongConsumer;
+
+/**
+ * Http/2 Stream
+ */
+class Stream extends ExchangeImpl {
+
+ void debugPrint() {
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ <T> CompletableFuture<T> responseBodyAsync(HttpResponse.BodyProcessor<T> processor) {
+ return null;
+ }
+
+ Stream(HttpClientImpl client, Http2Connection connection, Exchange e) {
+ super(e);
+ }
+
+ @Override
+ HttpResponseImpl getResponse() throws IOException {
+ return null;
+ }
+
+ @Override
+ void sendRequest() throws IOException, InterruptedException {
+ }
+
+ @Override
+ void sendHeadersOnly() throws IOException, InterruptedException {
+ }
+
+ @Override
+ void sendBody() throws IOException, InterruptedException {
+ }
+
+ @Override
+ CompletableFuture<Void> sendHeadersAsync() {
+ return null;
+ }
+
+ @Override
+ CompletableFuture<HttpResponseImpl> getResponseAsync(Void v) {
+ return null;
+ }
+
+ @Override
+ CompletableFuture<Void> sendBodyAsync() {
+ return null;
+ }
+
+ @Override
+ void cancel() {
+ }
+
+
+ @Override
+ CompletableFuture<Void> sendRequestAsync() {
+ return null;
+ }
+
+ @Override
+ <T> T responseBody(HttpResponse.BodyProcessor<T> processor) throws IOException {
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+/**
+ * Timeout event delivered by selector thread. Executes given handler
+ * if the timer not cancelled first.
+ *
+ * Register with HttpClientImpl.registerTimer(TimeoutEvent)
+ *
+ * Cancel with HttpClientImpl.cancelTimer(TimeoutEvent)
+ */
+abstract class TimeoutEvent {
+
+ final long timeval;
+
+ long delta; // used when on queue
+
+ TimeoutEvent(long timeval) { this.timeval = timeval; }
+
+ public abstract void handle();
+
+ /** Returns the timevalue in milli-seconds */
+ public long timevalMillis() { return timeval; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+package java.net.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.net.NetPermission;
+import java.net.URI;
+import java.net.URLPermission;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.net.ssl.SSLParameters;
+import sun.net.NetProperties;
+
+/**
+ * Miscellaneous utilities
+ */
+class Utils {
+
+ /**
+ * Allocated buffer size. Must never be higher than 16K. But can be lower
+ * if smaller allocation units preferred. HTTP/2 mandates that all
+ * implementations support frame payloads of at least 16K.
+ */
+ public static final int BUFSIZE = 16 * 1024;
+
+ /** Validates a RFC7230 token */
+ static void validateToken(String token, String errormsg) {
+ int length = token.length();
+ for (int i = 0; i < length; i++) {
+ int c = token.codePointAt(i);
+ if (c >= 0x30 && c <= 0x39 // 0 - 9
+ || (c >= 0x61 && c <= 0x7a) // a - z
+ || (c >= 0x41 && c <= 0x5a) // A - Z
+ || (c >= 0x21 && c <= 0x2e && c != 0x22 && c != 0x27 && c != 0x2c)
+ || (c >= 0x5e && c <= 0x60)
+ || (c == 0x7c) || (c == 0x7e)) {
+ } else {
+ throw new IllegalArgumentException(errormsg);
+ }
+ }
+ }
+
+ /**
+ * Return sthe security permission required for the given details.
+ * If method is CONNECT, then uri must be of form "scheme://host:port"
+ */
+ static URLPermission getPermission(URI uri,
+ String method,
+ Map<String, List<String>> headers) {
+ StringBuilder sb = new StringBuilder();
+
+ String urlstring, actionstring;
+
+ if (method.equals("CONNECT")) {
+ urlstring = uri.toString();
+ actionstring = "CONNECT";
+ } else {
+ sb.append(uri.getScheme())
+ .append("://")
+ .append(uri.getHost())
+ .append(uri.getPath());
+ urlstring = sb.toString();
+
+ sb = new StringBuilder();
+ sb.append(method);
+ if (headers != null && !headers.isEmpty()) {
+ sb.append(':');
+ Set<String> keys = headers.keySet();
+ boolean first = true;
+ for (String key : keys) {
+ if (!first) {
+ sb.append(',');
+ }
+ sb.append(key);
+ first = false;
+ }
+ }
+ actionstring = sb.toString();
+ }
+ return new URLPermission(urlstring, actionstring);
+ }
+
+ static void checkNetPermission(String target) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ return;
+ NetPermission np = new NetPermission(target);
+ sm.checkPermission(np);
+ }
+
+ static int getIntegerNetProperty(String name, int defaultValue) {
+ return AccessController.doPrivileged((PrivilegedAction<Integer>)() ->
+ NetProperties.getInteger(name, defaultValue) );
+ }
+
+ static String getNetProperty(String name) {
+ return AccessController.doPrivileged((PrivilegedAction<String>)() ->
+ NetProperties.get(name) );
+ }
+
+ static SSLParameters copySSLParameters(SSLParameters p) {
+ SSLParameters p1 = new SSLParameters();
+ p1.setAlgorithmConstraints(p.getAlgorithmConstraints());
+ p1.setCipherSuites(p.getCipherSuites());
+ p1.setEnableRetransmissions(p.getEnableRetransmissions());
+ p1.setEndpointIdentificationAlgorithm(p.getEndpointIdentificationAlgorithm());
+ p1.setMaximumPacketSize(p.getMaximumPacketSize());
+ p1.setNeedClientAuth(p.getNeedClientAuth());
+ p1.setProtocols(p.getProtocols().clone());
+ p1.setSNIMatchers(p.getSNIMatchers());
+ p1.setServerNames(p.getServerNames());
+ p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder());
+ p1.setWantClientAuth(p.getWantClientAuth());
+ return p1;
+ }
+
+
+ /** Resumes reading into the given buffer. */
+ static void unflip(ByteBuffer buf) {
+ buf.position(buf.limit());
+ buf.limit(buf.capacity());
+ }
+
+ /**
+ * Set limit to position, and position to mark.
+ *
+ *
+ * @param buffer
+ * @param mark
+ */
+ static void flipToMark(ByteBuffer buffer, int mark) {
+ buffer.limit(buffer.position());
+ buffer.position(mark);
+ }
+
+ /** Compact and leave ready for reading. */
+ static void compact(List<ByteBuffer> buffers) {
+ for (ByteBuffer b : buffers) {
+ b.compact();
+ b.flip();
+ }
+ }
+
+ static String stackTrace(Throwable t) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ String s = null;
+ try {
+ PrintStream p = new PrintStream(bos, true, "US-ASCII");
+ t.printStackTrace(p);
+ s = bos.toString("US-ASCII");
+ } catch (UnsupportedEncodingException ex) {
+ // can't happen
+ }
+ return s;
+ }
+
+ /** Copies as much of src to dst as possible. */
+ static void copy (ByteBuffer src, ByteBuffer dst) {
+ int srcLen = src.remaining();
+ int dstLen = dst.remaining();
+ if (srcLen > dstLen) {
+ int diff = srcLen - dstLen;
+ int limit = src.limit();
+ src.limit(limit - diff);
+ dst.put(src);
+ src.limit(limit);
+ } else {
+ dst.put(src);
+ }
+ }
+
+ static ByteBuffer copy(ByteBuffer src) {
+ ByteBuffer dst = ByteBuffer.allocate(src.remaining());
+ dst.put(src);
+ dst.flip();
+ return dst;
+ }
+
+ static String combine(String[] s) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ boolean first = true;
+ for (String s1 : s) {
+ if (!first) {
+ sb.append(", ");
+ first = false;
+ }
+ sb.append(s1);
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/package-info.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+/**
+ * <h2>High level HTTP API</h2>
+ * This provides a high-level client interface to HTTP (versions 1.1 and 2).
+ * Synchronous and asynchronous (via
+ * {@link java.util.concurrent.CompletableFuture}) modes are provided. The main
+ * classes defined are:
+ * <ul>
+ * <li>{@link java.net.http.HttpClient}</li>
+ * <li>{@link java.net.http.HttpRequest}</li>
+ * <li>{@link java.net.http.HttpResponse}</li>
+ * </ul>
+ *
+ * @since 9
+ */
+package java.net.http;
--- a/jdk/src/jdk.jdi/share/native/libdt_shmem/shmemBack.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/jdk.jdi/share/native/libdt_shmem/shmemBack.c Wed Jul 05 21:23:38 2017 +0200
@@ -338,7 +338,7 @@
return JDWPTRANSPORT_ERROR_NONE;
}
-JNIEXPORT jint JNICALL
+jint JNICALL
jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
jint version, jdwpTransportEnv** result)
{
--- a/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c Wed Jul 05 21:23:38 2017 +0200
@@ -784,7 +784,7 @@
return JDWPTRANSPORT_ERROR_NONE;
}
-JNIEXPORT jint JNICALL
+jint JNICALL
jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
jint version, jdwpTransportEnv** result)
{
--- a/jdk/src/jdk.localedata/share/classes/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/src/jdk.localedata/share/classes/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo Wed Jul 05 21:23:38 2017 +0200
@@ -1,2 +1,2 @@
sun.util.resources.provider.NonBaseLocaleDataMetaInfo
-sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo_jdk_localedata
+sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo
--- a/jdk/test/ProblemList.txt Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/ProblemList.txt Wed Jul 05 21:23:38 2017 +0200
@@ -200,6 +200,9 @@
java/nio/file/WatchService/Basic.java solaris-all
java/nio/file/WatchService/LotsOfEvents.java solaris-all
+# 8149712
+java/nio/charset/coders/BashStreams.java generic-all
+
############################################################################
# jdk_rmi
--- a/jdk/test/com/sun/net/httpserver/FileServerHandler.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/com/sun/net/httpserver/FileServerHandler.java Wed Jul 05 21:23:38 2017 +0200
@@ -23,6 +23,7 @@
import java.util.*;
import java.util.concurrent.*;
+import java.util.logging.*;
import java.io.*;
import java.net.*;
import java.security.*;
@@ -36,6 +37,10 @@
* Must be given an abs pathname to the document root.
* Directory listings together with text + html files
* can be served.
+ *
+ * File Server created on files sub-path
+ *
+ * Echo server created on echo sub-path
*/
public class FileServerHandler implements HttpHandler {
@@ -44,14 +49,24 @@
System.out.println ("usage: java FileServerHandler rootDir port logfilename");
System.exit(1);
}
+ Logger logger = Logger.getLogger("com.sun.net.httpserver");
+ ConsoleHandler ch = new ConsoleHandler();
+ logger.setLevel(Level.ALL);
+ ch.setLevel(Level.ALL);
+ logger.addHandler(ch);
+
String rootDir = args[0];
int port = Integer.parseInt (args[1]);
String logfile = args[2];
- HttpServer server = HttpServer.create (new InetSocketAddress (8000), 0);
+ HttpServer server = HttpServer.create (new InetSocketAddress (port), 0);
HttpHandler h = new FileServerHandler (rootDir);
+ HttpHandler h1 = new EchoHandler ();
- HttpContext c = server.createContext ("/", h);
+ HttpContext c = server.createContext ("/files", h);
c.getFilters().add (new LogFilter (new File (logfile)));
+ HttpContext c1 = server.createContext ("/echo", h1);
+ c.getFilters().add (new LogFilter (new File (logfile)));
+ c1.getFilters().add (new LogFilter (new File (logfile)));
server.setExecutor (Executors.newCachedThreadPool());
server.start ();
}
@@ -72,7 +87,8 @@
URI uri = t.getRequestURI();
String path = uri.getPath();
- while (is.read () != -1) ;
+ int x = 0;
+ while (is.read () != -1) x++;
is.close();
File f = new File (docroot, path);
if (!f.exists()) {
@@ -164,3 +180,61 @@
t.close();
}
}
+
+class EchoHandler implements HttpHandler {
+
+ byte[] read(InputStream is) throws IOException {
+ byte[] buf = new byte[1024];
+ byte[] result = new byte[0];
+
+ while (true) {
+ int n = is.read(buf);
+ if (n > 0) {
+ byte[] b1 = new byte[result.length + n];
+ System.arraycopy(result, 0, b1, 0, result.length);
+ System.arraycopy(buf, 0, b1, result.length, n);
+ result = b1;
+ } else if (n == -1) {
+ return result;
+ }
+ }
+ }
+
+ public void handle (HttpExchange t)
+ throws IOException
+ {
+ InputStream is = t.getRequestBody();
+ Headers map = t.getRequestHeaders();
+ String fixedrequest = map.getFirst ("XFixed");
+
+ // return the number of bytes received (no echo)
+ String summary = map.getFirst ("XSummary");
+ if (fixedrequest != null && summary == null) {
+ byte[] in = read(is);
+ t.sendResponseHeaders(200, in.length);
+ OutputStream os = t.getResponseBody();
+ os.write(in);
+ os.close();
+ is.close();
+ } else {
+ OutputStream os = t.getResponseBody();
+ byte[] buf = new byte[64 * 1024];
+ t.sendResponseHeaders(200, 0);
+ int n, count=0;;
+
+ while ((n = is.read(buf)) != -1) {
+ if (summary == null) {
+ os.write(buf, 0, n);
+ }
+ count += n;
+ }
+ if (summary != null) {
+ String s = Integer.toString(count);
+ os.write(s.getBytes());
+ }
+ os.close();
+ is.close();
+ }
+ }
+}
+
--- a/jdk/test/java/lang/invoke/T8139885.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/lang/invoke/T8139885.java Wed Jul 05 21:23:38 2017 +0200
@@ -26,6 +26,7 @@
/* @test
* @bug 8139885
* @bug 8143798
+ * @bug 8150825
* @run testng/othervm -ea -esa test.java.lang.invoke.T8139885
*/
@@ -315,14 +316,17 @@
{intid, MethodHandles.identity(double.class)},
{intid, MethodHandles.dropArguments(intid, 0, String.class)},
{intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class)},
- {errTarget, errCleanup}
+ {errTarget, errCleanup},
+ {TryFinally.MH_voidTarget, TryFinally.MH_voidCleanup}
};
String[] messages = {
"target and return types must match: double != int",
"cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable",
"cleanup second argument and target return type must match: (Throwable,double,int)int != int",
"cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
- errCleanup.type() + " != " + errTarget.type()
+ errCleanup.type() + " != " + errTarget.type(),
+ "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
+ TryFinally.MH_voidCleanup.type() + " != " + TryFinally.MH_voidTarget.type()
};
for (int i = 0; i < cases.length; ++i) {
boolean caught = false;
@@ -908,6 +912,10 @@
return r + " (but " + first + " first)!";
}
+ static void voidTarget() {}
+
+ static void voidCleanup(Throwable t, int a) {}
+
static final Class<TryFinally> TRY_FINALLY = TryFinally.class;
static final MethodType MT_greet = methodType(String.class, String.class);
@@ -916,6 +924,8 @@
static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class);
static final MethodType MT_greetMore = methodType(String.class, String.class, String.class);
static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class);
+ static final MethodType MT_voidTarget = methodType(void.class);
+ static final MethodType MT_voidCleanup = methodType(void.class, Throwable.class, int.class);
static final MethodHandle MH_greet;
static final MethodHandle MH_exclaim;
@@ -923,6 +933,8 @@
static final MethodHandle MH_printMore;
static final MethodHandle MH_greetMore;
static final MethodHandle MH_exclaimMore;
+ static final MethodHandle MH_voidTarget;
+ static final MethodHandle MH_voidCleanup;
static final MethodType MT_hello = methodType(String.class, String.class);
static final MethodType MT_printHello = methodType(void.class, String.class);
@@ -936,6 +948,8 @@
MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore);
MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore);
MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore);
+ MH_voidTarget = LOOKUP.findStatic(TRY_FINALLY, "voidTarget", MT_voidTarget);
+ MH_voidCleanup = LOOKUP.findStatic(TRY_FINALLY, "voidCleanup", MT_voidCleanup);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
--- a/jdk/test/java/net/SocketOption/OptionsTest.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/net/SocketOption/OptionsTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -54,6 +54,7 @@
Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
+ Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)),
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
};
@@ -61,6 +62,7 @@
static Test[] serverSocketTests = new Test[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
+ Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
};
@@ -68,6 +70,7 @@
Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)),
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
+ Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
};
@@ -97,15 +100,19 @@
Socket c = new Socket("127.0.0.1", srv.getLocalPort());
Socket s = srv.accept();
) {
+ Set<SocketOption<?>> options = c.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
for (int i=0; i<socketTests.length; i++) {
Test test = socketTests[i];
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(Socket.class, c,test.option);
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("S Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
+ if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
+ c.setOption((SocketOption)test.option, test.testValue);
+ Object getval = c.getOption((SocketOption)test.option);
+ Object legacyget = legacyGetOption(Socket.class, c,test.option);
+ if (!getval.equals(legacyget)) {
+ Formatter f = new Formatter();
+ f.format("S Err %d: %s/%s", i, getval, legacyget);
+ throw new RuntimeException(f.toString());
+ }
}
}
}
@@ -115,15 +122,19 @@
try (
DatagramSocket c = new DatagramSocket(0);
) {
+ Set<SocketOption<?>> options = c.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
for (int i=0; i<dgSocketTests.length; i++) {
Test test = dgSocketTests[i];
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(DatagramSocket.class, c,test.option);
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("DG Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
+ if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
+ c.setOption((SocketOption)test.option, test.testValue);
+ Object getval = c.getOption((SocketOption)test.option);
+ Object legacyget = legacyGetOption(DatagramSocket.class, c,test.option);
+ if (!getval.equals(legacyget)) {
+ Formatter f = new Formatter();
+ f.format("DG Err %d: %s/%s", i, getval, legacyget);
+ throw new RuntimeException(f.toString());
+ }
}
}
}
@@ -151,17 +162,21 @@
try (
ServerSocket c = new ServerSocket(0);
) {
+ Set<SocketOption<?>> options = c.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
for (int i=0; i<serverSocketTests.length; i++) {
Test test = serverSocketTests[i];
- c.setOption((SocketOption)test.option, test.testValue);
- Object getval = c.getOption((SocketOption)test.option);
- Object legacyget = legacyGetOption(
- ServerSocket.class, c, test.option
- );
- if (!getval.equals(legacyget)) {
- Formatter f = new Formatter();
- f.format("SS Err %d: %s/%s", i, getval, legacyget);
- throw new RuntimeException(f.toString());
+ if (!(test.option == StandardSocketOptions.SO_REUSEPORT && !reuseport)) {
+ c.setOption((SocketOption)test.option, test.testValue);
+ Object getval = c.getOption((SocketOption)test.option);
+ Object legacyget = legacyGetOption(
+ ServerSocket.class, c, test.option
+ );
+ if (!getval.equals(legacyget)) {
+ Formatter f = new Formatter();
+ f.format("SS Err %d: %s/%s", i, getval, legacyget);
+ throw new RuntimeException(f.toString());
+ }
}
}
}
@@ -174,6 +189,8 @@
{
if (type.equals(Socket.class)) {
Socket socket = (Socket)s;
+ Set<SocketOption<?>> options = socket.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
if (option.equals(StandardSocketOptions.SO_KEEPALIVE)) {
return Boolean.valueOf(socket.getKeepAlive());
@@ -183,6 +200,8 @@
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
+ return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
} else if (option.equals(StandardSocketOptions.SO_LINGER)) {
return Integer.valueOf(socket.getSoLinger());
} else if (option.equals(StandardSocketOptions.IP_TOS)) {
@@ -194,10 +213,15 @@
}
} else if (type.equals(ServerSocket.class)) {
ServerSocket socket = (ServerSocket)s;
+ Set<SocketOption<?>> options = socket.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
+
if (option.equals(StandardSocketOptions.SO_RCVBUF)) {
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
+ return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
} else if (option.equals(StandardSocketOptions.IP_TOS)) {
return Integer.valueOf(jdk.net.Sockets.getOption(
socket, StandardSocketOptions.IP_TOS));
@@ -206,6 +230,8 @@
}
} else if (type.equals(DatagramSocket.class)) {
DatagramSocket socket = (DatagramSocket)s;
+ Set<SocketOption<?>> options = socket.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
return Integer.valueOf(socket.getSendBufferSize());
@@ -213,6 +239,8 @@
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
+ return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
} else if (option.equals(StandardSocketOptions.IP_TOS)) {
return Integer.valueOf(socket.getTrafficClass());
} else {
@@ -221,6 +249,8 @@
} else if (type.equals(MulticastSocket.class)) {
MulticastSocket socket = (MulticastSocket)s;
+ Set<SocketOption<?>> options = socket.supportedOptions();
+ boolean reuseport = options.contains(StandardSocketOptions.SO_REUSEPORT);
if (option.equals(StandardSocketOptions.SO_SNDBUF)) {
return Integer.valueOf(socket.getSendBufferSize());
@@ -228,6 +258,8 @@
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.SO_REUSEPORT) && reuseport) {
+ return Boolean.valueOf(socket.getOption(StandardSocketOptions.SO_REUSEPORT));
} else if (option.equals(StandardSocketOptions.IP_TOS)) {
return Integer.valueOf(socket.getTrafficClass());
} else if (option.equals(StandardSocketOptions.IP_MULTICAST_IF)) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/APIErrors.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2015, 2016, 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 8087112
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext ProxyServer
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ * @run main/othervm APIErrors
+ */
+//package javaapplication16;
+
+import com.sun.net.httpserver.*;
+import java.io.IOException;
+import java.net.*;
+import java.net.http.*;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.function.Supplier;
+
+/**
+ * Does stupid things with API, to check appropriate errors/exceptions thrown
+ */
+public class APIErrors {
+
+ static HttpServer s1 = null;
+ static ExecutorService executor = null;
+ static int port;
+ static HttpClient client;
+ static String httproot, fileuri, fileroot;
+ static List<HttpClient> clients = new LinkedList<>();
+
+ public static void main(String[] args) throws Exception {
+ initServer();
+ fileroot = System.getProperty("test.src") + "/docs";
+
+ client = HttpClient.create().build();
+
+ clients.add(HttpClient.getDefault());
+
+ try {
+ test1();
+ test2();
+ test3();
+ } finally {
+ s1.stop(0);
+ executor.shutdownNow();
+ for (HttpClient client : clients)
+ client.executorService().shutdownNow();
+ }
+ }
+
+ static void reject(Runnable r, Class<? extends Exception> extype) {
+ try {
+ r.run();
+ throw new RuntimeException("Expected: " + extype);
+ } catch (Throwable t) {
+ if (!extype.isAssignableFrom(t.getClass())) {
+ throw new RuntimeException("Wrong exception type: " + extype + " / "
+ +t.getClass());
+ }
+ }
+ }
+
+ static void accept(Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ throw new RuntimeException("Unexpected exception: " + t);
+ }
+ }
+
+ static void checkNonNull(Supplier<?> r) {
+ if (r.get() == null)
+ throw new RuntimeException("Unexpected null return:");
+ }
+
+ static void assertTrue(Supplier<Boolean> r) {
+ if (r.get() == false)
+ throw new RuntimeException("Assertion failure:");
+ }
+
+ // HttpClient.Builder
+ static void test1() throws Exception {
+ System.out.println("Test 1");
+ HttpClient.Builder cb = HttpClient.create();
+ InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 5000);
+ reject(() -> { cb.priority(-1);}, IllegalArgumentException.class);
+ reject(() -> { cb.priority(500);}, IllegalArgumentException.class);
+ accept(() -> { cb.priority(1);});
+ accept(() -> { cb.priority(255);});
+
+ accept(() -> {clients.add(cb.build()); clients.add(cb.build());});
+ }
+
+ static void test2() throws Exception {
+ System.out.println("Test 2");
+ HttpClient.Builder cb = HttpClient.create();
+ InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 5000);
+ cb.proxy(ProxySelector.of(addr));
+ HttpClient c = cb.build();
+ clients.add(c);
+ checkNonNull(()-> {return c.executorService();});
+ assertTrue(()-> {return c.followRedirects() == HttpClient.Redirect.NEVER;});
+ assertTrue(()-> {return !c.authenticator().isPresent();});
+ }
+
+ static URI accessibleURI() {
+ return URI.create(fileuri);
+ }
+
+ static HttpRequest request() {
+ return HttpRequest.create(accessibleURI())
+ .GET();
+ }
+
+ static void test3() throws Exception {
+ System.out.println("Test 3");
+ reject(()-> {
+ try {
+ HttpRequest r1 = request();
+ HttpResponse resp = r1.response();
+ HttpResponse resp1 = r1.response();
+ } catch (IOException |InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }, IllegalStateException.class);
+
+ reject(()-> {
+ try {
+ HttpRequest r1 = request();
+ HttpResponse resp = r1.response();
+ HttpResponse resp1 = r1.responseAsync().get();
+ } catch (IOException |InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }, IllegalStateException.class);
+ reject(()-> {
+ try {
+ HttpRequest r1 = request();
+ HttpResponse resp1 = r1.responseAsync().get();
+ HttpResponse resp = r1.response();
+ } catch (IOException |InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }, IllegalStateException.class);
+ }
+
+ static class Auth extends java.net.Authenticator {
+ int count = 0;
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ if (count++ == 0) {
+ return new PasswordAuthentication("user", "passwd".toCharArray());
+ } else {
+ return new PasswordAuthentication("user", "goober".toCharArray());
+ }
+ }
+ int count() {
+ return count;
+ }
+ }
+
+ public static void initServer() throws Exception {
+ String root = System.getProperty ("test.src")+ "/docs";
+ InetSocketAddress addr = new InetSocketAddress (0);
+ s1 = HttpServer.create (addr, 0);
+ if (s1 instanceof HttpsServer) {
+ throw new RuntimeException ("should not be httpsserver");
+ }
+ HttpHandler h = new FileServerHandler(root);
+
+ HttpContext c1 = s1.createContext("/files", h);
+
+ executor = Executors.newCachedThreadPool();
+ s1.setExecutor (executor);
+ s1.start();
+
+ port = s1.getAddress().getPort();
+ System.out.println("HTTP server port = " + port);
+ httproot = "http://127.0.0.1:" + port + "/files/";
+ fileuri = httproot + "foo.txt";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/BasicAuthTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+/**
+ * @test
+ * @bug 8087112
+ * @run main/othervm BasicAuthTest
+ * @summary Basic Authentication Test
+ */
+
+import com.sun.net.httpserver.BasicAuthenticator;
+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 java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.URI;
+import java.net.http.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+public class BasicAuthTest {
+
+ static volatile boolean ok;
+ static final String RESPONSE = "Hello world";
+ static final String POST_BODY = "This is the POST body 123909090909090";
+
+ public static void main(String[] args) throws Exception {
+ HttpServer server = HttpServer.create(new InetSocketAddress(0), 10);
+ ExecutorService e = Executors.newCachedThreadPool();
+ Handler h = new Handler();
+ HttpContext serverContext = server.createContext("/test", h);
+ int port = server.getAddress().getPort();
+ System.out.println("Server port = " + port);
+
+ ClientAuth ca = new ClientAuth();
+ ServerAuth sa = new ServerAuth("foo realm");
+ serverContext.setAuthenticator(sa);
+ server.setExecutor(e);
+ server.start();
+ HttpClient client = HttpClient.create()
+ .authenticator(ca)
+ .build();
+
+ try {
+ URI uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/test/foo");
+ HttpRequest req = client.request(uri).GET();
+
+ HttpResponse resp = req.response();
+ ok = resp.statusCode() == 200 &&
+ resp.body(HttpResponse.asString()).equals(RESPONSE);
+
+ if (!ok || ca.count != 1)
+ throw new RuntimeException("Test failed");
+
+ // repeat same request, should succeed but no additional authenticator calls
+
+ req = client.request(uri).GET();
+ resp = req.response();
+ ok = resp.statusCode() == 200 &&
+ resp.body(HttpResponse.asString()).equals(RESPONSE);
+
+ if (!ok || ca.count != 1)
+ throw new RuntimeException("Test failed");
+
+ // try a POST
+
+ req = client.request(uri)
+ .body(HttpRequest.fromString(POST_BODY))
+ .POST();
+ resp = req.response();
+ ok = resp.statusCode() == 200;
+
+ if (!ok || ca.count != 1)
+ throw new RuntimeException("Test failed");
+ } finally {
+ client.executorService().shutdownNow();
+ server.stop(0);
+ e.shutdownNow();
+ }
+ System.out.println("OK");
+ }
+
+ static class ServerAuth extends BasicAuthenticator {
+
+ ServerAuth(String realm) {
+ super(realm);
+ }
+
+ @Override
+ public boolean checkCredentials(String username, String password) {
+ if (!"user".equals(username) || !"passwd".equals(password)) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ static class ClientAuth extends java.net.Authenticator {
+ volatile int count = 0;
+
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ count++;
+ return new PasswordAuthentication("user", "passwd".toCharArray());
+ }
+ }
+
+ static class Handler implements HttpHandler {
+ static volatile boolean ok;
+
+ @Override
+ public void handle(HttpExchange he) throws IOException {
+ String method = he.getRequestMethod();
+ InputStream is = he.getRequestBody();
+ if (method.equalsIgnoreCase("POST")) {
+ String requestBody = new String(is.readAllBytes(), US_ASCII);
+ if (!requestBody.equals(POST_BODY)) {
+ he.sendResponseHeaders(500, -1);
+ ok = false;
+ } else {
+ he.sendResponseHeaders(200, -1);
+ ok = true;
+ }
+ } else { // GET
+ he.sendResponseHeaders(200, RESPONSE.length());
+ OutputStream os = he.getResponseBody();
+ os.write(RESPONSE.getBytes(US_ASCII));
+ os.close();
+ ok = true;
+ }
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/HeadersTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+import java.net.http.HttpRequest;
+import java.net.URI;
+
+/**
+ * @test
+ * @bug 8087112
+ * @summary Basic test for headers
+ */
+public class HeadersTest {
+
+ static final URI TEST_URI = URI.create("http://www.foo.com/");
+
+ static void bad(String name) {
+ HttpRequest.Builder builder = HttpRequest.create(TEST_URI);
+ try {
+ builder.header(name, "foo");
+ throw new RuntimeException("Expected IAE for header:" + name);
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ static void good(String name) {
+ HttpRequest.Builder builder = HttpRequest.create(TEST_URI);
+ try {
+ builder.header(name, "foo");
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException("Unexpected IAE for header:" + name);
+ }
+ }
+
+ public static void main(String[] args) {
+ bad("bad:header");
+ bad("Foo\n");
+ good("X-Foo!");
+ good("Bar~");
+ good("x");
+ bad(" ");
+ bad("Bar\r\n");
+ good("Hello#world");
+ good("Qwer#ert");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/HttpUtils.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import static java.net.http.HttpRequest.fromByteArray;
+import static java.net.http.HttpRequest.fromByteArrays;
+import static java.net.http.HttpRequest.fromFile;
+import static java.net.http.HttpRequest.fromInputStream;
+import static java.net.http.HttpRequest.fromString;
+import java.net.http.HttpResponse;
+import static java.net.http.HttpResponse.asByteArray;
+import static java.net.http.HttpResponse.asFile;
+import static java.net.http.HttpResponse.asInputStream;
+import static java.net.http.HttpResponse.asString;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public final class HttpUtils {
+
+ static final int DEFAULT_OFFSET = 10;
+ static final int DEFAULT_LENGTH = 1000;
+ static final String midSizedFilename = "/files/notsobigfile.txt";
+ static final String smallFilename = "/files/smallfile.txt";
+ static final Path midSizedFile;
+ static final Path smallFile;
+ static final String fileroot;
+
+ public enum RequestBody {
+ STRING, FILE, BYTE_ARRAY, BYTE_ARRAY_OFFSET, INPUTSTREAM, STRING_WITH_CHARSET,
+ }
+
+ static {
+ fileroot = System.getProperty("test.src") + "/docs";
+ midSizedFile = Paths.get(fileroot + midSizedFilename);
+ smallFile = Paths.get(fileroot + smallFilename);
+ }
+
+ static public String getFileContent(String path) throws IOException {
+ FileInputStream fis = new FileInputStream(path);
+ byte[] buf = new byte[2 * 1024];
+ StringBuilder sb = new StringBuilder();
+ int byteRead;
+ while ((byteRead = fis.read(buf)) != -1) {
+ sb.append(new String(buf, 0, byteRead, "US-ASCII"));
+ }
+ return sb.toString();
+ }
+
+ public static HttpRequest.Builder getHttpRequestBuilder(final HttpClient client,
+ final String requestType,
+ final URI uri)
+ throws IOException
+ {
+ HttpRequest.Builder builder;
+ String filename = smallFile.toFile().getAbsolutePath();
+ String fileContents = HttpUtils.getFileContent(filename);
+ byte buf[] = fileContents.getBytes();
+ switch (requestType) {
+ case "InputStream":
+ InputStream inputStream = new FileInputStream(smallFile.toFile());
+ builder = client.request(uri)
+ .body(fromInputStream(inputStream));
+ break;
+ case "byteArray":
+ builder = client.request(uri)
+ .body(fromByteArray(buf));
+ break;
+ case "byteArrays":
+ Iterable iterable = Arrays.asList(buf);
+ builder = client.request(uri)
+ .body(fromByteArrays(iterable.iterator()));
+ break;
+ case "string":
+ builder = client.request(uri)
+ .body(fromString(fileContents));
+ break;
+ case "byteArray_offset":
+ builder = client.request(uri)
+ .body(fromByteArray(buf,
+ DEFAULT_OFFSET,
+ DEFAULT_LENGTH));
+ break;
+ case "file":
+ builder = client.request(uri)
+ .body(fromFile(smallFile));
+ break;
+ case "string_charset":
+ builder = client.request(uri)
+ .body(fromString(new String(buf),
+ Charset.defaultCharset()));
+ break;
+ default:
+ builder = null;
+ break;
+ }
+ return builder;
+ }
+
+ public static void checkResponse(final HttpResponse response,
+ String requestType,
+ final String responseType)
+ throws IOException
+ {
+ String filename = smallFile.toFile().getAbsolutePath();
+ String fileContents = HttpUtils.getFileContent(filename);
+ if (requestType.equals("byteArray_offset")) {
+ fileContents = fileContents.substring(DEFAULT_OFFSET,
+ DEFAULT_OFFSET + DEFAULT_LENGTH);
+ }
+ byte buf[] = fileContents.getBytes();
+ String responseBody;
+ switch (responseType) {
+ case "string":
+ responseBody = response.body(asString());
+ if (!responseBody.equals(fileContents)) {
+ throw new RuntimeException();
+ }
+ break;
+ case "byteArray":
+ byte arr[] = response.body(asByteArray());
+ if (!Arrays.equals(arr, buf)) {
+ throw new RuntimeException();
+ }
+ break;
+ case "file":
+ response.body(asFile(Paths.get("barf.txt")));
+ Path downloaded = Paths.get("barf.txt");
+ if (Files.size(downloaded) != fileContents.length()) {
+ throw new RuntimeException("Size mismatch");
+ }
+ break;
+ case "InputStream":
+ InputStream is = response.body(asInputStream());
+ byte arr1[] = new byte[1024];
+ int byteRead;
+ StringBuilder sb = new StringBuilder();
+ while ((byteRead = is.read(arr1)) != -1) {
+ sb.append(new String(arr1, 0, byteRead));
+ }
+ if (!sb.toString().equals(fileContents)) {
+ throw new RuntimeException();
+ }
+ break;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/ImmutableHeaders.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+/**
+ * @test
+ * @bug 8087112
+ * @run main/othervm ImmutableHeaders
+ * @summary ImmutableHeaders
+ */
+
+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.Headers;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.URI;
+import java.net.http.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.List;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+public class ImmutableHeaders {
+
+ final static String RESPONSE = "Hello world";
+
+ public static void main(String[] args) throws Exception {
+ HttpServer server = HttpServer.create(new InetSocketAddress(0), 10);
+ ExecutorService e = Executors.newCachedThreadPool();
+ Handler h = new Handler();
+ HttpContext serverContext = server.createContext("/test", h);
+ int port = server.getAddress().getPort();
+ System.out.println("Server port = " + port);
+
+ server.setExecutor(e);
+ server.start();
+ HttpClient client = HttpClient.create()
+ .build();
+
+ try {
+ URI uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/test/foo");
+ HttpRequest req = client.request(uri)
+ .headers("X-Foo", "bar")
+ .headers("X-Bar", "foo")
+ .GET();
+
+ try {
+ HttpHeaders hd = req.headers();
+ List<String> v = hd.allValues("X-Foo");
+ if (!v.get(0).equals("bar"))
+ throw new RuntimeException("Test failed");
+ v.add("XX");
+ throw new RuntimeException("Test failed");
+ } catch (UnsupportedOperationException ex) {
+ }
+ HttpResponse resp = req.response();
+ try {
+ HttpHeaders hd = resp.headers();
+ List<String> v = hd.allValues("X-Foo-Response");
+ if (!v.get(0).equals("resp"))
+ throw new RuntimeException("Test failed");
+ v.add("XX");
+ throw new RuntimeException("Test failed");
+ } catch (UnsupportedOperationException ex) {
+ }
+
+ } finally {
+ client.executorService().shutdownNow();
+ server.stop(0);
+ e.shutdownNow();
+ }
+ System.out.println("OK");
+ }
+
+ static class Handler implements HttpHandler {
+
+ @Override
+ public void handle(HttpExchange he) throws IOException {
+ String method = he.getRequestMethod();
+ InputStream is = he.getRequestBody();
+ Headers h = he.getResponseHeaders();
+ h.add("X-Foo-Response", "resp");
+ he.sendResponseHeaders(200, RESPONSE.length());
+ OutputStream os = he.getResponseBody();
+ os.write(RESPONSE.getBytes(US_ASCII));
+ os.close();
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/LightWeightHttpServer.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+/**
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext ProxyServer
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ */
+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 com.sun.net.httpserver.HttpsServer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.net.ssl.SSLContext;
+import jdk.testlibrary.SimpleSSLContext;
+
+public class LightWeightHttpServer {
+
+ static SSLContext ctx;
+ static HttpServer httpServer;
+ static HttpsServer httpsServer;
+ static ExecutorService executor;
+ static int port;
+ static int httpsport;
+ static String httproot;
+ static String httpsroot;
+ static ProxyServer proxy;
+ static int proxyPort;
+ static RedirectErrorHandler redirectErrorHandler, redirectErrorHandlerSecure;
+ static RedirectHandler redirectHandler, redirectHandlerSecure;
+ static DelayHandler delayHandler;
+ static final String midSizedFilename = "/files/notsobigfile.txt";
+ static final String smallFilename = "/files/smallfile.txt";
+ static Path midSizedFile;
+ static Path smallFile;
+ static String fileroot;
+
+ public static void initServer() throws IOException {
+
+ Logger logger = Logger.getLogger("com.sun.net.httpserver");
+ ConsoleHandler ch = new ConsoleHandler();
+ logger.setLevel(Level.ALL);
+ ch.setLevel(Level.ALL);
+ logger.addHandler(ch);
+
+ String root = System.getProperty("test.src") + "/docs";
+ InetSocketAddress addr = new InetSocketAddress(0);
+ httpServer = HttpServer.create(addr, 0);
+ if (httpServer instanceof HttpsServer) {
+ throw new RuntimeException("should not be httpsserver");
+ }
+ httpsServer = HttpsServer.create(addr, 0);
+ HttpHandler h = new FileServerHandler(root);
+
+ HttpContext c1 = httpServer.createContext("/files", h);
+ HttpContext c2 = httpsServer.createContext("/files", h);
+ HttpContext c3 = httpServer.createContext("/echo", new EchoHandler());
+ redirectHandler = new RedirectHandler("/redirect");
+ redirectHandlerSecure = new RedirectHandler("/redirect");
+ HttpContext c4 = httpServer.createContext("/redirect", redirectHandler);
+ HttpContext c41 = httpsServer.createContext("/redirect", redirectHandlerSecure);
+ HttpContext c5 = httpsServer.createContext("/echo", new EchoHandler());
+ HttpContext c6 = httpServer.createContext("/keepalive", new KeepAliveHandler());
+ redirectErrorHandler = new RedirectErrorHandler("/redirecterror");
+ redirectErrorHandlerSecure = new RedirectErrorHandler("/redirecterror");
+ HttpContext c7 = httpServer.createContext("/redirecterror", redirectErrorHandler);
+ HttpContext c71 = httpsServer.createContext("/redirecterror", redirectErrorHandlerSecure);
+ delayHandler = new DelayHandler();
+ HttpContext c8 = httpServer.createContext("/delay", delayHandler);
+ HttpContext c81 = httpsServer.createContext("/delay", delayHandler);
+
+ executor = Executors.newCachedThreadPool();
+ httpServer.setExecutor(executor);
+ httpsServer.setExecutor(executor);
+ ctx = new SimpleSSLContext().get();
+ httpsServer.setHttpsConfigurator(new HttpsConfigurator(ctx));
+ httpServer.start();
+ httpsServer.start();
+
+ port = httpServer.getAddress().getPort();
+ System.out.println("HTTP server port = " + port);
+ httpsport = httpsServer.getAddress().getPort();
+ System.out.println("HTTPS server port = " + httpsport);
+ httproot = "http://127.0.0.1:" + port + "/";
+ httpsroot = "https://127.0.0.1:" + httpsport + "/";
+
+ proxy = new ProxyServer(0, false);
+ proxyPort = proxy.getPort();
+ System.out.println("Proxy port = " + proxyPort);
+ }
+
+ public static void stop() throws IOException {
+ if (httpServer != null) {
+ httpServer.stop(0);
+ }
+ if (httpsServer != null) {
+ httpsServer.stop(0);
+ }
+ if (proxy != null) {
+ proxy.close();
+ }
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ }
+
+ static class RedirectErrorHandler implements HttpHandler {
+
+ String root;
+ volatile int count = 1;
+
+ RedirectErrorHandler(String root) {
+ this.root = root;
+ }
+
+ synchronized int count() {
+ return count;
+ }
+
+ synchronized void increment() {
+ count++;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange t)
+ throws IOException {
+ byte[] buf = new byte[2048];
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+
+ Headers map = t.getResponseHeaders();
+ String redirect = root + "/foo/" + Integer.toString(count);
+ increment();
+ map.add("Location", redirect);
+ t.sendResponseHeaders(301, -1);
+ t.close();
+ }
+ }
+
+ static class RedirectHandler implements HttpHandler {
+
+ String root;
+ volatile int count = 0;
+
+ RedirectHandler(String root) {
+ this.root = root;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange t)
+ throws IOException {
+ byte[] buf = new byte[2048];
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+
+ Headers map = t.getResponseHeaders();
+
+ if (count++ < 1) {
+ map.add("Location", root + "/foo/" + count);
+ } else {
+ map.add("Location", SmokeTest.midSizedFilename);
+ }
+ t.sendResponseHeaders(301, -1);
+ t.close();
+ }
+
+ int count() {
+ return count;
+ }
+
+ void reset() {
+ count = 0;
+ }
+ }
+
+ static class KeepAliveHandler implements HttpHandler {
+
+ volatile int counter = 0;
+ HashSet<Integer> portSet = new HashSet<>();
+ volatile int[] ports = new int[4];
+
+ void sleep(int n) {
+ try {
+ Thread.sleep(n);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange t)
+ throws IOException {
+ int remotePort = t.getRemoteAddress().getPort();
+ String result = "OK";
+
+ int n = counter++;
+ /// First test
+ if (n < 4) {
+ ports[n] = remotePort;
+ }
+ if (n == 3) {
+ // check all values in ports[] are the same
+ if (ports[0] != ports[1] || ports[2] != ports[3]
+ || ports[0] != ports[2]) {
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ }
+ }
+ // Second test
+ if (n >= 4 && n < 8) {
+ // delay to ensure ports are different
+ sleep(500);
+ ports[n - 4] = remotePort;
+ }
+ if (n == 7) {
+ // should be all different
+ if (ports[0] == ports[1] || ports[2] == ports[3]
+ || ports[0] == ports[2]) {
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ System.out.printf("Ports: %d, %d, %d, %d\n",
+ ports[0], ports[1], ports[2], ports[3]);
+ }
+ // setup for third test
+ for (int i = 0; i < 4; i++) {
+ portSet.add(ports[i]);
+ }
+ }
+ // Third test
+ if (n > 7) {
+ // just check that port is one of the ones in portSet
+ if (!portSet.contains(remotePort)) {
+ System.out.println("UNEXPECTED REMOTE PORT " + remotePort);
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ }
+ }
+ byte[] buf = new byte[2048];
+
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+ t.sendResponseHeaders(200, result.length());
+ OutputStream o = t.getResponseBody();
+ o.write(result.getBytes("US-ASCII"));
+ t.close();
+ }
+ }
+
+ static class DelayHandler implements HttpHandler {
+
+ CyclicBarrier bar1 = new CyclicBarrier(2);
+ CyclicBarrier bar2 = new CyclicBarrier(2);
+ CyclicBarrier bar3 = new CyclicBarrier(2);
+
+ CyclicBarrier barrier1() {
+ return bar1;
+ }
+
+ CyclicBarrier barrier2() {
+ return bar2;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange he) throws IOException {
+ byte[] buf = Util.readAll(he.getRequestBody());
+ try {
+ bar1.await();
+ bar2.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ }
+ he.sendResponseHeaders(200, -1); // will probably fail
+ he.close();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/ManyRequests.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2015, 2016, 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 8087112
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ * @run main/othervm ManyRequests
+ * @summary Send a large number of requests asynchronously
+ */
+
+//package javaapplication16;
+
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import javax.net.ssl.SSLContext;
+import jdk.testlibrary.SimpleSSLContext;
+
+public class ManyRequests {
+
+ public static void main(String[] args) throws Exception {
+ SSLContext ctx = new SimpleSSLContext().get();
+
+ InetSocketAddress addr = new InetSocketAddress(0);
+ HttpsServer server = HttpsServer.create(addr, 0);
+ server.setHttpsConfigurator(new HttpsConfigurator(ctx));
+
+ HttpClient client = HttpClient.create()
+ .sslContext(ctx)
+ .build();
+ try {
+ test(server, client);
+ System.out.println("OK");
+ } finally {
+ server.stop(0);
+ client.executorService().shutdownNow();
+ }
+ }
+
+ static final int REQUESTS = 1000;
+
+ static void test(HttpsServer server, HttpClient client) throws Exception {
+ int port = server.getAddress().getPort();
+ URI uri = new URI("https://127.0.0.1:" + port + "/foo/x");
+ server.createContext("/foo", new EchoHandler());
+ server.start();
+
+ RequestLimiter limiter = new RequestLimiter(40);
+ Random rand = new Random();
+ CompletableFuture<Void>[] results = new CompletableFuture[REQUESTS];
+ HashMap<HttpRequest,byte[]> bodies = new HashMap<>();
+
+ for (int i=0; i<REQUESTS; i++) {
+ byte[] buf = new byte[i+1]; // different size bodies
+ rand.nextBytes(buf);
+ HttpRequest r = client.request(uri)
+ .body(HttpRequest.fromByteArray(buf))
+ .POST();
+ bodies.put(r, buf);
+
+ results[i] =
+ limiter.whenOkToSend()
+ .thenCompose((v) -> r.responseAsync())
+ .thenCompose((resp) -> {
+ limiter.requestComplete();
+ if (resp.statusCode() != 200) {
+ resp.bodyAsync(HttpResponse.ignoreBody());
+ String s = "Expected 200, got: " + resp.statusCode();
+ return completedWithIOException(s);
+ }
+ return resp.bodyAsync(HttpResponse.asByteArray())
+ .thenApply((b) -> new Pair<>(resp, b));
+ })
+ .thenAccept((pair) -> {
+ HttpRequest request = pair.t.request();
+ byte[] requestBody = bodies.get(request);
+ check(Arrays.equals(requestBody, pair.u),
+ "bodies not equal");
+
+ });
+ }
+ // wait for them all to complete and throw exception in case of error
+ CompletableFuture.allOf(results).join();
+ }
+
+ static <T> CompletableFuture<T> completedWithIOException(String message) {
+ CompletableFuture<T> cf = new CompletableFuture<>();
+ cf.completeExceptionally(new IOException(message));
+ return cf;
+ }
+
+ static final class Pair<T,U> {
+ Pair(T t, U u) {
+ this.t = t; this.u = u;
+ }
+ T t;
+ U u;
+ }
+
+ /**
+ * A simple limiter for controlling the number of requests to be run in
+ * parallel whenOkToSend() is called which returns a CF<Void> that allows
+ * each individual request to proceed, or block temporarily (blocking occurs
+ * on the waiters list here. As each request actually completes
+ * requestComplete() is called to notify this object, and allow some
+ * requests to continue.
+ */
+ static class RequestLimiter {
+
+ static final CompletableFuture<Void> COMPLETED_FUTURE =
+ CompletableFuture.completedFuture(null);
+
+ final int maxnumber;
+ final LinkedList<CompletableFuture<Void>> waiters;
+ int number;
+ boolean blocked;
+
+ RequestLimiter(int maximum) {
+ waiters = new LinkedList<>();
+ maxnumber = maximum;
+ }
+
+ synchronized void requestComplete() {
+ number--;
+ // don't unblock until number of requests has halved.
+ if ((blocked && number <= maxnumber / 2) ||
+ (!blocked && waiters.size() > 0)) {
+ int toRelease = Math.min(maxnumber - number, waiters.size());
+ for (int i=0; i<toRelease; i++) {
+ CompletableFuture<Void> f = waiters.remove();
+ number ++;
+ f.complete(null);
+ }
+ blocked = number >= maxnumber;
+ }
+ }
+
+ synchronized CompletableFuture<Void> whenOkToSend() {
+ if (blocked || number + 1 >= maxnumber) {
+ blocked = true;
+ CompletableFuture<Void> r = new CompletableFuture<>();
+ waiters.add(r);
+ return r;
+ } else {
+ number++;
+ return COMPLETED_FUTURE;
+ }
+ }
+ }
+
+ static void check(boolean cond, Object... msg) {
+ if (cond)
+ return;
+ StringBuilder sb = new StringBuilder();
+ for (Object o : msg)
+ sb.append(o);
+ throw new RuntimeException(sb.toString());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/ProxyServer.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.security.*;
+
+/**
+ * A minimal proxy server that supports CONNECT tunneling. It does not do
+ * any header transformations. In future this could be added.
+ * Two threads are created per client connection. So, it's not
+ * intended for large numbers of parallel connections.
+ */
+public class ProxyServer extends Thread implements Closeable {
+
+ ServerSocket listener;
+ int port;
+ volatile boolean debug;
+
+ /**
+ * Create proxy on port (zero means don't care). Call getPort()
+ * to get the assigned port.
+ */
+ public ProxyServer(Integer port) throws IOException {
+ this(port, false);
+ }
+
+ public ProxyServer(Integer port, Boolean debug) throws IOException {
+ this.debug = debug;
+ listener = new ServerSocket(port);
+ this.port = listener.getLocalPort();
+ setName("ProxyListener");
+ setDaemon(true);
+ connections = new LinkedList<>();
+ start();
+ }
+
+ public ProxyServer(String s) { }
+
+ /**
+ * Returns the port number this proxy is listening on
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Shuts down the proxy, probably aborting any connections
+ * currently open
+ */
+ public void close() throws IOException {
+ if (debug) System.out.println("Proxy: closing");
+ done = true;
+ listener.close();
+ for (Connection c : connections) {
+ if (c.running()) {
+ c.close();
+ }
+ }
+ }
+
+ List<Connection> connections;
+
+ volatile boolean done;
+
+ public void run() {
+ if (System.getSecurityManager() == null) {
+ execute();
+ } else {
+ // so calling domain does not need to have socket permission
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ execute();
+ return null;
+ }
+ });
+ }
+ }
+
+ public void execute() {
+ try {
+ while(!done) {
+ Socket s = listener.accept();
+ if (debug)
+ System.out.println("Client: " + s);
+ Connection c = new Connection(s);
+ connections.add(c);
+ }
+ } catch(Throwable e) {
+ if (debug && !done) {
+ System.out.println("Fatal error: Listener: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Transparently forward everything, once we know what the destination is
+ */
+ class Connection {
+
+ Socket clientSocket, serverSocket;
+ Thread out, in;
+ volatile InputStream clientIn, serverIn;
+ volatile OutputStream clientOut, serverOut;
+
+ boolean forwarding = false;
+
+ final static int CR = 13;
+ final static int LF = 10;
+
+ Connection(Socket s) throws IOException {
+ this.clientSocket= s;
+ this.clientIn = new BufferedInputStream(s.getInputStream());
+ this.clientOut = s.getOutputStream();
+ init();
+ }
+
+ byte[] readHeaders(InputStream is) throws IOException {
+ byte[] outbuffer = new byte[8000];
+ int crlfcount = 0;
+ int bytecount = 0;
+ int c;
+ while ((c=is.read()) != -1 && bytecount < outbuffer.length) {
+ outbuffer[bytecount++] = (byte)c;
+ if (debug) System.out.write(c);
+ // were looking for CRLFCRLF sequence
+ if (c == CR || c == LF) {
+ switch(crlfcount) {
+ case 0:
+ if (c == CR) crlfcount ++;
+ break;
+ case 1:
+ if (c == LF) crlfcount ++;
+ break;
+ case 2:
+ if (c == CR) crlfcount ++;
+ break;
+ case 3:
+ if (c == LF) crlfcount ++;
+ break;
+ }
+ } else {
+ crlfcount = 0;
+ }
+ if (crlfcount == 4) {
+ break;
+ }
+ }
+ byte[] ret = new byte[bytecount];
+ System.arraycopy(outbuffer, 0, ret, 0, bytecount);
+ return ret;
+ }
+
+ boolean running() {
+ return out.isAlive() || in.isAlive();
+ }
+
+ public void close() throws IOException {
+ if (debug) System.out.println("Closing connection (proxy)");
+ if (serverSocket != null) serverSocket.close();
+ if (clientSocket != null) clientSocket.close();
+ }
+
+ int findCRLF(byte[] b) {
+ for (int i=0; i<b.length-1; i++) {
+ if (b[i] == CR && b[i+1] == LF) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void init() {
+ try {
+ byte[] buf = readHeaders(clientIn);
+ int p = findCRLF(buf);
+ if (p == -1) {
+ close();
+ return;
+ }
+ String cmd = new String(buf, 0, p, "US-ASCII");
+ String[] params = cmd.split(" ");
+ if (params[0].equals("CONNECT")) {
+ doTunnel(params[1]);
+ } else {
+ doProxy(params[1], buf, p, cmd);
+ }
+ } catch (IOException e) {
+ if (debug) {
+ System.out.println (e);
+ }
+ try {close(); } catch (IOException e1) {}
+ }
+ }
+
+ void doProxy(String dest, byte[] buf, int p, String cmdLine)
+ throws IOException
+ {
+ try {
+ URI uri = new URI(dest);
+ if (!uri.isAbsolute()) {
+ throw new IOException("request URI not absolute");
+ }
+ dest = uri.getAuthority();
+ // now extract the path from the URI and recreate the cmd line
+ int sp = cmdLine.indexOf(' ');
+ String method = cmdLine.substring(0, sp);
+ cmdLine = method + " " + uri.getPath() + " HTTP/1.1";
+ int x = cmdLine.length() - 1;
+ int i = p;
+ while (x >=0) {
+ buf[i--] = (byte)cmdLine.charAt(x--);
+ }
+ i++;
+
+ commonInit(dest, 80);
+ serverOut.write(buf, i, buf.length-i);
+ proxyCommon();
+
+ } catch (URISyntaxException e) {
+ throw new IOException(e);
+ }
+ }
+
+ void commonInit(String dest, int defaultPort) throws IOException {
+ int port;
+ String[] hostport = dest.split(":");
+ if (hostport.length == 1) {
+ port = defaultPort;
+ } else {
+ port = Integer.parseInt(hostport[1]);
+ }
+ if (debug) System.out.printf("Server: (%s/%d)\n", hostport[0], port);
+ serverSocket = new Socket(hostport[0], port);
+ serverOut = serverSocket.getOutputStream();
+
+ serverIn = new BufferedInputStream(serverSocket.getInputStream());
+ }
+
+ void proxyCommon() throws IOException {
+ out = new Thread(() -> {
+ try {
+ byte[] bb = new byte[8000];
+ int n;
+ while ((n = clientIn.read(bb)) != -1) {
+ serverOut.write(bb, 0, n);
+ }
+ serverSocket.close();
+ clientSocket.close();
+ } catch (IOException e) {
+ if (debug) {
+ System.out.println (e);
+ }
+ }
+ });
+ in = new Thread(() -> {
+ try {
+ byte[] bb = new byte[8000];
+ int n;
+ while ((n = serverIn.read(bb)) != -1) {
+ clientOut.write(bb, 0, n);
+ }
+ serverSocket.close();
+ clientSocket.close();
+ } catch (IOException e) {
+ if (debug) {
+ System.out.println(e);
+ e.printStackTrace();
+ }
+ }
+ });
+ out.setName("Proxy-outbound");
+ out.setDaemon(true);
+ in.setDaemon(true);
+ in.setName("Proxy-inbound");
+ out.start();
+ in.start();
+ }
+
+ void doTunnel(String dest) throws IOException {
+ commonInit(dest, 443);
+ clientOut.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());
+ proxyCommon();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ int port = Integer.parseInt(args[0]);
+ boolean debug = args.length > 1 && args[1].equals("-debug");
+ System.out.println("Debugging : " + debug);
+ ProxyServer ps = new ProxyServer(port, debug);
+ System.out.println("Proxy server listening on port " + ps.getPort());
+ while (true) {
+ Thread.sleep(5000);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/QuickResponses.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * @test
+ * @bug 8087112
+ * @build Server
+ * @run main/othervm -Djava.net.HttpClient.log=all QuickResponses
+ */
+
+/**
+ * Tests the buffering of data on connections across multiple
+ * responses
+ */
+public class QuickResponses {
+
+ static Server server;
+
+ static String response(String body) {
+ return "HTTP/1.1 200 OK\r\nContent-length: " + Integer.toString(body.length())
+ + "\r\n\r\n" + body;
+ }
+
+ static final String responses[] = {
+ "Lorem ipsum",
+ "dolor sit amet",
+ "consectetur adipiscing elit, sed do eiusmod tempor",
+ "quis nostrud exercitation ullamco",
+ "laboris nisi",
+ "ut",
+ "aliquip ex ea commodo consequat." +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse" +
+ "cillum dolore eu fugiat nulla pariatur.",
+ "Excepteur sint occaecat cupidatat non proident."
+ };
+
+ static String entireResponse() {
+ String s = "";
+ for (String r : responses) {
+ s += response(r);
+ }
+ return s;
+ }
+
+ public static void main(String[] args) throws Exception {
+ server = new Server(0);
+ URI uri = new URI(server.getURL());
+
+ HttpRequest request = HttpRequest.create(uri)
+ .GET();
+
+ CompletableFuture<HttpResponse> cf1 = request.responseAsync();
+ Server.Connection s1 = server.activity();
+ s1.send(entireResponse());
+
+
+ HttpResponse r = cf1.join();
+ if (r.statusCode()!= 200 || !r.body(HttpResponse.asString()).equals(responses[0]))
+ throw new RuntimeException("Failed on first response");
+
+ //now get the same identical response, synchronously to ensure same connection
+ int remaining = responses.length - 1;
+
+ for (int i=0; i<remaining; i++) {
+ r = HttpRequest.create(uri)
+ .GET()
+ .response();
+ if (r.statusCode()!= 200)
+ throw new RuntimeException("Failed");
+
+ String body = r.body(HttpResponse.asString());
+ if (!body.equals(responses[i+1]))
+ throw new RuntimeException("Failed");
+ }
+ HttpClient.getDefault().executorService().shutdownNow();
+ System.out.println("OK");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/RequestBodyTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015, 2016, 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 8087112
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext ProxyServer
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ * @run main/othervm RequestBodyTest
+ */
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.Executors;
+import javax.net.ssl.SSLContext;
+
+public class RequestBodyTest {
+
+ final static String STRING = "string";
+ final static String BYTE_ARRAY = "byteArray";
+ final static String BYTE_ARRAYS = "byteArrays";
+ final static String BYTE_ARRAY_OFFSET = "byteArray_offset";
+ final static String FILE = "file";
+ final static String STRING_CHARSET = "string_charset";
+ final static String INPUTSTREAM = "InputStream";
+
+ final static String midSizedFilename = "/files/notsobigfile.txt";
+ final static String smallFilename = "/files/smallfile.txt";
+ static Path midSizedFile;
+ static Path smallFile;
+ static String fileroot;
+ static HttpClient client;
+ static SSLContext ctx;
+ static String httproot;
+ static String httpsroot;
+
+ public static void main(String args[]) throws Exception {
+ fileroot = System.getProperty("test.src") + "/docs";
+ midSizedFile = Paths.get(fileroot + midSizedFilename);
+ smallFile = Paths.get(fileroot + smallFilename);
+ //start the server
+ LightWeightHttpServer.initServer();
+
+ httproot = LightWeightHttpServer.httproot;
+ httpsroot = LightWeightHttpServer.httpsroot;
+ ctx = LightWeightHttpServer.ctx;
+ client = HttpClient.create().sslContext(ctx)
+ .followRedirects(HttpClient.Redirect.ALWAYS)
+ .executorService(Executors.newCachedThreadPool())
+ .build();
+
+ String TARGET = httproot + "echo/foo";
+ boolean isSync = false;
+ requestBodyTypes(TARGET, STRING, STRING, isSync);
+ requestBodyTypes(TARGET, STRING, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, STRING, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, STRING, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, STRING, FILE, isSync);
+
+ requestBodyTypes(TARGET, BYTE_ARRAY, STRING, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, FILE, isSync);
+
+ requestBodyTypes(TARGET, BYTE_ARRAYS, STRING, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, FILE, isSync);
+
+ requestBodyTypes(TARGET, INPUTSTREAM, STRING, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, FILE, isSync);
+
+ requestBodyTypes(TARGET, FILE, STRING, isSync);
+ requestBodyTypes(TARGET, FILE, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, FILE, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, FILE, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, FILE, FILE, isSync);
+
+ isSync = true;
+ requestBodyTypes(TARGET, STRING, STRING, isSync);
+ requestBodyTypes(TARGET, STRING, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, STRING, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, STRING, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, STRING, FILE, isSync);
+
+ requestBodyTypes(TARGET, BYTE_ARRAY, STRING, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAY, FILE, isSync);
+
+ requestBodyTypes(TARGET, BYTE_ARRAYS, STRING, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, BYTE_ARRAYS, FILE, isSync);
+
+ requestBodyTypes(TARGET, INPUTSTREAM, STRING, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, INPUTSTREAM, FILE, isSync);
+
+ requestBodyTypes(TARGET, FILE, STRING, isSync);
+ requestBodyTypes(TARGET, FILE, BYTE_ARRAY, isSync);
+ requestBodyTypes(TARGET, FILE, BYTE_ARRAYS, isSync);
+ requestBodyTypes(TARGET, FILE, INPUTSTREAM, isSync);
+ requestBodyTypes(TARGET, FILE, FILE, isSync);
+
+ }
+
+ static void requestBodyTypes(final String target,
+ final String requestType,
+ final String responseType,
+ final boolean isAsync)
+ throws Exception
+ {
+ System.out.println("Running test_request_body_type " + requestType +
+ " and response type " + responseType + " and sync=" + isAsync);
+ URI uri = new URI(target);
+ byte buf[];
+ String filename = smallFile.toFile().getAbsolutePath();
+ String fileContents = HttpUtils.getFileContent(filename);
+ buf = fileContents.getBytes();
+ HttpRequest.Builder builder = HttpUtils.getHttpRequestBuilder(client,
+ requestType,
+ uri);
+ HttpResponse response;
+ if (!isAsync) {
+ response = builder.GET().response();
+ } else {
+ response = builder.GET().responseAsync().join();
+ }
+ HttpUtils.checkResponse(response, requestType, responseType);
+ System.out.println("OK");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/Server.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+//package javaapplication16;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A cut-down Http/1 Server for testing various error situations
+ *
+ * use interrupt() to halt
+ */
+public class Server extends Thread {
+
+ ServerSocket ss;
+ List<Connection> sockets;
+ AtomicInteger counter = new AtomicInteger(0);
+
+ // waits up to 20 seconds for something to happen
+ // dont use this unless certain activity coming.
+ public Connection activity() {
+ for (int i = 0; i < 80 * 100; i++) {
+ for (Connection c : sockets) {
+ if (c.poll()) {
+ return c;
+ }
+ }
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException e) {
+ }
+ }
+ return null;
+ }
+
+ // clears all current connections on Server.
+ public void reset() {
+ for (Connection c : sockets) {
+ c.close();
+ }
+ }
+
+ /**
+ * Reads data into an ArrayBlockingQueue<String> where each String
+ * is a line of input, that was terminated by CRLF (not included)
+ */
+ class Connection extends Thread {
+ Connection(Socket s) throws IOException {
+ this.socket = s;
+ id = counter.incrementAndGet();
+ is = s.getInputStream();
+ os = s.getOutputStream();
+ incoming = new ArrayBlockingQueue<>(100);
+ setName("Server-Connection");
+ setDaemon(true);
+ start();
+ }
+ final Socket socket;
+ final int id;
+ final InputStream is;
+ final OutputStream os;
+ final ArrayBlockingQueue<String> incoming;
+
+ final static String CRLF = "\r\n";
+
+ // sentinel indicating connection closed
+ final static String CLOSED = "C.L.O.S.E.D";
+ volatile boolean closed = false;
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[256];
+ String s = "";
+ try {
+ while (true) {
+ int n = is.read(buf);
+ if (n == -1) {
+ cleanup();
+ return;
+ }
+ String s0 = new String(buf, 0, n, StandardCharsets.ISO_8859_1);
+ s = s + s0;
+ int i;
+ while ((i=s.indexOf(CRLF)) != -1) {
+ String s1 = s.substring(0, i+2);
+ incoming.put(s1);
+ if (i+2 == s.length()) {
+ s = "";
+ break;
+ }
+ s = s.substring(i+2);
+ }
+ }
+ } catch (IOException |InterruptedException e1) {
+ cleanup();
+ } catch (Throwable t) {
+ System.out.println("X: " + t);
+ cleanup();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Server.Connection: " + socket.toString();
+ }
+
+ public void sendHttpResponse(int code, String body, String... headers)
+ throws IOException
+ {
+ String r1 = "HTTP/1.1 " + Integer.toString(code) + " status" + CRLF;
+ for (int i=0; i<headers.length; i+=2) {
+ r1 += headers[i] + ": " + headers[i+1] + CRLF;
+ }
+ int clen = body == null ? 0 : body.length();
+ r1 += "Content-Length: " + Integer.toString(clen) + CRLF;
+ r1 += CRLF;
+ if (body != null) {
+ r1 += body;
+ }
+ send(r1);
+ }
+
+ // content-length is 10 bytes too many
+ public void sendIncompleteHttpResponseBody(int code) throws IOException {
+ String body = "Hello World Helloworld Goodbye World";
+ String r1 = "HTTP/1.1 " + Integer.toString(code) + " status" + CRLF;
+ int clen = body.length() + 10;
+ r1 += "Content-Length: " + Integer.toString(clen) + CRLF;
+ r1 += CRLF;
+ if (body != null) {
+ r1 += body;
+ }
+ send(r1);
+ }
+
+ public void sendIncompleteHttpResponseHeaders(int code)
+ throws IOException
+ {
+ String r1 = "HTTP/1.1 " + Integer.toString(code) + " status" + CRLF;
+ send(r1);
+ }
+
+ public void send(String r) throws IOException {
+ os.write(r.getBytes(StandardCharsets.ISO_8859_1));
+ }
+
+ public synchronized void close() {
+ cleanup();
+ closed = true;
+ incoming.clear();
+ }
+
+ public String nextInput(long timeout, TimeUnit unit) {
+ String result = "";
+ while (poll()) {
+ try {
+ String s = incoming.poll(timeout, unit);
+ if (s == null && closed) {
+ return CLOSED;
+ } else {
+ result += s;
+ }
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ return result;
+ }
+
+ public String nextInput() {
+ return nextInput(0, TimeUnit.SECONDS);
+ }
+
+ public boolean poll() {
+ return incoming.peek() != null;
+ }
+
+ private void cleanup() {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ sockets.remove(this);
+ }
+ }
+
+ Server(int port) throws IOException {
+ ss = new ServerSocket(port);
+ sockets = Collections.synchronizedList(new LinkedList<>());
+ setName("Test-Server");
+ setDaemon(true);
+ start();
+ }
+
+ Server() throws IOException {
+ this(0);
+ }
+
+ int port() {
+ return ss.getLocalPort();
+ }
+
+ public String getURL() {
+ return "http://127.0.0.1:" + port() + "/foo/";
+ }
+
+ public void close() {
+ try {
+ ss.close();
+ } catch (IOException e) {
+ }
+ for (Connection c : sockets) {
+ c.close();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ Socket s = ss.accept();
+ Connection c = new Connection(s);
+ sockets.add(c);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/SmokeTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 2015, 2016, 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 8087112
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext ProxyServer
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ * @run main/othervm SmokeTest
+ */
+
+//package javaapplication16;
+
+import com.sun.net.httpserver.*;
+import java.net.*;
+import java.net.http.*;
+import java.io.*;
+import java.util.concurrent.*;
+import javax.net.ssl.*;
+import java.nio.file.*;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import jdk.testlibrary.SimpleSSLContext;
+import static java.net.http.HttpRequest.*;
+import static java.net.http.HttpResponse.*;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * * Basic smoke test for Http/1.1 client
+ * - basic request response
+ * - request body POST
+ * - response body GET
+ * - redirect
+ * - chunked request/response
+ * - SSL
+ * - proxies
+ * - 100 continue
+ * - check keep alive appears to be working
+ * - cancel of long request
+ *
+ * Uses a FileServerHandler serving a couple of known files
+ * in docs directory.
+ */
+public class SmokeTest {
+ static SSLContext ctx;
+ static HttpServer s1 ;
+ static HttpsServer s2;
+ static ExecutorService executor;
+ static int port;
+ static int httpsport;
+ static String httproot;
+ static String httpsroot;
+ static HttpClient client;
+ static ProxyServer proxy;
+ static int proxyPort;
+ static RedirectErrorHandler redirectErrorHandler, redirectErrorHandlerSecure;
+ static RedirectHandler redirectHandler, redirectHandlerSecure;
+ static DelayHandler delayHandler;
+ final static String midSizedFilename = "/files/notsobigfile.txt";
+ final static String smallFilename = "/files/smallfile.txt";
+ static Path midSizedFile;
+ static Path smallFile;
+ static String fileroot;
+
+ static String getFileContent(String path) throws IOException {
+ FileInputStream fis = new FileInputStream(path);
+ byte[] buf = new byte[2000];
+ StringBuilder sb = new StringBuilder();
+ int n;
+ while ((n=fis.read(buf)) != -1) {
+ sb.append(new String(buf, 0, n, "US-ASCII"));
+ }
+ return sb.toString();
+ }
+
+ public static void main(String[] args) throws Exception {
+ initServer();
+ fileroot = System.getProperty ("test.src", ".")+ "/docs";
+ midSizedFile = Paths.get(fileroot + midSizedFilename);
+ smallFile = Paths.get(fileroot + smallFilename);
+
+ client = HttpClient.create()
+ .sslContext(ctx)
+ .followRedirects(HttpClient.Redirect.ALWAYS)
+ .executorService(Executors.newCachedThreadPool())
+ .build();
+
+ try {
+ test1(httproot + "files/foo.txt", true);
+
+ test1(httproot + "files/foo.txt", false);
+ test1(httpsroot + "files/foo.txt", true);
+ test1(httpsroot + "files/foo.txt", false);
+ test2(httproot + "echo/foo", "This is a short test");
+ test2(httpsroot + "echo/foo", "This is a short test");
+
+ test3(httproot + "redirect/foo.txt");
+ test3(httpsroot + "redirect/foo.txt");
+ test4(httproot + "files/foo.txt");
+ test4(httpsroot + "files/foo.txt");
+ test5(httproot + "echo/foo", true);
+ test5(httpsroot + "echo/foo", true);
+ test5(httproot + "echo/foo", false);
+ test5(httpsroot + "echo/foo", false);
+
+ test6(httproot + "echo/foo", true);
+ test6(httpsroot + "echo/foo", true);
+ test6(httproot + "echo/foo", false);
+ test6(httpsroot + "echo/foo", false);
+
+ test7(httproot + "keepalive/foo");
+
+ test8(httproot + "files/foo.txt", true);
+ test8(httproot + "files/foo.txt", false);
+ test8(httpsroot + "files/foo.txt", true);
+ test8(httpsroot + "files/foo.txt", false);
+ // disabled test9();
+
+ test10(httproot + "redirecterror/foo.txt");
+
+ test10(httpsroot + "redirecterror/foo.txt");
+
+ test11(httproot + "echo/foo");
+ test11(httpsroot + "echo/foo");
+ //test12(httproot + "delay/foo", delayHandler);
+
+ } finally {
+ s1.stop(0);
+ s2.stop(0);
+ proxy.close();
+ executor.shutdownNow();
+ client.executorService().shutdownNow();
+ }
+ }
+
+ static class Auth extends java.net.Authenticator {
+ volatile int count = 0;
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ if (count++ == 0) {
+ return new PasswordAuthentication("user", "passwd".toCharArray());
+ } else {
+ return new PasswordAuthentication("user", "goober".toCharArray());
+ }
+ }
+ int count() {
+ return count;
+ }
+ }
+
+ // Basic test
+ static void test1(String target, boolean fixedLen) throws Exception {
+ System.out.print("test1: " + target);
+ URI uri = new URI(target);
+
+ HttpRequest.Builder builder = client.request(uri)
+ .body(noBody());
+
+ if (fixedLen) {
+ builder.header("XFixed", "yes");
+ }
+
+ HttpResponse response = builder.GET().response();
+
+ String body = response.body(asString());
+ if (!body.equals("This is foo.txt\r\n")) {
+ throw new RuntimeException();
+ }
+
+ // repeat async
+ response = builder.GET().responseAsync().join();
+
+ body = response.body(asString());
+ if (!body.equals("This is foo.txt\r\n")) {
+ throw new RuntimeException();
+ }
+ System.out.println(" OK");
+ }
+
+ // POST use echo to check reply
+ static void test2(String s, String body) throws Exception {
+ System.out.print("test2: " + s);
+ URI uri = new URI(s);
+
+ HttpResponse response = client.request(uri)
+ .body(fromString(body))
+ .POST()
+ .response();
+
+ if (response.statusCode() != 200) {
+ throw new RuntimeException(
+ "Expected 200, got [ " + response.statusCode() + " ]");
+ }
+ String reply = response.body(asString());
+ if (!reply.equals(body)) {
+ throw new RuntimeException(
+ "Body mismatch: expected [" + body + "], got [" + reply + "]");
+ }
+ System.out.println(" OK");
+ }
+
+ // Redirect
+ static void test3(String s) throws Exception {
+ System.out.print("test3: " + s);
+ URI uri = new URI(s);
+ RedirectHandler handler = uri.getScheme().equals("https")
+ ? redirectHandlerSecure : redirectHandler;
+
+ HttpResponse response = client.request(uri)
+ .body(noBody())
+ .GET()
+ .response();
+
+ if (response.statusCode() != 200) {
+ throw new RuntimeException(
+ "Expected 200, got [ " + response.statusCode() + " ]");
+ } else {
+ response.body(HttpResponse.asFile(Paths.get("redir1.txt")));
+ }
+
+ Path downloaded = Paths.get("redir1.txt");
+ if (Files.size(downloaded) != Files.size(midSizedFile)) {
+ throw new RuntimeException("Size mismatch");
+ }
+
+ System.out.printf(" (count: %d) ", handler.count());
+ // repeat with async api
+
+ handler.reset();
+
+ response = client.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync()
+ .join();
+
+ if (response.statusCode() != 200) {
+ throw new RuntimeException(
+ "Expected 200, got [ " + response.statusCode() + " ]");
+ } else {
+ response.body(HttpResponse.asFile(Paths.get("redir2.txt")));
+ }
+
+ downloaded = Paths.get("redir2.txt");
+ if (Files.size(downloaded) != Files.size(midSizedFile)) {
+ throw new RuntimeException("Size mismatch 2");
+ }
+ System.out.printf(" (count: %d) ", handler.count());
+ System.out.println(" OK");
+ }
+
+ // Proxies
+ static void test4(String s) throws Exception {
+ System.out.print("test4: " + s);
+ URI uri = new URI(s);
+ InetSocketAddress proxyAddr = new InetSocketAddress("127.0.0.1", proxyPort);
+ String filename = fileroot + uri.getPath();
+
+ HttpClient cl = HttpClient.create()
+ .proxy(ProxySelector.of(proxyAddr))
+ .sslContext(ctx)
+ .build();
+
+ CompletableFuture<String> fut = cl.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync()
+ .thenCompose((HttpResponse response) ->
+ response.bodyAsync(asString())
+ );
+
+ String body = fut.get(5, TimeUnit.HOURS);
+
+ String fc = getFileContent(filename);
+
+ if (!body.equals(fc)) {
+ throw new RuntimeException(
+ "Body mismatch: expected [" + body + "], got [" + fc + "]");
+ }
+ cl.executorService().shutdownNow();
+ System.out.println(" OK");
+ }
+
+ // 100 Continue: use echo target
+ static void test5(String target, boolean fixedLen) throws Exception {
+ System.out.print("test5: " + target);
+ URI uri = new URI(target);
+ String requestBody = generateString(12 * 1024 + 13);
+
+ HttpRequest.Builder builder = client.request(uri)
+ .expectContinue(true)
+ .body(fromString(requestBody));
+
+ if (fixedLen) {
+ builder.header("XFixed", "yes");
+ }
+
+ HttpResponse response = builder.GET().response();
+
+ String body = response.body(asString());
+
+ if (!body.equals(requestBody)) {
+ throw new RuntimeException(
+ "Body mismatch: expected [" + body + "], got [" + body + "]");
+ }
+ System.out.println(" OK");
+ }
+
+ // use echo
+ static void test6(String target, boolean fixedLen) throws Exception {
+ System.out.print("test6: " + target);
+ URI uri = new URI(target);
+ String requestBody = generateString(12 * 1024 + 3);
+
+ HttpRequest.Builder builder = client.request(uri)
+ .body(noBody());
+
+ if (fixedLen) {
+ builder.header("XFixed", "yes");
+ }
+
+ HttpResponse response = builder.GET().response();
+
+ if (response.statusCode() != 200) {
+ throw new RuntimeException(
+ "Expected 200, got [ " + response.statusCode() + " ]");
+ }
+
+ String responseBody = response.body(asString());
+
+ if (responseBody.equals(requestBody)) {
+ throw new RuntimeException(
+ "Body mismatch: expected [" + requestBody + "], got [" + responseBody + "]");
+ }
+ System.out.println(" OK");
+ }
+
+ @SuppressWarnings("rawtypes")
+ static void test7(String target) throws Exception {
+ System.out.print("test7: " + target);
+
+ // First test
+ URI uri = new URI(target);
+ for (int i=0; i<4; i++) {
+ HttpResponse r = client.request(uri)
+ .body(noBody())
+ .GET()
+ .response();
+ String body = r.body(asString());
+ if (!body.equals("OK")) {
+ throw new RuntimeException("Expected OK, got: " + body);
+ }
+ }
+
+ // Second test: 4 x parallel
+ List<CompletableFuture<HttpResponse>> futures = new LinkedList<>();
+ for (int i=0; i<4; i++) {
+ futures.add(client.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync());
+ }
+ // all sent?
+ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
+ .join();
+
+ List<CompletableFuture<String>> futureBodies = new LinkedList<>();
+ for (int i=0; i<4; i++) {
+ futureBodies.add(futures.get(i)
+ .join()
+ .bodyAsync(asString()));
+ }
+ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
+ .join();
+
+ for (CompletableFuture<String> future : futureBodies) {
+ String body = future.get();
+ if (!body.equals("OK")) {
+ throw new RuntimeException("Expected OK, got: " + body);
+ }
+ }
+
+ // Third test: Multiple of 4 parallel requests
+ BlockingQueue<String> q = new LinkedBlockingQueue<>();
+ for (int i=0; i<4; i++) {
+ client.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync()
+ .thenApply((HttpResponse resp) -> {
+ String body = resp.body(asString());
+ putQ(q, body);
+ return body;
+ });
+ }
+ // we've sent four requests. Now, just send another request
+ // as each response is received. The idea is to ensure that
+ // only four sockets ever get used.
+
+ for (int i=0; i<100; i++) {
+ // block until response received
+ String body = takeQ(q);
+ if (!body.equals("OK")) {
+ throw new RuntimeException(body);
+ }
+ client.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync()
+ .thenApply((HttpResponse resp) -> {
+ String body1 = resp.body(asString());
+ putQ(q, body1);
+ return body1;
+ });
+ }
+ // should be four left
+ for (int i=0; i<4; i++) {
+ takeQ(q);
+ }
+ System.out.println(" OK");
+ }
+
+ static String takeQ(BlockingQueue<String> q) {
+ String r = null;
+ try {
+ r = q.take();
+ } catch (InterruptedException e) {}
+
+ return r;
+ }
+
+ static void putQ(BlockingQueue<String> q, String o) {
+ try {
+ q.put(o);
+ } catch (InterruptedException e) {
+ // can't happen
+ }
+ }
+
+ static void test8(String target, boolean fixedLen) throws Exception {
+ System.out.print("test8: " + target);
+ URI uri = new URI(target);
+
+ HttpRequest.Builder builder = client.request(uri)
+ .body(noBody());
+
+ if (fixedLen) {
+ builder.header("XFixed", "yes");
+ }
+
+ HttpResponse response = builder.GET().response();
+
+ StringBuilder sb = new StringBuilder();
+
+ InputStream is = response.body(asInputStream());
+ int c;
+ byte[] buf = new byte[2048];
+ while ((c = is.read(buf)) != -1) {
+ for (int i=0; i<c; i++)
+ sb.append((char)buf[i]);
+ }
+ is.close();
+ String body = sb.toString();
+
+ if (!body.equals("This is foo.txt\r\n")) {
+ throw new RuntimeException("Expected \"This is foo.txt\", got: " + body);
+ }
+ System.out.println(" OK");
+ }
+
+ // Chunked output stream
+ static void test11(String target) throws Exception {
+ System.out.print("test11: " + target);
+ URI uri = new URI(target);
+
+ FileInputStream file = new FileInputStream(smallFile.toFile());
+
+ HttpRequest.Builder builder = client.request(uri)
+ .body(HttpRequest.fromInputStream(file));
+ HttpResponse response = builder.POST().response();
+
+ if (response.statusCode() != 200) {
+ throw new RuntimeException("Wrong response code");
+ }
+
+ Path download = Paths.get("test11.txt");
+ download.toFile().delete();
+ response.body(HttpResponse.asFile(download));
+
+ if (Files.size(download) != Files.size(smallFile)) {
+ System.out.println("Original size: " + Files.size(smallFile));
+ System.out.println("Downloaded size: " + Files.size(download));
+ throw new RuntimeException("Size mismatch");
+ }
+ System.out.println(" OK");
+ }
+
+ // cancel
+ /*
+ static void test12(String target, DelayHandler h) throws Exception {
+ System.out.print("test12: " + target);
+ URI uri = new URI(target);
+
+ HttpRequest.Builder builder = client
+ .request(uri)
+ .body(HttpRequest.fromString("Hello world"));
+
+ HttpRequest request = builder
+ .GET();
+ request.sendAsync();
+ h.barrier1().await();
+ // request has been processed
+ CompletableFuture<HttpResponse> cf = request.responseAsync();
+ request.cancel();
+ h.barrier2().await();
+ try {
+ HttpResponse r = cf.get();
+ throw new RuntimeException("failed 2");
+ } catch (Exception e) {
+ }
+ System.out.println(" OK");
+ }
+*/
+ static void delay(int seconds) {
+ try {
+ Thread.sleep(seconds * 1000);
+ } catch (InterruptedException e) {
+ }
+ }
+/*
+ // test won't work until sending fully decoupled from receiving in impl
+ static void test9() throws Exception {
+ System.out.print("test9: ");
+ UploadServer up = new UploadServer(1000 * 1000);
+ int size = up.size();
+ String u = "http://127.0.0.1:" + up.port() + "/";
+ URI uri = new URI(u);
+
+ HttpRequest request = client
+ .request(uri)
+ .body(new HttpRequestBodyProcessor() {
+ @Override
+ public ByteBuffer onRequestBodyChunk(ByteBuffer b) throws IOException {
+ // slow things down
+ delay(1);
+ b.position(b.limit()); // fill it
+ return b;
+ }
+ @Override
+ public long onRequestStart(HttpRequest req) throws IOException {
+ return size;
+ }
+ })
+ .PUT();
+
+ CompletableFuture<HttpRequest> cf1 = request.sendAsync();
+ CompletableFuture<HttpResponse> cf = request.responseAsync();
+
+ HttpResponse resp = cf.get(1, TimeUnit.MINUTES);
+ if (resp.statusCode() != 201) {
+ throw new RuntimeException("failed: wrong response code");
+ }
+ delay(2); // allow some data to be sent
+ request.cancel();
+ delay(1);
+ if (up.failed()) {
+ throw new RuntimeException("failed to cancel request");
+ }
+ System.out.println(" OK");
+ }
+ */
+ // Redirect loop: return an error after a certain number of redirects
+ static void test10(String s) throws Exception {
+ System.out.print("test10: " + s);
+ URI uri = new URI(s);
+ RedirectErrorHandler handler = uri.getScheme().equals("https")
+ ? redirectErrorHandlerSecure : redirectErrorHandler;
+
+ CompletableFuture<HttpResponse> cf = client.request(uri)
+ .body(noBody())
+ .GET()
+ .responseAsync();
+
+ try {
+ HttpResponse response = cf.join();
+ throw new RuntimeException("Exepected Completion Exception");
+ } catch (CompletionException e) {
+ //System.out.println(e);
+ }
+
+ System.out.printf(" (Calls %d) ", handler.count());
+ System.out.println(" OK");
+ }
+
+ static final int NUM = 50;
+
+ static Random random = new Random();
+ static final String alphabet = "ABCDEFGHIJKLMNOPQRST";
+
+ static char randomChar() {
+ return alphabet.charAt(random.nextInt(alphabet.length()));
+ }
+
+ static String generateString(int length) {
+ StringBuilder sb = new StringBuilder(length);
+ for (int i=0; i<length; i++) {
+ sb.append(randomChar());
+ }
+ return sb.toString();
+ }
+
+ static void initServer() throws Exception {
+ Logger logger = Logger.getLogger("com.sun.net.httpserver");
+ ConsoleHandler ch = new ConsoleHandler();
+ logger.setLevel(Level.ALL);
+ ch.setLevel(Level.ALL);
+ logger.addHandler(ch);
+
+ String root = System.getProperty ("test.src")+ "/docs";
+ InetSocketAddress addr = new InetSocketAddress (0);
+ s1 = HttpServer.create (addr, 0);
+ if (s1 instanceof HttpsServer) {
+ throw new RuntimeException ("should not be httpsserver");
+ }
+ s2 = HttpsServer.create (addr, 0);
+ HttpHandler h = new FileServerHandler(root);
+
+ HttpContext c1 = s1.createContext("/files", h);
+ HttpContext c2 = s2.createContext("/files", h);
+ HttpContext c3 = s1.createContext("/echo", new EchoHandler());
+ redirectHandler = new RedirectHandler("/redirect");
+ redirectHandlerSecure = new RedirectHandler("/redirect");
+ HttpContext c4 = s1.createContext("/redirect", redirectHandler);
+ HttpContext c41 = s2.createContext("/redirect", redirectHandlerSecure);
+ HttpContext c5 = s2.createContext("/echo", new EchoHandler());
+ HttpContext c6 = s1.createContext("/keepalive", new KeepAliveHandler());
+ redirectErrorHandler = new RedirectErrorHandler("/redirecterror");
+ redirectErrorHandlerSecure = new RedirectErrorHandler("/redirecterror");
+ HttpContext c7 = s1.createContext("/redirecterror", redirectErrorHandler);
+ HttpContext c71 = s2.createContext("/redirecterror", redirectErrorHandlerSecure);
+ delayHandler = new DelayHandler();
+ HttpContext c8 = s1.createContext("/delay", delayHandler);
+ HttpContext c81 = s2.createContext("/delay", delayHandler);
+
+ executor = Executors.newCachedThreadPool();
+ s1.setExecutor(executor);
+ s2.setExecutor(executor);
+ ctx = new SimpleSSLContext().get();
+ s2.setHttpsConfigurator(new HttpsConfigurator(ctx));
+ s1.start();
+ s2.start();
+
+ port = s1.getAddress().getPort();
+ System.out.println("HTTP server port = " + port);
+ httpsport = s2.getAddress().getPort();
+ System.out.println("HTTPS server port = " + httpsport);
+ httproot = "http://127.0.0.1:" + port + "/";
+ httpsroot = "https://127.0.0.1:" + httpsport + "/";
+
+ proxy = new ProxyServer(0, false);
+ proxyPort = proxy.getPort();
+ System.out.println("Proxy port = " + proxyPort);
+ }
+}
+
+class UploadServer extends Thread {
+ int statusCode;
+ ServerSocket ss;
+ int port;
+ int size;
+ Object lock;
+ boolean failed = false;
+
+ UploadServer(int size) throws IOException {
+ this.statusCode = statusCode;
+ this.size = size;
+ ss = new ServerSocket(0);
+ port = ss.getLocalPort();
+ lock = new Object();
+ }
+
+ int port() {
+ return port;
+ }
+
+ int size() {
+ return size;
+ }
+
+ // wait a sec before calling this
+ boolean failed() {
+ synchronized(lock) {
+ return failed;
+ }
+ }
+
+ @Override
+ public void run () {
+ int nbytes = 0;
+ Socket s = null;
+
+ synchronized(lock) {
+ try {
+ s = ss.accept();
+
+ InputStream is = s.getInputStream();
+ OutputStream os = s.getOutputStream();
+ os.write("HTTP/1.1 201 OK\r\nContent-length: 0\r\n\r\n".getBytes());
+ int n;
+ byte[] buf = new byte[8000];
+ while ((n=is.read(buf)) != -1) {
+ nbytes += n;
+ }
+ } catch (IOException e) {
+ System.out.println ("read " + nbytes);
+ System.out.println ("size " + size);
+ failed = nbytes >= size;
+ } finally {
+ try {
+ ss.close();
+ if (s != null)
+ s.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+}
+
+class RedirectHandler implements HttpHandler {
+ String root;
+ volatile int count = 0;
+
+ RedirectHandler(String root) {
+ this.root = root;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange t)
+ throws IOException
+ {
+ byte[] buf = new byte[2048];
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+
+ Headers responseHeaders = t.getResponseHeaders();
+
+ if (count++ < 1) {
+ responseHeaders.add("Location", root + "/foo/" + count);
+ } else {
+ responseHeaders.add("Location", SmokeTest.midSizedFilename);
+ }
+ t.sendResponseHeaders(301, -1);
+ t.close();
+ }
+
+ int count() {
+ return count;
+ }
+
+ void reset() {
+ count = 0;
+ }
+}
+
+class RedirectErrorHandler implements HttpHandler {
+ String root;
+ volatile int count = 1;
+
+ RedirectErrorHandler(String root) {
+ this.root = root;
+ }
+
+ synchronized int count() {
+ return count;
+ }
+
+ synchronized void increment() {
+ count++;
+ }
+
+ @Override
+ public synchronized void handle (HttpExchange t)
+ throws IOException
+ {
+ byte[] buf = new byte[2048];
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+
+ Headers map = t.getResponseHeaders();
+ String redirect = root + "/foo/" + Integer.toString(count);
+ increment();
+ map.add("Location", redirect);
+ t.sendResponseHeaders(301, -1);
+ t.close();
+ }
+}
+
+class Util {
+ static byte[] readAll(InputStream is) throws IOException {
+ byte[] buf = new byte[1024];
+ byte[] result = new byte[0];
+
+ while (true) {
+ int n = is.read(buf);
+ if (n > 0) {
+ byte[] b1 = new byte[result.length + n];
+ System.arraycopy(result, 0, b1, 0, result.length);
+ System.arraycopy(buf, 0, b1, result.length, n);
+ result = b1;
+ } else if (n == -1) {
+ return result;
+ }
+ }
+ }
+}
+
+class DelayHandler implements HttpHandler {
+
+ CyclicBarrier bar1 = new CyclicBarrier(2);
+ CyclicBarrier bar2 = new CyclicBarrier(2);
+ CyclicBarrier bar3 = new CyclicBarrier(2);
+
+ CyclicBarrier barrier1() {
+ return bar1;
+ }
+
+ CyclicBarrier barrier2() {
+ return bar2;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange he) throws IOException {
+ byte[] buf = Util.readAll(he.getRequestBody());
+ try {
+ bar1.await();
+ bar2.await();
+ } catch (Exception e) {}
+ he.sendResponseHeaders(200, -1); // will probably fail
+ he.close();
+ }
+
+}
+
+// check for simple hardcoded sequence and use remote address
+// to check.
+// First 4 requests executed in sequence (should use same connection/address)
+// Next 4 requests parallel (should use different addresses)
+// Then send 4 requests in parallel x 100 times (same four addresses used all time)
+
+class KeepAliveHandler implements HttpHandler {
+ volatile int counter = 0;
+
+ HashSet<Integer> portSet = new HashSet<>();
+
+ volatile int[] ports = new int[4];
+
+ void sleep(int n) {
+ try {
+ Thread.sleep(n);
+ } catch (InterruptedException e) {}
+ }
+
+ @Override
+ public synchronized void handle (HttpExchange t)
+ throws IOException
+ {
+ int remotePort = t.getRemoteAddress().getPort();
+ String result = "OK";
+
+ int n = counter++;
+ /// First test
+ if (n < 4) {
+ ports[n] = remotePort;
+ }
+ if (n == 3) {
+ // check all values in ports[] are the same
+ if (ports[0] != ports[1] || ports[2] != ports[3]
+ || ports[0] != ports[2]) {
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ }
+ }
+ // Second test
+ if (n >=4 && n < 8) {
+ // delay to ensure ports are different
+ sleep(500);
+ ports[n-4] = remotePort;
+ }
+ if (n == 7) {
+ // should be all different
+ if (ports[0] == ports[1] || ports[2] == ports[3]
+ || ports[0] == ports[2]) {
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ System.out.printf("Ports: %d, %d, %d, %d\n", ports[0], ports[1], ports[2], ports[3]);
+ }
+ // setup for third test
+ for (int i=0; i<4; i++) {
+ portSet.add(ports[i]);
+ }
+ }
+ // Third test
+ if (n > 7) {
+ // just check that port is one of the ones in portSet
+ if (!portSet.contains(remotePort)) {
+ System.out.println ("UNEXPECTED REMOTE PORT " + remotePort);
+ result = "Error " + Integer.toString(n);
+ System.out.println(result);
+ }
+ }
+ byte[] buf = new byte[2048];
+
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+ t.sendResponseHeaders(200, result.length());
+ OutputStream o = t.getResponseBody();
+ o.write(result.getBytes("US-ASCII"));
+ t.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/SplitResponse.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+//package javaapplication16;
+
+import java.io.IOException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * @test
+ * @bug 8087112
+ * @build Server
+ * @run main/othervm -Djava.net.HttpClient.log=all SplitResponse
+ */
+
+/**
+ * Similar test to QuickResponses except that each byte of the response
+ * is sent in a separate packet, which tests the stability of the implementation
+ * for receiving unusual packet sizes.
+ */
+public class SplitResponse {
+
+ static Server server;
+
+ static String response(String body) {
+ return "HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-length: "
+ + Integer.toString(body.length())
+ + "\r\n\r\n" + body;
+ }
+
+ static final String responses[] = {
+ "Lorem ipsum",
+ "dolor sit amet",
+ "consectetur adipiscing elit, sed do eiusmod tempor",
+ "quis nostrud exercitation ullamco",
+ "laboris nisi",
+ "ut",
+ "aliquip ex ea commodo consequat." +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse" +
+ "cillum dolore eu fugiat nulla pariatur.",
+ "Excepteur sint occaecat cupidatat non proident."
+ };
+
+ public static void main(String[] args) throws Exception {
+ server = new Server(0);
+ URI uri = new URI(server.getURL());
+
+ HttpRequest request;
+ HttpResponse r;
+ CompletableFuture<HttpResponse> cf1;
+
+ for (int i=0; i<responses.length; i++) {
+ cf1 = HttpRequest.create(uri)
+ .GET()
+ .responseAsync();
+ String body = responses[i];
+
+ Server.Connection c = server.activity();
+ sendSplitResponse(response(body), c);
+ r = cf1.get();
+ if (r.statusCode()!= 200)
+ throw new RuntimeException("Failed");
+
+ String rxbody = r.body(HttpResponse.asString());
+ System.out.println("received " + rxbody);
+ if (!rxbody.equals(body))
+ throw new RuntimeException("Failed");
+ c.close();
+ }
+ HttpClient.getDefault().executorService().shutdownNow();
+ System.out.println("OK");
+ }
+
+ // send the response one byte at a time with a small delay between bytes
+ // to ensure that each byte is read in a separate read
+ static void sendSplitResponse(String s, Server.Connection conn) {
+ System.out.println("Sending: ");
+ Thread t = new Thread(() -> {
+ try {
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ String onechar = s.substring(i, i + 1);
+ conn.send(onechar);
+ Thread.sleep(30);
+ }
+ System.out.println("sent");
+ } catch (IOException | InterruptedException e) {
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/TimeoutTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.HttpTimeoutException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @test
+ * @bug 8087112
+ * @run main/othervm TimeoutTest
+ */
+
+public class TimeoutTest {
+
+ static int[] timeouts = {6, 4, 8, 6, 6, 4};
+ static HttpRequest[] rqs = new HttpRequest[timeouts.length];
+ static LinkedBlockingQueue<HttpRequest> queue = new LinkedBlockingQueue<>();
+ static volatile boolean error = false;
+ static ExecutorService executor = Executors.newCachedThreadPool();
+
+ public static void main(String[] args) throws Exception {
+ try {
+ dotest();
+ } finally {
+ HttpClient.getDefault().executorService().shutdownNow();
+ executor.shutdownNow();
+ }
+ }
+ public static void dotest() throws Exception {
+ System.out.println("Test takes over 40 seconds");
+ ServerSocket ss = new ServerSocket(0, 20);
+ int port = ss.getLocalPort();
+
+ URI uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/foo");
+ int i = 0;
+ for (int timeout : timeouts) {
+ HttpRequest request;
+ (request = rqs[i] = HttpRequest.create(uri)
+ .timeout(TimeUnit.SECONDS, timeout)
+ .GET())
+ .responseAsync()
+ .whenComplete((HttpResponse r, Throwable t) -> {
+ if (!(t.getCause() instanceof HttpTimeoutException)) {
+ System.out.println("Wrong exception type:" + t.toString());
+ error = true;
+ }
+ if (t != null) {
+ queue.add(request);
+ }
+ })
+ .thenAccept((HttpResponse r) -> {
+ r.bodyAsync(HttpResponse.ignoreBody());
+ });
+ i++;
+ }
+
+ System.out.println("SUBMITTED");
+
+ checkReturnOrder();
+
+ if (error)
+ throw new RuntimeException("Failed");
+
+ // Repeat blocking in separate threads. Use queue to wait.
+ System.out.println("DOING BLOCKING");
+
+ i = 0;
+ for (int timeout : timeouts) {
+ HttpRequest req = HttpRequest.create(uri)
+ .timeout(TimeUnit.SECONDS, timeout)
+ .GET();
+ rqs[i] = req;
+ executor.execute(() -> {
+ try {
+ req.response().body(HttpResponse.ignoreBody());
+ } catch (HttpTimeoutException e) {
+ queue.offer(req);
+ } catch (IOException | InterruptedException ee) {
+ error = true;
+ }
+ });
+ i++;
+ }
+
+ checkReturnOrder();
+
+ if (error)
+ throw new RuntimeException("Failed");
+ }
+
+ static void checkReturnOrder() throws InterruptedException {
+ // wait for exceptions and check order
+ for (int j = 0; j < timeouts.length; j++) {
+ HttpRequest req = queue.take();
+ switch (j) {
+ case 0:
+ case 1:
+ if (req != rqs[1] && req != rqs[5]) {
+ System.out.printf("Expected 1 or 5. Got %s\n", getRequest(req));
+ throw new RuntimeException("Error");
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ if (req != rqs[0] && req != rqs[3] && req != rqs[4]) {
+ System.out.printf("Expected r1, r4 or r5. Got %s\n", getRequest(req));
+ throw new RuntimeException("Error");
+ }
+ break;
+ case 5:
+ if (req != rqs[2]) {
+ System.out.printf("Expected r3. Got %s\n", getRequest(req));
+ throw new RuntimeException("Error");
+ }
+ }
+ }
+ System.out.println("Return order ok");
+ }
+
+ static String getRequest(HttpRequest req) {
+ for (int i=0; i<rqs.length; i++) {
+ if (req == rqs[i]) {
+ return "[" + Integer.toString(i) + "]";
+ }
+ }
+ return "unknown";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/docs/files/foo.txt Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,1 @@
+This is foo.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/docs/files/notsobigfile.txt Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
+This is a not so big file at all
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/docs/files/smallfile.txt Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,1792 @@
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
+This is a small file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/0.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy: 0
+
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/1.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 1
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/10.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,22 @@
+// Policy 10
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:*";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/11.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 11
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:*";
+ permission java.net.URLPermission "socket://127.0.0.1:27301", "CONNECT";
+};
+
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/12.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 11
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:*";
+ permission java.net.URLPermission "socket://127.0.0.1:27301", "CONNECT";
+};
+
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/15.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,27 @@
+// Policy 11
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:*";
+ permission java.net.URLPermission "socket://127.0.0.1:27301", "CONNECT";
+
+ // Test checks for this explicitly
+ permission java.net.RuntimePermission "foobar";
+};
+
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/2.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 2
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/*", "GET";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/3.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 3
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/redirect/foo.txt", "GET";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/4.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,25 @@
+// Policy 4
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/redirect/foo.txt", "GET";
+ permission java.net.URLPermission "http://127.0.0.1:*/redirect/bar.txt", "GET";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/5.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 5
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/redirect/bar.txt", "GET";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/6.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 6
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "POST";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/7.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 7
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:X-Bar";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/8.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 8
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:X-Foo1,X-Foo,X-Bar";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/9.policy Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,24 @@
+// Policy 9
+grant {
+ // permissions common to all tests
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.util.PropertyPermission "test.classes", "read";
+ permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
+ permission java.net.NetPermission "getDefaultHttpClient";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.util.logging.LoggingPermission "control", "";
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen";
+ permission java.io.FilePermission "${test.src}${/}docs${/}-", "read";
+ permission java.lang.RuntimePermission "createClassLoader";
+
+
+ // permissions specific to this test
+ permission java.net.URLPermission "http://127.0.0.1:*/files/foo.txt", "GET:*";
+};
+
+// For proxy only. Not being tested
+grant codebase "file:${test.classes}/proxydir/-" {
+ permission java.net.SocketPermission "localhost:1024-", "accept,listen,connect";
+ permission java.net.SocketPermission "127.0.0.1:1024-", "connect,resolve";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/httpclient/security/Security.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ */
+
+/**
+ * @test
+ * @bug 8087112
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @compile ../../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../../com/sun/net/httpserver/FileServerHandler.java
+ * @compile ../ProxyServer.java
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 0
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=1.policy Security 1
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=2.policy Security 2
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=3.policy Security 3
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=4.policy Security 4
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=5.policy Security 5
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=6.policy Security 6
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=7.policy Security 7
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=8.policy Security 8
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=9.policy Security 9
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=10.policy Security 10
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=11.policy Security 11
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=12.policy Security 12
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=0.policy Security 13
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=1.policy Security 14
+ * @run main/othervm/secure=java.lang.SecurityManager/policy=15.policy Security 15
+ */
+
+import com.sun.net.httpserver.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.net.*;
+import java.net.http.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.ByteBuffer;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.function.*;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Security checks test
+ */
+public class Security {
+
+ static HttpServer s1 = null;
+ static ExecutorService executor=null;
+ static int port;
+ static HttpClient client;
+ static String httproot, fileuri, fileroot, redirectroot;
+ static List<HttpClient> clients = new LinkedList<>();
+ static URI uri;
+
+ interface Test {
+ public void execute() throws IOException, InterruptedException;
+ }
+
+ static class TestAndResult {
+ Test test;
+ boolean result;
+
+ TestAndResult (Test t, boolean result) {
+ this.test = t;
+ this.result = result;
+ }
+ }
+
+ static TestAndResult test(boolean result, Test t) {
+ return new TestAndResult(t, result);
+ }
+
+ static TestAndResult[] tests;
+ static String testclasses;
+ static File subdir;
+
+ /**
+ * The ProxyServer class is compiled by jtreg, but we want to
+ * move it so it is not on the application claspath. We want to
+ * load it through a separate classloader so that it has a separate
+ * protection domain and security permissions.
+ *
+ * Its permissions are in the second grant block in each policy file
+ */
+ static void setupProxy() throws IOException, ClassNotFoundException, NoSuchMethodException {
+ testclasses = System.getProperty("test.classes");
+ subdir = new File (testclasses, "proxydir");
+ subdir.mkdir();
+
+ movefile("ProxyServer.class");
+ movefile("ProxyServer$Connection.class");
+ movefile("ProxyServer$1.class");
+
+ URL url = subdir.toURL();
+ System.out.println("URL for class loader = " + url);
+ URLClassLoader urlc = new URLClassLoader(new URL[] {url});
+ proxyClass = Class.forName("ProxyServer", true, urlc);
+ proxyConstructor = proxyClass.getConstructor(Integer.class, Boolean.class);
+ }
+
+ static void movefile(String f) throws IOException {
+ Path src = Paths.get(testclasses, f);
+ Path dest = subdir.toPath().resolve(f);
+ if (!dest.toFile().exists()) {
+ System.out.printf("moving %s to %s\n", src.toString(), dest.toString());
+ Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
+ } else {
+ System.out.printf("NOT moving %s to %s\n", src.toString(), dest.toString());
+ }
+ }
+
+ static Object getProxy(int port, boolean b) throws Exception {
+ return proxyConstructor.newInstance(port, b);
+ }
+
+ static Class<?> proxyClass;
+ static Constructor<?> proxyConstructor;
+
+ static void setupTests() {
+ tests = new TestAndResult[]{
+ // (0) policy does not have permission for file. Should fail
+ test(false, () -> { // Policy 0
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (1) policy has permission for file URL
+ test(true, () -> { //Policy 1
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (2) policy has permission for all file URLs under /files
+ test(true, () -> { // Policy 2
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (3) policy has permission for first URL but not redirected URL
+ test(false, () -> { // Policy 3
+ URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (4) policy has permission for both first URL and redirected URL
+ test(true, () -> { // Policy 4
+ URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (5) policy has permission for redirected but not first URL
+ test(false, () -> { // Policy 5
+ URI u = URI.create("http://127.0.0.1:" + port + "/redirect/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (6) policy has permission for file URL, but not method
+ test(false, () -> { //Policy 6
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (7) policy has permission for file URL, method, but not header
+ test(false, () -> { //Policy 7
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .header("X-Foo", "bar")
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (8) policy has permission for file URL, method and header
+ test(true, () -> { //Policy 8
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .header("X-Foo", "bar")
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (9) policy has permission for file URL, method and header
+ test(true, () -> { //Policy 9
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .headers("X-Foo", "bar", "X-Bar", "foo")
+ .GET();
+ HttpResponse response = request.response();
+ }),
+ // (10) policy has permission for destination URL but not for proxy
+ test(false, () -> { //Policy 10
+ directProxyTest(27208, true);
+ }),
+ // (11) policy has permission for both destination URL and proxy
+ test(true, () -> { //Policy 11
+ directProxyTest(27301, true);
+ }),
+ // (12) policy has permission for both destination URL and proxy
+ test(false, () -> { //Policy 11
+ directProxyTest(28301, false);
+ }),
+ // (13) async version of test 0
+ test(false, () -> { // Policy 0
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ try {
+ HttpResponse response = request.responseAsync().get();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof SecurityException) {
+ throw (SecurityException)e.getCause();
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }),
+ // (14) async version of test 1
+ test(true, () -> { //Policy 1
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ try {
+ HttpResponse response = request.responseAsync().get();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof SecurityException) {
+ throw (SecurityException)e.getCause();
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }),
+ // (15) check that user provided unprivileged code running on a worker
+ // thread does not gain ungranted privileges.
+ test(false, () -> { //Policy 12
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = client.request(u)
+ .GET();
+ HttpResponse response = request.response();
+ HttpResponse.BodyProcessor<String> stproc = HttpResponse.asString();
+
+ CompletableFuture<String> cf;
+ cf = response.bodyAsync(new HttpResponse.BodyProcessor<String>() {
+ public void onResponseBodyChunk(ByteBuffer b) throws IOException {
+ // do some mischief here
+ SecurityManager sm = System.getSecurityManager();
+ System.setSecurityManager(null);
+ System.setSecurityManager(sm);
+ // problem if we get this far
+ stproc.onResponseBodyChunk(b);
+ }
+ public String onResponseBodyStart(long contentLength,
+ HttpHeaders responseHeaders,
+ LongConsumer fc) throws IOException {
+
+ SecurityManager sm = System.getSecurityManager();
+ // should succeed.
+ sm.checkPermission(new RuntimePermission("foobar"));
+ return stproc.onResponseBodyStart(contentLength,responseHeaders, fc);
+ }
+ public String onResponseComplete() throws IOException {
+ return stproc.onResponseComplete();
+ }
+ public void onResponseError(Throwable t) {
+ stproc.onResponseError(t);
+ }
+ }
+ );
+ try {
+ System.out.println("Body = " + cf.get());// should not reach here
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof SecurityException) {
+ throw (SecurityException)e.getCause();
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ })
+ };
+ }
+
+ private static void directProxyTest(int proxyPort, boolean samePort) throws IOException, InterruptedException {
+ Object proxy = null;
+ try {
+ proxy = getProxy(proxyPort, true);
+ } catch (IOException e) {
+ System.out.println("Cannot bind. Not running test");
+ throw new SecurityException("test not run");
+ } catch (Exception ee) {
+ throw new RuntimeException(ee);
+ }
+ System.out.println("Proxy port = " + proxyPort);
+ if (!samePort)
+ proxyPort++;
+
+ HttpClient cl = HttpClient.create()
+ .proxy(ProxySelector.of(
+ new InetSocketAddress("127.0.0.1", proxyPort)))
+ .build();
+ clients.add(cl);
+
+ URI u = URI.create("http://127.0.0.1:" + port + "/files/foo.txt");
+ HttpRequest request = cl.request(u)
+ .headers("X-Foo", "bar", "X-Bar", "foo")
+ .GET();
+ HttpResponse response = request.response();
+ }
+
+ static void runtest(Test r, String policy, boolean succeeds) {
+ System.out.println("Using policy file: " + policy);
+ try {
+ r.execute();
+ if (!succeeds) {
+ System.out.println("FAILED: expected security exception");
+ throw new RuntimeException("Failed");
+ }
+ System.out.println (policy + " succeeded as expected");
+ } catch (SecurityException e) {
+ if (succeeds) {
+ System.out.println("FAILED");
+ throw new RuntimeException(e);
+ }
+ System.out.println (policy + " threw exception as expected");
+ } catch (IOException | InterruptedException ee) {
+ throw new RuntimeException(ee);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ initServer();
+ setupProxy();
+ fileroot = System.getProperty ("test.src")+ "/docs";
+ int testnum = Integer.parseInt(args[0]);
+ String policy = args[0];
+
+ client = HttpClient
+ .create()
+ .followRedirects(HttpClient.Redirect.ALWAYS)
+ .build();
+
+ clients.add(HttpClient.getDefault());
+ clients.add(client);
+
+ try {
+ setupTests();
+ TestAndResult tr = tests[testnum];
+ runtest(tr.test, policy, tr.result);
+ } finally {
+ s1.stop(0);
+ //executor.shutdownNow();
+ for (HttpClient client : clients)
+ client.executorService().shutdownNow();
+ }
+ }
+
+ // create Http Server on port range below. So, we can
+ HttpServer createServer() {
+ HttpServer server;
+ for (int i=25800; i<26800; i++) {
+ InetSocketAddress a = new InetSocketAddress(i);
+ try {
+ server = HttpServer.create(a, 0);
+ return server;
+ } catch (IOException e) {}
+ }
+ return null;
+ }
+
+ public static void initServer() throws Exception {
+ Logger logger = Logger.getLogger("com.sun.net.httpserver");
+ ConsoleHandler ch = new ConsoleHandler();
+ logger.setLevel(Level.ALL);
+ ch.setLevel(Level.ALL);
+ logger.addHandler(ch);
+ String root = System.getProperty ("test.src")+ "/docs";
+ InetSocketAddress addr = new InetSocketAddress (0);
+ s1 = HttpServer.create (addr, 0);
+ if (s1 instanceof HttpsServer) {
+ throw new RuntimeException ("should not be httpsserver");
+ }
+ HttpHandler h = new FileServerHandler (root);
+ HttpContext c = s1.createContext ("/files", h);
+
+ HttpHandler h1 = new RedirectHandler ("/redirect");
+ HttpContext c1 = s1.createContext ("/redirect", h1);
+
+ executor = Executors.newCachedThreadPool();
+ s1.setExecutor (executor);
+ s1.start();
+
+ port = s1.getAddress().getPort();
+ System.out.println("HTTP server port = " + port);
+ httproot = "http://127.0.0.1:" + port + "/files/";
+ redirectroot = "http://127.0.0.1:" + port + "/redirect/";
+ uri = new URI(httproot);
+ fileuri = httproot + "foo.txt";
+ }
+
+ static class RedirectHandler implements HttpHandler {
+
+ String root;
+ int count = 0;
+
+ RedirectHandler(String root) {
+ this.root = root;
+ }
+
+ synchronized int count() {
+ return count;
+ }
+
+ synchronized void increment() {
+ count++;
+ }
+
+ @Override
+ public synchronized void handle(HttpExchange t)
+ throws IOException {
+ byte[] buf = new byte[2048];
+ System.out.println("Server: " + t.getRequestURI());
+ try (InputStream is = t.getRequestBody()) {
+ while (is.read(buf) != -1) ;
+ }
+ increment();
+ if (count() == 1) {
+ Headers map = t.getResponseHeaders();
+ String redirect = "/redirect/bar.txt";
+ map.add("Location", redirect);
+ t.sendResponseHeaders(301, -1);
+ t.close();
+ } else {
+ String response = "Hello world";
+ t.sendResponseHeaders(200, response.length());
+ OutputStream os = t.getResponseBody();
+ os.write(response.getBytes(StandardCharsets.ISO_8859_1));
+ t.close();
+ }
+ }
+ }
+}
--- a/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -141,8 +141,11 @@
try {
// check supported options
Set<SocketOption<?>> options = ch.supportedOptions();
+ boolean reuseport = options.contains(SO_REUSEPORT);
if (!options.contains(SO_REUSEADDR))
throw new RuntimeException("SO_REUSEADDR should be supported");
+ if (!options.contains(SO_REUSEPORT) && reuseport)
+ throw new RuntimeException("SO_REUSEPORT should be supported");
if (!options.contains(SO_RCVBUF))
throw new RuntimeException("SO_RCVBUF should be supported");
@@ -156,6 +159,13 @@
checkOption(ch, SO_REUSEADDR, true);
ch.setOption(SO_REUSEADDR, false);
checkOption(ch, SO_REUSEADDR, false);
+
+ if (reuseport) {
+ ch.setOption(SO_REUSEPORT, true);
+ checkOption(ch, SO_REUSEPORT, true);
+ ch.setOption(SO_REUSEPORT, false);
+ checkOption(ch, SO_REUSEPORT, false);
+ }
} finally {
ch.close();
}
--- a/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/channels/AsynchronousSocketChannel/Basic.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -37,6 +37,7 @@
import java.util.concurrent.atomic.*;
import java.io.Closeable;
import java.io.IOException;
+import java.util.Set;
public class Basic {
static final Random rand = new Random();
@@ -165,6 +166,15 @@
// read others (can't check as actual value is implementation dependent)
ch.getOption(SO_RCVBUF);
ch.getOption(SO_SNDBUF);
+
+ Set<SocketOption<?>> options = ch.supportedOptions();
+ boolean reuseport = options.contains(SO_REUSEPORT);
+ if (reuseport) {
+ if (ch.getOption(SO_REUSEPORT))
+ throw new RuntimeException("Default of SO_REUSEPORT should be 'false'");
+ if (!ch.setOption(SO_REUSEPORT, true).getOption(SO_REUSEPORT))
+ throw new RuntimeException("SO_REUSEPORT did not change");
+ }
}
}
--- a/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -50,9 +50,17 @@
// check supported options
Set<SocketOption<?>> options = dc.supportedOptions();
- List<? extends SocketOption<?>> expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF,
- SO_REUSEADDR, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF, IP_MULTICAST_TTL,
- IP_MULTICAST_LOOP);
+ boolean reuseport = options.contains(SO_REUSEPORT);
+ List<? extends SocketOption<?>> expected;
+ if (reuseport) {
+ expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF,
+ SO_REUSEADDR, SO_REUSEPORT, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF,
+ IP_MULTICAST_TTL, IP_MULTICAST_LOOP);
+ } else {
+ expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF,
+ SO_REUSEADDR, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF, IP_MULTICAST_TTL,
+ IP_MULTICAST_LOOP);
+ }
for (SocketOption opt: expected) {
if (!options.contains(opt))
throw new RuntimeException(opt.name() + " should be supported");
@@ -83,7 +91,12 @@
checkOption(dc, SO_REUSEADDR, true);
dc.setOption(SO_REUSEADDR, false);
checkOption(dc, SO_REUSEADDR, false);
-
+ if (reuseport) {
+ dc.setOption(SO_REUSEPORT, true);
+ checkOption(dc, SO_REUSEPORT, true);
+ dc.setOption(SO_REUSEPORT, false);
+ checkOption(dc, SO_REUSEPORT, false);
+ }
// bind socket
dc.bind(new InetSocketAddress(0));
--- a/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -49,8 +49,11 @@
// check supported options
Set<SocketOption<?>> options = ssc.supportedOptions();
+ boolean reuseport = options.contains(SO_REUSEPORT);
if (!options.contains(SO_REUSEADDR))
throw new RuntimeException("SO_REUSEADDR should be supported");
+ if (!options.contains(SO_REUSEPORT) && reuseport)
+ throw new RuntimeException("SO_REUSEPORT should be supported");
if (!options.contains(SO_RCVBUF))
throw new RuntimeException("SO_RCVBUF should be supported");
@@ -64,6 +67,12 @@
checkOption(ssc, SO_REUSEADDR, true);
ssc.setOption(SO_REUSEADDR, false);
checkOption(ssc, SO_REUSEADDR, false);
+ if (reuseport) {
+ ssc.setOption(SO_REUSEPORT, true);
+ checkOption(ssc, SO_REUSEPORT, true);
+ ssc.setOption(SO_REUSEPORT, false);
+ checkOption(ssc, SO_REUSEPORT, false);
+ }
// NullPointerException
try {
--- a/jdk/test/java/nio/charset/coders/BashStreams.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/charset/coders/BashStreams.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
* @summary Stochastic test of charset-based streams
- * @key randomness
+ * @key randomness intermittent
*/
import java.io.*;
--- a/jdk/test/java/nio/file/Files/probeContentType/Basic.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/nio/file/Files/probeContentType/Basic.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -29,8 +29,9 @@
* @run main/othervm Basic
*/
+import java.io.*;
import java.nio.file.*;
-import java.io.*;
+import java.util.stream.Stream;
/**
* Uses Files.probeContentType to probe html file, custom file type, and minimal
@@ -38,6 +39,9 @@
*/
public class Basic {
+ private static final boolean IS_UNIX =
+ ! System.getProperty("os.name").startsWith("Windows");
+
static Path createHtmlFile() throws IOException {
Path file = Files.createTempFile("foo", ".html");
try (OutputStream out = Files.newOutputStream(file)) {
@@ -51,10 +55,61 @@
return Files.createTempFile("red", ".grape");
}
- static void checkContentTypes(String[] extensions, String[] expectedTypes)
+ private static int checkContentTypes(String expected, String actual) {
+ assert expected != null;
+ assert actual != null;
+
+ if (!expected.equals(actual)) {
+ if (IS_UNIX) {
+ Path userMimeTypes =
+ Paths.get(System.getProperty("user.home"), ".mime.types");
+ if (!Files.exists(userMimeTypes)) {
+ System.out.println(userMimeTypes + " does not exist");
+ } else if (!Files.isReadable(userMimeTypes)) {
+ System.out.println(userMimeTypes + " is not readable");
+ } else {
+ System.out.println(userMimeTypes + " contents:");
+ try (Stream<String> lines = Files.lines(userMimeTypes)) {
+ lines.forEach(System.out::println);
+ System.out.println("");
+ } catch (IOException ioe) {
+ System.err.println("Problem reading "
+ + userMimeTypes);
+ }
+ }
+
+ Path etcMimeTypes = Paths.get("/etc/mime.types");
+ if (!Files.exists(etcMimeTypes)) {
+ System.out.println(etcMimeTypes + " does not exist");
+ } else if (!Files.isReadable(etcMimeTypes)) {
+ System.out.println(etcMimeTypes + " is not readable");
+ } else {
+ System.out.println(etcMimeTypes + " contents:");
+ try (Stream<String> lines = Files.lines(etcMimeTypes)) {
+ lines.forEach(System.out::println);
+ System.out.println("");
+ } catch (IOException ioe) {
+ System.err.println("Problem reading "
+ + etcMimeTypes);
+ }
+ }
+ }
+
+ System.err.println("Expected \"" + expected
+ + "\" but obtained \""
+ + actual + "\"");
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int checkOSXContentTypes(String[] extensions, String[] expectedTypes)
throws IOException {
if (extensions.length != expectedTypes.length) {
- throw new IllegalArgumentException("Parameter array lengths differ");
+ throw new IllegalArgumentException
+ ("Parameter array lengths differ");
}
int failures = 0;
@@ -79,12 +134,11 @@
}
}
- if (failures > 0) {
- throw new RuntimeException("Test failed!");
- }
+ return failures;
}
public static void main(String[] args) throws IOException {
+ int failures = 0;
// exercise default file type detector
Path file = createHtmlFile();
@@ -93,8 +147,7 @@
if (type == null) {
System.err.println("Content type cannot be determined - test skipped");
} else {
- if (!type.equals("text/html"))
- throw new RuntimeException("Unexpected type: " + type);
+ failures += checkContentTypes("text/html", type);
}
} finally {
Files.delete(file);
@@ -106,8 +159,7 @@
String type = Files.probeContentType(file);
if (type == null)
throw new RuntimeException("Custom file type detector not installed?");
- if (!type.equals("grape/unknown"))
- throw new RuntimeException("Unexpected type: " + type);
+ failures += checkContentTypes("grape/unknown", type);
} finally {
Files.delete(file);
}
@@ -122,7 +174,11 @@
"image/jpeg", "audio/mpeg", "video/mp4", "application/pdf",
"image/png"
};
- checkContentTypes(extensions, expectedTypes);
+ failures += checkOSXContentTypes(extensions, expectedTypes);
+ }
+
+ if (failures > 0) {
+ throw new RuntimeException("Test failed!");
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarAPI.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test the extended API and the aliasing additions in JarFile that
+ * support multi-release jar files
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarAPI
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static java.util.jar.JarFile.Release;
+import static sun.misc.Version.jdkMajorVersion; // fixme JEP 223 Version
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+public class MultiReleaseJarAPI {
+ String userdir = System.getProperty("user.dir",".");
+ File unversioned = new File(userdir, "unversioned.jar");
+ File multirelease = new File(userdir, "multi-release.jar");
+ File signedmultirelease = new File(userdir, "signed-multi-release.jar");
+ Release[] values = JarFile.Release.values();
+
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
+ creator.compileEntries();
+ creator.buildUnversionedJar();
+ creator.buildMultiReleaseJar();
+ creator.buildSignedMultiReleaseJar();
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ Files.delete(unversioned.toPath());
+ Files.delete(multirelease.toPath());
+ Files.delete(signedmultirelease.toPath());
+ }
+
+ @Test
+ public void isMultiReleaseJar() throws Exception {
+ try (JarFile jf = new JarFile(unversioned)) {
+ Assert.assertFalse(jf.isMultiRelease());
+ }
+
+ try (JarFile jf = new JarFile(multirelease)) {
+ Assert.assertTrue(jf.isMultiRelease());
+ }
+ }
+
+ @Test
+ public void testVersioning() throws Exception {
+ // multi-release jar
+ JarFile jar = new JarFile(multirelease);
+ Assert.assertEquals(Release.BASE, jar.getVersion());
+ jar.close();
+
+ for (Release value : values) {
+ System.err.println("test versioning for Release " + value);
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, value)) {
+ Assert.assertEquals(value, jf.getVersion());
+ }
+ }
+
+ // regular, unversioned, jar
+ for (Release value : values) {
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, value)) {
+ Assert.assertEquals(Release.BASE, jf.getVersion());
+ }
+ }
+
+ // assure that we have a Release object corresponding to the actual runtime version
+ String version = "VERSION_" + jdkMajorVersion();
+ boolean runtimeVersionExists = false;
+ for (Release value : values) {
+ if (version.equals(value.name())) runtimeVersionExists = true;
+ }
+ Assert.assertTrue(runtimeVersionExists);
+ }
+
+ @Test
+ public void testAliasing() throws Exception {
+ for (Release value : values) {
+ System.err.println("test aliasing for Release " + value);
+ String name = value.name();
+ String prefix;
+ if (name.equals("BASE")) {
+ prefix = "";
+ } else if (name.equals("RUNTIME")) {
+ prefix = "META-INF/versions/" + jdkMajorVersion() + "/";
+ } else {
+ prefix = "META-INF/versions/" + name.substring(8) + "/";
+ }
+ // test both multi-release jars
+ readAndCompare(multirelease, value, "README", prefix + "README");
+ readAndCompare(multirelease, value, "version/Version.class", prefix + "version/Version.class");
+ // and signed multi-release jars
+ readAndCompare(signedmultirelease, value, "README", prefix + "README");
+ readAndCompare(signedmultirelease, value, "version/Version.class", prefix + "version/Version.class");
+ }
+ }
+
+ private void readAndCompare(File jar, Release version, String name, String realName) throws Exception {
+ byte[] baseBytes;
+ byte[] versionedBytes;
+ try (JarFile jf = new JarFile(jar, true, ZipFile.OPEN_READ, Release.BASE)) {
+ ZipEntry ze = jf.getEntry(realName);
+ try (InputStream is = jf.getInputStream(ze)) {
+ baseBytes = is.readAllBytes();
+ }
+ }
+ assert baseBytes.length > 0;
+
+ try (JarFile jf = new JarFile(jar, true, ZipFile.OPEN_READ, version)) {
+ ZipEntry ze = jf.getEntry(name);
+ try (InputStream is = jf.getInputStream(ze)) {
+ versionedBytes = is.readAllBytes();
+ }
+ }
+ assert versionedBytes.length > 0;
+
+ Assert.assertTrue(Arrays.equals(baseBytes, versionedBytes));
+ }
+
+ @Test
+ public void testNames() throws Exception {
+ String rname = "version/Version.class";
+ String vname = "META-INF/versions/9/version/Version.class";
+ ZipEntry ze1;
+ ZipEntry ze2;
+ try (JarFile jf = new JarFile(multirelease)) {
+ ze1 = jf.getEntry(vname);
+ }
+ Assert.assertEquals(ze1.getName(), vname);
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.VERSION_9)) {
+ ze2 = jf.getEntry(rname);
+ }
+ Assert.assertEquals(ze2.getName(), rname);
+ Assert.assertNotEquals(ze1.getName(), ze2.getName());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarHttpProperties.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test the System properties for JarFile that support multi-release jar files
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarHttpProperties
+ * @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarHttpProperties
+ */
+
+import com.sun.net.httpserver.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class MultiReleaseJarHttpProperties extends MultiReleaseJarProperties {
+ private SimpleHttpServer server;
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ server = new SimpleHttpServer();
+ server.start();
+ super.initialize();
+ }
+
+ @Override
+ protected void initializeClassLoader() throws Exception {
+ URL[] urls = new URL[]{
+ new URL("http://localhost:" + server.getPort() + "/multi-release-jar")
+ };
+ cldr = new URLClassLoader(urls);
+ // load any class, Main is convenient and in the root entries
+ rootClass = cldr.loadClass("version.Main");
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ // Windows requires server to stop before file is deleted
+ if (server != null)
+ server.stop();
+ super.close();
+ }
+
+ /*
+ * jdk.util.jar.enableMultiRelease=force is a no-op for URLClassLoader
+ */
+
+ @Test
+ public void testURLClassLoader() throws Throwable {
+ Class<?> vcls = cldr.loadClass("version.Version");
+ invokeMethod(vcls, rtVersion);
+ }
+
+ @Test
+ public void testGetResourceAsStream() throws Exception {
+ String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
+ // use rootClass as a base for getting resources
+ getResourceAsStream(rootClass, resource);
+ }
+
+ @Test
+ public void testGetResource() throws Exception {
+ String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
+ // use rootClass as a base for getting resources
+ getResource(rootClass, resource);
+ }
+}
+
+/**
+ * Extremely simple server that only performs one task. The server listens for
+ * requests on the ephemeral port. If it sees a request that begins with
+ * "/multi-release-jar", it consumes the request and returns a stream of bytes
+ * representing the jar file multi-release.jar found in "userdir".
+ */
+class SimpleHttpServer {
+ private static final String userdir = System.getProperty("user.dir", ".");
+ private static final Path multirelease = Paths.get(userdir, "multi-release.jar");
+
+ private final HttpServer server;
+
+ public SimpleHttpServer() throws IOException {
+ server = HttpServer.create();
+ }
+
+ public void start() throws IOException {
+ server.bind(new InetSocketAddress(0), 0);
+ server.createContext("/multi-release-jar", t -> {
+ try (InputStream is = t.getRequestBody()) {
+ is.readAllBytes(); // probably not necessary to consume request
+ byte[] bytes = Files.readAllBytes(multirelease);
+ t.sendResponseHeaders(200, bytes.length);
+ try (OutputStream os = t.getResponseBody()) {
+ os.write(bytes);
+ }
+ }
+ });
+ server.setExecutor(null); // creates a default executor
+ server.start();
+ }
+
+ public void stop() {
+ server.stop(0);
+ }
+
+ int getPort() {
+ return server.getAddress().getPort();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarIterators.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test the extended API and the aliasing additions in JarFile that
+ * support multi-release jar files
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarIterators
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
+
+import static java.util.jar.JarFile.Release;
+import static sun.misc.Version.jdkMajorVersion; // fixme JEP 223 Version
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+public class MultiReleaseJarIterators {
+ String userdir = System.getProperty("user.dir", ".");
+ File unversioned = new File(userdir, "unversioned.jar");
+ File multirelease = new File(userdir, "multi-release.jar");
+ Map<String,JarEntry> uvEntries = new HashMap<>();
+ Map<String,JarEntry> mrEntries = new HashMap<>();
+ Map<String,JarEntry> baseEntries = new HashMap<>();
+ Map<String,JarEntry> v9Entries = new HashMap<>();
+ Map<String, JarEntry> v10Entries = new HashMap<>();
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
+ creator.compileEntries();
+ creator.buildUnversionedJar();
+ creator.buildMultiReleaseJar();
+
+ try (JarFile jf = new JarFile(multirelease)) {
+ for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements(); ) {
+ JarEntry je = e.nextElement();
+ String name = je.getName();
+ mrEntries.put(name, je);
+ if (name.startsWith("META-INF/versions/")) {
+ if (name.startsWith("META-INF/versions/9/")) {
+ v9Entries.put(name.substring(20), je);
+ } else if (name.startsWith("META-INF/versions/10/")) {
+ v10Entries.put(name.substring(21), je);
+ }
+ } else {
+ baseEntries.put(name, je);
+ }
+ }
+ }
+ Assert.assertEquals(mrEntries.size(), 14);
+ Assert.assertEquals(baseEntries.size(), 6);
+ Assert.assertEquals(v9Entries.size(), 5);
+ Assert.assertEquals(v10Entries.size(), 3);
+
+ try (JarFile jf = new JarFile(unversioned)) {
+ jf.entries().asIterator().forEachRemaining(je -> uvEntries.put(je.getName(), je));
+ }
+ Assert.assertEquals(uvEntries.size(), 6);
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ Files.delete(unversioned.toPath());
+ Files.delete(multirelease.toPath());
+ }
+
+ @Test
+ public void testMultiReleaseJar() throws IOException {
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ)) {
+ testEnumeration(jf, mrEntries);
+ testStream(jf, mrEntries);
+ }
+
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.BASE)) {
+ testEnumeration(jf, baseEntries);
+ testStream(jf, baseEntries);
+ }
+
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.VERSION_9)) {
+ testEnumeration(jf, v9Entries);
+ testStream(jf, v9Entries);
+ }
+
+ try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.RUNTIME)) {
+ Map<String,JarEntry> expectedEntries;
+ switch (jdkMajorVersion()) {
+ case 9:
+ expectedEntries = v9Entries;
+ break;
+ case 10: // won't get here until JDK 10
+ expectedEntries = v10Entries;
+ break;
+ default:
+ expectedEntries = baseEntries;
+ break;
+ }
+
+ testEnumeration(jf, expectedEntries);
+ testStream(jf, expectedEntries);
+ }
+ }
+
+ @Test
+ public void testUnversionedJar() throws IOException {
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ)) {
+ testEnumeration(jf, uvEntries);
+ testStream(jf, uvEntries);
+ }
+
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Release.BASE)) {
+ testEnumeration(jf, uvEntries);
+ testStream(jf, uvEntries);
+ }
+
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Release.VERSION_9)) {
+ testEnumeration(jf, uvEntries);
+ testStream(jf, uvEntries);
+ }
+
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Release.RUNTIME)) {
+ testEnumeration(jf, uvEntries);
+ testStream(jf, uvEntries);
+ }
+ }
+
+ private void testEnumeration(JarFile jf, Map<String,JarEntry> expectedEntries) {
+ Map<String, JarEntry> actualEntries = new HashMap<>();
+ for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements(); ) {
+ JarEntry je = e.nextElement();
+ actualEntries.put(je.getName(), je);
+ }
+
+ testEntries(jf, actualEntries, expectedEntries);
+ }
+
+
+ private void testStream(JarFile jf, Map<String,JarEntry> expectedEntries) {
+ Map<String,JarEntry> actualEntries = jf.stream().collect(Collectors.toMap(je -> je.getName(), je -> je));
+
+ testEntries(jf, actualEntries, expectedEntries);
+ }
+
+ private void testEntries(JarFile jf, Map<String,JarEntry> actualEntries, Map<String,JarEntry> expectedEntries) {
+ /* For multi-release jar files constructed with a Release object,
+ * actualEntries contain versionedEntries that are considered part of the
+ * public API. They have a 1-1 correspondence with baseEntries,
+ * so entries that are not part of the public API won't be present,
+ * i.e. those entries with a name that starts with version/PackagePrivate
+ * in this particular jar file (multi-release.jar)
+ */
+
+ Map<String,JarEntry> entries;
+ if (expectedEntries == mrEntries) {
+ Assert.assertEquals(actualEntries.size(), mrEntries.size());
+ entries = mrEntries;
+ } else if (expectedEntries == uvEntries) {
+ Assert.assertEquals(actualEntries.size(), uvEntries.size());
+ entries = uvEntries;
+ } else {
+ Assert.assertEquals(actualEntries.size(), baseEntries.size()); // this is correct
+ entries = baseEntries;
+ }
+
+ entries.keySet().forEach(name -> {
+ JarEntry ee = expectedEntries.get(name);
+ if (ee == null) ee = entries.get(name);
+ JarEntry ae = actualEntries.get(name);
+ try {
+ compare(jf, ae, ee);
+ } catch (IOException x) {
+ throw new RuntimeException(x);
+ }
+ });
+ }
+
+ private void compare(JarFile jf, JarEntry actual, JarEntry expected) throws IOException {
+ byte[] abytes;
+ byte[] ebytes;
+
+ try (InputStream is = jf.getInputStream(actual)) {
+ abytes = is.readAllBytes();
+ }
+
+ try (InputStream is = jf.getInputStream(expected)) {
+ ebytes = is.readAllBytes();
+ }
+
+ Assert.assertEquals(abytes, ebytes);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarProperties.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test the System properties for JarFile that support multi-release jar files
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=0 MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=100 MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=8 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=9 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.version=10 -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarProperties
+ * @run testng/othervm -Djdk.util.jar.enableMultiRelease=force MultiReleaseJarProperties
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import static sun.misc.Version.jdkMajorVersion; // fixme JEP 223 Version
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class MultiReleaseJarProperties {
+ final static int ROOTVERSION = 8; // magic number from knowledge of internals
+ final static String userdir = System.getProperty("user.dir", ".");
+ final static File multirelease = new File(userdir, "multi-release.jar");
+ protected int rtVersion;
+ boolean force;
+ protected ClassLoader cldr;
+ protected Class<?> rootClass;
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
+ creator.compileEntries();
+ creator.buildMultiReleaseJar();
+
+ rtVersion = Integer.getInteger("jdk.util.jar.version", jdkMajorVersion());
+ String mrprop = System.getProperty("jdk.util.jar.enableMultiRelease", "");
+ if (mrprop.equals("false")) {
+ rtVersion = ROOTVERSION;
+ } else if (rtVersion < ROOTVERSION) {
+ rtVersion = ROOTVERSION;
+ } else if (rtVersion > jdkMajorVersion()) {
+ rtVersion = jdkMajorVersion();
+ }
+ force = mrprop.equals("force");
+
+ initializeClassLoader();
+ }
+
+ protected void initializeClassLoader() throws Exception {
+ URL[] urls = new URL[]{multirelease.toURI().toURL()};
+ cldr = new URLClassLoader(urls);
+ // load any class, Main is convenient and in the root entries
+ rootClass = cldr.loadClass("version.Main");
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ ((URLClassLoader)cldr).close();
+ Files.delete(multirelease.toPath());
+ }
+
+ /*
+ * jdk.util.jar.enableMultiRelease=force is a no-op for URLClassLoader
+ */
+ @Test
+ public void testURLClassLoader() throws Throwable {
+ Class<?> vcls = cldr.loadClass("version.Version");
+ invokeMethod(vcls, rtVersion);
+ }
+
+ protected void invokeMethod(Class<?> vcls, int expected) throws Throwable {
+ MethodType mt = MethodType.methodType(int.class);
+ MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt);
+ Assert.assertEquals(expected, (int) mh.invoke(vcls.newInstance()));
+ }
+
+ /*
+ * jdk.util.jar.enableMultiRelease=force should affect a custom class loader
+ */
+ @Test
+ public void testClassLoader() throws Throwable {
+ try (JarFile jf = new JarFile(multirelease)) { // do not set runtime versioning
+ ClassLoader cldr = new CustomClassLoader(jf);
+ Class<?> vcls = cldr.loadClass("version.Version");
+ if (rtVersion == 9) {
+ try {
+ cldr.loadClass("version.PackagePrivate");
+ } catch (ClassNotFoundException x) {
+ if (force) throw x;
+ }
+ }
+ invokeMethod(vcls, force ? rtVersion : ROOTVERSION);
+ }
+ }
+
+ private static class CustomClassLoader extends ClassLoader {
+ private final JarFile jf;
+
+ CustomClassLoader(JarFile jf) throws Exception {
+ super(null);
+ this.jf = jf;
+ }
+
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ try {
+ byte[] b;
+ String entryName = name.replace(".", "/") + ".class";
+ JarEntry je = jf.getJarEntry(entryName);
+ if (je != null) {
+ try (InputStream is = jf.getInputStream(je)) {
+ b = new byte[(int) je.getSize()];
+ is.read(b);
+ }
+ return defineClass(name, b, 0, b.length);
+ }
+ throw new ClassNotFoundException(name);
+ } catch (IOException x) {
+ throw new ClassNotFoundException(x.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testGetResourceAsStream() throws Exception {
+ String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
+ // use fileRootClass as a base for getting resources
+ getResourceAsStream(rootClass, resource);
+ }
+
+ protected void getResourceAsStream(Class<?> rootClass, String resource) throws Exception {
+ try (InputStream is = rootClass.getResourceAsStream(resource)) {
+ byte[] bytes = is.readAllBytes();
+ resource = new String(bytes);
+ }
+ String match = "return " + rtVersion + ";";
+ Assert.assertTrue(resource.contains(match));
+ }
+
+ @Test
+ public void testGetResource() throws Exception {
+ String resource = rtVersion == 9 ? "/version/PackagePrivate.java" : "/version/Version.java";
+ // use rootClass as a base for getting resources
+ getResource(rootClass, resource);
+ }
+
+ protected void getResource(Class<?> rootClass, String resource) throws Exception {
+ URL url = rootClass.getResource(resource);
+ try (InputStream is = url.openStream()) {
+ byte[] bytes = is.readAllBytes();
+ resource = new String(bytes);
+ }
+ String match = "return " + rtVersion + ";";
+ Assert.assertTrue(resource.contains(match));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarSecurity.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test potential security related issues
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarSecurity
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.security.CodeSigner;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipFile;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class MultiReleaseJarSecurity {
+ String userdir = System.getProperty("user.dir",".");
+ File multirelease = new File(userdir, "multi-release.jar");
+ File signedmultirelease = new File(userdir, "signed-multi-release.jar");
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
+ creator.compileEntries();
+ creator.buildMultiReleaseJar();
+ creator.buildSignedMultiReleaseJar();
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ Files.delete(multirelease.toPath());
+ Files.delete(signedmultirelease.toPath());
+ }
+
+ @Test
+ public void testCertsAndSigners() throws IOException {
+ try (JarFile jf = new JarFile(signedmultirelease, true, ZipFile.OPEN_READ, JarFile.Release.RUNTIME)) {
+ int version = sun.misc.Version.jdkMajorVersion(); // fixme JEP 223 Version
+ CertsAndSigners vcas = new CertsAndSigners(jf, jf.getJarEntry("version/Version.class"));
+ CertsAndSigners rcas = new CertsAndSigners(jf, jf.getJarEntry("META-INF/versions/" + version + "/version/Version.class"));
+ Assert.assertTrue(Arrays.equals(rcas.getCertificates(), vcas.getCertificates()));
+ Assert.assertTrue(Arrays.equals(rcas.getCodeSigners(), vcas.getCodeSigners()));
+ }
+ }
+
+ private static class CertsAndSigners {
+ final private JarFile jf;
+ final private JarEntry je;
+ private boolean readComplete;
+
+ CertsAndSigners(JarFile jf, JarEntry je) {
+ this.jf = jf;
+ this.je = je;
+ }
+
+ Certificate[] getCertificates() throws IOException {
+ readEntry();
+ return je.getCertificates();
+ }
+
+ CodeSigner[] getCodeSigners() throws IOException {
+ readEntry();
+ return je.getCodeSigners();
+ }
+
+ private void readEntry() throws IOException {
+ if (!readComplete) {
+ try (InputStream is = jf.getInputStream(je)) {
+ is.readAllBytes();
+ }
+ readComplete = true;
+ }
+ }
+ }
+}
--- a/jdk/test/java/util/logging/LogManagerAppContextDeadlock.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/java/util/logging/LogManagerAppContextDeadlock.java Wed Jul 05 21:23:38 2017 +0200
@@ -141,6 +141,7 @@
t1.setDaemon(true);
t1.start();
Thread t2 = new Thread() {
+ public Object logger;
public void run() {
sem3.release();
try {
@@ -151,7 +152,10 @@
Thread.interrupted();
}
System.out.println("Logger.getLogger(name).info(name)");
- Logger.getLogger(test.name());//.info(name);
+ // stick the logger in an instance variable to prevent it
+ // from being garbage collected before the main thread
+ // calls LogManager.getLogger() below.
+ logger = Logger.getLogger(test.name());//.info(name);
System.out.println("Done: Logger.getLogger(name).info(name)");
}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/crypto/JceSecurity/FinalRestricted.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, 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 8149417
+ * @summary Use final restricted flag
+ */
+
+import java.security.*;
+import java.lang.reflect.*;
+
+public class FinalRestricted {
+
+ public static void main(String[] args) throws Exception {
+
+ int modifiers = Class.forName("javax.crypto.JceSecurity")
+ .getDeclaredField("isRestricted").getModifiers();
+ if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers) &&
+ Modifier.isPrivate(modifiers))) {
+ throw new Exception("JceSecurity.isRestricted is not " +
+ "a private static final field!");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/Stapling/StapleEnableProps.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8145854
+ * @summary SSLContextImpl.statusResponseManager should be generated if required
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @run main/othervm StapleEnableProps
+ */
+
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.io.*;
+import java.math.BigInteger;
+import java.security.*;
+import java.nio.*;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+
+public class StapleEnableProps {
+
+ /*
+ * Enables logging of the SSLEngine operations.
+ */
+ private static final boolean logging = true;
+
+ /*
+ * Enables the JSSE system debugging system property:
+ *
+ * -Djavax.net.debug=all
+ *
+ * This gives a lot of low-level information about operations underway,
+ * including specific handshake messages, and might be best examined
+ * after gaining some familiarity with this application.
+ */
+ private static final boolean debug = false;
+
+ // These two ByteBuffer references will be used to hang onto ClientHello
+ // messages with and without the status_request[_v2] extensions. These
+ // will be used in the server-side stapling tests.
+ private static ByteBuffer cHelloStaple;
+ private static ByteBuffer cHelloNoStaple;
+
+ // The following items are used to set up the keystores.
+ private static final String passwd = "passphrase";
+ private static final String ROOT_ALIAS = "root";
+ private static final String INT_ALIAS = "intermediate";
+ private static final String SSL_ALIAS = "ssl";
+
+ // PKI components we will need for this test
+ private static KeyManagerFactory kmf;
+ private static TrustManagerFactory tmf;
+ private static KeyStore rootKeystore; // Root CA Keystore
+ private static KeyStore intKeystore; // Intermediate CA Keystore
+ private static KeyStore serverKeystore; // SSL Server Keystore
+ private static KeyStore trustStore; // SSL Client trust store
+ private static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder
+ private static int rootOcspPort; // Port for root OCSP
+ private static SimpleOCSPServer intOcsp; // Intermediate CA OCSP server
+ private static int intOcspPort; // Port for intermediate OCSP
+
+ // A few helpful TLS definitions to make it easier
+ private static final int HELLO_EXT_STATUS_REQ = 5;
+ private static final int HELLO_EXT_STATUS_REQ_V2 = 17;
+
+ /*
+ * Main entry point for this test.
+ */
+ public static void main(String args[]) throws Exception {
+ if (debug) {
+ System.setProperty("javax.net.debug", "ssl");
+ }
+
+ // Create the PKI we will use for the test and start the OCSP servers
+ createPKI();
+
+ // Set up the KeyManagerFactory and TrustManagerFactory
+ kmf = KeyManagerFactory.getInstance("PKIX");
+ kmf.init(serverKeystore, passwd.toCharArray());
+ tmf = TrustManagerFactory.getInstance("PKIX");
+ tmf.init(trustStore);
+
+ // Run the client and server property tests
+ testClientProp();
+ testServerProp();
+
+ }
+
+ private static void testClientProp() throws Exception {
+ SSLEngineResult clientResult;
+
+ // Test with the client-side enable property set to true
+ System.out.println("=========================================");
+ System.out.println("Client Test 1: " +
+ "jdk.tls.client.enableStatusRequestExtension = true");
+ System.out.println("=========================================");
+
+ System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+ "true");
+ SSLContext ctxStaple = SSLContext.getInstance("TLS");
+ ctxStaple.init(null, tmf.getTrustManagers(), null);
+ SSLEngine engine = ctxStaple.createSSLEngine();
+ engine.setUseClientMode(true);
+ SSLSession session = engine.getSession();
+ ByteBuffer clientOut = ByteBuffer.wrap("I'm a Client".getBytes());
+ ByteBuffer cTOs =
+ ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+ // Create and check the ClientHello message
+ clientResult = engine.wrap(clientOut, cTOs);
+ log("client wrap: ", clientResult);
+ if (clientResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Client wrap got status: " +
+ clientResult.getStatus());
+ }
+ cTOs.flip();
+ System.out.println(dumpHexBytes(cTOs));
+ checkClientHello(cTOs, true, true);
+ cHelloStaple = cTOs;
+
+ // Test with the property set to false
+ System.out.println("=========================================");
+ System.out.println("Client Test 2: " +
+ "jdk.tls.client.enableStatusRequestExtension = false");
+ System.out.println("=========================================");
+
+ System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+ "false");
+ SSLContext ctxNoStaple = SSLContext.getInstance("TLS");
+ ctxNoStaple.init(null, tmf.getTrustManagers(), null);
+ engine = ctxNoStaple.createSSLEngine();
+ engine.setUseClientMode(true);
+ session = engine.getSession();
+ cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+ // Create and check the ClientHello message
+ clientResult = engine.wrap(clientOut, cTOs);
+ log("client wrap: ", clientResult);
+ if (clientResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Client wrap got status: " +
+ clientResult.getStatus());
+ }
+ cTOs.flip();
+ System.out.println(dumpHexBytes(cTOs));
+ checkClientHello(cTOs, false, false);
+ cHelloNoStaple = cTOs;
+ }
+
+ private static void testServerProp() throws Exception {
+ SSLEngineResult serverResult;
+ HandshakeStatus hsStat;
+
+ // Test with the server-side enable property set to true
+ System.out.println("=========================================");
+ System.out.println("Server Test 1: " +
+ "jdk.tls.server.enableStatusRequestExtension = true");
+ System.out.println("=========================================");
+
+ System.setProperty("jdk.tls.server.enableStatusRequestExtension",
+ "true");
+ SSLContext ctxStaple = SSLContext.getInstance("TLS");
+ ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ SSLEngine engine = ctxStaple.createSSLEngine();
+ engine.setUseClientMode(false);
+ SSLSession session = engine.getSession();
+ ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes());
+ ByteBuffer serverIn =
+ ByteBuffer.allocate(session.getApplicationBufferSize() + 50);
+ ByteBuffer sTOc =
+ ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+ // Consume the client hello
+ serverResult = engine.unwrap(cHelloStaple, serverIn);
+ log("server unwrap: ", serverResult);
+ if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Server unwrap got status: " +
+ serverResult.getStatus());
+ } else if (serverResult.getHandshakeStatus() !=
+ SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ throw new SSLException("Server unwrap expected NEED_TASK, got: " +
+ serverResult.getHandshakeStatus());
+ }
+ runDelegatedTasks(serverResult, engine);
+ if (engine.getHandshakeStatus() !=
+ SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ throw new SSLException("Expected NEED_WRAP, got: " +
+ engine.getHandshakeStatus());
+ }
+
+ // Generate a TLS record with the ServerHello
+ serverResult = engine.wrap(serverOut, sTOc);
+ log("client wrap: ", serverResult);
+ if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Client wrap got status: " +
+ serverResult.getStatus());
+ }
+ sTOc.flip();
+ System.out.println(dumpHexBytes(sTOc));
+ checkServerHello(sTOc, false, true);
+
+ // Flip the client hello so we can reuse it in the next test.
+ cHelloStaple.flip();
+
+ // Test with the server-side enable property set to false
+ System.out.println("=========================================");
+ System.out.println("Server Test 2: " +
+ "jdk.tls.server.enableStatusRequestExtension = false");
+ System.out.println("=========================================");
+
+ System.setProperty("jdk.tls.server.enableStatusRequestExtension",
+ "false");
+ SSLContext ctxNoStaple = SSLContext.getInstance("TLS");
+ ctxNoStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ engine = ctxNoStaple.createSSLEngine();
+ engine.setUseClientMode(false);
+ session = engine.getSession();
+ serverIn = ByteBuffer.allocate(session.getApplicationBufferSize() + 50);
+ sTOc = ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+ // Consume the client hello
+ serverResult = engine.unwrap(cHelloStaple, serverIn);
+ log("server unwrap: ", serverResult);
+ if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Server unwrap got status: " +
+ serverResult.getStatus());
+ } else if (serverResult.getHandshakeStatus() !=
+ SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ throw new SSLException("Server unwrap expected NEED_TASK, got: " +
+ serverResult.getHandshakeStatus());
+ }
+ runDelegatedTasks(serverResult, engine);
+ if (engine.getHandshakeStatus() !=
+ SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ throw new SSLException("Expected NEED_WRAP, got: " +
+ engine.getHandshakeStatus());
+ }
+
+ // Generate a TLS record with the ServerHello
+ serverResult = engine.wrap(serverOut, sTOc);
+ log("client wrap: ", serverResult);
+ if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
+ throw new SSLException("Client wrap got status: " +
+ serverResult.getStatus());
+ }
+ sTOc.flip();
+ System.out.println(dumpHexBytes(sTOc));
+ checkServerHello(sTOc, false, false);
+ }
+
+ /*
+ * If the result indicates that we have outstanding tasks to do,
+ * go ahead and run them in this thread.
+ */
+ private static void runDelegatedTasks(SSLEngineResult result,
+ SSLEngine engine) throws Exception {
+
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ log("\trunning delegated task...");
+ runnable.run();
+ }
+ HandshakeStatus hsStatus = engine.getHandshakeStatus();
+ if (hsStatus == HandshakeStatus.NEED_TASK) {
+ throw new Exception(
+ "handshake shouldn't need additional tasks");
+ }
+ log("\tnew HandshakeStatus: " + hsStatus);
+ }
+ }
+
+ private static void log(String str, SSLEngineResult result) {
+ if (!logging) {
+ return;
+ }
+ HandshakeStatus hsStatus = result.getHandshakeStatus();
+ log(str +
+ result.getStatus() + "/" + hsStatus + ", " +
+ result.bytesConsumed() + "/" + result.bytesProduced() +
+ " bytes");
+ if (hsStatus == HandshakeStatus.FINISHED) {
+ log("\t...ready for application data");
+ }
+ }
+
+ private static void log(String str) {
+ if (logging) {
+ System.out.println(str);
+ }
+ }
+
+ /**
+ * Dump a ByteBuffer as a hexdump to stdout. The dumping routine will
+ * start at the current position of the buffer and run to its limit.
+ * After completing the dump, the position will be returned to its
+ * starting point.
+ *
+ * @param data the ByteBuffer to dump to stdout.
+ *
+ * @return the hexdump of the byte array.
+ */
+ private static String dumpHexBytes(ByteBuffer data) {
+ StringBuilder sb = new StringBuilder();
+ if (data != null) {
+ int i = 0;
+ data.mark();
+ while (data.hasRemaining()) {
+ if (i % 16 == 0 && i != 0) {
+ sb.append("\n");
+ }
+ sb.append(String.format("%02X ", data.get()));
+ i++;
+ }
+ data.reset();
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Tests the ClientHello for the presence (or not) of the status_request
+ * and status_request_v2 hello extensions. It is assumed that the provided
+ * ByteBuffer has its position set at the first byte of the TLS record
+ * containing the ClientHello and contains the entire hello message. Upon
+ * successful completion of this method the ByteBuffer will have its
+ * position reset to the initial offset in the buffer. If an exception is
+ * thrown the position at the time of the exception will be preserved.
+ *
+ * @param data the ByteBuffer containing the ClientHello bytes
+ * @param statReqPresent true if the status_request hello extension should
+ * be present.
+ * @param statReqV2Present true if the status_request_v2 hello extension
+ * should be present.
+ *
+ * @throws SSLException if the presence or lack of either the
+ * status_request or status_request_v2 extensions is inconsistent with
+ * the expected settings in the statReqPresent or statReqV2Present
+ * parameters.
+ */
+ private static void checkClientHello(ByteBuffer data,
+ boolean statReqPresent, boolean statReqV2Present)
+ throws SSLException {
+ boolean hasV1 = false;
+ boolean hasV2 = false;
+ Objects.requireNonNull(data);
+ data.mark();
+
+ // Process the TLS record header
+ int type = Byte.toUnsignedInt(data.get());
+ int ver_major = Byte.toUnsignedInt(data.get());
+ int ver_minor = Byte.toUnsignedInt(data.get());
+ int recLen = Short.toUnsignedInt(data.getShort());
+
+ // Simple sanity checks
+ if (type != 22) {
+ throw new SSLException("Not a handshake: Type = " + type);
+ } else if (recLen > data.remaining()) {
+ throw new SSLException("Incomplete record in buffer: " +
+ "Record length = " + recLen + ", Remaining = " +
+ data.remaining());
+ }
+
+ // Grab the handshake message header.
+ int msgHdr = data.getInt();
+ int msgType = (msgHdr >> 24) & 0x000000FF;
+ int msgLen = msgHdr & 0x00FFFFFF;
+
+ // More simple sanity checks
+ if (msgType != 1) {
+ throw new SSLException("Not a ClientHello: Type = " + msgType);
+ }
+
+ // Skip over the protocol version and client random
+ data.position(data.position() + 34);
+
+ // Jump past the session ID (if there is one)
+ int sessLen = Byte.toUnsignedInt(data.get());
+ if (sessLen != 0) {
+ data.position(data.position() + sessLen);
+ }
+
+ // Jump past the cipher suites
+ int csLen = Short.toUnsignedInt(data.getShort());
+ if (csLen != 0) {
+ data.position(data.position() + csLen);
+ }
+
+ // ...and the compression
+ int compLen = Byte.toUnsignedInt(data.get());
+ if (compLen != 0) {
+ data.position(data.position() + compLen);
+ }
+
+ // Now for the fun part. Go through the extensions and look
+ // for the two status request exts.
+ int extsLen = Short.toUnsignedInt(data.getShort());
+ while (data.hasRemaining()) {
+ int extType = Short.toUnsignedInt(data.getShort());
+ int extLen = Short.toUnsignedInt(data.getShort());
+ hasV1 |= (extType == HELLO_EXT_STATUS_REQ);
+ hasV2 |= (extType == HELLO_EXT_STATUS_REQ_V2);
+ data.position(data.position() + extLen);
+ }
+
+ if (hasV1 != statReqPresent) {
+ throw new SSLException("The status_request extension is " +
+ "inconsistent with the expected result: expected = " +
+ statReqPresent + ", actual = " + hasV1);
+ } else if (hasV2 != statReqV2Present) {
+ throw new SSLException("The status_request_v2 extension is " +
+ "inconsistent with the expected result: expected = " +
+ statReqV2Present + ", actual = " + hasV2);
+ }
+
+ // We should be at the end of the ClientHello
+ data.reset();
+ }
+
+ /**
+ * Tests the ServerHello for the presence (or not) of the status_request
+ * or status_request_v2 hello extension. It is assumed that the provided
+ * ByteBuffer has its position set at the first byte of the TLS record
+ * containing the ServerHello and contains the entire hello message. Upon
+ * successful completion of this method the ByteBuffer will have its
+ * position reset to the initial offset in the buffer. If an exception is
+ * thrown the position at the time of the exception will be preserved.
+ *
+ * @param statReqPresent true if the status_request hello extension should
+ * be present.
+ * @param statReqV2Present true if the status_request_v2 hello extension
+ * should be present.
+ *
+ * @throws SSLException if the presence or lack of either the
+ * status_request or status_request_v2 extensions is inconsistent with
+ * the expected settings in the statReqPresent or statReqV2Present
+ * parameters.
+ */
+ private static void checkServerHello(ByteBuffer data,
+ boolean statReqPresent, boolean statReqV2Present)
+ throws SSLException {
+ boolean hasV1 = false;
+ boolean hasV2 = false;
+ Objects.requireNonNull(data);
+ int startPos = data.position();
+ data.mark();
+
+ // Process the TLS record header
+ int type = Byte.toUnsignedInt(data.get());
+ int ver_major = Byte.toUnsignedInt(data.get());
+ int ver_minor = Byte.toUnsignedInt(data.get());
+ int recLen = Short.toUnsignedInt(data.getShort());
+
+ // Simple sanity checks
+ if (type != 22) {
+ throw new SSLException("Not a handshake: Type = " + type);
+ } else if (recLen > data.remaining()) {
+ throw new SSLException("Incomplete record in buffer: " +
+ "Record length = " + recLen + ", Remaining = " +
+ data.remaining());
+ }
+
+ // Grab the handshake message header.
+ int msgHdr = data.getInt();
+ int msgType = (msgHdr >> 24) & 0x000000FF;
+ int msgLen = msgHdr & 0x00FFFFFF;
+
+ // More simple sanity checks
+ if (msgType != 2) {
+ throw new SSLException("Not a ServerHello: Type = " + msgType);
+ }
+
+ // Skip over the protocol version and server random
+ data.position(data.position() + 34);
+
+ // Jump past the session ID
+ int sessLen = Byte.toUnsignedInt(data.get());
+ if (sessLen != 0) {
+ data.position(data.position() + sessLen);
+ }
+
+ // Skip the cipher suite and compression method
+ data.position(data.position() + 3);
+
+ // Go through the extensions and look for the request extension
+ // expected by the caller.
+ int extsLen = Short.toUnsignedInt(data.getShort());
+ while (data.position() < recLen + startPos + 5) {
+ int extType = Short.toUnsignedInt(data.getShort());
+ int extLen = Short.toUnsignedInt(data.getShort());
+ hasV1 |= (extType == HELLO_EXT_STATUS_REQ);
+ hasV2 |= (extType == HELLO_EXT_STATUS_REQ_V2);
+ data.position(data.position() + extLen);
+ }
+
+ if (hasV1 != statReqPresent) {
+ throw new SSLException("The status_request extension is " +
+ "inconsistent with the expected result: expected = " +
+ statReqPresent + ", actual = " + hasV1);
+ } else if (hasV2 != statReqV2Present) {
+ throw new SSLException("The status_request_v2 extension is " +
+ "inconsistent with the expected result: expected = " +
+ statReqV2Present + ", actual = " + hasV2);
+ }
+
+ // Reset the position to the initial spot at the start of this method.
+ data.reset();
+ }
+
+ /**
+ * Creates the PKI components necessary for this test, including
+ * Root CA, Intermediate CA and SSL server certificates, the keystores
+ * for each entity, a client trust store, and starts the OCSP responders.
+ */
+ private static void createPKI() throws Exception {
+ CertificateBuilder cbld = new CertificateBuilder();
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ KeyStore.Builder keyStoreBuilder =
+ KeyStore.Builder.newInstance("PKCS12", null,
+ new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+ // Generate Root, IntCA, EE keys
+ KeyPair rootCaKP = keyGen.genKeyPair();
+ log("Generated Root CA KeyPair");
+ KeyPair intCaKP = keyGen.genKeyPair();
+ log("Generated Intermediate CA KeyPair");
+ KeyPair sslKP = keyGen.genKeyPair();
+ log("Generated SSL Cert KeyPair");
+
+ // Set up the Root CA Cert
+ cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+ cbld.setPublicKey(rootCaKP.getPublic());
+ cbld.setSerialNumber(new BigInteger("1"));
+ // Make a 3 year validity starting from 60 days ago
+ long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+ long end = start + TimeUnit.DAYS.toMillis(1085);
+ cbld.setValidity(new Date(start), new Date(end));
+ addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+ addCommonCAExts(cbld);
+ // Make our Root CA Cert!
+ X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+ "SHA256withRSA");
+ log("Root CA Created:\n" + certInfo(rootCert));
+
+ // Now build a keystore and add the keys and cert
+ rootKeystore = keyStoreBuilder.getKeyStore();
+ java.security.cert.Certificate[] rootChain = {rootCert};
+ rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+ passwd.toCharArray(), rootChain);
+
+ // Now fire up the OCSP responder
+ rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+ rootOcsp.enableLog(logging);
+ rootOcsp.setNextUpdateInterval(3600);
+ rootOcsp.start();
+ Thread.sleep(1000); // Give the server a second to start up
+ rootOcspPort = rootOcsp.getPort();
+ String rootRespURI = "http://localhost:" + rootOcspPort;
+ log("Root OCSP Responder URI is " + rootRespURI);
+
+ // Now that we have the root keystore and OCSP responder we can
+ // create our intermediate CA.
+ cbld.reset();
+ cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+ cbld.setPublicKey(intCaKP.getPublic());
+ cbld.setSerialNumber(new BigInteger("100"));
+ // Make a 2 year validity starting from 30 days ago
+ start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+ end = start + TimeUnit.DAYS.toMillis(730);
+ cbld.setValidity(new Date(start), new Date(end));
+ addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+ addCommonCAExts(cbld);
+ cbld.addAIAExt(Collections.singletonList(rootRespURI));
+ // Make our Intermediate CA Cert!
+ X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+ "SHA256withRSA");
+ log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+ // Provide intermediate CA cert revocation info to the Root CA
+ // OCSP responder.
+ Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+ new HashMap<>();
+ revInfo.put(intCaCert.getSerialNumber(),
+ new SimpleOCSPServer.CertStatusInfo(
+ SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+ rootOcsp.updateStatusDb(revInfo);
+
+ // Now build a keystore and add the keys, chain and root cert as a TA
+ intKeystore = keyStoreBuilder.getKeyStore();
+ java.security.cert.Certificate[] intChain = {intCaCert, rootCert};
+ intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+ passwd.toCharArray(), intChain);
+ intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+ // Now fire up the Intermediate CA OCSP responder
+ intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+ INT_ALIAS, null);
+ intOcsp.enableLog(logging);
+ intOcsp.setNextUpdateInterval(3600);
+ intOcsp.start();
+ Thread.sleep(1000);
+ intOcspPort = intOcsp.getPort();
+ String intCaRespURI = "http://localhost:" + intOcspPort;
+ log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+ // Last but not least, let's make our SSLCert and add it to its own
+ // Keystore
+ cbld.reset();
+ cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+ cbld.setPublicKey(sslKP.getPublic());
+ cbld.setSerialNumber(new BigInteger("4096"));
+ // Make a 1 year validity starting from 7 days ago
+ start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+ end = start + TimeUnit.DAYS.toMillis(365);
+ cbld.setValidity(new Date(start), new Date(end));
+
+ // Add extensions
+ addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+ boolean[] kuBits = {true, false, true, false, false, false,
+ false, false, false};
+ cbld.addKeyUsageExt(kuBits);
+ List<String> ekuOids = new ArrayList<>();
+ ekuOids.add("1.3.6.1.5.5.7.3.1");
+ ekuOids.add("1.3.6.1.5.5.7.3.2");
+ cbld.addExtendedKeyUsageExt(ekuOids);
+ cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+ cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+ // Make our SSL Server Cert!
+ X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+ "SHA256withRSA");
+ log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+ // Provide SSL server cert revocation info to the Intermeidate CA
+ // OCSP responder.
+ revInfo = new HashMap<>();
+ revInfo.put(sslCert.getSerialNumber(),
+ new SimpleOCSPServer.CertStatusInfo(
+ SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+ intOcsp.updateStatusDb(revInfo);
+
+ // Now build a keystore and add the keys, chain and root cert as a TA
+ serverKeystore = keyStoreBuilder.getKeyStore();
+ java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+ serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+ passwd.toCharArray(), sslChain);
+ serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+ // And finally a Trust Store for the client
+ trustStore = keyStoreBuilder.getKeyStore();
+ trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+ }
+
+ private static void addCommonExts(CertificateBuilder cbld,
+ PublicKey subjKey, PublicKey authKey) throws IOException {
+ cbld.addSubjectKeyIdExt(subjKey);
+ cbld.addAuthorityKeyIdExt(authKey);
+ }
+
+ private static void addCommonCAExts(CertificateBuilder cbld)
+ throws IOException {
+ cbld.addBasicConstraintsExt(true, true, -1);
+ // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+ boolean[] kuBitSettings = {true, false, false, false, false, true,
+ true, false, false};
+ cbld.addKeyUsageExt(kuBitSettings);
+ }
+
+ /**
+ * Helper routine that dumps only a few cert fields rather than
+ * the whole toString() output.
+ *
+ * @param cert an X509Certificate to be displayed
+ *
+ * @return the String output of the issuer, subject and
+ * serial number
+ */
+ private static String certInfo(X509Certificate cert) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+ append("\n");
+ sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+ append("\n");
+ sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/misc/Unsafe/CopySwap.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import jdk.internal.misc.Unsafe;
+import java.lang.reflect.Field;
+
+/*
+ * @test
+ * @summary Test Unsafe.copySwapMemory
+ * @modules java.base/jdk.internal.misc
+ */
+public class CopySwap {
+ private static final boolean DEBUG = Boolean.getBoolean("CopySwap.DEBUG");
+
+ public static final long KB = 1024;
+ public static final long MB = KB * 1024;
+ public static final long GB = MB * 1024;
+
+ private static final Unsafe UNSAFE;
+ private static final int SMALL_COPY_SIZE = 32;
+ private static final int BASE_ALIGNMENT = 16;
+
+ static {
+ try {
+ Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to get Unsafe instance.", e);
+ }
+ }
+
+ private static long alignDown(long value, long alignment) {
+ return value & ~(alignment - 1);
+ }
+
+ private static long alignUp(long value, long alignment) {
+ return (value + alignment - 1) & ~(alignment - 1);
+ }
+
+ private static boolean isAligned(long value, long alignment) {
+ return value == alignDown(value, alignment);
+ }
+
+ private CopySwap() {
+ }
+
+ /**
+ * Generate verification data for a given offset
+ *
+ * The verification data is used to verify that the correct bytes
+ * have indeed been copied and byte swapped.
+ *
+ * The data is generated based on the offset (in bytes) into the
+ * source buffer. For a native buffer the offset is relative to
+ * the base pointer. For a heap array it is relative to the
+ * address of the first array element.
+ *
+ * This method will return the result of doing an elementSize byte
+ * read starting at offset (in bytes).
+ *
+ * @param offset offset into buffer
+ * @param elemSize size (in bytes) of the element
+ *
+ * @return the verification data, only the least significant
+ * elemSize*8 bits are set, zero extended
+ */
+ private long getVerificationDataForOffset(long offset, long elemSize) {
+ byte[] bytes = new byte[(int)elemSize];
+
+ for (long i = 0; i < elemSize; i++) {
+ bytes[(int)i] = (byte)(offset + i);
+ }
+
+ long o = UNSAFE.arrayBaseOffset(byte[].class);
+
+ switch ((int)elemSize) {
+ case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
+ case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
+ case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
+ case 8: return UNSAFE.getLongUnaligned(bytes, o);
+ default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+ }
+ }
+
+ /**
+ * Verify byte swapped data
+ *
+ * @param ptr the data to verify
+ * @param srcOffset the srcOffset (in bytes) from which the copy started,
+ * used as key to regenerate the verification data
+ * @param dstOffset the offset (in bytes) in the array at which to start
+ * the verification, relative to the first element in the array
+ * @param size size (in bytes) of data to to verify
+ * @param elemSize size (in bytes) of the individual array elements
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
+ for (long offset = 0; offset < size; offset += elemSize) {
+ long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
+ long expected = byteSwap(expectedUnswapped, elemSize);
+
+ long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
+
+ if (expected != actual) {
+ throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
+ " dstOffset: 0x" + Long.toHexString(dstOffset) +
+ " size: 0x" + Long.toHexString(size) +
+ " offset: 0x" + Long.toHexString(offset) +
+ " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
+ " expected: 0x" + Long.toHexString(expected) +
+ " != actual: 0x" + Long.toHexString(actual));
+ }
+ }
+ }
+
+ /**
+ * Initialize an array with verification friendly data
+ *
+ * @param ptr pointer to the data to initialize
+ * @param size size (in bytes) of the data
+ * @param elemSize size (in bytes) of the individual elements
+ */
+ private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
+ for (long offset = 0; offset < size; offset++) {
+ byte data = (byte)getVerificationDataForOffset(offset, 1);
+
+ if (ptr.isOnHeap()) {
+ UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data);
+ } else {
+ UNSAFE.putByte(ptr.getOffset() + offset, data);
+ }
+ }
+ }
+
+ /**
+ * Allocate a primitive array
+ *
+ * @param size size (in bytes) of all the array elements (elemSize * length)
+ * @param elemSize the size of the array elements
+ *
+ * @return a newly allocated primitive array
+ */
+ Object allocArray(long size, long elemSize) {
+ int length = (int)(size / elemSize);
+
+ switch ((int)elemSize) {
+ case 2: return new short[length];
+ case 4: return new int[length];
+ case 8: return new long[length];
+ default:
+ throw new IllegalArgumentException("Invalid element size: " + elemSize);
+ }
+ }
+
+ /**
+ * Get the value of a primitive array entry
+ *
+ * @param ptr pointer to the data
+ * @param offset offset (in bytes) of the array element, relative to the first element in the array
+ *
+ * @return the array element, as an unsigned long
+ */
+ private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
+ if (ptr.isOnHeap()) {
+ Object o = ptr.getObject();
+ int index = (int)(offset / elemSize);
+
+ if (o instanceof short[]) {
+ short[] arr = (short[])o;
+ return Short.toUnsignedLong(arr[index]);
+ } else if (o instanceof int[]) {
+ int[] arr = (int[])o;
+ return Integer.toUnsignedLong(arr[index]);
+ } else if (o instanceof long[]) {
+ long[] arr = (long[])o;
+ return arr[index];
+ } else {
+ throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
+ }
+ } else {
+ long addr = ptr.getOffset() + offset;
+
+ switch ((int)elemSize) {
+ case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
+ case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
+ case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
+ case 8: return UNSAFE.getLongUnaligned(null, addr);
+ default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+ }
+ }
+ }
+
+ private void putValue(long addr, long elemSize, long value) {
+ switch ((int)elemSize) {
+ case 1: UNSAFE.putByte(addr, (byte)value); break;
+ case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
+ case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
+ case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
+ default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+ }
+ }
+
+ /**
+ * Get the size of the elements for an array
+ *
+ * @param o a primitive heap array
+ *
+ * @return the size (in bytes) of the individual array elements
+ */
+ private long getArrayElemSize(Object o) {
+ if (o instanceof short[]) {
+ return 2;
+ } else if (o instanceof int[]) {
+ return 4;
+ } else if (o instanceof long[]) {
+ return 8;
+ } else {
+ throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
+ }
+ }
+
+ /**
+ * Byte swap a value
+ *
+ * @param value the value to swap, only the bytes*8 least significant bits are used
+ * @param size size (in bytes) of the value
+ *
+ * @return the byte swapped value in the bytes*8 least significant bits
+ */
+ private long byteSwap(long value, long size) {
+ switch ((int)size) {
+ case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
+ case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
+ case 8: return Long.reverseBytes(value);
+ default: throw new IllegalArgumentException("Invalid element size: " + size);
+ }
+ }
+
+ /**
+ * Verify data in a heap array which has *not* been byte swapped
+ *
+ * @param ptr the data to verify
+ * @param startOffset the offset (in bytes) at which to start the verification
+ * @param size size (in bytes) of the data to verify
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void verifyUnswappedData(GenericPointer ptr, long startOffset, long size) {
+ for (long elemOffset = startOffset; elemOffset < startOffset + size; elemOffset++) {
+ byte expected = (byte)getVerificationDataForOffset(elemOffset, 1);
+
+ byte actual;
+ if (ptr.isOnHeap()) {
+ actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + elemOffset);
+ } else {
+ actual = UNSAFE.getByte(ptr.getOffset() + elemOffset);
+ }
+
+ if (expected != actual) {
+ throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
+ " size: 0x" + Long.toHexString(size) +
+ " elemOffset: 0x" + Long.toHexString(elemOffset) +
+ " expected: 0x" + Long.toHexString(expected) +
+ " != actual: 0x" + Long.toHexString(actual));
+ }
+ }
+ }
+
+
+ /**
+ * Copy and byte swap data from the source to the destination
+ *
+ * This method will pre-populate the whole source and destination
+ * buffers with verification friendly data. It will then use
+ * copySwapMemory to fill part of the destination buffer with
+ * swapped data from the source. Some space (padding) will be
+ * left before and after the data in the destination buffer, which
+ * should not be touched/overwritten by the copy call.
+ *
+ * Note: Both source and destination buffers will be overwritten!
+ *
+ * @param src source buffer to copy from
+ * @param srcOffset the offset (in bytes) in the source buffer, relative to
+ * the first array element, at which to start reading data
+ * @param dst destination buffer to copy to
+ * @param dstOffset the offset (in bytes) in the destination
+ * buffer, relative to the first array element, at which to
+ * start writing data
+ * @param bufSize the size (in bytes) of the src and dst arrays
+ * @param copyBytes the size (in bytes) of the copy to perform,
+ * must be a multiple of elemSize
+ * @param elemSize the size (in bytes) of the elements to byte swap
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void testCopySwap(GenericPointer src, long srcOffset,
+ GenericPointer dst, long dstOffset,
+ long bufSize, long copyBytes, long elemSize) {
+ if (!isAligned(copyBytes, elemSize)) {
+ throw new IllegalArgumentException(
+ "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")");
+ }
+ if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) {
+ throw new IllegalArgumentException(
+ "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")");
+ }
+ if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) {
+ throw new IllegalArgumentException(
+ "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")");
+ }
+ if (srcOffset + copyBytes > bufSize) {
+ throw new IllegalArgumentException(
+ "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
+ }
+ if (dstOffset + copyBytes > bufSize) {
+ throw new IllegalArgumentException(
+ "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
+ }
+
+ // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
+ initVerificationData(src, bufSize, elemSize);
+ if (!src.equals(dst)) {
+ initVerificationData(dst, bufSize, elemSize);
+ }
+
+ if (DEBUG) {
+ System.out.println("===before===");
+ for (int offset = 0; offset < bufSize; offset += elemSize) {
+ long srcValue = getArrayElem(src, offset, elemSize);
+ long dstValue = getArrayElem(dst, offset, elemSize);
+
+ System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
+ " src=0x" + Long.toHexString(srcValue) +
+ " dst=0x" + Long.toHexString(dstValue));
+ }
+ }
+
+ // Copy & swap data into the middle of the destination buffer
+ UNSAFE.copySwapMemory(src.getObject(),
+ src.getOffset() + srcOffset,
+ dst.getObject(),
+ dst.getOffset() + dstOffset,
+ copyBytes,
+ elemSize);
+
+ if (DEBUG) {
+ System.out.println("===after===");
+ for (int offset = 0; offset < bufSize; offset += elemSize) {
+ long srcValue = getArrayElem(src, offset, elemSize);
+ long dstValue = getArrayElem(dst, offset, elemSize);
+
+ System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
+ " src=0x" + Long.toHexString(srcValue) +
+ " dst=0x" + Long.toHexString(dstValue));
+ }
+ }
+
+ // Verify the the front padding is unchanged
+ verifyUnswappedData(dst, 0, dstOffset);
+
+ // Verify swapped data
+ verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize);
+
+ // Verify that the back back padding is unchanged
+ long frontAndDataBytes = dstOffset + copyBytes;
+ long trailingBytes = bufSize - frontAndDataBytes;
+ verifyUnswappedData(dst, frontAndDataBytes, trailingBytes);
+ }
+
+ /**
+ * Test various configurations copy-swapping from one buffer to the other
+ *
+ * @param src the source buffer to copy from
+ * @param dst the destination buffer to copy to
+ * @param size size (in bytes) of the buffers
+ * @param elemSize size (in bytes) of the individual elements
+ *
+ * @throws RuntimeException if an error is found
+ */
+ public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) {
+ // offset in source from which to start reading data
+ for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
+
+ // offset in destination at which to start writing data
+ for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
+
+ // number of bytes to copy
+ long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
+ for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
+ try {
+ testCopySwap(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize);
+ } catch (RuntimeException e) {
+ // Wrap the exception in another exception to catch the relevant configuration data
+ throw new RuntimeException("testBufferPair: " +
+ "src=" + src +
+ " dst=" + dst +
+ " elemSize=0x" + Long.toHexString(elemSize) +
+ " copyBytes=0x" + Long.toHexString(copyBytes) +
+ " srcOffset=0x" + Long.toHexString(srcOffset) +
+ " dstOffset=0x" + Long.toHexString(dstOffset),
+ e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test copying between various permutations of buffers
+ *
+ * @param buffers buffers to permute (src x dst)
+ * @param size size (in bytes) of buffers
+ * @param elemSize size (in bytes) of individual elements
+ *
+ * @throws RuntimeException if an error is found
+ */
+ public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) {
+ for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
+ for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
+ testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize);
+ }
+ }
+ }
+
+ /**
+ * Test copying of a specific element size
+ *
+ * @param size size (in bytes) of buffers to allocate
+ * @param elemSize size (in bytes) of individual elements
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void testElemSize(long size, long elemSize) {
+ long buf1Raw = 0;
+ long buf2Raw = 0;
+
+ try {
+ buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+ long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
+
+ buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+ long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
+
+ GenericPointer[] buffers = {
+ new GenericPointer(buf1),
+ new GenericPointer(buf2),
+ new GenericPointer(allocArray(size, elemSize)),
+ new GenericPointer(allocArray(size, elemSize))
+ };
+
+ testPermuteBuffers(buffers, size, elemSize);
+ } finally {
+ if (buf1Raw != 0) {
+ UNSAFE.freeMemory(buf1Raw);
+ }
+ if (buf2Raw != 0) {
+ UNSAFE.freeMemory(buf2Raw);
+ }
+ }
+ }
+
+ /**
+ * Verify that small copy swaps work
+ */
+ private void testSmallCopy() {
+ int smallBufSize = SMALL_COPY_SIZE;
+
+ // Test various element types and heap/native combinations
+ for (long elemSize = 2; elemSize <= 8; elemSize <<= 1) {
+ testElemSize(smallBufSize, elemSize);
+ }
+ }
+
+
+ /**
+ * Verify that large copy swaps work
+ */
+ private void testLargeCopy() {
+ long size = 2 * GB + 8;
+ long bufRaw = 0;
+
+ // Check that a large native copy succeeds
+ try {
+ try {
+ bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+ } catch (OutOfMemoryError e) {
+ // Accept failure, skip test
+ return;
+ }
+
+ long buf = alignUp(bufRaw, BASE_ALIGNMENT);
+
+ UNSAFE.copySwapMemory(null, buf, null, buf, size, 8);
+ } catch (Exception e) {
+ throw new RuntimeException("copySwapMemory of large buffer failed");
+ } finally {
+ if (bufRaw != 0) {
+ UNSAFE.freeMemory(bufRaw);
+ }
+ }
+ }
+
+ /**
+ * Run positive tests
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void testPositive() {
+ testSmallCopy();
+ testLargeCopy();
+ }
+
+ /**
+ * Run negative tests, testing corner cases and the various exceptions
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void testNegative() {
+ long bufRaw = 0;
+
+ try {
+ bufRaw = UNSAFE.allocateMemory(1024);
+ long buf = alignUp(bufRaw, BASE_ALIGNMENT);
+ short[] arr = new short[16];
+
+ // Check various illegal element sizes
+ for (int elemSize = 2; elemSize <= 8; elemSize <<= 1) {
+ long[] illegalSizes = { -1, 1, elemSize - 1, elemSize + 1, elemSize * 2 - 1 };
+ for (long size : illegalSizes) {
+ try {
+ // Check that illegal elemSize throws an IAE
+ UNSAFE.copySwapMemory(null, buf, null, buf, size, elemSize);
+ throw new RuntimeException("copySwapMemory failed to throw IAE for size=" + size + " elemSize=" + elemSize);
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+ }
+
+ try {
+ // Check that negative srcOffset throws an IAE
+ UNSAFE.copySwapMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw IAE for srcOffset=-1");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+
+ try {
+ // Check that negative dstOffset throws an IAE
+ UNSAFE.copySwapMemory(arr, UNSAFE.arrayBaseOffset(arr.getClass()), arr, -1, 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw IAE for destOffset=-1");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+
+ long illegalElemSizes[] = { 0, 1, 3, 5, 6, 7, 9, 10, -1 };
+ for (long elemSize : illegalElemSizes) {
+ try {
+ // Check that elemSize 1 throws an IAE
+ UNSAFE.copySwapMemory(null, buf, null, buf, 16, elemSize);
+ throw new RuntimeException("copySwapMemory failed to throw NPE");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ try {
+ // Check that a NULL source throws NPE
+ UNSAFE.copySwapMemory(null, 0, null, buf, 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw NPE");
+ } catch (NullPointerException e) {
+ // good
+ }
+
+ try {
+ // Check that a NULL destination throws NPE
+ UNSAFE.copySwapMemory(null, buf, null, 0, 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw NPE");
+ } catch (NullPointerException e) {
+ // good
+ }
+
+ try {
+ // Check that a reference array destination throws IAE
+ UNSAFE.copySwapMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16, 8);
+ throw new RuntimeException("copySwapMemory failed to throw NPE");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+
+ // Check that invalid source & dest pointers throw IAEs (only relevant on 32-bit platforms)
+ if (UNSAFE.addressSize() == 4) {
+ long invalidPtr = (long)1 << 35; // Pick a random bit in upper 32 bits
+
+ try {
+ // Check that an invalid (not 32-bit clean) source pointer throws IAE
+ UNSAFE.copySwapMemory(null, invalidPtr, null, buf, 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw IAE for srcOffset 0x" +
+ Long.toHexString(invalidPtr));
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+
+ try {
+ // Check that an invalid (not 32-bit clean) source pointer throws IAE
+ UNSAFE.copySwapMemory(null, buf, null, invalidPtr, 16, 2);
+ throw new RuntimeException("copySwapMemory failed to throw IAE for destOffset 0x" +
+ Long.toHexString(invalidPtr));
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+ } finally {
+ if (bufRaw != 0) {
+ UNSAFE.freeMemory(bufRaw);
+ }
+ }
+ }
+
+ /**
+ * Run all tests
+ *
+ * @throws RuntimeException if an error is found
+ */
+ private void test() {
+ testPositive();
+ testNegative();
+ }
+
+ public static void main(String[] args) {
+ CopySwap cs = new CopySwap();
+ cs.test();
+ }
+
+ /**
+ * Helper class to represent a "pointer" - either a heap array or
+ * a pointer to a native buffer.
+ *
+ * In the case of a native pointer, the Object is null and the offset is
+ * the absolute address of the native buffer.
+ *
+ * In the case of a heap object, the Object is a primitive array, and
+ * the offset will be set to the base offset to the first element, meaning
+ * the object and the offset together form a double-register pointer.
+ */
+ static class GenericPointer {
+ private final Object o;
+ private final long offset;
+
+ private GenericPointer(Object o, long offset) {
+ this.o = o;
+ this.offset = offset;
+ }
+
+ public String toString() {
+ return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
+ }
+
+ public boolean equals(Object other) {
+ if (!(other instanceof GenericPointer)) {
+ return false;
+ }
+
+ GenericPointer otherp = (GenericPointer)other;
+
+ return o == otherp.o && offset == otherp.offset;
+ }
+
+ GenericPointer(Object o) {
+ this(o, UNSAFE.arrayBaseOffset(o.getClass()));
+ }
+
+ GenericPointer(long offset) {
+ this(null, offset);
+ }
+
+ public boolean isOnHeap() {
+ return o != null;
+ }
+
+ public Object getObject() {
+ return o;
+ }
+
+ public long getOffset() {
+ return offset;
+ }
+ }
+}
--- a/jdk/test/sun/misc/SunMiscSignalTest.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/sun/misc/SunMiscSignalTest.java Wed Jul 05 21:23:38 2017 +0200
@@ -180,6 +180,12 @@
return newArray;
}
+ // Return true if the signal is one of the shutdown signals known to the VM
+ private static boolean isShutdownSignal(Signal signal) {
+ String name = signal.getName();
+ return name.equals("INT") || name.equals("HUP") || name.equals("TERM");
+ }
+
/**
* Quick verification of supported signals using sun.misc.Signal.
*
@@ -201,14 +207,24 @@
Assert.assertEquals(signal.toString(), "SIG" + name, "toString() mismatch, ");
try {
- SignalHandler old = Signal.handle(signal, h);
+ orig = Signal.handle(signal, h);
+ printf("oldHandler: %s%n", orig);
Assert.assertEquals(CanRegister.YES, register, "Unexpected handle succeeded " + name);
try {
Signal.raise(signal);
Assert.assertEquals(CanRaise.YES, raise, "Unexpected raise success for " + name);
Invoked inv = h.semaphore().tryAcquire(Utils.adjustTimeout(100L),
TimeUnit.MILLISECONDS) ? Invoked.YES : Invoked.NO;
- Assert.assertEquals(inv, invoked, "handler not invoked;");
+ if (!isShutdownSignal(signal)) {
+ // Normal case
+ Assert.assertEquals(inv, invoked, "handler not invoked;");
+ } else {
+ if (orig == SignalHandler.SIG_IGN) {
+ Assert.assertEquals(inv, Invoked.NO, "handler should not be invoked");
+ } else {
+ Assert.assertEquals(inv, invoked, "handler not invoked;");
+ }
+ }
} catch (IllegalArgumentException uoe3) {
Assert.assertNotEquals(CanRaise.YES, raise, "raise failed for " + name +
": " + uoe3.getMessage());
@@ -270,14 +286,22 @@
}
// Test expected exception when raising a signal when no handler defined
- @Test(expectedExceptions = IllegalArgumentException.class)
+ @Test
static void testRaiseNoConsumer() {
Signal signal = new Signal("INT");
SignalHandler orig = null;
try {
- Signal.handle(signal, SignalHandler.SIG_DFL);
+ orig = Signal.handle(signal, SignalHandler.SIG_DFL);
+ printf("oldHandler: %s%n", orig);
+ if (orig == SignalHandler.SIG_IGN) {
+ // SIG_IGN for TERM means it cannot be handled
+ return;
+ }
Signal.raise(signal);
- } finally {
+ Assert.fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ printf("IAE message: %s%n", iae.getMessage());
+ } finally {
// Restore original signal handler
if (orig != null && signal != null) {
Signal.handle(signal, orig);
@@ -296,7 +320,13 @@
}
Handler handler = new Handler();
Signal signal = new Signal("INT");
- Signal.handle(signal, handler);
+ SignalHandler orig = Signal.handle(signal, handler);
+ printf("oldHandler: %s%n", orig);
+ if (orig == SignalHandler.SIG_IGN) {
+ // SIG_IGN for INT means it cannot be handled
+ return;
+ }
+
Signal.raise(signal);
boolean handled = handler.semaphore()
.tryAcquire(Utils.adjustTimeout(100L), TimeUnit.MILLISECONDS);
@@ -332,6 +362,10 @@
Handler h1 = new Handler();
Handler h2 = new Handler();
SignalHandler orig = Signal.handle(signal, h1);
+ if (orig == SignalHandler.SIG_IGN) {
+ // SIG_IGN for TERM means it cannot be handled
+ return;
+ }
try {
SignalHandler prev = Signal.handle(signal, h2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/protocol/jar/MultiReleaseJarURLConnection.java Wed Jul 05 21:23:38 2017 +0200
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, 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 8132734
+ * @summary Test that URL connections to multi-release jars can be runtime versioned
+ * @library /lib/testlibrary/java/util/jar
+ * @build Compiler JarBuilder CreateMultiReleaseTestJars
+ * @run testng MultiReleaseJarURLConnection
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.jar.JarFile;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class MultiReleaseJarURLConnection {
+ String userdir = System.getProperty("user.dir",".");
+ String urlFile = "jar:file:" + userdir + "/multi-release.jar!/";
+ String urlEntry = urlFile + "version/Version.java";
+
+ @BeforeClass
+ public void initialize() throws Exception {
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
+ creator.compileEntries();
+ creator.buildMultiReleaseJar();
+ }
+
+ @AfterClass
+ public void close() throws IOException {
+ Files.delete(Paths.get(userdir, "multi-release.jar"));
+ }
+
+ @Test
+ public void testRuntimeVersioning() throws Exception {
+ Assert.assertTrue(readAndCompare(new URL(urlEntry), "return 8"));
+ // #runtime is "magic"
+ Assert.assertTrue(readAndCompare(new URL(urlEntry + "#runtime"), "return 9"));
+ // #fragment or any other fragment is not magic
+ Assert.assertTrue(readAndCompare(new URL(urlEntry + "#fragment"), "return 8"));
+ // cached entities not affected
+ Assert.assertTrue(readAndCompare(new URL(urlEntry), "return 8"));
+ }
+
+ @Test
+ public void testCachedJars() throws Exception {
+ URL rootUrl = new URL(urlFile);
+ JarURLConnection juc = (JarURLConnection)rootUrl.openConnection();
+ JarFile rootJar = juc.getJarFile();
+ JarFile.Release root = rootJar.getVersion();
+
+ URL runtimeUrl = new URL(urlFile + "#runtime");
+ juc = (JarURLConnection)runtimeUrl.openConnection();
+ JarFile runtimeJar = juc.getJarFile();
+ JarFile.Release runtime = runtimeJar.getVersion();
+ Assert.assertNotEquals(root, runtime);
+
+ juc = (JarURLConnection)rootUrl.openConnection();
+ JarFile jar = juc.getJarFile();
+ Assert.assertEquals(jar.getVersion(), root);
+ Assert.assertEquals(jar, rootJar);
+
+ juc = (JarURLConnection)runtimeUrl.openConnection();
+ jar = juc.getJarFile();
+ Assert.assertEquals(jar.getVersion(), runtime);
+ Assert.assertEquals(jar, runtimeJar);
+
+ rootJar.close();
+ runtimeJar.close();
+ jar.close(); // probably not needed
+ }
+
+ private boolean readAndCompare(URL url, String match) throws Exception {
+ boolean result;
+ // necessary to do it this way, instead of openStream(), so we can
+ // close underlying JarFile, otherwise windows can't delete the file
+ URLConnection conn = url.openConnection();
+ try (InputStream is = conn.getInputStream()) {
+ byte[] bytes = is.readAllBytes();
+ result = (new String(bytes)).contains(match);
+ }
+ if (conn instanceof JarURLConnection) {
+ ((JarURLConnection)conn).getJarFile().close();
+ }
+ return result;
+ }
+}
--- a/jdk/test/tools/pack200/Pack200Test.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/tools/pack200/Pack200Test.java Wed Jul 05 21:23:38 2017 +0200
@@ -81,7 +81,9 @@
System.out.println("Packing [" + in.toString() + "]");
// Call the packer
Utils.pack(jarFile, packFile);
+ System.out.println("Done Packing [" + in.toString() + "]");
jarFile.close();
+ System.out.println("Start leak check");
leakCheck();
System.out.println(" Unpacking using java unpacker");
--- a/jdk/test/tools/pack200/Utils.java Thu Feb 25 09:41:40 2016 -0800
+++ b/jdk/test/tools/pack200/Utils.java Wed Jul 05 21:23:38 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -324,6 +324,9 @@
private static void findFiles0(File startDir, List<File> list,
FileFilter filter) throws IOException {
File[] foundFiles = startDir.listFiles(filter);
+ if (foundFiles == null) {
+ return;
+ }
list.addAll(Arrays.asList(foundFiles));
File[] dirs = startDir.listFiles(DIR_FILTER);
for (File dir : dirs) {
--- a/make/Init.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/make/Init.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -154,6 +154,9 @@
# Do nothing
endif
+ # Do not let make delete spec files even if aborted while doing a reconfigure
+ .PRECIOUS: $(SPECS)
+
# Unless reconfigure is explicitely called, let all main targets depend on
# the spec files to be up to date.
ifeq ($(findstring reconfigure, $(INIT_TARGETS)), )
--- a/make/common/CORE_PKGS.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/make/common/CORE_PKGS.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -103,6 +103,7 @@
java.lang.reflect \
java.math \
java.net \
+ java.net.http \
java.net.spi \
java.nio \
java.nio.channels \
--- a/make/common/NativeCompilation.gmk Thu Feb 25 09:41:40 2016 -0800
+++ b/make/common/NativeCompilation.gmk Wed Jul 05 21:23:38 2017 +0200
@@ -651,12 +651,15 @@
$$($1_RES): $$($1_VERSIONINFO_RESOURCE) $$($1_RES_VARDEPS_FILE)
$$(call LogInfo, Compiling resource $$(notdir $$($1_VERSIONINFO_RESOURCE)) (for $$(notdir $$($1_TARGET))))
+ $$(call MakeDir, $$(@D) $$($1_OBJECT_DIR))
$$(call ExecuteWithLog, $$@, \
$$($1_RC) $$($1_RC_FLAGS) $$($1_SYSROOT_CFLAGS) $(CC_OUT_OPTION)$$@ \
$$($1_VERSIONINFO_RESOURCE))
# Windows RC compiler does not support -showIncludes, so we mis-use CL for this.
- $$($1_CC) $$($1_RC_FLAGS) $$($1_SYSROOT_CFLAGS) -showIncludes -nologo -TC \
- $(CC_OUT_OPTION)$$($1_RES_DEP).obj $$($1_VERSIONINFO_RESOURCE) > $$($1_RES_DEP).raw 2>&1 || true ; \
+ $$(call ExecuteWithLog, $$($1_RES_DEP).obj, \
+ $$($1_CC) $$($1_RC_FLAGS) $$($1_SYSROOT_CFLAGS) -showIncludes -nologo -TC \
+ $(CC_OUT_OPTION)$$($1_RES_DEP).obj -P -Fi$$($1_RES_DEP).pp \
+ $$($1_VERSIONINFO_RESOURCE)) > $$($1_RES_DEP).raw 2>&1 || true ; \
$(ECHO) $$($1_RES): \\ > $$($1_RES_DEP) ; \
$(SED) $(WINDOWS_SHOWINCLUDE_SED_PATTERN) $$($1_RES_DEP).raw >> $$($1_RES_DEP) ; \
$(SED) $(DEPENDENCY_TARGET_SED_PATTERN) $$($1_RES_DEP) > $$($1_RES_DEP_TARGETS)
@@ -670,7 +673,7 @@
$1_REAL_MAPFILE := $$($1_OBJECT_DIR)/mapfile
$$($1_REAL_MAPFILE) : $$($1_MAPFILE) $$($1_REORDER)
- $$(MKDIR) -p $$(@D)
+ $$(call MakeDir, $$(@D))
$$(CP) $$($1_MAPFILE) $$@.tmp
$$(SED) -e 's=OUTPUTDIR=$$($1_OBJECT_DIR)=' $$($1_REORDER) >> $$@.tmp
$$(MV) $$@.tmp $$@
@@ -768,7 +771,10 @@
$1_EXTRA_LDFLAGS += "-implib:$$($1_OBJECT_DIR)/$$($1_LIBRARY).lib"
endif
- $1_EXTRA_LIBS += $(GLOBAL_LIBS)
+ # Create loadmap on AIX. Helps in diagnosing some problems.
+ ifneq ($(COMPILER_BINDCMD_FILE_FLAG),)
+ $1_EXTRA_LDFLAGS += $(COMPILER_BINDCMD_FILE_FLAG)$$($1_OBJECT_DIR)/$$($1_NOSUFFIX).loadmap
+ endif
$1_VARDEPS := $$($1_LD) $$($1_SYSROOT_LDFLAGS) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) \
$$($1_LIBS) $$($1_EXTRA_LIBS) $$($1_CREATE_DEBUGINFO_CMDS) \
@@ -846,8 +852,6 @@
ifneq (,$$($1_PROGRAM))
# A executable binary has been specified, setup the target for it.
- $1_EXTRA_LIBS += $(GLOBAL_LIBS)
-
$1_VARDEPS := $$($1_LD) $$($1_SYSROOT_LDFLAGS) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) \
$$($1_LIBS) $$($1_EXTRA_LIBS) $$($1_MT) \
$$($1_CODESIGN) $$($1_CREATE_DEBUGINFO_CMDS) $$($1_MANIFEST_VERSION) \
--- a/modules.xml Thu Feb 25 09:41:40 2016 -0800
+++ b/modules.xml Wed Jul 05 21:23:38 2017 +0200
@@ -316,6 +316,10 @@
<to>java.instrument</to>
</export>
<export>
+ <name>sun.net</name>
+ <to>java.httpclient</to>
+ </export>
+ <export>
<name>sun.net.dns</name>
<to>java.security.jgss</to>
<to>jdk.naming.dns</to>
@@ -496,6 +500,7 @@
<to>java.logging</to>
<to>java.management</to>
<to>java.prefs</to>
+ <to>java.httpclient</to>
</export>
<export>
<name>sun.util.resources</name>
@@ -520,6 +525,7 @@
<depend re-exports="true">java.compact2</depend>
<depend re-exports="true">java.compiler</depend>
<depend re-exports="true">java.instrument</depend>
+ <depend re-exports="true">java.httpclient</depend>
<depend re-exports="true">java.management</depend>
<depend re-exports="true">java.naming</depend>
<depend re-exports="true">java.prefs</depend>
@@ -838,6 +844,13 @@
</export>
</module>
<module>
+ <name>java.httpclient</name>
+ <depend>java.base</depend>
+ <export>
+ <name>java.net.http</name>
+ </export>
+ </module>
+ <module>
<name>java.instrument</name>
<depend>java.base</depend>
<export>