Esempio n. 1
0
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    HttpRequest request = (HttpRequest) e.getMessage();

    if (request.getContent().readableBytes() == 0) {
      BaseTransport.respond(e.getChannel(), INTERNAL_SERVER_ERROR, "Payload expected.");
      return;
    }

    transportMetrics.messagesReceived.mark();
    transportMetrics.messagesReceivedSize.update(request.getContent().readableBytes());

    // logger.debug("Received {}", request.getContent().toString(CharsetUtil.UTF_8));

    String contentTypeHeader = request.getHeader(CONTENT_TYPE);
    if (contentTypeHeader == null) {
      contentTypeHeader = BaseTransport.CONTENT_TYPE_PLAIN;
    }

    String decodedContent;
    if (BaseTransport.CONTENT_TYPE_FORM.equals(contentTypeHeader)) {
      QueryStringDecoder decoder =
          new QueryStringDecoder("?" + request.getContent().toString(CharsetUtil.UTF_8));
      List<String> d = decoder.getParameters().get("d");
      if (d == null) {
        BaseTransport.respond(e.getChannel(), INTERNAL_SERVER_ERROR, "Payload expected.");
        return;
      }
      decodedContent = d.get(0);
    } else {
      decodedContent = request.getContent().toString(CharsetUtil.UTF_8);
    }

    if (decodedContent.length() == 0) {
      BaseTransport.respond(e.getChannel(), INTERNAL_SERVER_ERROR, "Payload expected.");
      return;
    }

    String[] messages = MAPPER.readValue(decodedContent, String[].class);
    for (String message : messages) {
      SockJsMessage jsMessage = new SockJsMessage(message);
      ctx.sendUpstream(new UpstreamMessageEvent(e.getChannel(), jsMessage, e.getRemoteAddress()));
    }

    if (isJsonpEnabled) {
      BaseTransport.respond(e.getChannel(), OK, "ok");
    } else {
      BaseTransport.respond(e.getChannel(), NO_CONTENT, "");
    }
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    RequestV2 request = null;
    RendererConfiguration renderer = null;
    String userAgentString = null;
    StringBuilder unknownHeaders = new StringBuilder();
    String separator = "";
    boolean isWindowsMediaPlayer = false;

    HttpRequest nettyRequest = this.nettyRequest = (HttpRequest) e.getMessage();

    InetSocketAddress remoteAddress = (InetSocketAddress) e.getChannel().getRemoteAddress();
    InetAddress ia = remoteAddress.getAddress();

    // Apply the IP filter
    if (filterIp(ia)) {
      e.getChannel().close();
      LOGGER.trace("Access denied for address " + ia + " based on IP filter");
      return;
    }

    LOGGER.trace("Opened request handler on socket " + remoteAddress);
    PMS.get().getRegistry().disableGoToSleep();

    if (HttpMethod.GET.equals(nettyRequest.getMethod())) {
      request = new RequestV2("GET", nettyRequest.getUri().substring(1));
    } else if (HttpMethod.POST.equals(nettyRequest.getMethod())) {
      request = new RequestV2("POST", nettyRequest.getUri().substring(1));
    } else if (HttpMethod.HEAD.equals(nettyRequest.getMethod())) {
      request = new RequestV2("HEAD", nettyRequest.getUri().substring(1));
    } else {
      request =
          new RequestV2(nettyRequest.getMethod().getName(), nettyRequest.getUri().substring(1));
    }

    LOGGER.trace(
        "Request: "
            + nettyRequest.getProtocolVersion().getText()
            + " : "
            + request.getMethod()
            + " : "
            + request.getArgument());

    if (nettyRequest.getProtocolVersion().getMinorVersion() == 0) {
      request.setHttp10(true);
    }

    // The handler makes a couple of attempts to recognize a renderer from its requests.
    // IP address matches from previous requests are preferred, when that fails request
    // header matches are attempted and if those fail as well we're stuck with the
    // default renderer.

    // Attempt 1: try to recognize the renderer by its socket address from previous requests
    renderer = RendererConfiguration.getRendererConfigurationBySocketAddress(ia);

    if (renderer != null) {
      if (!"WMP".equals(renderer.getRendererName())) {
        PMS.get().setRendererfound(renderer);
        request.setMediaRenderer(renderer);
        LOGGER.trace(
            "Matched media renderer \"" + renderer.getRendererName() + "\" based on address " + ia);
      } else {
        LOGGER.trace("Detected and blocked Windows Media Player");
        isWindowsMediaPlayer = true;
      }
    }

    for (String name : nettyRequest.getHeaderNames()) {
      String headerLine = name + ": " + nettyRequest.getHeader(name);
      LOGGER.trace("Received on socket: " + headerLine);

      if (renderer == null
          && headerLine != null
          && headerLine.toUpperCase().startsWith("USER-AGENT")) {
        userAgentString = headerLine.substring(headerLine.indexOf(":") + 1).trim();

        // Attempt 2: try to recognize the renderer by matching the "User-Agent" header
        renderer = RendererConfiguration.getRendererConfigurationByUA(userAgentString);

        if (renderer != null) {
          if (!"WMP".equals(renderer.getRendererName())) {
            request.setMediaRenderer(renderer);
            renderer.associateIP(ia); // Associate IP address for later requests
            PMS.get().setRendererfound(renderer);
            LOGGER.trace(
                "Matched media renderer \""
                    + renderer.getRendererName()
                    + "\" based on header \""
                    + headerLine
                    + "\"");
          } else if (!isWindowsMediaPlayer) {
            LOGGER.trace("Detected and blocked Windows Media Player");
            isWindowsMediaPlayer = true;
          }
        }
      }

      if (renderer == null && headerLine != null) {
        // Attempt 3: try to recognize the renderer by matching an additional header
        renderer = RendererConfiguration.getRendererConfigurationByUAAHH(headerLine);

        if (renderer != null) {
          request.setMediaRenderer(renderer);
          renderer.associateIP(ia); // Associate IP address for later requests
          PMS.get().setRendererfound(renderer);
          LOGGER.trace(
              "Matched media renderer \""
                  + renderer.getRendererName()
                  + "\" based on header \""
                  + headerLine
                  + "\"");
        }
      }

      try {
        StringTokenizer s = new StringTokenizer(headerLine);
        String temp = s.nextToken();
        if (temp.toUpperCase().equals("SOAPACTION:")) {
          request.setSoapaction(s.nextToken());
        } else if (temp.toUpperCase().equals("CALLBACK:")) {
          request.setSoapaction(s.nextToken());
        } else if (headerLine.toUpperCase().indexOf("RANGE: BYTES=") > -1) {
          String nums =
              headerLine.substring(headerLine.toUpperCase().indexOf("RANGE: BYTES=") + 13).trim();
          StringTokenizer st = new StringTokenizer(nums, "-");
          if (!nums.startsWith("-")) {
            request.setLowRange(Long.parseLong(st.nextToken()));
          }
          if (!nums.startsWith("-") && !nums.endsWith("-")) {
            request.setHighRange(Long.parseLong(st.nextToken()));
          } else {
            request.setHighRange(-1);
          }
        } else if (headerLine.toLowerCase().indexOf("transfermode.dlna.org:") > -1) {
          request.setTransferMode(
              headerLine
                  .substring(headerLine.toLowerCase().indexOf("transfermode.dlna.org:") + 22)
                  .trim());
        } else if (headerLine.toLowerCase().indexOf("getcontentfeatures.dlna.org:") > -1) {
          request.setContentFeatures(
              headerLine
                  .substring(headerLine.toLowerCase().indexOf("getcontentfeatures.dlna.org:") + 28)
                  .trim());
        } else {
          Matcher matcher = TIMERANGE_PATTERN.matcher(headerLine);
          if (matcher.find()) {
            String first = matcher.group(1);
            if (first != null) {
              request.setTimeRangeStartString(first);
            }
            String end = matcher.group(2);
            if (end != null) {
              request.setTimeRangeEndString(end);
            }
          } else {
            // If we made it to here, none of the previous header checks matched.
            // Unknown headers make interesting logging info when we cannot recognize
            // the media renderer, so keep track of the truly unknown ones.
            boolean isKnown = false;

            // Try to match possible known headers.
            for (String knownHeaderString : KNOWN_HEADERS) {
              if (headerLine.toLowerCase().startsWith(knownHeaderString.toLowerCase())) {
                isKnown = true;
                break;
              }
            }

            if (!isKnown) {
              // Truly unknown header, therefore interesting. Save for later use.
              unknownHeaders.append(separator).append(headerLine);
              separator = ", ";
            }
          }
        }
      } catch (Exception ee) {
        LOGGER.error("Error parsing HTTP headers", ee);
      }
    }

    if (!isWindowsMediaPlayer) {
      if (request != null) {
        // Still no media renderer recognized?
        if (request.getMediaRenderer() == null) {

          // Attempt 4: Not really an attempt; all other attempts to recognize
          // the renderer have failed. The only option left is to assume the
          // default renderer.
          request.setMediaRenderer(RendererConfiguration.getDefaultConf());
          LOGGER.trace(
              "Using default media renderer: " + request.getMediaRenderer().getRendererName());

          if (userAgentString != null && !userAgentString.equals("FDSSDP")) {
            // We have found an unknown renderer
            LOGGER.info(
                "Media renderer was not recognized. Possible identifying HTTP headers: User-Agent: "
                    + userAgentString
                    + ("".equals(unknownHeaders.toString())
                        ? ""
                        : ", " + unknownHeaders.toString()));
            PMS.get().setRendererfound(request.getMediaRenderer());
          }
        } else {
          if (userAgentString != null) {
            LOGGER.debug("HTTP User-Agent: " + userAgentString);
          }

          LOGGER.trace(
              "Recognized media renderer: " + request.getMediaRenderer().getRendererName());
        }
      }

      if (HttpHeaders.getContentLength(nettyRequest) > 0) {
        byte data[] = new byte[(int) HttpHeaders.getContentLength(nettyRequest)];
        ChannelBuffer content = nettyRequest.getContent();
        content.readBytes(data);
        request.setTextContent(new String(data, "UTF-8"));
      }

      if (request != null) {
        LOGGER.trace(
            "HTTP: "
                + request.getArgument()
                + " / "
                + request.getLowRange()
                + "-"
                + request.getHighRange());
      }

      writeResponse(e, request, ia);
    }
  }
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {

      HttpRequest request = (HttpRequest) e.getMessage();
      if (request.getMethod() != GET) {
        sendError(ctx, METHOD_NOT_ALLOWED);
        return;
      }

      // Parsing the URL into key-values
      final Map<String, List<String>> params =
          new QueryStringDecoder(request.getUri()).getParameters();
      final List<String> types = params.get("type");
      final List<String> taskIdList = params.get("ta");
      final List<String> subQueryIds = params.get("sid");
      final List<String> partitionIds = params.get("p");

      if (types == null || taskIdList == null || subQueryIds == null || partitionIds == null) {
        sendError(ctx, "Required type, taskIds, subquery Id, and partition id", BAD_REQUEST);
        return;
      }

      if (types.size() != 1 || subQueryIds.size() != 1) {
        sendError(ctx, "Required type, taskIds, subquery Id, and partition id", BAD_REQUEST);
        return;
      }

      final List<FileChunk> chunks = Lists.newArrayList();

      String repartitionType = types.get(0);
      String sid = subQueryIds.get(0);
      String partitionId = partitionIds.get(0);
      List<String> taskIds = splitMaps(taskIdList);

      // the working dir of tajo worker for each query
      String queryBaseDir = queryId + "/output" + "/";

      LOG.info(
          "PullServer request param: repartitionType="
              + repartitionType
              + ", sid="
              + sid
              + ", partitionId="
              + partitionId
              + ", taskIds="
              + taskIdList);

      String taskLocalDir = conf.get(ConfVars.WORKER_TEMPORAL_DIR.varname);
      if (taskLocalDir == null || taskLocalDir.equals("")) {
        LOG.error("Tajo local directory should be specified.");
      }
      LOG.info("PullServer baseDir: " + taskLocalDir + "/" + queryBaseDir);

      // if a subquery requires a range partitioning
      if (repartitionType.equals("r")) {
        String ta = taskIds.get(0);
        Path path =
            localFS.makeQualified(
                lDirAlloc.getLocalPathToRead(
                    queryBaseDir + "/" + sid + "/" + ta + "/output/", conf));

        String startKey = params.get("start").get(0);
        String endKey = params.get("end").get(0);
        boolean last = params.get("final") != null;

        FileChunk chunk;
        try {
          chunk = getFileCunks(path, startKey, endKey, last);
        } catch (Throwable t) {
          LOG.error("ERROR Request: " + request.getUri(), t);
          sendError(ctx, "Cannot get file chunks to be sent", BAD_REQUEST);
          return;
        }
        if (chunk != null) {
          chunks.add(chunk);
        }

        // if a subquery requires a hash repartition
      } else if (repartitionType.equals("h")) {
        for (String ta : taskIds) {
          Path path =
              localFS.makeQualified(
                  lDirAlloc.getLocalPathToRead(
                      queryBaseDir + "/" + sid + "/" + ta + "/output/" + partitionId, conf));
          File file = new File(path.toUri());
          FileChunk chunk = new FileChunk(file, 0, file.length());
          chunks.add(chunk);
        }
      } else {
        LOG.error("Unknown repartition type: " + repartitionType);
        return;
      }

      // Write the content.
      Channel ch = e.getChannel();
      if (chunks.size() == 0) {
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, NO_CONTENT);
        ch.write(response);
        if (!isKeepAlive(request)) {
          ch.close();
        }
      } else {
        FileChunk[] file = chunks.toArray(new FileChunk[chunks.size()]);
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        long totalSize = 0;
        for (FileChunk chunk : file) {
          totalSize += chunk.length();
        }
        setContentLength(response, totalSize);

        // Write the initial line and the header.
        ch.write(response);

        ChannelFuture writeFuture = null;

        for (FileChunk chunk : file) {
          writeFuture = sendFile(ctx, ch, chunk);
          if (writeFuture == null) {
            sendError(ctx, NOT_FOUND);
            return;
          }
        }

        // Decide whether to close the connection or not.
        if (!isKeepAlive(request)) {
          // Close the connection when the whole content is written out.
          writeFuture.addListener(ChannelFutureListener.CLOSE);
        }
      }
    }
