8180498: Remove httpclient internal APIs which supply ByteBuffers to read calls

Reviewed-by: chegar, dfuchs
This commit is contained in:
Michael McMahon 2017-05-22 17:31:07 +01:00
parent 448c3c6d9a
commit 92ce207628
13 changed files with 60 additions and 114 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -189,11 +189,6 @@ class AsyncSSLConnection extends HttpConnection
throw new UnsupportedOperationException("Not supported."); throw new UnsupportedOperationException("Not supported.");
} }
@Override
protected int readImpl(ByteBuffer buffer) throws IOException {
throw new UnsupportedOperationException("Not supported.");
}
@Override @Override
CompletableFuture<Void> whenReceivingResponse() { CompletableFuture<Void> whenReceivingResponse() {
throw new UnsupportedOperationException("Not supported."); throw new UnsupportedOperationException("Not supported.");

View File

@ -33,7 +33,6 @@ import java.net.SocketPermission;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLPermission; import java.net.URLPermission;
import java.nio.ByteBuffer;
import java.security.AccessControlContext; import java.security.AccessControlContext;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
@ -76,9 +75,6 @@ final class Exchange<T> {
boolean upgrading; // to HTTP/2 boolean upgrading; // to HTTP/2
final PushGroup<?,T> pushGroup; final PushGroup<?,T> pushGroup;
// buffer for receiving response headers
private volatile ByteBuffer rxBuffer;
Exchange(HttpRequestImpl request, MultiExchange<?,T> multi) { Exchange(HttpRequestImpl request, MultiExchange<?,T> multi) {
this.request = request; this.request = request;
this.upgrading = false; this.upgrading = false;
@ -121,17 +117,6 @@ final class Exchange<T> {
return client; return client;
} }
ByteBuffer getBuffer() {
if(rxBuffer == null) {
synchronized (this) {
if(rxBuffer == null) {
rxBuffer = Utils.getExchangeBuffer();
}
}
}
return rxBuffer;
}
public Response response() throws IOException, InterruptedException { public Response response() throws IOException, InterruptedException {
return responseImpl(null); return responseImpl(null);
} }

View File

@ -55,7 +55,7 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
final HttpConnection connection; final HttpConnection connection;
final HttpClientImpl client; final HttpClientImpl client;
final Executor executor; final Executor executor;
final ByteBuffer buffer; // used for receiving volatile ByteBuffer buffer; // used for receiving
@Override @Override
public String toString() { public String toString() {
@ -74,7 +74,7 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
this.client = exchange.client(); this.client = exchange.client();
this.executor = exchange.executor(); this.executor = exchange.executor();
this.operations = new LinkedList<>(); this.operations = new LinkedList<>();
this.buffer = exchange.getBuffer(); this.buffer = Utils.EMPTY_BYTEBUFFER;
if (connection != null) { if (connection != null) {
this.connection = connection; this.connection = connection;
} else { } else {
@ -157,7 +157,9 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
try { try {
response = new Http1Response<>(connection, this); response = new Http1Response<>(connection, this);
response.readHeaders(); response.readHeaders();
return response.response(); Response r = response.response();
buffer = response.getBuffer();
return r;
} catch (Throwable t) { } catch (Throwable t) {
connection.close(); connection.close();
throw t; throw t;
@ -213,7 +215,9 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
return MinimalFuture.supply( () -> { return MinimalFuture.supply( () -> {
response = new Http1Response<>(connection, Http1Exchange.this); response = new Http1Response<>(connection, Http1Exchange.this);
response.readHeaders(); response.readHeaders();
return response.response(); Response r = response.response();
buffer = response.getBuffer();
return r;
}, executor); }, executor);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -44,7 +44,7 @@ class Http1Response<T> {
private final HttpConnection connection; private final HttpConnection connection;
private ResponseHeaders headers; private ResponseHeaders headers;
private int responseCode; private int responseCode;
private final ByteBuffer buffer; // same buffer used for reading status line and headers private ByteBuffer buffer;
private final Http1Exchange<T> exchange; private final Http1Exchange<T> exchange;
private final boolean redirecting; // redirecting private final boolean redirecting; // redirecting
private boolean return2Cache; // return connection to cache when finished private boolean return2Cache; // return connection to cache when finished
@ -96,6 +96,10 @@ class Http1Response<T> {
return finished; return finished;
} }
ByteBuffer getBuffer() {
return buffer;
}
int fixupContentLen(int clen) { int fixupContentLen(int clen) {
if (request.method().equalsIgnoreCase("HEAD")) { if (request.method().equalsIgnoreCase("HEAD")) {
return 0; return 0;
@ -194,12 +198,15 @@ class Http1Response<T> {
static final char CR = '\r'; static final char CR = '\r';
static final char LF = '\n'; static final char LF = '\n';
private int getBuffer() throws IOException { private int obtainBuffer() throws IOException {
int n = buffer.remaining(); int n = buffer.remaining();
if (n == 0) { if (n == 0) {
buffer.clear(); buffer = connection.read();
return connection.read(buffer); if (buffer == null) {
return -1;
}
n = buffer.remaining();
} }
return n; return n;
} }
@ -207,18 +214,17 @@ class Http1Response<T> {
String readStatusLine() throws IOException { String readStatusLine() throws IOException {
boolean cr = false; boolean cr = false;
StringBuilder statusLine = new StringBuilder(128); StringBuilder statusLine = new StringBuilder(128);
ByteBuffer b = buffer; while ((obtainBuffer()) != -1) {
while (getBuffer() != -1) { byte[] buf = buffer.array();
byte[] buf = b.array(); int offset = buffer.position();
int offset = b.position(); int len = buffer.limit() - offset;
int len = b.limit() - offset;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char c = (char) buf[i+offset]; char c = (char) buf[i+offset];
if (cr) { if (cr) {
if (c == LF) { if (c == LF) {
b.position(i + 1 + offset); buffer.position(i + 1 + offset);
return statusLine.toString(); return statusLine.toString();
} else { } else {
throw new IOException("invalid status line"); throw new IOException("invalid status line");
@ -231,7 +237,7 @@ class Http1Response<T> {
} }
} }
// unlikely, but possible, that multiple reads required // unlikely, but possible, that multiple reads required
b.position(b.limit()); buffer.position(buffer.limit());
} }
return null; return null;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -323,12 +323,9 @@ abstract class HttpConnection implements Closeable {
} }
} }
final int read(ByteBuffer buffer) throws IOException {
return readImpl(buffer);
}
final ByteBuffer read() throws IOException { final ByteBuffer read() throws IOException {
return readImpl(); ByteBuffer b = readImpl();
return b;
} }
/* /*
@ -337,9 +334,6 @@ abstract class HttpConnection implements Closeable {
*/ */
protected abstract ByteBuffer readImpl() throws IOException; protected abstract ByteBuffer readImpl() throws IOException;
/** Reads as much as possible into given buffer and returns amount read. */
protected abstract int readImpl(ByteBuffer buffer) throws IOException;
@Override @Override
public String toString() { public String toString() {
return "HttpConnection: " + channel().toString(); return "HttpConnection: " + channel().toString();

View File

@ -311,8 +311,7 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
} }
} }
@Override private int readImpl(ByteBuffer buf) throws IOException {
protected int readImpl(ByteBuffer buf) throws IOException {
int mark = buf.position(); int mark = buf.position();
int n; int n;
// FIXME: this hack works in conjunction with the corresponding change // FIXME: this hack works in conjunction with the corresponding change

View File

@ -156,11 +156,6 @@ class PlainTunnelingConnection extends HttpConnection {
return delegate.readImpl(); return delegate.readImpl();
} }
@Override
protected int readImpl(ByteBuffer buffer) throws IOException {
return delegate.readImpl(buffer);
}
@Override @Override
boolean isSecure() { boolean isSecure() {
return false; return false;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -148,11 +148,7 @@ class ResponseContent {
// make sure we have at least 1 byte to look at // make sure we have at least 1 byte to look at
private void getHunk() throws IOException { private void getHunk() throws IOException {
if (chunkbuf == null || !chunkbuf.hasRemaining()) { if (chunkbuf == null || !chunkbuf.hasRemaining()) {
if (chunkbuf == null) { chunkbuf = connection.read();
chunkbuf = Utils.getBuffer();
}
chunkbuf.clear();
connection.read(chunkbuf);
} }
} }
@ -256,7 +252,6 @@ class ResponseContent {
private void pushBodyFixed(ByteBuffer b) throws IOException { private void pushBodyFixed(ByteBuffer b) throws IOException {
int remaining = contentLength; int remaining = contentLength;
//lastBufferUsed = b;
while (b.hasRemaining() && remaining > 0) { while (b.hasRemaining() && remaining > 0) {
ByteBuffer buffer = Utils.getBuffer(); ByteBuffer buffer = Utils.getBuffer();
int amount = Math.min(b.remaining(), remaining); int amount = Math.min(b.remaining(), remaining);
@ -265,22 +260,14 @@ class ResponseContent {
buffer.flip(); buffer.flip();
dataConsumer.accept(Optional.of(buffer)); dataConsumer.accept(Optional.of(buffer));
} }
//client.returnBuffer(b);
while (remaining > 0) { while (remaining > 0) {
ByteBuffer buffer = Utils.getBuffer(); ByteBuffer buffer = connection.read();
int xx = connection.read(buffer); if (buffer == null)
if (xx == -1)
throw new IOException("connection closed"); throw new IOException("connection closed");
int bytesread = buffer.remaining(); int bytesread = buffer.remaining();
// assume for now that pipelining not implemented // assume for now that pipelining not implemented
if (bytesread > remaining) { if (bytesread > remaining) {
System.err.println("xx = " + xx);
System.err.println("bytesread = " + bytesread);
System.err.println("remaining = " + remaining);
for (int i=0; i<remaining; i++) {
System.err.printf("%x ", buffer.get());
}
throw new IOException("too many bytes read"); throw new IOException("too many bytes read");
} }
remaining -= bytesread; remaining -= bytesread;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -72,7 +72,7 @@ final class ResponseHeaders implements HttpHeaders {
static final class InputStreamWrapper extends InputStream { static final class InputStreamWrapper extends InputStream {
final HttpConnection connection; final HttpConnection connection;
final ByteBuffer buffer; ByteBuffer buffer;
int lastRead = -1; // last byte read from the buffer int lastRead = -1; // last byte read from the buffer
int consumed = 0; // number of bytes consumed. int consumed = 0; // number of bytes consumed.
InputStreamWrapper(HttpConnection connection, ByteBuffer buffer) { InputStreamWrapper(HttpConnection connection, ByteBuffer buffer) {
@ -83,9 +83,8 @@ final class ResponseHeaders implements HttpHeaders {
@Override @Override
public int read() throws IOException { public int read() throws IOException {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
buffer.clear(); buffer = connection.read();
int n = connection.read(buffer); if (buffer == null) {
if (n == -1) {
return lastRead = -1; return lastRead = -1;
} }
} }
@ -97,6 +96,16 @@ final class ResponseHeaders implements HttpHeaders {
} }
} }
private static void display(Map<String, List<String>> map) {
map.forEach((k,v) -> {
System.out.print (k + ": ");
for (String val : v) {
System.out.print(val + ", ");
}
System.out.println("");
});
}
private Map<String, List<String>> parse(InputStreamWrapper input) private Map<String, List<String>> parse(InputStreamWrapper input)
throws IOException throws IOException
{ {
@ -114,7 +123,6 @@ final class ResponseHeaders implements HttpHeaders {
// finds is CR. This only happens if there are no headers, and // finds is CR. This only happens if there are no headers, and
// only one byte will be consumed from the buffer. In this case // only one byte will be consumed from the buffer. In this case
// the next byte MUST be LF // the next byte MUST be LF
//System.err.println("Last character read is: " + (byte)lastRead);
if (input.read() != LF) { if (input.read() != LF) {
throw new IOException("Unexpected byte sequence when no headers: " throw new IOException("Unexpected byte sequence when no headers: "
+ ((int)CR) + " " + input.lastRead + ((int)CR) + " " + input.lastRead

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -162,10 +162,11 @@ class SSLConnection extends HttpConnection {
@Override @Override
protected ByteBuffer readImpl() throws IOException { protected ByteBuffer readImpl() throws IOException {
ByteBuffer dst = ByteBuffer.allocate(8192); WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192));
int n = readImpl(dst); // TODO: check for closure
int n = r.result.bytesProduced();
if (n > 0) { if (n > 0) {
return dst; return r.buf;
} else if (n == 0) { } else if (n == 0) {
return Utils.EMPTY_BYTEBUFFER; return Utils.EMPTY_BYTEBUFFER;
} else { } else {
@ -173,19 +174,6 @@ class SSLConnection extends HttpConnection {
} }
} }
@Override
protected int readImpl(ByteBuffer buf) throws IOException {
// TODO: need to ensure that buf is big enough for application data
WrapperResult r = sslDelegate.recvData(buf);
// TODO: check for closure
String s = "Receive) ";
//debugPrint(s, r.buf);
if (r.result.bytesProduced() > 0) {
assert buf == r.buf;
}
return r.result.bytesProduced();
}
@Override @Override
boolean connected() { boolean connected() {
return delegate.connected(); return delegate.connected();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -104,9 +104,7 @@ class SSLDelegate {
} }
SSLEngineResult result; SSLEngineResult result;
/* if passed in buffer was not big enough then the a reallocated buffer ByteBuffer buf; // buffer containing result data
* is returned here */
ByteBuffer buf;
} }
int app_buf_size; int app_buf_size;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -164,19 +164,10 @@ class SSLTunnelConnection extends HttpConnection {
@Override @Override
protected ByteBuffer readImpl() throws IOException { protected ByteBuffer readImpl() throws IOException {
return sslDelegate.recvData(Utils.EMPTY_BYTEBUFFER).buf; // fix this, make the read normal ByteBuffer buf = Utils.getBuffer();
}
@Override
protected int readImpl(ByteBuffer buf) throws IOException {
WrapperResult r = sslDelegate.recvData(buf); WrapperResult r = sslDelegate.recvData(buf);
// TODO: check for closure return r.buf;
String s = "Receive) ";
//debugPrint(s, r.buf);
if (r.result.bytesProduced() > 0) {
assert buf == r.buf;
}
return r.result.bytesProduced();
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -214,10 +214,6 @@ public class ResponseHeadersTest {
protected ByteBuffer readImpl() throws IOException { protected ByteBuffer readImpl() throws IOException {
throw new AssertionError("Bad test assumption: should not have reached here!"); throw new AssertionError("Bad test assumption: should not have reached here!");
} }
@Override
protected int readImpl(ByteBuffer buffer) throws IOException {
throw new AssertionError("Bad test assumption: should not have reached here!");
}
} }
public static HttpHeaders createResponseHeaders(ByteBuffer buffer) public static HttpHeaders createResponseHeaders(ByteBuffer buffer)