All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jsync.sockjs.impl.BaseTransport Maven / Gradle / Ivy

There is a newer version: 1.10.13
Show newest version
/*
 * Copyright (c) 2011-2013 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.jsync.sockjs.impl;

import io.jsync.Handler;
import io.jsync.MultiMap;
import io.jsync.VoidHandler;
import io.jsync.http.HttpServerRequest;
import io.jsync.http.HttpServerResponse;
import io.jsync.impl.AsyncInternal;
import io.jsync.json.JsonArray;
import io.jsync.json.JsonObject;
import io.jsync.logging.Logger;
import io.jsync.logging.impl.LoggerFactory;
import io.jsync.sockjs.SockJSSocket;
import io.jsync.utils.StringEscapeUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Random;

/**
 * @author Tim Fox
 */
class BaseTransport {

    protected static final String COMMON_PATH_ELEMENT_RE = "\\/[^\\/\\.]+\\/([^\\/\\.]+)\\/";
    private static final Logger log = LoggerFactory.getLogger(BaseTransport.class);
    private static final long RAND_OFFSET = 2L << 30;
    protected final AsyncInternal async;
    protected final Map sessions;
    protected JsonObject config;

    public BaseTransport(AsyncInternal async, Map sessions, JsonObject config) {
        this.async = async;
        this.sessions = sessions;
        this.config = config;
    }

    static void setJSESSIONID(JsonObject config, HttpServerRequest req) {
        String cookies = req.headers().get("cookie");
        if (config.getBoolean("insert_JSESSIONID")) {
            //Preserve existing JSESSIONID, if any
            if (cookies != null) {
                String[] parts;
                if (cookies.contains(";")) {
                    parts = cookies.split(";");
                } else {
                    parts = new String[]{cookies};
                }
                for (String part : parts) {
                    if (part.startsWith("JSESSIONID")) {
                        cookies = part + "; path=/";
                        break;
                    }
                }
            }
            if (cookies == null) {
                cookies = "JSESSIONID=dummy; path=/";
            }
            req.response().headers().set("Set-Cookie", cookies);
        }
    }

    static void setCORS(HttpServerRequest req) {
        String origin = req.headers().get("origin");
        if (origin == null || "null".equals(origin)) {
            origin = "*";
        }
        req.response().headers().set("Access-Control-Allow-Origin", origin);
        req.response().headers().set("Access-Control-Allow-Credentials", "true");
        String hdr = req.headers().get("Access-Control-Request-Headers");
        if (hdr != null) {
            req.response().headers().set("Access-Control-Allow-Headers", hdr);
        }
    }

    static Handler createInfoHandler(final JsonObject config) {
        return new Handler() {
            boolean websocket = !config.getArray("disabled_transports").contains(Transport.WEBSOCKET.toString());

            public void handle(HttpServerRequest req) {
                if (log.isTraceEnabled()) log.trace("In Info handler");
                req.response().headers().set("Content-Type", "application/json; charset=UTF-8");
                setNoCacheHeaders(req);
                JsonObject json = new JsonObject();
                json.putBoolean("websocket", websocket);
                json.putBoolean("cookie_needed", config.getBoolean("insert_JSESSIONID"));
                json.putArray("origins", new JsonArray().add("*:*"));
                // Java ints are signed, so we need to use a long and add the offset so
                // the result is not negative
                json.putNumber("entropy", RAND_OFFSET + new Random().nextInt());
                setCORS(req);
                req.response().end(json.encode());
            }
        };
    }

    static void setNoCacheHeaders(HttpServerRequest req) {
        req.response().headers().set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
    }

    static Handler createCORSOptionsHandler(final JsonObject config, final String methods) {
        return new Handler() {
            public void handle(HttpServerRequest req) {
                if (log.isTraceEnabled()) log.trace("In CORS options handler");
                req.response().headers().set("Cache-Control", "public,max-age=31536000");
                long oneYearSeconds = 365 * 24 * 60 * 60;
                long oneYearms = oneYearSeconds * 1000;
                String expires = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz").format(new Date(System.currentTimeMillis() + oneYearms));
                req.response().headers().set("Expires", expires);
                req.response().headers().set("Access-Control-Allow-Methods", methods);
                req.response().headers().set("Access-Control-Max-Age", String.valueOf(oneYearSeconds));
                setCORS(req);
                setJSESSIONID(config, req);
                req.response().setStatusCode(204);
                req.response().end();
            }
        };
    }

    // We remove cookie headers for security reasons. See https://github.com/sockjs/sockjs-node section on
    // Authorisation
    static MultiMap removeCookieHeaders(MultiMap headers) {
        return headers.remove("cookie");
    }

    protected Session getSession(final long timeout, final long heartbeatPeriod, final String sessionID,
                                 Handler sockHandler) {
        Session session = sessions.get(sessionID);
        if (session == null) {
            session = new Session(async, sessions, sessionID, timeout, heartbeatPeriod, sockHandler);
            sessions.put(sessionID, session);
        }
        return session;
    }

    protected void sendInvalidJSON(HttpServerResponse response) {
        if (log.isTraceEnabled()) log.trace("Broken JSON");
        response.setStatusCode(500);
        response.end("Broken JSON encoding.");
    }

    protected String escapeForJavaScript(String str) {
        try {
            str = StringEscapeUtils.escapeJavaScript(str);
        } catch (Exception e) {
            log.error("Failed to escape", e);
            str = null;
        }
        return str;
    }

    protected static abstract class BaseListener implements TransportListener {

        protected void addCloseHandler(HttpServerResponse resp, final Session session) {
            resp.closeHandler(new VoidHandler() {
                public void handle() {
                    if (log.isTraceEnabled()) log.trace("Connection closed (from client?), closing session");
                    // Connection has been closed fron the client or network error so
                    // we remove the session
                    session.shutdown();
                    close();
                }
            });
        }

        public void sessionClosed() {
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy