public static Route route(Http.Request request) { if (Logger.isTraceEnabled()) { Logger.trace("Route: " + request.path + " - " + request.querystring); } // request method may be overriden if a x-http-method-override parameter is given if (request.querystring != null && methodOverride.matches(request.querystring)) { Matcher matcher = methodOverride.matcher(request.querystring); if (matcher.matches()) { if (Logger.isTraceEnabled()) { Logger.trace( "request method %s overriden to %s ", request.method, matcher.group("method")); } request.method = matcher.group("method"); } } for (Route route : routes) { Map<String, String> args = route.matches(request.method, request.path, request.format, request.domain); if (args != null) { request.routeArgs = args; request.action = route.action; if (args.containsKey("format")) { request.format = args.get("format"); } if (request.action.indexOf("{") > -1) { // more optimization ? for (String arg : request.routeArgs.keySet()) { request.action = request.action.replace("{" + arg + "}", request.routeArgs.get(arg)); } } if (request.action.equals("404")) { throw new NotFound(route.path); } return route; } } // Not found - if the request was a HEAD, let's see if we can find a corresponding GET if (request.method.equalsIgnoreCase("head")) { request.method = "GET"; Route route = route(request); request.method = "HEAD"; if (route != null) { return route; } } throw new NotFound(request.method, request.path); }
private void websocketHandshake(final ChannelHandlerContext ctx, HttpRequest req, MessageEvent e) throws Exception { // Create the WebSocket handshake response. HttpResponse res = new DefaultHttpResponse( HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake")); res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET); res.addHeader(CONNECTION, HttpHeaders.Values.UPGRADE); // Fill in the headers and contents depending on handshake method. if (req.containsHeader(SEC_WEBSOCKET_KEY1) && req.containsHeader(SEC_WEBSOCKET_KEY2)) { // New handshake method with a challenge: res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); res.addHeader( SEC_WEBSOCKET_LOCATION, "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri()); String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(SEC_WEBSOCKET_PROTOCOL, protocol); } // Calculate the answer of the challenge. String key1 = req.getHeader(SEC_WEBSOCKET_KEY1); String key2 = req.getHeader(SEC_WEBSOCKET_KEY2); int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1.replaceAll("[^ ]", "").length()); int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2.replaceAll("[^ ]", "").length()); long c = req.getContent().readLong(); ChannelBuffer input = ChannelBuffers.buffer(16); input.writeInt(a); input.writeInt(b); input.writeLong(c); try { ChannelBuffer output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array())); res.setContent(output); } catch (NoSuchAlgorithmException ex) { throw new UnexpectedException(ex); } } else { // Old handshake method with no challenge: res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); res.addHeader( WEBSOCKET_LOCATION, "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri()); String protocol = req.getHeader(WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(WEBSOCKET_PROTOCOL, protocol); } } // Keep the original request Http.Request request = parseRequest(ctx, req); // Route the websocket request request.method = "WS"; Map<String, String> route = Router.route(request.method, request.path); if (!route.containsKey("action")) { // No route found to handle this websocket connection ctx.getChannel() .write(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND)); return; } // Upgrade the connection and send the handshake response. ChannelPipeline p = ctx.getChannel().getPipeline(); p.remove("aggregator"); p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder()); // Connect ctx.getChannel().write(res); p.replace("encoder", "wsencoder", new WebSocketFrameEncoder()); req.setMethod(new HttpMethod("WEBSOCKET")); // Inbound Http.Inbound inbound = new Http.Inbound() { @Override public boolean isOpen() { return ctx.getChannel().isOpen(); } }; channels.put(ctx, inbound); // Outbound Http.Outbound outbound = new Http.Outbound() { final List<ChannelFuture> writeFutures = Collections.synchronizedList(new ArrayList<ChannelFuture>()); Promise<Void> closeTask; synchronized void writeAndClose(ChannelFuture writeFuture) { if (!writeFuture.isDone()) { writeFutures.add(writeFuture); writeFuture.addListener( new ChannelFutureListener() { public void operationComplete(ChannelFuture cf) throws Exception { writeFutures.remove(cf); futureClose(); } }); } } void futureClose() { if (closeTask != null && writeFutures.isEmpty()) { closeTask.invoke(null); } } @Override public void send(String data) { if (!isOpen()) { throw new IllegalStateException("The outbound channel is closed"); } writeAndClose(ctx.getChannel().write(new DefaultWebSocketFrame(data))); } @Override public void send(byte opcode, byte[] data, int offset, int length) { if (!isOpen()) { throw new IllegalStateException("The outbound channel is closed"); } writeAndClose( ctx.getChannel() .write(new DefaultWebSocketFrame(opcode, wrappedBuffer(data, offset, length)))); } @Override public synchronized boolean isOpen() { return ctx.getChannel().isOpen() && closeTask == null; } @Override public synchronized void close() { closeTask = new Promise<Void>(); closeTask.onRedeem( new Action<Promise<Void>>() { public void invoke(Promise<Void> completed) { writeFutures.clear(); ctx.getChannel().disconnect(); closeTask = null; } }); futureClose(); } }; Invoker.invoke(new WebSocketInvocation(route, request, inbound, outbound, ctx, e)); }