Search in sources :

Example 1 with StartStopListenerDelegate

use of net.pms.external.StartStopListenerDelegate in project UniversalMediaServer by UniversalMediaServer.

the class RequestHandler method run.

@Override
public void run() {
    Request request = null;
    StartStopListenerDelegate startStopListenerDelegate = new StartStopListenerDelegate(socket.getInetAddress().getHostAddress());
    try {
        int receivedContentLength = -1;
        String userAgentString = null;
        ArrayList<String> identifiers = new ArrayList<>();
        RendererConfiguration renderer = null;
        InetSocketAddress remoteAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
        InetAddress ia = remoteAddress.getAddress();
        boolean isSelf = ia.getHostAddress().equals(PMS.get().getServer().getHost());
        // Apply the IP filter
        if (filterIp(ia)) {
            throw new IOException("Access denied for address " + ia + " based on IP filter");
        }
        // 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.
        boolean unrecognized = renderer == null || !renderer.loaded;
        RendererConfiguration.SortedHeaderMap sortedHeaders = unrecognized ? new RendererConfiguration.SortedHeaderMap() : null;
        // Gather all the headers
        ArrayList<String> headerLines = new ArrayList<>();
        String line = br.readLine();
        while (line != null && line.length() > 0) {
            headerLines.add(line);
            if (sortedHeaders != null) {
                sortedHeaders.put(line);
            }
            line = br.readLine();
        }
        if (unrecognized) {
            // Attempt 2: try to recognize the renderer by matching headers
            renderer = RendererConfiguration.getRendererConfigurationByHeaders(sortedHeaders, ia);
        }
        for (String headerLine : headerLines) {
            // The request object is created inside the while loop.
            if (request != null && request.getMediaRenderer() == null && renderer != null) {
                request.setMediaRenderer(renderer);
            }
            if (headerLine.toUpperCase().startsWith("USER-AGENT")) {
                // Is the request from our own Cling service, i.e. self-originating?
                if (isSelf && headerLine.contains("UMS/")) {
                    // LOGGER.trace("Ignoring self-originating request from {}:{}", ia, remoteAddress.getPort());
                    return;
                }
                userAgentString = headerLine.substring(headerLine.indexOf(':') + 1).trim();
            } else if (renderer != null && headerLine.startsWith("X-PANASONIC-DMP-Profile:")) {
                PanasonicDmpProfiles.parsePanasonicDmpProfiles(headerLine, renderer);
            }
            try {
                StringTokenizer s = new StringTokenizer(headerLine);
                String temp = s.nextToken();
                if (temp.equals("SUBSCRIBE") || temp.equals("GET") || temp.equals("POST") || temp.equals("HEAD")) {
                    request = new Request(temp, s.nextToken().substring(1));
                    if (s.hasMoreTokens() && s.nextToken().equals("HTTP/1.0")) {
                        request.setHttp10(true);
                    }
                } else if (request != null && temp.toUpperCase().equals("CALLBACK:")) {
                    request.setSoapaction(s.nextToken());
                } else if (request != null && temp.toUpperCase().equals("SOAPACTION:")) {
                    request.setSoapaction(s.nextToken());
                } else if (headerLine.toUpperCase().contains("CONTENT-LENGTH:")) {
                    receivedContentLength = Integer.parseInt(headerLine.substring(headerLine.toUpperCase().indexOf("CONTENT-LENGTH: ") + 16));
                } 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 if (headerLine.toUpperCase().contains("TIMESEEKRANGE.DLNA.ORG: NPT=")) {
                    // firmware 2.50+
                    String timeseek = headerLine.substring(headerLine.toUpperCase().indexOf("TIMESEEKRANGE.DLNA.ORG: NPT=") + 28);
                    if (timeseek.endsWith("-")) {
                        timeseek = timeseek.substring(0, timeseek.length() - 1);
                    } else if (timeseek.indexOf('-') > -1) {
                        timeseek = timeseek.substring(0, timeseek.indexOf('-'));
                    }
                    request.setTimeseek(convertStringToTime(timeseek));
                } else if (headerLine.toUpperCase().contains("TIMESEEKRANGE.DLNA.ORG : NPT=")) {
                    // firmware 2.40
                    String timeseek = headerLine.substring(headerLine.toUpperCase().indexOf("TIMESEEKRANGE.DLNA.ORG : NPT=") + 29);
                    if (timeseek.endsWith("-")) {
                        timeseek = timeseek.substring(0, timeseek.length() - 1);
                    } else if (timeseek.indexOf('-') > -1) {
                        timeseek = timeseek.substring(0, timeseek.indexOf('-'));
                    }
                    request.setTimeseek(convertStringToTime(timeseek));
                } 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.
                    String lowerCaseHeaderLine = headerLine.toLowerCase();
                    for (String knownHeaderString : KNOWN_HEADERS) {
                        if (lowerCaseHeaderLine.startsWith(knownHeaderString.toLowerCase())) {
                            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.
                        identifiers.add(headerLine);
                    }
                }
            } catch (IllegalArgumentException e) {
                LOGGER.error("Error parsing HTTP headers: {}", e.getMessage());
                LOGGER.trace("", e);
            }
        }
        if (request != null) {
            // Still no media renderer recognized?
            if (renderer == 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.
                renderer = RendererConfiguration.resolve(ia, null);
                request.setMediaRenderer(renderer);
                if (renderer != null) {
                    LOGGER.debug("Using default media renderer \"{}\"", renderer.getConfName());
                    if (userAgentString != null && !userAgentString.equals("FDSSDP")) {
                        // We have found an unknown renderer
                        identifiers.add(0, "User-Agent: " + userAgentString);
                        renderer.setIdentifiers(identifiers);
                        LOGGER.info("Media renderer was not recognized. Possible identifying HTTP headers:\n{}", StringUtils.join(identifiers, "\n"));
                    }
                } else {
                    // it means we know via upnp that it's not really a renderer.
                    return;
                }
            } else if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Recognized media renderer \"{}\"", renderer.getRendererName());
            }
        }
        if (receivedContentLength > 0) {
            char[] buf = new char[receivedContentLength];
            br.read(buf);
            if (request != null) {
                String textContent = new String(buf);
                request.setTextContent(textContent);
                if (LOGGER.isTraceEnabled()) {
                    logMessageReceived(headerLines, textContent, socket.getRemoteSocketAddress(), renderer);
                }
            } else if (LOGGER.isTraceEnabled()) {
                logMessageReceived(headerLines, null, socket.getRemoteSocketAddress(), renderer);
            }
        }
        if (request != null) {
            request.answer(output, startStopListenerDelegate);
        }
        if (request != null && request.getInputStream() != null) {
            request.getInputStream().close();
        }
    } catch (IOException e) {
        LOGGER.error("Unexpected IO error in {}: {}", getClass().getName(), e.getMessage());
        // LOGGER.trace("", e);
        if (request != null && request.getInputStream() != null) {
            try {
                LOGGER.trace("Closing input stream: {}", request.getInputStream());
                request.getInputStream().close();
            } catch (IOException e1) {
                LOGGER.error("Error closing input stream: {}", e1);
                LOGGER.trace("", e1);
            }
        }
    } finally {
        try {
            output.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            LOGGER.error("Error closing connection: {}", e.getMessage());
            LOGGER.trace("", e);
        }
        startStopListenerDelegate.stop();
    }
}
Also used : InetSocketAddress(java.net.InetSocketAddress) ArrayList(java.util.ArrayList) IOException(java.io.IOException) StartStopListenerDelegate(net.pms.external.StartStopListenerDelegate) StringTokenizer(java.util.StringTokenizer) RendererConfiguration(net.pms.configuration.RendererConfiguration) InetAddress(java.net.InetAddress)

