Merge
Reviewed-by: alanb, dfuchs
This commit is contained in:
commit
cf5bb12731
@ -32,6 +32,7 @@
|
||||
#include "classfile/stackMapTableFormat.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/verifier.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
@ -212,6 +213,11 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
|
||||
exception_name == vmSymbols::java_lang_ClassFormatError())) {
|
||||
log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name());
|
||||
log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name());
|
||||
// Exclude any classes that fail over during dynamic dumping
|
||||
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||
SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping");
|
||||
SystemDictionaryShared::set_excluded(klass);
|
||||
}
|
||||
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
|
||||
exception_message = message_buffer;
|
||||
exception_name = inference_verify(
|
||||
|
@ -2601,7 +2601,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
const TypeInt* init_t = phase->type(in(Init) )->is_int();
|
||||
const TypeInt* limit_t = phase->type(in(Limit))->is_int();
|
||||
int stride_p;
|
||||
jlong stride_p;
|
||||
jlong lim, ini;
|
||||
julong max;
|
||||
if (stride_con > 0) {
|
||||
@ -2610,10 +2610,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
ini = init_t->_lo;
|
||||
max = (julong)max_jint;
|
||||
} else {
|
||||
stride_p = -stride_con;
|
||||
stride_p = -(jlong)stride_con;
|
||||
lim = init_t->_hi;
|
||||
ini = limit_t->_lo;
|
||||
max = (julong)min_jint;
|
||||
max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000
|
||||
}
|
||||
julong range = lim - ini + stride_p;
|
||||
if (range <= max) {
|
||||
|
@ -416,6 +416,10 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
|
||||
#ifdef ASSERT
|
||||
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
|
||||
#endif
|
||||
_has_int_index_after_convI2L(false),
|
||||
_int_index_after_convI2L_offset(0),
|
||||
_int_index_after_convI2L_invar(nullptr),
|
||||
_int_index_after_convI2L_scale(0),
|
||||
_nstack(nstack), _analyze_only(analyze_only), _stack_idx(0)
|
||||
#ifndef PRODUCT
|
||||
, _tracer(vloop.is_trace_pointer_analysis())
|
||||
@ -495,6 +499,11 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_safe_to_use_as_simple_form(base, adr)) {
|
||||
assert(!valid(), "does not have simple form");
|
||||
return;
|
||||
}
|
||||
|
||||
_base = base;
|
||||
_adr = adr;
|
||||
assert(valid(), "Usable");
|
||||
@ -508,6 +517,10 @@ VPointer::VPointer(VPointer* p) :
|
||||
#ifdef ASSERT
|
||||
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
|
||||
#endif
|
||||
_has_int_index_after_convI2L(false),
|
||||
_int_index_after_convI2L_offset(0),
|
||||
_int_index_after_convI2L_invar(nullptr),
|
||||
_int_index_after_convI2L_scale(0),
|
||||
_nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx)
|
||||
#ifndef PRODUCT
|
||||
, _tracer(p->_tracer._is_trace_alignment)
|
||||
@ -530,6 +543,354 @@ int VPointer::invar_factor() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency
|
||||
// (i.e. which loads/stores can be packed) based on the simple form:
|
||||
//
|
||||
// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
|
||||
//
|
||||
// However, we parse the compound-long-int form:
|
||||
//
|
||||
// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
|
||||
// int_index = int_offset + int_invar + int_scale * iv
|
||||
//
|
||||
// In general, the simple and the compound-long-int form do not always compute the same pointer
|
||||
// at runtime. For example, the simple form would give a different result due to an overflow
|
||||
// in the int_index.
|
||||
//
|
||||
// Example:
|
||||
// For both forms, we have:
|
||||
// iv = 0
|
||||
// scale = 1
|
||||
//
|
||||
// We now account the offset and invar once to the long part and once to the int part:
|
||||
// Pointer 1 (long offset and long invar):
|
||||
// long_offset = min_int
|
||||
// long_invar = min_int
|
||||
// int_offset = 0
|
||||
// int_invar = 0
|
||||
//
|
||||
// Pointer 2 (int offset and int invar):
|
||||
// long_offset = 0
|
||||
// long_invar = 0
|
||||
// int_offset = min_int
|
||||
// int_invar = min_int
|
||||
//
|
||||
// This gives us the following pointers:
|
||||
// Compound-long-int form pointers:
|
||||
// Form:
|
||||
// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv)
|
||||
//
|
||||
// Pointers:
|
||||
// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0)
|
||||
// = adr + min_int + min_int
|
||||
// = adr - 2^32
|
||||
//
|
||||
// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0)
|
||||
// = adr + ConvI2L(min_int + min_int)
|
||||
// = adr + 0
|
||||
// = adr
|
||||
//
|
||||
// Simple form pointers:
|
||||
// Form:
|
||||
// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
|
||||
// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv)
|
||||
//
|
||||
// Pointers:
|
||||
// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0
|
||||
// = adr + min_int + min_int
|
||||
// = adr - 2^32
|
||||
// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0
|
||||
// = adr + min_int + min_int
|
||||
// = adr - 2^32
|
||||
//
|
||||
// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical.
|
||||
//
|
||||
// Hence, we need to determine in which cases it is safe to make decisions based on the simple
|
||||
// form, rather than the compound-long-int form. If we cannot prove that using the simple form
|
||||
// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid VPointer,
|
||||
// and the associated memop cannot be vectorized.
|
||||
bool VPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const {
|
||||
#ifndef _LP64
|
||||
// On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the
|
||||
// parsed pointer form is always the simple form, with int operations:
|
||||
//
|
||||
// pointer = adr + offset + invar + scale * iv
|
||||
//
|
||||
assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv");
|
||||
return true;
|
||||
#else
|
||||
|
||||
// Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no
|
||||
// int_index overflow. This implies that the conversion to long can be done separately:
|
||||
//
|
||||
// ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv)
|
||||
//
|
||||
// And hence, the simple form is guaranteed to be identical to the compound-long-int form at
|
||||
// runtime and the VPointer is safe/valid to be used.
|
||||
const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr();
|
||||
if (ary_ptr_t != nullptr) {
|
||||
if (!_mem->is_unsafe_access()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We did not find the int_index. Just to be safe, reject this VPointer.
|
||||
if (!_has_int_index_after_convI2L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int int_offset = _int_index_after_convI2L_offset;
|
||||
Node* int_invar = _int_index_after_convI2L_invar;
|
||||
int int_scale = _int_index_after_convI2L_scale;
|
||||
int long_scale = _scale / int_scale;
|
||||
|
||||
// If "int_index = iv", then the simple form is identical to the compound-long-int form.
|
||||
//
|
||||
// int_index = int_offset + int_invar + int_scale * iv
|
||||
// = 0 0 1 * iv
|
||||
// = iv
|
||||
if (int_offset == 0 && int_invar == nullptr && int_scale == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge":
|
||||
//
|
||||
// pointer1 = adr + ConvI2L(int_index1)
|
||||
// pointer2 = adr + ConvI2L(int_index2)
|
||||
//
|
||||
// int_index1 = max_int + 0 = max_int -> very close to but before the overflow
|
||||
// int_index2 = max_int + 1 = min_int -> just enough to get the overflow
|
||||
//
|
||||
// When looking at the difference of pointer1 and pointer2, we notice that it is very large
|
||||
// (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is
|
||||
// an actual out-of-bounds access at runtime. These would normally be prevented by range checks
|
||||
// at runtime. However, if the access was done by using Unsafe, where range checks are omitted,
|
||||
// then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to
|
||||
// do anything, including changing the behavior.
|
||||
//
|
||||
// If we can set the right conditions, we have a guarantee that an overflow is either impossible
|
||||
// (no overflow or range checks preventing that) or undefined behavior. In both cases, we are
|
||||
// safe to do a vectorization.
|
||||
//
|
||||
// Approach: We want to prove a lower bound for the distance between these two pointers, and an
|
||||
// upper bound for the size of a memory object. We can derive such an upper bound for
|
||||
// arrays. We know they have at most 2^31 elements. If we know the size of the elements
|
||||
// in bytes, we have:
|
||||
//
|
||||
// array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes
|
||||
// >= array_size_in_bytes (ARR)
|
||||
//
|
||||
// If some small difference "delta" leads to an int_index overflow, we know that the
|
||||
// int_index1 before overflow must have been close to max_int, and the int_index2 after
|
||||
// the overflow must be close to min_int:
|
||||
//
|
||||
// pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1)
|
||||
// =approx adr + long_offset + long_invar + long_scale * max_int
|
||||
//
|
||||
// pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2)
|
||||
// =approx adr + long_offset + long_invar + long_scale * min_int
|
||||
//
|
||||
// We realize that the pointer difference is very large:
|
||||
//
|
||||
// difference =approx long_scale * 2^32
|
||||
//
|
||||
// Hence, if we set the right condition for long_scale and array_element_size_in_bytes,
|
||||
// we can prove that an overflow is impossible (or would imply undefined behaviour).
|
||||
//
|
||||
// We must now take this intuition, and develop a rigorous proof. We start by stating the problem
|
||||
// more precisely, with the help of some definitions and the Statement we are going to prove.
|
||||
//
|
||||
// Definition:
|
||||
// Two VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()),
|
||||
// iff all of these conditions apply for the simple form:
|
||||
// 1) Both VPointers are valid.
|
||||
// 2) The adr are identical, or both are array bases of different arrays.
|
||||
// 3) They have identical scale.
|
||||
// 4) They have identical invar.
|
||||
// 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF)
|
||||
//
|
||||
// For the Vectorization Optimization, we pair-wise compare VPointers and determine if they are:
|
||||
// 1) "not comparable":
|
||||
// We do not optimize them (assume they alias, not assume adjacency).
|
||||
//
|
||||
// Whenever we chose this option based on the simple form, it is also correct based on the
|
||||
// compound-long-int form, since we make no optimizations based on it.
|
||||
//
|
||||
// 2) "comparable" with different array bases at runtime:
|
||||
// We assume they do not alias (remove memory edges), but not assume adjacency.
|
||||
//
|
||||
// Whenever we have two different array bases for the simple form, we also have different
|
||||
// array bases for the compound-long-form. Since VPointers provably point to different
|
||||
// memory objects, they can never alias.
|
||||
//
|
||||
// 3) "comparable" with the same base address:
|
||||
// We compute the relative pointer difference, and based on the load/store size we can
|
||||
// compute aliasing and adjacency.
|
||||
//
|
||||
// We must find a condition under which the pointer difference of the simple form is
|
||||
// identical to the pointer difference of the compound-long-form. We do this with the
|
||||
// Statement below, which we then proceed to prove.
|
||||
//
|
||||
// Statement:
|
||||
// If two VPointers satisfy these 3 conditions:
|
||||
// 1) They are "comparable".
|
||||
// 2) They have the same base address.
|
||||
// 3) Their long_scale is a multiple of the array element size in bytes:
|
||||
//
|
||||
// abs(long_scale) % array_element_size_in_bytes = 0 (A)
|
||||
//
|
||||
// Then their pointer difference of the simple form is identical to the pointer difference
|
||||
// of the compound-long-int form.
|
||||
//
|
||||
// More precisely:
|
||||
// Such two VPointers by definition have identical adr, invar, and scale.
|
||||
// Their simple form is:
|
||||
//
|
||||
// s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1)
|
||||
// s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2)
|
||||
//
|
||||
// Thus, the pointer difference of the simple forms collapses to the difference in offsets:
|
||||
//
|
||||
// s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C)
|
||||
//
|
||||
// Their compound-long-int form for these VPointer is:
|
||||
//
|
||||
// c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1)
|
||||
// int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2)
|
||||
//
|
||||
// c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3)
|
||||
// int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4)
|
||||
//
|
||||
// And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2):
|
||||
//
|
||||
// offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5)
|
||||
// offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6)
|
||||
//
|
||||
// invar = long_invar1 + long_scale1 * ConvI2L(int_invar1)
|
||||
// = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7)
|
||||
//
|
||||
// scale = long_scale1 * ConvI2L(int_scale1)
|
||||
// = long_scale2 * ConvI2L(int_scale2) (D8)
|
||||
//
|
||||
// The pointer difference of the compound-long-int form is defined as:
|
||||
//
|
||||
// c_difference = c_pointer1 - c_pointer2
|
||||
//
|
||||
// Thus, the statement claims that for the two VPointer we have:
|
||||
//
|
||||
// s_difference = c_difference (Statement)
|
||||
//
|
||||
// We prove the Statement with the help of a Lemma:
|
||||
//
|
||||
// Lemma:
|
||||
// There is some integer x, such that:
|
||||
//
|
||||
// c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma)
|
||||
//
|
||||
// From condition (DIFF), we can derive:
|
||||
//
|
||||
// abs(s_difference) < 2^31 (E)
|
||||
//
|
||||
// Assuming the Lemma, we prove the Statement:
|
||||
// If "x = 0" (intuitively: the int_index does not overflow), then:
|
||||
// c_difference = s_difference
|
||||
// and hence the simple form computes the same pointer difference as the compound-long-int form.
|
||||
// If "x != 0" (intuitively: the int_index overflows), then:
|
||||
// abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32)
|
||||
// >= array_element_size_in_bytes * 2^32 - abs(s_difference)
|
||||
// -- apply (E) --
|
||||
// > array_element_size_in_bytes * 2^32 - 2^31
|
||||
// >= array_element_size_in_bytes * 2^31
|
||||
// -- apply (ARR) --
|
||||
// >= max_possible_array_size_in_bytes
|
||||
// >= array_size_in_bytes
|
||||
//
|
||||
// This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size.
|
||||
// Thus, at least one of the two pointers must be outside of the array bounds. But we can assume
|
||||
// that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence,
|
||||
// we are allowed to do anything. We can also "safely" use the simple form in this case even though
|
||||
// it might not match the compound-long-int form at runtime.
|
||||
// QED Statement.
|
||||
//
|
||||
// We must now prove the Lemma.
|
||||
//
|
||||
// ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that:
|
||||
//
|
||||
// ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F)
|
||||
//
|
||||
// It follows, that there is an integer y1 such that:
|
||||
//
|
||||
// ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv)
|
||||
// -- apply (F) --
|
||||
// = ConvI2L(int_offset1)
|
||||
// + ConvI2L(int_invar1)
|
||||
// + ConvI2L(int_scale1) * ConvI2L(iv)
|
||||
// + y1 * 2^32 (G)
|
||||
//
|
||||
// Thus, we can write the compound-long-int form (D1) as:
|
||||
//
|
||||
// c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1)
|
||||
// -- apply (G) --
|
||||
// = adr
|
||||
// + long_offset1
|
||||
// + long_invar1
|
||||
// + long_scale1 * ConvI2L(int_offset1)
|
||||
// + long_scale1 * ConvI2L(int_invar1)
|
||||
// + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv)
|
||||
// + long_scale1 * y1 * 2^32 (H)
|
||||
//
|
||||
// And we can write the simple form as:
|
||||
//
|
||||
// s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv)
|
||||
// -- apply (D5, D7, D8) --
|
||||
// = adr
|
||||
// + long_offset1
|
||||
// + long_scale1 * ConvI2L(int_offset1)
|
||||
// + long_invar1
|
||||
// + long_scale1 * ConvI2L(int_invar1)
|
||||
// + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K)
|
||||
//
|
||||
// We now compute the pointer difference between the simple (K) and compound-long-int form (H).
|
||||
// Most terms cancel out immediately:
|
||||
//
|
||||
// sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L)
|
||||
//
|
||||
// Rearranging the equation (L), we get:
|
||||
//
|
||||
// c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M)
|
||||
//
|
||||
// And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer
|
||||
// x1, such that (M) implies:
|
||||
//
|
||||
// c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N)
|
||||
//
|
||||
// With an analogue equation for c_pointer2, we can now compute the pointer difference for
|
||||
// the compound-long-int form:
|
||||
//
|
||||
// c_difference = c_pointer1 - c_pointer2
|
||||
// -- apply (N) --
|
||||
// = s_pointer1 + array_element_size_in_bytes * x1 * 2^32
|
||||
// -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32)
|
||||
// -- where "x = x1 - x2" --
|
||||
// = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32
|
||||
// -- apply (C) --
|
||||
// = s_difference + array_element_size_in_bytes * x * 2^32
|
||||
// QED Lemma.
|
||||
if (ary_ptr_t != nullptr) {
|
||||
BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type();
|
||||
if (is_java_primitive(array_element_bt)) {
|
||||
int array_element_size_in_bytes = type2aelembytes(array_element_bt);
|
||||
if (abs(long_scale) % array_element_size_in_bytes == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// General case: we do not know if it is safe to use the simple form.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VPointer::is_loop_member(Node* n) const {
|
||||
Node* n_c = phase()->get_ctrl(n);
|
||||
return lpt()->is_member(phase()->get_loop(n_c));
|
||||
@ -582,11 +943,40 @@ bool VPointer::scaled_iv_plus_offset(Node* n) {
|
||||
}
|
||||
} else if (opc == Op_SubI || opc == Op_SubL) {
|
||||
if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) {
|
||||
// (offset1 + invar1 + scale * iv) - (offset2 + invar2)
|
||||
// Subtraction handled via "negate" flag of "offset_plus_k".
|
||||
NOT_PRODUCT(_tracer.scaled_iv_plus_offset_6(n);)
|
||||
return true;
|
||||
}
|
||||
if (offset_plus_k(n->in(1)) && scaled_iv_plus_offset(n->in(2))) {
|
||||
_scale *= -1;
|
||||
VPointer tmp(this);
|
||||
if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) {
|
||||
// (offset1 + invar1) - (offset2 + invar2 + scale * iv)
|
||||
// Subtraction handled explicitly below.
|
||||
assert(_scale == 0, "shouldn't be set yet");
|
||||
// _scale = -tmp._scale
|
||||
if (!try_MulI_no_overflow(-1, tmp._scale, _scale)) {
|
||||
return false; // mul overflow.
|
||||
}
|
||||
// _offset -= tmp._offset
|
||||
if (!try_SubI_no_overflow(_offset, tmp._offset, _offset)) {
|
||||
return false; // sub overflow.
|
||||
}
|
||||
// _invar -= tmp._invar
|
||||
if (tmp._invar != nullptr) {
|
||||
maybe_add_to_invar(tmp._invar, true);
|
||||
#ifdef ASSERT
|
||||
_debug_invar_scale = tmp._debug_invar_scale;
|
||||
_debug_negate_invar = !tmp._debug_negate_invar;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward info about the int_index:
|
||||
assert(!_has_int_index_after_convI2L, "no previous int_index discovered");
|
||||
_has_int_index_after_convI2L = tmp._has_int_index_after_convI2L;
|
||||
_int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset;
|
||||
_int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar;
|
||||
_int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale;
|
||||
|
||||
NOT_PRODUCT(_tracer.scaled_iv_plus_offset_7(n);)
|
||||
return true;
|
||||
}
|
||||
@ -628,10 +1018,52 @@ bool VPointer::scaled_iv(Node* n) {
|
||||
}
|
||||
} else if (opc == Op_LShiftI) {
|
||||
if (n->in(1) == iv() && n->in(2)->is_Con()) {
|
||||
_scale = 1 << n->in(2)->get_int();
|
||||
if (!try_LShiftI_no_overflow(1, n->in(2)->get_int(), _scale)) {
|
||||
return false; // shift overflow.
|
||||
}
|
||||
NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);)
|
||||
return true;
|
||||
}
|
||||
} else if (opc == Op_ConvI2L && !has_iv()) {
|
||||
// So far we have not found the iv yet, and are about to enter a ConvI2L subgraph,
|
||||
// which may be the int index (that might overflow) for the memory access, of the form:
|
||||
//
|
||||
// int_index = int_offset + int_invar + int_scale * iv
|
||||
//
|
||||
// If we simply continue parsing with the current VPointer, then the int_offset and
|
||||
// int_invar simply get added to the long offset and invar. But for the checks in
|
||||
// VPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the
|
||||
// int_index. Thus, we must parse it explicitly here. For this, we use a temporary
|
||||
// VPointer, to pattern match the int_index sub-expression of the address.
|
||||
|
||||
NOT_PRODUCT(Tracer::Depth dddd;)
|
||||
VPointer tmp(this);
|
||||
NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);)
|
||||
|
||||
if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) {
|
||||
// We successfully matched an integer index, of the form:
|
||||
// int_index = int_offset + int_invar + int_scale * iv
|
||||
// Forward scale.
|
||||
assert(_scale == 0 && tmp._scale != 0, "iv only found just now");
|
||||
_scale = tmp._scale;
|
||||
// Accumulate offset.
|
||||
if (!try_AddI_no_overflow(_offset, tmp._offset, _offset)) {
|
||||
return false; // add overflow.
|
||||
}
|
||||
// Accumulate invar.
|
||||
if (tmp._invar != nullptr) {
|
||||
maybe_add_to_invar(tmp._invar, false);
|
||||
}
|
||||
// Set info about the int_index:
|
||||
assert(!_has_int_index_after_convI2L, "no previous int_index discovered");
|
||||
_has_int_index_after_convI2L = true;
|
||||
_int_index_after_convI2L_offset = tmp._offset;
|
||||
_int_index_after_convI2L_invar = tmp._invar;
|
||||
_int_index_after_convI2L_scale = tmp._scale;
|
||||
|
||||
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
|
||||
return true;
|
||||
}
|
||||
} else if (opc == Op_ConvI2L || opc == Op_CastII) {
|
||||
if (scaled_iv_plus_offset(n->in(1))) {
|
||||
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
|
||||
@ -647,9 +1079,20 @@ bool VPointer::scaled_iv(Node* n) {
|
||||
NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);)
|
||||
|
||||
if (tmp.scaled_iv_plus_offset(n->in(1))) {
|
||||
int scale = n->in(2)->get_int();
|
||||
_scale = tmp._scale << scale;
|
||||
_offset += tmp._offset << scale;
|
||||
int shift = n->in(2)->get_int();
|
||||
// Accumulate scale.
|
||||
if (!try_LShiftI_no_overflow(tmp._scale, shift, _scale)) {
|
||||
return false; // shift overflow.
|
||||
}
|
||||
// Accumulate offset.
|
||||
int shifted_offset = 0;
|
||||
if (!try_LShiftI_no_overflow(tmp._offset, shift, shifted_offset)) {
|
||||
return false; // shift overflow.
|
||||
}
|
||||
if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) {
|
||||
return false; // add overflow.
|
||||
}
|
||||
// Accumulate invar.
|
||||
if (tmp._invar != nullptr) {
|
||||
BasicType bt = tmp._invar->bottom_type()->basic_type();
|
||||
assert(bt == T_INT || bt == T_LONG, "");
|
||||
@ -658,6 +1101,14 @@ bool VPointer::scaled_iv(Node* n) {
|
||||
_debug_invar_scale = n->in(2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward info about the int_index:
|
||||
assert(!_has_int_index_after_convI2L, "no previous int_index discovered");
|
||||
_has_int_index_after_convI2L = tmp._has_int_index_after_convI2L;
|
||||
_int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset;
|
||||
_int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar;
|
||||
_int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale;
|
||||
|
||||
NOT_PRODUCT(_tracer.scaled_iv_9(n, _scale, _offset, _invar);)
|
||||
return true;
|
||||
}
|
||||
@ -675,7 +1126,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
|
||||
|
||||
int opc = n->Opcode();
|
||||
if (opc == Op_ConI) {
|
||||
_offset += negate ? -(n->get_int()) : n->get_int();
|
||||
if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);)
|
||||
return true;
|
||||
} else if (opc == Op_ConL) {
|
||||
@ -684,7 +1137,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
|
||||
if (t->higher_equal(TypeLong::INT)) {
|
||||
jlong loff = n->get_long();
|
||||
jint off = (jint)loff;
|
||||
_offset += negate ? -off : loff;
|
||||
if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);)
|
||||
return true;
|
||||
}
|
||||
@ -699,11 +1154,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
|
||||
if (opc == Op_AddI) {
|
||||
if (n->in(2)->is_Con() && invariant(n->in(1))) {
|
||||
maybe_add_to_invar(n->in(1), negate);
|
||||
_offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
|
||||
if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, negate, _offset);)
|
||||
return true;
|
||||
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
|
||||
_offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
|
||||
if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
maybe_add_to_invar(n->in(2), negate);
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);)
|
||||
return true;
|
||||
@ -712,11 +1171,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
|
||||
if (opc == Op_SubI) {
|
||||
if (n->in(2)->is_Con() && invariant(n->in(1))) {
|
||||
maybe_add_to_invar(n->in(1), negate);
|
||||
_offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
|
||||
if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, negate, _offset);)
|
||||
return true;
|
||||
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
|
||||
_offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
|
||||
if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
|
||||
return false; // add/sub overflow.
|
||||
}
|
||||
maybe_add_to_invar(n->in(2), !negate);
|
||||
NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);)
|
||||
return true;
|
||||
@ -806,6 +1269,57 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) {
|
||||
_invar = register_if_new(add);
|
||||
}
|
||||
|
||||
bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) {
|
||||
jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2));
|
||||
jint int_offset = java_add( offset1, offset2);
|
||||
if (long_offset != int_offset) {
|
||||
return false;
|
||||
}
|
||||
result = int_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) {
|
||||
jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2));
|
||||
jint int_offset = java_subtract( offset1, offset2);
|
||||
if (long_offset != int_offset) {
|
||||
return false;
|
||||
}
|
||||
result = int_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) {
|
||||
if (is_sub) {
|
||||
return try_SubI_no_overflow(offset1, offset2, result);
|
||||
} else {
|
||||
return try_AddI_no_overflow(offset1, offset2, result);
|
||||
}
|
||||
}
|
||||
|
||||
bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) {
|
||||
if (shift < 0 || shift > 31) {
|
||||
return false;
|
||||
}
|
||||
jlong long_offset = java_shift_left((jlong)(offset), shift);
|
||||
jint int_offset = java_shift_left( offset, shift);
|
||||
if (long_offset != int_offset) {
|
||||
return false;
|
||||
}
|
||||
result = int_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) {
|
||||
jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2));
|
||||
jint int_offset = java_multiply( offset1, offset2);
|
||||
if (long_offset != int_offset) {
|
||||
return false;
|
||||
}
|
||||
result = int_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We use two comparisons, because a subtraction could underflow.
|
||||
#define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \
|
||||
if (a < b) { return -1; } \
|
||||
|
@ -670,13 +670,51 @@ private:
|
||||
// A vectorization pointer (VPointer) has information about an address for
|
||||
// dependence checking and vector alignment. It's usually bound to a memory
|
||||
// operation in a counted loop for vectorizable analysis.
|
||||
//
|
||||
// We parse and represent pointers of the simple form:
|
||||
//
|
||||
// pointer = adr + offset + invar + scale * ConvI2L(iv)
|
||||
//
|
||||
// Where:
|
||||
//
|
||||
// adr: the base address of an array (base = adr)
|
||||
// OR
|
||||
// some address to off-heap memory (base = TOP)
|
||||
//
|
||||
// offset: a constant offset
|
||||
// invar: a runtime variable, which is invariant during the loop
|
||||
// scale: scaling factor
|
||||
// iv: loop induction variable
|
||||
//
|
||||
// But more precisely, we parse the composite-long-int form:
|
||||
//
|
||||
// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv)
|
||||
//
|
||||
// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
|
||||
// int_index = int_offset + int_invar + int_scale * iv
|
||||
//
|
||||
// However, for aliasing and adjacency checks (e.g. VPointer::cmp()) we always use the simple form to make
|
||||
// decisions. Hence, we must make sure to only create a "valid" VPointer if the optimisations based on the
|
||||
// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on
|
||||
// if the int_index overflows, but the precise conditions are given in VPointer::is_safe_to_use_as_simple_form().
|
||||
//
|
||||
// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv)
|
||||
// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv)
|
||||
//
|
||||
// scale = long_scale * ConvI2L(int_scale)
|
||||
// offset = long_offset + long_scale * ConvI2L(int_offset)
|
||||
// invar = long_invar + long_scale * ConvI2L(int_invar)
|
||||
//
|
||||
// pointer = adr + offset + invar + scale * ConvI2L(iv)
|
||||
//
|
||||
class VPointer : public ArenaObj {
|
||||
protected:
|
||||
MemNode* const _mem; // My memory reference node
|
||||
const VLoop& _vloop;
|
||||
|
||||
Node* _base; // null if unsafe nonheap reference
|
||||
Node* _adr; // address pointer
|
||||
// Components of the simple form:
|
||||
Node* _base; // Base address of an array OR null if some off-heap memory.
|
||||
Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer.
|
||||
int _scale; // multiplier for iv (in bytes), 0 if no loop iv
|
||||
int _offset; // constant offset (in bytes)
|
||||
|
||||
@ -687,6 +725,13 @@ class VPointer : public ArenaObj {
|
||||
Node* _debug_invar_scale; // multiplier for invariant
|
||||
#endif
|
||||
|
||||
// The int_index components of the compound-long-int form. Used to decide if it is safe to use the
|
||||
// simple form rather than the compound-long-int form that was parsed.
|
||||
bool _has_int_index_after_convI2L;
|
||||
int _int_index_after_convI2L_offset;
|
||||
Node* _int_index_after_convI2L_invar;
|
||||
int _int_index_after_convI2L_scale;
|
||||
|
||||
Node_Stack* _nstack; // stack used to record a vpointer trace of variants
|
||||
bool _analyze_only; // Used in loop unrolling only for vpointer trace
|
||||
uint _stack_idx; // Used in loop unrolling only for vpointer trace
|
||||
@ -726,6 +771,8 @@ class VPointer : public ArenaObj {
|
||||
VPointer(VPointer* p);
|
||||
NONCOPYABLE(VPointer);
|
||||
|
||||
bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const;
|
||||
|
||||
public:
|
||||
bool valid() const { return _adr != nullptr; }
|
||||
bool has_iv() const { return _scale != 0; }
|
||||
@ -751,10 +798,43 @@ class VPointer : public ArenaObj {
|
||||
return _invar == q._invar;
|
||||
}
|
||||
|
||||
// We compute if and how two VPointers can alias at runtime, i.e. if the two addressed regions of memory can
|
||||
// ever overlap. There are essentially 3 relevant return states:
|
||||
// - NotComparable: Synonymous to "unknown aliasing".
|
||||
// We have no information about how the two VPointers can alias. They could overlap, refer
|
||||
// to another location in the same memory object, or point to a completely different object.
|
||||
// -> Memory edge required. Aliasing unlikely but possible.
|
||||
//
|
||||
// - Less / Greater: Synonymous to "never aliasing".
|
||||
// The two VPointers may point into the same memory object, but be non-aliasing (i.e. we
|
||||
// know both address regions inside the same memory object, but these regions are non-
|
||||
// overlapping), or the VPointers point to entirely different objects.
|
||||
// -> No memory edge required. Aliasing impossible.
|
||||
//
|
||||
// - Equal: Synonymous to "overlap, or point to different memory objects".
|
||||
// The two VPointers either overlap on the same memory object, or point to two different
|
||||
// memory objects.
|
||||
// -> Memory edge required. Aliasing likely.
|
||||
//
|
||||
// In a future refactoring, we can simplify to two states:
|
||||
// - NeverAlias: instead of Less / Greater
|
||||
// - MayAlias: instead of Equal / NotComparable
|
||||
//
|
||||
// Two VPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply:
|
||||
// 1) Both are valid, i.e. expressible in the compound-long-int or simple form.
|
||||
// 2) The adr are identical, or both are array bases of different arrays.
|
||||
// 3) They have identical scale.
|
||||
// 4) They have identical invar.
|
||||
// 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31.
|
||||
int cmp(const VPointer& q) const {
|
||||
if (valid() && q.valid() &&
|
||||
(_adr == q._adr || (_base == _adr && q._base == q._adr)) &&
|
||||
_scale == q._scale && invar_equals(q)) {
|
||||
jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset));
|
||||
jlong max_diff = (jlong)1 << 31;
|
||||
if (difference >= max_diff) {
|
||||
return NotComparable;
|
||||
}
|
||||
bool overlap = q._offset < _offset + memory_size() &&
|
||||
_offset < q._offset + q.memory_size();
|
||||
return overlap ? Equal : (_offset < q._offset ? Less : Greater);
|
||||
@ -859,6 +939,12 @@ class VPointer : public ArenaObj {
|
||||
|
||||
void maybe_add_to_invar(Node* new_invar, bool negate);
|
||||
|
||||
static bool try_AddI_no_overflow(int offset1, int offset2, int& result);
|
||||
static bool try_SubI_no_overflow(int offset1, int offset2, int& result);
|
||||
static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result);
|
||||
static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result);
|
||||
static bool try_MulI_no_overflow(int offset1, int offset2, int& result);
|
||||
|
||||
Node* register_if_new(Node* n) const;
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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,7 @@ final class DHPrivateKey implements PrivateKey,
|
||||
private final BigInteger x;
|
||||
|
||||
// the key bytes, without the algorithm information
|
||||
// cannot be final as it's re-assigned for deserialization
|
||||
private byte[] key;
|
||||
|
||||
// the encoded key
|
||||
@ -70,6 +71,131 @@ final class DHPrivateKey implements PrivateKey,
|
||||
// the private-value length (optional)
|
||||
private final int l;
|
||||
|
||||
private static class DHComponents {
|
||||
final BigInteger x;
|
||||
final BigInteger p;
|
||||
final BigInteger g;
|
||||
final int l;
|
||||
final byte[] key;
|
||||
|
||||
DHComponents(BigInteger x, BigInteger p, BigInteger g, int l,
|
||||
byte[] key) {
|
||||
this.x = x;
|
||||
this.p = p;
|
||||
this.g = g;
|
||||
this.l = l;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
// parses the specified encoding into a DHComponents object
|
||||
private static DHComponents decode(byte[] encodedKey)
|
||||
throws IOException {
|
||||
DerValue val = null;
|
||||
|
||||
try {
|
||||
val = new DerValue(encodedKey);
|
||||
if (val.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("Key not a SEQUENCE");
|
||||
}
|
||||
|
||||
// version
|
||||
BigInteger parsedVersion = val.data.getBigInteger();
|
||||
if (!parsedVersion.equals(PKCS8_VERSION)) {
|
||||
throw new IOException("version mismatch: (supported: " +
|
||||
PKCS8_VERSION + ", parsed: " + parsedVersion);
|
||||
}
|
||||
|
||||
// privateKeyAlgorithm
|
||||
DerValue algid = val.data.getDerValue();
|
||||
if (algid.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("AlgId is not a SEQUENCE");
|
||||
}
|
||||
DerInputStream derInStream = algid.toDerInputStream();
|
||||
ObjectIdentifier oid = derInStream.getOID();
|
||||
if (oid == null) {
|
||||
throw new IOException("Null OID");
|
||||
}
|
||||
if (derInStream.available() == 0) {
|
||||
throw new IOException("Parameters missing");
|
||||
}
|
||||
// parse the parameters
|
||||
DerValue params = derInStream.getDerValue();
|
||||
if (params.tag == DerValue.tag_Null) {
|
||||
throw new IOException("Null parameters");
|
||||
}
|
||||
if (params.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("Parameters not a SEQUENCE");
|
||||
}
|
||||
params.data.reset();
|
||||
BigInteger p = params.data.getBigInteger();
|
||||
BigInteger g = params.data.getBigInteger();
|
||||
// Private-value length is OPTIONAL
|
||||
int l = (params.data.available() != 0 ?
|
||||
params.data.getInteger() : 0);
|
||||
// should have no trailing data
|
||||
if (params.data.available() != 0) {
|
||||
throw new IOException("Extra parameter data");
|
||||
}
|
||||
|
||||
// privateKey
|
||||
byte[] key = val.data.getOctetString();
|
||||
DerInputStream in = new DerInputStream(key);
|
||||
BigInteger x = in.getBigInteger();
|
||||
|
||||
// should have no trailing data
|
||||
if (val.data.available() != 0) {
|
||||
throw new IOException("Excess trailing data");
|
||||
}
|
||||
return new DHComponents(x, p, g, l, key);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException("Error parsing key encoding", e);
|
||||
} finally {
|
||||
if (val != null) {
|
||||
val.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the ASN.1 encoding
|
||||
private static byte[] encode(BigInteger p, BigInteger g, int l,
|
||||
byte[] key) {
|
||||
DerOutputStream tmp = new DerOutputStream();
|
||||
|
||||
// version
|
||||
tmp.putInteger(PKCS8_VERSION);
|
||||
|
||||
// privateKeyAlgorithm
|
||||
DerOutputStream algid = new DerOutputStream();
|
||||
|
||||
// store OID
|
||||
algid.putOID(DHPublicKey.DH_OID);
|
||||
// encode parameters
|
||||
DerOutputStream params = new DerOutputStream();
|
||||
params.putInteger(p);
|
||||
params.putInteger(g);
|
||||
if (l != 0) {
|
||||
params.putInteger(l);
|
||||
}
|
||||
// wrap parameters into SEQUENCE
|
||||
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
|
||||
params.toByteArray());
|
||||
// store parameter SEQUENCE in algid
|
||||
algid.putDerValue(paramSequence);
|
||||
// wrap algid into SEQUENCE
|
||||
tmp.write(DerValue.tag_Sequence, algid);
|
||||
|
||||
// privateKey
|
||||
tmp.putOctetString(key);
|
||||
|
||||
// make it a SEQUENCE
|
||||
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
|
||||
byte[] encoded = val.toByteArray();
|
||||
val.clear();
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a DH private key out of a private value <code>x</code>, a prime
|
||||
* modulus <code>p</code>, and a base generator <code>g</code>.
|
||||
@ -79,7 +205,7 @@ final class DHPrivateKey implements PrivateKey,
|
||||
* @param g the base generator
|
||||
*/
|
||||
DHPrivateKey(BigInteger x, BigInteger p, BigInteger g)
|
||||
throws InvalidKeyException {
|
||||
throws InvalidKeyException {
|
||||
this(x, p, g, 0);
|
||||
}
|
||||
|
||||
@ -98,12 +224,16 @@ final class DHPrivateKey implements PrivateKey,
|
||||
this.p = p;
|
||||
this.g = g;
|
||||
this.l = l;
|
||||
|
||||
byte[] xbytes = x.toByteArray();
|
||||
DerValue val = new DerValue(DerValue.tag_Integer, xbytes);
|
||||
this.key = val.toByteArray();
|
||||
val.clear();
|
||||
Arrays.fill(xbytes, (byte) 0);
|
||||
encode();
|
||||
try {
|
||||
this.key = val.toByteArray();
|
||||
} finally {
|
||||
val.clear();
|
||||
Arrays.fill(xbytes, (byte) 0);
|
||||
}
|
||||
this.encodedKey = encode(p, g, l, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,75 +245,18 @@ final class DHPrivateKey implements PrivateKey,
|
||||
* a Diffie-Hellman private key
|
||||
*/
|
||||
DHPrivateKey(byte[] encodedKey) throws InvalidKeyException {
|
||||
DerValue val = null;
|
||||
this.encodedKey = encodedKey.clone();
|
||||
DHComponents dc;
|
||||
try {
|
||||
val = new DerValue(encodedKey);
|
||||
if (val.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException ("Key not a SEQUENCE");
|
||||
}
|
||||
|
||||
//
|
||||
// version
|
||||
//
|
||||
BigInteger parsedVersion = val.data.getBigInteger();
|
||||
if (!parsedVersion.equals(PKCS8_VERSION)) {
|
||||
throw new IOException("version mismatch: (supported: " +
|
||||
PKCS8_VERSION + ", parsed: " +
|
||||
parsedVersion);
|
||||
}
|
||||
|
||||
//
|
||||
// privateKeyAlgorithm
|
||||
//
|
||||
DerValue algid = val.data.getDerValue();
|
||||
if (algid.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException("AlgId is not a SEQUENCE");
|
||||
}
|
||||
DerInputStream derInStream = algid.toDerInputStream();
|
||||
ObjectIdentifier oid = derInStream.getOID();
|
||||
if (oid == null) {
|
||||
throw new InvalidKeyException("Null OID");
|
||||
}
|
||||
if (derInStream.available() == 0) {
|
||||
throw new InvalidKeyException("Parameters missing");
|
||||
}
|
||||
// parse the parameters
|
||||
DerValue params = derInStream.getDerValue();
|
||||
if (params.tag == DerValue.tag_Null) {
|
||||
throw new InvalidKeyException("Null parameters");
|
||||
}
|
||||
if (params.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException("Parameters not a SEQUENCE");
|
||||
}
|
||||
params.data.reset();
|
||||
this.p = params.data.getBigInteger();
|
||||
this.g = params.data.getBigInteger();
|
||||
// Private-value length is OPTIONAL
|
||||
if (params.data.available() != 0) {
|
||||
this.l = params.data.getInteger();
|
||||
} else {
|
||||
this.l = 0;
|
||||
}
|
||||
if (params.data.available() != 0) {
|
||||
throw new InvalidKeyException("Extra parameter data");
|
||||
}
|
||||
|
||||
//
|
||||
// privateKey
|
||||
//
|
||||
this.key = val.data.getOctetString();
|
||||
|
||||
DerInputStream in = new DerInputStream(this.key);
|
||||
this.x = in.getBigInteger();
|
||||
|
||||
this.encodedKey = encodedKey.clone();
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
throw new InvalidKeyException("Error parsing key encoding", e);
|
||||
} finally {
|
||||
if (val != null) {
|
||||
val.clear();
|
||||
}
|
||||
dc = decode(this.encodedKey);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidKeyException("Invalid encoding", e);
|
||||
}
|
||||
this.x = dc.x;
|
||||
this.p = dc.p;
|
||||
this.g = dc.g;
|
||||
this.l = dc.l;
|
||||
this.key = dc.key;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,55 +277,9 @@ final class DHPrivateKey implements PrivateKey,
|
||||
* Get the encoding of the key.
|
||||
*/
|
||||
public synchronized byte[] getEncoded() {
|
||||
encode();
|
||||
return encodedKey.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the encodedKey field if it has not been calculated.
|
||||
* Could generate null.
|
||||
*/
|
||||
private void encode() {
|
||||
if (this.encodedKey == null) {
|
||||
DerOutputStream tmp = new DerOutputStream();
|
||||
|
||||
//
|
||||
// version
|
||||
//
|
||||
tmp.putInteger(PKCS8_VERSION);
|
||||
|
||||
//
|
||||
// privateKeyAlgorithm
|
||||
//
|
||||
DerOutputStream algid = new DerOutputStream();
|
||||
|
||||
// store OID
|
||||
algid.putOID(DHPublicKey.DH_OID);
|
||||
// encode parameters
|
||||
DerOutputStream params = new DerOutputStream();
|
||||
params.putInteger(this.p);
|
||||
params.putInteger(this.g);
|
||||
if (this.l != 0) {
|
||||
params.putInteger(this.l);
|
||||
}
|
||||
// wrap parameters into SEQUENCE
|
||||
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
|
||||
params.toByteArray());
|
||||
// store parameter SEQUENCE in algid
|
||||
algid.putDerValue(paramSequence);
|
||||
// wrap algid into SEQUENCE
|
||||
tmp.write(DerValue.tag_Sequence, algid);
|
||||
|
||||
// privateKey
|
||||
tmp.putOctetString(this.key);
|
||||
|
||||
// make it a SEQUENCE
|
||||
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
|
||||
this.encodedKey = val.toByteArray();
|
||||
val.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private value, <code>x</code>.
|
||||
*
|
||||
@ -307,10 +334,7 @@ final class DHPrivateKey implements PrivateKey,
|
||||
*/
|
||||
@java.io.Serial
|
||||
private Object writeReplace() throws java.io.ObjectStreamException {
|
||||
encode();
|
||||
return new KeyRep(KeyRep.Type.PRIVATE,
|
||||
getAlgorithm(),
|
||||
getFormat(),
|
||||
return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(),
|
||||
encodedKey);
|
||||
}
|
||||
|
||||
@ -330,11 +354,28 @@ final class DHPrivateKey implements PrivateKey,
|
||||
if ((key == null) || (key.length == 0)) {
|
||||
throw new InvalidObjectException("key not deserializable");
|
||||
}
|
||||
this.key = key.clone();
|
||||
if ((encodedKey == null) || (encodedKey.length == 0)) {
|
||||
throw new InvalidObjectException(
|
||||
"encoded key not deserializable");
|
||||
}
|
||||
this.encodedKey = encodedKey.clone();
|
||||
// check if the "encodedKey" value matches the deserialized fields
|
||||
DHComponents c;
|
||||
byte[] encodedKeyIntern = encodedKey.clone();
|
||||
try {
|
||||
c = decode(encodedKeyIntern);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidObjectException("Invalid encoding", e);
|
||||
}
|
||||
if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p)
|
||||
|| !c.g.equals(g) || c.l != l) {
|
||||
throw new InvalidObjectException(
|
||||
"encoded key not matching internal fields");
|
||||
}
|
||||
// zero out external arrays
|
||||
Arrays.fill(key, (byte)0x00);
|
||||
Arrays.fill(encodedKey, (byte)0x00);
|
||||
// use self-created internal copies
|
||||
this.key = c.key;
|
||||
this.encodedKey = encodedKeyIntern;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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 @@
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyRep;
|
||||
@ -70,6 +71,116 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
static final ObjectIdentifier DH_OID =
|
||||
ObjectIdentifier.of(KnownOIDs.DiffieHellman);
|
||||
|
||||
private static class DHComponents {
|
||||
final BigInteger y;
|
||||
final BigInteger p;
|
||||
final BigInteger g;
|
||||
final int l;
|
||||
final byte[] key;
|
||||
|
||||
DHComponents(BigInteger y, BigInteger p, BigInteger g, int l,
|
||||
byte[] key) {
|
||||
this.y = y;
|
||||
this.p = p;
|
||||
this.g = g;
|
||||
this.l = l;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
// parses the specified encoding into a DHComponents object
|
||||
private static DHComponents decode(byte[] encodedKey)
|
||||
throws IOException {
|
||||
DerValue val = null;
|
||||
|
||||
try {
|
||||
val = new DerValue(encodedKey);
|
||||
if (val.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("Invalid key format");
|
||||
}
|
||||
|
||||
// algorithm identifier
|
||||
DerValue algid = val.data.getDerValue();
|
||||
if (algid.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("AlgId is not a SEQUENCE");
|
||||
}
|
||||
DerInputStream derInStream = algid.toDerInputStream();
|
||||
ObjectIdentifier oid = derInStream.getOID();
|
||||
if (oid == null) {
|
||||
throw new IOException("Null OID");
|
||||
}
|
||||
if (derInStream.available() == 0) {
|
||||
throw new IOException("Parameters missing");
|
||||
}
|
||||
|
||||
// parse the parameters
|
||||
DerValue params = derInStream.getDerValue();
|
||||
if (params.tag == DerValue.tag_Null) {
|
||||
throw new IOException("Null parameters");
|
||||
}
|
||||
if (params.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("Parameters not a SEQUENCE");
|
||||
}
|
||||
params.data.reset();
|
||||
|
||||
BigInteger p = params.data.getBigInteger();
|
||||
BigInteger g = params.data.getBigInteger();
|
||||
// Private-value length is OPTIONAL
|
||||
int l = (params.data.available() != 0 ? params.data.getInteger() :
|
||||
0);
|
||||
if (params.data.available() != 0) {
|
||||
throw new IOException("Extra parameter data");
|
||||
}
|
||||
|
||||
// publickey
|
||||
byte[] key = val.data.getBitString();
|
||||
DerInputStream in = new DerInputStream(key);
|
||||
BigInteger y = in.getBigInteger();
|
||||
|
||||
if (val.data.available() != 0) {
|
||||
throw new IOException("Excess key data");
|
||||
}
|
||||
return new DHComponents(y, p, g, l, key);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException("Error parsing key encoding", e);
|
||||
}
|
||||
}
|
||||
|
||||
// generates the ASN.1 encoding
|
||||
private static byte[] encode(BigInteger p, BigInteger g, int l,
|
||||
byte[] key) {
|
||||
DerOutputStream algid = new DerOutputStream();
|
||||
|
||||
// store oid in algid
|
||||
algid.putOID(DH_OID);
|
||||
|
||||
// encode parameters
|
||||
DerOutputStream params = new DerOutputStream();
|
||||
params.putInteger(p);
|
||||
params.putInteger(g);
|
||||
if (l != 0) {
|
||||
params.putInteger(l);
|
||||
}
|
||||
|
||||
// wrap parameters into SEQUENCE
|
||||
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
|
||||
params.toByteArray());
|
||||
// store parameter SEQUENCE in algid
|
||||
algid.putDerValue(paramSequence);
|
||||
|
||||
// wrap algid into SEQUENCE, and store it in key encoding
|
||||
DerOutputStream tmpDerKey = new DerOutputStream();
|
||||
tmpDerKey.write(DerValue.tag_Sequence, algid);
|
||||
|
||||
// store key data
|
||||
tmpDerKey.putBitString(key);
|
||||
|
||||
// wrap algid and key into SEQUENCE
|
||||
DerOutputStream derKey = new DerOutputStream();
|
||||
derKey.write(DerValue.tag_Sequence, tmpDerKey);
|
||||
return derKey.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a DH public key out of a public value <code>y</code>, a prime
|
||||
* modulus <code>p</code>, and a base generator <code>g</code>.
|
||||
@ -102,7 +213,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
this.l = l;
|
||||
this.key = new DerValue(DerValue.tag_Integer,
|
||||
this.y.toByteArray()).toByteArray();
|
||||
this.encodedKey = getEncoded();
|
||||
this.encodedKey = encode(p, g, l, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,68 +225,19 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
* a Diffie-Hellman public key
|
||||
*/
|
||||
DHPublicKey(byte[] encodedKey) throws InvalidKeyException {
|
||||
InputStream inStream = new ByteArrayInputStream(encodedKey);
|
||||
this.encodedKey = encodedKey.clone();
|
||||
|
||||
DHComponents dc;
|
||||
try {
|
||||
DerValue derKeyVal = new DerValue(inStream);
|
||||
if (derKeyVal.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException ("Invalid key format");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the algorithm identifier
|
||||
*/
|
||||
DerValue algid = derKeyVal.data.getDerValue();
|
||||
if (algid.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException("AlgId is not a SEQUENCE");
|
||||
}
|
||||
DerInputStream derInStream = algid.toDerInputStream();
|
||||
ObjectIdentifier oid = derInStream.getOID();
|
||||
if (oid == null) {
|
||||
throw new InvalidKeyException("Null OID");
|
||||
}
|
||||
if (derInStream.available() == 0) {
|
||||
throw new InvalidKeyException("Parameters missing");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the parameters
|
||||
*/
|
||||
DerValue params = derInStream.getDerValue();
|
||||
if (params.tag == DerValue.tag_Null) {
|
||||
throw new InvalidKeyException("Null parameters");
|
||||
}
|
||||
if (params.tag != DerValue.tag_Sequence) {
|
||||
throw new InvalidKeyException("Parameters not a SEQUENCE");
|
||||
}
|
||||
params.data.reset();
|
||||
this.p = params.data.getBigInteger();
|
||||
this.g = params.data.getBigInteger();
|
||||
// Private-value length is OPTIONAL
|
||||
if (params.data.available() != 0) {
|
||||
this.l = params.data.getInteger();
|
||||
} else {
|
||||
this.l = 0;
|
||||
}
|
||||
if (params.data.available() != 0) {
|
||||
throw new InvalidKeyException("Extra parameter data");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the key
|
||||
*/
|
||||
this.key = derKeyVal.data.getBitString();
|
||||
|
||||
DerInputStream in = new DerInputStream(this.key);
|
||||
this.y = in.getBigInteger();
|
||||
|
||||
if (derKeyVal.data.available() != 0) {
|
||||
throw new InvalidKeyException("Excess key data");
|
||||
}
|
||||
|
||||
this.encodedKey = encodedKey.clone();
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
throw new InvalidKeyException("Error parsing key encoding", e);
|
||||
dc = decode(this.encodedKey);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidKeyException("Invalid encoding", e);
|
||||
}
|
||||
this.y = dc.y;
|
||||
this.p = dc.p;
|
||||
this.g = dc.g;
|
||||
this.l = dc.l;
|
||||
this.key = dc.key;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,37 +258,6 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
* Get the encoding of the key.
|
||||
*/
|
||||
public synchronized byte[] getEncoded() {
|
||||
if (this.encodedKey == null) {
|
||||
DerOutputStream algid = new DerOutputStream();
|
||||
|
||||
// store oid in algid
|
||||
algid.putOID(DH_OID);
|
||||
|
||||
// encode parameters
|
||||
DerOutputStream params = new DerOutputStream();
|
||||
params.putInteger(this.p);
|
||||
params.putInteger(this.g);
|
||||
if (this.l != 0) {
|
||||
params.putInteger(this.l);
|
||||
}
|
||||
// wrap parameters into SEQUENCE
|
||||
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
|
||||
params.toByteArray());
|
||||
// store parameter SEQUENCE in algid
|
||||
algid.putDerValue(paramSequence);
|
||||
|
||||
// wrap algid into SEQUENCE, and store it in key encoding
|
||||
DerOutputStream tmpDerKey = new DerOutputStream();
|
||||
tmpDerKey.write(DerValue.tag_Sequence, algid);
|
||||
|
||||
// store key data
|
||||
tmpDerKey.putBitString(this.key);
|
||||
|
||||
// wrap algid and key into SEQUENCE
|
||||
DerOutputStream derKey = new DerOutputStream();
|
||||
derKey.write(DerValue.tag_Sequence, tmpDerKey);
|
||||
this.encodedKey = derKey.toByteArray();
|
||||
}
|
||||
return this.encodedKey.clone();
|
||||
}
|
||||
|
||||
@ -263,8 +294,9 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
+ Debug.toHexString(this.p)
|
||||
+ LINE_SEP + "g:" + LINE_SEP
|
||||
+ Debug.toHexString(this.g));
|
||||
if (this.l != 0)
|
||||
if (this.l != 0) {
|
||||
sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@ -304,7 +336,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
return new KeyRep(KeyRep.Type.PUBLIC,
|
||||
getAlgorithm(),
|
||||
getFormat(),
|
||||
getEncoded());
|
||||
encodedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -323,11 +355,28 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
|
||||
if ((key == null) || (key.length == 0)) {
|
||||
throw new InvalidObjectException("key not deserializable");
|
||||
}
|
||||
this.key = key.clone();
|
||||
if ((encodedKey == null) || (encodedKey.length == 0)) {
|
||||
throw new InvalidObjectException(
|
||||
"encoded key not deserializable");
|
||||
}
|
||||
this.encodedKey = encodedKey.clone();
|
||||
// check if the "encodedKey" value matches the deserialized fields
|
||||
DHComponents c;
|
||||
byte[] encodedKeyIntern = encodedKey.clone();
|
||||
try {
|
||||
c = decode(encodedKeyIntern);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidObjectException("Invalid encoding", e);
|
||||
}
|
||||
if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p)
|
||||
|| !c.g.equals(g) || c.l != l) {
|
||||
throw new InvalidObjectException(
|
||||
"encoded key not matching internal fields");
|
||||
}
|
||||
// zero out external arrays
|
||||
Arrays.fill(key, (byte)0x00);
|
||||
Arrays.fill(encodedKey, (byte)0x00);
|
||||
// use self-created internal copies
|
||||
this.key = c.key;
|
||||
this.encodedKey = encodedKeyIntern;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
@ -194,22 +194,24 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
|
||||
return key.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
if ((key == null) || (key.length == 0)) {
|
||||
throw new InvalidObjectException("TlsMasterSecretKey is null");
|
||||
}
|
||||
key = key.clone();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
if (key == null || key.length == 0) {
|
||||
throw new InvalidObjectException("TlsMasterSecretKey is null");
|
||||
}
|
||||
byte[] temp = key;
|
||||
this.key = temp.clone();
|
||||
Arrays.fill(temp, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,15 @@ to determine the proxy that should be used for connecting to a given URI.</P>
|
||||
</OL>
|
||||
<P>The channel binding tokens generated are of the type "tls-server-end-point" as defined in
|
||||
RFC 5929.</P>
|
||||
|
||||
<LI><P><B>{@systemProperty jdk.http.maxHeaderSize}</B> (default: 393216 or 384kB)<BR>
|
||||
This is the maximum header field section size that a client is prepared to accept.
|
||||
This is computed as the sum of the size of the uncompressed header name, plus
|
||||
the size of the uncompressed header value, plus an overhead of 32 bytes for
|
||||
each field section line. If a peer sends a field section that exceeds this
|
||||
size a {@link java.net.ProtocolException ProtocolException} will be raised.
|
||||
This applies to all versions of the HTTP protocol. A value of zero or a negative
|
||||
value means no limit. If left unspecified, the default value is 393216 bytes.
|
||||
</UL>
|
||||
<P>All these properties are checked only once at startup.</P>
|
||||
<a id="AddressCache"></a>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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
|
||||
@ -407,6 +407,11 @@ implements Serializable
|
||||
@SuppressWarnings("unchecked")
|
||||
Hashtable<Class<?>, PermissionCollection> perms =
|
||||
(Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);
|
||||
|
||||
if (perms == null) {
|
||||
throw new InvalidObjectException("perms can't be null");
|
||||
}
|
||||
|
||||
permsMap = new ConcurrentHashMap<>(perms.size()*2);
|
||||
permsMap.putAll(perms);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -152,20 +152,20 @@ public final class SignedObject implements Serializable {
|
||||
*/
|
||||
public SignedObject(Serializable object, PrivateKey signingKey,
|
||||
Signature signingEngine)
|
||||
throws IOException, InvalidKeyException, SignatureException {
|
||||
// creating a stream pipe-line, from a to b
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
ObjectOutput a = new ObjectOutputStream(b);
|
||||
throws IOException, InvalidKeyException, SignatureException {
|
||||
// creating a stream pipe-line, from a to b
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
ObjectOutput a = new ObjectOutputStream(b);
|
||||
|
||||
// write and flush the object content to byte array
|
||||
a.writeObject(object);
|
||||
a.flush();
|
||||
a.close();
|
||||
this.content = b.toByteArray();
|
||||
b.close();
|
||||
// write and flush the object content to byte array
|
||||
a.writeObject(object);
|
||||
a.flush();
|
||||
a.close();
|
||||
this.content = b.toByteArray();
|
||||
b.close();
|
||||
|
||||
// now sign the encapsulated object
|
||||
this.sign(signingKey, signingEngine);
|
||||
// now sign the encapsulated object
|
||||
this.sign(signingKey, signingEngine);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,12 +245,12 @@ public final class SignedObject implements Serializable {
|
||||
* @throws SignatureException if signing fails.
|
||||
*/
|
||||
private void sign(PrivateKey signingKey, Signature signingEngine)
|
||||
throws InvalidKeyException, SignatureException {
|
||||
// initialize the signing engine
|
||||
signingEngine.initSign(signingKey);
|
||||
signingEngine.update(this.content.clone());
|
||||
this.signature = signingEngine.sign().clone();
|
||||
this.thealgorithm = signingEngine.getAlgorithm();
|
||||
throws InvalidKeyException, SignatureException {
|
||||
// initialize the signing engine
|
||||
signingEngine.initSign(signingKey);
|
||||
signingEngine.update(this.content.clone());
|
||||
this.signature = signingEngine.sign();
|
||||
this.thealgorithm = signingEngine.getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,10 +263,16 @@ public final class SignedObject implements Serializable {
|
||||
*/
|
||||
@Serial
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
ObjectInputStream.GetField fields = s.readFields();
|
||||
content = ((byte[])fields.get("content", null)).clone();
|
||||
signature = ((byte[])fields.get("signature", null)).clone();
|
||||
thealgorithm = (String)fields.get("thealgorithm", null);
|
||||
throws IOException, ClassNotFoundException {
|
||||
ObjectInputStream.GetField fields = s.readFields();
|
||||
byte[] c = (byte[]) fields.get("content", null);
|
||||
byte[] sig = (byte[]) fields.get("signature", null);
|
||||
String a = (String) fields.get("thealgorithm", null);
|
||||
if (c == null || sig == null || a == null) {
|
||||
throw new InvalidObjectException("One or more null fields");
|
||||
}
|
||||
content = c.clone();
|
||||
signature = sig.clone();
|
||||
thealgorithm = a;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2024, 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
|
||||
@ -27,6 +27,7 @@ package java.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.Serializable;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.Certificate;
|
||||
@ -78,7 +79,7 @@ public final class Timestamp implements Serializable {
|
||||
* {@code null}.
|
||||
*/
|
||||
public Timestamp(Date timestamp, CertPath signerCertPath) {
|
||||
if (timestamp == null || signerCertPath == null) {
|
||||
if (isNull(timestamp, signerCertPath)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.timestamp = new Date(timestamp.getTime()); // clone
|
||||
@ -166,9 +167,16 @@ public final class Timestamp implements Serializable {
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream ois)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
if (isNull(timestamp, signerCertPath)) {
|
||||
throw new InvalidObjectException("Invalid null field(s)");
|
||||
}
|
||||
myhash = -1;
|
||||
timestamp = new Date(timestamp.getTime());
|
||||
}
|
||||
|
||||
private static boolean isNull(Date d, CertPath c) {
|
||||
return (d == null || c == null);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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,6 +29,7 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamField;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@ -196,23 +197,32 @@ implements java.io.Serializable
|
||||
ObjectInputStream.GetField gfields = in.readFields();
|
||||
|
||||
// Get permissions
|
||||
@SuppressWarnings("unchecked")
|
||||
// writeObject writes a Hashtable<String, Vector<UnresolvedPermission>>
|
||||
// for the permissions key, so this cast is safe, unless the data is corrupt.
|
||||
Hashtable<String, Vector<UnresolvedPermission>> permissions =
|
||||
(Hashtable<String, Vector<UnresolvedPermission>>)
|
||||
gfields.get("permissions", null);
|
||||
perms = new ConcurrentHashMap<>(permissions.size()*2);
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Hashtable<String, Vector<UnresolvedPermission>> permissions =
|
||||
(Hashtable<String, Vector<UnresolvedPermission>>)
|
||||
gfields.get("permissions", null);
|
||||
|
||||
// Convert each entry (Vector) into a List
|
||||
Set<Map.Entry<String, Vector<UnresolvedPermission>>> set = permissions.entrySet();
|
||||
for (Map.Entry<String, Vector<UnresolvedPermission>> e : set) {
|
||||
// Convert Vector into ArrayList
|
||||
Vector<UnresolvedPermission> vec = e.getValue();
|
||||
List<UnresolvedPermission> list = new CopyOnWriteArrayList<>(vec);
|
||||
if (permissions == null) {
|
||||
throw new InvalidObjectException("Invalid null permissions");
|
||||
}
|
||||
|
||||
// Add to Hashtable being serialized
|
||||
perms.put(e.getKey(), list);
|
||||
perms = new ConcurrentHashMap<>(permissions.size()*2);
|
||||
|
||||
// Convert each entry (Vector) into a List
|
||||
Set<Map.Entry<String, Vector<UnresolvedPermission>>> set = permissions.entrySet();
|
||||
for (Map.Entry<String, Vector<UnresolvedPermission>> e : set) {
|
||||
// Convert Vector into ArrayList
|
||||
Vector<UnresolvedPermission> vec = e.getValue();
|
||||
List<UnresolvedPermission> list = new CopyOnWriteArrayList<>(vec);
|
||||
|
||||
// Add to Hashtable being serialized
|
||||
perms.put(e.getKey(), list);
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
throw new InvalidObjectException("Invalid type for permissions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2024, 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,7 @@ package java.security.cert;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException {
|
||||
|
||||
private transient Map<String, Extension> extensions;
|
||||
|
||||
private static boolean isNull(Date revocationDate,
|
||||
CRLReason reason, X500Principal authority,
|
||||
Map<String, Extension> extensions) {
|
||||
return (revocationDate == null || reason == null || authority == null
|
||||
|| extensions == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code CertificateRevokedException} with
|
||||
* the specified revocation date, reason code, authority name, and map
|
||||
@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException {
|
||||
*/
|
||||
public CertificateRevokedException(Date revocationDate, CRLReason reason,
|
||||
X500Principal authority, Map<String, Extension> extensions) {
|
||||
if (revocationDate == null || reason == null || authority == null ||
|
||||
extensions == null) {
|
||||
if (isNull(revocationDate, reason, authority, extensions)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.revocationDate = new Date(revocationDate.getTime());
|
||||
@ -234,9 +241,6 @@ public class CertificateRevokedException extends CertificateException {
|
||||
// (revocationDate, reason, authority)
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Defensively copy the revocation date
|
||||
revocationDate = new Date(revocationDate.getTime());
|
||||
|
||||
// Read in the size (number of mappings) of the extensions map
|
||||
// and create the extensions map
|
||||
int size = ois.readInt();
|
||||
@ -247,6 +251,13 @@ public class CertificateRevokedException extends CertificateException {
|
||||
} else {
|
||||
extensions = HashMap.newHashMap(Math.min(size, 20));
|
||||
}
|
||||
// make sure all fields are set before checking
|
||||
if (isNull(revocationDate, reason, authority, extensions)) {
|
||||
throw new InvalidObjectException("Invalid null field(s)");
|
||||
}
|
||||
|
||||
// Defensively copy the revocation date
|
||||
revocationDate = new Date(revocationDate.getTime());
|
||||
|
||||
// Read in the extensions and put the mappings in the extensions map
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
@ -41,6 +41,7 @@ package java.text;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -1181,6 +1182,8 @@ public class MessageFormat extends Format {
|
||||
maximumArgumentNumber = argumentNumbers[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX
|
||||
Object[] resultArray = new Object[maximumArgumentNumber + 1];
|
||||
|
||||
int patternOffset = 0;
|
||||
@ -1459,6 +1462,9 @@ public class MessageFormat extends Format {
|
||||
* @serial
|
||||
*/
|
||||
private int[] argumentNumbers = new int[INITIAL_FORMATS];
|
||||
// Implementation limit for ArgumentIndex pattern element. Valid indices must
|
||||
// be less than this value
|
||||
private static final int MAX_ARGUMENT_INDEX = 10000;
|
||||
|
||||
/**
|
||||
* One less than the number of entries in {@code offsets}. Can also be thought of
|
||||
@ -1639,6 +1645,11 @@ public class MessageFormat extends Format {
|
||||
+ argumentNumber);
|
||||
}
|
||||
|
||||
if (argumentNumber >= MAX_ARGUMENT_INDEX) {
|
||||
throw new IllegalArgumentException(
|
||||
argumentNumber + " exceeds the ArgumentIndex implementation limit");
|
||||
}
|
||||
|
||||
// resize format information arrays if necessary
|
||||
if (offsetNumber >= formats.length) {
|
||||
int newLength = formats.length * 2;
|
||||
@ -2006,24 +2017,53 @@ public class MessageFormat extends Format {
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
boolean isValid = maxOffset >= -1
|
||||
&& formats.length > maxOffset
|
||||
&& offsets.length > maxOffset
|
||||
&& argumentNumbers.length > maxOffset;
|
||||
ObjectInputStream.GetField fields = in.readFields();
|
||||
if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets")
|
||||
|| fields.defaulted("formats") || fields.defaulted("locale")
|
||||
|| fields.defaulted("pattern") || fields.defaulted("maxOffset")){
|
||||
throw new InvalidObjectException("Stream has missing data");
|
||||
}
|
||||
|
||||
locale = (Locale) fields.get("locale", null);
|
||||
String patt = (String) fields.get("pattern", null);
|
||||
int maxOff = fields.get("maxOffset", -2);
|
||||
int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone();
|
||||
int[] offs = ((int[]) fields.get("offsets", null)).clone();
|
||||
Format[] fmts = ((Format[]) fields.get("formats", null)).clone();
|
||||
|
||||
// Check arrays/maxOffset have correct value/length
|
||||
boolean isValid = maxOff >= -1 && argNums.length > maxOff
|
||||
&& offs.length > maxOff && fmts.length > maxOff;
|
||||
|
||||
// Check the correctness of arguments and offsets
|
||||
if (isValid) {
|
||||
int lastOffset = pattern.length() + 1;
|
||||
for (int i = maxOffset; i >= 0; --i) {
|
||||
if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
|
||||
int lastOffset = patt.length() + 1;
|
||||
for (int i = maxOff; i >= 0; --i) {
|
||||
if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX
|
||||
|| offs[i] < 0 || offs[i] > lastOffset) {
|
||||
isValid = false;
|
||||
break;
|
||||
} else {
|
||||
lastOffset = offsets[i];
|
||||
lastOffset = offs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
|
||||
throw new InvalidObjectException("Stream has invalid data");
|
||||
}
|
||||
maxOffset = maxOff;
|
||||
pattern = patt;
|
||||
offsets = offs;
|
||||
formats = fmts;
|
||||
argumentNumbers = argNums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization without data not supported for this class.
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObjectNoData() throws ObjectStreamException {
|
||||
throw new InvalidObjectException("Deserialized MessageFormat objects need data");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2024, 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
|
||||
@ -100,11 +100,9 @@ public class SecretKeySpec implements KeySpec, SecretKey {
|
||||
* is null or <code>key</code> is null or empty.
|
||||
*/
|
||||
public SecretKeySpec(byte[] key, String algorithm) {
|
||||
if (key == null || algorithm == null) {
|
||||
throw new IllegalArgumentException("Missing argument");
|
||||
}
|
||||
if (key.length == 0) {
|
||||
throw new IllegalArgumentException("Empty key");
|
||||
String errMsg = doSanityCheck(key, algorithm);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
this.key = key.clone();
|
||||
this.algorithm = algorithm;
|
||||
@ -266,14 +264,22 @@ public class SecretKeySpec implements KeySpec, SecretKey {
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
String errMsg = doSanityCheck(key, algorithm);
|
||||
if (errMsg != null) {
|
||||
throw new InvalidObjectException(errMsg);
|
||||
}
|
||||
byte[] temp = key;
|
||||
this.key = temp.clone();
|
||||
Arrays.fill(temp, (byte) 0);
|
||||
}
|
||||
|
||||
private static String doSanityCheck(byte[] key, String algorithm) {
|
||||
String errMsg = null;
|
||||
if (key == null || algorithm == null) {
|
||||
throw new InvalidObjectException("Missing argument");
|
||||
}
|
||||
|
||||
this.key = key.clone();
|
||||
if (key.length == 0) {
|
||||
throw new InvalidObjectException("Invalid key length");
|
||||
errMsg = "Missing argument";
|
||||
} else if (key.length == 0) {
|
||||
errMsg = "Empty key";
|
||||
}
|
||||
return errMsg;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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,20 +102,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
|
||||
public ChoiceCallback(String prompt, String[] choices,
|
||||
int defaultChoice, boolean multipleSelectionsAllowed) {
|
||||
|
||||
if (prompt == null || prompt.isEmpty() ||
|
||||
choices == null || choices.length == 0 ||
|
||||
defaultChoice < 0 || defaultChoice >= choices.length)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
choices = (choices == null || choices.length == 0 ? choices :
|
||||
choices.clone());
|
||||
String errMsg = doSanityCheck(prompt, choices, defaultChoice,
|
||||
multipleSelectionsAllowed);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
this.prompt = prompt;
|
||||
this.defaultChoice = defaultChoice;
|
||||
this.multipleSelectionsAllowed = multipleSelectionsAllowed;
|
||||
|
||||
this.choices = choices.clone();
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
if (choices[i] == null || choices[i].isEmpty())
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.choices = choices;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,9 +181,11 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
|
||||
* @see #getSelectedIndexes
|
||||
*/
|
||||
public void setSelectedIndexes(int[] selections) {
|
||||
if (!multipleSelectionsAllowed)
|
||||
if (!multipleSelectionsAllowed) {
|
||||
throw new UnsupportedOperationException();
|
||||
this.selections = selections == null ? null : selections.clone();
|
||||
}
|
||||
this.selections = ((selections == null || selections.length == 0) ?
|
||||
selections : selections.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,26 +211,35 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
choices = (choices == null || choices.length == 0 ?
|
||||
choices : choices.clone());
|
||||
String errMsg = doSanityCheck(prompt, choices, defaultChoice,
|
||||
multipleSelectionsAllowed);
|
||||
if (errMsg != null) {
|
||||
throw new InvalidObjectException(errMsg);
|
||||
}
|
||||
|
||||
selections = (selections == null || selections.length == 0 ?
|
||||
selections : selections.clone());
|
||||
if (selections != null && selections.length > 1 &&
|
||||
!multipleSelectionsAllowed) {
|
||||
throw new InvalidObjectException("Multiple selections not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
private static String doSanityCheck(String prompt, String[] choices,
|
||||
int defaultChoice, boolean allowMultiple) {
|
||||
if ((prompt == null) || prompt.isEmpty() ||
|
||||
(choices == null) || (choices.length == 0) ||
|
||||
(defaultChoice < 0) || (defaultChoice >= choices.length)) {
|
||||
throw new InvalidObjectException(
|
||||
"Missing/invalid prompt/choices");
|
||||
return "Missing/invalid prompt/choices";
|
||||
}
|
||||
|
||||
choices = choices.clone();
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
if ((choices[i] == null) || choices[i].isEmpty())
|
||||
throw new InvalidObjectException("Null/empty choices");
|
||||
}
|
||||
|
||||
if (selections != null) {
|
||||
selections = selections.clone();
|
||||
if (!multipleSelectionsAllowed && (selections.length != 1)) {
|
||||
throw new InvalidObjectException(
|
||||
"Multiple selections not allowed");
|
||||
if ((choices[i] == null) || choices[i].isEmpty()) {
|
||||
return "Null/empty choices value";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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 @@
|
||||
package javax.security.auth.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
*/
|
||||
public ConfirmationCallback(int messageType,
|
||||
int optionType, int defaultOption) {
|
||||
|
||||
if (messageType < INFORMATION || messageType > ERROR ||
|
||||
optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
switch (optionType) {
|
||||
case YES_NO_OPTION:
|
||||
if (defaultOption != YES && defaultOption != NO)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
case YES_NO_CANCEL_OPTION:
|
||||
if (defaultOption != YES && defaultOption != NO &&
|
||||
defaultOption != CANCEL)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
case OK_CANCEL_OPTION:
|
||||
if (defaultOption != OK && defaultOption != CANCEL)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
String errMsg = doSanityCheck(messageType, optionType, false, null,
|
||||
defaultOption, null, false);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
|
||||
this.prompt = null;
|
||||
@ -250,21 +236,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
public ConfirmationCallback(int messageType,
|
||||
String[] options, int defaultOption) {
|
||||
|
||||
if (messageType < INFORMATION || messageType > ERROR ||
|
||||
options == null || options.length == 0 ||
|
||||
defaultOption < 0 || defaultOption >= options.length)
|
||||
throw new IllegalArgumentException();
|
||||
if (options != null) {
|
||||
options = options.clone();
|
||||
}
|
||||
String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true,
|
||||
options, defaultOption, null, false);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
|
||||
this.prompt = null;
|
||||
this.messageType = messageType;
|
||||
this.optionType = UNSPECIFIED_OPTION;
|
||||
this.defaultOption = defaultOption;
|
||||
|
||||
this.options = options.clone();
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
if (options[i] == null || options[i].isEmpty())
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,27 +289,11 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
public ConfirmationCallback(String prompt, int messageType,
|
||||
int optionType, int defaultOption) {
|
||||
|
||||
if (prompt == null || prompt.isEmpty() ||
|
||||
messageType < INFORMATION || messageType > ERROR ||
|
||||
optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
switch (optionType) {
|
||||
case YES_NO_OPTION:
|
||||
if (defaultOption != YES && defaultOption != NO)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
case YES_NO_CANCEL_OPTION:
|
||||
if (defaultOption != YES && defaultOption != NO &&
|
||||
defaultOption != CANCEL)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
case OK_CANCEL_OPTION:
|
||||
if (defaultOption != OK && defaultOption != CANCEL)
|
||||
throw new IllegalArgumentException();
|
||||
break;
|
||||
String errMsg = doSanityCheck(messageType, optionType, false, null,
|
||||
defaultOption, prompt, true);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
|
||||
this.prompt = prompt;
|
||||
this.messageType = messageType;
|
||||
this.optionType = optionType;
|
||||
@ -369,22 +338,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
public ConfirmationCallback(String prompt, int messageType,
|
||||
String[] options, int defaultOption) {
|
||||
|
||||
if (prompt == null || prompt.isEmpty() ||
|
||||
messageType < INFORMATION || messageType > ERROR ||
|
||||
options == null || options.length == 0 ||
|
||||
defaultOption < 0 || defaultOption >= options.length)
|
||||
throw new IllegalArgumentException();
|
||||
if (options != null) {
|
||||
options = options.clone();
|
||||
}
|
||||
String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true,
|
||||
options, defaultOption, prompt, true);
|
||||
if (errMsg != null) {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
|
||||
this.prompt = prompt;
|
||||
this.messageType = messageType;
|
||||
this.optionType = UNSPECIFIED_OPTION;
|
||||
this.defaultOption = defaultOption;
|
||||
|
||||
this.options = options.clone();
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
if (options[i] == null || options[i].isEmpty())
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,6 +458,49 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
return selection;
|
||||
}
|
||||
|
||||
private static String doSanityCheck(int msgType, int optionType,
|
||||
boolean isUnspecifiedOption, String[] options, int defOption,
|
||||
String prompt, boolean checkPrompt) {
|
||||
// validate msgType
|
||||
if (msgType < INFORMATION || msgType > ERROR) {
|
||||
return "Invalid msgType";
|
||||
}
|
||||
// validate prompt if checkPrompt == true
|
||||
if (checkPrompt && (prompt == null || prompt.isEmpty())) {
|
||||
return "Invalid prompt";
|
||||
}
|
||||
// validate optionType
|
||||
if (isUnspecifiedOption) {
|
||||
if (optionType != UNSPECIFIED_OPTION) {
|
||||
return "Invalid optionType";
|
||||
}
|
||||
// check options
|
||||
if (options == null || options.length == 0 ||
|
||||
defOption < 0 || defOption >= options.length) {
|
||||
return "Invalid options and/or default option";
|
||||
}
|
||||
for (String ov : options) {
|
||||
if (ov == null || ov.isEmpty()) {
|
||||
return "Invalid option value";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) {
|
||||
return "Invalid optionType";
|
||||
}
|
||||
// validate defOption based on optionType
|
||||
if ((optionType == YES_NO_OPTION && (defOption != YES &&
|
||||
defOption != NO)) ||
|
||||
(optionType == YES_NO_CANCEL_OPTION && (defOption != YES &&
|
||||
defOption != NO && defOption != CANCEL)) ||
|
||||
(optionType == OK_CANCEL_OPTION && (defOption != OK &&
|
||||
defOption != CANCEL))) {
|
||||
return "Invalid default option";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
@ -502,8 +512,15 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
|
||||
if (options != null) {
|
||||
options = options.clone();
|
||||
}
|
||||
String errMsg = doSanityCheck(messageType, optionType,
|
||||
(optionType == UNSPECIFIED_OPTION), options, defaultOption,
|
||||
prompt, false);
|
||||
if (errMsg != null) {
|
||||
throw new InvalidObjectException(errMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
@ -178,7 +178,9 @@ public class PasswordCallback implements Callback, java.io.Serializable {
|
||||
}
|
||||
|
||||
if (inputPassword != null) {
|
||||
inputPassword = inputPassword.clone();
|
||||
char[] temp = inputPassword;
|
||||
inputPassword = temp.clone();
|
||||
Arrays.fill(temp, '0');
|
||||
cleanable = CleanerFactory.cleaner().register(
|
||||
this, cleanerFor(inputPassword));
|
||||
}
|
||||
|
@ -30,6 +30,8 @@
|
||||
package sun.net.www;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
|
||||
@ -45,11 +47,32 @@ public final class MessageHeader {
|
||||
private String[] values;
|
||||
private int nkeys;
|
||||
|
||||
// max number of bytes for headers, <=0 means unlimited;
|
||||
// this corresponds to the length of the names, plus the length
|
||||
// of the values, plus an overhead of 32 bytes per name: value
|
||||
// pair.
|
||||
// Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
// see RFC 9113, section 6.5.2.
|
||||
// https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
private final int maxHeaderSize;
|
||||
|
||||
// Aggregate size of the field lines (name + value + 32) x N
|
||||
// that have been parsed and accepted so far.
|
||||
// This is defined as a long to force promotion to long
|
||||
// and avoid overflows; see checkNewSize;
|
||||
private long size;
|
||||
|
||||
public MessageHeader () {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public MessageHeader (int maxHeaderSize) {
|
||||
this.maxHeaderSize = maxHeaderSize;
|
||||
grow();
|
||||
}
|
||||
|
||||
public MessageHeader (InputStream is) throws java.io.IOException {
|
||||
maxHeaderSize = 0;
|
||||
parseHeader(is);
|
||||
}
|
||||
|
||||
@ -476,10 +499,28 @@ public final class MessageHeader {
|
||||
public void parseHeader(InputStream is) throws java.io.IOException {
|
||||
synchronized (this) {
|
||||
nkeys = 0;
|
||||
size = 0;
|
||||
}
|
||||
mergeHeader(is);
|
||||
}
|
||||
|
||||
private void checkMaxHeaderSize(int sz) throws ProtocolException {
|
||||
if (maxHeaderSize > 0) checkNewSize(size, sz, 0);
|
||||
}
|
||||
|
||||
private long checkNewSize(long size, int name, int value) throws ProtocolException {
|
||||
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
|
||||
long newSize = size + name + value + 32;
|
||||
if (maxHeaderSize > 0 && newSize > maxHeaderSize) {
|
||||
Arrays.fill(keys, 0, nkeys, null);
|
||||
Arrays.fill(values,0, nkeys, null);
|
||||
nkeys = 0;
|
||||
throw new ProtocolException(String.format("Header size too big: %s > %s",
|
||||
newSize, maxHeaderSize));
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
|
||||
/** Parse and merge a MIME header from an input stream. */
|
||||
@SuppressWarnings("fallthrough")
|
||||
public void mergeHeader(InputStream is) throws java.io.IOException {
|
||||
@ -493,7 +534,15 @@ public final class MessageHeader {
|
||||
int c;
|
||||
boolean inKey = firstc > ' ';
|
||||
s[len++] = (char) firstc;
|
||||
checkMaxHeaderSize(len);
|
||||
parseloop:{
|
||||
// We start parsing for a new name value pair here.
|
||||
// The max header size includes an overhead of 32 bytes per
|
||||
// name value pair.
|
||||
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
|
||||
long maxRemaining = maxHeaderSize > 0
|
||||
? maxHeaderSize - size - 32
|
||||
: Long.MAX_VALUE;
|
||||
while ((c = is.read()) >= 0) {
|
||||
switch (c) {
|
||||
case ':':
|
||||
@ -527,6 +576,9 @@ public final class MessageHeader {
|
||||
s = ns;
|
||||
}
|
||||
s[len++] = (char) c;
|
||||
if (maxHeaderSize > 0 && len > maxRemaining) {
|
||||
checkMaxHeaderSize(len);
|
||||
}
|
||||
}
|
||||
firstc = -1;
|
||||
}
|
||||
@ -548,6 +600,9 @@ public final class MessageHeader {
|
||||
v = new String();
|
||||
else
|
||||
v = String.copyValueOf(s, keyend, len - keyend);
|
||||
int klen = k == null ? 0 : k.length();
|
||||
|
||||
size = checkNewSize(size, klen, v.length());
|
||||
add(k, v);
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
*/
|
||||
private static final int bufSize4ES;
|
||||
|
||||
private static final int maxHeaderSize;
|
||||
|
||||
/*
|
||||
* Restrict setting of request headers through the public api
|
||||
* consistent with JavaScript XMLHttpRequest2 with a few
|
||||
@ -288,6 +290,19 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} else {
|
||||
restrictedHeaderSet = null;
|
||||
}
|
||||
|
||||
int defMaxHeaderSize = 384 * 1024;
|
||||
String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize");
|
||||
int maxHeaderSizeVal = defMaxHeaderSize;
|
||||
if (maxHeaderSizeStr != null) {
|
||||
try {
|
||||
maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr);
|
||||
} catch (NumberFormatException n) {
|
||||
maxHeaderSizeVal = defMaxHeaderSize;
|
||||
}
|
||||
}
|
||||
if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0;
|
||||
maxHeaderSize = maxHeaderSizeVal;
|
||||
}
|
||||
|
||||
static final String httpVersion = "HTTP/1.1";
|
||||
@ -754,7 +769,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
ps = (PrintStream) http.getOutputStream();
|
||||
connected=true;
|
||||
responses = new MessageHeader();
|
||||
responses = new MessageHeader(maxHeaderSize);
|
||||
setRequests=false;
|
||||
writeRequests();
|
||||
}
|
||||
@ -912,7 +927,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
throws IOException {
|
||||
super(checkURL(u));
|
||||
requests = new MessageHeader();
|
||||
responses = new MessageHeader();
|
||||
responses = new MessageHeader(maxHeaderSize);
|
||||
userHeaders = new MessageHeader();
|
||||
this.handler = handler;
|
||||
instProxy = p;
|
||||
@ -2810,7 +2825,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
|
||||
// clear out old response headers!!!!
|
||||
responses = new MessageHeader();
|
||||
responses = new MessageHeader(maxHeaderSize);
|
||||
if (stat == HTTP_USE_PROXY) {
|
||||
/* This means we must re-request the resource through the
|
||||
* proxy denoted in the "Location:" field of the response.
|
||||
@ -3000,7 +3015,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
} catch (IOException e) { }
|
||||
}
|
||||
responseCode = -1;
|
||||
responses = new MessageHeader();
|
||||
responses = new MessageHeader(maxHeaderSize);
|
||||
connected = false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2024, 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 @@
|
||||
package sun.security.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.security.AccessController;
|
||||
import java.security.DrbgParameters;
|
||||
import java.security.PrivilegedAction;
|
||||
@ -272,11 +273,18 @@ public final class DRBG extends SecureRandomSpi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param s the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
if (mdp.mech == null) {
|
||||
if (mdp == null || mdp.mech == null) {
|
||||
throw new IllegalArgumentException("Input data is corrupted");
|
||||
}
|
||||
createImpl();
|
||||
|
@ -213,8 +213,6 @@ final class ClientHello {
|
||||
// ignore cookie
|
||||
hos.putBytes16(getEncodedCipherSuites());
|
||||
hos.putBytes8(compressionMethod);
|
||||
extensions.send(hos); // In TLS 1.3, use of certain
|
||||
// extensions is mandatory.
|
||||
} catch (IOException ioe) {
|
||||
// unlikely
|
||||
}
|
||||
@ -1426,6 +1424,9 @@ final class ClientHello {
|
||||
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
|
||||
SSLHandshake.SERVER_HELLO);
|
||||
|
||||
// Reset the ClientHello non-zero offset fragment allowance
|
||||
shc.acceptCliHelloFragments = false;
|
||||
|
||||
//
|
||||
// produce
|
||||
//
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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,12 +40,23 @@ import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
final class DTLSInputRecord extends InputRecord implements DTLSRecord {
|
||||
private DTLSReassembler reassembler = null;
|
||||
private int readEpoch;
|
||||
private SSLContextImpl sslContext;
|
||||
|
||||
DTLSInputRecord(HandshakeHash handshakeHash) {
|
||||
super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
|
||||
this.readEpoch = 0;
|
||||
}
|
||||
|
||||
// Method to set TransportContext
|
||||
public void setTransportContext(TransportContext tc) {
|
||||
this.tc = tc;
|
||||
}
|
||||
|
||||
// Method to set SSLContext
|
||||
public void setSSLContext(SSLContextImpl sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
void changeReadCiphers(SSLReadCipher readCipher) {
|
||||
this.readCipher = readCipher;
|
||||
@ -537,6 +548,27 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a sufficiently-large initial ClientHello fragment into one that
|
||||
* stops immediately after the compression methods. This is only used
|
||||
* for the initial CH message fragment at offset 0.
|
||||
*
|
||||
* @param srcFrag the fragment actually received by the DTLSReassembler
|
||||
* @param limit the size of the new, cloned/truncated handshake fragment
|
||||
*
|
||||
* @return a truncated handshake fragment that is sized to look like a
|
||||
* complete message, but actually contains only up to the compression
|
||||
* methods (no extensions)
|
||||
*/
|
||||
private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag,
|
||||
int limit) {
|
||||
return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit),
|
||||
srcFrag.contentType, srcFrag.majorVersion,
|
||||
srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch,
|
||||
srcFrag.recordSeq, srcFrag.handshakeType, limit,
|
||||
srcFrag.messageSeq, srcFrag.fragmentOffset, limit);
|
||||
}
|
||||
|
||||
private static final class HoleDescriptor {
|
||||
int offset; // fragment_offset
|
||||
int limit; // fragment_offset + fragment_length
|
||||
@ -640,10 +672,17 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
|
||||
// Queue up a handshake message.
|
||||
void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
|
||||
if (!isDesirable(hsf)) {
|
||||
// Not a dedired record, discard it.
|
||||
// Not a desired record, discard it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) {
|
||||
// validate the first or subsequent ClientHello message
|
||||
if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the retransmission messages if necessary.
|
||||
cleanUpRetransmit(hsf);
|
||||
|
||||
@ -769,6 +808,100 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
|
||||
}
|
||||
}
|
||||
|
||||
private HandshakeFragment valHello(HandshakeFragment hsf,
|
||||
boolean firstHello) {
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext) tc.handshakeContext;
|
||||
// Drop any fragment that is not a zero offset until we've received
|
||||
// a second (or possibly later) CH message that passes the cookie
|
||||
// check.
|
||||
if (shc == null || !shc.acceptCliHelloFragments) {
|
||||
if (hsf.fragmentOffset != 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Let this fragment through to the DTLSReassembler as-is
|
||||
return hsf;
|
||||
}
|
||||
|
||||
try {
|
||||
ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment);
|
||||
|
||||
ProtocolVersion pv = ProtocolVersion.valueOf(
|
||||
Record.getInt16(fragmentData));
|
||||
if (!pv.isDTLS) {
|
||||
return null;
|
||||
}
|
||||
// Read the random (32 bytes)
|
||||
if (fragmentData.remaining() < 32) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine("Rejected client hello fragment (bad random len) " +
|
||||
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
fragmentData.position(fragmentData.position() + 32);
|
||||
|
||||
// SessionID
|
||||
byte[] sessId = Record.getBytes8(fragmentData);
|
||||
if (sessId.length > 0 &&
|
||||
!SSLConfiguration.enableDtlsResumeCookie) {
|
||||
// If we are in a resumption it is possible that the cookie
|
||||
// exchange will be skipped. This is a server-side setting
|
||||
// and it is NOT the default. If enableDtlsResumeCookie is
|
||||
// false though, then we will buffer fragments since there
|
||||
// is no cookie exchange to execute prior to performing
|
||||
// reassembly.
|
||||
return hsf;
|
||||
}
|
||||
|
||||
// Cookie
|
||||
byte[] cookie = Record.getBytes8(fragmentData);
|
||||
if (firstHello && cookie.length != 0) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " +
|
||||
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// CipherSuites
|
||||
Record.getBytes16(fragmentData);
|
||||
// Compression methods
|
||||
Record.getBytes8(fragmentData);
|
||||
|
||||
// If it's the first fragment, we'll truncate it and push it
|
||||
// through the reassembler.
|
||||
if (firstHello) {
|
||||
return truncateChFragment(hsf, fragmentData.position());
|
||||
} else {
|
||||
HelloCookieManager hcMgr = sslContext.
|
||||
getHelloCookieManager(ProtocolVersion.DTLS10);
|
||||
ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0,
|
||||
fragmentData.position());
|
||||
ClientHello.ClientHelloMessage chMsg =
|
||||
new ClientHello.ClientHelloMessage(shc, msgFragBuf, null);
|
||||
if (!hcMgr.isCookieValid(shc, chMsg, cookie)) {
|
||||
// Bad cookie check, truncate it and let the ClientHello
|
||||
// consumer recheck, fail and take the appropriate action.
|
||||
return truncateChFragment(hsf, fragmentData.position());
|
||||
} else {
|
||||
// It's a good cookie, return the original handshake
|
||||
// fragment and let it go into the DTLSReassembler like
|
||||
// any other fragment so we can wait for the rest of
|
||||
// the CH message.
|
||||
shc.acceptCliHelloFragments = true;
|
||||
return hsf;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine("Rejected client hello fragment " +
|
||||
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Queue up a ChangeCipherSpec message
|
||||
void queueUpChangeCipherSpec(RecordFragment rf)
|
||||
throws SSLProtocolException {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2024, 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,6 +55,7 @@ class ServerHandshakeContext extends HandshakeContext {
|
||||
CertificateMessage.CertificateEntry currentCertEntry;
|
||||
private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
|
||||
final long statusRespTimeout;
|
||||
boolean acceptCliHelloFragments = false;
|
||||
|
||||
|
||||
ServerHandshakeContext(SSLContextImpl sslContext,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2024, 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
|
||||
@ -156,6 +156,11 @@ final class TransportContext implements ConnectionContext {
|
||||
|
||||
this.acc = AccessController.getContext();
|
||||
this.consumers = new HashMap<>();
|
||||
|
||||
if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) {
|
||||
dtlsInputRecord.setTransportContext(this);
|
||||
dtlsInputRecord.setSSLContext(this.sslContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch plaintext to a specific consumer.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2024, 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
|
||||
@ -127,13 +127,24 @@ public final class ObjectIdentifier implements Serializable {
|
||||
// Is the component's field calculated?
|
||||
private transient boolean componentsCalculated = false;
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param is the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream is)
|
||||
throws IOException, ClassNotFoundException {
|
||||
is.defaultReadObject();
|
||||
|
||||
if (encoding == null) { // from an old version
|
||||
int[] comp = (int[])components;
|
||||
if (components == null) {
|
||||
throw new InvalidObjectException("OID components is null");
|
||||
}
|
||||
|
||||
int[] comp = ((int[]) components).clone();
|
||||
if (componentLen > comp.length) {
|
||||
componentLen = comp.length;
|
||||
}
|
||||
@ -142,7 +153,9 @@ public final class ObjectIdentifier implements Serializable {
|
||||
// will be performed again in init().
|
||||
checkOidSize(componentLen);
|
||||
init(comp, componentLen);
|
||||
components = comp;
|
||||
} else {
|
||||
encoding = encoding.clone(); // defensive copying
|
||||
checkOidSize(encoding.length);
|
||||
check(encoding);
|
||||
}
|
||||
@ -261,6 +274,7 @@ public final class ObjectIdentifier implements Serializable {
|
||||
encoding = in.getDerValue().getOID().encoding;
|
||||
}
|
||||
|
||||
// set 'encoding' field based on the specified 'components' and 'length'
|
||||
private void init(int[] components, int length) throws IOException {
|
||||
int pos = 0;
|
||||
byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,11 +25,13 @@
|
||||
|
||||
package sun.security.x509;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.DSAParams;
|
||||
|
||||
import java.util.Arrays;
|
||||
import sun.security.util.*;
|
||||
|
||||
|
||||
@ -72,33 +74,42 @@ import sun.security.util.*;
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public final
|
||||
class AlgIdDSA extends AlgorithmId implements DSAParams
|
||||
{
|
||||
public final class AlgIdDSA extends AlgorithmId implements DSAParams {
|
||||
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 3437177836797504046L;
|
||||
|
||||
private static class DSAComponents {
|
||||
private final BigInteger p;
|
||||
private final BigInteger q;
|
||||
private final BigInteger g;
|
||||
DSAComponents(BigInteger p, BigInteger q, BigInteger g) {
|
||||
this.p = p;
|
||||
this.q = q;
|
||||
this.g = g;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The three unsigned integer parameters.
|
||||
*/
|
||||
private BigInteger p , q, g;
|
||||
private BigInteger p, q, g;
|
||||
|
||||
/** Returns the DSS/DSA parameter "P" */
|
||||
public BigInteger getP () { return p; }
|
||||
public BigInteger getP() { return p; }
|
||||
|
||||
/** Returns the DSS/DSA parameter "Q" */
|
||||
public BigInteger getQ () { return q; }
|
||||
public BigInteger getQ() { return q; }
|
||||
|
||||
/** Returns the DSS/DSA parameter "G" */
|
||||
public BigInteger getG () { return g; }
|
||||
public BigInteger getG() { return g; }
|
||||
|
||||
/**
|
||||
* Default constructor. The OID and parameters must be
|
||||
* deserialized before this algorithm ID is used.
|
||||
*/
|
||||
@Deprecated
|
||||
public AlgIdDSA () {}
|
||||
public AlgIdDSA() {}
|
||||
|
||||
/**
|
||||
* Constructs a DSS/DSA Algorithm ID from numeric parameters.
|
||||
@ -109,7 +120,7 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
|
||||
* @param q the DSS/DSA parameter "Q"
|
||||
* @param g the DSS/DSA parameter "G"
|
||||
*/
|
||||
public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) {
|
||||
public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) {
|
||||
super (DSA_oid);
|
||||
|
||||
if (p != null || q != null || g != null) {
|
||||
@ -120,8 +131,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
|
||||
this.p = p;
|
||||
this.q = q;
|
||||
this.g = g;
|
||||
initializeParams ();
|
||||
|
||||
// For algorithm IDs which haven't been created from a DER
|
||||
// encoded value, need to create DER encoding and store it
|
||||
// into "encodedParams"
|
||||
encodedParams = encode(p, q, g);
|
||||
} catch (IOException e) {
|
||||
/* this should not happen */
|
||||
throw new ProviderException ("Construct DSS/DSA Algorithm ID");
|
||||
@ -133,50 +146,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
|
||||
* Returns "DSA", indicating the Digital Signature Algorithm (DSA) as
|
||||
* defined by the Digital Signature Standard (DSS), FIPS 186.
|
||||
*/
|
||||
public String getName ()
|
||||
{ return "DSA"; }
|
||||
|
||||
|
||||
/*
|
||||
* For algorithm IDs which haven't been created from a DER encoded
|
||||
* value, "params" must be created.
|
||||
*/
|
||||
private void initializeParams () throws IOException {
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(p);
|
||||
out.putInteger(q);
|
||||
out.putInteger(g);
|
||||
DerOutputStream result = new DerOutputStream();
|
||||
result.write(DerValue.tag_Sequence, out);
|
||||
encodedParams = result.toByteArray();
|
||||
public String getName() {
|
||||
return "DSA";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses algorithm parameters P, Q, and G. They're found
|
||||
* in the "params" member, which never needs to be changed.
|
||||
*/
|
||||
protected void decodeParams () throws IOException {
|
||||
if (encodedParams == null) {
|
||||
throw new IOException("DSA alg params are null");
|
||||
}
|
||||
|
||||
DerValue params = new DerValue(encodedParams);
|
||||
if (params.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("DSA alg parsing error");
|
||||
}
|
||||
|
||||
params.data.reset ();
|
||||
|
||||
this.p = params.data.getBigInteger();
|
||||
this.q = params.data.getBigInteger();
|
||||
this.g = params.data.getBigInteger();
|
||||
|
||||
if (params.data.available () != 0)
|
||||
throw new IOException ("AlgIdDSA params, extra="+
|
||||
params.data.available ());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns a formatted string describing the parameters.
|
||||
*/
|
||||
@ -197,4 +170,44 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
|
||||
"\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream. Override to check
|
||||
* on the 'p', 'q', 'g', and 'encodedParams'.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream) throws IOException {
|
||||
try {
|
||||
stream.defaultReadObject();
|
||||
// if any of the 'p', 'q', 'g', 'encodedParams' is non-null,
|
||||
// then they must be all non-null w/ matching encoding
|
||||
if ((p != null || q != null || g != null || encodedParams != null)
|
||||
&& !Arrays.equals(encodedParams, encode(p, q, g))) {
|
||||
throw new InvalidObjectException("Invalid DSA alg params");
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the DER encoding w/ the specified 'p', 'q', 'g'
|
||||
*/
|
||||
private static byte[] encode(BigInteger p, BigInteger q,
|
||||
BigInteger g) throws IOException {
|
||||
if (p == null || q == null || g == null) {
|
||||
throw new InvalidObjectException("invalid null value");
|
||||
}
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(p);
|
||||
out.putInteger(q);
|
||||
out.putInteger(g);
|
||||
DerOutputStream result = new DerOutputStream();
|
||||
result.write(DerValue.tag_Sequence, out);
|
||||
return result.toByteArray();
|
||||
}
|
||||
}
|
||||
|
@ -130,3 +130,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic
|
||||
#jdk.http.ntlm.transparentAuth=trustedHosts
|
||||
#
|
||||
jdk.http.ntlm.transparentAuth=disabled
|
||||
|
||||
#
|
||||
# Maximum HTTP field section size that a client is prepared to accept
|
||||
#
|
||||
# jdk.http.maxHeaderSize=393216
|
||||
#
|
||||
# This is the maximum header field section size that a client is prepared to accept.
|
||||
# This is computed as the sum of the size of the uncompressed header name, plus
|
||||
# the size of the uncompressed header value, plus an overhead of 32 bytes for
|
||||
# each field section line. If a peer sends a field section that exceeds this
|
||||
# size a {@link java.net.ProtocolException ProtocolException} will be raised.
|
||||
# This applies to all versions of the HTTP protocol. A value of zero or a negative
|
||||
# value means no limit. If left unspecified, the default value is 393216 bytes
|
||||
# or 384kB.
|
||||
#
|
||||
# Note: This property is currently used by the JDK Reference implementation. It
|
||||
# is not guaranteed to be examined and used by other implementations.
|
||||
|
@ -41,6 +41,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpHeaders;
|
||||
@ -69,6 +70,8 @@ import static jdk.internal.net.http.common.Utils.permissionForProxy;
|
||||
*/
|
||||
final class Exchange<T> {
|
||||
|
||||
static final int MAX_NON_FINAL_RESPONSES =
|
||||
Utils.getIntegerNetProperty("jdk.httpclient.maxNonFinalResponses", 8);
|
||||
final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
|
||||
|
||||
final HttpRequestImpl request;
|
||||
@ -93,6 +96,8 @@ final class Exchange<T> {
|
||||
// exchange so that it can be aborted/timed out mid setup.
|
||||
final ConnectionAborter connectionAborter = new ConnectionAborter();
|
||||
|
||||
final AtomicInteger nonFinalResponses = new AtomicInteger();
|
||||
|
||||
Exchange(HttpRequestImpl request, MultiExchange<T> multi) {
|
||||
this.request = request;
|
||||
this.upgrading = false;
|
||||
@ -359,7 +364,7 @@ final class Exchange<T> {
|
||||
|
||||
public void h2Upgrade() {
|
||||
upgrading = true;
|
||||
request.setH2Upgrade(client.client2());
|
||||
request.setH2Upgrade(this);
|
||||
}
|
||||
|
||||
synchronized IOException getCancelCause() {
|
||||
@ -482,9 +487,9 @@ final class Exchange<T> {
|
||||
Log.logResponse(r1::toString);
|
||||
int rcode = r1.statusCode();
|
||||
if (rcode == 100) {
|
||||
nonFinalResponses.incrementAndGet();
|
||||
Log.logTrace("Received 100-Continue: sending body");
|
||||
if (debug.on())
|
||||
debug.log("Received 100-Continue for %s", r1);
|
||||
if (debug.on()) debug.log("Received 100-Continue for %s", r1);
|
||||
CompletableFuture<Response> cf =
|
||||
exchImpl.sendBodyAsync()
|
||||
.thenCompose(exIm -> exIm.getResponseAsync(parentExecutor));
|
||||
@ -492,9 +497,9 @@ final class Exchange<T> {
|
||||
cf = wrapForLog(cf);
|
||||
return cf;
|
||||
} else {
|
||||
Log.logTrace("Expectation failed: Received {0}", rcode);
|
||||
if (debug.on())
|
||||
debug.log("Expect-Continue failed (%d) for: %s", rcode, r1);
|
||||
Log.logTrace("Expectation failed: Received {0}",
|
||||
rcode);
|
||||
if (debug.on()) debug.log("Expect-Continue failed (%d) for: %s", rcode, r1);
|
||||
if (upgrading && rcode == 101) {
|
||||
IOException failed = new IOException(
|
||||
"Unable to handle 101 while waiting for 100");
|
||||
@ -559,12 +564,20 @@ final class Exchange<T> {
|
||||
+ rsp.statusCode());
|
||||
}
|
||||
assert exchImpl != null : "Illegal state - current exchange isn't set";
|
||||
// ignore this Response and wait again for the subsequent response headers
|
||||
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
|
||||
// we recompose the CF again into the ignore1xxResponse check/function because
|
||||
// the 1xx response is allowed to be sent multiple times for a request, before
|
||||
// a final response arrives
|
||||
return cf.thenCompose(this::ignore1xxResponse);
|
||||
int count = nonFinalResponses.incrementAndGet();
|
||||
if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) {
|
||||
return MinimalFuture.failedFuture(
|
||||
new ProtocolException(String.format(
|
||||
"Too many interim responses received: %s > %s",
|
||||
count, MAX_NON_FINAL_RESPONSES)));
|
||||
} else {
|
||||
// ignore this Response and wait again for the subsequent response headers
|
||||
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
|
||||
// we recompose the CF again into the ignore1xxResponse check/function because
|
||||
// the 1xx response is allowed to be sent multiple times for a request, before
|
||||
// a final response arrives
|
||||
return cf.thenCompose(this::ignore1xxResponse);
|
||||
}
|
||||
} else {
|
||||
// return the already completed future
|
||||
return MinimalFuture.completedFuture(rsp);
|
||||
@ -829,6 +842,14 @@ final class Exchange<T> {
|
||||
return multi.version();
|
||||
}
|
||||
|
||||
boolean pushEnabled() {
|
||||
return pushGroup != null;
|
||||
}
|
||||
|
||||
String h2cSettingsStrings() {
|
||||
return client.client2().getSettingsString(pushEnabled());
|
||||
}
|
||||
|
||||
String dbgString() {
|
||||
return dbgTag;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.internal.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -53,6 +54,12 @@ class Http1HeaderParser {
|
||||
private int responseCode;
|
||||
private HttpHeaders headers;
|
||||
private Map<String,List<String>> privateMap = new HashMap<>();
|
||||
private long size;
|
||||
|
||||
private static final int K = 1024;
|
||||
private static final int MAX_HTTP_HEADER_SIZE = Utils.getIntegerNetProperty(
|
||||
"jdk.http.maxHeaderSize",
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K, true);
|
||||
|
||||
enum State { INITIAL,
|
||||
STATUS_LINE,
|
||||
@ -164,11 +171,16 @@ class Http1HeaderParser {
|
||||
return (char)(input.get() & 0xFF);
|
||||
}
|
||||
|
||||
private void readResumeStatusLine(ByteBuffer input) {
|
||||
private void readResumeStatusLine(ByteBuffer input) throws ProtocolException {
|
||||
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
|
||||
int count = 0;
|
||||
char c = 0;
|
||||
while (input.hasRemaining() && (c = get(input)) != CR) {
|
||||
if (c == LF) break;
|
||||
sb.append(c);
|
||||
if (++count > max) {
|
||||
checkMaxHeaderSize(sb.length());
|
||||
}
|
||||
}
|
||||
if (c == CR) {
|
||||
state = State.STATUS_LINE_FOUND_CR;
|
||||
@ -185,6 +197,7 @@ class Http1HeaderParser {
|
||||
}
|
||||
|
||||
statusLine = sb.toString();
|
||||
size = size + 32 + statusLine.length();
|
||||
sb = new StringBuilder();
|
||||
if (!statusLine.startsWith("HTTP/1.")) {
|
||||
throw protocolException("Invalid status line: \"%s\"", statusLine);
|
||||
@ -205,7 +218,23 @@ class Http1HeaderParser {
|
||||
state = State.STATUS_LINE_END;
|
||||
}
|
||||
|
||||
private void maybeStartHeaders(ByteBuffer input) {
|
||||
private void checkMaxHeaderSize(int sz) throws ProtocolException {
|
||||
long s = size + sz + 32;
|
||||
if (MAX_HTTP_HEADER_SIZE > 0 && s > MAX_HTTP_HEADER_SIZE) {
|
||||
throw new ProtocolException(String.format("Header size too big: %s > %s",
|
||||
s, MAX_HTTP_HEADER_SIZE));
|
||||
}
|
||||
}
|
||||
static private long newSize(long size, int name, int value) throws ProtocolException {
|
||||
long newSize = size + name + value + 32;
|
||||
if (MAX_HTTP_HEADER_SIZE > 0 && newSize > MAX_HTTP_HEADER_SIZE) {
|
||||
throw new ProtocolException(String.format("Header size too big: %s > %s",
|
||||
newSize, MAX_HTTP_HEADER_SIZE));
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
|
||||
private void maybeStartHeaders(ByteBuffer input) throws ProtocolException {
|
||||
assert state == State.STATUS_LINE_END;
|
||||
assert sb.length() == 0;
|
||||
char c = get(input);
|
||||
@ -215,6 +244,7 @@ class Http1HeaderParser {
|
||||
state = State.STATUS_LINE_END_LF;
|
||||
} else {
|
||||
sb.append(c);
|
||||
checkMaxHeaderSize(sb.length());
|
||||
state = State.HEADER;
|
||||
}
|
||||
}
|
||||
@ -232,9 +262,11 @@ class Http1HeaderParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readResumeHeader(ByteBuffer input) {
|
||||
private void readResumeHeader(ByteBuffer input) throws ProtocolException {
|
||||
assert state == State.HEADER;
|
||||
assert input.hasRemaining();
|
||||
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
|
||||
int count = 0;
|
||||
while (input.hasRemaining()) {
|
||||
char c = get(input);
|
||||
if (c == CR) {
|
||||
@ -248,6 +280,9 @@ class Http1HeaderParser {
|
||||
if (c == HT)
|
||||
c = SP;
|
||||
sb.append(c);
|
||||
if (++count > max) {
|
||||
checkMaxHeaderSize(sb.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,12 +303,12 @@ class Http1HeaderParser {
|
||||
if (!Utils.isValidValue(value)) {
|
||||
throw protocolException("Invalid header value \"%s: %s\"", name, value);
|
||||
}
|
||||
|
||||
size = newSize(size, name.length(), value.length());
|
||||
privateMap.computeIfAbsent(name.toLowerCase(Locale.US),
|
||||
k -> new ArrayList<>()).add(value);
|
||||
}
|
||||
|
||||
private void resumeOrLF(ByteBuffer input) {
|
||||
private void resumeOrLF(ByteBuffer input) throws ProtocolException {
|
||||
assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF;
|
||||
char c = state == State.HEADER_FOUND_LF ? LF : get(input);
|
||||
if (c == LF) {
|
||||
@ -283,10 +318,12 @@ class Http1HeaderParser {
|
||||
state = State.HEADER_FOUND_CR_LF;
|
||||
} else if (c == SP || c == HT) {
|
||||
sb.append(SP); // parity with MessageHeaders
|
||||
checkMaxHeaderSize(sb.length());
|
||||
state = State.HEADER;
|
||||
} else {
|
||||
sb = new StringBuilder();
|
||||
sb.append(c);
|
||||
checkMaxHeaderSize(1);
|
||||
state = State.HEADER;
|
||||
}
|
||||
}
|
||||
@ -312,6 +349,7 @@ class Http1HeaderParser {
|
||||
} else if (c == SP || c == HT) {
|
||||
assert sb.length() != 0;
|
||||
sb.append(SP); // continuation line
|
||||
checkMaxHeaderSize(sb.length());
|
||||
state = State.HEADER;
|
||||
} else {
|
||||
if (sb.length() > 0) {
|
||||
@ -322,6 +360,7 @@ class Http1HeaderParser {
|
||||
addHeaderFromString(headerString);
|
||||
}
|
||||
sb.append(c);
|
||||
checkMaxHeaderSize(sb.length());
|
||||
state = State.HEADER;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import jdk.internal.net.http.common.Log;
|
||||
import jdk.internal.net.http.common.Logger;
|
||||
import jdk.internal.net.http.common.MinimalFuture;
|
||||
import jdk.internal.net.http.common.Utils;
|
||||
@ -46,6 +45,7 @@ import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
|
||||
|
||||
/**
|
||||
* Http2 specific aspects of HttpClientImpl
|
||||
@ -98,16 +98,20 @@ class Http2ClientImpl {
|
||||
CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req,
|
||||
Exchange<?> exchange) {
|
||||
String key = Http2Connection.keyFor(req);
|
||||
boolean pushEnabled = exchange.pushEnabled();
|
||||
|
||||
connectionPoolLock.lock();
|
||||
try {
|
||||
Http2Connection connection = connections.get(key);
|
||||
if (connection != null) {
|
||||
try {
|
||||
if (!connection.tryReserveForPoolCheckout() || !connection.reserveStream(true)) {
|
||||
if (!connection.tryReserveForPoolCheckout()
|
||||
|| !connection.reserveStream(true, pushEnabled)) {
|
||||
if (debug.on())
|
||||
debug.log("removing connection from pool since it couldn't be" +
|
||||
" reserved for use: %s", connection);
|
||||
" reserved for use%s: %s",
|
||||
pushEnabled ? " with server push enabled" : "",
|
||||
connection);
|
||||
removeFromPool(connection);
|
||||
} else {
|
||||
// fast path if connection already exists
|
||||
@ -137,7 +141,7 @@ class Http2ClientImpl {
|
||||
try {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.reserveStream(true);
|
||||
conn.reserveStream(true, exchange.pushEnabled());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e); // shouldn't happen
|
||||
}
|
||||
@ -183,10 +187,21 @@ class Http2ClientImpl {
|
||||
}
|
||||
Http2Connection c1 = connections.putIfAbsent(key, c);
|
||||
if (c1 != null) {
|
||||
c.setFinalStream();
|
||||
if (debug.on())
|
||||
debug.log("existing entry in connection pool for %s", key);
|
||||
return false;
|
||||
if (c.serverPushEnabled() && !c1.serverPushEnabled()) {
|
||||
c1.setFinalStream();
|
||||
connections.remove(key, c1);
|
||||
connections.put(key, c);
|
||||
if (debug.on()) {
|
||||
debug.log("Replacing %s with %s in connection pool", c1, c);
|
||||
}
|
||||
if (c1.shouldClose()) c1.close();
|
||||
return true;
|
||||
} else {
|
||||
c.setFinalStream();
|
||||
if (debug.on())
|
||||
debug.log("existing entry in connection pool for %s", key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (debug.on())
|
||||
debug.log("put in the connection pool: %s", c);
|
||||
@ -250,8 +265,8 @@ class Http2ClientImpl {
|
||||
}
|
||||
|
||||
/** Returns the client settings as a base64 (url) encoded string */
|
||||
String getSettingsString() {
|
||||
SettingsFrame sf = getClientSettings();
|
||||
String getSettingsString(boolean defaultServerPush) {
|
||||
SettingsFrame sf = getClientSettings(defaultServerPush);
|
||||
byte[] settings = sf.toByteArray(); // without the header
|
||||
Base64.Encoder encoder = Base64.getUrlEncoder()
|
||||
.withoutPadding();
|
||||
@ -261,14 +276,7 @@ class Http2ClientImpl {
|
||||
private static final int K = 1024;
|
||||
|
||||
private static int getParameter(String property, int min, int max, int defaultValue) {
|
||||
int value = Utils.getIntegerNetProperty(property, defaultValue);
|
||||
// use default value if misconfigured
|
||||
if (value < min || value > max) {
|
||||
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
|
||||
"using default={4}", property, value, min, max, defaultValue);
|
||||
value = defaultValue;
|
||||
}
|
||||
return value;
|
||||
return Utils.getIntegerNetProperty(property, min, max, defaultValue, true);
|
||||
}
|
||||
|
||||
// used for the connection window, to have a connection window size
|
||||
@ -288,7 +296,18 @@ class Http2ClientImpl {
|
||||
streamWindow, Integer.MAX_VALUE, defaultValue);
|
||||
}
|
||||
|
||||
SettingsFrame getClientSettings() {
|
||||
/**
|
||||
* This method is used to test whether pushes are globally
|
||||
* disabled on all connections.
|
||||
* @return true if pushes are globally disabled on all connections
|
||||
*/
|
||||
boolean serverPushDisabled() {
|
||||
return getParameter(
|
||||
"jdk.httpclient.enablepush",
|
||||
0, 1, 1) == 0;
|
||||
}
|
||||
|
||||
SettingsFrame getClientSettings(boolean defaultServerPush) {
|
||||
SettingsFrame frame = new SettingsFrame();
|
||||
// default defined for HTTP/2 is 4 K, we use 16 K.
|
||||
frame.setParameter(HEADER_TABLE_SIZE, getParameter(
|
||||
@ -297,14 +316,15 @@ class Http2ClientImpl {
|
||||
// O: does not accept push streams. 1: accepts push streams.
|
||||
frame.setParameter(ENABLE_PUSH, getParameter(
|
||||
"jdk.httpclient.enablepush",
|
||||
0, 1, 1));
|
||||
0, 1, defaultServerPush ? 1 : 0));
|
||||
// HTTP/2 recommends to set the number of concurrent streams
|
||||
// no lower than 100. We use 100. 0 means no stream would be
|
||||
// accepted. That would render the client to be non functional,
|
||||
// so we won't let 0 be configured for our Http2ClientImpl.
|
||||
// no lower than 100. We use 100, unless push promises are
|
||||
// disabled.
|
||||
int initialServerStreams = frame.getParameter(ENABLE_PUSH) == 0
|
||||
? 0 : 100;
|
||||
frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter(
|
||||
"jdk.httpclient.maxstreams",
|
||||
1, Integer.MAX_VALUE, 100));
|
||||
0, Integer.MAX_VALUE, initialServerStreams));
|
||||
// Maximum size is 2^31-1. Don't allow window size to be less
|
||||
// than the minimum frame size as this is likely to be a
|
||||
// configuration error. HTTP/2 specify a default of 64 * K -1,
|
||||
@ -317,6 +337,14 @@ class Http2ClientImpl {
|
||||
frame.setParameter(MAX_FRAME_SIZE, getParameter(
|
||||
"jdk.httpclient.maxframesize",
|
||||
16 * K, 16 * K * K -1, 16 * K));
|
||||
// Maximum field section size we're prepared to accept
|
||||
// This is the uncompressed name + value size + 32 per field line
|
||||
int maxHeaderSize = getParameter(
|
||||
"jdk.http.maxHeaderSize",
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K);
|
||||
// If the property is <= 0 the value is unlimited
|
||||
if (maxHeaderSize <= 0) maxHeaderSize = -1;
|
||||
frame.setParameter(MAX_HEADER_LIST_SIZE, maxHeaderSize);
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import java.io.UncheckedIOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -49,6 +50,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
@ -88,10 +90,12 @@ import jdk.internal.net.http.hpack.DecodingCallback;
|
||||
import jdk.internal.net.http.hpack.Encoder;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_INITIAL_WINDOW_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.INITIAL_WINDOW_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
|
||||
|
||||
/**
|
||||
* An Http2Connection. Encapsulates the socket(channel) and any SSLEngine used
|
||||
@ -327,6 +331,45 @@ class Http2Connection {
|
||||
}
|
||||
}
|
||||
|
||||
private final class PushPromiseDecoder extends HeaderDecoder implements DecodingCallback {
|
||||
|
||||
final int parentStreamId;
|
||||
final int pushPromiseStreamId;
|
||||
final Stream<?> parent;
|
||||
final AtomicReference<Throwable> errorRef = new AtomicReference<>();
|
||||
|
||||
PushPromiseDecoder(int parentStreamId, int pushPromiseStreamId, Stream<?> parent) {
|
||||
this.parentStreamId = parentStreamId;
|
||||
this.pushPromiseStreamId = pushPromiseStreamId;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addHeader(String name, String value) {
|
||||
if (errorRef.get() == null) {
|
||||
super.addHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException {
|
||||
try {
|
||||
DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize);
|
||||
} catch (ProtocolException pe) {
|
||||
if (parent != null) {
|
||||
if (errorRef.compareAndSet(null, pe)) {
|
||||
// cancel the parent stream
|
||||
resetStream(pushPromiseStreamId, ResetFrame.REFUSED_STREAM);
|
||||
parent.onProtocolError(pe);
|
||||
}
|
||||
} else {
|
||||
// interrupt decoding and closes the connection
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final int HALF_CLOSED_LOCAL = 1;
|
||||
private static final int HALF_CLOSED_REMOTE = 2;
|
||||
@ -355,7 +398,7 @@ class Http2Connection {
|
||||
private final Decoder hpackIn;
|
||||
final SettingsFrame clientSettings;
|
||||
private volatile SettingsFrame serverSettings;
|
||||
private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
|
||||
private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
|
||||
private volatile PushContinuationState pushContinuationState;
|
||||
private final String key; // for HttpClientImpl.connections map
|
||||
private final FramesDecoder framesDecoder;
|
||||
@ -370,12 +413,24 @@ class Http2Connection {
|
||||
private final FramesController framesController = new FramesController();
|
||||
private final Http2TubeSubscriber subscriber;
|
||||
final ConnectionWindowUpdateSender windowUpdater;
|
||||
private volatile Throwable cause;
|
||||
private final AtomicReference<Throwable> cause = new AtomicReference<>();
|
||||
private volatile Supplier<ByteBuffer> initial;
|
||||
private volatile Stream<?> initialStream;
|
||||
|
||||
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
|
||||
private ValidatingHeadersConsumer orphanedConsumer;
|
||||
private final AtomicInteger orphanedHeaders = new AtomicInteger();
|
||||
|
||||
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
|
||||
static final int MAX_LITERAL_WITH_INDEXING =
|
||||
Utils.getIntegerNetProperty("jdk.httpclient.maxLiteralWithIndexing",512);
|
||||
|
||||
// The maximum number of HEADER frames, CONTINUATION frames, or PUSH_PROMISE frames
|
||||
// referring to an already closed or non-existent stream that a client will accept to
|
||||
// process. Receiving frames referring to non-existent or closed streams doesn't necessarily
|
||||
// constitute an HTTP/2 protocol error, but receiving too many may indicate a problem
|
||||
// with the connection. If this limit is reached, a {@link java.net.ProtocolException
|
||||
// ProtocolException} will be raised and the connection will be closed.
|
||||
static final int MAX_ORPHANED_HEADERS = 1024;
|
||||
|
||||
// TODO: need list of control frames from other threads
|
||||
// that need to be sent
|
||||
@ -383,19 +438,21 @@ class Http2Connection {
|
||||
private Http2Connection(HttpConnection connection,
|
||||
Http2ClientImpl client2,
|
||||
int nextstreamid,
|
||||
String key) {
|
||||
String key,
|
||||
boolean defaultServerPush) {
|
||||
this.connection = connection;
|
||||
this.client2 = client2;
|
||||
this.subscriber = new Http2TubeSubscriber(client2.client());
|
||||
this.nextstreamid = nextstreamid;
|
||||
this.key = key;
|
||||
this.clientSettings = this.client2.getClientSettings();
|
||||
this.clientSettings = this.client2.getClientSettings(defaultServerPush);
|
||||
this.framesDecoder = new FramesDecoder(this::processFrame,
|
||||
clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE));
|
||||
// serverSettings will be updated by server
|
||||
this.serverSettings = SettingsFrame.defaultRFCSettings();
|
||||
this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
|
||||
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
|
||||
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE),
|
||||
clientSettings.getParameter(MAX_HEADER_LIST_SIZE), MAX_LITERAL_WITH_INDEXING);
|
||||
if (debugHpack.on()) {
|
||||
debugHpack.log("For the record:" + super.toString());
|
||||
debugHpack.log("Decoder created: %s", hpackIn);
|
||||
@ -414,14 +471,16 @@ class Http2Connection {
|
||||
private Http2Connection(HttpConnection connection,
|
||||
Http2ClientImpl client2,
|
||||
Exchange<?> exchange,
|
||||
Supplier<ByteBuffer> initial)
|
||||
Supplier<ByteBuffer> initial,
|
||||
boolean defaultServerPush)
|
||||
throws IOException, InterruptedException
|
||||
{
|
||||
this(connection,
|
||||
client2,
|
||||
3, // stream 1 is registered during the upgrade
|
||||
keyFor(connection));
|
||||
reserveStream(true);
|
||||
keyFor(connection),
|
||||
defaultServerPush);
|
||||
reserveStream(true, clientSettings.getFlag(ENABLE_PUSH));
|
||||
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
|
||||
|
||||
Stream<?> initialStream = createStream(exchange);
|
||||
@ -454,7 +513,8 @@ class Http2Connection {
|
||||
Exchange<?> exchange,
|
||||
Supplier<ByteBuffer> initial)
|
||||
{
|
||||
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial));
|
||||
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial,
|
||||
exchange.pushEnabled()));
|
||||
}
|
||||
|
||||
// Requires TLS handshake. So, is really async
|
||||
@ -478,7 +538,8 @@ class Http2Connection {
|
||||
.thenCompose(notused-> {
|
||||
CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
|
||||
try {
|
||||
Http2Connection hc = new Http2Connection(request, h2client, connection);
|
||||
Http2Connection hc = new Http2Connection(request, h2client,
|
||||
connection, exchange.pushEnabled());
|
||||
cf.complete(hc);
|
||||
} catch (IOException e) {
|
||||
cf.completeExceptionally(e);
|
||||
@ -493,13 +554,15 @@ class Http2Connection {
|
||||
*/
|
||||
private Http2Connection(HttpRequestImpl request,
|
||||
Http2ClientImpl h2client,
|
||||
HttpConnection connection)
|
||||
HttpConnection connection,
|
||||
boolean defaultServerPush)
|
||||
throws IOException
|
||||
{
|
||||
this(connection,
|
||||
h2client,
|
||||
1,
|
||||
keyFor(request));
|
||||
keyFor(request),
|
||||
defaultServerPush);
|
||||
|
||||
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
|
||||
|
||||
@ -522,24 +585,30 @@ class Http2Connection {
|
||||
// if false returned then a new Http2Connection is required
|
||||
// if true, the stream may be assigned to this connection
|
||||
// for server push, if false returned, then the stream should be cancelled
|
||||
boolean reserveStream(boolean clientInitiated) throws IOException {
|
||||
boolean reserveStream(boolean clientInitiated, boolean pushEnabled) throws IOException {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return reserveStream0(clientInitiated);
|
||||
return reserveStream0(clientInitiated, pushEnabled);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean reserveStream0(boolean clientInitiated) throws IOException {
|
||||
private boolean reserveStream0(boolean clientInitiated, boolean pushEnabled) throws IOException {
|
||||
if (finalStream()) {
|
||||
return false;
|
||||
}
|
||||
if (clientInitiated && (lastReservedClientStreamid + 2) >= MAX_CLIENT_STREAM_ID) {
|
||||
// If requesting to reserve a stream for an exchange for which push is enabled,
|
||||
// we will reserve the stream in this connection only if this connection is also
|
||||
// push enabled, unless pushes are globally disabled.
|
||||
boolean pushCompatible = !clientInitiated || !pushEnabled
|
||||
|| this.serverPushEnabled()
|
||||
|| client2.serverPushDisabled();
|
||||
if (clientInitiated && (lastReservedClientStreamid >= MAX_CLIENT_STREAM_ID -2 || !pushCompatible)) {
|
||||
setFinalStream();
|
||||
client2.removeFromPool(this);
|
||||
return false;
|
||||
} else if (!clientInitiated && (lastReservedServerStreamid + 2) >= MAX_SERVER_STREAM_ID) {
|
||||
} else if (!clientInitiated && (lastReservedServerStreamid >= MAX_SERVER_STREAM_ID - 2)) {
|
||||
setFinalStream();
|
||||
client2.removeFromPool(this);
|
||||
return false;
|
||||
@ -564,6 +633,15 @@ class Http2Connection {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean shouldClose() {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return finalStream() && streams.isEmpty();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an IOException if h2 was not negotiated
|
||||
*/
|
||||
@ -691,6 +769,10 @@ class Http2Connection {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public boolean serverPushEnabled() {
|
||||
return clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
|
||||
}
|
||||
|
||||
boolean offerConnection() {
|
||||
return client2.offerConnection(this);
|
||||
}
|
||||
@ -795,7 +877,7 @@ class Http2Connection {
|
||||
}
|
||||
|
||||
Throwable getRecordedCause() {
|
||||
return cause;
|
||||
return cause.get();
|
||||
}
|
||||
|
||||
void shutdown(Throwable t) {
|
||||
@ -804,11 +886,11 @@ class Http2Connection {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (!markShutdownRequested()) return;
|
||||
Throwable initialCause = this.cause;
|
||||
if (initialCause == null && t != null) this.cause = t;
|
||||
cause.compareAndSet(null, t);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
if (Log.errors()) {
|
||||
if (t!= null && (!(t instanceof EOFException) || isActive())) {
|
||||
Log.logError(t);
|
||||
@ -819,6 +901,7 @@ class Http2Connection {
|
||||
}
|
||||
}
|
||||
client2.removeFromPool(this);
|
||||
subscriber.stop(cause.get());
|
||||
for (Stream<?> s : streams.values()) {
|
||||
try {
|
||||
s.connectionClosing(t);
|
||||
@ -872,17 +955,39 @@ class Http2Connection {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame instanceof PushPromiseFrame && !serverPushEnabled()) {
|
||||
String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0";
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
|
||||
return;
|
||||
}
|
||||
|
||||
Stream<?> stream = getStream(streamid);
|
||||
var nextstreamid = this.nextstreamid;
|
||||
if (stream == null && (streamid & 0x01) == 0x01 && streamid >= nextstreamid) {
|
||||
String protocolError = String.format(
|
||||
"received a frame for a non existing streamid(%s) >= nextstreamid(%s)",
|
||||
streamid, nextstreamid);
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
|
||||
return;
|
||||
}
|
||||
if (stream == null && pushContinuationState == null) {
|
||||
// Should never receive a frame with unknown stream id
|
||||
|
||||
if (frame instanceof HeaderFrame) {
|
||||
if (frame instanceof HeaderFrame hf) {
|
||||
String protocolError = checkMaxOrphanedHeadersExceeded(hf);
|
||||
if (protocolError != null) {
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
|
||||
return;
|
||||
}
|
||||
// always decode the headers as they may affect
|
||||
// connection-level HPACK decoding state
|
||||
DecodingCallback decoder = new ValidatingHeadersConsumer()::onDecoded;
|
||||
if (orphanedConsumer == null || frame.getClass() != ContinuationFrame.class) {
|
||||
orphanedConsumer = new ValidatingHeadersConsumer();
|
||||
}
|
||||
DecodingCallback decoder = orphanedConsumer::onDecoded;
|
||||
try {
|
||||
decodeHeaders((HeaderFrame) frame, decoder);
|
||||
} catch (UncheckedIOException e) {
|
||||
decodeHeaders(hf, decoder);
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
@ -910,29 +1015,41 @@ class Http2Connection {
|
||||
|
||||
// While push frame is not null, the only acceptable frame on this
|
||||
// stream is a Continuation frame
|
||||
if (pushContinuationState != null) {
|
||||
PushContinuationState pcs = pushContinuationState;
|
||||
if (pcs != null) {
|
||||
if (frame instanceof ContinuationFrame cf) {
|
||||
if (stream == null) {
|
||||
String protocolError = checkMaxOrphanedHeadersExceeded(cf);
|
||||
if (protocolError != null) {
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (streamid == pushContinuationState.pushContFrame.streamid())
|
||||
handlePushContinuation(stream, cf);
|
||||
else
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, "Received a Continuation Frame with an " +
|
||||
"unexpected stream id");
|
||||
} catch (UncheckedIOException e) {
|
||||
if (streamid == pcs.pushContFrame.streamid())
|
||||
handlePushContinuation(pcs, stream, cf);
|
||||
else {
|
||||
String protocolError = "Received a CONTINUATION with " +
|
||||
"unexpected stream id: " + streamid + " != "
|
||||
+ pcs.pushContFrame.streamid();
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
|
||||
}
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
debug.log("Error handling Push Promise with Continuation: " + e.getMessage(), e);
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
pushContinuationState = null;
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, "Expected a Continuation frame but received " + frame);
|
||||
String protocolError = "Expected a CONTINUATION frame but received " + frame;
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (frame instanceof PushPromiseFrame pp) {
|
||||
try {
|
||||
handlePushPromise(stream, pp);
|
||||
} catch (UncheckedIOException e) {
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
@ -940,7 +1057,7 @@ class Http2Connection {
|
||||
// decode headers
|
||||
try {
|
||||
decodeHeaders(hf, stream.rspHeadersConsumer());
|
||||
} catch (UncheckedIOException e) {
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
debug.log("Error decoding headers: " + e.getMessage(), e);
|
||||
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
|
||||
return;
|
||||
@ -953,6 +1070,16 @@ class Http2Connection {
|
||||
}
|
||||
}
|
||||
|
||||
private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) {
|
||||
if (MAX_ORPHANED_HEADERS > 0 ) {
|
||||
int orphaned = orphanedHeaders.incrementAndGet();
|
||||
if (orphaned < 0 || orphaned > MAX_ORPHANED_HEADERS) {
|
||||
return "Too many orphaned header frames received on connection";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final void dropDataFrame(DataFrame df) {
|
||||
if (isMarked(closedState, SHUTDOWN_REQUESTED)) return;
|
||||
if (debug.on()) {
|
||||
@ -977,38 +1104,65 @@ class Http2Connection {
|
||||
private <T> void handlePushPromise(Stream<T> parent, PushPromiseFrame pp)
|
||||
throws IOException
|
||||
{
|
||||
int promisedStreamid = pp.getPromisedStream();
|
||||
if ((promisedStreamid & 0x01) != 0x00) {
|
||||
throw new ProtocolException("Received PUSH_PROMISE for stream " + promisedStreamid);
|
||||
}
|
||||
int streamId = pp.streamid();
|
||||
if ((streamId & 0x01) != 0x01) {
|
||||
throw new ProtocolException("Received PUSH_PROMISE on stream " + streamId);
|
||||
}
|
||||
// always decode the headers as they may affect connection-level HPACK
|
||||
// decoding state
|
||||
assert pushContinuationState == null;
|
||||
HeaderDecoder decoder = new HeaderDecoder();
|
||||
decodeHeaders(pp, decoder::onDecoded);
|
||||
int promisedStreamid = pp.getPromisedStream();
|
||||
PushPromiseDecoder decoder = new PushPromiseDecoder(streamId, promisedStreamid, parent);
|
||||
decodeHeaders(pp, decoder);
|
||||
if (pp.endHeaders()) {
|
||||
completePushPromise(promisedStreamid, parent, decoder.headers());
|
||||
if (decoder.errorRef.get() == null) {
|
||||
completePushPromise(promisedStreamid, parent, decoder.headers());
|
||||
}
|
||||
} else {
|
||||
pushContinuationState = new PushContinuationState(decoder, pp);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void handlePushContinuation(Stream<T> parent, ContinuationFrame cf)
|
||||
private <T> void handlePushContinuation(PushContinuationState pcs, Stream<T> parent, ContinuationFrame cf)
|
||||
throws IOException {
|
||||
var pcs = pushContinuationState;
|
||||
decodeHeaders(cf, pcs.pushContDecoder::onDecoded);
|
||||
assert pcs.pushContFrame.streamid() == cf.streamid() : String.format(
|
||||
"Received CONTINUATION on a different stream %s != %s",
|
||||
cf.streamid(), pcs.pushContFrame.streamid());
|
||||
decodeHeaders(cf, pcs.pushContDecoder);
|
||||
// if all continuations are sent, set pushWithContinuation to null
|
||||
if (cf.endHeaders()) {
|
||||
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
|
||||
pcs.pushContDecoder.headers());
|
||||
if (pcs.pushContDecoder.errorRef.get() == null) {
|
||||
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
|
||||
pcs.pushContDecoder.headers());
|
||||
}
|
||||
pushContinuationState = null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void completePushPromise(int promisedStreamid, Stream<T> parent, HttpHeaders headers)
|
||||
throws IOException {
|
||||
if (parent == null) {
|
||||
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
|
||||
return;
|
||||
}
|
||||
HttpRequestImpl parentReq = parent.request;
|
||||
if (promisedStreamid < nextPushStream) {
|
||||
// From RFC 9113 section 5.1.1:
|
||||
// The identifier of a newly established stream MUST be numerically
|
||||
// greater than all streams that the initiating endpoint has
|
||||
// opened or reserved.
|
||||
protocolError(ResetFrame.PROTOCOL_ERROR, String.format(
|
||||
"Unexpected stream identifier: %s < %s", promisedStreamid, nextPushStream));
|
||||
return;
|
||||
}
|
||||
if (promisedStreamid != nextPushStream) {
|
||||
// we don't support skipping stream ids;
|
||||
resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR);
|
||||
return;
|
||||
} else if (!reserveStream(false)) {
|
||||
} else if (!reserveStream(false, true)) {
|
||||
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
|
||||
return;
|
||||
} else {
|
||||
@ -1177,11 +1331,17 @@ class Http2Connection {
|
||||
private void protocolError(int errorCode, String msg)
|
||||
throws IOException
|
||||
{
|
||||
String protocolError = "protocol error" + (msg == null?"":(": " + msg));
|
||||
ProtocolException protocolException =
|
||||
new ProtocolException(protocolError);
|
||||
if (markHalfClosedLocal()) {
|
||||
framesDecoder.close(protocolError);
|
||||
subscriber.stop(protocolException);
|
||||
if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException);
|
||||
GoAwayFrame frame = new GoAwayFrame(0, errorCode);
|
||||
sendFrame(frame);
|
||||
}
|
||||
shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg))));
|
||||
shutdown(protocolException);
|
||||
}
|
||||
|
||||
private void handleSettings(SettingsFrame frame)
|
||||
@ -1356,7 +1516,7 @@ class Http2Connection {
|
||||
|
||||
<T> Stream.PushedStream<T> createPushStream(Stream<T> parent, Exchange<T> pushEx) {
|
||||
PushGroup<T> pg = parent.exchange.getPushGroup();
|
||||
return new Stream.PushedStream<>(pg, this, pushEx);
|
||||
return new Stream.PushedStream<>(parent, pg, this, pushEx);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1466,16 +1626,18 @@ class Http2Connection {
|
||||
private List<ByteBuffer> encodeHeadersImpl(int bufferSize, HttpHeaders... headers) {
|
||||
ByteBuffer buffer = getHeaderBuffer(bufferSize);
|
||||
List<ByteBuffer> buffers = new ArrayList<>();
|
||||
for(HttpHeaders header : headers) {
|
||||
for (HttpHeaders header : headers) {
|
||||
for (Map.Entry<String, List<String>> e : header.map().entrySet()) {
|
||||
String lKey = e.getKey().toLowerCase(Locale.US);
|
||||
List<String> values = e.getValue();
|
||||
for (String value : values) {
|
||||
hpackOut.header(lKey, value);
|
||||
while (!hpackOut.encode(buffer)) {
|
||||
buffer.flip();
|
||||
buffers.add(buffer);
|
||||
buffer = getHeaderBuffer(bufferSize);
|
||||
if (!buffer.hasRemaining()) {
|
||||
buffer.flip();
|
||||
buffers.add(buffer);
|
||||
buffer = getHeaderBuffer(bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1514,7 +1676,7 @@ class Http2Connection {
|
||||
Throwable cause = null;
|
||||
synchronized (this) {
|
||||
if (isMarked(closedState, SHUTDOWN_REQUESTED)) {
|
||||
cause = this.cause;
|
||||
cause = this.cause.get();
|
||||
if (cause == null) {
|
||||
cause = new IOException("Connection closed");
|
||||
}
|
||||
@ -1553,6 +1715,8 @@ class Http2Connection {
|
||||
Stream<?> stream = registerNewStream(oh);
|
||||
// provide protection from inserting unordered frames between Headers and Continuation
|
||||
if (stream != null) {
|
||||
// we are creating a new stream: reset orphaned header count
|
||||
orphanedHeaders.set(0);
|
||||
publisher.enqueue(encodeHeaders(oh, stream));
|
||||
}
|
||||
} else {
|
||||
@ -1621,7 +1785,7 @@ class Http2Connection {
|
||||
private volatile Flow.Subscription subscription;
|
||||
private volatile boolean completed;
|
||||
private volatile boolean dropped;
|
||||
private volatile Throwable error;
|
||||
private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
|
||||
private final ConcurrentLinkedQueue<ByteBuffer> queue
|
||||
= new ConcurrentLinkedQueue<>();
|
||||
private final SequentialScheduler scheduler =
|
||||
@ -1642,10 +1806,9 @@ class Http2Connection {
|
||||
asyncReceive(buffer);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Throwable x = error;
|
||||
if (x == null) error = t;
|
||||
errorRef.compareAndSet(null, t);
|
||||
} finally {
|
||||
Throwable x = error;
|
||||
Throwable x = errorRef.get();
|
||||
if (x != null) {
|
||||
if (debug.on()) debug.log("Stopping scheduler", x);
|
||||
scheduler.stop();
|
||||
@ -1680,6 +1843,7 @@ class Http2Connection {
|
||||
|
||||
@Override
|
||||
public void onNext(List<ByteBuffer> item) {
|
||||
if (completed) return;
|
||||
if (debug.on()) debug.log(() -> "onNext: got " + Utils.remaining(item)
|
||||
+ " bytes in " + item.size() + " buffers");
|
||||
queue.addAll(item);
|
||||
@ -1688,19 +1852,21 @@ class Http2Connection {
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
if (completed) return;
|
||||
if (debug.on()) debug.log(() -> "onError: " + throwable);
|
||||
error = throwable;
|
||||
errorRef.compareAndSet(null, throwable);
|
||||
completed = true;
|
||||
runOrSchedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
if (completed) return;
|
||||
String msg = isActive()
|
||||
? "EOF reached while reading"
|
||||
: "Idle connection closed by HTTP/2 peer";
|
||||
if (debug.on()) debug.log(msg);
|
||||
error = new EOFException(msg);
|
||||
errorRef.compareAndSet(null, new EOFException(msg));
|
||||
completed = true;
|
||||
runOrSchedule();
|
||||
}
|
||||
@ -1712,6 +1878,18 @@ class Http2Connection {
|
||||
// then we might not need the 'dropped' boolean?
|
||||
dropped = true;
|
||||
}
|
||||
|
||||
void stop(Throwable error) {
|
||||
if (errorRef.compareAndSet(null, error)) {
|
||||
completed = true;
|
||||
scheduler.stop();
|
||||
queue.clear();
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
}
|
||||
queue.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
|
@ -964,7 +964,9 @@ final class HttpClientImpl extends HttpClient implements Trackable {
|
||||
// SSLException
|
||||
throw new SSLException(msg, throwable);
|
||||
} else if (throwable instanceof ProtocolException) {
|
||||
throw new ProtocolException(msg);
|
||||
ProtocolException pe = new ProtocolException(msg);
|
||||
pe.initCause(throwable);
|
||||
throw pe;
|
||||
} else if (throwable instanceof IOException) {
|
||||
throw new IOException(msg, throwable);
|
||||
} else {
|
||||
|
@ -287,10 +287,10 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
|
||||
|
||||
InetSocketAddress authority() { return authority; }
|
||||
|
||||
void setH2Upgrade(Http2ClientImpl h2client) {
|
||||
void setH2Upgrade(Exchange<?> exchange) {
|
||||
systemHeadersBuilder.setHeader("Connection", "Upgrade, HTTP2-Settings");
|
||||
systemHeadersBuilder.setHeader("Upgrade", Alpns.H2C);
|
||||
systemHeadersBuilder.setHeader("HTTP2-Settings", h2client.getSettingsString());
|
||||
systemHeadersBuilder.setHeader("HTTP2-Settings", exchange.h2cSettingsStrings());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2024, 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.nio.file.Paths;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
@ -137,16 +138,21 @@ public final class ResponseBodyHandlers {
|
||||
if (!initiatingURI.getHost().equalsIgnoreCase(pushRequestURI.getHost()))
|
||||
return;
|
||||
|
||||
String initiatingScheme = initiatingURI.getScheme();
|
||||
String pushRequestScheme = pushRequestURI.getScheme();
|
||||
|
||||
if (!initiatingScheme.equalsIgnoreCase(pushRequestScheme)) return;
|
||||
|
||||
int initiatingPort = initiatingURI.getPort();
|
||||
if (initiatingPort == -1 ) {
|
||||
if ("https".equalsIgnoreCase(initiatingURI.getScheme()))
|
||||
if ("https".equalsIgnoreCase(initiatingScheme))
|
||||
initiatingPort = 443;
|
||||
else
|
||||
initiatingPort = 80;
|
||||
}
|
||||
int pushPort = pushRequestURI.getPort();
|
||||
if (pushPort == -1 ) {
|
||||
if ("https".equalsIgnoreCase(pushRequestURI.getScheme()))
|
||||
if ("https".equalsIgnoreCase(pushRequestScheme))
|
||||
pushPort = 443;
|
||||
else
|
||||
pushPort = 80;
|
||||
|
@ -30,6 +30,7 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpResponse.BodyHandler;
|
||||
import java.net.http.HttpResponse.ResponseInfo;
|
||||
@ -43,6 +44,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.concurrent.Flow.Subscription;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -52,10 +54,13 @@ import java.net.http.HttpHeaders;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpResponse.BodySubscriber;
|
||||
|
||||
import jdk.internal.net.http.common.*;
|
||||
import jdk.internal.net.http.frame.*;
|
||||
import jdk.internal.net.http.hpack.DecodingCallback;
|
||||
|
||||
import static jdk.internal.net.http.Exchange.MAX_NON_FINAL_RESPONSES;
|
||||
|
||||
/**
|
||||
* Http/2 Stream handling.
|
||||
*
|
||||
@ -140,6 +145,9 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
private volatile boolean closed;
|
||||
private volatile boolean endStreamSent;
|
||||
private volatile boolean finalResponseCodeReceived;
|
||||
private volatile boolean trailerReceived;
|
||||
private AtomicInteger nonFinalResponseCount = new AtomicInteger();
|
||||
|
||||
// Indicates the first reason that was invoked when sending a ResetFrame
|
||||
// to the server. A streamState of 0 indicates that no reset was sent.
|
||||
// (see markStream(int code)
|
||||
@ -520,16 +528,38 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
// The Hpack decoder decodes into one of these consumers of name,value pairs
|
||||
|
||||
DecodingCallback rspHeadersConsumer() {
|
||||
return rspHeadersConsumer::onDecoded;
|
||||
return rspHeadersConsumer;
|
||||
}
|
||||
|
||||
String checkInterimResponseCountExceeded() {
|
||||
// this is also checked by Exchange - but tracking it here too provides
|
||||
// a more informative message.
|
||||
int count = nonFinalResponseCount.incrementAndGet();
|
||||
if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) {
|
||||
return String.format(
|
||||
"Stream %s PROTOCOL_ERROR: too many interim responses received: %s > %s",
|
||||
streamid, count, MAX_NON_FINAL_RESPONSES);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void handleResponse(HeaderFrame hf) throws IOException {
|
||||
HttpHeaders responseHeaders = responseHeadersBuilder.build();
|
||||
|
||||
if (!finalResponseCodeReceived) {
|
||||
responseCode = (int) responseHeaders
|
||||
.firstValueAsLong(":status")
|
||||
.orElseThrow(() -> new IOException("no statuscode in response"));
|
||||
try {
|
||||
responseCode = (int) responseHeaders
|
||||
.firstValueAsLong(":status")
|
||||
.orElseThrow(() -> new ProtocolException(String.format(
|
||||
"Stream %s PROTOCOL_ERROR: no status code in response",
|
||||
streamid)));
|
||||
} catch (ProtocolException cause) {
|
||||
cancelImpl(cause, ResetFrame.PROTOCOL_ERROR);
|
||||
rspHeadersConsumer.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
String protocolErrorMsg = null;
|
||||
// If informational code, response is partially complete
|
||||
if (responseCode < 100 || responseCode > 199) {
|
||||
this.finalResponseCodeReceived = true;
|
||||
@ -537,23 +567,31 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
// see RFC 9113 section 8.1:
|
||||
// A HEADERS frame with the END_STREAM flag set that carries an
|
||||
// informational status code is malformed
|
||||
String msg = ("Stream %s PROTOCOL_ERROR: " +
|
||||
"HEADERS frame with status %s has END_STREAM flag set")
|
||||
.formatted(streamid, responseCode);
|
||||
protocolErrorMsg = String.format(
|
||||
"Stream %s PROTOCOL_ERROR: " +
|
||||
"HEADERS frame with status %s has END_STREAM flag set",
|
||||
streamid, responseCode);
|
||||
} else {
|
||||
protocolErrorMsg = checkInterimResponseCountExceeded();
|
||||
}
|
||||
|
||||
if (protocolErrorMsg != null) {
|
||||
if (debug.on()) {
|
||||
debug.log(msg);
|
||||
debug.log(protocolErrorMsg);
|
||||
}
|
||||
cancelImpl(new IOException(msg), ResetFrame.PROTOCOL_ERROR);
|
||||
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
|
||||
rspHeadersConsumer.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
response = new Response(
|
||||
request, exchange, responseHeaders, connection(),
|
||||
responseCode, HttpClient.Version.HTTP_2);
|
||||
|
||||
/* TODO: review if needs to be removed
|
||||
the value is not used, but in case `content-length` doesn't parse as
|
||||
long, there will be NumberFormatException. If left as is, make sure
|
||||
code up the stack handles NFE correctly. */
|
||||
/* TODO: review if needs to be removed
|
||||
the value is not used, but in case `content-length` doesn't parse as
|
||||
long, there will be NumberFormatException. If left as is, make sure
|
||||
code up the stack handles NFE correctly. */
|
||||
responseHeaders.firstValueAsLong("content-length");
|
||||
|
||||
if (Log.headers()) {
|
||||
@ -572,6 +610,15 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
Log.dumpHeaders(sb, " ", responseHeaders);
|
||||
Log.logHeaders(sb.toString());
|
||||
}
|
||||
if (trailerReceived) {
|
||||
String protocolErrorMsg = String.format(
|
||||
"Stream %s PROTOCOL_ERROR: trailers already received", streamid);
|
||||
if (debug.on()) {
|
||||
debug.log(protocolErrorMsg);
|
||||
}
|
||||
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
|
||||
}
|
||||
trailerReceived = true;
|
||||
rspHeadersConsumer.reset();
|
||||
}
|
||||
|
||||
@ -1182,7 +1229,7 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
|
||||
/**
|
||||
* A List of responses relating to this stream. Normally there is only
|
||||
* one response, but intermediate responses like 100 are allowed
|
||||
* one response, but interim responses like 100 are allowed
|
||||
* and must be passed up to higher level before continuing. Deals with races
|
||||
* such as if responses are returned before the CFs get created by
|
||||
* getResponseAsync()
|
||||
@ -1401,7 +1448,7 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
cancelImpl(e, ResetFrame.CANCEL);
|
||||
}
|
||||
|
||||
private void cancelImpl(final Throwable e, final int resetFrameErrCode) {
|
||||
void cancelImpl(final Throwable e, final int resetFrameErrCode) {
|
||||
errorRef.compareAndSet(null, e);
|
||||
if (debug.on()) {
|
||||
if (streamid == 0) debug.log("cancelling stream: %s", (Object)e);
|
||||
@ -1511,6 +1558,7 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
}
|
||||
|
||||
static class PushedStream<T> extends Stream<T> {
|
||||
final Stream<T> parent;
|
||||
final PushGroup<T> pushGroup;
|
||||
// push streams need the response CF allocated up front as it is
|
||||
// given directly to user via the multi handler callback function.
|
||||
@ -1520,16 +1568,17 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
volatile HttpResponse.BodyHandler<T> pushHandler;
|
||||
private volatile boolean finalPushResponseCodeReceived;
|
||||
|
||||
PushedStream(PushGroup<T> pushGroup,
|
||||
PushedStream(Stream<T> parent,
|
||||
PushGroup<T> pushGroup,
|
||||
Http2Connection connection,
|
||||
Exchange<T> pushReq) {
|
||||
// ## no request body possible, null window controller
|
||||
super(connection, pushReq, null);
|
||||
this.parent = parent;
|
||||
this.pushGroup = pushGroup;
|
||||
this.pushReq = pushReq.request();
|
||||
this.pushCF = new MinimalFuture<>();
|
||||
this.responseCF = new MinimalFuture<>();
|
||||
|
||||
}
|
||||
|
||||
CompletableFuture<HttpResponse<T>> responseCF() {
|
||||
@ -1617,7 +1666,16 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
.orElse(-1);
|
||||
|
||||
if (responseCode == -1) {
|
||||
completeResponseExceptionally(new IOException("No status code"));
|
||||
cancelImpl(new ProtocolException("No status code"), ResetFrame.PROTOCOL_ERROR);
|
||||
rspHeadersConsumer.reset();
|
||||
return;
|
||||
} else if (responseCode >= 100 && responseCode < 200) {
|
||||
String protocolErrorMsg = checkInterimResponseCountExceeded();
|
||||
if (protocolErrorMsg != null) {
|
||||
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
|
||||
rspHeadersConsumer.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.finalPushResponseCodeReceived = true;
|
||||
@ -1727,7 +1785,9 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private class HeadersConsumer extends ValidatingHeadersConsumer {
|
||||
private class HeadersConsumer extends ValidatingHeadersConsumer implements DecodingCallback {
|
||||
|
||||
boolean maxHeaderListSizeReached;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
@ -1740,6 +1800,9 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
public void onDecoded(CharSequence name, CharSequence value)
|
||||
throws UncheckedIOException
|
||||
{
|
||||
if (maxHeaderListSizeReached) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String n = name.toString();
|
||||
String v = value.toString();
|
||||
@ -1762,6 +1825,23 @@ class Stream<T> extends ExchangeImpl<T> {
|
||||
protected String formatMessage(String message, String header) {
|
||||
return "malformed response: " + super.formatMessage(message, header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException {
|
||||
if (maxHeaderListSizeReached) return;
|
||||
try {
|
||||
DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize);
|
||||
} catch (ProtocolException cause) {
|
||||
maxHeaderListSizeReached = true;
|
||||
// If this is a push stream: cancel the parent.
|
||||
if (Stream.this instanceof Stream.PushedStream<?> ps) {
|
||||
ps.parent.onProtocolError(cause);
|
||||
}
|
||||
// cancel the stream, continue processing
|
||||
onProtocolError(cause);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class Http2StreamResponseSubscriber<U> extends HttpBodySubscriberWrapper<U> {
|
||||
|
@ -39,7 +39,11 @@ public class HeaderDecoder extends ValidatingHeadersConsumer {
|
||||
String n = name.toString();
|
||||
String v = value.toString();
|
||||
super.onDecoded(n, v);
|
||||
headersBuilder.addHeader(n, v);
|
||||
addHeader(n, v);
|
||||
}
|
||||
|
||||
protected void addHeader(String name, String value) {
|
||||
headersBuilder.addHeader(name, value);
|
||||
}
|
||||
|
||||
public HttpHeaders headers() {
|
||||
|
@ -616,6 +616,19 @@ public final class Utils {
|
||||
Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue))));
|
||||
}
|
||||
|
||||
public static int getIntegerNetProperty(String property, int min, int max, int defaultValue, boolean log) {
|
||||
int value = Utils.getIntegerNetProperty(property, defaultValue);
|
||||
// use default value if misconfigured
|
||||
if (value < min || value > max) {
|
||||
if (log && Log.errors()) {
|
||||
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
|
||||
"using default={4}", property, value, min, max, defaultValue);
|
||||
}
|
||||
value = defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static SSLParameters copySSLParameters(SSLParameters p) {
|
||||
SSLParameters p1 = new SSLParameters();
|
||||
p1.setAlgorithmConstraints(p.getAlgorithmConstraints());
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
@ -27,6 +27,7 @@ package jdk.internal.net.http.hpack;
|
||||
import jdk.internal.net.http.hpack.HPACK.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -107,12 +108,16 @@ public final class Decoder {
|
||||
private final StringReader stringReader;
|
||||
private final StringBuilder name;
|
||||
private final StringBuilder value;
|
||||
private final int maxHeaderListSize;
|
||||
private final int maxIndexed;
|
||||
private int intValue;
|
||||
private boolean firstValueRead;
|
||||
private boolean firstValueIndex;
|
||||
private boolean nameHuffmanEncoded;
|
||||
private boolean valueHuffmanEncoded;
|
||||
private int capacity;
|
||||
private long size;
|
||||
private int indexed;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Decoder} with the specified initial capacity of the
|
||||
@ -129,6 +134,31 @@ public final class Decoder {
|
||||
* if capacity is negative
|
||||
*/
|
||||
public Decoder(int capacity) {
|
||||
this(capacity, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Decoder} with the specified initial capacity of the
|
||||
* header table, a max header list size, and a maximum number of literals
|
||||
* with indexing per header section.
|
||||
*
|
||||
* <p> The value of the capacity has to be agreed between decoder and encoder out-of-band,
|
||||
* e.g. by a protocol that uses HPACK
|
||||
* (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
|
||||
*
|
||||
* @param capacity
|
||||
* a non-negative integer
|
||||
* @param maxHeaderListSize
|
||||
* a maximum value for the header list size. This is the uncompressed
|
||||
* names size + uncompressed values size + 32 bytes per field line
|
||||
* @param maxIndexed
|
||||
* the maximum number of literal with indexing we're prepared to handle
|
||||
* for a header field section
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if capacity is negative
|
||||
*/
|
||||
public Decoder(int capacity, int maxHeaderListSize, int maxIndexed) {
|
||||
id = DECODERS_IDS.incrementAndGet();
|
||||
logger = HPACK.getLogger().subLogger("Decoder#" + id);
|
||||
if (logger.isLoggable(NORMAL)) {
|
||||
@ -145,6 +175,8 @@ public final class Decoder {
|
||||
toString(), hashCode);
|
||||
});
|
||||
}
|
||||
this.maxHeaderListSize = maxHeaderListSize;
|
||||
this.maxIndexed = maxIndexed;
|
||||
setMaxCapacity0(capacity);
|
||||
table = new SimpleHeaderTable(capacity, logger.subLogger("HeaderTable"));
|
||||
integerReader = new IntegerReader();
|
||||
@ -242,22 +274,25 @@ public final class Decoder {
|
||||
requireNonNull(consumer, "consumer");
|
||||
if (logger.isLoggable(NORMAL)) {
|
||||
logger.log(NORMAL, () -> format("reading %s, end of header block? %s",
|
||||
headerBlock, endOfHeaderBlock));
|
||||
headerBlock, endOfHeaderBlock));
|
||||
}
|
||||
while (headerBlock.hasRemaining()) {
|
||||
proceed(headerBlock, consumer);
|
||||
}
|
||||
if (endOfHeaderBlock && state != State.READY) {
|
||||
logger.log(NORMAL, () -> format("unexpected end of %s representation",
|
||||
state));
|
||||
state));
|
||||
throw new IOException("Unexpected end of header block");
|
||||
}
|
||||
if (endOfHeaderBlock) {
|
||||
size = indexed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void proceed(ByteBuffer input, DecodingCallback action)
|
||||
throws IOException {
|
||||
switch (state) {
|
||||
case READY -> resumeReady(input);
|
||||
case READY -> resumeReady(input, action);
|
||||
case INDEXED -> resumeIndexed(input, action);
|
||||
case LITERAL -> resumeLiteral(input, action);
|
||||
case LITERAL_WITH_INDEXING -> resumeLiteralWithIndexing(input, action);
|
||||
@ -268,7 +303,7 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeReady(ByteBuffer input) {
|
||||
private void resumeReady(ByteBuffer input, DecodingCallback action) throws IOException {
|
||||
int b = input.get(input.position()) & 0xff; // absolute read
|
||||
State s = states.get(b);
|
||||
if (logger.isLoggable(EXTRA)) {
|
||||
@ -289,6 +324,9 @@ public final class Decoder {
|
||||
}
|
||||
break;
|
||||
case LITERAL_WITH_INDEXING:
|
||||
if (maxIndexed > 0 && ++indexed > maxIndexed) {
|
||||
action.onMaxLiteralWithIndexingReached(indexed, maxIndexed);
|
||||
}
|
||||
state = State.LITERAL_WITH_INDEXING;
|
||||
firstValueIndex = (b & 0b0011_1111) != 0;
|
||||
if (firstValueIndex) {
|
||||
@ -315,6 +353,12 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMaxHeaderListSize(long sz, DecodingCallback consumer) throws ProtocolException {
|
||||
if (maxHeaderListSize > 0 && sz > maxHeaderListSize) {
|
||||
consumer.onMaxHeaderListSizeReached(sz, maxHeaderListSize);
|
||||
}
|
||||
}
|
||||
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | 1 | Index (7+) |
|
||||
@ -332,6 +376,8 @@ public final class Decoder {
|
||||
}
|
||||
try {
|
||||
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
|
||||
size = size + 32 + f.name.length() + f.value.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onIndexed(intValue, f.name, f.value);
|
||||
} finally {
|
||||
state = State.READY;
|
||||
@ -374,7 +420,7 @@ public final class Decoder {
|
||||
//
|
||||
private void resumeLiteral(ByteBuffer input, DecodingCallback action)
|
||||
throws IOException {
|
||||
if (!completeReading(input)) {
|
||||
if (!completeReading(input, action)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -385,6 +431,8 @@ public final class Decoder {
|
||||
intValue, value, valueHuffmanEncoded));
|
||||
}
|
||||
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
|
||||
size = size + 32 + f.name.length() + value.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteral(intValue, f.name, value, valueHuffmanEncoded);
|
||||
} else {
|
||||
if (logger.isLoggable(NORMAL)) {
|
||||
@ -392,6 +440,8 @@ public final class Decoder {
|
||||
"literal without indexing ('%s', huffman=%b, '%s', huffman=%b)",
|
||||
name, nameHuffmanEncoded, value, valueHuffmanEncoded));
|
||||
}
|
||||
size = size + 32 + name.length() + value.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
|
||||
}
|
||||
} finally {
|
||||
@ -425,7 +475,7 @@ public final class Decoder {
|
||||
private void resumeLiteralWithIndexing(ByteBuffer input,
|
||||
DecodingCallback action)
|
||||
throws IOException {
|
||||
if (!completeReading(input)) {
|
||||
if (!completeReading(input, action)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -445,6 +495,8 @@ public final class Decoder {
|
||||
}
|
||||
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
|
||||
n = f.name;
|
||||
size = size + 32 + n.length() + v.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded);
|
||||
} else {
|
||||
n = name.toString();
|
||||
@ -453,6 +505,8 @@ public final class Decoder {
|
||||
"literal with incremental indexing ('%s', huffman=%b, '%s', huffman=%b)",
|
||||
n, nameHuffmanEncoded, value, valueHuffmanEncoded));
|
||||
}
|
||||
size = size + 32 + n.length() + v.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded);
|
||||
}
|
||||
table.put(n, v);
|
||||
@ -486,7 +540,7 @@ public final class Decoder {
|
||||
private void resumeLiteralNeverIndexed(ByteBuffer input,
|
||||
DecodingCallback action)
|
||||
throws IOException {
|
||||
if (!completeReading(input)) {
|
||||
if (!completeReading(input, action)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -497,6 +551,8 @@ public final class Decoder {
|
||||
intValue, value, valueHuffmanEncoded));
|
||||
}
|
||||
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
|
||||
size = size + 32 + f.name.length() + value.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded);
|
||||
} else {
|
||||
if (logger.isLoggable(NORMAL)) {
|
||||
@ -504,6 +560,8 @@ public final class Decoder {
|
||||
"literal never indexed ('%s', huffman=%b, '%s', huffman=%b)",
|
||||
name, nameHuffmanEncoded, value, valueHuffmanEncoded));
|
||||
}
|
||||
size = size + 32 + name.length() + value.length();
|
||||
checkMaxHeaderListSize(size, action);
|
||||
action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
|
||||
}
|
||||
} finally {
|
||||
@ -541,7 +599,7 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean completeReading(ByteBuffer input) throws IOException {
|
||||
private boolean completeReading(ByteBuffer input, DecodingCallback action) throws IOException {
|
||||
if (!firstValueRead) {
|
||||
if (firstValueIndex) {
|
||||
if (!integerReader.read(input)) {
|
||||
@ -551,6 +609,8 @@ public final class Decoder {
|
||||
integerReader.reset();
|
||||
} else {
|
||||
if (!stringReader.read(input, name)) {
|
||||
long sz = size + 32 + name.length();
|
||||
checkMaxHeaderListSize(sz, action);
|
||||
return false;
|
||||
}
|
||||
nameHuffmanEncoded = stringReader.isHuffmanEncoded();
|
||||
@ -560,6 +620,8 @@ public final class Decoder {
|
||||
return false;
|
||||
} else {
|
||||
if (!stringReader.read(input, value)) {
|
||||
long sz = size + 32 + name.length() + value.length();
|
||||
checkMaxHeaderListSize(sz, action);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
package jdk.internal.net.http.hpack;
|
||||
|
||||
import java.net.ProtocolException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
@ -292,4 +293,17 @@ public interface DecodingCallback {
|
||||
* new capacity of the header table
|
||||
*/
|
||||
default void onSizeUpdate(int capacity) { }
|
||||
|
||||
default void onMaxHeaderListSizeReached(long size, int maxHeaderListSize)
|
||||
throws ProtocolException {
|
||||
throw new ProtocolException(String
|
||||
.format("Size exceeds MAX_HEADERS_LIST_SIZE: %s > %s",
|
||||
size, maxHeaderListSize));
|
||||
}
|
||||
|
||||
default void onMaxLiteralWithIndexingReached(long indexed, int maxIndexed)
|
||||
throws ProtocolException {
|
||||
throw new ProtocolException(String.format("Too many literal with indexing: %s > %s",
|
||||
indexed, maxIndexed));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
@ -258,9 +258,10 @@ public class Encoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
assert encoding : "encoding is false";
|
||||
}
|
||||
|
||||
private boolean isHuffmanBetterFor(CharSequence value) {
|
||||
protected final boolean isHuffmanBetterFor(CharSequence value) {
|
||||
// prefer Huffman encoding only if it is strictly smaller than Latin-1
|
||||
return huffmanWriter.lengthOf(value) < value.length();
|
||||
}
|
||||
@ -340,6 +341,10 @@ public class Encoder {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected final int tableIndexOf(CharSequence name, CharSequence value) {
|
||||
return getHeaderTable().indexOf(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the {@linkplain #header(CharSequence, CharSequence) set up}
|
||||
* header into the given buffer.
|
||||
@ -380,6 +385,7 @@ public class Encoder {
|
||||
writer.reset(); // FIXME: WHY?
|
||||
encoding = false;
|
||||
}
|
||||
assert done || encoding : "done: " + done + ", encoding: " + encoding;
|
||||
return done;
|
||||
}
|
||||
|
||||
@ -542,4 +548,8 @@ public class Encoder {
|
||||
"Previous encoding operation hasn't finished yet");
|
||||
}
|
||||
}
|
||||
|
||||
protected final Logger logger() {
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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
|
||||
@ -115,6 +115,25 @@
|
||||
* The HTTP/2 client maximum frame size in bytes. The server is not permitted to send a frame
|
||||
* larger than this.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.maxLiteralWithIndexing}</b> (default: 512)<br>
|
||||
* The maximum number of header field lines (header name and value pairs) that a
|
||||
* client is willing to add to the HPack Decoder dynamic table during the decoding
|
||||
* of an entire header field section.
|
||||
* This is purely an implementation limit.
|
||||
* If a peer sends a field section with encoding that
|
||||
* exceeds this limit a {@link java.net.ProtocolException ProtocolException} will be raised.
|
||||
* A value of zero or a negative value means no limit.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.maxNonFinalResponses}</b> (default: 8)<br>
|
||||
* The maximum number of interim (non-final) responses that a client is prepared
|
||||
* to accept on a request-response stream before the final response is received.
|
||||
* Interim responses are responses with a status in the range [100, 199] inclusive.
|
||||
* This is purely an implementation limit.
|
||||
* If a peer sends a number of interim response that exceeds this limit before
|
||||
* sending the final response, a {@link java.net.ProtocolException ProtocolException}
|
||||
* will be raised.
|
||||
* A value of zero or a negative value means no limit.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.maxstreams}</b> (default: 100)<br>
|
||||
* The maximum number of HTTP/2 push streams that the client will permit servers to open
|
||||
* simultaneously.
|
||||
@ -155,6 +174,15 @@
|
||||
* conf/net.properties)<br>A comma separated list of HTTP authentication scheme names, that
|
||||
* are disallowed for use by the HTTP client implementation, for HTTP CONNECT tunneling.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.http.maxHeaderSize}</b> (default: 393216 or 384kB)
|
||||
* <br>The maximum header field section size that the client is prepared to accept.
|
||||
* This is computed as the sum of the size of the uncompressed header name, plus
|
||||
* the size of the uncompressed header value, plus an overhead of 32 bytes for
|
||||
* each field section line. If a peer sends a field section that exceeds this
|
||||
* size a {@link java.net.ProtocolException ProtocolException} will be raised.
|
||||
* This applies to all versions of the protocol. A value of zero or a negative
|
||||
* value means no limit.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @moduleGraph
|
||||
* @since 11
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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,7 +170,7 @@ public final class EncryptionKey implements SecretKey {
|
||||
if (destroyed) {
|
||||
return "Destroyed EncryptionKey";
|
||||
}
|
||||
return "key " + key.toString();
|
||||
return "EncryptionKey: " + key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
@ -27,7 +27,6 @@ package javax.security.auth.kerberos;
|
||||
|
||||
import javax.security.auth.Destroyable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -140,8 +139,7 @@ public final class KerberosCredMessage implements Destroyable {
|
||||
if (destroyed) {
|
||||
return "Destroyed KerberosCredMessage";
|
||||
} else {
|
||||
return "KRB_CRED from " + sender + " to " + recipient + ":\n"
|
||||
+ Base64.getUrlEncoder().encodeToString(message);
|
||||
return "KRB_CRED from " + sender + " to " + recipient;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, 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
|
||||
@ -273,9 +273,9 @@ public class KerberosKey implements SecretKey {
|
||||
if (destroyed) {
|
||||
return "Destroyed KerberosKey";
|
||||
}
|
||||
return "Kerberos Principal " + principal +
|
||||
"Key Version " + versionNum +
|
||||
"key " + key.toString();
|
||||
return "KerberosKey: principal " + principal +
|
||||
", version " + versionNum +
|
||||
", key " + key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, 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,7 +30,8 @@ import java.util.Arrays;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.security.auth.Destroyable;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
|
||||
import sun.security.jgss.krb5.Krb5Util;
|
||||
import sun.security.krb5.Asn1Exception;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
import sun.security.krb5.EncryptionKey;
|
||||
@ -225,15 +226,8 @@ class KeyImpl implements SecretKey, Destroyable, Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
HexDumpEncoder hd = new HexDumpEncoder();
|
||||
return "EncryptionKey: keyType=" + keyType
|
||||
+ " keyBytes (hex dump)="
|
||||
+ (keyBytes == null || keyBytes.length == 0 ?
|
||||
" Empty Key" :
|
||||
'\n' + hd.encodeBuffer(keyBytes)
|
||||
+ '\n');
|
||||
|
||||
|
||||
return "keyType=" + keyType
|
||||
+ ", " + Krb5Util.keyInfo(keyBytes);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
|
@ -33,8 +33,10 @@ import sun.security.jgss.spi.*;
|
||||
import sun.security.jgss.TokenTracker;
|
||||
import sun.security.krb5.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.*;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.kerberos.ServicePermission;
|
||||
@ -899,15 +901,11 @@ class Krb5Context implements GSSContextSpi {
|
||||
|
||||
public final byte[] wrap(byte[] inBuf, int offset, int len,
|
||||
MessageProp msgProp) throws GSSException {
|
||||
if (DEBUG != null) {
|
||||
DEBUG.println("Krb5Context.wrap: data=["
|
||||
+ getHexBytes(inBuf, offset, len)
|
||||
+ "]");
|
||||
}
|
||||
|
||||
if (state != STATE_DONE)
|
||||
throw new GSSException(GSSException.NO_CONTEXT, -1,
|
||||
"Wrap called in invalid state!");
|
||||
if (state != STATE_DONE) {
|
||||
throw new GSSException(GSSException.NO_CONTEXT, -1,
|
||||
"Wrap called in invalid state!");
|
||||
}
|
||||
|
||||
byte[] encToken = null;
|
||||
try {
|
||||
@ -1050,12 +1048,6 @@ class Krb5Context implements GSSContextSpi {
|
||||
setSequencingAndReplayProps(token, msgProp);
|
||||
}
|
||||
|
||||
if (DEBUG != null) {
|
||||
DEBUG.println("Krb5Context.unwrap: data=["
|
||||
+ getHexBytes(data, 0, data.length)
|
||||
+ "]");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -1405,8 +1397,22 @@ class Krb5Context implements GSSContextSpi {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Kerberos session key: etype: " + key.getEType() + "\n" +
|
||||
new HexDumpEncoder().encodeBuffer(key.getBytes());
|
||||
return "Kerberos session key: etype=" + key.getEType()
|
||||
+ ", " + Krb5Util.keyInfo(key.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException
|
||||
("KerberosSessionKey not directly deserializable");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1477,5 +1483,4 @@ class Krb5Context implements GSSContextSpi {
|
||||
public void setAuthzData(AuthorizationData authzData) {
|
||||
this.authzData = authzData;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, 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,9 @@ import javax.security.auth.kerberos.KerberosTicket;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import java.io.Serial;
|
||||
import java.net.InetAddress;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.Date;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
@ -400,4 +402,17 @@ public class Krb5InitCredential
|
||||
throw ge;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("Krb5InitCredential not deserializable");
|
||||
}
|
||||
}
|
||||
|
@ -187,4 +187,19 @@ public class Krb5Util {
|
||||
KeyTab ktab, PrincipalName cname) {
|
||||
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
|
||||
}
|
||||
|
||||
public static String keyInfo(byte[] data) {
|
||||
if (data == null) {
|
||||
return "null key";
|
||||
} else if (data.length == 0) {
|
||||
return "empty key";
|
||||
} else {
|
||||
for (byte b : data) {
|
||||
if (b != 0) {
|
||||
return data.length + "-byte key";
|
||||
}
|
||||
}
|
||||
return data.length + "-byte zero key";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package sun.security.krb5;
|
||||
|
||||
import sun.security.jgss.krb5.Krb5Util;
|
||||
import sun.security.util.*;
|
||||
import sun.security.krb5.internal.*;
|
||||
import sun.security.krb5.internal.crypto.*;
|
||||
@ -498,12 +499,7 @@ public class EncryptionKey
|
||||
|
||||
public String toString() {
|
||||
return "EncryptionKey: keyType=" + keyType
|
||||
+ " kvno=" + kvno
|
||||
+ " keyValue (hex dump)="
|
||||
+ (keyValue == null || keyValue.length == 0 ?
|
||||
" Empty Key" : '\n'
|
||||
+ Krb5.hexDumper.encodeBuffer(keyValue)
|
||||
+ '\n');
|
||||
+ ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,28 +83,36 @@ import static sun.security.krb5.internal.Krb5.DEBUG;
|
||||
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
|
||||
* http://www.ietf.org/rfc/rfc4120.txt</a>.
|
||||
*/
|
||||
// The instance fields not statically typed as Serializable are ASN.1
|
||||
// encoded and written by the writeObject method.
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
public class KRBError implements java.io.Serializable {
|
||||
static final long serialVersionUID = 3643809337475284503L;
|
||||
|
||||
private int pvno;
|
||||
private int msgType;
|
||||
private KerberosTime cTime; //optional
|
||||
private Integer cuSec; //optional
|
||||
private KerberosTime sTime;
|
||||
private Integer suSec;
|
||||
private int errorCode;
|
||||
private Realm crealm; //optional
|
||||
private PrincipalName cname; //optional
|
||||
private PrincipalName sname;
|
||||
private String eText; //optional
|
||||
private byte[] eData; //optional
|
||||
private Checksum eCksum; //optional
|
||||
private transient int pvno;
|
||||
private transient int msgType;
|
||||
private transient KerberosTime cTime; //optional
|
||||
private transient Integer cuSec; //optional
|
||||
private transient KerberosTime sTime;
|
||||
private transient Integer suSec;
|
||||
private transient int errorCode;
|
||||
private transient Realm crealm; //optional
|
||||
private transient PrincipalName cname; //optional
|
||||
private transient PrincipalName sname;
|
||||
private transient String eText; //optional
|
||||
private transient byte[] eData; //optional
|
||||
private transient Checksum eCksum; //optional
|
||||
|
||||
private PAData[] pa; // PA-DATA in eData
|
||||
private transient PAData[] pa; // PA-DATA in eData
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param is the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream is)
|
||||
throws IOException, ClassNotFoundException {
|
||||
try {
|
||||
|
@ -320,9 +320,6 @@ public class Krb5 {
|
||||
public static final Debug DEBUG = Debug.of("krb5", GetPropertyAction
|
||||
.privilegedGetProperty("sun.security.krb5.debug"));
|
||||
|
||||
public static final sun.security.util.HexDumpEncoder hexDumper =
|
||||
new sun.security.util.HexDumpEncoder();
|
||||
|
||||
static {
|
||||
errMsgList = new Hashtable<Integer,String> ();
|
||||
errMsgList.put(KDC_ERR_NONE, "No error");
|
||||
|
@ -195,10 +195,6 @@ public class Kinit {
|
||||
System.out.print("Password for " + princName + ":");
|
||||
System.out.flush();
|
||||
psswd = Password.readPassword(System.in);
|
||||
if (DEBUG != null) {
|
||||
DEBUG.println(">>> Kinit console input " +
|
||||
new String(psswd));
|
||||
}
|
||||
}
|
||||
builder = new KrbAsReqBuilder(principal, psswd);
|
||||
} else {
|
||||
|
@ -240,6 +240,19 @@ abstract class P11Key implements Key, Length {
|
||||
return new KeyRep(type, getAlgorithm(), format, getEncodedInternal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("P11Key not directly deserializable");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
token.ensureValid();
|
||||
String s1 = token.provider.getName() + " " + algorithm + " " + type
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2024, 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,9 +187,23 @@ final class P11SecureRandom extends SecureRandomSpi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param in the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
if (token == null) {
|
||||
throw new InvalidObjectException("token is null");
|
||||
}
|
||||
if (mixBuffer != null) {
|
||||
mixBuffer = mixBuffer.clone();
|
||||
}
|
||||
// assign default values to non-null transient fields
|
||||
iBuffer = new byte[IBUFFER_SIZE];
|
||||
ibuffered = 0;
|
||||
|
@ -1947,6 +1947,19 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
return new SunPKCS11Rep(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of this object from the stream.
|
||||
*
|
||||
* @param stream the {@code ObjectInputStream} from which data is read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws ClassNotFoundException if a serialized class cannot be loaded
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException("SunPKCS11 not directly deserializable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialized representation of the SunPKCS11 provider.
|
||||
*/
|
||||
|
@ -127,11 +127,6 @@ public class CK_PBE_PARAMS {
|
||||
sb.append(pPassword.length);
|
||||
sb.append(Constants.NEWLINE);
|
||||
|
||||
sb.append(Constants.INDENT);
|
||||
sb.append("pPassword: ");
|
||||
sb.append(pPassword);
|
||||
sb.append(Constants.NEWLINE);
|
||||
|
||||
sb.append(Constants.INDENT);
|
||||
sb.append("ulSaltLen: ");
|
||||
sb.append(pSalt.length);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
@ -70,6 +70,16 @@
|
||||
* while the headers are being read, then the connection is terminated and the request ignored.
|
||||
* If the value is less than or equal to zero, then the default value is used.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty sun.net.httpserver.maxReqHeaderSize}</b> (default: 393216 or 384kB)<br>
|
||||
* The maximum header field section size that the server is prepared to accept.
|
||||
* This is computed as the sum of the size of the header name, plus
|
||||
* the size of the header value, plus an overhead of 32 bytes for
|
||||
* each field section line. The request line counts as a first field section line,
|
||||
* where the name is empty and the value is the whole line.
|
||||
* If this limit is exceeded while the headers are being read, then the connection
|
||||
* is terminated and the request ignored.
|
||||
* If the value is less than or equal to zero, there is no limit.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty sun.net.httpserver.maxReqTime}</b> (default: -1)<br>
|
||||
* The maximum time in milliseconds allowed to receive a request headers and body.
|
||||
* In practice, the actual time is a function of request size, network speed, and handler
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -44,8 +44,10 @@ class Request {
|
||||
private SocketChannel chan;
|
||||
private InputStream is;
|
||||
private OutputStream os;
|
||||
private final int maxReqHeaderSize;
|
||||
|
||||
Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
|
||||
this.maxReqHeaderSize = ServerConfig.getMaxReqHeaderSize();
|
||||
is = rawInputStream;
|
||||
os = rawout;
|
||||
do {
|
||||
@ -75,6 +77,7 @@ class Request {
|
||||
public String readLine () throws IOException {
|
||||
boolean gotCR = false, gotLF = false;
|
||||
pos = 0; lineBuf = new StringBuffer();
|
||||
long lsize = 32;
|
||||
while (!gotLF) {
|
||||
int c = is.read();
|
||||
if (c == -1) {
|
||||
@ -87,20 +90,27 @@ class Request {
|
||||
gotCR = false;
|
||||
consume (CR);
|
||||
consume (c);
|
||||
lsize = lsize + 2;
|
||||
}
|
||||
} else {
|
||||
if (c == CR) {
|
||||
gotCR = true;
|
||||
} else {
|
||||
consume (c);
|
||||
lsize = lsize + 1;
|
||||
}
|
||||
}
|
||||
if (maxReqHeaderSize > 0 && lsize > maxReqHeaderSize) {
|
||||
throw new IOException("Maximum header (" +
|
||||
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
|
||||
ServerConfig.getMaxReqHeaderSize() + ".");
|
||||
}
|
||||
}
|
||||
lineBuf.append (buf, 0, pos);
|
||||
return new String (lineBuf);
|
||||
}
|
||||
|
||||
private void consume (int c) {
|
||||
private void consume (int c) throws IOException {
|
||||
if (pos == BUF_LEN) {
|
||||
lineBuf.append (buf);
|
||||
pos = 0;
|
||||
@ -138,13 +148,22 @@ class Request {
|
||||
len = 1;
|
||||
firstc = c;
|
||||
}
|
||||
long hsize = startLine.length() + 32L;
|
||||
|
||||
while (firstc != LF && firstc != CR && firstc >= 0) {
|
||||
int keyend = -1;
|
||||
int c;
|
||||
boolean inKey = firstc > ' ';
|
||||
s[len++] = (char) firstc;
|
||||
hsize = hsize + 1;
|
||||
parseloop:{
|
||||
// We start parsing for a new name value pair here.
|
||||
// The max header size includes an overhead of 32 bytes per
|
||||
// name value pair.
|
||||
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
|
||||
long maxRemaining = maxReqHeaderSize > 0
|
||||
? maxReqHeaderSize - hsize - 32
|
||||
: Long.MAX_VALUE;
|
||||
while ((c = is.read()) >= 0) {
|
||||
switch (c) {
|
||||
/*fallthrough*/
|
||||
@ -178,6 +197,11 @@ class Request {
|
||||
s = ns;
|
||||
}
|
||||
s[len++] = (char) c;
|
||||
if (maxReqHeaderSize > 0 && len > maxRemaining) {
|
||||
throw new IOException("Maximum header (" +
|
||||
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
|
||||
ServerConfig.getMaxReqHeaderSize() + ".");
|
||||
}
|
||||
}
|
||||
firstc = -1;
|
||||
}
|
||||
@ -205,6 +229,13 @@ class Request {
|
||||
"sun.net.httpserver.maxReqHeaders) exceeded, " +
|
||||
ServerConfig.getMaxReqHeaders() + ".");
|
||||
}
|
||||
hsize = hsize + len + 32;
|
||||
if (maxReqHeaderSize > 0 && hsize > maxReqHeaderSize) {
|
||||
throw new IOException("Maximum header (" +
|
||||
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
|
||||
ServerConfig.getMaxReqHeaderSize() + ".");
|
||||
}
|
||||
|
||||
if (k == null) { // Headers disallows null keys, use empty string
|
||||
k = ""; // instead to represent invalid key
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2024, 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,6 +49,7 @@ class ServerConfig {
|
||||
// timing out request/response if max request/response time is configured
|
||||
private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000;
|
||||
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
|
||||
private static final int DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024;
|
||||
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
|
||||
|
||||
private static long idleTimerScheduleMillis;
|
||||
@ -62,6 +63,9 @@ class ServerConfig {
|
||||
private static int maxIdleConnections;
|
||||
// The maximum number of request headers allowable
|
||||
private static int maxReqHeaders;
|
||||
// a maximum value for the header list size. This is the
|
||||
// names size + values size + 32 bytes per field line
|
||||
private static int maxReqHeadersSize;
|
||||
// max time a request or response is allowed to take
|
||||
private static long maxReqTime;
|
||||
private static long maxRspTime;
|
||||
@ -107,6 +111,14 @@ class ServerConfig {
|
||||
maxReqHeaders = DEFAULT_MAX_REQ_HEADERS;
|
||||
}
|
||||
|
||||
// a value <= 0 means unlimited
|
||||
maxReqHeadersSize = Integer.getInteger(
|
||||
"sun.net.httpserver.maxReqHeaderSize",
|
||||
DEFAULT_MAX_REQ_HEADER_SIZE);
|
||||
if (maxReqHeadersSize <= 0) {
|
||||
maxReqHeadersSize = 0;
|
||||
}
|
||||
|
||||
maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
|
||||
DEFAULT_MAX_REQ_TIME);
|
||||
|
||||
@ -215,6 +227,10 @@ class ServerConfig {
|
||||
return maxReqHeaders;
|
||||
}
|
||||
|
||||
static int getMaxReqHeaderSize() {
|
||||
return maxReqHeadersSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the maximum amount of time the server will wait for the request to be read
|
||||
* completely. This method can return a value of 0 or negative to imply no maximum limit has
|
||||
|
@ -43,7 +43,7 @@ import sun.security.krb5.*;
|
||||
import sun.security.jgss.krb5.Krb5Util;
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.util.Debug;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
|
||||
import static sun.security.util.ResourcesMgr.getAuthResourceString;
|
||||
|
||||
/**
|
||||
@ -769,15 +769,11 @@ public class Krb5LoginModule implements LoginModule {
|
||||
|
||||
if (debug != null) {
|
||||
debug.println("principal is " + principal);
|
||||
HexDumpEncoder hd = new HexDumpEncoder();
|
||||
if (ktab != null) {
|
||||
debug.println("Will use keytab");
|
||||
} else if (storeKey) {
|
||||
for (int i = 0; i < encKeys.length; i++) {
|
||||
debug.println("EncryptionKey: keyType=" +
|
||||
encKeys[i].getEType() +
|
||||
" keyBytes (hex dump)=" +
|
||||
hd.encodeBuffer(encKeys[i].getBytes()));
|
||||
debug.println(encKeys[i].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -868,7 +864,7 @@ public class Krb5LoginModule implements LoginModule {
|
||||
}
|
||||
if (debug != null) {
|
||||
debug.println
|
||||
("password is " + new String(password));
|
||||
("Get password from shared state");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (c) 2024, 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
|
||||
@ -35,7 +36,6 @@ import java.nio.ByteOrder;
|
||||
* @test
|
||||
* @bug 8300258
|
||||
* @key randomness
|
||||
* @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64")
|
||||
* @summary C2: vectorization fails on simple ByteBuffer loop
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
@ -147,193 +147,420 @@ public class TestVectorizationMismatchedAccess {
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteLong1(byte[] dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteLong1a(byte[] dest, long[] src) {
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteLong1")
|
||||
public static void testByteLong1_runner() {
|
||||
runAndVerify(() -> testByteLong1(byteArray, longArray), 0);
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteLong1b(byte[] dest, long[] src) {
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteLong2(byte[] dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"})
|
||||
public static void testByteLong1c(byte[] dest, long[] src) {
|
||||
long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit)
|
||||
for (int i = 0; i < src.length - 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, base + 8 * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteLong1d(byte[] dest, long[] src) {
|
||||
long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit)
|
||||
for (int i = 0; i < src.length - 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, base + 8L * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteLong1a", "testByteLong1b", "testByteLong1c", "testByteLong1d"})
|
||||
public static void testByteLong1_runner() {
|
||||
runAndVerify(() -> testByteLong1a(byteArray, longArray), 0);
|
||||
runAndVerify(() -> testByteLong1b(byteArray, longArray), 0);
|
||||
testByteLong1c(byteArray, longArray);
|
||||
testByteLong1d(byteArray, longArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteLong2a(byte[] dest, long[] src) {
|
||||
for (int i = 1; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteLong2")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteLong2b(byte[] dest, long[] src) {
|
||||
for (int i = 1; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteLong2a", "testByteLong2b"})
|
||||
public static void testByteLong2_runner() {
|
||||
runAndVerify(() -> testByteLong2(byteArray, longArray), -8);
|
||||
runAndVerify(() -> testByteLong2a(byteArray, longArray), -8);
|
||||
runAndVerify(() -> testByteLong2b(byteArray, longArray), -8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteLong3(byte[] dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteLong3a(byte[] dest, long[] src) {
|
||||
for (int i = 0; i < src.length - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteLong3")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteLong3b(byte[] dest, long[] src) {
|
||||
for (int i = 0; i < src.length - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteLong3a", "testByteLong3b"})
|
||||
public static void testByteLong3_runner() {
|
||||
runAndVerify(() -> testByteLong3(byteArray, longArray), 8);
|
||||
runAndVerify(() -> testByteLong3a(byteArray, longArray), 8);
|
||||
runAndVerify(() -> testByteLong3b(byteArray, longArray), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"},
|
||||
applyIf = {"AlignVector", "false"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
// AlignVector cannot guarantee that invar is aligned.
|
||||
public static void testByteLong4(byte[] dest, long[] src, int start, int stop) {
|
||||
public static void testByteLong4a(byte[] dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteLong4")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"},
|
||||
applyIf = {"AlignVector", "false"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
// AlignVector cannot guarantee that invar is aligned.
|
||||
public static void testByteLong4b(byte[] dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteLong4a", "testByteLong4b"})
|
||||
public static void testByteLong4_runner() {
|
||||
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
|
||||
runAndVerify(() -> testByteLong4(byteArray, longArray, 0, size), 0);
|
||||
runAndVerify(() -> testByteLong4a(byteArray, longArray, 0, size), 0);
|
||||
runAndVerify(() -> testByteLong4b(byteArray, longArray, 0, size), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteLong5(byte[] dest, long[] src, int start, int stop) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteLong5a(byte[] dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteLong5")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteLong5b(byte[] dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteLong5a", "testByteLong5b"})
|
||||
public static void testByteLong5_runner() {
|
||||
baseOffset = 1;
|
||||
runAndVerify(() -> testByteLong5(byteArray, longArray, 0, size-1), 8);
|
||||
runAndVerify(() -> testByteLong5a(byteArray, longArray, 0, size-1), 8);
|
||||
runAndVerify(() -> testByteLong5b(byteArray, longArray, 0, size-1), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteByte1(byte[] dest, byte[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteByte1a(byte[] dest, byte[] src) {
|
||||
for (int i = 0; i < src.length / 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteByte1")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteByte1b(byte[] dest, byte[] src) {
|
||||
for (int i = 0; i < src.length / 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteByte1a", "testByteByte1b"})
|
||||
public static void testByteByte1_runner() {
|
||||
runAndVerify2(() -> testByteByte1(byteArray, byteArray), 0);
|
||||
runAndVerify2(() -> testByteByte1a(byteArray, byteArray), 0);
|
||||
runAndVerify2(() -> testByteByte1b(byteArray, byteArray), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testByteByte2(byte[] dest, byte[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
|
||||
// might get fixed with JDK-8325155.
|
||||
public static void testByteByte2a(byte[] dest, byte[] src) {
|
||||
for (int i = 1; i < src.length / 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteByte2")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
|
||||
applyIfPlatform = {"64-bit", "true"})
|
||||
// 32-bit: address has ConvL2I for cast of long to address, not supported.
|
||||
public static void testByteByte2b(byte[] dest, byte[] src) {
|
||||
for (int i = 1; i < src.length / 8; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteByte2a", "testByteByte2b"})
|
||||
public static void testByteByte2_runner() {
|
||||
runAndVerify2(() -> testByteByte2(byteArray, byteArray), -8);
|
||||
runAndVerify2(() -> testByteByte2a(byteArray, byteArray), -8);
|
||||
runAndVerify2(() -> testByteByte2b(byteArray, byteArray), -8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte3(byte[] dest, byte[] src) {
|
||||
public static void testByteByte3a(byte[] dest, byte[] src) {
|
||||
for (int i = 0; i < src.length / 8 - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteByte3")
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte3b(byte[] dest, byte[] src) {
|
||||
for (int i = 0; i < src.length / 8 - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteByte3a", "testByteByte3b"})
|
||||
public static void testByteByte3_runner() {
|
||||
runAndVerify2(() -> testByteByte3(byteArray, byteArray), 8);
|
||||
runAndVerify2(() -> testByteByte3a(byteArray, byteArray), 8);
|
||||
runAndVerify2(() -> testByteByte3b(byteArray, byteArray), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte4(byte[] dest, byte[] src, int start, int stop) {
|
||||
public static void testByteByte4a(byte[] dest, byte[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteByte4")
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte4b(byte[] dest, byte[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteByte4a", "testByteByte4b"})
|
||||
public static void testByteByte4_runner() {
|
||||
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
|
||||
runAndVerify2(() -> testByteByte4(byteArray, byteArray, 0, size), 0);
|
||||
runAndVerify2(() -> testByteByte4a(byteArray, byteArray, 0, size), 0);
|
||||
runAndVerify2(() -> testByteByte4b(byteArray, byteArray, 0, size), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte5(byte[] dest, byte[] src, int start, int stop) {
|
||||
public static void testByteByte5a(byte[] dest, byte[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testByteByte5")
|
||||
@Test
|
||||
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
|
||||
public static void testByteByte5b(byte[] dest, byte[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testByteByte5a", "testByteByte5b"})
|
||||
public static void testByteByte5_runner() {
|
||||
baseOffset = 1;
|
||||
runAndVerify2(() -> testByteByte5(byteArray, byteArray, 0, size-1), 8);
|
||||
runAndVerify2(() -> testByteByte5a(byteArray, byteArray, 0, size-1), 8);
|
||||
runAndVerify2(() -> testByteByte5b(byteArray, byteArray, 0, size-1), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testOffHeapLong1(long dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P(dest + 8 * (i + int_con))
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong1a(long dest, long[] src) {
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8 * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testOffHeapLong1")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P(dest + 8L * (i + int_con))
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong1b(long dest, long[] src) {
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8L * i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testOffHeapLong1a", "testOffHeapLong1b"})
|
||||
public static void testOffHeapLong1_runner() {
|
||||
runAndVerify3(() -> testOffHeapLong1(baseOffHeap, longArray), 0);
|
||||
runAndVerify3(() -> testOffHeapLong1a(baseOffHeap, longArray), 0);
|
||||
runAndVerify3(() -> testOffHeapLong1b(baseOffHeap, longArray), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testOffHeapLong2(long dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong2a(long dest, long[] src) {
|
||||
for (int i = 1; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testOffHeapLong2")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong2b(long dest, long[] src) {
|
||||
for (int i = 1; i < src.length; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testOffHeapLong2a", "testOffHeapLong2b"})
|
||||
public static void testOffHeapLong2_runner() {
|
||||
runAndVerify3(() -> testOffHeapLong2(baseOffHeap, longArray), -8);
|
||||
runAndVerify3(() -> testOffHeapLong2a(baseOffHeap, longArray), -8);
|
||||
runAndVerify3(() -> testOffHeapLong2b(baseOffHeap, longArray), -8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
public static void testOffHeapLong3(long dest, long[] src) {
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong3a(long dest, long[] src) {
|
||||
for (int i = 0; i < src.length - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testOffHeapLong3")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
public static void testOffHeapLong3b(long dest, long[] src) {
|
||||
for (int i = 0; i < src.length - 1; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testOffHeapLong3a", "testOffHeapLong3b"})
|
||||
public static void testOffHeapLong3_runner() {
|
||||
runAndVerify3(() -> testOffHeapLong3(baseOffHeap, longArray), 8);
|
||||
runAndVerify3(() -> testOffHeapLong3a(baseOffHeap, longArray), 8);
|
||||
runAndVerify3(() -> testOffHeapLong3b(baseOffHeap, longArray), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
applyIf = {"AlignVector", "false"})
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
// applyIf = {"AlignVector", "false"})
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
// AlignVector cannot guarantee that invar is aligned.
|
||||
public static void testOffHeapLong4(long dest, long[] src, int start, int stop) {
|
||||
public static void testOffHeapLong4a(long dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testOffHeapLong4")
|
||||
@Test
|
||||
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
|
||||
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
|
||||
// applyIf = {"AlignVector", "false"})
|
||||
// FAILS: adr is CastX2P
|
||||
// See: JDK-8331576
|
||||
// AlignVector cannot guarantee that invar is aligned.
|
||||
public static void testOffHeapLong4b(long dest, long[] src, int start, int stop) {
|
||||
for (int i = start; i < stop; i++) {
|
||||
UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = {"testOffHeapLong4a", "testOffHeapLong4b"})
|
||||
public static void testOffHeapLong4_runner() {
|
||||
baseOffset = 8;
|
||||
runAndVerify3(() -> testOffHeapLong4(baseOffHeap, longArray, 0, size-1), 8);
|
||||
runAndVerify3(() -> testOffHeapLong4a(baseOffHeap, longArray, 0, size-1), 8);
|
||||
runAndVerify3(() -> testOffHeapLong4b(baseOffHeap, longArray, 0, size-1), 8);
|
||||
}
|
||||
}
|
||||
|
@ -1363,7 +1363,7 @@ public class TestAlignVector {
|
||||
static Object[] test17a(long[] a) {
|
||||
// Unsafe: vectorizes with profiling (not xcomp)
|
||||
for (int i = 0; i < RANGE; i++) {
|
||||
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
|
||||
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
|
||||
long v = UNSAFE.getLongUnaligned(a, adr);
|
||||
UNSAFE.putLongUnaligned(a, adr, v + 1);
|
||||
}
|
||||
@ -1375,7 +1375,7 @@ public class TestAlignVector {
|
||||
static Object[] test17b(long[] a) {
|
||||
// Not alignable
|
||||
for (int i = 0; i < RANGE-1; i++) {
|
||||
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
|
||||
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
|
||||
long v = UNSAFE.getLongUnaligned(a, adr);
|
||||
UNSAFE.putLongUnaligned(a, adr, v + 1);
|
||||
}
|
||||
@ -1392,7 +1392,7 @@ public class TestAlignVector {
|
||||
static Object[] test17c(long[] a) {
|
||||
// Unsafe: aligned vectorizes
|
||||
for (int i = 0; i < RANGE-1; i+=4) {
|
||||
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
|
||||
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
|
||||
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
|
||||
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
|
||||
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);
|
||||
@ -1422,7 +1422,7 @@ public class TestAlignVector {
|
||||
static Object[] test17d(long[] a) {
|
||||
// Not alignable
|
||||
for (int i = 0; i < RANGE-1; i+=4) {
|
||||
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
|
||||
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
|
||||
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
|
||||
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
|
||||
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);
|
||||
|
@ -1090,11 +1090,11 @@ public class TestAlignVectorFuzzer {
|
||||
int init = init_con_or_var();
|
||||
int limit = limit_con_or_var();
|
||||
int stride = stride_con();
|
||||
int scale = scale_con();
|
||||
int offset = offset1_con_or_var();
|
||||
long scale = scale_con();
|
||||
long offset = offset1_con_or_var();
|
||||
|
||||
for (int i = init; i < limit; i += stride) {
|
||||
int adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
|
||||
long adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
|
||||
int v = UNSAFE.getIntUnaligned(a, adr);
|
||||
UNSAFE.putIntUnaligned(a, adr, v + 1);
|
||||
}
|
||||
@ -1105,19 +1105,19 @@ public class TestAlignVectorFuzzer {
|
||||
int init = init_con_or_var();
|
||||
int limit = limit_con_or_var();
|
||||
int stride = stride_con();
|
||||
int scale = scale_con();
|
||||
int offset1 = offset1_con_or_var();
|
||||
int offset2 = offset2_con_or_var();
|
||||
int offset3 = offset3_con_or_var();
|
||||
long scale = scale_con();
|
||||
long offset1 = offset1_con_or_var();
|
||||
long offset2 = offset2_con_or_var();
|
||||
long offset3 = offset3_con_or_var();
|
||||
|
||||
int h1 = hand_unrolling1_con();
|
||||
int h2 = hand_unrolling2_con();
|
||||
int h3 = hand_unrolling3_con();
|
||||
|
||||
for (int i = init; i < limit; i += stride) {
|
||||
int adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
|
||||
int adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
|
||||
int adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
|
||||
long adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
|
||||
long adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
|
||||
long adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
|
||||
|
||||
if (h1 >= 1) { UNSAFE.putIntUnaligned(a, adr1 + 0*4, UNSAFE.getIntUnaligned(a, adr1 + 0*4) + 1); }
|
||||
if (h1 >= 2) { UNSAFE.putIntUnaligned(a, adr1 + 1*4, UNSAFE.getIntUnaligned(a, adr1 + 1*4) + 1); }
|
||||
|
@ -172,10 +172,10 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
static void test2(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// int and float arrays are two slices. But we pretend both are of type int.
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1);
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1);
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0);
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4);
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1);
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1);
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0);
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,10 +248,10 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// same as test2, except that reordering leads to different semantics
|
||||
// explanation analogue to test4
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); // B
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); // B
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,18 +275,18 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
long[] dataLa, long[] dataLb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// Chain of parallelizable op and conversion
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,18 +307,18 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
long[] dataLa, long[] dataLb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// Cycle involving 3 memory slices
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,19 +340,19 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
long[] dataLa, long[] dataLb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// 2-cycle, with more ops after
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
// more stuff after
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,19 +373,19 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
long[] dataLa, long[] dataLb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// 2-cycle, with more stuff before
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
// 2-cycle
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,18 +423,18 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
//
|
||||
// The cycle thus does not only go via packs, but also scalar ops.
|
||||
//
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; // A
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; // R: constant mismatch
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; // S
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; // U
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; // V
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // B: moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; // A
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; // R: constant mismatch
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; // S
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; // U
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; // V
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // B: moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,8 +463,8 @@ public class TestIndependentPacksWithCyclicDependency {
|
||||
|
||||
static void verify(String name, float[] data, float[] gold) {
|
||||
for (int i = 0; i < RANGE; i++) {
|
||||
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
|
||||
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
|
||||
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
|
||||
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
|
||||
if (datav != goldv) {
|
||||
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
|
||||
}
|
||||
|
@ -58,18 +58,18 @@ public class TestIndependentPacksWithCyclicDependency2 {
|
||||
long[] dataLa, long[] dataLb) {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// For explanation, see test 10 in TestIndependentPacksWithCyclicDependency.java
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
|
||||
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
|
||||
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
|
||||
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43;
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
|
||||
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
|
||||
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
|
||||
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
|
||||
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
|
||||
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,8 +83,8 @@ public class TestIndependentPacksWithCyclicDependency2 {
|
||||
|
||||
static void verify(String name, float[] data, float[] gold) {
|
||||
for (int i = 0; i < RANGE; i++) {
|
||||
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
|
||||
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
|
||||
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
|
||||
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
|
||||
if (datav != goldv) {
|
||||
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
|
||||
}
|
||||
|
@ -124,10 +124,10 @@ public class TestScheduleReordersScalarMemops {
|
||||
for (int i = 0; i < RANGE; i+=2) {
|
||||
// Do the same as test0, but without int-float conversion.
|
||||
// This should reproduce on machines where conversion is not implemented.
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A +1
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] * 11); // B *11
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A +1
|
||||
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
|
||||
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
|
||||
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] * 11); // B *11
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
@ -361,7 +362,7 @@ public class ExpectContinueTest implements HttpServerAdapters {
|
||||
}
|
||||
if (exceptionally && testThrowable != null) {
|
||||
err.println("Finished exceptionally Test throwable: " + testThrowable);
|
||||
assertEquals(IOException.class, testThrowable.getClass());
|
||||
assertEquals(testThrowable.getClass(), ProtocolException.class);
|
||||
} else if (exceptionally) {
|
||||
throw new TestException("Expected case to finish with an IOException but testException is null");
|
||||
} else if (resp != null) {
|
||||
|
@ -136,6 +136,7 @@ public class ShutdownNow implements HttpServerAdapters {
|
||||
if (message.equals("shutdownNow")) return true;
|
||||
// exception from cancelling an HTTP/2 stream
|
||||
if (message.matches("Stream [0-9]+ cancelled")) return true;
|
||||
if (message.contains("connection closed locally")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpHeaders;
|
||||
@ -195,7 +196,8 @@ public class PushPromiseContinuation {
|
||||
client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph);
|
||||
|
||||
CompletionException t = expectThrows(CompletionException.class, () -> cf.join());
|
||||
assertEquals(t.getCause().getClass(), IOException.class, "Expected an IOException but got " + t.getCause());
|
||||
assertEquals(t.getCause().getClass(), ProtocolException.class,
|
||||
"Expected a ProtocolException but got " + t.getCause());
|
||||
System.err.println("Client received the following expected exception: " + t.getCause());
|
||||
faultyServer.stop();
|
||||
}
|
||||
@ -222,7 +224,10 @@ public class PushPromiseContinuation {
|
||||
|
||||
static class Http2PushPromiseHeadersExchangeImpl extends Http2TestExchangeImpl {
|
||||
|
||||
Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, SSLSession sslSession, BodyOutputStream os, Http2TestServerConnection conn, boolean pushAllowed) {
|
||||
Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders,
|
||||
HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is,
|
||||
SSLSession sslSession, BodyOutputStream os,
|
||||
Http2TestServerConnection conn, boolean pushAllowed) {
|
||||
super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2024, 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
|
||||
@ -241,6 +241,7 @@ public interface HttpServerAdapters {
|
||||
public abstract void close();
|
||||
public abstract InetSocketAddress getRemoteAddress();
|
||||
public abstract String getConnectionKey();
|
||||
public abstract InetSocketAddress getLocalAddress();
|
||||
public void serverPush(URI uri, HttpHeaders headers, byte[] body) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||
serverPush(uri, headers, bais);
|
||||
@ -303,7 +304,10 @@ public interface HttpServerAdapters {
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return exchange.getRemoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getLocalAddress() {
|
||||
return exchange.getLocalAddress();
|
||||
}
|
||||
@Override
|
||||
public URI getRequestURI() { return exchange.getRequestURI(); }
|
||||
@Override
|
||||
@ -370,6 +374,10 @@ public interface HttpServerAdapters {
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return exchange.getRemoteAddress();
|
||||
}
|
||||
@Override
|
||||
public InetSocketAddress getLocalAddress() {
|
||||
return exchange.getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionKey() {
|
||||
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2023, 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 jdk.httpclient.test.lib.http2;
|
||||
|
||||
import java.util.function.*;
|
||||
|
||||
import jdk.internal.net.http.hpack.Encoder;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static jdk.internal.net.http.hpack.HPACK.Logger.Level.EXTRA;
|
||||
import static jdk.internal.net.http.hpack.HPACK.Logger.Level.NORMAL;
|
||||
|
||||
public class HpackTestEncoder extends Encoder {
|
||||
|
||||
public HpackTestEncoder(int maxCapacity) {
|
||||
super(maxCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the given header {@code (name, value)} with possibly sensitive
|
||||
* value.
|
||||
*
|
||||
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
|
||||
* this encoder will compress it using a special representation
|
||||
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3. Literal Header Field Never Indexed</a>).
|
||||
*
|
||||
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
|
||||
*
|
||||
* @param name
|
||||
* the name
|
||||
* @param value
|
||||
* the value
|
||||
* @param sensitive
|
||||
* whether or not the value is sensitive
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if any of the arguments are {@code null}
|
||||
* @throws IllegalStateException
|
||||
* if the encoder hasn't fully encoded the previous header, or
|
||||
* hasn't yet started to encode it
|
||||
* @see #header(CharSequence, CharSequence)
|
||||
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
|
||||
*/
|
||||
public void header(CharSequence name,
|
||||
CharSequence value,
|
||||
boolean sensitive) throws IllegalStateException {
|
||||
if (sensitive || getMaxCapacity() == 0) {
|
||||
super.header(name, value, true);
|
||||
} else {
|
||||
header(name, value, false, (n,v) -> false);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets up the given header {@code (name, value)} with possibly sensitive
|
||||
* value.
|
||||
*
|
||||
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
|
||||
* this encoder will compress it using a special representation
|
||||
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3. Literal Header Field Never Indexed</a>).
|
||||
*
|
||||
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
|
||||
*
|
||||
* @param name
|
||||
* the name
|
||||
* @param value
|
||||
* the value
|
||||
* @param insertionPolicy
|
||||
* a bipredicate to indicate whether a name value pair
|
||||
* should be added to the dynamic table
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if any of the arguments are {@code null}
|
||||
* @throws IllegalStateException
|
||||
* if the encoder hasn't fully encoded the previous header, or
|
||||
* hasn't yet started to encode it
|
||||
* @see #header(CharSequence, CharSequence)
|
||||
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
|
||||
*/
|
||||
public void header(CharSequence name,
|
||||
CharSequence value,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy)
|
||||
throws IllegalStateException {
|
||||
header(name, value, false, insertionPolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the given header {@code (name, value)} with possibly sensitive
|
||||
* value.
|
||||
*
|
||||
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
|
||||
* this encoder will compress it using a special representation
|
||||
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">
|
||||
* 6.2.3. Literal Header Field Never Indexed</a>).
|
||||
*
|
||||
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
|
||||
*
|
||||
* @param name
|
||||
* the name
|
||||
* @param value
|
||||
* the value
|
||||
* @param sensitive
|
||||
* whether or not the value is sensitive
|
||||
* @param insertionPolicy
|
||||
* a bipredicate to indicate whether a name value pair
|
||||
* should be added to the dynamic table
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if any of the arguments are {@code null}
|
||||
* @throws IllegalStateException
|
||||
* if the encoder hasn't fully encoded the previous header, or
|
||||
* hasn't yet started to encode it
|
||||
* @see #header(CharSequence, CharSequence)
|
||||
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
|
||||
*/
|
||||
public void header(CharSequence name,
|
||||
CharSequence value,
|
||||
boolean sensitive,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy)
|
||||
throws IllegalStateException {
|
||||
if (sensitive == true || getMaxCapacity() == 0 || !insertionPolicy.test(name, value)) {
|
||||
super.header(name, value, sensitive);
|
||||
return;
|
||||
}
|
||||
var logger = logger();
|
||||
// Arguably a good balance between complexity of implementation and
|
||||
// efficiency of encoding
|
||||
requireNonNull(name, "name");
|
||||
requireNonNull(value, "value");
|
||||
var t = getHeaderTable();
|
||||
int index = tableIndexOf(name, value);
|
||||
if (logger.isLoggable(NORMAL)) {
|
||||
logger.log(NORMAL, () -> format("encoding with indexing ('%s', '%s'): index:%s",
|
||||
name, value, index));
|
||||
}
|
||||
if (index > 0) {
|
||||
indexed(index);
|
||||
} else {
|
||||
boolean huffmanValue = isHuffmanBetterFor(value);
|
||||
if (index < 0) {
|
||||
literalWithIndexing(-index, value, huffmanValue);
|
||||
} else {
|
||||
boolean huffmanName = isHuffmanBetterFor(name);
|
||||
literalWithIndexing(name, huffmanName, value, huffmanValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int calculateCapacity(int maxCapacity) {
|
||||
return maxCapacity;
|
||||
}
|
||||
|
||||
}
|
@ -29,9 +29,12 @@ import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiPredicate;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import jdk.internal.net.http.common.HttpHeadersBuilder;
|
||||
import jdk.internal.net.http.frame.Http2Frame;
|
||||
|
||||
public interface Http2TestExchange {
|
||||
|
||||
@ -53,6 +56,12 @@ public interface Http2TestExchange {
|
||||
|
||||
void sendResponseHeaders(int rCode, long responseLength) throws IOException;
|
||||
|
||||
default void sendResponseHeaders(int rCode, long responseLength,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy)
|
||||
throws IOException {
|
||||
sendResponseHeaders(rCode, responseLength);
|
||||
}
|
||||
|
||||
InetSocketAddress getRemoteAddress();
|
||||
|
||||
int getResponseCode();
|
||||
@ -65,6 +74,10 @@ public interface Http2TestExchange {
|
||||
|
||||
void serverPush(URI uri, HttpHeaders headers, InputStream content);
|
||||
|
||||
default void sendFrames(List<Http2Frame> frames) throws IOException {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a PING on this exchanges connection, and completes the returned CF
|
||||
* with the number of milliseconds it took to get a valid response.
|
||||
|
@ -27,6 +27,7 @@ import jdk.httpclient.test.lib.http2.Http2TestServerConnection.ResponseHeaders;
|
||||
import jdk.internal.net.http.common.HttpHeadersBuilder;
|
||||
import jdk.internal.net.http.frame.HeaderFrame;
|
||||
import jdk.internal.net.http.frame.HeadersFrame;
|
||||
import jdk.internal.net.http.frame.Http2Frame;
|
||||
import jdk.internal.net.http.frame.ResetFrame;
|
||||
|
||||
import javax.net.ssl.SSLSession;
|
||||
@ -39,6 +40,7 @@ import java.net.http.HttpHeaders;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
public class Http2TestExchangeImpl implements Http2TestExchange {
|
||||
|
||||
@ -132,8 +134,13 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
|
||||
return os;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
|
||||
sendResponseHeaders(rCode, responseLength, (n,v) -> false);
|
||||
}
|
||||
@Override
|
||||
public void sendResponseHeaders(int rCode, long responseLength,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy)
|
||||
throws IOException {
|
||||
// Do not set Content-Length for 100, and do not set END_STREAM
|
||||
if (rCode == 100) responseLength = 0;
|
||||
|
||||
@ -147,7 +154,7 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
|
||||
HttpHeaders headers = rspheadersBuilder.build();
|
||||
|
||||
ResponseHeaders response
|
||||
= new ResponseHeaders(headers);
|
||||
= new ResponseHeaders(headers, insertionPolicy);
|
||||
response.streamid(streamid);
|
||||
response.setFlag(HeaderFrame.END_HEADERS);
|
||||
|
||||
@ -172,6 +179,11 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
|
||||
conn.outputQ.put(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFrames(List<Http2Frame> frames) throws IOException {
|
||||
conn.sendFrames(frames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return (InetSocketAddress) conn.socket.getRemoteSocketAddress();
|
||||
|
@ -24,6 +24,8 @@
|
||||
package jdk.httpclient.test.lib.http2;
|
||||
|
||||
import jdk.internal.net.http.common.HttpHeadersBuilder;
|
||||
import jdk.internal.net.http.common.Log;
|
||||
import jdk.internal.net.http.frame.ContinuationFrame;
|
||||
import jdk.internal.net.http.frame.DataFrame;
|
||||
import jdk.internal.net.http.frame.ErrorFrame;
|
||||
import jdk.internal.net.http.frame.FramesDecoder;
|
||||
@ -80,6 +82,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
@ -87,6 +90,7 @@ import java.util.function.Predicate;
|
||||
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE;
|
||||
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
|
||||
|
||||
/**
|
||||
@ -105,7 +109,7 @@ public class Http2TestServerConnection {
|
||||
final Http2TestExchangeSupplier exchangeSupplier;
|
||||
final InputStream is;
|
||||
final OutputStream os;
|
||||
volatile Encoder hpackOut;
|
||||
volatile HpackTestEncoder hpackOut;
|
||||
volatile Decoder hpackIn;
|
||||
volatile SettingsFrame clientSettings;
|
||||
final SettingsFrame serverSettings;
|
||||
@ -421,7 +425,9 @@ public class Http2TestServerConnection {
|
||||
}
|
||||
|
||||
public int getMaxFrameSize() {
|
||||
return clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
|
||||
var max = clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
|
||||
if (max <= 0) max = DEFAULT_MAX_FRAME_SIZE;
|
||||
return max;
|
||||
}
|
||||
|
||||
/** Sends a pre-canned HTTP/1.1 response. */
|
||||
@ -482,7 +488,7 @@ public class Http2TestServerConnection {
|
||||
//System.out.println("ServerSettings: " + serverSettings);
|
||||
//System.out.println("ClientSettings: " + clientSettings);
|
||||
|
||||
hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
|
||||
hpackOut = new HpackTestEncoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
|
||||
hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
|
||||
|
||||
if (!secure) {
|
||||
@ -812,6 +818,14 @@ public class Http2TestServerConnection {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFrames(List<Http2Frame> frames) throws IOException {
|
||||
synchronized (outputQ) {
|
||||
for (var frame : frames) {
|
||||
outputQ.put(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpHeadersBuilder createNewHeadersBuilder() {
|
||||
return new HttpHeadersBuilder();
|
||||
}
|
||||
@ -938,26 +952,38 @@ public class Http2TestServerConnection {
|
||||
return (streamid & 0x01) == 0x00;
|
||||
}
|
||||
|
||||
final ReentrantLock headersLock = new ReentrantLock();
|
||||
|
||||
/** Encodes an group of headers, without any ordering guarantees. */
|
||||
public List<ByteBuffer> encodeHeaders(HttpHeaders headers) {
|
||||
return encodeHeaders(headers, (n,v) -> false);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> encodeHeaders(HttpHeaders headers,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy) {
|
||||
List<ByteBuffer> buffers = new LinkedList<>();
|
||||
|
||||
ByteBuffer buf = getBuffer();
|
||||
boolean encoded;
|
||||
for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
|
||||
List<String> values = entry.getValue();
|
||||
String key = entry.getKey().toLowerCase();
|
||||
for (String value : values) {
|
||||
do {
|
||||
hpackOut.header(key, value);
|
||||
encoded = hpackOut.encode(buf);
|
||||
if (!encoded) {
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
buf = getBuffer();
|
||||
}
|
||||
} while (!encoded);
|
||||
headersLock.lock();
|
||||
try {
|
||||
for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
|
||||
List<String> values = entry.getValue();
|
||||
String key = entry.getKey().toLowerCase();
|
||||
for (String value : values) {
|
||||
hpackOut.header(key, value, insertionPolicy);
|
||||
do {
|
||||
encoded = hpackOut.encode(buf);
|
||||
if (!encoded && !buf.hasRemaining()) {
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
buf = getBuffer();
|
||||
}
|
||||
} while (!encoded);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
headersLock.unlock();
|
||||
}
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
@ -970,18 +996,23 @@ public class Http2TestServerConnection {
|
||||
|
||||
ByteBuffer buf = getBuffer();
|
||||
boolean encoded;
|
||||
for (Map.Entry<String, String> entry : headers) {
|
||||
String value = entry.getValue();
|
||||
String key = entry.getKey().toLowerCase();
|
||||
do {
|
||||
headersLock.lock();
|
||||
try {
|
||||
for (Map.Entry<String, String> entry : headers) {
|
||||
String value = entry.getValue();
|
||||
String key = entry.getKey().toLowerCase();
|
||||
hpackOut.header(key, value);
|
||||
encoded = hpackOut.encode(buf);
|
||||
if (!encoded) {
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
buf = getBuffer();
|
||||
}
|
||||
} while (!encoded);
|
||||
do {
|
||||
encoded = hpackOut.encode(buf);
|
||||
if (!encoded && !buf.hasRemaining()) {
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
buf = getBuffer();
|
||||
}
|
||||
} while (!encoded);
|
||||
}
|
||||
} finally {
|
||||
headersLock.unlock();
|
||||
}
|
||||
buf.flip();
|
||||
buffers.add(buf);
|
||||
@ -1008,10 +1039,50 @@ public class Http2TestServerConnection {
|
||||
break;
|
||||
} else throw x;
|
||||
}
|
||||
if (frame instanceof ResponseHeaders) {
|
||||
ResponseHeaders rh = (ResponseHeaders)frame;
|
||||
HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers));
|
||||
writeFrame(hf);
|
||||
if (frame instanceof ResponseHeaders rh) {
|
||||
var buffers = encodeHeaders(rh.headers, rh.insertionPolicy);
|
||||
int maxFrameSize = Math.min(rh.getMaxFrameSize(), getMaxFrameSize() - 64);
|
||||
int next = 0;
|
||||
int cont = 0;
|
||||
do {
|
||||
// If the total size of headers exceeds the max frame
|
||||
// size we need to split the headers into one
|
||||
// HeadersFrame + N x ContinuationFrames
|
||||
int remaining = maxFrameSize;
|
||||
var list = new ArrayList<ByteBuffer>(buffers.size());
|
||||
for (; next < buffers.size(); next++) {
|
||||
var b = buffers.get(next);
|
||||
var len = b.remaining();
|
||||
if (!b.hasRemaining()) continue;
|
||||
if (len <= remaining) {
|
||||
remaining -= len;
|
||||
list.add(b);
|
||||
} else {
|
||||
if (next == 0) {
|
||||
list.add(b.slice(b.position(), remaining));
|
||||
b.position(b.position() + remaining);
|
||||
remaining = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
int flags = rh.getFlags();
|
||||
if (next != buffers.size()) {
|
||||
flags = flags & ~HeadersFrame.END_HEADERS;
|
||||
}
|
||||
if (cont > 0) {
|
||||
flags = flags & ~HeadersFrame.END_STREAM;
|
||||
}
|
||||
HeaderFrame hf = cont == 0
|
||||
? new HeadersFrame(rh.streamid(), flags, list)
|
||||
: new ContinuationFrame(rh.streamid(), flags, list);
|
||||
if (Log.headers()) {
|
||||
// avoid too much chatter: log only if Log.headers() is enabled
|
||||
System.err.println("TestServer writing " + hf);
|
||||
}
|
||||
writeFrame(hf);
|
||||
cont++;
|
||||
} while (next < buffers.size());
|
||||
} else if (frame instanceof OutgoingPushPromise) {
|
||||
handlePush((OutgoingPushPromise)frame);
|
||||
} else
|
||||
@ -1322,11 +1393,29 @@ public class Http2TestServerConnection {
|
||||
// for the hashmap.
|
||||
|
||||
public static class ResponseHeaders extends Http2Frame {
|
||||
HttpHeaders headers;
|
||||
final HttpHeaders headers;
|
||||
final BiPredicate<CharSequence, CharSequence> insertionPolicy;
|
||||
|
||||
final int maxFrameSize;
|
||||
|
||||
public ResponseHeaders(HttpHeaders headers) {
|
||||
this(headers, (n,v) -> false);
|
||||
}
|
||||
public ResponseHeaders(HttpHeaders headers, BiPredicate<CharSequence, CharSequence> insertionPolicy) {
|
||||
this(headers, insertionPolicy, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public ResponseHeaders(HttpHeaders headers,
|
||||
BiPredicate<CharSequence, CharSequence> insertionPolicy,
|
||||
int maxFrameSize) {
|
||||
super(0, 0);
|
||||
this.headers = headers;
|
||||
this.insertionPolicy = insertionPolicy;
|
||||
this.maxFrameSize = maxFrameSize;
|
||||
}
|
||||
|
||||
public int getMaxFrameSize() {
|
||||
return maxFrameSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 8331446
|
||||
* @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the
|
||||
* ArgumentIndex element in the MessageFormat pattern syntax. This
|
||||
* should be checked during construction/applyPattern/readObject and should effectively
|
||||
* prevent parse/format from being invoked with values over the limit.
|
||||
* @run junit MaxArgumentIndexTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class MaxArgumentIndexTest {
|
||||
|
||||
// A MessageFormat pattern that contains an ArgumentIndex value
|
||||
// which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000)
|
||||
// As this check is exclusive, 10,000 will violate the limit
|
||||
private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}";
|
||||
|
||||
// Check String constructor enforces the limit
|
||||
@Test
|
||||
public void constructorTest() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX));
|
||||
}
|
||||
|
||||
// Check String, Locale constructor enforces the limit
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void constructorWithLocaleTest(Locale locale) {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale));
|
||||
}
|
||||
|
||||
// Provide some basic common locale values
|
||||
private static Stream<Locale> constructorWithLocaleTest() {
|
||||
return Stream.of(null, Locale.US, Locale.ROOT);
|
||||
}
|
||||
|
||||
// Edge case: Test a locale dependent subformat (with null locale) with a
|
||||
// violating ArgumentIndex. In this instance, the violating ArgumentIndex
|
||||
// will be caught and IAE thrown instead of the NPE
|
||||
@Test
|
||||
public void localeDependentSubFormatTest() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new MessageFormat("{10000,number,short}", null));
|
||||
// For reference
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> new MessageFormat("{999,number,short}", null));
|
||||
}
|
||||
|
||||
// Check that the static format method enforces the limit
|
||||
@Test
|
||||
public void staticFormatTest() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1}));
|
||||
}
|
||||
|
||||
// Check that applyPattern(String) enforces the limit
|
||||
@Test
|
||||
public void applyPatternTest() {
|
||||
MessageFormat mf = new MessageFormat("");
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX));
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 8331446
|
||||
* @summary Check correctness of deserialization
|
||||
* @run junit SerializationTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class SerializationTest {
|
||||
|
||||
// Ensure basic correctness of serialization round trip
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void serializationRoundTrip(MessageFormat expectedMf)
|
||||
throws IOException, ClassNotFoundException {
|
||||
byte[] bytes = ser(expectedMf);
|
||||
MessageFormat actualMf = (MessageFormat) deSer(bytes);
|
||||
assertEquals(expectedMf, actualMf);
|
||||
}
|
||||
|
||||
// Various valid MessageFormats
|
||||
private static Stream<MessageFormat> serializationRoundTrip() {
|
||||
return Stream.of(
|
||||
// basic pattern
|
||||
new MessageFormat("{0} foo"),
|
||||
// Multiple arguments
|
||||
new MessageFormat("{0} {1} foo"),
|
||||
// duplicate arguments
|
||||
new MessageFormat("{0} {0} {1} foo"),
|
||||
// Non-ascending arguments
|
||||
new MessageFormat("{1} {0} foo"),
|
||||
// With locale
|
||||
new MessageFormat("{1} {0} foo", Locale.UK),
|
||||
// With null locale. (NPE not thrown, if no format defined)
|
||||
new MessageFormat("{1} {0} foo", null),
|
||||
// With formats
|
||||
new MessageFormat("{0,number,short} {0} {1,date,long} foo")
|
||||
);
|
||||
}
|
||||
|
||||
// Utility method to serialize
|
||||
private static byte[] ser(Object obj) throws IOException {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new
|
||||
ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new
|
||||
ObjectOutputStream(byteArrayOutputStream);
|
||||
oos.writeObject(obj);
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
// Utility method to deserialize
|
||||
private static Object deSer(byte[] bytes) throws
|
||||
IOException, ClassNotFoundException {
|
||||
ByteArrayInputStream byteArrayInputStream = new
|
||||
ByteArrayInputStream(bytes);
|
||||
ObjectInputStream ois = new
|
||||
ObjectInputStream(byteArrayInputStream);
|
||||
return ois.readObject();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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,7 +26,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8043758
|
||||
* @bug 8043758 8307383
|
||||
* @summary Datagram Transport Layer Security (DTLS)
|
||||
* @modules java.base/sun.security.util
|
||||
* @library /test/lib
|
||||
@ -36,6 +36,7 @@
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@ -73,11 +74,34 @@ public class InvalidRecords extends DTLSOverDatagram {
|
||||
// ClientHello with cookie
|
||||
needInvalidRecords.set(false);
|
||||
System.out.println("invalidate ClientHello message");
|
||||
if (ba[ba.length - 1] == (byte)0xFF) {
|
||||
ba[ba.length - 1] = (byte)0xFE;
|
||||
// We will alter the compression method field in order to make the cookie
|
||||
// check fail.
|
||||
ByteBuffer chRec = ByteBuffer.wrap(ba);
|
||||
// Skip 59 bytes past the record header (13), the handshake header (12),
|
||||
// the protocol version (2), and client random (32)
|
||||
chRec.position(59);
|
||||
// Jump past the session ID
|
||||
int len = Byte.toUnsignedInt(chRec.get());
|
||||
chRec.position(chRec.position() + len);
|
||||
// Skip the cookie
|
||||
len = Byte.toUnsignedInt(chRec.get());
|
||||
chRec.position(chRec.position() + len);
|
||||
// Skip past cipher suites
|
||||
len = Short.toUnsignedInt(chRec.getShort());
|
||||
chRec.position(chRec.position() + len);
|
||||
// Read the data on the compression methods, should be at least 1
|
||||
len = Byte.toUnsignedInt(chRec.get());
|
||||
if (len >= 1) {
|
||||
System.out.println("Detected compression methods (count = " + len + ")");
|
||||
} else {
|
||||
ba[ba.length - 1] = (byte)0xFF;
|
||||
throw new RuntimeException("Got zero length comp methods");
|
||||
}
|
||||
// alter the first comp method.
|
||||
int compMethodVal = Byte.toUnsignedInt(chRec.get(chRec.position()));
|
||||
System.out.println("Changing value at position " + chRec.position() +
|
||||
" from " + compMethodVal + " to " + ++compMethodVal);
|
||||
chRec.put(chRec.position(), (byte)compMethodVal);
|
||||
}
|
||||
|
||||
return super.createHandshakePacket(ba, socketAddr);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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,7 +34,15 @@ public class MFLNTest extends SSLEngineTestCase {
|
||||
public static void main(String[] args) {
|
||||
setUpAndStartKDCIfNeeded();
|
||||
System.setProperty("jsse.enableMFLNExtension", "true");
|
||||
for (int mfl = 4096; mfl >= 256; mfl /= 2) {
|
||||
String testMode = System.getProperty("test.mode", "norm");
|
||||
int mflLen;
|
||||
if (testMode.equals("norm_sni")) {
|
||||
mflLen = 512;
|
||||
} else {
|
||||
mflLen = 256;
|
||||
}
|
||||
|
||||
for (int mfl = 4096; mfl >= mflLen; mfl /= 2) {
|
||||
System.out.println("=============================================="
|
||||
+ "==============");
|
||||
System.out.printf("Testsing DTLS handshake with MFL = %d%n", mfl);
|
||||
|
Loading…
x
Reference in New Issue
Block a user