Skip to content

Commit 966176f

Browse files
committed
Merge pull request AsyncHttpClient#552 from oleksiys/master
merged
2 parents 3b4aa0c + aaf7c17 commit 966176f

File tree

8 files changed

+435
-29
lines changed

8 files changed

+435
-29
lines changed

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/EventHandler.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -163,8 +163,9 @@ public void onInitialLineParsed(HttpHeader httpHeader, FilterChainContext ctx) {
163163
}
164164
}
165165
}
166-
final GrizzlyResponseStatus responseStatus = new GrizzlyResponseStatus((HttpResponsePacket) httpHeader, context.getRequest()
167-
.getURI(), config);
166+
final GrizzlyResponseStatus responseStatus =
167+
new GrizzlyResponseStatus((HttpResponsePacket) httpHeader,
168+
context.getRequest().getURI(), config);
168169
context.setResponseStatus(responseStatus);
169170
if (context.getStatusHandler() != null) {
170171
return;

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyAsyncHttpProvider.java

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2012-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -73,6 +73,7 @@
7373
import java.util.concurrent.ExecutorService;
7474
import java.util.concurrent.TimeUnit;
7575
import java.util.concurrent.TimeoutException;
76+
import org.glassfish.grizzly.spdy.SpdyVersion;
7677

7778
/**
7879
* A Grizzly 2.0-based implementation of {@link AsyncHttpProvider}.
@@ -441,8 +442,9 @@ void timeout(final Connection c) {
441442
// ---------------------------------------------------------- Nested Classes
442443

443444
private static final class ProtocolNegotiator implements ClientSideNegotiator {
445+
private static final SpdyVersion[] SUPPORTED_SPDY_VERSIONS =
446+
{SpdyVersion.SPDY_3_1, SpdyVersion.SPDY_3};
444447

445-
private static final String SPDY = "spdy/3";
446448
private static final String HTTP = "HTTP/1.1";
447449

448450
private final FilterChain spdyFilterChain;
@@ -465,23 +467,31 @@ public boolean wantNegotiate(SSLEngine engine) {
465467
}
466468

467469
@Override
468-
public String selectProtocol(SSLEngine engine, LinkedHashSet<String> strings) {
469-
GrizzlyAsyncHttpProvider.LOGGER.info("ProtocolSelector::selectProtocol: " + strings);
470+
public String selectProtocol(SSLEngine engine, LinkedHashSet<String> protocols) {
471+
GrizzlyAsyncHttpProvider.LOGGER.info("ProtocolSelector::selectProtocol: " + protocols);
470472
final Connection connection = NextProtoNegSupport.getConnection(engine);
471473

472-
// Give preference to SPDY/3. If not available, check for HTTP as a
473-
// fallback
474-
if (strings.contains(SPDY)) {
475-
GrizzlyAsyncHttpProvider.LOGGER.info("ProtocolSelector::selecting: " + SPDY);
476-
SSLConnectionContext sslCtx = SSLUtils.getSslConnectionContext(connection);
477-
sslCtx.setNewConnectionFilterChain(spdyFilterChain);
478-
final SpdySession spdySession = new SpdySession(connection, false, spdyHandlerFilter);
479-
spdySession.setLocalInitialWindowSize(spdyHandlerFilter.getInitialWindowSize());
480-
spdySession.setLocalMaxConcurrentStreams(spdyHandlerFilter.getMaxConcurrentStreams());
481-
Utils.setSpdyConnection(connection);
482-
SpdySession.bind(connection, spdySession);
483-
return SPDY;
484-
} else if (strings.contains(HTTP)) {
474+
// Give preference to SPDY/3.1 or SPDY/3. If not available, check for HTTP as a
475+
// fallback
476+
for (SpdyVersion version : SUPPORTED_SPDY_VERSIONS) {
477+
final String versionDef = version.toString();
478+
if (protocols.contains(versionDef)) {
479+
GrizzlyAsyncHttpProvider.LOGGER.info("ProtocolSelector::selecting: " + versionDef);
480+
SSLConnectionContext sslCtx = SSLUtils.getSslConnectionContext(connection);
481+
sslCtx.setNewConnectionFilterChain(spdyFilterChain);
482+
final SpdySession spdySession =
483+
version.newSession(connection, false, spdyHandlerFilter);
484+
485+
spdySession.setLocalStreamWindowSize(spdyHandlerFilter.getInitialWindowSize());
486+
spdySession.setLocalMaxConcurrentStreams(spdyHandlerFilter.getMaxConcurrentStreams());
487+
Utils.setSpdyConnection(connection);
488+
SpdySession.bind(connection, spdySession);
489+
490+
return versionDef;
491+
}
492+
}
493+
494+
if (protocols.contains(HTTP)) {
485495
GrizzlyAsyncHttpProvider.LOGGER.info("ProtocolSelector::selecting: " + HTTP);
486496
// Use the default HTTP FilterChain.
487497
return HTTP;

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyResponseStatus.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2012-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -38,7 +38,8 @@ public class GrizzlyResponseStatus extends HttpResponseStatus {
3838
private final int majorVersion;
3939
private final int minorVersion;
4040
private final String protocolText;
41-
41+
private final HttpResponsePacket response;
42+
4243
// ------------------------------------------------------------ Constructors
4344

4445
public GrizzlyResponseStatus(final HttpResponsePacket response, final URI uri, AsyncHttpClientConfig config) {
@@ -49,6 +50,8 @@ public GrizzlyResponseStatus(final HttpResponsePacket response, final URI uri, A
4950
majorVersion = response.getProtocol().getMajorVersion();
5051
minorVersion = response.getProtocol().getMinorVersion();
5152
protocolText = response.getProtocolString();
53+
54+
this.response = response;
5255
}
5356

5457
// ----------------------------------------- Methods from HttpResponseStatus
@@ -105,4 +108,11 @@ public int getProtocolMinorVersion() {
105108
public String getProtocolText() {
106109
return protocolText;
107110
}
111+
112+
/**
113+
* @return internal Grizzly {@link HttpResponsePacket}
114+
*/
115+
public HttpResponsePacket getResponse() {
116+
return response;
117+
}
108118
}

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/HttpTxContext.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -33,6 +33,10 @@
3333
import java.io.IOException;
3434
import java.util.concurrent.atomic.AtomicInteger;
3535
import java.util.concurrent.atomic.AtomicLong;
36+
import org.asynchttpclient.providers.grizzly.filters.events.GracefulCloseEvent;
37+
import org.glassfish.grizzly.Connection;
38+
import org.glassfish.grizzly.filterchain.FilterChain;
39+
import org.glassfish.grizzly.http.HttpResponsePacket;
3640

