NanoHttpd / nanohttpd

Tiny, easily embeddable HTTP server in Java.
http://nanohttpd.org
BSD 3-Clause "New" or "Revised" License
6.92k stars 1.69k forks source link

The solution of "Explicit termination medthod 'end' not called" on android #492

Open fcp12138 opened 6 years ago

fcp12138 commented 6 years ago

use NanoHttpd on android,I find this error:

03-14 17:28:25.858 E/StrictMode( 2530): java.lang.Throwable: Explicit termination method 'end' not called 03-14 17:28:25.858 E/StrictMode( 2530): at dalvik.system.CloseGuard.open(CloseGuard.java:184) 03-14 17:28:25.858 E/StrictMode( 2530): at java.util.zip.Deflater.<init>(Deflater.java:192) 03-14 17:28:25.858 E/StrictMode( 2530): at java.util.zip.GZIPOutputStream.<init>(GZIPOutputStream.java:81) 03-14 17:28:25.858 E/StrictMode( 2530): at java.util.zip.GZIPOutputStream.<init>(GZIPOutputStream.java:54) 03-14 17:28:25.858 E/StrictMode( 2530): at org.nanohttpd.protocols.http.response.Response.sendBodyWithCorrectEncoding(Response.java:308) 03-14 17:28:25.858 E/StrictMode( 2530): at org.nanohttpd.protocols.http.response.Response.sendBodyWithCorrectTransferAndEncoding(Response.java:299) 03-14 17:28:25.858 E/StrictMode( 2530): at org.nanohttpd.protocols.http.response.Response.send(Response.java:268) 03-14 17:28:25.858 E/StrictMode( 2530): at org.nanohttpd.protocols.http.HTTPSession.execute(HTTPSession.java:435) 03-14 17:28:25.858 E/StrictMode( 2530): at org.nanohttpd.protocols.http.ClientHandler.run(ClientHandler.java:75) 03-14 17:28:25.858 E/StrictMode( 2530): at java.lang.Thread.run(Thread.java:818)

And this is My solution

For more details

org.nanohttpd.protocols.http.response.Response

private OutputStream sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException { if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream); OutputStream end = sendBodyWithCorrectEncoding(chunkedOutputStream, -1); chunkedOutputStream.finish(); return end; } else { return sendBodyWithCorrectEncoding(outputStream, pending); } } private OutputStream sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException { if (useGzipWhenAccepted()) { GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); sendBody(gzipOutputStream, -1); gzipOutputStream.finish(); return gzipOutputStream; } else { sendBody(outputStream, pending); return outputStream; } }

` /**

`


org.nanohttpd.protocols.http.HTTPSession

` @Override public void execute() throws IOException { Response r = null; try { // Read the first 8192 bytes. // The full header should fit in here. // Apache's default header limit is 8KB. // Do NOT assume that a single read will get the entire header // at once! byte[] buf = new byte[HTTPSession.BUFSIZE]; this.splitbyte = 0; this.rlen = 0; int read = -1; this.inputStream.mark(HTTPSession.BUFSIZE); try { read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); } catch (SSLException e) { throw e; } catch (IOException e) { NanoHTTPD.safeClose(this.inputStream); NanoHTTPD.safeClose(this.outputStream); throw new SocketException("NanoHttpd Shutdown"); } if (read == -1) { // socket was been closed NanoHTTPD.safeClose(this.inputStream); NanoHTTPD.safeClose(this.outputStream); throw new SocketException("NanoHttpd Shutdown"); } while (read > 0) { this.rlen += read; this.splitbyte = findHeaderEnd(buf, this.rlen); if (this.splitbyte > 0) { break; } read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); }

        if (this.splitbyte < this.rlen) {
            this.inputStream.reset();
            this.inputStream.skip(this.splitbyte);
        }

        this.parms = new HashMap<String, List<String>>();
        if (null == this.headers) {
            this.headers = new HashMap<String, String>();
        } else {
            this.headers.clear();
        }

        // Create a BufferedReader for parsing the header.
        BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen)));

        // Decode the header into parms and header java properties
        Map<String, String> pre = new HashMap<String, String>();
        decodeHeader(hin, pre, this.parms, this.headers);

        if (null != this.remoteIp) {
            this.headers.put("remote-addr", this.remoteIp);
            this.headers.put("http-client-ip", this.remoteIp);
        }

        this.method = Method.lookup(pre.get("method"));
        if (this.method == null) {
            throw new ResponseException(Status.BAD_REQUEST, "BAD REQUEST: Syntax error. HTTP verb " + pre.get("method") + " unhandled.");
        }

        this.uri = pre.get("uri");

        this.cookies = new CookieHandler(this.headers);

        String connection = this.headers.get("connection");
        boolean keepAlive = "HTTP/1.1".equals(protocolVersion) && (connection == null || !connection.matches("(?i).*close.*"));

        // Ok, now do the serve()

        // TODO: long body_size = getBodySize();
        // TODO: long pos_before_serve = this.inputStream.totalRead()
        // (requires implementation for totalRead())
        r = httpd.handle(this);
        // TODO: this.inputStream.skip(body_size -
        // (this.inputStream.totalRead() - pos_before_serve))

        if (r == null) {
            throw new ResponseException(Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
        } else {
            String acceptEncoding = this.headers.get("accept-encoding");
            this.cookies.unloadQueue(r);
            r.setRequestMethod(this.method);
            if (acceptEncoding == null || !acceptEncoding.contains("gzip")) {
                r.setUseGzip(false);
            }
            r.setKeepAlive(keepAlive);
            OutputStream end = r.send(this.outputStream);
            end.close();
        }
        if (!keepAlive || r.isCloseConnection()) {
            throw new SocketException("NanoHttpd Shutdown");
        }
    } catch (SocketException e) {
        // throw it out to close socket object (finalAccept)
        throw e;
    } catch (SocketTimeoutException ste) {
        // treat socket timeouts the same way we treat socket exceptions
        // i.e. close the stream & finalAccept object by throwing the
        // exception up the call stack.
        throw ste;
    } catch (SSLException ssle) {
        //Response resp = Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SSL PROTOCOL FAILURE: " + ssle.getMessage());
        //resp.send(this.outputStream);
        NanoHTTPD.safeClose(this.outputStream);
    } catch (IOException ioe) {
        //Response resp = Response.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
        //resp.send(this.outputStream);
        NanoHTTPD.safeClose(this.outputStream);
    } catch (ResponseException re) {
        //Response resp = Response.newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
        //resp.send(this.outputStream);
        NanoHTTPD.safeClose(this.outputStream);
    } finally {
        NanoHTTPD.safeClose(r);
        this.tempFileManager.clear();
    }
}

`

dblachut-adb commented 5 years ago

+1 on the issue. If I am not mistaken it should be enough to call gzipOutputStream.close(); after gzipOutputStream.finish(); in sendBodyWithCorrectEncoding?

Edit: I think I was wrong.

LordFokas commented 5 years ago

I think a recent PR fixed that. Do you guys mind testing with the current master branch?

mfrtrifork commented 2 years ago

@LordFokas will there be a new release with this? I see that there is no release since Aug 2016. Is this repo dead?