예제 #1
0
  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);
  }
예제 #2
0
  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;
  }
예제 #3
0
  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);
    }
  }
예제 #5
0
  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");
  }
예제 #6
0
  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);
  }
예제 #8
0
  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);
  }
예제 #9
0
 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);
  }
예제 #11
0
 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;
 }
예제 #12
0
 /* 此函数封装一个响应信息
  * 当需要发送信息给用户时,此函数将被调用
  * @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);
 }
예제 #13
0
  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);
  }
예제 #15
0
 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());
 }
예제 #16
0
 @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);
   }
 }
예제 #17
0
  @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);
  }
예제 #18
0
 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);
   }
 }
예제 #19
0
  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);
  }
예제 #22
0
 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);
   }
 }
예제 #23
0
 @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);
   }
 }
예제 #24
0
  /**
   * 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 = &quot;", "&quot;)");
            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;
  }
예제 #25
0
  @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;
  }
예제 #26
0
 private ChannelFuture write(ChannelBuffer responseBuffer) {
   response.setContent(responseBuffer);
   return ctx.getChannel().write(response);
 }
예제 #27
0
 public static void send(
     byte[] bytes, HttpResponse response, HttpRequest request, ChannelHandlerContext context) {
   response.setContent(ChannelBuffers.wrappedBuffer(bytes));
   send(response, request, context);
 }
예제 #28
0
  /** 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);
  }
예제 #29
0
  /**
   * 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();
  }
예제 #30
0
  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));
  }