Esempio n. 4
0
  public void serveStatic(
      RenderStatic renderStatic,
      ChannelHandlerContext ctx,
      Request request,
      Response response,
      HttpRequest nettyRequest,
      MessageEvent e) {
    Logger.trace("serveStatic: begin");
    HttpResponse nettyResponse =
        new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.status));
    if (exposePlayServer) {
      nettyResponse.setHeader(SERVER, signature);
    }
    try {
      VirtualFile file = Play.getVirtualFile(renderStatic.file);
      if (file != null && file.exists() && file.isDirectory()) {
        file = file.child("index.html");
        if (file != null) {
          renderStatic.file = file.relativePath();
        }
      }
      if ((file == null || !file.exists())) {
        serve404(
            new NotFound("The file " + renderStatic.file + " does not exist"),
            ctx,
            request,
            nettyRequest);
      } else {
        boolean raw = false;
        for (PlayPlugin plugin : Play.plugins) {
          if (plugin.serveStatic(file, Request.current(), Response.current())) {
            raw = true;
            break;
          }
        }
        if (raw) {
          copyResponse(ctx, request, response, nettyRequest);
        } else {
          final File localFile = file.getRealFile();
          final boolean keepAlive = isKeepAlive(nettyRequest);
          nettyResponse = addEtag(nettyRequest, nettyResponse, localFile);

          if (nettyResponse.getStatus().equals(HttpResponseStatus.NOT_MODIFIED)) {

            Channel ch = e.getChannel();

            // Write the initial line and the header.
            ChannelFuture writeFuture = ch.write(nettyResponse);
            if (!keepAlive) {
              // Write the content.
              writeFuture.addListener(ChannelFutureListener.CLOSE);
            }
          } else {

            final RandomAccessFile raf = new RandomAccessFile(localFile, "r");
            try {
              long fileLength = raf.length();

              Logger.trace("keep alive " + keepAlive);
              Logger.trace(
                  "content type " + (MimeTypes.getContentType(localFile.getName(), "text/plain")));

              if (keepAlive && !nettyResponse.getStatus().equals(HttpResponseStatus.NOT_MODIFIED)) {
                // Add 'Content-Length' header only for a keep-alive connection.
                Logger.trace("file length " + fileLength);
                setContentLength(nettyResponse, fileLength);
              }

              nettyResponse.setHeader(
                  CONTENT_TYPE, (MimeTypes.getContentType(localFile.getName(), "text/plain")));

              Channel ch = e.getChannel();

              // Write the initial line and the header.
              ChannelFuture writeFuture = ch.write(nettyResponse);

              // Write the content.
              if (!nettyRequest.getMethod().equals(HttpMethod.HEAD)) {
                writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
              } else {
                raf.close();
              }

              if (!keepAlive) {
                // Close the connection when the whole content is written out.
                writeFuture.addListener(ChannelFutureListener.CLOSE);
              }
            } catch (Throwable exx) {
              try {
                raf.close();
              } catch (Throwable ex) {
                /* Left empty */
              }
              try {
                ctx.getChannel().close();
              } catch (Throwable ex) {
                /* Left empty */
              }
            }
          }
        }
      }
    } catch (Throwable ez) {
      Logger.error(ez, "serveStatic for request %s", request.method + " " + request.url);
      try {
        HttpResponse errorResponse =
            new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
        ChannelBuffer buf =
            ChannelBuffers.copiedBuffer("Internal Error (check logs)".getBytes("utf-8"));
        errorResponse.setContent(buf);
        ChannelFuture future = ctx.getChannel().write(errorResponse);
        future.addListener(ChannelFutureListener.CLOSE);
      } catch (Exception ex) {
        Logger.error(ez, "serveStatic for request %s", request.method + " " + request.url);
      }
    }
    Logger.trace("serveStatic: end");
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    RequestV2 request = null;
    RendererConfiguration renderer = null;
    String userAgentString = null;
    StringBuilder unknownHeaders = new StringBuilder();
    String separator = "";

    HttpRequest nettyRequest = this.nettyRequest = (HttpRequest) e.getMessage();

    InetSocketAddress remoteAddress = (InetSocketAddress) e.getChannel().getRemoteAddress();
    InetAddress ia = remoteAddress.getAddress();

    // Is the request from our own Cling service, i.e. self-originating?
    boolean isSelf =
        ia.getHostAddress().equals(PMS.get().getServer().getHost())
            && nettyRequest.headers().get(HttpHeaders.Names.USER_AGENT) != null
            && nettyRequest.headers().get(HttpHeaders.Names.USER_AGENT).contains("UMS/");

    // Filter if required
    if (isSelf || filterIp(ia)) {
      e.getChannel().close();
      LOGGER.trace(
          isSelf
              ? ("Ignoring self-originating request from " + ia + ":" + remoteAddress.getPort())
              : ("Access denied for address " + ia + " based on IP filter"));
      return;
    }

    LOGGER.trace("Opened request handler on socket " + remoteAddress);
    PMS.get().getRegistry().disableGoToSleep();
    request = new RequestV2(nettyRequest.getMethod().getName(), nettyRequest.getUri().substring(1));
    LOGGER.trace(
        "Request: "
            + nettyRequest.getProtocolVersion().getText()
            + " : "
            + request.getMethod()
            + " : "
            + request.getArgument());

    if (nettyRequest.getProtocolVersion().getMinorVersion() == 0) {
      request.setHttp10(true);
    }

    HttpHeaders headers = nettyRequest.headers();

    // The handler makes a couple of attempts to recognize a renderer from its requests.
    // IP address matches from previous requests are preferred, when that fails request
    // header matches are attempted and if those fail as well we're stuck with the
    // default renderer.

    // Attempt 1: try to recognize the renderer by its socket address from previous requests
    renderer = RendererConfiguration.getRendererConfigurationBySocketAddress(ia);

    // If the renderer exists but isn't marked as loaded it means it's unrecognized
    // by upnp and we still need to attempt http recognition here.
    if (renderer == null || !renderer.loaded) {
      // Attempt 2: try to recognize the renderer by matching headers
      renderer = RendererConfiguration.getRendererConfigurationByHeaders(headers.entries(), ia);
    }

    if (renderer != null) {
      request.setMediaRenderer(renderer);
    }

    Set<String> headerNames = headers.names();
    Iterator<String> iterator = headerNames.iterator();
    while (iterator.hasNext()) {
      String name = iterator.next();
      String headerLine = name + ": " + headers.get(name);
      LOGGER.trace("Received on socket: " + headerLine);

      if (headerLine.toUpperCase().startsWith("USER-AGENT")) {
        userAgentString = headerLine.substring(headerLine.indexOf(':') + 1).trim();
      }

      try {
        StringTokenizer s = new StringTokenizer(headerLine);
        String temp = s.nextToken();
        if (temp.toUpperCase().equals("SOAPACTION:")) {
          request.setSoapaction(s.nextToken());
        } else if (temp.toUpperCase().equals("CALLBACK:")) {
          request.setSoapaction(s.nextToken());
        } else if (headerLine.toUpperCase().contains("RANGE: BYTES=")) {
          String nums =
              headerLine.substring(headerLine.toUpperCase().indexOf("RANGE: BYTES=") + 13).trim();
          StringTokenizer st = new StringTokenizer(nums, "-");
          if (!nums.startsWith("-")) {
            request.setLowRange(Long.parseLong(st.nextToken()));
          }
          if (!nums.startsWith("-") && !nums.endsWith("-")) {
            request.setHighRange(Long.parseLong(st.nextToken()));
          } else {
            request.setHighRange(-1);
          }
        } else if (headerLine.toLowerCase().contains("transfermode.dlna.org:")) {
          request.setTransferMode(
              headerLine
                  .substring(headerLine.toLowerCase().indexOf("transfermode.dlna.org:") + 22)
                  .trim());
        } else if (headerLine.toLowerCase().contains("getcontentfeatures.dlna.org:")) {
          request.setContentFeatures(
              headerLine
                  .substring(headerLine.toLowerCase().indexOf("getcontentfeatures.dlna.org:") + 28)
                  .trim());
        } else {
          Matcher matcher = TIMERANGE_PATTERN.matcher(headerLine);
          if (matcher.find()) {
            String first = matcher.group(1);
            if (first != null) {
              request.setTimeRangeStartString(first);
            }
            String end = matcher.group(2);
            if (end != null) {
              request.setTimeRangeEndString(end);
            }
          } else {
            /**
             * If we made it to here, none of the previous header checks matched. Unknown headers
             * make interesting logging info when we cannot recognize the media renderer, so keep
             * track of the truly unknown ones.
             */
            boolean isKnown = false;

            // Try to match known headers.
            String lowerCaseHeaderLine = headerLine.toLowerCase();
            for (String knownHeaderString : KNOWN_HEADERS) {
              if (lowerCaseHeaderLine.startsWith(knownHeaderString)) {
                isKnown = true;
                break;
              }
            }

            // It may be unusual but already known
            if (renderer != null) {
              String additionalHeader = renderer.getUserAgentAdditionalHttpHeader();
              if (StringUtils.isNotBlank(additionalHeader)
                  && lowerCaseHeaderLine.startsWith(additionalHeader)) {
                isKnown = true;
              }
            }

            if (!isKnown) {
              // Truly unknown header, therefore interesting. Save for later use.
              unknownHeaders.append(separator).append(headerLine);
              separator = ", ";
            }
          }
        }
      } catch (Exception ee) {
        LOGGER.error("Error parsing HTTP headers", ee);
      }
    }

    // Still no media renderer recognized?
    if (request.getMediaRenderer() == null) {

      // Attempt 3: Not really an attempt; all other attempts to recognize
      // the renderer have failed. The only option left is to assume the
      // default renderer.
      request.setMediaRenderer(RendererConfiguration.resolve(ia, null));
      if (request.getMediaRenderer() != null) {
        LOGGER.trace(
            "Using default media renderer: " + request.getMediaRenderer().getRendererName());

        if (userAgentString != null && !userAgentString.equals("FDSSDP")) {
          // We have found an unknown renderer
          LOGGER.info(
              "Media renderer was not recognized. Possible identifying HTTP headers: User-Agent: "
                  + userAgentString
                  + ("".equals(unknownHeaders.toString()) ? "" : ", " + unknownHeaders.toString()));
          PMS.get().setRendererFound(request.getMediaRenderer());
        }
      } else {
        // If RendererConfiguration.resolve() didn't return the default renderer
        // it means we know via upnp that it's not really a renderer.
        return;
      }
    } else {
      if (userAgentString != null) {
        LOGGER.debug("HTTP User-Agent: " + userAgentString);
      }

      LOGGER.trace("Recognized media renderer: " + request.getMediaRenderer().getRendererName());
    }

    if (nettyRequest.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
      byte data[] = new byte[(int) HttpHeaders.getContentLength(nettyRequest)];
      ChannelBuffer content = nettyRequest.getContent();
      content.readBytes(data);
      request.setTextContent(new String(data, "UTF-8"));
    }

    LOGGER.trace(
        "HTTP: "
            + request.getArgument()
            + " / "
            + request.getLowRange()
            + "-"
            + request.getHighRange());

    writeResponse(ctx, e, request, ia);
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    HttpRequest request = (HttpRequest) e.getMessage();
    if (request.getMethod() != GET) {
      sendError(ctx, METHOD_NOT_ALLOWED);
      return;
    }

    String base =
        ContainerLocalizer.USERCACHE
            + "/"
            + userName
            + "/"
            + ContainerLocalizer.APPCACHE
            + "/"
            + appId
            + "/output"
            + "/";

    final Map<String, List<String>> params =
        new QueryStringDecoder(request.getUri()).getParameters();

    List<FileChunk> chunks = Lists.newArrayList();
    List<String> taskIds = splitMaps(params.get("ta"));
    int sid = Integer.valueOf(params.get("sid").get(0));
    int partitionId = Integer.valueOf(params.get("p").get(0));
    for (String ta : taskIds) {

      File file = new File(base + "/" + sid + "/" + ta + "/output/" + partitionId);
      FileChunk chunk = new FileChunk(file, 0, file.length());
      chunks.add(chunk);
    }

    FileChunk[] file = chunks.toArray(new FileChunk[chunks.size()]);
    //    try {
    //      file = retriever.handle(ctx, request);
    //    } catch (FileNotFoundException fnf) {
    //      LOG.error(fnf);
    //      sendError(ctx, NOT_FOUND);
    //      return;
    //    } catch (IllegalArgumentException iae) {
    //      LOG.error(iae);
    //      sendError(ctx, BAD_REQUEST);
    //      return;
    //    } catch (FileAccessForbiddenException fafe) {
    //      LOG.error(fafe);
    //      sendError(ctx, FORBIDDEN);
    //      return;
    //    } catch (IOException ioe) {
    //      LOG.error(ioe);
    //      sendError(ctx, INTERNAL_SERVER_ERROR);
    //      return;
    //    }

    // Write the content.
    Channel ch = e.getChannel();
    if (file == null) {
      HttpResponse response = new DefaultHttpResponse(HTTP_1_1, NO_CONTENT);
      ch.write(response);
      if (!isKeepAlive(request)) {
        ch.close();
      }
    } else {
      HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
      long totalSize = 0;
      for (FileChunk chunk : file) {
        totalSize += chunk.length();
      }
      setContentLength(response, totalSize);

      // Write the initial line and the header.
      ch.write(response);

      ChannelFuture writeFuture = null;

      for (FileChunk chunk : file) {
        writeFuture = sendFile(ctx, ch, chunk);
        if (writeFuture == null) {
          sendError(ctx, NOT_FOUND);
          return;
        }
      }

      // Decide whether to close the connection or not.
      if (!isKeepAlive(request)) {
        // Close the connection when the whole content is written out.
        writeFuture.addListener(ChannelFutureListener.CLOSE);
      }
    }
  }