Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 75d4f54

Browse files
committed
JERSEY-3163: NettyResponseWriter must not close the connection/channel on every request
test: start netty-helloworld and execute $ curl -v localhost:8080/helloworld localhost:8080/helloworld Change-Id: I40039645369c06061f7cb5df86034f1cf6aa904d
1 parent ce1ea30 commit 75d4f54

File tree

6 files changed

+47
-25
lines changed

6 files changed

+47
-25
lines changed

connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/JerseyChunkedInput.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,16 +192,7 @@ public ByteBuffer get() {
192192

193193
@Override
194194
public void write(final byte[] b) throws IOException {
195-
196-
final byte[] bytes = new byte[b.length];
197-
System.arraycopy(b, 0, bytes, 0, b.length);
198-
199-
write(new Provider<ByteBuffer>() {
200-
@Override
201-
public ByteBuffer get() {
202-
return ByteBuffer.wrap(bytes);
203-
}
204-
});
195+
write(b, 0, b.length);
205196
}
206197

207198
@Override

connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyInputStream.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ public class NettyInputStream extends InputStream {
6464
public int read() throws IOException {
6565
return 0;
6666
}
67+
68+
@Override
69+
public String toString() {
70+
return "END_OF_INPUT " + super.toString();
71+
}
6772
};
6873

6974
/**
@@ -74,6 +79,11 @@ public int read() throws IOException {
7479
public int read() throws IOException {
7580
return 0;
7681
}
82+
83+
@Override
84+
public String toString() {
85+
return "END_OF_INPUT_ERROR " + super.toString();
86+
}
7787
};
7888

7989
private final LinkedBlockingDeque<InputStream> isList;

containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public void channelRead(final ChannelHandlerContext ctx, Object msg) {
103103
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
104104
}
105105

106+
isList.clear(); // clearing the content - possible leftover from previous request processing.
106107
final ContainerRequest requestContext = createContainerRequest(ctx, req);
107108

108109
requestContext.setWriter(new NettyResponseWriter(ctx, req, container));

containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttp2ResponseWriter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import io.netty.buffer.ByteBuf;
5555
import io.netty.channel.ChannelFutureListener;
5656
import io.netty.channel.ChannelHandlerContext;
57+
import io.netty.handler.codec.http.DefaultFullHttpResponse;
5758
import io.netty.handler.codec.http.HttpHeaderNames;
5859
import io.netty.handler.codec.http.HttpMethod;
5960
import io.netty.handler.codec.http.HttpResponseStatus;
@@ -134,14 +135,12 @@ public void flush() throws IOException {
134135

135136
@Override
136137
public void close() throws IOException {
137-
ctx.writeAndFlush(new DefaultHttp2DataFrame(true));
138-
ctx.close();
138+
ctx.write(new DefaultHttp2DataFrame(true)).addListener(NettyResponseWriter.FLUSH_FUTURE);
139139
}
140140
};
141141

142142
} else {
143143
ctx.writeAndFlush(new DefaultHttp2DataFrame(true));
144-
ctx.close();
145144
return null;
146145
}
147146
}

containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyResponseWriter.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,15 @@
4545
import java.util.Map;
4646
import java.util.concurrent.ScheduledFuture;
4747
import java.util.concurrent.TimeUnit;
48+
import java.util.logging.Level;
49+
import java.util.logging.Logger;
4850

49-
import io.netty.buffer.Unpooled;
51+
import org.glassfish.jersey.netty.connector.internal.JerseyChunkedInput;
52+
import org.glassfish.jersey.server.ContainerException;
53+
import org.glassfish.jersey.server.ContainerResponse;
54+
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
55+
56+
import io.netty.channel.ChannelFuture;
5057
import io.netty.channel.ChannelFutureListener;
5158
import io.netty.channel.ChannelHandlerContext;
5259
import io.netty.handler.codec.http.DefaultFullHttpResponse;
@@ -58,10 +65,7 @@
5865
import io.netty.handler.codec.http.HttpRequest;
5966
import io.netty.handler.codec.http.HttpResponseStatus;
6067
import io.netty.handler.codec.http.HttpUtil;
61-
import org.glassfish.jersey.netty.connector.internal.JerseyChunkedInput;
62-
import org.glassfish.jersey.server.ContainerException;
63-
import org.glassfish.jersey.server.ContainerResponse;
64-
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
68+
import io.netty.handler.codec.http.LastHttpContent;
6569

6670
/**
6771
* Netty implementation of {@link ContainerResponseWriter}.
@@ -70,23 +74,41 @@
7074
*/
7175
class NettyResponseWriter implements ContainerResponseWriter {
7276

77+
private static final Logger LOGGER = Logger.getLogger(NettyResponseWriter.class.getName());
78+
79+
static final ChannelFutureListener FLUSH_FUTURE = new ChannelFutureListener() {
80+
@Override
81+
public void operationComplete(ChannelFuture future) throws Exception {
82+
future.channel().flush();
83+
}
84+
};
85+
7386
private final ChannelHandlerContext ctx;
7487
private final HttpRequest req;
7588
private final NettyHttpContainer container;
7689

7790
private volatile ScheduledFuture<?> suspendTimeoutFuture;
7891
private volatile Runnable suspendTimeoutHandler;
7992

93+
private boolean responseWritten = false;
94+
8095
NettyResponseWriter(ChannelHandlerContext ctx, HttpRequest req, NettyHttpContainer container) {
8196
this.ctx = ctx;
8297
this.req = req;
8398
this.container = container;
8499
}
85100

86101
@Override
87-
public OutputStream writeResponseStatusAndHeaders(long contentLength, ContainerResponse responseContext)
102+
public synchronized OutputStream writeResponseStatusAndHeaders(long contentLength, ContainerResponse responseContext)
88103
throws ContainerException {
89104

105+
if (responseWritten) {
106+
LOGGER.log(Level.FINE, "Response already written.");
107+
return null;
108+
}
109+
110+
responseWritten = true;
111+
90112
String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase();
91113
int statusCode = responseContext.getStatus();
92114

@@ -122,14 +144,14 @@ public OutputStream writeResponseStatusAndHeaders(long contentLength, ContainerR
122144
JerseyChunkedInput jerseyChunkedInput = new JerseyChunkedInput(ctx.channel());
123145

124146
if (HttpUtil.isTransferEncodingChunked(response)) {
125-
ctx.write(new HttpChunkedInput(jerseyChunkedInput)).addListener(ChannelFutureListener.CLOSE);
147+
ctx.write(new HttpChunkedInput(jerseyChunkedInput)).addListener(FLUSH_FUTURE);
126148
} else {
127-
ctx.write(jerseyChunkedInput).addListener(ChannelFutureListener.CLOSE);
149+
ctx.write(new HttpChunkedInput(jerseyChunkedInput)).addListener(FLUSH_FUTURE);
128150
}
129151
return jerseyChunkedInput;
130152

131153
} else {
132-
ctx.write(Unpooled.buffer(0)).addListener(ChannelFutureListener.CLOSE);
154+
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
133155
return null;
134156
}
135157
}
@@ -178,8 +200,7 @@ public void commit() {
178200

179201
@Override
180202
public void failure(Throwable error) {
181-
ctx.writeAndFlush(
182-
new DefaultFullHttpResponse(req.protocolVersion(), HttpResponseStatus.INTERNAL_SERVER_ERROR))
203+
ctx.writeAndFlush(new DefaultFullHttpResponse(req.protocolVersion(), HttpResponseStatus.INTERNAL_SERVER_ERROR))
183204
.addListener(ChannelFutureListener.CLOSE);
184205
}
185206

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,7 @@
19431943
<mimepull.version>1.9.6</mimepull.version>
19441944
<moxy.version>2.6.0</moxy.version>
19451945
<mustache.version>0.8.17</mustache.version>
1946-
<netty.version>4.1.4.Final</netty.version>
1946+
<netty.version>4.1.5.Final</netty.version>
19471947
<osgi.version>4.2.0</osgi.version>
19481948
<pax.exam.version>4.9.1</pax.exam.version>
19491949
<pax.web.version>0.7.4</pax.web.version><!-- TODO: UPGRADE! -->

0 commit comments

Comments
 (0)