private static void parseHeaders(@NotNull HttpResponse response, @NotNull ByteBuf buffer) { StringBuilder builder = new StringBuilder(); while (buffer.isReadable()) { builder.setLength(0); String key = null; boolean valueExpected = true; while (true) { int b = buffer.readByte(); if (b < 0 || b == '\n') { break; } if (b != '\r') { if (valueExpected && b == ':') { valueExpected = false; key = builder.toString(); builder.setLength(0); MessageDecoder.skipWhitespace(buffer); } else { builder.append((char) b); } } } if (builder.length() == 0) { // end of headers return; } // skip standard headers if (StringUtil.isEmpty(key) || StringUtilRt.startsWithIgnoreCase(key, "http") || StringUtilRt.startsWithIgnoreCase(key, "X-Accel-")) { continue; } String value = builder.toString(); if (key.equalsIgnoreCase("status")) { int index = value.indexOf(' '); if (index == -1) { LOG.warn("Cannot parse status: " + value); response.setStatus(HttpResponseStatus.OK); } else { response.setStatus( HttpResponseStatus.valueOf(Integer.parseInt(value.substring(0, index)))); } } else if (!(key.startsWith("http") || key.startsWith("HTTP"))) { response.headers().add(key, value); } } }
@Override public void fatalError(String message, String stack) { if (log.isDebugEnabled()) { log.debug("Sending HTTP error due to script error {}", message); } StringBuilder msg = new StringBuilder(message); if (stack != null) { msg.append('\n'); msg.append(stack); } ByteBuf data = Unpooled.copiedBuffer(msg, Charsets.UTF8); response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR); response.headers().add("Content-Type", "text/plain"); response.headers().add("Content-Length", data.readableBytes()); calculateKeepAlive(true); channel.write(response); DefaultHttpContent chunk = new DefaultHttpContent(data); channel.write(chunk); sendLastChunk(); channel.flush(); if (!keepAlive) { shutDown(); } }
/** * Handle the web socket handshake for the web socket specification <a href= * "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17">HyBi versions 13-17</a>. * Versions 13-17 share the same wire protocol. * * <p>Browser request to the server: * * <pre> * GET /chat HTTP/1.1 * Host: server.example.com * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== * Sec-WebSocket-Origin: http://example.com * Sec-WebSocket-Protocol: chat, superchat * Sec-WebSocket-Version: 13 * </pre> * * <p>Server response: * * <pre> * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= * Sec-WebSocket-Protocol: chat * </pre> * * @param channel Channel * @param req HTTP request */ @Override public ChannelFuture handshake(Channel channel, HttpRequest req) { if (logger.isDebugEnabled()) { logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.getId())); } HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID; byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); String accept = WebSocketUtil.base64(sha1); if (logger.isDebugEnabled()) { logger.debug( String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept)); } res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); res.addHeader(Names.CONNECTION, Names.UPGRADE); res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); } ChannelFuture future = channel.write(res); // Upgrade the connection and send the handshake response. ChannelPipeline p = channel.getPipeline(); if (p.get(HttpChunkAggregator.class) != null) { p.remove(HttpChunkAggregator.class); } p.replace( HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, allowExtensions, this.getMaxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); return future; }
@Override public void setStatusCode(int code) { response.setStatus(HttpResponseStatus.valueOf(code)); }
@Override public AssembledHttpResponse setStatus(HttpResponseStatus status) { response.setStatus(status); return this; }
@Override public void setStatus(ResponseStatus status) throws RestServiceException { responseMetadata.setStatus(getHttpResponseStatus(status)); logger.trace( "Set status to {} for response on channel {}", responseMetadata.getStatus(), ctx.channel()); }
@Override public void setStatus(int sc, String sm) { response.setStatus(new HttpResponseStatus(sc, sm)); }
@Override public void setStatus(int sc) { response.setStatus(HttpResponseStatus.valueOf(sc)); }
@Override public void sendError(int sc) throws IOException { checkNotCommitted(); response.setStatus(HttpResponseStatus.valueOf(sc)); }
@Override public void sendError(int sc, String msg) throws IOException { checkNotCommitted(); response.setStatus(new HttpResponseStatus(sc, msg)); }