Skip to content

Commit

Permalink
Refactor HttpTest to run the tests with Http2 when it makes sense + f…
Browse files Browse the repository at this point in the history
…ix related bugs in http2
  • Loading branch information
vietj committed Mar 22, 2016
1 parent a6b334d commit 2a2bce3
Show file tree
Hide file tree
Showing 12 changed files with 3,528 additions and 3,426 deletions.
1 change: 0 additions & 1 deletion src/main/java/io/vertx/core/http/HttpConnection.java
Expand Up @@ -35,7 +35,6 @@
* - add rawMethod * - add rawMethod
* - byte distribution algorithm configurability (options ? connection ?) * - byte distribution algorithm configurability (options ? connection ?)
* - netSocket() interaction are not exactly the same than with http/1.x : see if we can make is the same * - netSocket() interaction are not exactly the same than with http/1.x : see if we can make is the same
* - run HttpTest with HTTP2
* - handle data frame padding * - handle data frame padding
* - documentation * - documentation
* - examples * - examples
Expand Down
78 changes: 42 additions & 36 deletions src/main/java/io/vertx/core/http/impl/Http2ClientConnection.java
Expand Up @@ -137,17 +137,18 @@ public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promi


static class Http2ClientStream extends VertxHttp2Stream<Http2ClientConnection> implements HttpClientStream { static class Http2ClientStream extends VertxHttp2Stream<Http2ClientConnection> implements HttpClientStream {


private HttpClientRequestBase req; private HttpClientRequestBase request;
private HttpClientResponseImpl resp; private HttpClientResponseImpl response;
private boolean ended; private boolean requestEnded;
private boolean responseEnded;


public Http2ClientStream(Http2ClientConnection conn, Http2Stream stream) throws Http2Exception { public Http2ClientStream(Http2ClientConnection conn, Http2Stream stream) throws Http2Exception {
this(conn, null, stream); this(conn, null, stream);
} }


public Http2ClientStream(Http2ClientConnection conn, HttpClientRequestBase req, Http2Stream stream) throws Http2Exception { public Http2ClientStream(Http2ClientConnection conn, HttpClientRequestBase request, Http2Stream stream) throws Http2Exception {
super(conn, stream); super(conn, stream);
this.req = req; this.request = request;
} }


@Override @Override
Expand All @@ -158,104 +159,104 @@ public HttpVersion version() {
@Override @Override
void callEnd() { void callEnd() {
if (conn.metrics.isEnabled()) { if (conn.metrics.isEnabled()) {
HttpClientRequestBase req = this.req; if (request.exceptionOccurred) {
if (req.exceptionOccurred) { conn.metrics.requestReset(request.metric());
conn.metrics.requestReset(req.metric());
} else { } else {
conn.metrics.responseEnd(req.metric(), resp); conn.metrics.responseEnd(request.metric(), response);
} }
} }
ended = true; responseEnded = true;
// Should use a shared immutable object for CaseInsensitiveHeaders ? // Should use a shared immutable object for CaseInsensitiveHeaders ?
resp.handleEnd(null, new CaseInsensitiveHeaders()); response.handleEnd(null, new CaseInsensitiveHeaders());
} }


@Override @Override
void callHandler(Buffer buf) { void callHandler(Buffer buf) {
resp.handleChunk(buf); response.handleChunk(buf);
} }


@Override @Override
void callReset(long errorCode) { void callReset(long errorCode) {
if (!ended) { if (!responseEnded) {
ended = true; responseEnded = true;
if (conn.metrics.isEnabled()) { if (conn.metrics.isEnabled()) {
HttpClientRequestBase req = this.req; conn.metrics.requestReset(request.metric());
conn.metrics.requestReset(req.metric());
} }
handleException(new StreamResetException(errorCode)); handleException(new StreamResetException(errorCode));
} }
} }


@Override @Override
void handleClose() { void handleClose() {
if (!ended) { if (!responseEnded) {
ended = true; responseEnded = true;
if (conn.metrics.isEnabled()) { if (conn.metrics.isEnabled()) {
HttpClientRequestBase req = this.req; conn.metrics.requestReset(request.metric());
conn.metrics.requestReset(req.metric());
} }
handleException(new VertxException("Connection was closed")); // Put that in utility class handleException(new VertxException("Connection was closed")); // Put that in utility class
} }
} }


@Override @Override
public void handleInterestedOpsChanged() { public void handleInterestedOpsChanged() {
if (req instanceof HttpClientRequestImpl && !isNotWritable()) { if (request instanceof HttpClientRequestImpl && !isNotWritable()) {
if (!isNotWritable()) { if (!isNotWritable()) {
((HttpClientRequestImpl)req).handleDrained(); ((HttpClientRequestImpl) request).handleDrained();
} }
} }
} }


@Override @Override
void handleUnknownFrame(int type, int flags, Buffer buff) { void handleUnknownFrame(int type, int flags, Buffer buff) {
resp.handleUnknowFrame(new HttpFrameImpl(type, flags, buff)); response.handleUnknowFrame(new HttpFrameImpl(type, flags, buff));
} }


void handleHeaders(Http2Headers headers, boolean end) { void handleHeaders(Http2Headers headers, boolean end) {
if (resp == null || resp.statusCode() == 100) { if (response == null || response.statusCode() == 100) {
int status; int status;
String statusMessage; String statusMessage;
try { try {
status = Integer.parseInt(headers.status().toString()); status = Integer.parseInt(headers.status().toString());
statusMessage = HttpResponseStatus.valueOf(status).reasonPhrase(); statusMessage = HttpResponseStatus.valueOf(status).reasonPhrase();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
handleException(e); handleException(e);
encoder.writeRstStream(handlerContext, stream.id(), 0x01 /* PROTOCOL_ERROR */, handlerContext.newPromise()); encoder.writeRstStream(handlerContext, stream.id(), 0x01 /* PROTOCOL_ERROR */, handlerContext.newPromise());
channel.flush(); channel.flush();
return; return;
} }
resp = new HttpClientResponseImpl( response = new HttpClientResponseImpl(
req, request,
HttpVersion.HTTP_2, HttpVersion.HTTP_2,
this, this,
status, status,
statusMessage, statusMessage,
new Http2HeadersAdaptor(headers) new Http2HeadersAdaptor(headers)
); );
req.handleResponse(resp); request.handleResponse(response);
if (end) { if (end) {
handleEnd(); handleEnd();
} }
} else if (end) { } else if (end) {
resp.handleEnd(null, new Http2HeadersAdaptor(headers)); response.handleEnd(null, new Http2HeadersAdaptor(headers));
} }
} }


void handleException(Throwable exception) { void handleException(Throwable exception) {
context.executeFromIO(() -> { if (!requestEnded || response == null) {
req.handleException(exception);
});
if (resp != null) {
context.executeFromIO(() -> { context.executeFromIO(() -> {
resp.handleException(exception); request.handleException(exception);
});
}
if (response != null) {
context.executeFromIO(() -> {
response.handleException(exception);
}); });
} }
} }


void handlePushPromise(HttpClientRequestBase promised) throws Http2Exception { void handlePushPromise(HttpClientRequestBase promised) throws Http2Exception {
((HttpClientRequestImpl)req).handlePush(promised); ((HttpClientRequestImpl) request).handlePush(promised);
} }


@Override @Override
Expand Down Expand Up @@ -291,7 +292,7 @@ public void writeHeadWithContent(HttpMethod method, String uri, MultiMap headers
h.set(HttpHeaderNames.ACCEPT_ENCODING, DEFLATE_GZIP); h.set(HttpHeaderNames.ACCEPT_ENCODING, DEFLATE_GZIP);
} }
if (conn.metrics.isEnabled()) { if (conn.metrics.isEnabled()) {
((HttpClientRequestImpl)req).metric(conn.metrics.requestBegin(conn.metric, conn.localAddress(), conn.remoteAddress(), req)); request.metric(conn.metrics.requestBegin(conn.metric, conn.localAddress(), conn.remoteAddress(), request));
} }
encoder.writeHeaders(handlerContext, stream.id(), h, 0, end && content == null, handlerContext.newPromise()); encoder.writeHeaders(handlerContext, stream.id(), h, 0, end && content == null, handlerContext.newPromise());
if (content != null) { if (content != null) {
Expand Down Expand Up @@ -323,17 +324,22 @@ public Context getContext() {
@Override @Override
public void doSetWriteQueueMaxSize(int size) { public void doSetWriteQueueMaxSize(int size) {
} }

@Override @Override
public boolean isNotWritable() { public boolean isNotWritable() {
return !conn.handler.connection().remote().flowController().isWritable(stream); return !conn.handler.connection().remote().flowController().isWritable(stream);
} }

@Override @Override
public void beginRequest(HttpClientRequestImpl request) { public void beginRequest(HttpClientRequestImpl request) {
req = request; this.request = request;
} }

@Override @Override
public void endRequest() { public void endRequest() {
requestEnded = true;
} }

@Override @Override
public void reset(long code) { public void reset(long code) {
encoder.writeRstStream(handlerContext, stream.id(), code, handlerContext.newPromise()); encoder.writeRstStream(handlerContext, stream.id(), code, handlerContext.newPromise());
Expand Down
Expand Up @@ -431,12 +431,12 @@ public MultiMap formAttributes() {


@Override @Override
public ServerWebSocket upgrade() { public ServerWebSocket upgrade() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException("HTTP/2 request cannot be upgraded to a websocket");
} }


@Override @Override
public boolean isEnded() { public boolean isEnded() {
throw new UnsupportedOperationException(); return ended;
} }


@Override @Override
Expand Down
20 changes: 13 additions & 7 deletions src/main/java/io/vertx/core/http/impl/Http2ServerResponseImpl.java
Expand Up @@ -61,7 +61,7 @@ public class Http2ServerResponseImpl implements HttpServerResponse {
private final boolean push; private final boolean push;
private final Object metric; private final Object metric;
private final String host; private final String host;
private Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); private Http2Headers headers = new DefaultHttp2Headers();
private Http2HeadersAdaptor headersMap; private Http2HeadersAdaptor headersMap;
private Http2Headers trailers; private Http2Headers trailers;
private Http2HeadersAdaptor trailedMap; private Http2HeadersAdaptor trailedMap;
Expand Down Expand Up @@ -162,7 +162,6 @@ public HttpServerResponse setStatusCode(int statusCode) {
} }
checkHeadWritten(); checkHeadWritten();
this.statusCode = statusCode; this.statusCode = statusCode;
headers.status("" + statusCode);
return this; return this;
} }


Expand Down Expand Up @@ -359,6 +358,7 @@ private boolean checkSendHeaders(boolean end) {
headersEndHandler.handle(null); headersEndHandler.handle(null);
} }
headWritten = true; headWritten = true;
headers.status(Integer.toString(statusCode));
encoder.writeHeaders(ctx, stream.id(), headers, 0, end, ctx.newPromise()); encoder.writeHeaders(ctx, stream.id(), headers, 0, end, ctx.newPromise());
if (end) { if (end) {
ctx.flush(); ctx.flush();
Expand All @@ -376,12 +376,12 @@ void write(ByteBuf chunk, boolean end) {
} }
int len = chunk.readableBytes(); int len = chunk.readableBytes();
boolean empty = len == 0; boolean empty = len == 0;
boolean sent = checkSendHeaders(empty && end); boolean sent = checkSendHeaders(end && empty && trailers == null);
if (!empty || (!sent && end)) { if (!empty || (!sent && end)) {
stream_.writeData(chunk, end && trailers == null); stream_.writeData(chunk, end && trailers == null);
bytesWritten += len; bytesWritten += len;
} }
if (trailers != null && end) { if (end && trailers != null) {
encoder.writeHeaders(ctx, stream.id(), trailers, 0, true, ctx.newPromise()); encoder.writeHeaders(ctx, stream.id(), trailers, 0, true, ctx.newPromise());
} }
if (end && bodyEndHandler != null) { if (end && bodyEndHandler != null) {
Expand Down Expand Up @@ -482,8 +482,14 @@ public HttpServerResponse sendFile(String filename, long offset, long length, Ha
} }


long contentLength = Math.min(length, file.length() - offset); long contentLength = Math.min(length, file.length() - offset);
if (headers.get("content-length") == null) { if (headers.get(HttpHeaderNames.CONTENT_LENGTH) == null) {
putHeader("content-length", String.valueOf(contentLength)); putHeader(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(contentLength));
}
if (headers.get(HttpHeaderNames.CONTENT_TYPE) == null) {
String contentType = MimeMapping.getMimeTypeForFilename(filename);
if (contentType != null) {
putHeader(HttpHeaderNames.CONTENT_TYPE, contentType);
}
} }
checkSendHeaders(false); checkSendHeaders(false);


Expand All @@ -507,7 +513,7 @@ public HttpServerResponse sendFile(String filename, long offset, long length, Ha


@Override @Override
public void close() { public void close() {
throw new UnsupportedOperationException(); connection.close();
} }


@Override @Override
Expand Down
Expand Up @@ -454,13 +454,9 @@ private void doSendFile(String filename, long offset, long length, Handler<Async
putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength)); putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
} }
if (!contentTypeSet()) { if (!contentTypeSet()) {
int li = filename.lastIndexOf('.'); String contentType = MimeMapping.getMimeTypeForFilename(filename);
if (li != -1 && li != filename.length() - 1) { if (contentType != null) {
String ext = filename.substring(li + 1, filename.length()); putHeader(HttpHeaders.CONTENT_TYPE, contentType);
String contentType = MimeMapping.getMimeTypeForExtension(ext);
if (contentType != null) {
putHeader(HttpHeaders.CONTENT_TYPE, contentType);
}
} }
} }
prepareHeaders(); prepareHeaders();
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/vertx/core/http/impl/MimeMapping.java
Expand Up @@ -1015,4 +1015,12 @@ public class MimeMapping {
public static String getMimeTypeForExtension(String ext) { public static String getMimeTypeForExtension(String ext) {
return m.get(ext); return m.get(ext);
} }
public static String getMimeTypeForFilename(String filename) {
int li = filename.lastIndexOf('.');
if (li != -1 && li != filename.length() - 1) {
String ext = filename.substring(li + 1, filename.length());
return MimeMapping.getMimeTypeForExtension(ext);
}
return null;
}
} }
Expand Up @@ -78,7 +78,7 @@ public static Iterable<Object[]> data() {
{KeyCert.PEM, Trust.JKS, false, false, false, Collections.emptyList()}, {KeyCert.PEM, Trust.JKS, false, false, false, Collections.emptyList()},
{KeyCert.PKCS12_CA, Trust.JKS_CA, false, false, false, Collections.emptyList()}, {KeyCert.PKCS12_CA, Trust.JKS_CA, false, false, false, Collections.emptyList()},
{KeyCert.PEM_CA, Trust.PKCS12_CA, false, false, false, Collections.emptyList()}, {KeyCert.PEM_CA, Trust.PKCS12_CA, false, false, false, Collections.emptyList()},
{KeyCert.JKS, Trust.PEM_CA, false, true, false, Arrays.asList(HttpTest.ENABLED_CIPHER_SUITES)}, {KeyCert.JKS, Trust.PEM_CA, false, true, false, Arrays.asList(Http1xTest.ENABLED_CIPHER_SUITES)},
}); });
} }


Expand Down

0 comments on commit 2a2bce3

Please sign in to comment.