3741
public final class HttpTxContext {
3842

@@ -62,10 +66,21 @@ public final class HttpTxContext {
6266
private HandShake handshake;
6367
private ProtocolHandler protocolHandler;
6468
private WebSocket webSocket;
65-
private CloseListener listener = new CloseListener<Closeable, CloseType>() {
69+
private final CloseListener listener = new CloseListener<Closeable, CloseType>() {
6670
@Override
6771
public void onClosed(Closeable closeable, CloseType type) throws IOException {
68-
if (CloseType.REMOTELY.equals(type)) {
72+
if (isGracefullyFinishResponseOnClose()) {
73+
// Connection was closed.
74+
// This event is fired only for responses, which don't have
75+
// associated transfer-encoding or content-length.
76+
// We have to complete such a request-response processing gracefully.
77+
final Connection c = responseStatus.getResponse()
78+
.getRequest().getConnection();
79+
final FilterChain fc = (FilterChain) c.getProcessor();
80+
81+
fc.fireEventUpstream(c,
82+
new GracefulCloseEvent(HttpTxContext.this), null);
83+
} else if (CloseType.REMOTELY.equals(type)) {
6984
abort(AsyncHttpProviderUtils.REMOTELY_CLOSED_EXCEPTION);
7085
}
7186
}
@@ -252,6 +267,12 @@ public void setWebSocket(WebSocket webSocket) {
252267
this.webSocket = webSocket;
253268
}
254269

270+
private boolean isGracefullyFinishResponseOnClose() {
271+
final HttpResponsePacket response = responseStatus.getResponse();
272+
return !response.getProcessingState().isKeepAlive() &&
273+
!response.isChunked() && response.getContentLength() == -1;
274+
}
275+
255276
// ------------------------------------------------- Package Private Methods
256277

257278
public HttpTxContext copy() {

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/filters/AsyncHttpClientEventFilter.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -21,6 +21,10 @@
2121
import org.glassfish.grizzly.http.HttpHeader;
2222

2323
import java.io.IOException;
24+
import org.asynchttpclient.providers.grizzly.filters.events.GracefulCloseEvent;
25+
import org.glassfish.grizzly.filterchain.FilterChainEvent;
26+
import org.glassfish.grizzly.filterchain.NextAction;
27+
import org.glassfish.grizzly.http.HttpResponsePacket;
2428

2529
/**
2630
* Extension of the {@link HttpClientFilter} that is responsible for handling
@@ -45,6 +49,28 @@ public AsyncHttpClientEventFilter(final EventHandler eventHandler, final int max
4549
this.eventHandler = eventHandler;
4650
}
4751

52+
@Override
53+
public NextAction handleEvent(final FilterChainContext ctx,
54+
final FilterChainEvent event) throws IOException {
55+
if (event.type() == GracefulCloseEvent.class) {
56+
// Connection was closed.
57+
// This event is fired only for responses, which don't have
58+
// associated transfer-encoding or content-length.
59+
// We have to complete such a request-response processing gracefully.
60+
final GracefulCloseEvent closeEvent = (GracefulCloseEvent) event;
61+
final HttpResponsePacket response = closeEvent.getHttpTxContext()
62+
.getResponseStatus().getResponse();
63+
response.getProcessingState().getHttpContext().attach(ctx);
64+
65+
onHttpPacketParsed(response, ctx);
66+
67+
return ctx.getStopAction();
68+
}
69+
70+
return ctx.getInvokeAction();
71+
}
72+
73+
4874
@Override
4975
public void exceptionOccurred(FilterChainContext ctx, Throwable error) {
5076
eventHandler.exceptionOccurred(ctx, error);

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/filters/AsyncHttpClientFilter.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -281,21 +281,26 @@ private boolean sendAsGrizzlyRequest(final RequestInfoHolder requestInfoHolder,
281281
sendingCtx = checkAndHandleFilterChainUpdate(ctx, sendingCtx);
282282
}
283283
final Connection c = ctx.getConnection();
284+
final HttpContext httpCtx;
284285
if (!Utils.isSpdyConnection(c)) {
285-
HttpContext.newInstance(ctx, c, c, c);
286+
httpCtx = HttpContext.newInstance(c, c, c, requestPacketLocal);
286287
} else {
287288
SpdySession session = SpdySession.get(c);
288289
final Lock lock = session.getNewClientStreamLock();
289290
try {
290291
lock.lock();
291292
SpdyStream stream = session.openStream(requestPacketLocal, session.getNextLocalStreamId(), 0, 0, 0, false,
292293
!requestPacketLocal.isExpectContent());
293-
HttpContext.newInstance(ctx, stream, stream, stream);
294+
httpCtx = HttpContext.newInstance(stream, stream, stream, requestPacketLocal);
294295
} finally {
295296
lock.unlock();
296297
}
297298
}
299+
httpCtx.attach(ctx);
298300
HttpTxContext.set(ctx, httpTxContext);
301+
requestPacketLocal.getProcessingState().setHttpContext(httpCtx);
302+
requestPacketLocal.setConnection(c);
303+
299304
return sendRequest(sendingCtx, request, requestPacketLocal);
300305
}
301306

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2014 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
package org.asynchttpclient.providers.grizzly.filters.events;
15+
16+
import org.asynchttpclient.providers.grizzly.HttpTxContext;
17+
import org.glassfish.grizzly.filterchain.FilterChainEvent;
18+
19+
/**
20+
* {@link FilterChainEvent} to gracefully complete the request-response processing
21+
* when {@link Connection} is getting closed by the remote host.
22+
*
23+
* @since 1.8.7
24+
* @author The Grizzly Team
25+
*/
26+
public class GracefulCloseEvent implements FilterChainEvent {
27+
private final HttpTxContext httpTxContext;
28+
29+
public GracefulCloseEvent(HttpTxContext httpTxContext) {
30+
this.httpTxContext = httpTxContext;
31+
}
32+
33+
public HttpTxContext getHttpTxContext() {
34+
return httpTxContext;
35+
}
36+
37+
@Override
38+
public Object type() {
39+
return GracefulCloseEvent.class;
40+
}
41+
}

0 commit comments

Comments
 (0)