8229867: Re-examine synchronization usages in http and https protocol handlers
Reviewed-by: chegar, alanb, michaelm
This commit is contained in:
parent
6fe209b564
commit
65393a093c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2020, 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
|
||||
@ -294,7 +294,7 @@ class MessageHeader {
|
||||
* @param line the line to check.
|
||||
* @return true if the line might be a request line.
|
||||
*/
|
||||
private boolean isRequestline(String line) {
|
||||
private static boolean isRequestline(String line) {
|
||||
String k = line.trim();
|
||||
int i = k.lastIndexOf(' ');
|
||||
if (i <= 0) return false;
|
||||
@ -311,12 +311,23 @@ class MessageHeader {
|
||||
return (k.substring(i+1, len-3).equalsIgnoreCase("HTTP/"));
|
||||
}
|
||||
|
||||
/** Prints the key-value pairs represented by this
|
||||
header. Also prints the RFC required blank line
|
||||
at the end. Omits pairs with a null key. Omits
|
||||
colon if key-value pair is the requestline. */
|
||||
public void print(PrintStream p) {
|
||||
// no synchronization: use cloned arrays instead.
|
||||
String[] k; String[] v; int n;
|
||||
synchronized (this) { n = nkeys; k = keys.clone(); v = values.clone(); }
|
||||
print(n, k, v, p);
|
||||
}
|
||||
|
||||
|
||||
/** Prints the key-value pairs represented by this
|
||||
header. Also prints the RFC required blank line
|
||||
at the end. Omits pairs with a null key. Omits
|
||||
colon if key-value pair is the requestline. */
|
||||
public synchronized void print(PrintStream p) {
|
||||
private static void print(int nkeys, String[] keys, String[] values, PrintStream p) {
|
||||
for (int i = 0; i < nkeys; i++)
|
||||
if (keys[i] != null) {
|
||||
StringBuilder sb = new StringBuilder(keys[i]);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2020, 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,9 +25,8 @@
|
||||
|
||||
package sun.net.www;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import sun.net.ProgressSource;
|
||||
import sun.net.www.http.ChunkedInputStream;
|
||||
|
||||
@ -44,6 +43,7 @@ public class MeteredStream extends FilterInputStream {
|
||||
protected long markedCount = 0;
|
||||
protected int markLimit = -1;
|
||||
protected ProgressSource pi;
|
||||
private final ReentrantLock readLock = new ReentrantLock();
|
||||
|
||||
public MeteredStream(InputStream is, ProgressSource pi, long expected)
|
||||
{
|
||||
@ -57,7 +57,9 @@ public class MeteredStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
private final void justRead(long n) throws IOException {
|
||||
private final void justRead(long n) throws IOException {
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
if (n == -1) {
|
||||
|
||||
/*
|
||||
@ -99,6 +101,7 @@ public class MeteredStream extends FilterInputStream {
|
||||
* Returns true if the mark is valid, false otherwise
|
||||
*/
|
||||
private boolean isMarked() {
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
if (markLimit < 0) {
|
||||
return false;
|
||||
@ -113,94 +116,130 @@ public class MeteredStream extends FilterInputStream {
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized int read() throws java.io.IOException {
|
||||
if (closed) {
|
||||
return -1;
|
||||
public int read() throws java.io.IOException {
|
||||
lock();
|
||||
try {
|
||||
if (closed) return -1;
|
||||
int c = in.read();
|
||||
if (c != -1) {
|
||||
justRead(1);
|
||||
} else {
|
||||
justRead(c);
|
||||
}
|
||||
return c;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
int c = in.read();
|
||||
if (c != -1) {
|
||||
justRead(1);
|
||||
} else {
|
||||
justRead(c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public synchronized int read(byte b[], int off, int len)
|
||||
public int read(byte b[], int off, int len)
|
||||
throws java.io.IOException {
|
||||
if (closed) {
|
||||
return -1;
|
||||
lock();
|
||||
try {
|
||||
if (closed) return -1;
|
||||
|
||||
int n = in.read(b, off, len);
|
||||
justRead(n);
|
||||
return n;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
int n = in.read(b, off, len);
|
||||
justRead(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
public synchronized long skip(long n) throws IOException {
|
||||
public long skip(long n) throws IOException {
|
||||
lock();
|
||||
try {
|
||||
// REMIND: what does skip do on EOF????
|
||||
if (closed) return 0;
|
||||
|
||||
// REMIND: what does skip do on EOF????
|
||||
if (closed) {
|
||||
return 0;
|
||||
if (in instanceof ChunkedInputStream) {
|
||||
n = in.skip(n);
|
||||
} else {
|
||||
// just skip min(n, num_bytes_left)
|
||||
long min = (n > expected - count) ? expected - count : n;
|
||||
n = in.skip(min);
|
||||
}
|
||||
justRead(n);
|
||||
return n;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
|
||||
if (in instanceof ChunkedInputStream) {
|
||||
n = in.skip(n);
|
||||
}
|
||||
else {
|
||||
// just skip min(n, num_bytes_left)
|
||||
long min = (n > expected - count) ? expected - count: n;
|
||||
n = in.skip(min);
|
||||
}
|
||||
justRead(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
if (pi != null)
|
||||
pi.finishTracking();
|
||||
lock();
|
||||
try {
|
||||
if (closed) return;
|
||||
if (pi != null)
|
||||
pi.finishTracking();
|
||||
|
||||
closed = true;
|
||||
in.close();
|
||||
closed = true;
|
||||
in.close();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int available() throws IOException {
|
||||
return closed ? 0: in.available();
|
||||
public int available() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
return closed ? 0 : in.available();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void mark(int readLimit) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
super.mark(readLimit);
|
||||
public void mark(int readLimit) {
|
||||
lock();
|
||||
try {
|
||||
if (closed) return;
|
||||
super.mark(readLimit);
|
||||
|
||||
/*
|
||||
* mark the count to restore upon reset
|
||||
*/
|
||||
markedCount = count;
|
||||
markLimit = readLimit;
|
||||
/*
|
||||
* mark the count to restore upon reset
|
||||
*/
|
||||
markedCount = count;
|
||||
markLimit = readLimit;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void reset() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
public void reset() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
if (closed) return;
|
||||
if (!isMarked()) {
|
||||
throw new IOException("Resetting to an invalid mark");
|
||||
}
|
||||
|
||||
if (!isMarked()) {
|
||||
throw new IOException ("Resetting to an invalid mark");
|
||||
count = markedCount;
|
||||
super.reset();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
|
||||
count = markedCount;
|
||||
super.reset();
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
if (closed) {
|
||||
return false;
|
||||
lock();
|
||||
try {
|
||||
if (closed) return false;
|
||||
return super.markSupported();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
return super.markSupported();
|
||||
}
|
||||
|
||||
public final void lock() {
|
||||
readLock.lock();
|
||||
}
|
||||
|
||||
public final void unlock() {
|
||||
readLock.unlock();
|
||||
}
|
||||
|
||||
public final boolean isLockHeldByCurrentThread() {
|
||||
return readLock.isHeldByCurrentThread();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2020, 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,9 +25,7 @@
|
||||
package sun.net.www.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import sun.net.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import sun.net.www.*;
|
||||
import sun.nio.cs.US_ASCII;
|
||||
|
||||
@ -41,8 +39,7 @@ import sun.nio.cs.US_ASCII;
|
||||
* can be hurried to the end of the stream if the bytes are available on
|
||||
* the underlying stream.
|
||||
*/
|
||||
public
|
||||
class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
public class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
|
||||
/**
|
||||
* The underlying stream
|
||||
@ -126,6 +123,8 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
*/
|
||||
private boolean closed;
|
||||
|
||||
private final ReentrantLock readLock = new ReentrantLock();
|
||||
|
||||
/*
|
||||
* Maximum chunk header size of 2KB + 2 bytes for CRLF
|
||||
*/
|
||||
@ -648,14 +647,19 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
* @exception IOException if an I/O error occurs.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public synchronized int read() throws IOException {
|
||||
ensureOpen();
|
||||
if (chunkPos >= chunkCount) {
|
||||
if (readAhead(true) <= 0) {
|
||||
return -1;
|
||||
public int read() throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (chunkPos >= chunkCount) {
|
||||
if (readAhead(true) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return chunkData[chunkPos++] & 0xff;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return chunkData[chunkPos++] & 0xff;
|
||||
}
|
||||
|
||||
|
||||
@ -670,42 +674,47 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
* the stream has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public synchronized int read(byte b[], int off, int len)
|
||||
public int read(byte b[], int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avail = chunkCount - chunkPos;
|
||||
if (avail <= 0) {
|
||||
/*
|
||||
* Optimization: if we're in the middle of the chunk read
|
||||
* directly from the underlying stream into the caller's
|
||||
* buffer
|
||||
*/
|
||||
if (state == STATE_READING_CHUNK) {
|
||||
return fastRead( b, off, len );
|
||||
readLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're not in the middle of a chunk so we must read ahead
|
||||
* until there is some chunk data available.
|
||||
*/
|
||||
avail = readAhead(true);
|
||||
if (avail < 0) {
|
||||
return -1; /* EOF */
|
||||
}
|
||||
}
|
||||
int cnt = (avail < len) ? avail : len;
|
||||
System.arraycopy(chunkData, chunkPos, b, off, cnt);
|
||||
chunkPos += cnt;
|
||||
int avail = chunkCount - chunkPos;
|
||||
if (avail <= 0) {
|
||||
/*
|
||||
* Optimization: if we're in the middle of the chunk read
|
||||
* directly from the underlying stream into the caller's
|
||||
* buffer
|
||||
*/
|
||||
if (state == STATE_READING_CHUNK) {
|
||||
return fastRead(b, off, len);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
/*
|
||||
* We're not in the middle of a chunk so we must read ahead
|
||||
* until there is some chunk data available.
|
||||
*/
|
||||
avail = readAhead(true);
|
||||
if (avail < 0) {
|
||||
return -1; /* EOF */
|
||||
}
|
||||
}
|
||||
int cnt = (avail < len) ? avail : len;
|
||||
System.arraycopy(chunkData, chunkPos, b, off, cnt);
|
||||
chunkPos += cnt;
|
||||
|
||||
return cnt;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -717,20 +726,25 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
* @exception IOException if an I/O error occurs.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public synchronized int available() throws IOException {
|
||||
ensureOpen();
|
||||
public int available() throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
int avail = chunkCount - chunkPos;
|
||||
if(avail > 0) {
|
||||
return avail;
|
||||
}
|
||||
int avail = chunkCount - chunkPos;
|
||||
if (avail > 0) {
|
||||
return avail;
|
||||
}
|
||||
|
||||
avail = readAhead(false);
|
||||
avail = readAhead(false);
|
||||
|
||||
if (avail < 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return avail;
|
||||
if (avail < 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return avail;
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -745,12 +759,18 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
*
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
public void close() throws IOException {
|
||||
if (closed) return;
|
||||
readLock.lock();
|
||||
try {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closeUnderlying();
|
||||
closed = true;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
closeUnderlying();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -762,22 +782,27 @@ class ChunkedInputStream extends InputStream implements Hurryable {
|
||||
* without blocking then this stream can't be hurried and should be
|
||||
* closed.
|
||||
*/
|
||||
public synchronized boolean hurry() {
|
||||
if (in == null || error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hurry() {
|
||||
readLock.lock();
|
||||
try {
|
||||
readAhead(false);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
if (in == null || error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
readAhead(false);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (state == STATE_DONE);
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (state == STATE_DONE);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2020, 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,6 +25,8 @@
|
||||
package sun.net.www.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.nio.cs.US_ASCII;
|
||||
|
||||
@ -32,7 +34,7 @@ import sun.nio.cs.US_ASCII;
|
||||
* OutputStream that sends the output to the underlying stream using chunked
|
||||
* encoding as specified in RFC 2068.
|
||||
*/
|
||||
public class ChunkedOutputStream extends PrintStream {
|
||||
public class ChunkedOutputStream extends OutputStream {
|
||||
|
||||
/* Default chunk size (including chunk header) if not specified */
|
||||
static final int DEFAULT_CHUNK_SIZE = 4096;
|
||||
@ -63,6 +65,8 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
/* header for a complete Chunk */
|
||||
private byte[] completeHeader;
|
||||
|
||||
private final Lock writeLock = new ReentrantLock();
|
||||
|
||||
/* return the size of the header for a particular chunk size */
|
||||
private static int getHeaderSize(int size) {
|
||||
return (Integer.toHexString(size)).length() + CRLF_SIZE;
|
||||
@ -85,7 +89,6 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
}
|
||||
|
||||
public ChunkedOutputStream(PrintStream o, int size) {
|
||||
super(o);
|
||||
out = o;
|
||||
|
||||
if (size <= 0) {
|
||||
@ -144,7 +147,7 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
reset();
|
||||
} else if (flushAll){
|
||||
/* complete the last chunk and flush it to underlying stream */
|
||||
if (size > 0){
|
||||
if (size > 0) {
|
||||
/* adjust a header start index in case the header of the last
|
||||
* chunk is shorter then preferedHeaderSize */
|
||||
|
||||
@ -168,18 +171,18 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
|
||||
out.flush();
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkError() {
|
||||
return out.checkError();
|
||||
var out = this.out;
|
||||
return out == null || out.checkError();
|
||||
}
|
||||
|
||||
/* Check that the output stream is still open */
|
||||
private void ensureOpen() {
|
||||
private void ensureOpen() throws IOException {
|
||||
if (out == null)
|
||||
setError();
|
||||
throw new IOException("closed");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -194,77 +197,92 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
* The size of the data is of course smaller than preferredChunkSize.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(byte b[], int off, int len) {
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
}
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* if b[] contains enough data then one loop cycle creates one complete
|
||||
* data chunk with a header, body and a footer, and then flushes the
|
||||
* chunk to the underlying stream. Otherwise, the last loop cycle
|
||||
* creates incomplete data chunk with empty header and with no footer
|
||||
* and stores this incomplete chunk in an internal buffer buf[]
|
||||
*/
|
||||
int bytesToWrite = len;
|
||||
int inputIndex = off; /* the index of the byte[] currently being written */
|
||||
/* if b[] contains enough data then one loop cycle creates one complete
|
||||
* data chunk with a header, body and a footer, and then flushes the
|
||||
* chunk to the underlying stream. Otherwise, the last loop cycle
|
||||
* creates incomplete data chunk with empty header and with no footer
|
||||
* and stores this incomplete chunk in an internal buffer buf[]
|
||||
*/
|
||||
int bytesToWrite = len;
|
||||
int inputIndex = off; /* the index of the byte[] currently being written */
|
||||
|
||||
do {
|
||||
/* enough data to complete a chunk */
|
||||
if (bytesToWrite >= spaceInCurrentChunk) {
|
||||
do {
|
||||
/* enough data to complete a chunk */
|
||||
if (bytesToWrite >= spaceInCurrentChunk) {
|
||||
|
||||
/* header */
|
||||
for (int i=0; i<completeHeader.length; i++)
|
||||
buf[i] = completeHeader[i];
|
||||
/* header */
|
||||
for (int i = 0; i < completeHeader.length; i++)
|
||||
buf[i] = completeHeader[i];
|
||||
|
||||
/* data */
|
||||
System.arraycopy(b, inputIndex, buf, count, spaceInCurrentChunk);
|
||||
inputIndex += spaceInCurrentChunk;
|
||||
bytesToWrite -= spaceInCurrentChunk;
|
||||
count += spaceInCurrentChunk;
|
||||
/* data */
|
||||
System.arraycopy(b, inputIndex, buf, count, spaceInCurrentChunk);
|
||||
inputIndex += spaceInCurrentChunk;
|
||||
bytesToWrite -= spaceInCurrentChunk;
|
||||
count += spaceInCurrentChunk;
|
||||
|
||||
/* footer */
|
||||
buf[count++] = FOOTER[0];
|
||||
buf[count++] = FOOTER[1];
|
||||
spaceInCurrentChunk = 0; //chunk is complete
|
||||
/* footer */
|
||||
buf[count++] = FOOTER[0];
|
||||
buf[count++] = FOOTER[1];
|
||||
spaceInCurrentChunk = 0; //chunk is complete
|
||||
|
||||
flush(false);
|
||||
if (checkError()){
|
||||
break;
|
||||
flush(false);
|
||||
if (checkError()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* not enough data to build a chunk */
|
||||
else {
|
||||
/* header */
|
||||
/* do not write header if not enough bytes to build a chunk yet */
|
||||
/* not enough data to build a chunk */
|
||||
else {
|
||||
/* header */
|
||||
/* do not write header if not enough bytes to build a chunk yet */
|
||||
|
||||
/* data */
|
||||
System.arraycopy(b, inputIndex, buf, count, bytesToWrite);
|
||||
count += bytesToWrite;
|
||||
size += bytesToWrite;
|
||||
spaceInCurrentChunk -= bytesToWrite;
|
||||
bytesToWrite = 0;
|
||||
/* data */
|
||||
System.arraycopy(b, inputIndex, buf, count, bytesToWrite);
|
||||
count += bytesToWrite;
|
||||
size += bytesToWrite;
|
||||
spaceInCurrentChunk -= bytesToWrite;
|
||||
bytesToWrite = 0;
|
||||
|
||||
/* footer */
|
||||
/* do not write header if not enough bytes to build a chunk yet */
|
||||
}
|
||||
} while (bytesToWrite > 0);
|
||||
/* footer */
|
||||
/* do not write header if not enough bytes to build a chunk yet */
|
||||
}
|
||||
} while (bytesToWrite > 0);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int _b) {
|
||||
byte b[] = {(byte)_b};
|
||||
write(b, 0, 1);
|
||||
public void write(int _b) throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
byte b[] = {(byte) _b};
|
||||
write(b, 0, 1);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void reset() {
|
||||
count = preferedHeaderSize;
|
||||
size = 0;
|
||||
spaceInCurrentChunk = preferredChunkDataSize;
|
||||
public void reset() {
|
||||
writeLock.lock();
|
||||
try {
|
||||
count = preferedHeaderSize;
|
||||
size = 0;
|
||||
spaceInCurrentChunk = preferredChunkDataSize;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@ -272,26 +290,36 @@ public class ChunkedOutputStream extends PrintStream {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
ensureOpen();
|
||||
public void close() {
|
||||
writeLock.lock();
|
||||
try {
|
||||
if (out == null) return;
|
||||
|
||||
/* if we have buffer a chunked send it */
|
||||
if (size > 0) {
|
||||
/* if we have buffer a chunked send it */
|
||||
if (size > 0) {
|
||||
flush(true);
|
||||
}
|
||||
|
||||
/* send a zero length chunk */
|
||||
flush(true);
|
||||
|
||||
/* don't close the underlying stream */
|
||||
out = null;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
||||
/* send a zero length chunk */
|
||||
flush(true);
|
||||
|
||||
/* don't close the underlying stream */
|
||||
out = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
ensureOpen();
|
||||
if (size > 0) {
|
||||
flush(true);
|
||||
public void flush() throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (size > 0) {
|
||||
flush(true);
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ import sun.util.logging.PlatformLogger;
|
||||
* @author jccollet
|
||||
*/
|
||||
public class HttpCapture {
|
||||
// HttpCapture does blocking I/O operations while holding monitors.
|
||||
// This is not a concern because it is rarely used.
|
||||
private File file;
|
||||
private boolean incoming = true;
|
||||
private BufferedWriter out;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,6 +30,8 @@ import java.net.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.net.NetworkClient;
|
||||
import sun.net.ProgressSource;
|
||||
import sun.net.www.MessageHeader;
|
||||
@ -47,6 +49,8 @@ import sun.security.action.GetPropertyAction;
|
||||
* @author Dave Brown
|
||||
*/
|
||||
public class HttpClient extends NetworkClient {
|
||||
private final ReentrantLock clientLock = new ReentrantLock();
|
||||
|
||||
// whether this httpclient comes from the cache
|
||||
protected boolean cachedHttpClient = false;
|
||||
|
||||
@ -316,22 +320,28 @@ public class HttpClient extends NetworkClient {
|
||||
boolean compatible = Objects.equals(ret.proxy, p)
|
||||
&& Objects.equals(ret.getAuthenticatorKey(), ak);
|
||||
if (compatible) {
|
||||
synchronized (ret) {
|
||||
ret.lock();
|
||||
try {
|
||||
ret.cachedHttpClient = true;
|
||||
assert ret.inCache;
|
||||
ret.inCache = false;
|
||||
if (httpuc != null && ret.needsTunneling())
|
||||
httpuc.setTunnelState(TUNNELING);
|
||||
logFinest("KeepAlive stream retrieved from the cache, " + ret);
|
||||
} finally {
|
||||
ret.unlock();
|
||||
}
|
||||
} else {
|
||||
// We cannot return this connection to the cache as it's
|
||||
// KeepAliveTimeout will get reset. We simply close the connection.
|
||||
// This should be fine as it is very rare that a connection
|
||||
// to the same host will not use the same proxy.
|
||||
synchronized(ret) {
|
||||
ret.lock();
|
||||
try {
|
||||
ret.inCache = false;
|
||||
ret.closeServer();
|
||||
} finally {
|
||||
ret.unlock();
|
||||
}
|
||||
ret = null;
|
||||
}
|
||||
@ -409,10 +419,11 @@ public class HttpClient extends NetworkClient {
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized boolean available() {
|
||||
protected boolean available() {
|
||||
boolean available = true;
|
||||
int old = -1;
|
||||
|
||||
lock();
|
||||
try {
|
||||
try {
|
||||
old = serverSocket.getSoTimeout();
|
||||
@ -436,21 +447,33 @@ public class HttpClient extends NetworkClient {
|
||||
logFinest("HttpClient.available(): " +
|
||||
"SocketException: not available");
|
||||
available = false;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
protected synchronized void putInKeepAliveCache() {
|
||||
if (inCache) {
|
||||
assert false : "Duplicate put to keep alive cache";
|
||||
return;
|
||||
protected void putInKeepAliveCache() {
|
||||
lock();
|
||||
try {
|
||||
if (inCache) {
|
||||
assert false : "Duplicate put to keep alive cache";
|
||||
return;
|
||||
}
|
||||
inCache = true;
|
||||
kac.put(url, null, this);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
inCache = true;
|
||||
kac.put(url, null, this);
|
||||
}
|
||||
|
||||
protected synchronized boolean isInKeepAliveCache() {
|
||||
return inCache;
|
||||
protected boolean isInKeepAliveCache() {
|
||||
lock();
|
||||
try {
|
||||
return inCache;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -497,8 +520,13 @@ public class HttpClient extends NetworkClient {
|
||||
/*
|
||||
* Returns true if this httpclient is from cache
|
||||
*/
|
||||
public synchronized boolean isCachedConnection() {
|
||||
return cachedHttpClient;
|
||||
public boolean isCachedConnection() {
|
||||
lock();
|
||||
try {
|
||||
return cachedHttpClient;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -516,9 +544,10 @@ public class HttpClient extends NetworkClient {
|
||||
/*
|
||||
* call openServer in a privileged block
|
||||
*/
|
||||
private synchronized void privilegedOpenServer(final InetSocketAddress server)
|
||||
private void privilegedOpenServer(final InetSocketAddress server)
|
||||
throws IOException
|
||||
{
|
||||
assert clientLock.isHeldByCurrentThread();
|
||||
try {
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedExceptionAction<>() {
|
||||
@ -544,48 +573,53 @@ public class HttpClient extends NetworkClient {
|
||||
|
||||
/*
|
||||
*/
|
||||
protected synchronized void openServer() throws IOException {
|
||||
protected void openServer() throws IOException {
|
||||
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
|
||||
if (security != null) {
|
||||
security.checkConnect(host, port);
|
||||
}
|
||||
lock();
|
||||
try {
|
||||
if (security != null) {
|
||||
security.checkConnect(host, port);
|
||||
}
|
||||
|
||||
if (keepingAlive) { // already opened
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.getProtocol().equals("http") ||
|
||||
url.getProtocol().equals("https") ) {
|
||||
|
||||
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
|
||||
sun.net.www.URLConnection.setProxiedHost(host);
|
||||
privilegedOpenServer((InetSocketAddress) proxy.address());
|
||||
usingProxy = true;
|
||||
return;
|
||||
} else {
|
||||
// make direct connection
|
||||
openServer(host, port);
|
||||
usingProxy = false;
|
||||
if (keepingAlive) { // already opened
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* we're opening some other kind of url, most likely an
|
||||
* ftp url.
|
||||
*/
|
||||
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
|
||||
sun.net.www.URLConnection.setProxiedHost(host);
|
||||
privilegedOpenServer((InetSocketAddress) proxy.address());
|
||||
usingProxy = true;
|
||||
return;
|
||||
if (url.getProtocol().equals("http") ||
|
||||
url.getProtocol().equals("https")) {
|
||||
|
||||
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
|
||||
sun.net.www.URLConnection.setProxiedHost(host);
|
||||
privilegedOpenServer((InetSocketAddress) proxy.address());
|
||||
usingProxy = true;
|
||||
return;
|
||||
} else {
|
||||
// make direct connection
|
||||
openServer(host, port);
|
||||
usingProxy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// make direct connection
|
||||
super.openServer(host, port);
|
||||
usingProxy = false;
|
||||
return;
|
||||
/* we're opening some other kind of url, most likely an
|
||||
* ftp url.
|
||||
*/
|
||||
if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
|
||||
sun.net.www.URLConnection.setProxiedHost(host);
|
||||
privilegedOpenServer((InetSocketAddress) proxy.address());
|
||||
usingProxy = true;
|
||||
return;
|
||||
} else {
|
||||
// make direct connection
|
||||
super.openServer(host, port);
|
||||
usingProxy = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1010,8 +1044,13 @@ public class HttpClient extends NetworkClient {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized InputStream getInputStream() {
|
||||
return serverInput;
|
||||
public InputStream getInputStream() {
|
||||
lock();
|
||||
try {
|
||||
return serverInput;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
@ -1084,4 +1123,12 @@ public class HttpClient extends NetworkClient {
|
||||
return ((InetSocketAddress)proxy.address()).getPort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
public final void lock() {
|
||||
clientLock.lock();
|
||||
}
|
||||
|
||||
public final void unlock() {
|
||||
clientLock.unlock();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -36,6 +36,8 @@ import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import jdk.internal.misc.InnocuousThread;
|
||||
import sun.security.action.GetIntegerAction;
|
||||
@ -74,6 +76,8 @@ public class KeepAliveCache
|
||||
|
||||
static final int LIFETIME = 5000;
|
||||
|
||||
// This class is never serialized (see writeObject/readObject).
|
||||
private final ReentrantLock cacheLock = new ReentrantLock();
|
||||
private Thread keepAliveTimer = null;
|
||||
|
||||
/**
|
||||
@ -86,76 +90,92 @@ public class KeepAliveCache
|
||||
* @param url The URL contains info about the host and port
|
||||
* @param http The HttpClient to be cached
|
||||
*/
|
||||
public synchronized void put(final URL url, Object obj, HttpClient http) {
|
||||
boolean startThread = (keepAliveTimer == null);
|
||||
if (!startThread) {
|
||||
if (!keepAliveTimer.isAlive()) {
|
||||
startThread = true;
|
||||
}
|
||||
}
|
||||
if (startThread) {
|
||||
clear();
|
||||
/* Unfortunately, we can't always believe the keep-alive timeout we got
|
||||
* back from the server. If I'm connected through a Netscape proxy
|
||||
* to a server that sent me a keep-alive
|
||||
* time of 15 sec, the proxy unilaterally terminates my connection
|
||||
* The robustness to get around this is in HttpClient.parseHTTP()
|
||||
*/
|
||||
final KeepAliveCache cache = this;
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public Void run() {
|
||||
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
|
||||
keepAliveTimer.setDaemon(true);
|
||||
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
keepAliveTimer.start();
|
||||
return null;
|
||||
public void put(final URL url, Object obj, HttpClient http) {
|
||||
cacheLock.lock();
|
||||
try {
|
||||
boolean startThread = (keepAliveTimer == null);
|
||||
if (!startThread) {
|
||||
if (!keepAliveTimer.isAlive()) {
|
||||
startThread = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (startThread) {
|
||||
clear();
|
||||
/* Unfortunately, we can't always believe the keep-alive timeout we got
|
||||
* back from the server. If I'm connected through a Netscape proxy
|
||||
* to a server that sent me a keep-alive
|
||||
* time of 15 sec, the proxy unilaterally terminates my connection
|
||||
* The robustness to get around this is in HttpClient.parseHTTP()
|
||||
*/
|
||||
final KeepAliveCache cache = this;
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public Void run() {
|
||||
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
|
||||
keepAliveTimer.setDaemon(true);
|
||||
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
keepAliveTimer.start();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KeepAliveKey key = new KeepAliveKey(url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
KeepAliveKey key = new KeepAliveKey(url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
|
||||
if (v == null) {
|
||||
int keepAliveTimeout = http.getKeepAliveTimeout();
|
||||
v = new ClientVector(keepAliveTimeout > 0 ?
|
||||
keepAliveTimeout * 1000 : LIFETIME);
|
||||
v.put(http);
|
||||
super.put(key, v);
|
||||
} else {
|
||||
v.put(http);
|
||||
if (v == null) {
|
||||
int keepAliveTimeout = http.getKeepAliveTimeout();
|
||||
v = new ClientVector(keepAliveTimeout > 0 ?
|
||||
keepAliveTimeout * 1000 : LIFETIME);
|
||||
v.put(http);
|
||||
super.put(key, v);
|
||||
} else {
|
||||
v.put(http);
|
||||
}
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* remove an obsolete HttpClient from its VectorCache */
|
||||
public synchronized void remove(HttpClient h, Object obj) {
|
||||
KeepAliveKey key = new KeepAliveKey(h.url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
if (v != null) {
|
||||
v.remove(h);
|
||||
if (v.isEmpty()) {
|
||||
removeVector(key);
|
||||
public void remove(HttpClient h, Object obj) {
|
||||
cacheLock.lock();
|
||||
try {
|
||||
KeepAliveKey key = new KeepAliveKey(h.url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
if (v != null) {
|
||||
v.remove(h);
|
||||
if (v.isEmpty()) {
|
||||
removeVector(key);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* called by a clientVector thread when all its connections have timed out
|
||||
* and that vector of connections should be removed.
|
||||
*/
|
||||
synchronized void removeVector(KeepAliveKey k) {
|
||||
private void removeVector(KeepAliveKey k) {
|
||||
assert cacheLock.isHeldByCurrentThread();
|
||||
super.remove(k);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this URL has a cached HttpClient
|
||||
*/
|
||||
public synchronized HttpClient get(URL url, Object obj) {
|
||||
KeepAliveKey key = new KeepAliveKey(url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
if (v == null) { // nothing in cache yet
|
||||
return null;
|
||||
public HttpClient get(URL url, Object obj) {
|
||||
cacheLock.lock();
|
||||
try {
|
||||
KeepAliveKey key = new KeepAliveKey(url, obj);
|
||||
ClientVector v = super.get(key);
|
||||
if (v == null) { // nothing in cache yet
|
||||
return null;
|
||||
}
|
||||
return v.get();
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
return v.get();
|
||||
}
|
||||
|
||||
/* Sleeps for an alloted timeout, then checks for timed out connections.
|
||||
@ -170,13 +190,15 @@ public class KeepAliveCache
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
// Remove all outdated HttpClients.
|
||||
synchronized (this) {
|
||||
cacheLock.lock();
|
||||
try {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
List<KeepAliveKey> keysToRemove = new ArrayList<>();
|
||||
|
||||
for (KeepAliveKey key : keySet()) {
|
||||
ClientVector v = get(key);
|
||||
synchronized (v) {
|
||||
v.lock();
|
||||
try {
|
||||
KeepAliveEntry e = v.peek();
|
||||
while (e != null) {
|
||||
if ((currentTime - e.idleStartTime) > v.nap) {
|
||||
@ -191,12 +213,16 @@ public class KeepAliveCache
|
||||
if (v.isEmpty()) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
} finally {
|
||||
v.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
for (KeepAliveKey key : keysToRemove) {
|
||||
removeVector(key);
|
||||
}
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
} while (!isEmpty());
|
||||
}
|
||||
@ -223,6 +249,7 @@ public class KeepAliveCache
|
||||
class ClientVector extends ArrayDeque<KeepAliveEntry> {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -8680532108106489459L;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
// sleep time in milliseconds, before cache clear
|
||||
int nap;
|
||||
@ -231,42 +258,65 @@ class ClientVector extends ArrayDeque<KeepAliveEntry> {
|
||||
this.nap = nap;
|
||||
}
|
||||
|
||||
synchronized HttpClient get() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Loop until we find a connection that has not timed out
|
||||
HttpClient hc = null;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
do {
|
||||
KeepAliveEntry e = pop();
|
||||
if ((currentTime - e.idleStartTime) > nap) {
|
||||
e.hc.closeServer();
|
||||
} else {
|
||||
hc = e.hc;
|
||||
HttpClient get() {
|
||||
lock();
|
||||
try {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
} while ((hc == null) && (!isEmpty()));
|
||||
return hc;
|
||||
|
||||
// Loop until we find a connection that has not timed out
|
||||
HttpClient hc = null;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
do {
|
||||
KeepAliveEntry e = pop();
|
||||
if ((currentTime - e.idleStartTime) > nap) {
|
||||
e.hc.closeServer();
|
||||
} else {
|
||||
hc = e.hc;
|
||||
}
|
||||
} while ((hc == null) && (!isEmpty()));
|
||||
return hc;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* return a still valid, unused HttpClient */
|
||||
synchronized void put(HttpClient h) {
|
||||
if (size() >= KeepAliveCache.getMaxConnections()) {
|
||||
h.closeServer(); // otherwise the connection remains in limbo
|
||||
} else {
|
||||
push(new KeepAliveEntry(h, System.currentTimeMillis()));
|
||||
void put(HttpClient h) {
|
||||
lock();
|
||||
try {
|
||||
if (size() >= KeepAliveCache.getMaxConnections()) {
|
||||
h.closeServer(); // otherwise the connection remains in limbo
|
||||
} else {
|
||||
push(new KeepAliveEntry(h, System.currentTimeMillis()));
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* remove an HttpClient */
|
||||
synchronized boolean remove(HttpClient h) {
|
||||
for (KeepAliveEntry curr : this) {
|
||||
if (curr.hc == h) {
|
||||
return super.remove(curr);
|
||||
boolean remove(HttpClient h) {
|
||||
lock();
|
||||
try {
|
||||
for (KeepAliveEntry curr : this) {
|
||||
if (curr.hc == h) {
|
||||
return super.remove(curr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final void lock() {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
final void unlock() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2020, 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
|
||||
@ -47,7 +47,8 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
boolean hurried;
|
||||
|
||||
// has this KeepAliveStream been put on the queue for asynchronous cleanup.
|
||||
protected boolean queuedForCleanup = false;
|
||||
// This flag is read from within KeepAliveCleanerEntry outside of any lock.
|
||||
protected volatile boolean queuedForCleanup = false;
|
||||
|
||||
private static final KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner();
|
||||
private static Thread cleanerThread; // null
|
||||
@ -64,15 +65,8 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
* Attempt to cache this connection
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
// If the inputstream is closed already, just return.
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this stream has already been queued for cleanup.
|
||||
if (queuedForCleanup) {
|
||||
return;
|
||||
}
|
||||
// If the inputstream is queued for cleanup, just return.
|
||||
if (queuedForCleanup) return;
|
||||
|
||||
// Skip past the data that's left in the Inputstream because
|
||||
// some sort of error may have occurred.
|
||||
@ -81,34 +75,45 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
// to hang around for nothing. So if we can't skip without blocking
|
||||
// we just close the socket and, therefore, terminate the keepAlive
|
||||
// NOTE: Don't close super class
|
||||
// For consistency, access to `expected` and `count` should be
|
||||
// protected by readLock
|
||||
lock();
|
||||
try {
|
||||
if (expected > count) {
|
||||
long nskip = expected - count;
|
||||
if (nskip <= available()) {
|
||||
do {} while ((nskip = (expected - count)) > 0L
|
||||
&& skip(Math.min(nskip, available())) > 0L);
|
||||
} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
|
||||
//put this KeepAliveStream on the queue so that the data remaining
|
||||
//on the socket can be cleanup asyncronously.
|
||||
queueForCleanup(new KeepAliveCleanerEntry(this, hc));
|
||||
} else {
|
||||
hc.closeServer();
|
||||
// If the inputstream is closed already, or if this stream
|
||||
// has already been queued for cleanup, just return.
|
||||
if (closed || queuedForCleanup) return;
|
||||
try {
|
||||
if (expected > count) {
|
||||
long nskip = expected - count;
|
||||
if (nskip <= available()) {
|
||||
do {
|
||||
} while ((nskip = (expected - count)) > 0L
|
||||
&& skip(Math.min(nskip, available())) > 0L);
|
||||
} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
|
||||
//put this KeepAliveStream on the queue so that the data remaining
|
||||
//on the socket can be cleanup asyncronously.
|
||||
queueForCleanup(new KeepAliveCleanerEntry(this, hc));
|
||||
} else {
|
||||
hc.closeServer();
|
||||
}
|
||||
}
|
||||
if (!closed && !hurried && !queuedForCleanup) {
|
||||
hc.finished();
|
||||
}
|
||||
} finally {
|
||||
if (pi != null)
|
||||
pi.finishTracking();
|
||||
|
||||
if (!queuedForCleanup) {
|
||||
// nulling out the underlying inputstream as well as
|
||||
// httpClient to let gc collect the memories faster
|
||||
in = null;
|
||||
hc = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
if (!closed && !hurried && !queuedForCleanup) {
|
||||
hc.finished();
|
||||
}
|
||||
} finally {
|
||||
if (pi != null)
|
||||
pi.finishTracking();
|
||||
|
||||
if (!queuedForCleanup) {
|
||||
// nulling out the underlying inputstream as well as
|
||||
// httpClient to let gc collect the memories faster
|
||||
in = null;
|
||||
hc = null;
|
||||
closed = true;
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +129,8 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
throw new IOException("mark/reset not supported");
|
||||
}
|
||||
|
||||
public synchronized boolean hurry() {
|
||||
public boolean hurry() {
|
||||
lock();
|
||||
try {
|
||||
/* CASE 0: we're actually already done */
|
||||
if (closed || count >= expected) {
|
||||
@ -147,11 +153,14 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
} catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void queueForCleanup(KeepAliveCleanerEntry kace) {
|
||||
synchronized(queue) {
|
||||
queue.lock();
|
||||
try {
|
||||
if(!kace.getQueuedForCleanup()) {
|
||||
if (!queue.offer(kace)) {
|
||||
kace.getHttpClient().closeServer();
|
||||
@ -159,7 +168,7 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
}
|
||||
|
||||
kace.setQueuedForCleanup();
|
||||
queue.notifyAll();
|
||||
queue.signalAll();
|
||||
}
|
||||
|
||||
boolean startCleanupThread = (cleanerThread == null);
|
||||
@ -181,14 +190,20 @@ class KeepAliveStream extends MeteredStream implements Hurryable {
|
||||
}
|
||||
});
|
||||
}
|
||||
} // queue
|
||||
} finally {
|
||||
queue.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Only called from KeepAliveStreamCleaner
|
||||
protected long remainingToRead() {
|
||||
assert isLockHeldByCurrentThread();
|
||||
return expected - count;
|
||||
}
|
||||
|
||||
// Only called from KeepAliveStreamCleaner
|
||||
protected void setClosed() {
|
||||
assert isLockHeldByCurrentThread();
|
||||
in = null;
|
||||
hc = null;
|
||||
closed = true;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,6 +30,8 @@ import java.util.LinkedList;
|
||||
import sun.net.NetProperties;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* This class is used to cleanup any remaining data that may be on a KeepAliveStream
|
||||
@ -78,6 +80,20 @@ class KeepAliveStreamCleaner
|
||||
|
||||
}
|
||||
|
||||
private final ReentrantLock queueLock = new ReentrantLock();
|
||||
private final Condition waiter = queueLock.newCondition();
|
||||
|
||||
final void signalAll() {
|
||||
waiter.signalAll();
|
||||
}
|
||||
|
||||
final void lock() {
|
||||
queueLock.lock();
|
||||
}
|
||||
|
||||
final void unlock() {
|
||||
queueLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(KeepAliveCleanerEntry e) {
|
||||
@ -94,11 +110,12 @@ class KeepAliveStreamCleaner
|
||||
|
||||
do {
|
||||
try {
|
||||
synchronized(this) {
|
||||
lock();
|
||||
try {
|
||||
long before = System.currentTimeMillis();
|
||||
long timeout = TIMEOUT;
|
||||
while ((kace = poll()) == null) {
|
||||
this.wait(timeout);
|
||||
waiter.wait(timeout);
|
||||
|
||||
long after = System.currentTimeMillis();
|
||||
long elapsed = after - before;
|
||||
@ -110,6 +127,8 @@ class KeepAliveStreamCleaner
|
||||
before = after;
|
||||
timeout -= elapsed;
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
|
||||
if(kace == null)
|
||||
@ -118,7 +137,8 @@ class KeepAliveStreamCleaner
|
||||
KeepAliveStream kas = kace.getKeepAliveStream();
|
||||
|
||||
if (kas != null) {
|
||||
synchronized(kas) {
|
||||
kas.lock();
|
||||
try {
|
||||
HttpClient hc = kace.getHttpClient();
|
||||
try {
|
||||
if (hc != null && !hc.isInKeepAliveCache()) {
|
||||
@ -147,6 +167,8 @@ class KeepAliveStreamCleaner
|
||||
} finally {
|
||||
kas.setClosed();
|
||||
}
|
||||
} finally {
|
||||
kas.unlock();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) { }
|
||||
|
@ -32,8 +32,10 @@ import java.util.HashMap;
|
||||
/**
|
||||
* @author Michael McMahon
|
||||
*/
|
||||
|
||||
public class AuthCacheImpl implements AuthCache {
|
||||
// No blocking IO is performed within the synchronized code blocks
|
||||
// in this class, so there is no need to convert this class to using
|
||||
// java.util.concurrent.locks
|
||||
HashMap<String,LinkedList<AuthCacheValue>> hashtable;
|
||||
|
||||
public AuthCacheImpl () {
|
||||
@ -46,7 +48,6 @@ public class AuthCacheImpl implements AuthCache {
|
||||
|
||||
// put a value in map according to primary key + secondary key which
|
||||
// is the path field of AuthenticationInfo
|
||||
|
||||
public synchronized void put (String pkey, AuthCacheValue value) {
|
||||
LinkedList<AuthCacheValue> list = hashtable.get (pkey);
|
||||
String skey = value.getPath();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2020, 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
|
||||
@ -31,6 +31,8 @@ import java.net.PasswordAuthentication;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
|
||||
import sun.net.www.HeaderParser;
|
||||
@ -125,8 +127,9 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
* at the same time, then all but the first will block until
|
||||
* the first completes its authentication.
|
||||
*/
|
||||
private static HashMap<String,Thread> requests = new HashMap<>();
|
||||
|
||||
private static final HashMap<String,Thread> requests = new HashMap<>();
|
||||
private static final ReentrantLock requestLock = new ReentrantLock();
|
||||
private static final Condition requestFinished = requestLock.newCondition();
|
||||
/*
|
||||
* check if AuthenticationInfo is available in the cache.
|
||||
* If not, check if a request for this destination is in progress
|
||||
@ -142,8 +145,9 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
// and we can revert to concurrent requests
|
||||
return cached;
|
||||
}
|
||||
synchronized (requests) {
|
||||
// check again after synchronizing, and if available
|
||||
requestLock.lock();
|
||||
try {
|
||||
// check again after locking, and if available
|
||||
// just return the cached value.
|
||||
cached = cache.apply(key);
|
||||
if (cached != null) return cached;
|
||||
@ -164,10 +168,10 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
// Otherwise, an other thread is currently performing authentication:
|
||||
// wait until it finishes.
|
||||
while (requests.containsKey(key)) {
|
||||
try {
|
||||
requests.wait ();
|
||||
} catch (InterruptedException e) {}
|
||||
requestFinished.awaitUninterruptibly();
|
||||
}
|
||||
} finally {
|
||||
requestLock.unlock();
|
||||
}
|
||||
/* entry may be in cache now. */
|
||||
return cache.apply(key);
|
||||
@ -177,13 +181,16 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
* so that other threads can continue.
|
||||
*/
|
||||
private static void requestCompleted (String key) {
|
||||
synchronized (requests) {
|
||||
requestLock.lock();
|
||||
try {
|
||||
Thread thread = requests.get(key);
|
||||
if (thread != null && thread == Thread.currentThread()) {
|
||||
boolean waspresent = requests.remove(key) != null;
|
||||
assert waspresent;
|
||||
}
|
||||
requests.notifyAll();
|
||||
requestFinished.signalAll();
|
||||
} finally {
|
||||
requestLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,9 +421,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
if (!serializeAuth) {
|
||||
return;
|
||||
}
|
||||
synchronized (requests) {
|
||||
requestCompleted(key);
|
||||
}
|
||||
requestCompleted(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -500,6 +505,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
String s1, s2; /* used for serialization of pw */
|
||||
|
||||
@java.io.Serial
|
||||
// should be safe to keep synchronized here
|
||||
private synchronized void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
@ -512,6 +518,7 @@ public abstract class AuthenticationInfo extends AuthCacheValue implements Clone
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
// should be safe to keep synchronized here
|
||||
private synchronized void writeObject(java.io.ObjectOutputStream s)
|
||||
throws IOException
|
||||
{
|
||||
|
@ -141,6 +141,9 @@ class BasicAuthentication extends AuthenticationInfo {
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
conn.setAuthenticationProperty(getHeaderName(), getHeaderValue(null,null));
|
||||
return true;
|
||||
}
|
||||
|
@ -78,6 +78,9 @@ class DigestAuthentication extends AuthenticationInfo {
|
||||
// One instance of these may be shared among several DigestAuthentication
|
||||
// instances as a result of a single authorization (for multiple domains)
|
||||
|
||||
// There don't appear to be any blocking IO calls performed from
|
||||
// within the synchronized code blocks in the Parameters class, so there don't
|
||||
// seem to be any need to migrate it to using java.util.concurrent.locks
|
||||
static class Parameters implements java.io.Serializable {
|
||||
private static final long serialVersionUID = -3584543755194526252L;
|
||||
|
||||
@ -298,6 +301,10 @@ class DigestAuthentication extends AuthenticationInfo {
|
||||
*/
|
||||
@Override
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
|
||||
params.setNonce (p.findValue("nonce"));
|
||||
params.setOpaque (p.findValue("opaque"));
|
||||
params.setQop (p.findValue("qop"));
|
||||
|
@ -81,6 +81,8 @@ import java.net.MalformedURLException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static sun.net.www.protocol.http.AuthScheme.BASIC;
|
||||
import static sun.net.www.protocol.http.AuthScheme.DIGEST;
|
||||
import static sun.net.www.protocol.http.AuthScheme.NTLM;
|
||||
@ -97,7 +99,7 @@ import sun.security.action.GetPropertyAction;
|
||||
|
||||
public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
|
||||
static String HTTP_CONNECT = "CONNECT";
|
||||
static final String HTTP_CONNECT = "CONNECT";
|
||||
|
||||
static final String version;
|
||||
public static final String userAgent;
|
||||
@ -353,7 +355,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* - getOutputStream()
|
||||
* - getInputStream())
|
||||
* - connect()
|
||||
* Access synchronized on this.
|
||||
* Access is protected by connectionLock.
|
||||
*/
|
||||
private boolean connecting = false;
|
||||
|
||||
@ -432,6 +434,22 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
private static final PlatformLogger logger =
|
||||
PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
|
||||
|
||||
/* Lock */
|
||||
private final ReentrantLock connectionLock = new ReentrantLock();
|
||||
|
||||
private final void lock() {
|
||||
connectionLock.lock();
|
||||
}
|
||||
|
||||
private final void unlock() {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
|
||||
public final boolean isLockHeldByCurrentThread() {
|
||||
return connectionLock.isHeldByCurrentThread();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* privileged request password authentication
|
||||
*
|
||||
@ -514,13 +532,18 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setAuthenticator(Authenticator auth) {
|
||||
if (connecting || connected) {
|
||||
throw new IllegalStateException(
|
||||
"Authenticator must be set before connecting");
|
||||
public void setAuthenticator(Authenticator auth) {
|
||||
lock();
|
||||
try {
|
||||
if (connecting || connected) {
|
||||
throw new IllegalStateException(
|
||||
"Authenticator must be set before connecting");
|
||||
}
|
||||
authenticator = Objects.requireNonNull(auth);
|
||||
authenticatorKey = AuthenticatorKeys.getKey(authenticator);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
authenticator = Objects.requireNonNull(auth);
|
||||
authenticatorKey = AuthenticatorKeys.getKey(authenticator);
|
||||
}
|
||||
|
||||
public String getAuthenticatorKey() {
|
||||
@ -563,18 +586,25 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setRequestMethod(String method)
|
||||
public void setRequestMethod(String method)
|
||||
throws ProtocolException {
|
||||
if (connecting) {
|
||||
throw new IllegalStateException("connect in progress");
|
||||
lock();
|
||||
try {
|
||||
if (connecting) {
|
||||
throw new IllegalStateException("connect in progress");
|
||||
}
|
||||
super.setRequestMethod(method);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
super.setRequestMethod(method);
|
||||
}
|
||||
|
||||
/* adds the standard key/val pairs to reqests if necessary & write to
|
||||
* given PrintStream
|
||||
*/
|
||||
private void writeRequests() throws IOException {
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
/* print all message headers in the MessageHeader
|
||||
* onto the wire - all the ones we've set and any
|
||||
* others that have been set
|
||||
@ -683,6 +713,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
} else if (poster != null) {
|
||||
/* add Content-Length & POST/PUT data */
|
||||
// safe to synchronize on poster: this is
|
||||
// a simple subclass of ByteArrayOutputStream
|
||||
synchronized (poster) {
|
||||
/* close it, so no more data can be added */
|
||||
poster.close();
|
||||
@ -1010,8 +1042,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
// overridden in HTTPS subclass
|
||||
|
||||
public void connect() throws IOException {
|
||||
synchronized (this) {
|
||||
lock();
|
||||
try {
|
||||
connecting = true;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
plainConnect();
|
||||
}
|
||||
@ -1057,11 +1092,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
return host + ":" + Integer.toString(port);
|
||||
}
|
||||
|
||||
protected void plainConnect() throws IOException {
|
||||
synchronized (this) {
|
||||
protected void plainConnect() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
SocketPermission p = URLtoSocketPermission(this.url);
|
||||
if (p != null) {
|
||||
@ -1330,28 +1368,34 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
*/
|
||||
|
||||
@Override
|
||||
public synchronized OutputStream getOutputStream() throws IOException {
|
||||
connecting = true;
|
||||
SocketPermission p = URLtoSocketPermission(this.url);
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
connecting = true;
|
||||
SocketPermission p = URLtoSocketPermission(this.url);
|
||||
|
||||
if (p != null) {
|
||||
try {
|
||||
return AccessController.doPrivilegedWithCombiner(
|
||||
new PrivilegedExceptionAction<>() {
|
||||
public OutputStream run() throws IOException {
|
||||
return getOutputStream0();
|
||||
}
|
||||
}, null, p
|
||||
);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
if (p != null) {
|
||||
try {
|
||||
return AccessController.doPrivilegedWithCombiner(
|
||||
new PrivilegedExceptionAction<>() {
|
||||
public OutputStream run() throws IOException {
|
||||
return getOutputStream0();
|
||||
}
|
||||
}, null, p
|
||||
);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
}
|
||||
} else {
|
||||
return getOutputStream0();
|
||||
}
|
||||
} else {
|
||||
return getOutputStream0();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized OutputStream getOutputStream0() throws IOException {
|
||||
private OutputStream getOutputStream0() throws IOException {
|
||||
assert isLockHeldByCurrentThread();
|
||||
try {
|
||||
if (!doOutput) {
|
||||
throw new ProtocolException("cannot write to a URLConnection"
|
||||
@ -1441,16 +1485,19 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
// we only want to capture the user defined Cookies once, as
|
||||
// they cannot be changed by user code after we are connected,
|
||||
// only internally.
|
||||
synchronized (this) {
|
||||
if (setUserCookies) {
|
||||
int k = requests.getKey("Cookie");
|
||||
if (k != -1)
|
||||
userCookies = requests.getValue(k);
|
||||
k = requests.getKey("Cookie2");
|
||||
if (k != -1)
|
||||
userCookies2 = requests.getValue(k);
|
||||
setUserCookies = false;
|
||||
}
|
||||
|
||||
// we should only reach here when called from
|
||||
// writeRequest, which in turn is only called by
|
||||
// getInputStream0
|
||||
assert isLockHeldByCurrentThread();
|
||||
if (setUserCookies) {
|
||||
int k = requests.getKey("Cookie");
|
||||
if (k != -1)
|
||||
userCookies = requests.getValue(k);
|
||||
k = requests.getKey("Cookie2");
|
||||
if (k != -1)
|
||||
userCookies2 = requests.getValue(k);
|
||||
setUserCookies = false;
|
||||
}
|
||||
|
||||
// remove old Cookie header before setting new one.
|
||||
@ -1508,30 +1555,36 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized InputStream getInputStream() throws IOException {
|
||||
connecting = true;
|
||||
SocketPermission p = URLtoSocketPermission(this.url);
|
||||
public InputStream getInputStream() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
connecting = true;
|
||||
SocketPermission p = URLtoSocketPermission(this.url);
|
||||
|
||||
if (p != null) {
|
||||
try {
|
||||
return AccessController.doPrivilegedWithCombiner(
|
||||
new PrivilegedExceptionAction<>() {
|
||||
public InputStream run() throws IOException {
|
||||
return getInputStream0();
|
||||
}
|
||||
}, null, p
|
||||
);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
if (p != null) {
|
||||
try {
|
||||
return AccessController.doPrivilegedWithCombiner(
|
||||
new PrivilegedExceptionAction<>() {
|
||||
public InputStream run() throws IOException {
|
||||
return getInputStream0();
|
||||
}
|
||||
}, null, p
|
||||
);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
}
|
||||
} else {
|
||||
return getInputStream0();
|
||||
}
|
||||
} else {
|
||||
return getInputStream0();
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("empty-statement")
|
||||
private synchronized InputStream getInputStream0() throws IOException {
|
||||
private InputStream getInputStream0() throws IOException {
|
||||
|
||||
assert isLockHeldByCurrentThread();
|
||||
if (!doInput) {
|
||||
throw new ProtocolException("Cannot read from URLConnection"
|
||||
+ " if doInput=false (call setDoInput(true))");
|
||||
@ -2010,6 +2063,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
*/
|
||||
private AuthenticationInfo
|
||||
resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
|
||||
|
||||
// Only called from getInputStream0 and doTunneling0
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
if ((proxyAuthentication != null )&&
|
||||
proxyAuthentication.getAuthScheme() != NTLM) {
|
||||
String raw = auth.raw();
|
||||
@ -2060,7 +2117,16 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
/**
|
||||
* establish a tunnel through proxy server
|
||||
*/
|
||||
public synchronized void doTunneling() throws IOException {
|
||||
public void doTunneling() throws IOException {
|
||||
lock();
|
||||
try {
|
||||
doTunneling0();
|
||||
} finally{
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void doTunneling0() throws IOException {
|
||||
int retryTunnel = 0;
|
||||
String statusLine = "";
|
||||
int respCode = 0;
|
||||
@ -2068,6 +2134,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
String proxyHost = null;
|
||||
int proxyPort = -1;
|
||||
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
// save current requests so that they can be restored after tunnel is setup.
|
||||
MessageHeader savedRequests = requests;
|
||||
requests = new MessageHeader();
|
||||
@ -2273,7 +2341,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* the connection.
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) {
|
||||
private AuthenticationInfo getHttpProxyAuthentication(AuthenticationHeader authhdr) {
|
||||
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
/* get authorization from authenticator */
|
||||
AuthenticationInfo ret = null;
|
||||
String raw = authhdr.raw();
|
||||
@ -2434,11 +2505,15 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
/**
|
||||
* Gets the authentication for an HTTP server, and applies it to
|
||||
* the connection.
|
||||
* @param authHdr the AuthenticationHeader which tells what auth scheme is
|
||||
* @param authhdr the AuthenticationHeader which tells what auth scheme is
|
||||
* preferred.
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
|
||||
private AuthenticationInfo getServerAuthentication(AuthenticationHeader authhdr) {
|
||||
|
||||
// Only called from getInputStream0
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
/* get authorization from authenticator */
|
||||
AuthenticationInfo ret = null;
|
||||
String raw = authhdr.raw();
|
||||
@ -2716,6 +2791,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
private boolean followRedirect0(String loc, int stat, URL locUrl)
|
||||
throws IOException
|
||||
{
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
disconnectInternal();
|
||||
if (streaming()) {
|
||||
throw new HttpRetryException (RETRY_MSG3, stat, loc);
|
||||
@ -3195,17 +3272,22 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* @param value the value to be set
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setRequestProperty(String key, String value) {
|
||||
if (connected || connecting)
|
||||
throw new IllegalStateException("Already connected");
|
||||
if (key == null)
|
||||
throw new NullPointerException ("key is null");
|
||||
public void setRequestProperty(String key, String value) {
|
||||
lock();
|
||||
try {
|
||||
if (connected || connecting)
|
||||
throw new IllegalStateException("Already connected");
|
||||
if (key == null)
|
||||
throw new NullPointerException("key is null");
|
||||
|
||||
if (isExternalMessageHeaderAllowed(key, value)) {
|
||||
requests.set(key, value);
|
||||
if (!key.equalsIgnoreCase("Content-Type")) {
|
||||
userHeaders.set(key, value);
|
||||
if (isExternalMessageHeaderAllowed(key, value)) {
|
||||
requests.set(key, value);
|
||||
if (!key.equalsIgnoreCase("Content-Type")) {
|
||||
userHeaders.set(key, value);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3221,21 +3303,26 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* @param key the keyword by which the request is known
|
||||
* (e.g., "<code>accept</code>").
|
||||
* @param value the value associated with it.
|
||||
* @see #getRequestProperties(java.lang.String)
|
||||
* @see #getRequestProperty(java.lang.String)
|
||||
* @since 1.4
|
||||
*/
|
||||
@Override
|
||||
public synchronized void addRequestProperty(String key, String value) {
|
||||
if (connected || connecting)
|
||||
throw new IllegalStateException("Already connected");
|
||||
if (key == null)
|
||||
throw new NullPointerException ("key is null");
|
||||
public void addRequestProperty(String key, String value) {
|
||||
lock();
|
||||
try {
|
||||
if (connected || connecting)
|
||||
throw new IllegalStateException("Already connected");
|
||||
if (key == null)
|
||||
throw new NullPointerException("key is null");
|
||||
|
||||
if (isExternalMessageHeaderAllowed(key, value)) {
|
||||
requests.add(key, value);
|
||||
if (!key.equalsIgnoreCase("Content-Type")) {
|
||||
if (isExternalMessageHeaderAllowed(key, value)) {
|
||||
requests.add(key, value);
|
||||
if (!key.equalsIgnoreCase("Content-Type")) {
|
||||
userHeaders.add(key, value);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3244,31 +3331,41 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
// the connected test.
|
||||
//
|
||||
public void setAuthenticationProperty(String key, String value) {
|
||||
// Only called by the implementation of AuthenticationInfo::setHeaders(...)
|
||||
// in AuthenticationInfo subclasses, which is only called from
|
||||
// methods from HttpURLConnection protected by the connectionLock.
|
||||
assert isLockHeldByCurrentThread();
|
||||
|
||||
checkMessageHeader(key, value);
|
||||
requests.set(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getRequestProperty (String key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// don't return headers containing security sensitive information
|
||||
for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
|
||||
if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
|
||||
public String getRequestProperty (String key) {
|
||||
lock();
|
||||
try {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!setUserCookies) {
|
||||
if (key.equalsIgnoreCase("Cookie")) {
|
||||
return userCookies;
|
||||
|
||||
// don't return headers containing security sensitive information
|
||||
for (int i = 0; i < EXCLUDE_HEADERS.length; i++) {
|
||||
if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (key.equalsIgnoreCase("Cookie2")) {
|
||||
return userCookies2;
|
||||
if (!setUserCookies) {
|
||||
if (key.equalsIgnoreCase("Cookie")) {
|
||||
return userCookies;
|
||||
}
|
||||
if (key.equalsIgnoreCase("Cookie2")) {
|
||||
return userCookies2;
|
||||
}
|
||||
}
|
||||
return requests.findValue(key);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
return requests.findValue(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3284,29 +3381,34 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* @since 1.4
|
||||
*/
|
||||
@Override
|
||||
public synchronized Map<String, List<String>> getRequestProperties() {
|
||||
if (connected)
|
||||
throw new IllegalStateException("Already connected");
|
||||
public Map<String, List<String>> getRequestProperties() {
|
||||
lock();
|
||||
try {
|
||||
if (connected)
|
||||
throw new IllegalStateException("Already connected");
|
||||
|
||||
// exclude headers containing security-sensitive info
|
||||
if (setUserCookies) {
|
||||
return requests.getHeaders(EXCLUDE_HEADERS);
|
||||
}
|
||||
/*
|
||||
* The cookies in the requests message headers may have
|
||||
* been modified. Use the saved user cookies instead.
|
||||
*/
|
||||
Map<String, List<String>> userCookiesMap = null;
|
||||
if (userCookies != null || userCookies2 != null) {
|
||||
userCookiesMap = new HashMap<>();
|
||||
if (userCookies != null) {
|
||||
userCookiesMap.put("Cookie", Arrays.asList(userCookies));
|
||||
// exclude headers containing security-sensitive info
|
||||
if (setUserCookies) {
|
||||
return requests.getHeaders(EXCLUDE_HEADERS);
|
||||
}
|
||||
if (userCookies2 != null) {
|
||||
userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
|
||||
/*
|
||||
* The cookies in the requests message headers may have
|
||||
* been modified. Use the saved user cookies instead.
|
||||
*/
|
||||
Map<String, List<String>> userCookiesMap = null;
|
||||
if (userCookies != null || userCookies2 != null) {
|
||||
userCookiesMap = new HashMap<>();
|
||||
if (userCookies != null) {
|
||||
userCookiesMap.put("Cookie", Arrays.asList(userCookies));
|
||||
}
|
||||
if (userCookies2 != null) {
|
||||
userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
|
||||
}
|
||||
}
|
||||
return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -3350,7 +3452,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* value to be used in milliseconds
|
||||
* @throws IllegalArgumentException if the timeout parameter is negative
|
||||
*
|
||||
* @see java.net.URLConnectiongetReadTimeout()
|
||||
* @see java.net.URLConnection#getReadTimeout()
|
||||
* @see java.io.InputStream#read()
|
||||
* @since 1.5
|
||||
*/
|
||||
@ -3469,6 +3571,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* @see java.io.FilterInputStream#in
|
||||
* @see java.io.FilterInputStream#reset()
|
||||
*/
|
||||
// safe to use synchronized here: super method is synchronized too
|
||||
// and involves no blocking operation; only mark & reset are
|
||||
// synchronized in the super class hierarchy.
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
super.mark(readlimit);
|
||||
@ -3499,6 +3604,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
* @see java.io.FilterInputStream#in
|
||||
* @see java.io.FilterInputStream#mark(int)
|
||||
*/
|
||||
// safe to use synchronized here: super method is synchronized too
|
||||
// and involves no blocking operation; only mark & reset are
|
||||
// synchronized in the super class hierarchy.
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
super.reset();
|
||||
@ -3679,8 +3787,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
if (error) {
|
||||
throw errorExcp;
|
||||
}
|
||||
if (((PrintStream)out).checkError()) {
|
||||
throw new IOException("Error writing request body to server");
|
||||
if (out instanceof PrintStream) {
|
||||
if (((PrintStream) out).checkError()) {
|
||||
throw new IOException("Error writing request body to server");
|
||||
}
|
||||
} else if (out instanceof ChunkedOutputStream) {
|
||||
if (((ChunkedOutputStream) out).checkError()) {
|
||||
throw new IOException("Error writing request body to server");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,6 +30,8 @@ import java.io.IOException;
|
||||
import java.net.Authenticator.RequestorType;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.net.www.HeaderParser;
|
||||
import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
|
||||
import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
|
||||
@ -57,6 +59,8 @@ class NegotiateAuthentication extends AuthenticationInfo {
|
||||
// the cache can be used only once, so after the first use, it's cleaned.
|
||||
static HashMap <String, Boolean> supported = null;
|
||||
static ThreadLocal <HashMap <String, Negotiator>> cache = null;
|
||||
private static final ReentrantLock negotiateLock = new ReentrantLock();
|
||||
|
||||
/* Whether cache is enabled for Negotiate/Kerberos */
|
||||
private static final boolean cacheSPNEGO;
|
||||
static {
|
||||
@ -101,40 +105,50 @@ class NegotiateAuthentication extends AuthenticationInfo {
|
||||
*
|
||||
* @return true if supported
|
||||
*/
|
||||
synchronized public static boolean isSupported(HttpCallerInfo hci) {
|
||||
if (supported == null) {
|
||||
supported = new HashMap<>();
|
||||
}
|
||||
String hostname = hci.host;
|
||||
hostname = hostname.toLowerCase();
|
||||
if (supported.containsKey(hostname)) {
|
||||
return supported.get(hostname);
|
||||
}
|
||||
|
||||
Negotiator neg = Negotiator.getNegotiator(hci);
|
||||
if (neg != null) {
|
||||
supported.put(hostname, true);
|
||||
// the only place cache.put is called. here we can make sure
|
||||
// the object is valid and the oneToken inside is not null
|
||||
if (cache == null) {
|
||||
cache = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected HashMap<String, Negotiator> initialValue() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
};
|
||||
public static boolean isSupported(HttpCallerInfo hci) {
|
||||
negotiateLock.lock();
|
||||
try {
|
||||
if (supported == null) {
|
||||
supported = new HashMap<>();
|
||||
}
|
||||
cache.get().put(hostname, neg);
|
||||
return true;
|
||||
} else {
|
||||
supported.put(hostname, false);
|
||||
return false;
|
||||
String hostname = hci.host;
|
||||
hostname = hostname.toLowerCase();
|
||||
if (supported.containsKey(hostname)) {
|
||||
return supported.get(hostname);
|
||||
}
|
||||
|
||||
Negotiator neg = Negotiator.getNegotiator(hci);
|
||||
if (neg != null) {
|
||||
supported.put(hostname, true);
|
||||
// the only place cache.put is called. here we can make sure
|
||||
// the object is valid and the oneToken inside is not null
|
||||
if (cache == null) {
|
||||
cache = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected HashMap<String, Negotiator> initialValue() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
};
|
||||
}
|
||||
cache.get().put(hostname, neg);
|
||||
return true;
|
||||
} else {
|
||||
supported.put(hostname, false);
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
negotiateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized HashMap<String, Negotiator> getCache() {
|
||||
if (cache == null) return null;
|
||||
return cache.get();
|
||||
private static HashMap<String, Negotiator> getCache() {
|
||||
negotiateLock.lock();
|
||||
try {
|
||||
if (cache == null) return null;
|
||||
return cache.get();
|
||||
} finally {
|
||||
negotiateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,7 +186,10 @@ class NegotiateAuthentication extends AuthenticationInfo {
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
|
||||
try {
|
||||
String response;
|
||||
|
@ -341,8 +341,10 @@ final class HttpsClient extends HttpClient
|
||||
boolean compatible = ((ret.proxy != null && ret.proxy.equals(p)) ||
|
||||
(ret.proxy == null && p == Proxy.NO_PROXY))
|
||||
&& Objects.equals(ret.getAuthenticatorKey(), ak);
|
||||
|
||||
if (compatible) {
|
||||
synchronized (ret) {
|
||||
ret.lock();
|
||||
try {
|
||||
ret.cachedHttpClient = true;
|
||||
assert ret.inCache;
|
||||
ret.inCache = false;
|
||||
@ -351,18 +353,23 @@ final class HttpsClient extends HttpClient
|
||||
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
|
||||
logger.finest("KeepAlive stream retrieved from the cache, " + ret);
|
||||
}
|
||||
} finally {
|
||||
ret.unlock();
|
||||
}
|
||||
} else {
|
||||
// We cannot return this connection to the cache as it's
|
||||
// KeepAliveTimeout will get reset. We simply close the connection.
|
||||
// This should be fine as it is very rare that a connection
|
||||
// to the same host will not use the same proxy.
|
||||
synchronized(ret) {
|
||||
ret.lock();
|
||||
try {
|
||||
if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
|
||||
logger.finest("Not returning this connection to cache: " + ret);
|
||||
}
|
||||
ret.inCache = false;
|
||||
ret.closeServer();
|
||||
} finally {
|
||||
ret.unlock();
|
||||
}
|
||||
ret = null;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2020, 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
|
||||
@ -225,7 +225,10 @@ public class NTLMAuthentication extends AuthenticationInfo {
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
|
||||
try {
|
||||
String response;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2020, 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
|
||||
@ -244,7 +244,11 @@ public class NTLMAuthentication extends AuthenticationInfo {
|
||||
* @return true if all goes well, false if no headers were set.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
|
||||
|
||||
// no need to synchronize here:
|
||||
// already locked by s.n.w.p.h.HttpURLConnection
|
||||
assert conn.isLockHeldByCurrentThread();
|
||||
|
||||
try {
|
||||
NTLMAuthSequence seq = (NTLMAuthSequence)conn.authObj();
|
||||
|
Loading…
x
Reference in New Issue
Block a user