@Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent me) throws Exception {
    if (!(me.getMessage() instanceof HttpRequest)) {
      ctx.sendUpstream(me);
      return;
    }

    HttpRequest request = (HttpRequest) me.getMessage();
    Object response;

    // Look up resource
    String path = request.getUri();
    String host = request.getHeader("host");
    log.debug("Received request for path:" + path);

    boolean showHtml = false;
    if (path.endsWith("?html")) {
      showHtml = true;
      path = path.replace("?html", "");
    }

    for (String service : resources.keySet()) {
      log.debug("Available Service: " + service);
    }

    Model model = resources.get(path);

    if (model != null) {
      if (request.getMethod() == HttpMethod.GET) {
        if (showHtml) {
          String html = HtmlCreator.createModelPage(model, new URI(path), host);
          response = ChannelBuffers.wrappedBuffer(html.getBytes(Charset.forName("UTF-8")));

          log.debug("Returned html page for resource: " + path);
        } else {
          response = new SelfDescription(model, new URI(request.getUri()));

          log.debug("Resource found: " + path);
        }
      } else {
        response =
            new DefaultHttpResponse(
                request.getProtocolVersion(), HttpResponseStatus.METHOD_NOT_ALLOWED);

        log.debug("Method not allowed: " + request.getMethod());
      }
    } else {
      response =
          HttpResponseFactory.createHttpResponse(
              request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND);

      log.debug("Resource not found: " + path);
    }

    // Send response
    ChannelFuture future = Channels.write(ctx.getChannel(), response);
    future.addListener(ChannelFutureListener.CLOSE);
  }
  /**
   * Interface method implementation. Reads and processes Http commands sent to the service proxy.
   * Expects data in the Http protocol.
   *
   * @see
   *     org.jboss.netty.channel.SimpleChannelUpstreamHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext,
   *     org.jboss.netty.channel.ChannelEvent)
   */
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent)
      throws Exception {

    HttpRequest request = (HttpRequest) messageEvent.getMessage();
    LOGGER.debug("Request is: " + request.getMethod() + " " + request.getUri());

    this.processRequestHeaders(request);

    ChannelBuffer inputBuffer = request.getContent();
    byte[] requestData = new byte[inputBuffer.readableBytes()];
    inputBuffer.readBytes(requestData, 0, requestData.length);

    // Prepare request Wrapper
    HttpRequestWrapper executorHttpRequest = new HttpRequestWrapper();
    executorHttpRequest.setData(requestData);
    executorHttpRequest.setMethod(request.getMethod().toString());
    executorHttpRequest.setUri(request.getUri());
    executorHttpRequest.setHeaders(request.getHeaders());
    executorHttpRequest.setProtocol(request.getProtocolVersion().getProtocolName());
    executorHttpRequest.setMajorVersion(request.getProtocolVersion().getMajorVersion());
    executorHttpRequest.setMinorVersion(request.getProtocolVersion().getMinorVersion());

    // executor
    String proxy = this.proxyMap.get(this.getRoutingKey(request));
    if (proxy == null) {
      proxy = this.proxyMap.get(RoutingHttpChannelHandler.ALL_ROUTES);
      LOGGER.info(
          "Routing key for : " + request.getUri() + " returned null. Using default proxy instead.");
    }
    Executor executor = this.repository.getExecutor(proxy, proxy, executorHttpRequest);

    // execute
    HttpResponse response = null;
    try {
      response = (HttpResponse) executor.execute();
    } catch (Exception e) {
      throw new RuntimeException(
          "Error in executing HTTP request:" + proxy + " URI:" + request.getUri(), e);
    } finally {

      // Publishes event both in case of success and failure.
      Class eventSource = (executor == null) ? this.getClass() : executor.getClass();
      if (eventProducer != null)
        eventProducer.publishEvent(executor, request.getUri(), eventSource, HTTP_HANDLER);
      else LOGGER.debug("eventProducer not set, not publishing event");
    }

    // send response
    writeCommandExecutionResponse(ctx, messageEvent, response);
  }
 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;
 }
  private void sendHttpResponse(Channel channel, HttpRequest request, HttpResponse response) {
    if (!channel.isOpen()) {
      return;
    }

    // response的内容已在各Listener中填充
    response.headers().set(Names.CONTENT_LENGTH, response.getContent().readableBytes());
    response.headers().set(Names.SERVER, "NAVI/1.1.4(UNIX)");

    if (!HttpHeaders.isKeepAlive(request)
        || response.getStatus() != HttpResponseStatus.OK
        || ServerConfigure.isChannelClose()) {
      response.headers().set(Names.CONNECTION, "close");
      channel.setAttachment(WRITING);
      ChannelFuture f = channel.write(response);
      f.addListener(ChannelFutureListener.CLOSE);
      f.addListener(
          new ChannelFutureListener() {

            public void operationComplete(ChannelFuture f) throws Exception {
              if (!f.isSuccess()) {
                log.error(f.getCause().getMessage(), f.getCause());
              }
            }
          });
    } else {
      if (request.getProtocolVersion() == HttpVersion.HTTP_1_0) {
        response.headers().add(Names.CONNECTION, "Keep-Alive");
      }
      channel.setAttachment(WRITING);
      ChannelFuture f = channel.write(response);
      f.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
      f.addListener(
          new ChannelFutureListener() {

            public void operationComplete(ChannelFuture f) throws Exception {
              if (!f.isSuccess()) {
                log.error(f.getCause().getMessage(), f.getCause());
              }
            }
          });
    }
  }
  private void writeResponse(
      ChannelHandlerContext ctx, MessageEvent e, RequestV2 request, InetAddress ia) {
    // Decide whether to close the connection or not.
    boolean close =
        HttpHeaders.Values.CLOSE.equalsIgnoreCase(
                nettyRequest.headers().get(HttpHeaders.Names.CONNECTION))
            || nettyRequest.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
                && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(
                    nettyRequest.headers().get(HttpHeaders.Names.CONNECTION));

    // Build the response object.
    HttpResponse response;
    if (request.getLowRange() != 0 || request.getHighRange() != 0) {
      response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.PARTIAL_CONTENT);
    } else {
      String soapAction = nettyRequest.headers().get("SOAPACTION");

      if (soapAction != null && soapAction.contains("X_GetFeatureList")) {
        LOGGER.debug("Invalid action in SOAPACTION: " + soapAction);
        response =
            new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
      } else {
        response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
      }
    }

    StartStopListenerDelegate startStopListenerDelegate =
        new StartStopListenerDelegate(ia.getHostAddress());
    // Attach it to the context so it can be invoked if connection is reset unexpectedly
    ctx.setAttachment(startStopListenerDelegate);

    try {
      request.answer(response, e, close, startStopListenerDelegate);
    } catch (IOException e1) {
      LOGGER.trace("HTTP request V2 IO error: " + e1.getMessage());
      // note: we don't call stop() here in a finally block as
      // answer() is non-blocking. we only (may) need to call it
      // here in the case of an exception. it's a no-op if it's
      // already been called
      startStopListenerDelegate.stop();
    }
  }
  @Override
  public void handle(HttpRequest request, Channel channel) throws Exception {
    // 设置调用方ip,X-Forwarded-For 是使用了代理(如nginx)会附加在HTTP头域上的
    if (request.headers().contains("X-Forwarded-For")) {
      request.headers().set(Names.HOST, request.headers().get("X-Forwarded-For"));
    } else {
      InetSocketAddress insocket = (InetSocketAddress) channel.getRemoteAddress();
      request.headers().set(Names.HOST, insocket.getAddress().getHostAddress());
    }

    HttpResponse response =
        new NaviHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK);

    for (INaviHttpRequestListener listener : listeners) {
      boolean res = listener.process(request, response);
      log.debug(listener.getClass().getName() + " is completed!");
      if (!res) {
        break;
      }
    }

    sendHttpResponse(channel, request, response);
  }
