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()); } } }); } }
@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); } }
public static boolean isKeepAlive(HttpMessage message) { return HttpHeaders.isKeepAlive(message) && message.getProtocolVersion().equals(HttpVersion.HTTP_1_1); }
@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); }