Package org.jboss.aerogear.io.netty.handler.codec.sockjs.handler

Source Code of org.jboss.aerogear.io.netty.handler.codec.sockjs.handler.Iframe

/*
* 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.buffer.Unpooled.copiedBuffer;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_MODIFIED;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.util.CharsetUtil.UTF_8;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsConfig;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Formatter;
import java.util.regex.Pattern;

/**
* IFrame is a way to get around problems in browsers where the streaming protocols do not support
* cross domain communication.
*
* The SockJS client library can in these cases issue a request with a path starting
* with '/iframe'. The class will respond with a iframe that contains SockJS JavaScript
* which is then able to do cross domain calls.
*/
final class Iframe {

    private static final Pattern PATH_PATTERN = Pattern.compile(".*/iframe[0-9-.a-z_]*.html");
    private static final long ONE_YEAR = 31536000000L;
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
        }
    };
    private static final ThreadLocal<MessageDigest> MESSAGE_DIGEST = new ThreadLocal<MessageDigest>() {
        @Override
        protected MessageDigest initialValue() {
            try {
                return MessageDigest.getInstance("MD5");
            } catch (final NoSuchAlgorithmException e) {
                throw new IllegalStateException("Could not create a new MD5 instance", e);
            }
        }
    };

    private Iframe() {
    }

    public static boolean matches(final String path) {
        return path.startsWith("/iframe");
    }

    public static FullHttpResponse response(final SockJsConfig config, final HttpRequest request) throws Exception {
        final QueryStringDecoder qsd = new QueryStringDecoder(request.getUri());
        final String path = qsd.path();

        if (!PATH_PATTERN.matcher(path).matches()) {
            return createResponse(request, NOT_FOUND, copiedBuffer("Not found", UTF_8));
        }

        if (request.headers().contains(HttpHeaders.Names.IF_NONE_MATCH)) {
            final FullHttpResponse response = createResponse(request, NOT_MODIFIED);
            response.headers().set(HttpHeaders.Names.SET_COOKIE, "JSESSIONID=dummy; path=/");
            return response;
        } else {
            final String content = createContent(config.sockJsUrl());
            final FullHttpResponse response = createResponse(request, OK, copiedBuffer(content, UTF_8));
            response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8");
            response.headers().set(HttpHeaders.Names.CACHE_CONTROL, "max-age=31536000, public");
            response.headers().set(HttpHeaders.Names.EXPIRES, generateExpires());
            final String etag = '\"' + generateMd5(content) + '\"';
            response.headers().set(HttpHeaders.Names.ETAG, etag);
            return response;
        }
    }

    private static String generateExpires() {
        return DATE_FORMAT.get().format(new Date(System.currentTimeMillis() + ONE_YEAR));
    }

    private static FullHttpResponse createResponse(final HttpRequest request, final HttpResponseStatus status) {
        return new DefaultFullHttpResponse(request.getProtocolVersion(), status);
    }

    private static FullHttpResponse createResponse(final HttpRequest request, final HttpResponseStatus status,
            final ByteBuf content) {
        return new DefaultFullHttpResponse(request.getProtocolVersion(), status, content);
    }

    private static String createContent(final String url) {
        return "<!DOCTYPE html>\n" +
        "<html>\n" +
        "<head>\n" +
        "  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n" +
        "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
        "  <script>\n" +
        "    document.domain = document.domain;\n" +
        "    _sockjs_onload = function(){SockJS.bootstrap_iframe();};\n" +
        "  </script>\n" +
        "  <script src=\"" + url + "\"></script>\n" +
        "</head>\n" +
        "<body>\n" +
        "  <h2>Don't panic!</h2>\n" +
        "  <p>This is a SockJS hidden iframe. It's used for cross domain magic.</p>\n" +
        "</body>\n" +
        "</html>";
    }

    private static String generateMd5(final String value) throws Exception {
        final byte[] digest = MESSAGE_DIGEST.get().digest(value.getBytes(UTF_8));
        Formatter formatter = null;
        try {
            formatter = new Formatter();
            for (byte b : digest) {
                formatter.format("%02x", b);
            }
            return formatter.toString().toLowerCase();
        } finally {
            if (formatter != null) {
                formatter.close();
            }
        }
    }

}
TOP

Related Classes of org.jboss.aerogear.io.netty.handler.codec.sockjs.handler.Iframe

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.