private void handleHttp(final ChannelHandlerContext ctx, final MessageEvent messageEvent) throws URISyntaxException, IOException {
boolean skipClose = false;
AtmosphereResponse response = null;
AtmosphereRequest request = null;
Action a = null;
boolean resumeOnBroadcast = false;
boolean keptOpen = false;
ChannelWriter asyncWriter = null;
String method = "GET";
boolean writeHeader = false;
boolean forceSuspend = false;
boolean aggregateBodyInMemory = config.aggregateRequestBodyInMemory();
try {
if (messageEvent.getMessage() instanceof HttpRequest) {
final HttpRequest hrequest = (HttpRequest) messageEvent.getMessage();
boolean ka = HttpHeaders.isKeepAlive(hrequest);
asyncWriter = config.supportChunking() ?
new ChunkedWriter(ctx.getChannel(), true, ka, channelBufferPool) :
new StreamWriter(ctx.getChannel(), true, ka);
method = hrequest.getMethod().getName();
// First let's try to see if it's a static resources
if (!hrequest.getUri().contains(HeaderConfig.X_ATMOSPHERE)) {
try {
hrequest.headers().add(STATIC_MAPPING, "true");
super.messageReceived(ctx, messageEvent);
if (HttpHeaders.getHeader(hrequest, SERVICED) != null) {
return;
}
} catch (Exception e) {
logger.debug("Unexpected State", e);
} finally {
hrequest.headers().set(STATIC_MAPPING, "false");
}
}
request = createAtmosphereRequest(ctx, hrequest);
request.setAttribute(KEEP_ALIVE, new Boolean(ka));
// Hacky. Is the POST doesn't contains a body, we must not close the connection yet.
AtmosphereRequest.Body b = request.body();
if (!aggregateBodyInMemory
&& !hrequest.getMethod().equals(GET)
&& !b.isEmpty()
&& (b.hasString() && b.asString().isEmpty())
|| (b.hasBytes() && b.asBytes().length == 0)) {
forceSuspend = true;
}
} else {
request = State.class.cast(ctx.getAttachment()).request;
boolean isLast = HttpChunk.class.cast(messageEvent.getMessage()).isLast();
Boolean ka = (Boolean) request.getAttribute(KEEP_ALIVE);
asyncWriter = config.supportChunking() ?
new ChunkedWriter(ctx.getChannel(), isLast, ka, channelBufferPool) :
new StreamWriter(ctx.getChannel(), isLast, ka);
method = request.getMethod();
ChannelBuffer internalBuffer = HttpChunk.class.cast(messageEvent.getMessage()).getContent();
if (!aggregateBodyInMemory && internalBuffer.hasArray()) {
request.body(internalBuffer.array());
} else {
logger.trace("Unable to read in memory the request's bytes. Using stream");
request.body(new ChannelBufferInputStream(internalBuffer));
}
if (!isLast) {
forceSuspend = true;
}
}
response = new AtmosphereResponse.Builder()
.asyncIOWriter(asyncWriter)
.writeHeader(writeHeader)
.destroyable(false)
.header("Connection", "Keep-Alive")
.header("Server", "Nettosphere/2.0")
.request(request).build();
if (config.supportChunking()) {
response.setHeader("Transfer-Encoding", "chunked");
}
a = framework.doCometSupport(request, response);
if (forceSuspend) {
a.type(Action.TYPE.SUSPEND);
// leave the stream open
keptOpen = true;
}
String transport = (String) request.getAttribute(FrameworkConfig.TRANSPORT_IN_USE);
if (transport == null) {
transport = request.getHeader(X_ATMOSPHERE_TRANSPORT);
}
if (a.type() == Action.TYPE.SUSPEND) {
if (transport != null && (transport.equalsIgnoreCase(HeaderConfig.STREAMING_TRANSPORT)
|| transport.equalsIgnoreCase(SSE_TRANSPORT))) {
keptOpen = true;
} else if (transport != null && (
transport.equalsIgnoreCase(HeaderConfig.LONG_POLLING_TRANSPORT) ||
transport.equalsIgnoreCase(HeaderConfig.JSONP_TRANSPORT))) {
resumeOnBroadcast = true;
}
}
final Action action = (Action) request.getAttribute(NettyCometSupport.SUSPEND);
final State state = new State(request, action == null ? Action.CONTINUE : action);
ctx.setAttachment(state);
if (action != null && action.type() == Action.TYPE.SUSPEND) {
if (action.timeout() != -1) {
final AtomicReference<ChannelWriter> w = new AtomicReference<ChannelWriter>(asyncWriter);
final AtomicReference<Future<?>> f = new AtomicReference<Future<?>>();
f.set(suspendTimer.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!w.get().isClosed() && (System.currentTimeMillis() - w.get().lastTick()) > action.timeout()) {
AtmosphereResourceImpl impl = state.resource();
if (impl != null) {
asynchronousProcessor.endRequest(impl, false);
f.get().cancel(true);
}
}
}
}, action.timeout(), action.timeout(), TimeUnit.MILLISECONDS));
}
} else if (action != null && action.type() == Action.TYPE.RESUME) {
resumeOnBroadcast = false;
}
} catch (AtmosphereMappingException ex) {
if (method.equalsIgnoreCase("GET")) {
logger.trace("Unable to map the request {}, trying static file", messageEvent.getMessage());
try {
skipClose = true;
super.messageReceived(ctx, messageEvent);
} catch (Exception e) {
logger.error("Unable to process request", e);
throw new IOException(e);
}
}
} catch (Throwable e) {
logger.error("Unable to process request", e);
throw new IOException(e);
} finally {
try {
if (asyncWriter != null && !resumeOnBroadcast && !keptOpen) {
if (!skipClose && response != null) {
asyncWriter.close(response);
} else {
httpChannels.add(ctx.getChannel());
}
}
} finally {
if (request != null && a != null && a.type() != Action.TYPE.SUSPEND) {
request.destroy();
response.destroy();
framework.notify(Action.TYPE.DESTROYED, request, response);
}
}
}