@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);
   }
 }
Exemple #2
0
  public File get() throws IOException {
    ClientBootstrap bootstrap =
        new ClientBootstrap(
            new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
    bootstrap.setOption("connectTimeoutMillis", 5000L); // set 5 sec
    bootstrap.setOption("receiveBufferSize", 1048576); // set 1M
    ChannelPipelineFactory factory = new HttpClientPipelineFactory(file);
    bootstrap.setPipelineFactory(factory);

    ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

    // Wait until the connection attempt succeeds or fails.
    Channel channel = future.awaitUninterruptibly().getChannel();
    if (!future.isSuccess()) {
      bootstrap.releaseExternalResources();
      throw new IOException(future.getCause());
    }

    String query = uri.getPath() + (uri.getQuery() != null ? "?" + uri.getQuery() : "");
    // Prepare the HTTP request.
    HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, query);
    request.setHeader(HttpHeaders.Names.HOST, host);
    LOG.info("Fetch: " + request.getUri());
    request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
    request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);

    // Send the HTTP request.
    channel.write(request);

    // Wait for the server to close the connection.
    channel.getCloseFuture().awaitUninterruptibly();

    // Shut down executor threads to exit.
    bootstrap.releaseExternalResources();

    return file;
  }
  @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 {
    if (!(e.getMessage() instanceof HttpRequest)) {
      super.messageReceived(ctx, e);
    }

    HttpRequest request = (HttpRequest) e.getMessage();
    URI uri = entityManager.normalizeURI(getEntityManager().getURIBase() + request.getUri());
    String path = uri.getPath();

    System.out.println("# received request: " + uri);

    /*
     * TODO:
     * - if GET "$base/entities/create", return entity creation form
     * - if GET "$base/entities/" || GET "$base/.well-known/servers" return entity list
     * - if POST "$base/entities/", call createEntity($postdata)
     *
     * - if GET "$base/sources/", return sources list
     * - if GET "$base/sources/edit" return sources edit form
     * - if POST "$base/sources/", call setSources($postdata)
     */

    if (uri.equals(entityManager.normalizeURI(pathPrefix + "/list-entities"))) {
      if (request.getMethod() == HttpMethod.GET) {
        StringBuffer sb = new StringBuffer();
        sb.append("<html><head><title>SLSE List</title></head><body><h1>SLSE List</h1><ol>");

        for (ServiceLevelSemanticEntity slse : this.slseCache.getAll()) {
          sb.append("<li>");
          sb.append(slse.getURI() + "<pre>" + slse.getDescribes() + "</pre>");
          sb.append("</li>");
        }

        sb.append("</ol></body></html>");

        Channels.write(ctx.getChannel(), Answer.create(sb.toString()));
      }
    } else if (uri.equals(entityManager.normalizeURI(pathPrefix + "/create-entity"))) {
      if (request.getMethod() == HttpMethod.GET) {
        // copy into
        // HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(),
        // HttpResponseStatus.OK);
        // response.setContent(ChannelBuffers.wrappedBuffer(htmlContent));
        Channels.write(ctx.getChannel(), Answer.create(new String(htmlContent)));
        //				Channels.write(ctx.getChannel(),
        //						Answer.create(new File("data/slse/ui/create_entity_form.html")));
      } else if (request.getMethod() == HttpMethod.POST) {
        QueryStringDecoder qsd =
            new QueryStringDecoder(
                "http://blub.blah/?" + request.getContent().toString(Charset.defaultCharset()));

        String elementsQuery = "";
        String name = "";
        boolean dependsOnSensorValues = false;
        boolean multiNodeQuery = false;

        for (Map.Entry<String, List<String>> entry : qsd.getParameters().entrySet()) {
          String key = entry.getKey();
          for (String value : entry.getValue()) {
            if (entry.getKey().equals("elementsQuery")) {
              elementsQuery = value;
            } else if (entry.getKey().equals("name")) {
              name = value;
            } else if (key.equals("dependsOnSensorValues") && value.equals("yes")) {
              dependsOnSensorValues = true;
            } else if (key.equals("multiNodeQuery") && value.equals("yes")) {
              multiNodeQuery = true;
            }
          }
        }

        System.out.println(
            "# adding rule: name="
                + name
                + " dependsOnSensorValues="
                + dependsOnSensorValues
                + " multiNodeQuery="
                + multiNodeQuery);

        slseBuilder.addRule(name, elementsQuery, dependsOnSensorValues, multiNodeQuery);

        Channels.write(
            ctx.getChannel(),
            Answer.create(
                "<html><head><meta http-equiv=\"REFRESH\" content=\"0;url=/\"></head></html>"));
      }
    } else {
      uri = entityManager.toThing(uri);
      Model r = ModelFactory.createDefaultModel();

      if (waitForPolling) {
        //    System.out.println("# waiting for esecache: " + uri);
        synchronized (eseCache) {
          while (!eseCache.isPollComplete()) {
            try {
              eseCache.wait(1000);
            } catch (InterruptedException ex) {
              ex
                  .printStackTrace(); // To change body of catch statement use File | Settings |
                                      // File Templates.
            }
          }
        }
      }

      //  System.out.println("# waiting for slseCache: " + uri);
      synchronized (slseCache) {
        if (slseCache.get(uri.toString()) == null) {
          System.out.println(
              "! SLSE not found in cache: " + uri.toString() + " returning empty model");
        } else {
          r.add(slseCache.get(uri.toString()).getModel());
        }
      }
      ChannelFuture future = Channels.write(ctx.getChannel(), r);
      if (!HttpHeaders.isKeepAlive(request)) {
        future.addListener(ChannelFutureListener.CLOSE);
      }
      // System.out.println("# done: " + uri);

    }
  }
    @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);
        }
      }
    }
