Skip to content

Commit

Permalink
Allow to send empty frames as some protocols might rely on that
Browse files Browse the repository at this point in the history
  • Loading branch information
vietj committed Mar 29, 2016
1 parent f2eadd7 commit a62fb85
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 21 deletions.
17 changes: 13 additions & 4 deletions src/main/asciidoc/dataobjects.adoc
Expand Up @@ -251,17 +251,26 @@ Set the maximum number of worker threads to be used by the Vert.x instance.
== GoAway

++++
@author <a href="mailto:julien@julienviet.com">Julien Viet</a>
A frame.
++++
'''

[cols=">25%,^25%,50%"]
[frame="topbot"]
|===
^|Name | Type ^| Description
|[[debugData]]`debugData`|`Buffer`|-
|[[errorCode]]`errorCode`|`Number (long)`|-
|[[lastStreamId]]`lastStreamId`|`Number (int)`|-
|[[debugData]]`debugData`|`Buffer`|
+++
Set the additional debug data
+++
|[[errorCode]]`errorCode`|`Number (long)`|
+++
@return the error code
+++
|[[lastStreamId]]`lastStreamId`|`Number (int)`|
+++
Set the last stream id.
+++
|===

[[Http2Settings]]
Expand Down
16 changes: 10 additions & 6 deletions src/main/asciidoc/java/http.adoc
Expand Up @@ -1558,6 +1558,8 @@ server.connectionHandler(connection -> {
});
----

NOTE: this only applies to the HTTP/2 protocol

==== Client connections

The `link:../../apidocs/io/vertx/core/http/HttpClientRequest.html#connection--[connection]` method returns the request connection on the client:
Expand All @@ -1576,6 +1578,8 @@ request.connectionHandler(connection -> {
});
----

NOTE: this only applies to the HTTP/2 protocol

==== Connection settings

The configuration of an HTTP/2 is configured by the `link:../../apidocs/io/vertx/core/http/Http2Settings.html[Http2Settings]` data object.
Expand Down Expand Up @@ -1617,24 +1621,24 @@ connection.remoteSettingsHandler(settings -> {

==== Connection shutdown

Calling `link:../../apidocs/io/vertx/core/http/HttpConnection.html#shutdown--[shutdown]` will send a `GO_AWAY` frame to the
Calling `link:../../apidocs/io/vertx/core/http/HttpConnection.html#shutdown--[shutdown]` will send a `GOAWAY` frame to the
remote side of the connection, asking it to stop creating streams: a client will stop doing new requests
and a server will stop pushing responses. After the `GO_AWAY` frame is sent, the connection
and a server will stop pushing responses. After the `GOAWAY` frame is sent, the connection
waits some time (30 seconds by default) until all current streams closed and close the connection:

[source,java]
----
connection.shutdown();
----

Connection `link:../../apidocs/io/vertx/core/http/HttpConnection.html#close--[close]` close is a shutdown with no delay, the `GO_AWAY`
Connection `link:../../apidocs/io/vertx/core/http/HttpConnection.html#close--[close]` close is a shutdown with no delay, the `GOAWAY`
frame will still be sent before the connection is closed.

The `link:../../apidocs/io/vertx/core/http/HttpConnection.html#closeHandler-io.vertx.core.Handler-[closeHandler]` notifies when connection is closed,
`link:../../apidocs/io/vertx/core/http/HttpConnection.html#shutdownHandler-io.vertx.core.Handler-[shutdownHandler]` notifies when all streams have been closed but the
connection is not yet closed.

Finally it's possible to just send a `GO_AWAY` frame, the main difference with a shutdown is that
Finally it's possible to just send a `GOAWAY` frame, the main difference with a shutdown is that
it will just tell the remote side of the connection to stop creating new streams without scheduling a connection
close:

Expand All @@ -1643,7 +1647,7 @@ close:
connection.goAway(0);
----

Conversely, it is also possible to be notified when `GO_AWAY` are received:
Conversely, it is also possible to be notified when `GOAWAY` are received:

[source,java]
----
Expand All @@ -1665,7 +1669,7 @@ connection.shutdownHandler(v -> {
});
----

This applies also when a `GO_AWAY` is received.
This applies also when a `GOAWAY` is received.

=== HttpClient usage

Expand Down
Expand Up @@ -273,9 +273,6 @@ public void writeHead(HttpMethod method, String uri, MultiMap headers, String ho

@Override
public void writeHeadWithContent(HttpMethod method, String uri, MultiMap headers, String hostHeader, boolean chunked, ByteBuf content, boolean end) {
if (content != null && content.readableBytes() == 0) {
content = null;
}
Http2Headers h = new DefaultHttp2Headers();
h.method(method.name());
if (method == HttpMethod.CONNECT) {
Expand Down
Expand Up @@ -345,7 +345,7 @@ public void end(Buffer chunk) {

@Override
public void end() {
end(Unpooled.EMPTY_BUFFER);
end((ByteBuf) null);
}

void toNetSocket() {
Expand All @@ -356,7 +356,7 @@ void toNetSocket() {

private void end(ByteBuf chunk) {
synchronized (conn) {
if (!chunked && !headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
if (chunk != null && !chunked && !headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
headers().set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(chunk.readableBytes()));
}
write(chunk, true);
Expand Down Expand Up @@ -392,10 +392,13 @@ void write(ByteBuf chunk, boolean end) {
if (end) {
handleEnded(false);
}
int len = chunk.readableBytes();
boolean empty = len == 0;
boolean sent = checkSendHeaders(end && empty && trailers == null);
if (!empty || (!sent && end)) {
boolean hasBody = chunk != null;
boolean sent = checkSendHeaders(end && !hasBody && trailers == null);
if (hasBody || (!sent && end)) {
if (chunk == null) {
chunk = Unpooled.EMPTY_BUFFER;
}
int len = chunk.readableBytes();
stream.writeData(chunk, end && trailers == null);
bytesWritten += len;
}
Expand Down
51 changes: 49 additions & 2 deletions src/test/java/io/vertx/test/core/Http2ClientTest.java
Expand Up @@ -243,6 +243,40 @@ public void testGet() throws Exception {
await();
}

@Test
public void testResponseBody() throws Exception {
testResponseBody(TestUtils.randomAlphaString(100));
}

@Test
public void testEmptyResponseBody() throws Exception {
testResponseBody("");
}

private void testResponseBody(String expected) throws Exception {
server.requestHandler(req -> {
HttpServerResponse resp = req.response();
resp.end(expected);
});
startServer();
client.get(DEFAULT_HTTPS_PORT, DEFAULT_HTTPS_HOST, "/somepath", resp -> {
AtomicInteger count = new AtomicInteger();
Buffer content = Buffer.buffer();
resp.handler(buff -> {
content.appendBuffer(buff);
count.incrementAndGet();
});
resp.endHandler(v -> {
assertTrue(count.get() > 0);
assertEquals(expected, content.toString());
testComplete();
});
})
.exceptionHandler(err -> fail())
.end();
await();
}

@Test
public void testOverrideAuthority() throws Exception {
server.requestHandler(req -> {
Expand Down Expand Up @@ -308,12 +342,25 @@ public void testBodyEndHandler() throws Exception {

@Test
public void testPost() throws Exception {
testPost(TestUtils.randomAlphaString(100));
}

@Test
public void testEmptyPost() throws Exception {
testPost("");
}

private void testPost(String expected) throws Exception {
Buffer content = Buffer.buffer();
String expected = TestUtils.randomAlphaString(100);
AtomicInteger count = new AtomicInteger();
server.requestHandler(req -> {
assertEquals(HttpMethod.POST, req.method());
req.handler(content::appendBuffer);
req.handler(buff -> {
content.appendBuffer(buff);
count.getAndIncrement();
});
req.endHandler(v -> {
assertTrue(count.get() > 0);
req.response().end();
});
});
Expand Down

0 comments on commit a62fb85

Please sign in to comment.