Example 2 with StartStopListenerDelegate

use of net.pms.external.StartStopListenerDelegate in project UniversalMediaServer by UniversalMediaServer.

the class RequestHandlerV2 method writeResponse.

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(request.isHttp10() ? HttpVersion.HTTP_1_0 : 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(request.isHttp10() ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
        } else {
            response = new DefaultHttpResponse(request.isHttp10() ? HttpVersion.HTTP_1_0 : 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.debug("HTTP request V2 IO error: " + e1.getMessage());
        LOGGER.trace("", e1);
        // 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();
    }
}
Also used : StartStopListenerDelegate(net.pms.external.StartStopListenerDelegate) IOException(java.io.IOException)

Example 3 with StartStopListenerDelegate

use of net.pms.external.StartStopListenerDelegate in project UniversalMediaServer by UniversalMediaServer.

the class WebRender method start.

public void start(DLNAResource dlna) {
    if (getPlayingRes() != dlna) {
        stop();
    }
    setPlayingRes(dlna);
    if (startStop == null) {
        startStop = new StartStopListenerDelegate(ip);
    }
    startStop.setRenderer(this);
    startStop.start(getPlayingRes());
}
Also used : StartStopListenerDelegate(net.pms.external.StartStopListenerDelegate)

Example 4 with StartStopListenerDelegate

use of net.pms.external.StartStopListenerDelegate in project UniversalMediaServer by UniversalMediaServer.

the class RequestHandlerV2 method exceptionCaught.

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
    Channel ch = e.getChannel();
    Throwable cause = e.getCause();
    if (cause instanceof TooLongFrameException) {
        sendError(ctx, HttpResponseStatus.BAD_REQUEST);
        return;
    }
    if (cause != null) {
        if (cause.getClass().equals(IOException.class)) {
            LOGGER.debug("Connection error: " + cause);
            StartStopListenerDelegate startStopListenerDelegate = (StartStopListenerDelegate) ctx.getAttachment();
            if (startStopListenerDelegate != null) {
                LOGGER.debug("Premature end, stopping...");
                startStopListenerDelegate.stop();
            }
        } else if (!cause.getClass().equals(ClosedChannelException.class)) {
            LOGGER.debug("Caught exception: {}", cause.getMessage());
            LOGGER.trace("", cause);
        }
    }
    if (ch.isConnected()) {
        sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
    }
    ch.close();
}
Also used : StartStopListenerDelegate(net.pms.external.StartStopListenerDelegate) TooLongFrameException(org.jboss.netty.handler.codec.frame.TooLongFrameException)

Aggregations

StartStopListenerDelegate (net.pms.external.StartStopListenerDelegate)4 IOException (java.io.IOException)2 InetAddress (java.net.InetAddress)1 InetSocketAddress (java.net.InetSocketAddress)1 ArrayList (java.util.ArrayList)1 StringTokenizer (java.util.StringTokenizer)1 RendererConfiguration (net.pms.configuration.RendererConfiguration)1 TooLongFrameException (org.jboss.netty.handler.codec.frame.TooLongFrameException)1