Example #7
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);
   }
 }
  @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);
    }
  }
  /** 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);
  }
  @Override
  protected void httpMessageReceived(
      ChannelHandlerContext ctx, MessageEvent e, HttpRequest httpRequest) throws Exception {

    HttpVersion version = httpRequest.getProtocolVersion();
    URI httpLocation = getEffectiveURI(httpRequest);
    if (httpLocation == null) {
      // see RFC-7230 section 5.4 Host
      HttpResponse httpResponse = new DefaultHttpResponse(version, BAD_REQUEST);
      ChannelFuture future = future(ctx.getChannel());
      write(ctx, future, httpResponse);
      return;
    }

    // channel's local address is resolved address so get the bind address from
    // server channel's attachment
    ChannelAddress transportCandidate =
        (ChannelAddress) ctx.getChannel().getParent().getAttachment();
    ChannelAddress candidate = new ChannelAddress(httpLocation, transportCandidate);

    Entry<ChannelAddress, HttpServerChannel> httpBinding = httpBindings.floorEntry(candidate);

    if (httpBinding == null) {
      HttpResponse httpResponse = new DefaultHttpResponse(version, NOT_FOUND);
      ChannelFuture future = future(ctx.getChannel());
      write(ctx, future, httpResponse);
      return;
    }

    HttpServerChannel parent = httpBinding.getValue();
    ChannelFactory factory = parent.getFactory();
    ChannelConfig config = parent.getConfig();
    ChannelPipelineFactory pipelineFactory = config.getPipelineFactory();
    ChannelPipeline pipeline = pipelineFactory.getPipeline();
    ChannelAddress httpLocalAddress = parent.getLocalAddress();

    Channel transport = ctx.getChannel();
    ChannelAddress remoteAddress = remoteAddress(transport);
    ChannelAddress httpRemoteAddress = new ChannelAddress(httpLocation, remoteAddress, true);

    HttpChildChannelSink sink = new HttpChildChannelSink(transport);
    HttpChildChannel httpChildChannel = new HttpChildChannel(parent, factory, pipeline, sink);
    HttpChannelConfig httpChildConfig = httpChildChannel.getConfig();
    httpChildConfig.setMethod(httpRequest.getMethod());
    httpChildConfig.setVersion(version);
    httpChildConfig.getReadHeaders().set(httpRequest.headers());
    httpChildConfig.setReadQuery(new QueryStringDecoder(httpRequest.getUri()));
    httpChildConfig.setWriteQuery(new QueryStringEncoder(httpRequest.getUri()));
    httpChildConfig.setStatus(HttpResponseStatus.OK);

    this.httpChildChannel = httpChildChannel;

    ChannelBuffer content = httpRequest.getContent();

    // update read state before firing channel events
    if (isTransferEncodingChunked(httpRequest)) {
      httpChildChannel.readState(HttpReadState.CONTENT_CHUNKED);
    } else if (isContentLengthSet(httpRequest)) {
      long contentLength = getContentLength(httpRequest);
      contentLength -= content.readableBytes();
      if (contentLength > 0) {
        httpChildChannel.readState(HttpReadState.CONTENT_CHUNKED);
      } else {
        httpChildChannel.readState(HttpReadState.CONTENT_COMPLETE);
      }
    } else {
      // see RFC-7230 section 3.3
      // content indicated by presence of Content-Length or Transfer-Encoding
      httpChildChannel.readState(HttpReadState.CONTENT_COMPLETE);
    }

    fireChannelOpen(httpChildChannel);

    httpChildChannel.setLocalAddress(httpLocalAddress);
    httpChildChannel.setBound();
    fireChannelBound(httpChildChannel, httpLocalAddress);

    httpChildChannel.setRemoteAddress(httpRemoteAddress);
    httpChildChannel.setConnected();
    fireChannelConnected(httpChildChannel, httpRemoteAddress);

    if (content.readable()) {
      fireMessageReceived(httpChildChannel, content);
    }

    // note: status may be set in reaction to one of the above events, such as CONNECTED
    //       so defer status code check until this point
    if (httpChildConfig.getStatus().getCode() == SWITCHING_PROTOCOLS.getCode()) {
      httpChildChannel.readState(HttpReadState.UPGRADED);
    }

    switch (httpChildChannel.readState()) {
      case CONTENT_COMPLETE:
        fireInputShutdown(httpChildChannel);
        this.httpChildChannel = null;
        if (httpChildChannel.setReadClosed()) {
          fireChannelDisconnected(httpChildChannel);
          fireChannelUnbound(httpChildChannel);
          fireChannelClosed(httpChildChannel);
        }
        break;
      default:
        break;
    }
  }
  @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);
  }