private static void sendStatus( HttpResponse response, @Nullable HttpRequest request, Channel channel, @Nullable String description) { response.setHeader(CONTENT_TYPE, "text/html"); if (request == null || request.getMethod() != HttpMethod.HEAD) { String message = response.getStatus().toString(); StringBuilder builder = new StringBuilder(); builder .append("<!doctype html><title>") .append(message) .append("</title>") .append("<h1 style=\"text-align: center\">") .append(message) .append("</h1>"); if (description != null) { builder.append("<p>").append(description).append("</p>"); } builder .append("<hr/><p style=\"text-align: center\">") .append(StringUtil.notNullize(getServerHeaderValue(), "")) .append("</p>"); response.setContent(ChannelBuffers.copiedBuffer(builder, CharsetUtil.UTF_8)); } send(response, channel, request); }
public static HttpResponse buildHttpServletResponse(HttpResponseExchange forwardResponse) throws IOException { if (null == forwardResponse) { return new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_TIMEOUT); } HttpResponse response = new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(forwardResponse.getResponseCode())); List<String[]> headers = forwardResponse.getHeaders(); for (String[] header : headers) { if (header[0].equalsIgnoreCase(HttpHeaders.Names.SET_COOKIE) || header[0].equalsIgnoreCase(HttpHeaders.Names.SET_COOKIE2)) { List<SetCookieHeaderValue> cookies = SetCookieHeaderValue.parse(header[1]); for (SetCookieHeaderValue cookie : cookies) { response.addHeader(header[0], cookie.toString()); } } else { response.addHeader(header[0], header[1]); } } byte[] content = forwardResponse.getBody(); if (null != content) { ChannelBuffer bufer = ChannelBuffers.wrappedBuffer(content); response.setContent(bufer); } return response; }
public static void serve404( NotFound e, ChannelHandlerContext ctx, Request request, HttpRequest nettyRequest) { Logger.trace("serve404: begin"); HttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); nettyResponse.setHeader(SERVER, signature); nettyResponse.setHeader(CONTENT_TYPE, "text/html"); Map<String, Object> binding = getBindingForErrors(e, false); String format = Request.current().format; if (format == null) { format = "txt"; } nettyResponse.setHeader( CONTENT_TYPE, (MimeTypes.getContentType("404." + format, "text/plain"))); String errorHtml = TemplateLoader.load("errors/404." + format).render(binding); try { ChannelBuffer buf = ChannelBuffers.copiedBuffer(errorHtml.getBytes("utf-8")); nettyResponse.setContent(buf); ChannelFuture writeFuture = ctx.getChannel().write(nettyResponse); writeFuture.addListener(ChannelFutureListener.CLOSE); } catch (UnsupportedEncodingException fex) { Logger.error(fex, "(utf-8 ?)"); } Logger.trace("serve404: end"); }
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { // Allow only GET methods. if (req.getMethod() != GET) { sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); return; } // Send the demo page and favicon.ico if (req.getUri().equals("/")) { HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK); ChannelBuffer content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); setContentLength(res, content.readableBytes()); res.setContent(content); sendHttpResponse(ctx, req, res); return; } else if (req.getUri().equals("/favicon.ico")) { HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND); sendHttpResponse(ctx, req, res); return; } // Handshake WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(this.getWebSocketLocation(req), null, false); this.handshaker = wsFactory.newHandshaker(ctx, req); if (this.handshaker == null) { wsFactory.sendUnsupportedWebSocketVersionResponse(ctx); } else { this.handshaker.executeOpeningHandshake(ctx, req); } }
protected static void writeResponse( ChannelHandlerContext ctx, Response response, HttpResponse nettyResponse, HttpRequest nettyRequest) { Logger.trace("writeResponse: begin"); byte[] content = null; final boolean keepAlive = isKeepAlive(nettyRequest); if (nettyRequest.getMethod().equals(HttpMethod.HEAD)) { content = new byte[0]; } else { content = response.out.toByteArray(); } ChannelBuffer buf = ChannelBuffers.copiedBuffer(content); nettyResponse.setContent(buf); if (keepAlive) { // Add 'Content-Length' header only for a keep-alive connection. Logger.trace("writeResponse: content length [" + response.out.size() + "]"); setContentLength(nettyResponse, response.out.size()); } ChannelFuture f = ctx.getChannel().write(nettyResponse); // Decide whether to close the connection or not. if (!keepAlive) { // Close the connection when the whole content is written out. f.addListener(ChannelFutureListener.CLOSE); } Logger.trace("writeResponse: end"); }
public void sendResponse(final MessageEvent e) { // Build the response object. final HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); final ByteArrayOutputStream bos = receivedData; try { bos.write(" successfully received by server".getBytes("UTF-8")); } catch (final UnsupportedEncodingException e1) { e1.printStackTrace(); } catch (final IOException e1) { e1.printStackTrace(); } final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(bos.toByteArray()); response.setContent(buffer); // response.setContent(arg0) // response.setContent(ChannelBuffers.copiedBuffer(buf.toString(), // CharsetUtil.UTF_8)); response.setHeader(CONTENT_TYPE, "application/octet-stream"); final ChannelFuture future = e.getChannel().write(response); // Close the non-keep-alive connection after the write operation is // done. // if (!keepAlive) { future.addListener(ChannelFutureListener.CLOSE); // } }
/** * Writes the specified TaskResult data to the channel output. Only the raw output data is written * and rest of the TaskResult fields are ignored * * @param ctx the ChannelHandlerContext * @param event the ChannelEvent * @throws Exception in case of any errors */ private void writeCommandExecutionResponse( ChannelHandlerContext ctx, ChannelEvent event, HttpResponse response) throws Exception { // Don't write anything if the response is null if (response == null) { // write empty response event .getChannel() .write(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NO_CONTENT)) .addListener(ChannelFutureListener.CLOSE); return; } org.jboss.netty.handler.codec.http.HttpResponse httpResponse = new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.getStatusLine().getStatusCode())); // write headers for (Header header : response.getAllHeaders()) { if (!RoutingHttpChannelHandler.REMOVE_HEADERS.contains(header.getName())) { httpResponse.setHeader(header.getName(), header.getValue()); } } // write entity HttpEntity responseEntity = response.getEntity(); byte[] responseData = EntityUtils.toByteArray(responseEntity); httpResponse.setContent(ChannelBuffers.copiedBuffer(responseData)); // write response event.getChannel().write(httpResponse).addListener(ChannelFutureListener.CLOSE); }
private void sendError(ChannelHandlerContext ctx, String message, HttpResponseStatus status) { HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status); response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); response.setContent(ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8)); // Close the connection as soon as the error message is sent. ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); }
public static void send( String contentType, CharSequence content, HttpRequest request, ChannelHandlerContext context) { HttpResponse response = create(contentType); response.setContent(ChannelBuffers.copiedBuffer(content, CharsetUtil.UTF_8)); send(response, request, context); }
private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8"); response.setContent( ChannelBuffers.copiedBuffer( "Failure: " + status.toString() + "\r\n", Charset.forName("UTF-8"))); // Close the connection as soon as the error message is sent. ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); }
public Deferred<HttpResponse> process(ChannelHandlerContext context, HttpRequest request) { HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK); response.setHeader("content-type", "text/html"); response.setContent( ChannelBuffers.copiedBuffer("Accepted ShutDown Request", Charset.defaultCharset())); Deferred<HttpResponse> deferred = new Deferred<HttpResponse>(); deferred.callback(response); shutdown(); super.doShutdown(context.getChannel()); return deferred; }
/* 此函数封装一个响应信息 * 当需要发送信息给用户时,此函数将被调用 * @param [res] 响应对象 * @param [setCookie] 是否需要强制浏览器设置cookie * @param [sessionId] 当setCookie为真时使用,服务器产生的sessionId * @param [content] 服务器响应内容 * @param [contentLength] 服务器响应内容长度 */ private void buildResponse( HttpResponse res, boolean setCookie, String sessionId, ChannelBuffer content, int contentLength) { if (setCookie == true) { res.setHeader(SET_COOKIE, sessionId); } res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); setContentLength(res, content.readableBytes()); res.setContent(content); }
private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { // Generate an error page if response status code is not OK (200). if (res.getStatus().getCode() != 200) { res.setContent(ChannelBuffers.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8)); setContentLength(res, res.getContent().readableBytes()); } // Send the response and close the connection if necessary. ChannelFuture f = ctx.getChannel().write(res); if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { f.addListener(ChannelFutureListener.CLOSE); } }
private void sendResponse( ChannelHandlerContext channel, HttpRequest request, String messageBody, HttpResponseStatus status) { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); if (messageBody != null && !messageBody.isEmpty()) { response.setContent(ChannelBuffers.copiedBuffer(messageBody, Constants.DEFAULT_CHARSET)); } Tracker.getInstance().trackResponse(request, response); HttpResponder.respond(channel, request, response); }
public void fire(Object something) { try { String result = (String) something; if (result.equals(RedisServer.TIME_OUT_STRING)) { logger.error("fire message timeout error, message id:" + getIdentify()); response.setContent( ChannelBuffers.wrappedBuffer( JResultUtil.getFailureJsonString(CoreProxyHandler.RESULT_TIMEOUT).getBytes())); response.setStatus(HttpResponseStatus.OK); } else { logger.info("fire message success, message id:" + getIdentify()); response.setContent( ChannelBuffers.wrappedBuffer(JResultUtil.getOkJsonString(result).getBytes())); response.setStatus(HttpResponseStatus.OK); } } catch (Exception e) { logger.error("fire message error, e:" + e.getMessage() + " result:" + something); response.setContent( ChannelBuffers.wrappedBuffer( JResultUtil.getFailureJsonString(CoreProxyHandler.RESULT_ERROR).getBytes())); response.setStatus(HttpResponseStatus.OK); } NettyHttpOutput.output(response, event.getChannel()); }
@Override public void messageReceived(ChannelHandlerContext context, MessageEvent e) throws Exception { if (e.getMessage() instanceof HttpRequest) { HttpRequest message = (HttpRequest) e.getMessage(); HttpResponse response; if (new QueryStringDecoder(message.getUri()).getPath().equals(START_TIME_PATH)) { response = new DefaultHttpResponse(HTTP_1_1, OK); response.setHeader("Access-Control-Allow-Origin", "*"); response.setContent( ChannelBuffers.copiedBuffer(getApplicationStartTime(), CharsetUtil.US_ASCII)); } else { response = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND); } context.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); } }
@Override public void service(ChannelHandlerContext context, HttpRequest request, HttpResponse response) { String path = request.getUri(); path = path.substring(getPrefix().length()).toLowerCase(); HashMap<String, String> params = getParams(request); String data = getData(path, params); ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ChannelBuffers.BIG_ENDIAN, data, UTF8); response.addHeader(HttpHeaders.Names.CONTENT_TYPE, CONTENT_TYPE); response.addHeader(HttpHeaders.Names.CONTENT_ENCODING, ENCODING); response.setContent(buffer); }
static void sendRedirect( final ChannelHandlerContext ctx, final ChannelEvent e, final Class<? extends ComponentId> compClass, final MappingHttpRequest request) { e.getFuture().cancel(); String redirectUri = null; if (Topology.isEnabled(compClass)) { // have an enabled service, lets use that final URI serviceUri = ServiceUris.remote(Topology.lookup(compClass)); redirectUri = serviceUri.toASCIIString() + request.getServicePath().replace(serviceUri.getPath(), ""); } else if (Topology.isEnabled( Eucalyptus.class)) { // can't find service info, redirect via clc master final URI serviceUri = ServiceUris.remote(Topology.lookup(Eucalyptus.class)); redirectUri = serviceUri.toASCIIString().replace(Eucalyptus.INSTANCE.getServicePath(), "") + request.getServicePath().replace(serviceUri.getPath(), ""); } HttpResponse response = null; if (redirectUri == null || isRedirectLoop(request, redirectUri)) { response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SERVICE_UNAVAILABLE); if (Logs.isDebug()) { String errorMessage = "Failed to lookup service for " + Components.lookup(compClass).getName() + " for path " + request.getServicePath() + ".\nCurrent state: \n\t" + Joiner.on("\n\t").join(Topology.enabledServices()); byte[] errorBytes = Exceptions.string(new ServiceStateException(errorMessage)).getBytes(); response.setContent(ChannelBuffers.wrappedBuffer(errorBytes)); } } else { response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.TEMPORARY_REDIRECT); if (request.getQuery() != null) { redirectUri += "?" + request.getQuery(); } response.setHeader(HttpHeaders.Names.LOCATION, redirectUri); } response.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); if (ctx.getChannel().isConnected()) { ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); } }
public HttpResponse generateResponse(HttpRequest request, String serverOrigin) throws Exception { HttpResponse response = new DefaultHttpResponse( HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake - IETF-00")); response.addHeader(HttpHeaders.Names.CONNECTION, "Upgrade"); response.addHeader(HttpHeaders.Names.UPGRADE, "WebSocket"); String origin = request.getHeader(Names.ORIGIN); if (origin == null) { origin = serverOrigin; } response.addHeader(Names.SEC_WEBSOCKET_ORIGIN, origin); response.addHeader(Names.SEC_WEBSOCKET_LOCATION, getWebSocketLocation(request)); String protocol = request.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); if (protocol != null) { response.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); } // Calculate the answer of the challenge. String key1 = request.getHeader(Names.SEC_WEBSOCKET_KEY1); String key2 = request.getHeader(Names.SEC_WEBSOCKET_KEY2); byte[] key3 = new byte[8]; request.getContent().readBytes(key3); byte[] solution = WebSocketChallenge00.solve(key1, key2, key3); ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(solution.length + 2); buffer.writeBytes(solution); response.addHeader("Content-Length", buffer.readableBytes()); response.setContent(buffer); response.setChunked(false); return response; }
@Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception { // store request, as this channel handler is created per pipeline HttpRequest request = (HttpRequest) messageEvent.getMessage(); LOG.debug("Message received: {}", request); HttpServerChannelHandler handler = getHandler(request); if (handler != null) { // store handler as attachment ctx.setAttachment(handler); handler.messageReceived(ctx, messageEvent); } else { // this service is not available, so send empty response back HttpResponse response = new DefaultHttpResponse(HTTP_1_1, SERVICE_UNAVAILABLE); response.setHeader(Exchange.CONTENT_TYPE, "text/plain"); response.setHeader(Exchange.CONTENT_LENGTH, 0); response.setContent(ChannelBuffers.copiedBuffer(new byte[] {})); messageEvent.getChannel().write(response); } }
public void writeResponse(byte[] responseValue) { this.responseContent = ChannelBuffers.dynamicBuffer(responseValue.length); this.responseContent.writeBytes(responseValue); // 1. Create the Response object HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); // 2. Set the right headers response.setHeader(CONTENT_TYPE, "binary"); response.setHeader(CONTENT_TRANSFER_ENCODING, "binary"); // 3. Copy the data into the payload response.setContent(responseContent); response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); if (logger.isDebugEnabled()) { logger.debug("Response = " + response); } // Write the response to the Netty Channel this.getRequestMessageEvent.getChannel().write(response); }
public void handleJsRequest(String path, HttpRequest req, HttpResponse resp) { if (req.containsHeader(IF_NONE_MATCH) || req.containsHeader(IF_MODIFIED_SINCE)) { resp.setStatus(HttpResponseStatus.NOT_MODIFIED); return; } String jsName = path.substring(1); ChannelBuffer buf = jsBufferMap.get(jsName); if (buf == null) { tryReloadJs(); if (jsName == null) { log.error("需要访问的JS名称为空。[req: " + req.getUri() + "]"); } buf = jsBufferMap.get(jsName); } if (buf != null) { resp.setContent(buf); resp.setHeader(CONTENT_LENGTH, buf.capacity()); resp.setHeader(CONTENT_TYPE, "text/javascript; charset=UTF-8"); resp.setHeader(CONTENT_ENCODING, "gzip"); // resp.setHeader(EXPIRES, System.currentTimeMillis() + expired); StringBuilder etagBuilder = new StringBuilder("W/\"") .append(buf.capacity()) .append("-") .append(System.currentTimeMillis()) .append("\""); resp.setHeader(ETAG, etagBuilder.toString()); LogRequestParser lrp = logRequestParserThreadLocal.get(); if (lrp == null) { lrp = new LogRequestParser(); } lrp.cdate.setTime(System.currentTimeMillis() + expired); resp.setHeader(EXPIRES, lrp.sdf.format(lrp.cdate)); } else { resp.setStatus(HttpResponseStatus.NOT_FOUND); } }
@Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (e.getMessage() instanceof HttpRequest && HttpMethod.GET.equals(((HttpRequest) e.getMessage()).getMethod())) { HttpRequest request = (HttpRequest) e.getMessage(); HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK); String resp = ""; for (Component c : Component.values()) { resp += String.format( "name=%-20.20s enabled=%-10.10s local=%-10.10s initialized=%-10.10s\n", c.name(), c.isEnabled(), c.isLocal(), c.isInitialized()); } ChannelBuffer buf = ChannelBuffers.copiedBuffer(resp.getBytes()); response.setContent(buf); response.addHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes())); response.addHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8"); ChannelFuture writeFuture = ctx.getChannel().write(response); writeFuture.addListener(ChannelFutureListener.CLOSE); } else { ctx.sendUpstream(e); } }
/** * Construct a proper HTTP response to a received request. After the response has been created, it * is sent and the resulting {@link ChannelFuture} object is returned. See <a * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a> for HTTP header * field definitions. * * @param output The {@link HttpResponse} object that will be used to construct the response. * @param e The {@link MessageEvent} object used to communicate with the client that sent the * request. * @param close Set to true to close the channel after sending the response. By default the * channel is not closed after sending. * @param startStopListenerDelegate The {@link StartStopListenerDelegate} object that is used to * notify plugins that the {@link DLNAResource} is about to start playing. * @return The {@link ChannelFuture} object via which the response was sent. * @throws IOException */ public ChannelFuture answer( HttpResponse output, MessageEvent e, final boolean close, final StartStopListenerDelegate startStopListenerDelegate) throws IOException { ChannelFuture future = null; long CLoverride = -2; // 0 and above are valid Content-Length values, -1 means omit StringBuilder response = new StringBuilder(); DLNAResource dlna = null; boolean xbox = mediaRenderer.isXBOX(); // Samsung 2012 TVs have a problematic preceding slash that needs to be removed. if (argument.startsWith("/")) { LOGGER.trace("Stripping preceding slash from: " + argument); argument = argument.substring(1); } if ((method.equals("GET") || method.equals("HEAD")) && argument.startsWith("console/")) { // Request to output a page to the HTLM console. output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html"); response.append(HTMLConsole.servePage(argument.substring(8))); } else if ((method.equals("GET") || method.equals("HEAD")) && argument.startsWith("get/")) { // Request to retrieve a file // Extract the resource id from the argument string. String id = argument.substring(argument.indexOf("get/") + 4, argument.lastIndexOf("/")); // Some clients escape the separators in their request, unescape them. id = id.replace("%24", "$"); // Retrieve the DLNAresource itself. List<DLNAResource> files = PMS.get().getRootFolder(mediaRenderer).getDLNAResources(id, false, 0, 0, mediaRenderer); if (transferMode != null) { output.setHeader("TransferMode.DLNA.ORG", transferMode); } if (files.size() == 1) { // DNLAresource was found. dlna = files.get(0); String fileName = argument.substring(argument.lastIndexOf("/") + 1); if (fileName.startsWith("thumbnail0000")) { // This is a request for a thumbnail file. output.setHeader(HttpHeaders.Names.CONTENT_TYPE, dlna.getThumbnailContentType()); output.setHeader(HttpHeaders.Names.ACCEPT_RANGES, "bytes"); output.setHeader(HttpHeaders.Names.EXPIRES, getFUTUREDATE() + " GMT"); output.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive"); if (mediaRenderer.isMediaParserV2()) { dlna.checkThumbnail(); } inputStream = dlna.getThumbnailInputStream(); } else if (fileName.indexOf("subtitle0000") > -1) { // This is a request for a subtitle file output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); output.setHeader(HttpHeaders.Names.EXPIRES, getFUTUREDATE() + " GMT"); List<DLNAMediaSubtitle> subs = dlna.getMedia().getSubtitlesCodes(); if (subs != null && !subs.isEmpty()) { // TODO: maybe loop subs to get the requested subtitle type instead of using the first // one DLNAMediaSubtitle sub = subs.get(0); inputStream = new java.io.FileInputStream(sub.getFile()); } } else { // This is a request for a regular file. // If range has not been initialized yet and the DLNAResource has its // own start and end defined, initialize range with those values before // requesting the input stream. Range.Time splitRange = dlna.getSplitRange(); if (range.getStart() == null && splitRange.getStart() != null) { range.setStart(splitRange.getStart()); } if (range.getEnd() == null && splitRange.getEnd() != null) { range.setEnd(splitRange.getEnd()); } inputStream = dlna.getInputStream( Range.create(lowRange, highRange, range.getStart(), range.getEnd()), mediaRenderer); // Some renderers (like Samsung devices) allow a custom header for a subtitle URL String subtitleHttpHeader = mediaRenderer.getSubtitleHttpHeader(); if (subtitleHttpHeader != null && !"".equals(subtitleHttpHeader)) { // Device allows a custom subtitle HTTP header; construct it List<DLNAMediaSubtitle> subs = dlna.getMedia().getSubtitlesCodes(); if (subs != null && !subs.isEmpty()) { DLNAMediaSubtitle sub = subs.get(0); int type = sub.getType(); if (type < DLNAMediaSubtitle.subExtensions.length) { String strType = DLNAMediaSubtitle.subExtensions[type - 1]; String subtitleUrl = "http://" + PMS.get().getServer().getHost() + ':' + PMS.get().getServer().getPort() + "/get/" + id + "/subtitle0000." + strType; output.setHeader(subtitleHttpHeader, subtitleUrl); } } } String name = dlna.getDisplayName(mediaRenderer); if (inputStream == null) { // No inputStream indicates that transcoding / remuxing probably crashed. LOGGER.error("There is no inputstream to return for " + name); } else { // Notify plugins that the DLNAresource is about to start playing startStopListenerDelegate.start(dlna); // Try to determine the content type of the file String rendererMimeType = getRendererMimeType(dlna.mimeType(), mediaRenderer); if (rendererMimeType != null && !"".equals(rendererMimeType)) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, rendererMimeType); } final DLNAMediaInfo media = dlna.getMedia(); if (media != null) { if (StringUtils.isNotBlank(media.getContainer())) { name += " [container: " + media.getContainer() + "]"; } if (StringUtils.isNotBlank(media.getCodecV())) { name += " [video: " + media.getCodecV() + "]"; } } PMS.get().getFrame().setStatusLine("Serving " + name); // Response generation: // We use -1 for arithmetic convenience but don't send it as a value. // If Content-Length < 0 we omit it, for Content-Range we use '*' to signify // unspecified. boolean chunked = mediaRenderer.isChunkedTransfer(); // Determine the total size. Note: when transcoding the length is // not known in advance, so DLNAMediaInfo.TRANS_SIZE will be returned instead. long totalsize = dlna.length(mediaRenderer); if (chunked && totalsize == DLNAMediaInfo.TRANS_SIZE) { // In chunked mode we try to avoid arbitrary values. totalsize = -1; } long remaining = totalsize - lowRange; long requested = highRange - lowRange; if (requested != 0) { // Determine the range (i.e. smaller of known or requested bytes) long bytes = remaining > -1 ? remaining : inputStream.available(); if (requested > 0 && bytes > requested) { bytes = requested + 1; } // Calculate the corresponding highRange (this is usually redundant). highRange = lowRange + bytes - (bytes > 0 ? 1 : 0); LOGGER.trace( (chunked ? "Using chunked response. " : "") + "Sending " + bytes + " bytes."); output.setHeader( HttpHeaders.Names.CONTENT_RANGE, "bytes " + lowRange + "-" + (highRange > -1 ? highRange : "*") + "/" + (totalsize > -1 ? totalsize : "*")); // Content-Length refers to the current chunk size here, though in chunked // mode if the request is open-ended and totalsize is unknown we omit it. if (chunked && requested < 0 && totalsize < 0) { CLoverride = -1; } else { CLoverride = bytes; } } else { // Content-Length refers to the total remaining size of the stream here. CLoverride = remaining; } // Calculate the corresponding highRange (this is usually redundant). highRange = lowRange + CLoverride - (CLoverride > 0 ? 1 : 0); if (contentFeatures != null) { output.setHeader("ContentFeatures.DLNA.ORG", dlna.getDlnaContentFeatures()); } output.setHeader(HttpHeaders.Names.ACCEPT_RANGES, "bytes"); output.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive"); } } } } else if ((method.equals("GET") || method.equals("HEAD")) && (argument.toLowerCase().endsWith(".png") || argument.toLowerCase().endsWith(".jpg") || argument.toLowerCase().endsWith(".jpeg"))) { if (argument.toLowerCase().endsWith(".png")) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "image/png"); } else { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "image/jpeg"); } output.setHeader(HttpHeaders.Names.ACCEPT_RANGES, "bytes"); output.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive"); output.setHeader(HttpHeaders.Names.EXPIRES, getFUTUREDATE() + " GMT"); inputStream = getResourceInputStream(argument); } else if ((method.equals("GET") || method.equals("HEAD")) && (argument.equals("description/fetch") || argument.endsWith("1.0.xml"))) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml; charset=\"utf-8\""); output.setHeader(HttpHeaders.Names.CACHE_CONTROL, "no-cache"); output.setHeader(HttpHeaders.Names.EXPIRES, "0"); output.setHeader(HttpHeaders.Names.ACCEPT_RANGES, "bytes"); output.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive"); inputStream = getResourceInputStream((argument.equals("description/fetch") ? "PMS.xml" : argument)); if (argument.equals("description/fetch")) { byte b[] = new byte[inputStream.available()]; inputStream.read(b); String s = new String(b); s = s.replace("[uuid]", PMS.get().usn()); // .substring(0, PMS.get().usn().length()-2)); String profileName = PMS.getConfiguration().getProfileName(); if (PMS.get().getServer().getHost() != null) { s = s.replace("[host]", PMS.get().getServer().getHost()); s = s.replace("[port]", "" + PMS.get().getServer().getPort()); } if (xbox) { LOGGER.debug("DLNA changes for Xbox 360"); s = s.replace( "Universal Media Server", "Universal Media Server [" + profileName + "] : Windows Media Connect"); s = s.replace( "<modelName>UMS</modelName>", "<modelName>Windows Media Connect</modelName>"); s = s.replace( "<serviceList>", "<serviceList>" + CRLF + "<service>" + CRLF + "<serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType>" + CRLF + "<serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId>" + CRLF + "<SCPDURL>/upnp/mrr/scpd</SCPDURL>" + CRLF + "<controlURL>/upnp/mrr/control</controlURL>" + CRLF + "</service>" + CRLF); } else { s = s.replace("Universal Media Server", "Universal Media Server [" + profileName + "]"); } if (!mediaRenderer.isPS3()) { // hacky stuff. replace the png icon by a jpeg one. Like mpeg2 remux, // really need a proper format compatibility list by renderer s = s.replace("<mimetype>image/png</mimetype>", "<mimetype>image/jpeg</mimetype>"); s = s.replace("/images/thumbnail-256.png", "/images/thumbnail-120.jpg"); s = s.replace(">256<", ">120<"); } response.append(s); inputStream = null; } } else if (method.equals("POST") && (argument.contains("MS_MediaReceiverRegistrar_control") || argument.contains("mrr/control"))) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml; charset=\"utf-8\""); response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); if (soapaction != null && soapaction.contains("IsAuthorized")) { response.append(HTTPXMLHelper.XBOX_2); response.append(CRLF); } else if (soapaction != null && soapaction.contains("IsValidated")) { response.append(HTTPXMLHelper.XBOX_1); response.append(CRLF); } response.append(HTTPXMLHelper.BROWSERESPONSE_FOOTER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } else if (method.equals("POST") && argument.endsWith("upnp/control/connection_manager")) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml; charset=\"utf-8\""); if (soapaction.indexOf("ConnectionManager:1#GetProtocolInfo") > -1) { response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.PROTOCOLINFO_RESPONSE); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } } else if (method.equals("POST") && argument.endsWith("upnp/control/content_directory")) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml; charset=\"utf-8\""); if (soapaction.indexOf("ContentDirectory:1#GetSystemUpdateID") > -1) { response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.GETSYSTEMUPDATEID_HEADER); response.append(CRLF); response.append("<Id>" + DLNAResource.getSystemUpdateId() + "</Id>"); response.append(CRLF); response.append(HTTPXMLHelper.GETSYSTEMUPDATEID_FOOTER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } else if (soapaction.indexOf("ContentDirectory:1#X_GetFeatureList") > -1) { // Added for Samsung 2012 TVs response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SAMSUNG_ERROR_RESPONSE); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } else if (soapaction.indexOf("ContentDirectory:1#GetSortCapabilities") > -1) { response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SORTCAPS_RESPONSE); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } else if (soapaction.indexOf("ContentDirectory:1#GetSearchCapabilities") > -1) { response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SEARCHCAPS_RESPONSE); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); } else if (soapaction.contains("ContentDirectory:1#Browse") || soapaction.contains("ContentDirectory:1#Search")) { objectID = getEnclosingValue(content, "<ObjectID>", "</ObjectID>"); String containerID = null; if ((objectID == null || objectID.length() == 0) && xbox) { containerID = getEnclosingValue(content, "<ContainerID>", "</ContainerID>"); if (!containerID.contains("$")) { objectID = "0"; } else { objectID = containerID; containerID = null; } } Object sI = getEnclosingValue(content, "<StartingIndex>", "</StartingIndex>"); Object rC = getEnclosingValue(content, "<RequestedCount>", "</RequestedCount>"); browseFlag = getEnclosingValue(content, "<BrowseFlag>", "</BrowseFlag>"); if (sI != null) { startingIndex = Integer.parseInt(sI.toString()); } if (rC != null) { requestCount = Integer.parseInt(rC.toString()); } response.append(HTTPXMLHelper.XML_HEADER); response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER); response.append(CRLF); if (soapaction.contains("ContentDirectory:1#Search")) { response.append(HTTPXMLHelper.SEARCHRESPONSE_HEADER); } else { response.append(HTTPXMLHelper.BROWSERESPONSE_HEADER); } response.append(CRLF); response.append(HTTPXMLHelper.RESULT_HEADER); response.append(HTTPXMLHelper.DIDL_HEADER); if (soapaction.contains("ContentDirectory:1#Search")) { browseFlag = "BrowseDirectChildren"; } // XBOX virtual containers ... d'oh! String searchCriteria = null; if (xbox && PMS.getConfiguration().getUseCache() && PMS.get().getLibrary() != null && containerID != null) { if (containerID.equals("7") && PMS.get().getLibrary().getAlbumFolder() != null) { objectID = PMS.get().getLibrary().getAlbumFolder().getResourceId(); } else if (containerID.equals("6") && PMS.get().getLibrary().getArtistFolder() != null) { objectID = PMS.get().getLibrary().getArtistFolder().getResourceId(); } else if (containerID.equals("5") && PMS.get().getLibrary().getGenreFolder() != null) { objectID = PMS.get().getLibrary().getGenreFolder().getResourceId(); } else if (containerID.equals("F") && PMS.get().getLibrary().getPlaylistFolder() != null) { objectID = PMS.get().getLibrary().getPlaylistFolder().getResourceId(); } else if (containerID.equals("4") && PMS.get().getLibrary().getAllFolder() != null) { objectID = PMS.get().getLibrary().getAllFolder().getResourceId(); } else if (containerID.equals("1")) { String artist = getEnclosingValue(content, "upnp:artist = "", "")"); if (artist != null) { objectID = PMS.get().getLibrary().getArtistFolder().getResourceId(); searchCriteria = artist; } } } List<DLNAResource> files = PMS.get() .getRootFolder(mediaRenderer) .getDLNAResources( objectID, browseFlag != null && browseFlag.equals("BrowseDirectChildren"), startingIndex, requestCount, mediaRenderer); if (searchCriteria != null && files != null) { for (int i = files.size() - 1; i >= 0; i--) { if (!files.get(i).getName().equals(searchCriteria)) { files.remove(i); } } if (files.size() > 0) { files = files.get(0).getChildren(); } } int minus = 0; if (files != null) { for (DLNAResource uf : files) { if (xbox && containerID != null) { uf.setFakeParentId(containerID); } if (uf.isCompatible(mediaRenderer) && (uf.getPlayer() == null || uf.getPlayer().isPlayerCompatible(mediaRenderer))) { response.append(uf.toString(mediaRenderer)); } else { minus++; } } } response.append(HTTPXMLHelper.DIDL_FOOTER); response.append(HTTPXMLHelper.RESULT_FOOTER); response.append(CRLF); int filessize = 0; if (files != null) { filessize = files.size(); } response.append("<NumberReturned>").append(filessize - minus).append("</NumberReturned>"); response.append(CRLF); DLNAResource parentFolder = null; if (files != null && filessize > 0) { parentFolder = files.get(0).getParent(); } if (browseFlag != null && browseFlag.equals("BrowseDirectChildren") && mediaRenderer.isMediaParserV2() && mediaRenderer.isDLNATreeHack()) { // with the new parser, files are parsed and analyzed *before* creating the DLNA tree, // every 10 items (the ps3 asks 10 by 10), // so we do not know exactly the total number of items in the DLNA folder to send // (regular files, plus the #transcode folder, maybe the #imdb one, also files can be // invalidated and hidden if format is broken or encrypted, etc.). // let's send a fake total size to force the renderer to ask following items int totalCount = startingIndex + requestCount + 1; // returns 11 when 10 asked if (filessize - minus <= 0) // if no more elements, send the startingIndex { totalCount = startingIndex; } response.append("<TotalMatches>").append(totalCount).append("</TotalMatches>"); } else if (browseFlag != null && browseFlag.equals("BrowseDirectChildren")) { response .append("<TotalMatches>") .append(((parentFolder != null) ? parentFolder.childrenNumber() : filessize) - minus) .append("</TotalMatches>"); } else { // from upnp spec: If BrowseMetadata is specified in the BrowseFlags then // TotalMatches = 1 response.append("<TotalMatches>1</TotalMatches>"); } response.append(CRLF); response.append("<UpdateID>"); if (parentFolder != null) { response.append(parentFolder.getUpdateId()); } else { response.append("1"); } response.append("</UpdateID>"); response.append(CRLF); if (soapaction.contains("ContentDirectory:1#Search")) { response.append(HTTPXMLHelper.SEARCHRESPONSE_FOOTER); } else { response.append(HTTPXMLHelper.BROWSERESPONSE_FOOTER); } response.append(CRLF); response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER); response.append(CRLF); // LOGGER.trace(response.toString()); } } else if (method.equals("SUBSCRIBE")) { output.setHeader("SID", PMS.get().usn()); output.setHeader("TIMEOUT", "Second-1800"); String cb = soapaction.replace("<", "").replace(">", ""); String faddr = cb.replace("http://", "").replace("/", ""); String addr = faddr.split(":")[0]; int port = Integer.parseInt(faddr.split(":")[1]); Socket sock = new Socket(addr, port); OutputStream out = sock.getOutputStream(); out.write(("NOTIFY /" + argument + " HTTP/1.1").getBytes()); out.write(CRLF.getBytes()); out.write(("SID: " + PMS.get().usn()).getBytes()); out.write(CRLF.getBytes()); out.write(("SEQ: " + 0).getBytes()); out.write(CRLF.getBytes()); out.write(("NT: upnp:event").getBytes()); out.write(CRLF.getBytes()); out.write(("NTS: upnp:propchange").getBytes()); out.write(CRLF.getBytes()); out.write(("HOST: " + faddr).getBytes()); out.write(CRLF.getBytes()); out.flush(); out.close(); if (argument.contains("connection_manager")) { response.append( HTTPXMLHelper.eventHeader("urn:schemas-upnp-org:service:ConnectionManager:1")); response.append(HTTPXMLHelper.eventProp("SinkProtocolInfo")); response.append(HTTPXMLHelper.eventProp("SourceProtocolInfo")); response.append(HTTPXMLHelper.eventProp("CurrentConnectionIDs")); response.append(HTTPXMLHelper.EVENT_FOOTER); } else if (argument.contains("content_directory")) { response.append( HTTPXMLHelper.eventHeader("urn:schemas-upnp-org:service:ContentDirectory:1")); response.append(HTTPXMLHelper.eventProp("TransferIDs")); response.append(HTTPXMLHelper.eventProp("ContainerUpdateIDs")); response.append( HTTPXMLHelper.eventProp("SystemUpdateID", "" + DLNAResource.getSystemUpdateId())); response.append(HTTPXMLHelper.EVENT_FOOTER); } } else if (method.equals("NOTIFY")) { output.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml"); output.setHeader("NT", "upnp:event"); output.setHeader("NTS", "upnp:propchange"); output.setHeader("SID", PMS.get().usn()); output.setHeader("SEQ", "0"); response.append("<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">"); response.append("<e:property>"); response.append("<TransferIDs></TransferIDs>"); response.append("</e:property>"); response.append("<e:property>"); response.append("<ContainerUpdateIDs></ContainerUpdateIDs>"); response.append("</e:property>"); response.append("<e:property>"); response .append("<SystemUpdateID>") .append(DLNAResource.getSystemUpdateId()) .append("</SystemUpdateID>"); response.append("</e:property>"); response.append("</e:propertyset>"); } output.setHeader("Server", PMS.get().getServerName()); if (response.length() > 0) { // A response message was constructed; convert it to data ready to be sent. byte responseData[] = response.toString().getBytes("UTF-8"); output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + responseData.length); // HEAD requests only require headers to be set, no need to set contents. if (!method.equals("HEAD")) { // Not a HEAD request, so set the contents of the response. ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseData); output.setContent(buf); } // Send the response to the client. future = e.getChannel().write(output); if (close) { // Close the channel after the response is sent. future.addListener(ChannelFutureListener.CLOSE); } } else if (inputStream != null) { // There is an input stream to send as a response. if (CLoverride > -2) { // Content-Length override has been set, send or omit as appropriate if (CLoverride > -1 && CLoverride != DLNAMediaInfo.TRANS_SIZE) { // Since PS3 firmware 2.50, it is wiser not to send an arbitrary Content-Length, // as the PS3 will display a network error and request the last seconds of the // transcoded video. Better to send no Content-Length at all. output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + CLoverride); } } else { int cl = inputStream.available(); LOGGER.trace("Available Content-Length: " + cl); output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + cl); } if (range.isStartOffsetAvailable() && dlna != null) { // Add timeseek information headers. String timeseekValue = DLNAMediaInfo.getDurationString(range.getStartOrZero()); String timetotalValue = dlna.getMedia().getDurationString(); String timeEndValue = range.isEndLimitAvailable() ? DLNAMediaInfo.getDurationString(range.getEnd()) : timetotalValue; output.setHeader( "TimeSeekRange.dlna.org", "npt=" + timeseekValue + "-" + timeEndValue + "/" + timetotalValue); output.setHeader( "X-Seek-Range", "npt=" + timeseekValue + "-" + timeEndValue + "/" + timetotalValue); } // Send the response headers to the client. future = e.getChannel().write(output); if (lowRange != DLNAMediaInfo.ENDFILE_POS && !method.equals("HEAD")) { // Send the response body to the client in chunks. ChannelFuture chunkWriteFuture = e.getChannel().write(new ChunkedStream(inputStream, BUFFER_SIZE)); // Add a listener to clean up after sending the entire response body. chunkWriteFuture.addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { try { PMS.get().getRegistry().reenableGoToSleep(); inputStream.close(); } catch (IOException e) { LOGGER.debug("Caught exception", e); } // Always close the channel after the response is sent because of // a freeze at the end of video when the channel is not closed. future.getChannel().close(); startStopListenerDelegate.stop(); } }); } else { // HEAD method is being used, so simply clean up after the response was sent. try { PMS.get().getRegistry().reenableGoToSleep(); inputStream.close(); } catch (IOException ioe) { LOGGER.debug("Caught exception", ioe); } if (close) { // Close the channel after the response is sent future.addListener(ChannelFutureListener.CLOSE); } startStopListenerDelegate.stop(); } } else { // No response data and no input stream. Seems we are merely serving up headers. if (lowRange > 0 && highRange > 0) { // FIXME: There is no content, so why set a length? output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "" + (highRange - lowRange + 1)); } else { output.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0"); } // Send the response headers to the client. future = e.getChannel().write(output); if (close) { // Close the channel after the response is sent. future.addListener(ChannelFutureListener.CLOSE); } } // Log trace information Iterator<String> it = output.getHeaderNames().iterator(); while (it.hasNext()) { String headerName = it.next(); LOGGER.trace("Sent to socket: " + headerName + ": " + output.getHeader(headerName)); } return future; }
@Override public HttpResponse toNettyResponse(Message message, NettyHttpConfiguration configuration) throws Exception { LOG.trace("toNettyResponse: {}", message); // the message body may already be a Netty HTTP response if (message.getBody() instanceof HttpResponse) { return (HttpResponse) message.getBody(); } // the response code is 200 for OK and 500 for failed boolean failed = message.getExchange().isFailed(); int defaultCode = failed ? 500 : 200; int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, defaultCode, int.class); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(code)); LOG.trace("HTTP Status Code: {}", code); TypeConverter tc = message.getExchange().getContext().getTypeConverter(); // append headers // must use entrySet to ensure case of keys is preserved for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // use an iterator as there can be multiple values. (must not use a delimiter) final Iterator<?> it = ObjectHelper.createIterator(value, null); while (it.hasNext()) { String headerValue = tc.convertTo(String.class, it.next()); if (headerValue != null && headerFilterStrategy != null && !headerFilterStrategy.applyFilterToCamelHeaders( key, headerValue, message.getExchange())) { LOG.trace("HTTP-Header: {}={}", key, headerValue); response.headers().add(key, headerValue); } } } Object body = message.getBody(); Exception cause = message.getExchange().getException(); // support bodies as native Netty ChannelBuffer buffer; // if there was an exception then use that as body if (cause != null) { if (configuration.isTransferException()) { // we failed due an exception, and transfer it as java serialized object ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(cause); oos.flush(); IOHelper.close(oos, bos); // the body should be the serialized java object of the exception body = ChannelBuffers.copiedBuffer(bos.toByteArray()); // force content type to be serialized java object message.setHeader( Exchange.CONTENT_TYPE, NettyHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT); } else { // we failed due an exception so print it as plain text StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); cause.printStackTrace(pw); // the body should then be the stacktrace body = ChannelBuffers.copiedBuffer(sw.toString().getBytes()); // force content type to be text/plain as that is what the stacktrace is message.setHeader(Exchange.CONTENT_TYPE, "text/plain"); } // and mark the exception as failure handled, as we handled it by returning it as the response ExchangeHelper.setFailureHandled(message.getExchange()); } if (body instanceof ChannelBuffer) { buffer = (ChannelBuffer) body; } else { // try to convert to buffer first buffer = message.getBody(ChannelBuffer.class); if (buffer == null) { // fallback to byte array as last resort byte[] data = message.getBody(byte[].class); if (data != null) { buffer = ChannelBuffers.copiedBuffer(data); } else { // and if byte array fails then try String String str; if (body != null) { str = message.getMandatoryBody(String.class); } else { str = ""; } buffer = ChannelBuffers.copiedBuffer(str.getBytes()); } } } if (buffer != null) { response.setContent(buffer); // We just need to reset the readerIndex this time if (buffer.readerIndex() == buffer.writerIndex()) { buffer.setIndex(0, buffer.writerIndex()); } // TODO How to enable the chunk transport int len = buffer.readableBytes(); // set content-length response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len); LOG.trace("Content-Length: {}", len); } // set the content type in the response. String contentType = MessageHelper.getContentType(message); if (contentType != null) { // set content-type response.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType); LOG.trace("Content-Type: {}", contentType); } // configure connection to accordingly to keep alive configuration // favor using the header from the message String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class); // Read the connection header from the exchange property if (connection == null) { connection = message.getExchange().getProperty(HttpHeaders.Names.CONNECTION, String.class); } if (connection == null) { // fallback and use the keep alive from the configuration if (configuration.isKeepAlive()) { connection = HttpHeaders.Values.KEEP_ALIVE; } else { connection = HttpHeaders.Values.CLOSE; } } response.headers().set(HttpHeaders.Names.CONNECTION, connection); // Just make sure we close the channel when the connection value is close if (connection.equalsIgnoreCase(HttpHeaders.Values.CLOSE)) { message.setHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, true); } LOG.trace("Connection: {}", connection); return response; }
private ChannelFuture write(ChannelBuffer responseBuffer) { response.setContent(responseBuffer); return ctx.getChannel().write(response); }
public static void send( byte[] bytes, HttpResponse response, HttpRequest request, ChannelHandlerContext context) { response.setContent(ChannelBuffers.wrappedBuffer(bytes)); send(response, request, context); }
/** Expected Message types: - HTTP Requests */ @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (!(e.getMessage() instanceof HttpRequest)) { super.messageReceived(ctx, e); return; } HttpRequest httpRequest = (HttpRequest) e.getMessage(); URI targetUri = toThing(URI.create("http://" + httpRequest.getHeader("HOST") + httpRequest.getUri())); log.debug("Received HTTP request for " + targetUri); if (httpRequest.getHeader("HOST").contains(DNS_WILDCARD_POSTFIX)) { String targetUriHost = InetAddress.getByName(targetUri.getHost()).getHostAddress(); // remove leading zeros per block targetUriHost = targetUriHost.replaceAll(":0000", ":0"); targetUriHost = targetUriHost.replaceAll(":000", ":0"); targetUriHost = targetUriHost.replaceAll(":00", ":0"); targetUriHost = targetUriHost.replaceAll("(:0)([ABCDEFabcdef123456789])", ":$2"); // return shortened IP targetUriHost = targetUriHost.replaceAll("((?:(?:^|:)0\\b){2,}):?(?!\\S*\\b\\1:0\\b)(\\S*)", "::$2"); log.debug("Target host: " + targetUriHost); String targetUriPath = targetUri.getRawPath(); log.debug("Target path: " + targetUriPath); if (IPAddressUtil.isIPv6LiteralAddress(targetUriHost)) { targetUriHost = "[" + targetUriHost + "]"; } targetUri = toThing(URI.create("http://" + targetUriHost + httpRequest.getUri())); log.debug("Shortened target URI: " + targetUri); } URI uriToCheck = targetUri; if ((uriToCheck.getQuery() != null) && (uriToCheck.getQuery().equals("html"))) { uriToCheck = toThing(URI.create("http://" + targetUri.getHost() + targetUri.getPath())); } if (entities.containsKey(uriToCheck)) { Backend backend = entities.get(uriToCheck); try { ctx.getPipeline().remove("Backend to handle request"); } catch (NoSuchElementException ex) { // Fine. There was no handler to be removed. } ctx.getPipeline().addLast("Backend to handle request", backend); log.debug("Forward request to " + backend); } else if (virtualEntities.containsKey(uriToCheck)) { Backend backend = virtualEntities.get(uriToCheck); try { ctx.getPipeline().remove("Backend to handle request"); } catch (NoSuchElementException ex) { // Fine. There was no handler to be removed. } ctx.getPipeline().addLast("Backend to handle request", backend); log.debug("Forward request to " + backend); } // else if (targetUriPath.equals(PATH_TO_SERVER_LIST)) { // // Handle request for resource at path ".well-known/core" // StringBuilder buf = new StringBuilder(); // for(URI entity: getServices()) { // buf.append(toThing(entity).toString() + "\n"); // } // Channels.write(ctx.getChannel(), // Answer.create(buf.toString()).setMime("text/plain")); // return; // } // else if("/visualizer".equals(targetUriPath)){ // try { // ctx.getPipeline().remove("Backend to handle request"); // } // catch(NoSuchElementException ex) { // //Fine. There was no handler to be removed. // } // ctx.getPipeline().addLast("VisualizerService", VisualizerService.getInstance()); // log.debug("Forward request to visualizer."); // } /*else if(targetUriPath.startsWith(SERVER_PATH_TO_SLSE_UI)) { String f = LOCAL_PATH_TO_SLSE_UI + targetUriPath.substring(SERVER_PATH_TO_SLSE_UI.length()); Channels.write(ctx.getChannel(), Answer.create(new File(f)).setMime("text/n3")); }*/ else if ("/".equals(targetUri.getRawPath())) { HttpResponse httpResponse = new DefaultHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.OK); httpResponse.setContent(getHtmlListOfServices()); ChannelFuture future = Channels.write(ctx.getChannel(), httpResponse); future.addListener(ChannelFutureListener.CLOSE); return; } else if (httpRequest.getUri().endsWith("spitfire-logo.png") || (httpRequest.getUri().endsWith("favicon.ico"))) { File img; if (httpRequest.getUri().endsWith("spitfire-logo.png")) { img = new File("spitfire-logo.png"); } else { img = new File("favicon.ico"); } int imgLength = (int) img.length(); FileInputStream in = new FileInputStream(img); byte[] imgMemory = new byte[imgLength]; in.read(imgMemory); in.close(); HttpResponse httpResponse = new DefaultHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.OK); httpResponse.setContent(ChannelBuffers.wrappedBuffer(imgMemory)); ChannelFuture future = Channels.write(ctx.getChannel(), httpResponse); future.addListener(ChannelFutureListener.CLOSE); if (httpRequest.getUri().endsWith("spitfire-logo.png")) { log.debug("Served request for Spitfire image."); } else { log.debug("Served favicon."); } return; } ctx.sendUpstream(e); }
/** * Sends nested multipart response. Outer multipart wraps all the keys requested. Each key has a * separate multipart for the versioned values. */ @Override public void sendResponse(StoreStats performanceStats, boolean isFromLocalZone, long startTimeInMs) throws Exception { // multiPartKeys is the outer multipart MimeMultipart multiPartKeys = new MimeMultipart(); ByteArrayOutputStream keysOutputStream = new ByteArrayOutputStream(); for (Entry<ByteArray, List<Versioned<byte[]>>> entry : versionedResponses.entrySet()) { ByteArray key = entry.getKey(); String contentLocationKey = "/" + this.storeName + "/" + new String(Base64.encodeBase64(key.get())); // Create the individual body part - for each key requested MimeBodyPart keyBody = new MimeBodyPart(); try { // Add the right headers keyBody.addHeader(CONTENT_TYPE, "application/octet-stream"); keyBody.addHeader(CONTENT_TRANSFER_ENCODING, "binary"); keyBody.addHeader(CONTENT_LOCATION, contentLocationKey); } catch (MessagingException me) { logger.error("Exception while constructing key body headers", me); keysOutputStream.close(); throw me; } // multiPartValues is the inner multipart MimeMultipart multiPartValues = new MimeMultipart(); for (Versioned<byte[]> versionedValue : entry.getValue()) { byte[] responseValue = versionedValue.getValue(); VectorClock vectorClock = (VectorClock) versionedValue.getVersion(); String eTag = RestUtils.getSerializedVectorClock(vectorClock); // Create the individual body part - for each versioned value of // a key MimeBodyPart valueBody = new MimeBodyPart(); try { // Add the right headers valueBody.addHeader(CONTENT_TYPE, "application/octet-stream"); valueBody.addHeader(CONTENT_TRANSFER_ENCODING, "binary"); valueBody.addHeader(RestMessageHeaders.X_VOLD_VECTOR_CLOCK, eTag); valueBody.setContent(responseValue, "application/octet-stream"); multiPartValues.addBodyPart(valueBody); } catch (MessagingException me) { logger.error("Exception while constructing value body part", me); keysOutputStream.close(); throw me; } } try { // Add the inner multipart as the content of the outer body part keyBody.setContent(multiPartValues); multiPartKeys.addBodyPart(keyBody); } catch (MessagingException me) { logger.error("Exception while constructing key body part", me); keysOutputStream.close(); throw me; } } try { multiPartKeys.writeTo(keysOutputStream); } catch (Exception e) { logger.error("Exception while writing mutipart to output stream", e); throw e; } ChannelBuffer responseContent = ChannelBuffers.dynamicBuffer(); responseContent.writeBytes(keysOutputStream.toByteArray()); // Create the Response object HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); // Set the right headers response.setHeader(CONTENT_TYPE, "multipart/binary"); response.setHeader(CONTENT_TRANSFER_ENCODING, "binary"); // Copy the data into the payload response.setContent(responseContent); response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); // Write the response to the Netty Channel this.messageEvent.getChannel().write(response); if (performanceStats != null && isFromLocalZone) { recordStats(performanceStats, startTimeInMs, Tracked.GET_ALL); } keysOutputStream.close(); }
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { // Allow only GET methods. if (req.getMethod() != GET) { sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); return; } // Send the demo page. if (req.getUri().equals("/")) { HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK); ChannelBuffer content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); setContentLength(res, content.readableBytes()); res.setContent(content); sendHttpResponse(ctx, req, res); return; } // Serve the WebSocket handshake request. if (req.getUri().equals(WEBSOCKET_PATH) && Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) && WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) { // Create the WebSocket handshake response. HttpResponse res = new DefaultHttpResponse( HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake")); res.addHeader(Names.UPGRADE, WEBSOCKET); res.addHeader(CONNECTION, 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, getWebSocketLocation(req)); 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); ChannelBuffer output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array())); res.setContent(output); } else { // Old handshake method with no challenge: res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req)); String protocol = req.getHeader(WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(WEBSOCKET_PROTOCOL, protocol); } } // Upgrade the connection and send the handshake response. ChannelPipeline p = ctx.getChannel().getPipeline(); p.remove("aggregator"); p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder()); ctx.getChannel().write(res); p.replace("encoder", "wsencoder", new WebSocketFrameEncoder()); return; } // Send an error page otherwise. sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); }