Exemple #6
0
  public Request parseRequest(ChannelHandlerContext ctx, HttpRequest nettyRequest)
      throws Exception {
    Logger.trace("parseRequest: begin");
    Logger.trace("parseRequest: URI = " + nettyRequest.getUri());
    int index = nettyRequest.getUri().indexOf("?");
    String querystring = "";

    String uri = nettyRequest.getUri();
    // Remove domain and port from URI if it's present.
    if (uri.startsWith("http://") || uri.startsWith("https://")) {
      // Begins searching / after 9th character (last / of https://)
      uri = uri.substring(uri.indexOf("/", 9));
    }

    String path = URLDecoder.decode(uri, "UTF-8");
    if (index != -1) {
      path = URLDecoder.decode(uri.substring(0, index), "UTF-8");
      querystring = uri.substring(index + 1);
    }

    final Request request = new Request();

    request.remoteAddress = getRemoteIPAddress(ctx);
    request.method = nettyRequest.getMethod().getName();
    request.path = path;
    request.querystring = querystring;
    final String contentType = nettyRequest.getHeader(CONTENT_TYPE);
    if (contentType != null) {
      request.contentType = contentType.split(";")[0].trim().toLowerCase();
    } else {
      request.contentType = "text/html";
    }

    if (nettyRequest.getHeader("X-HTTP-Method-Override") != null) {
      request.method = nettyRequest.getHeader("X-HTTP-Method-Override").intern();
    }

    ChannelBuffer b = nettyRequest.getContent();
    if (b instanceof FileChannelBuffer) {
      FileChannelBuffer buffer = (FileChannelBuffer) b;
      // An error occurred
      Integer max =
          Integer.valueOf(Play.configuration.getProperty("play.netty.maxContentLength", "-1"));

      request.body = buffer.getInputStream();
      if (!(max == -1 || request.body.available() < max)) {
        request.body = new ByteArrayInputStream(new byte[0]);
      }

    } else {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      IOUtils.copy(new ChannelBufferInputStream(b), out);
      byte[] n = out.toByteArray();
      request.body = new ByteArrayInputStream(n);
    }

    request.url = uri;
    request.host = nettyRequest.getHeader(HOST);
    request.isLoopback =
        ((InetSocketAddress) ctx.getChannel().getRemoteAddress()).getAddress().isLoopbackAddress()
            && request.host.matches("^127\\.0\\.0\\.1:?[0-9]*$");

    if (request.host == null) {
      request.host = "";
      request.port = 80;
      request.domain = "";
    } else {
      if (request.host.contains(":")) {
        final String[] host = request.host.split(":");
        request.port = Integer.parseInt(host[1]);
        request.domain = host[0];
      } else {
        request.port = 80;
        request.domain = request.host;
      }
    }

    if (Play.configuration.containsKey("XForwardedSupport")
        && nettyRequest.getHeader("X-Forwarded-For") != null) {
      if (!Arrays.asList(
              Play.configuration.getProperty("XForwardedSupport", "127.0.0.1").split(","))
          .contains(request.remoteAddress)) {
        throw new RuntimeException(
            "This proxy request is not authorized: " + request.remoteAddress);
      } else {
        request.secure =
            ("https".equals(Play.configuration.get("XForwardedProto"))
                || "https".equals(nettyRequest.getHeader("X-Forwarded-Proto"))
                || "on".equals(nettyRequest.getHeader("X-Forwarded-Ssl")));
        if (Play.configuration.containsKey("XForwardedHost")) {
          request.host = (String) Play.configuration.get("XForwardedHost");
        } else if (nettyRequest.getHeader("X-Forwarded-Host") != null) {
          request.host = nettyRequest.getHeader("X-Forwarded-Host");
        }
        if (nettyRequest.getHeader("X-Forwarded-For") != null) {
          request.remoteAddress = nettyRequest.getHeader("X-Forwarded-For");
        }
      }
    }

    addToRequest(nettyRequest, request);

    request.resolveFormat();

    request._init();

    Logger.trace("parseRequest: end");
    return request;
  }
