/* * Copyright 2015 Netflix, Inc. * * Licensed 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 io.reactivex.netty.protocol.http.ws.server; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketVersion; import io.netty.util.internal.StringUtil; import io.reactivex.netty.protocol.http.server.HttpServerRequest; import io.reactivex.netty.protocol.http.server.HttpServerResponse; import rx.Observable; import rx.Subscriber; import static io.netty.handler.codec.http.HttpHeaderNames.*; import static io.netty.handler.codec.http.HttpHeaderValues.*; /** * The websocket handshaker for sending handshake response back to the client. * * The defaults chosen by the handshaker can be altered by using the various methods here. */ public abstract class WebSocketHandshaker extends Observable<Void> { public static final int DEFAULT_MAX_FRAME_PAYLOAD_LENGTH = 65536; public static final boolean DEFAULT_ALLOW_EXTENSIONS = true; protected WebSocketHandshaker(OnSubscribe<Void> f) { super(f); } public abstract WebSocketHandshaker subprotocol(String... subprotocols); public abstract WebSocketHandshaker allowExtensions(boolean allowExtensions); public abstract WebSocketHandshaker location(String webSocketLocation); public abstract WebSocketHandshaker maxFramePayloadLength(int maxFramePayloadLength); public static WebSocketHandshaker newHandshaker(HttpServerRequest<?> request, HttpServerResponse<?> upgradeResponse, WebSocketHandler handler) { final WebSocketVersion wsVersion = getWsVersion(request); return V7to13Handshaker.createNew(wsVersion, request, upgradeResponse, handler); } public static WebSocketHandshaker newErrorHandshaker(Throwable error) { return new ErrorWebSocketHandshaker(error); } /** * <b>This is copied from {@link WebSocketServerHandshaker}</b> * * Selects the first matching supported sub protocol * * @param requestedSubprotocols CSV of protocols to be supported. e.g. "chat, superchat" * @return First matching supported sub protocol. Null if not found. */ protected static String selectSubprotocol(String requestedSubprotocols, String[] supportedSubProtocols) { if (requestedSubprotocols == null || supportedSubProtocols.length == 0) { return null; } String[] requestedSubprotocolArray = requestedSubprotocols.split(","); for (String p: requestedSubprotocolArray) { String requestedSubprotocol = p.trim(); for (String supportedSubprotocol: supportedSubProtocols) { if (WebSocketServerHandshaker.SUB_PROTOCOL_WILDCARD.equals(supportedSubprotocol) || requestedSubprotocol.equals(supportedSubprotocol)) { return requestedSubprotocol; } } } // No match found return null; } public static boolean isUpgradeRequested(HttpServerRequest<?> upgradeRequest) { return null != upgradeRequest && upgradeRequest.containsHeader(HttpHeaderNames.UPGRADE) && WEBSOCKET.contentEqualsIgnoreCase(upgradeRequest.getHeader(HttpHeaderNames.UPGRADE)); } public static boolean isUpgradeRequested(HttpRequest upgradeRequest) { return null != upgradeRequest && upgradeRequest.headers().contains(HttpHeaderNames.UPGRADE) && WEBSOCKET.contentEqualsIgnoreCase(upgradeRequest.headers() .get(HttpHeaderNames.UPGRADE)); } private static WebSocketVersion getWsVersion(HttpServerRequest<?> request) { String version = request.getHeader(SEC_WEBSOCKET_VERSION); switch (version) { case "0": return WebSocketVersion.V00; case "7": return WebSocketVersion.V07; case "8": return WebSocketVersion.V08; case "13": return WebSocketVersion.V13; default: return WebSocketVersion.UNKNOWN; } } private static class ErrorWebSocketHandshaker extends WebSocketHandshaker { public ErrorWebSocketHandshaker(final Throwable error) { super(new OnSubscribe<Void>() { @Override public void call(Subscriber<? super Void> subscriber) { subscriber.onError(error); } }); } @Override public WebSocketHandshaker subprotocol(String... subprotocols) { return this; } @Override public WebSocketHandshaker allowExtensions(boolean allowExtensions) { return this; } @Override public WebSocketHandshaker location(String webSocketLocation) { return this; } @Override public WebSocketHandshaker maxFramePayloadLength(int maxFramePayloadLength) { return this; } } }