/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.jboss.aerogear.io.netty.handler.codec.sockjs.handler;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_CREDENTIALS;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_METHODS;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_MAX_AGE;
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_REQUEST_HEADERS;
import static io.netty.handler.codec.http.HttpHeaders.Names.CACHE_CONTROL;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaders.Names.EXPIRES;
import static io.netty.handler.codec.http.HttpHeaders.Names.ORIGIN;
import static io.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE;
import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.transport.Transports;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
/**
* Handles CORS preflight requests for the sockjs-protocol.
*
*/
public class CorsInboundHandler extends SimpleChannelInboundHandler<HttpRequest> {
static final AttributeKey<CorsMetadata> CORS = AttributeKey.valueOf("cors.metadata");
@Override
public void messageReceived(final ChannelHandlerContext ctx, final HttpRequest request) throws Exception {
final CorsMetadata metadata = extractCorsMetadata(request);
if (isPreflightRequest(request)) {
handlePreflight(ctx, metadata, request);
} else {
ctx.channel().attr(CORS).set(metadata);
ctx.fireChannelRead(ReferenceCountUtil.retain(request));
}
}
private static void handlePreflight(final ChannelHandlerContext ctx, final CorsMetadata md,
final HttpRequest request) {
final HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), NO_CONTENT);
final HttpHeaders headers = response.headers();
headers.set(CONTENT_TYPE, Transports.CONTENT_TYPE_PLAIN);
headers.set(CACHE_CONTROL, "max-age=31536000, public");
headers.set(ACCESS_CONTROL_ALLOW_ORIGIN, md.origin());
headers.set(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.set(ACCESS_CONTROL_MAX_AGE, "31536000");
if (isPollingTransport(request.getUri())) {
headers.set(ACCESS_CONTROL_ALLOW_METHODS, "OPTIONS, POST");
} else {
headers.set(ACCESS_CONTROL_ALLOW_METHODS, "OPTIONS, GET");
}
headers.set(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type");
headers.set(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.set(EXPIRES, "dummy");
headers.set(SET_COOKIE, Transports.DEFAULT_COOKIE);
ctx.writeAndFlush(response);
}
private static boolean isPollingTransport(final String uri) {
return uri.contains(Transports.Type.XHR.path());
}
private static boolean isPreflightRequest(final HttpRequest request) {
return request.getMethod().equals(HttpMethod.OPTIONS);
}
private static CorsMetadata extractCorsMetadata(final HttpRequest request) {
final String origin = request.headers().get(ORIGIN);
final String headers = request.headers().get(ACCESS_CONTROL_REQUEST_HEADERS);
return new CorsMetadata(origin, headers);
}
}