Exemple #7
0
  private void websocketHandshake(final ChannelHandlerContext ctx, HttpRequest req, MessageEvent e)
      throws Exception {

    // Create the WebSocket handshake response.
    HttpResponse res =
        new DefaultHttpResponse(
            HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
    res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);
    res.addHeader(CONNECTION, HttpHeaders.Values.UPGRADE);

    // Fill in the headers and contents depending on handshake method.
    if (req.containsHeader(SEC_WEBSOCKET_KEY1) && req.containsHeader(SEC_WEBSOCKET_KEY2)) {
      // New handshake method with a challenge:
      res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
      res.addHeader(
          SEC_WEBSOCKET_LOCATION, "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri());
      String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
      if (protocol != null) {
        res.addHeader(SEC_WEBSOCKET_PROTOCOL, protocol);
      }

      // Calculate the answer of the challenge.
      String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
      String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
      int a =
          (int)
              (Long.parseLong(key1.replaceAll("[^0-9]", ""))
                  / key1.replaceAll("[^ ]", "").length());
      int b =
          (int)
              (Long.parseLong(key2.replaceAll("[^0-9]", ""))
                  / key2.replaceAll("[^ ]", "").length());
      long c = req.getContent().readLong();
      ChannelBuffer input = ChannelBuffers.buffer(16);
      input.writeInt(a);
      input.writeInt(b);
      input.writeLong(c);
      try {
        ChannelBuffer output =
            ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array()));
        res.setContent(output);
      } catch (NoSuchAlgorithmException ex) {
        throw new UnexpectedException(ex);
      }
    } else {
      // Old handshake method with no challenge:
      res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
      res.addHeader(
          WEBSOCKET_LOCATION, "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri());
      String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
      if (protocol != null) {
        res.addHeader(WEBSOCKET_PROTOCOL, protocol);
      }
    }

    // Keep the original request
    Http.Request request = parseRequest(ctx, req);

    // Route the websocket request
    request.method = "WS";
    Map<String, String> route = Router.route(request.method, request.path);
    if (!route.containsKey("action")) {
      // No route found to handle this websocket connection
      ctx.getChannel()
          .write(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND));
      return;
    }

    // Upgrade the connection and send the handshake response.
    ChannelPipeline p = ctx.getChannel().getPipeline();
    p.remove("aggregator");
    p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder());

    // Connect
    ctx.getChannel().write(res);

    p.replace("encoder", "wsencoder", new WebSocketFrameEncoder());
    req.setMethod(new HttpMethod("WEBSOCKET"));

    // Inbound
    Http.Inbound inbound =
        new Http.Inbound() {

          @Override
          public boolean isOpen() {
            return ctx.getChannel().isOpen();
          }
        };
    channels.put(ctx, inbound);

    // Outbound
    Http.Outbound outbound =
        new Http.Outbound() {

          final List<ChannelFuture> writeFutures =
              Collections.synchronizedList(new ArrayList<ChannelFuture>());
          Promise<Void> closeTask;

          synchronized void writeAndClose(ChannelFuture writeFuture) {
            if (!writeFuture.isDone()) {
              writeFutures.add(writeFuture);
              writeFuture.addListener(
                  new ChannelFutureListener() {

                    public void operationComplete(ChannelFuture cf) throws Exception {
                      writeFutures.remove(cf);
                      futureClose();
                    }
                  });
            }
          }

          void futureClose() {
            if (closeTask != null && writeFutures.isEmpty()) {
              closeTask.invoke(null);
            }
          }

          @Override
          public void send(String data) {
            if (!isOpen()) {
              throw new IllegalStateException("The outbound channel is closed");
            }
            writeAndClose(ctx.getChannel().write(new DefaultWebSocketFrame(data)));
          }

          @Override
          public void send(byte opcode, byte[] data, int offset, int length) {
            if (!isOpen()) {
              throw new IllegalStateException("The outbound channel is closed");
            }
            writeAndClose(
                ctx.getChannel()
                    .write(new DefaultWebSocketFrame(opcode, wrappedBuffer(data, offset, length))));
          }

          @Override
          public synchronized boolean isOpen() {
            return ctx.getChannel().isOpen() && closeTask == null;
          }

          @Override
          public synchronized void close() {
            closeTask = new Promise<Void>();
            closeTask.onRedeem(
                new Action<Promise<Void>>() {

                  public void invoke(Promise<Void> completed) {
                    writeFutures.clear();
                    ctx.getChannel().disconnect();
                    closeTask = null;
                  }
                });
            futureClose();
          }
        };

    Invoker.invoke(new WebSocketInvocation(route, request, inbound, outbound, ctx, e));
  }
  @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);
      }
    }
  }