HTTP-chunked-transfer

Ich Schreibe eine client-app, die eine Verbindung zu einem Dienst über das chunked transfer encoding. Der service gelegentlich trennt und mir wurde gesagt, es war deshalb, weil wir das senden einer null Stück in der Anfrage, so Tomcat schließt die Verbindung.

Ich bin mit dem Java HttpUrlConnection Klasse, um die Verbindung herzustellen und ich habe keine Ahnung, warum, es wäre das senden einer null-chunk und wie man es verhindern zu tun.

Hier ist der code.

URL m5url = new URL("https://hostedconnect.m5net.com/bobl/bobl?name=org.m5.apps.v1.cti.ClickToDial.subscribe");
StringBuffer sb = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"standalone=\"yes\"?>" 
                                   + "<Command>" 
                                   + "<Name>org.m5.apps.v1.cti.ClickToDial.subscribe</Name>"
                                   + "<Id>1</Id>" 
                                   + "<User>" + m5username + "</User>" 
                                   + "<Password>" + m5password + "</Password>" 
                                   + "<FormattedXml>true</FormattedXml>" 
                                   + "<ShallowResponse>FULL</ShallowResponse>" 
                                   + "</Command>");

conn = (HttpURLConnection) m5url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setReadTimeout(SESSION_TIMEOUT);
conn.setChunkedStreamingMode(0);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);

out = new DataOutputStream(conn.getOutputStream());
conn.connect();
out.writeBytes(sb.toString());
out.flush();

Wenn ich inputstream.readline es null, aber manchmal funktioniert es und manchmal nicht.

HTTP-chunked-transfer
Ok, so bin ich gründlich verwirrt. Ich habe aufgegeben mit der HttpURLConnection und begann mit der Socket-Klasse schreiben und alle Header und Daten manuell eingeben. Ohne senden der null-chunk-es scheint zu funktionieren die ganze Zeit. Mit der null-chunk-es schien zu arbeiten die ganze Zeit, außer wenn ich lief es in den debugger, wo Sie bekam die gleiche Fehlermeldung wie oben. Also habe ich ein sleep(100) nach sendet die Header und vor dem senden der Daten und es lief ohne debugger und es konsequent kam der Fehler. Also ich nehme an, in der HttpURLConnection-Klasse gibt es eine Verzögerung nach dem senden der Header, die ist, warum funktioniert es manchmal und nicht zu anderen Zeiten. Ich konnte einfach nicht senden, der null-chunk, aber ich würde wirklich gerne wissen, warum, dass würde den Fehler verursachen. Irgendwelche Ideen? Ich denke es ist ein bug in Tomcat.

Hier ist der code.

public class M5Connection
{
    public static final String urlBase = "/bobl/bobl";
    public static final String ENCODING = "ISO-8859-1";
    public static final String DELIMITER = "\r\n";
    protected URL url;
    private InputStream inputStream;
    protected OutputStream outputStream;
    protected Socket socket;
    protected BufferedReader reader;
    private boolean bProcessedHeaders;

    protected String resp = null;
    protected String errorMessage = null;


    /**
     * Start a new connection to the BOBL server.
     * @param server server name:port to connect to
     * @throws IOException
     */
    protected void initConnection(String server, int timeout) throws IOException
    {
        url = new URL(server + urlBase);
        int port = url.getPort();
        if (server.startsWith("https"))
        {
            if (port == -1) port = 443;
            else
                if (port == 80 || port == -1)port = 8080;
        }

        if (server.startsWith("https") == false)
        {
            socket = new Socket(url.getHost(), port);
        }
        else
        {           
            SocketFactory socketFactory = SSLSocketFactory.getDefault();
            socket = socketFactory.createSocket(url.getHost(), port);
        }

        socket.setSoTimeout(timeout);
        socket.setKeepAlive(true);
        socket.setSoLinger(false, 0);
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        reader = new BufferedReader(new InputStreamReader(inputStream));
    }

    public void initHttpsConnection(String server, int timeout) throws IOException
    {
        initConnection(server,timeout);
        sendHeaders();
        bProcessedHeaders = false;
    }

    private void sendHeaders() throws IOException {
        String path = url.getPath();
        StringBuffer outputBuffer = new StringBuffer();
        outputBuffer.append("POST " + path + " HTTP/1.1" + DELIMITER);
        outputBuffer.append("Host: " + url.getHost() + DELIMITER);
        outputBuffer.append("User-Agent: CometTest" + DELIMITER);
        outputBuffer.append("Connection: keep-alive" + DELIMITER);
        outputBuffer.append("Content-Type: text/plain" + DELIMITER);
        outputBuffer.append("Transfer-Encoding: chunked" + DELIMITER);
        outputBuffer.append(DELIMITER);
        byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING);
        outputStream.write(outputBytes);
        outputStream.flush();
    }

    /** Send some data to the server, HTTP/1.1 chunked style. */
    public void send(String chunkData) throws IOException {
        byte[] chunkBytes = chunkData.getBytes(ENCODING);
        String hexChunkLength = Integer.toHexString(chunkBytes.length);
        StringBuffer outputBuffer = new StringBuffer();
        outputBuffer.append(hexChunkLength);
        outputBuffer.append(DELIMITER);
        outputBuffer.append(chunkData);
        outputBuffer.append(DELIMITER);
        byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING);
        outputStream.write(outputBytes);
        outputStream.flush();

        outputBuffer = new StringBuffer();
        outputBuffer.append("0");
        outputBuffer.append(DELIMITER);
        outputBuffer.append(DELIMITER);
        outputBytes = outputBuffer.toString().getBytes(ENCODING);
        outputStream.write(outputBytes);
        outputStream.flush();
    }

    /**
     * Wait for a response from the server.
     * @return the string that the server returned.
     * @throws IOException
     */
    public String getRawResponse() throws IOException
    {
        String s;

        //just after we connect we expect to see the HTTP headers. Read and discard
        if (!bProcessedHeaders) {
            while (true){
                String line = reader.readLine();
                System.out.println("HEADER: " + line);

                if (line == null || line.equals("\r\n") || line.equals(""))
                    break;
            }
            bProcessedHeaders = true;
        }

        while (true)
        {       
            s = getChunk();     

            if (s == null)
                return null;

            if (s.equals("")) {
                continue;
            }

            //server will not emit XML if it is having real troubles
            if (s.charAt(0) != '<' || s.startsWith("<html>")) {
                System.out.println("Server says: " + s);
                continue;
            }
            return s;
        }
    }   

    /**
     * Expect chunked excoding back from the server. Read and return a chunk.
     * @return a string containing the HTTP chunk
     * @throws IOException
     */
    private String getChunk() throws IOException
    {
        StringBuffer buf = new StringBuffer();
        while (true)
        {
        //HTTP chunked mode, expect to see a line with the length in hex of the chunk that follows
            String s = reader.readLine();           

            if (s == null)
                throw new IOException();
            if (s.length() == 0)
                continue;

            int toread;
            try {
                toread = Integer.parseInt(s, 16);
            } catch (NumberFormatException e) {
                System.out.println("Number format error: " + s);
                return "";
            }

            if (toread == 0)
            {
                return null;
            }

            //read the chunk
            char[] data = new char[toread];
            int read = 0;
            while (read != toread)
            {
                read += reader.read(data, read, toread - read);
            }
            buf.append(data, 0, read);

            //for some reason tomcat only sends data in up to 8192 byte chunks
            if (toread != 8192)
                break;
        }
        return buf.toString();
    }   

    public void close()
    {
        try { socket.close(); } catch (IOException e) {}
    }

    public static void main(String[] args) throws Exception
    {
        M5Connection cnx = new M5Connection();
        cnx.initHttpsConnection("https://hostedconnect.m5net.com/bobl/bobl?name=org.m5.apps.v1.cti.ClickToDial.subscribe", 0);

        Thread.sleep(100);
        //
        //Create and send an XML command to listen for call state changes on our TN
        //
        String format = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
        "<Command>" +
        "    <Name>org.m5.apps.v1.cti.ClickToDial.subscribe</Name>" +
        "    <Id>1</Id>" +
        "    <User></User>" +
        "    <Password></Password>" +
        "    <FormattedXml>true</FormattedXml>" +
        "    <ShallowResponse>FULL</ShallowResponse>" +
        "</Command>";
        String command = format;
        System.out.println("SENDING " + command + "\n ------------ ");
        cnx.send(command);

        //
        //Now just wait for the responses
        //
        while (true)
        {
            String resp = cnx.getRawResponse();
            System.out.println(resp);
        }
    }
}
  • Sie haben versucht mit einer anderen Nummer in setChunkedStreamingMode()? Wenn ich verstehe dein problem richtig, dass Steuern würde die chunk-Größe nach docs.oracle.com/javase/7/docs/api/index.html.
  • Versuchen Sie anrufen connect rufen Sie vor getOutputStream. Ich habe noch nie gesehen es getan, in der Reihenfolge, die Sie verwenden und ich könnte mir vorstellen connect trampeln über die vorhandenen streams.
  • Ich habe versucht, die änderung der setchunkedstreamingmode und verschieben Sie die Verbindung, bevor die getoutputstream-und es ist noch immer das problem. Ich beigefügt ein Bild von der Anfrage und Antwort.
  • wahrscheinlich ein Fehler. Sie brauchen nicht mit einem "chunked" hier, denn die Länge ist festgelegt und bekannt.
  • Die Antwort muss sein, chunked, da die Länge unbekannt ist, sendet er die Pakete an uns, wie die Ereignisse kommen immer in Ihr system, so dass nicht der Wunsch den transfer-encoding chunked wenn die Antwort chunked? Dies ist meine erste Zeit den Umgang mit den chunked-encoding, also bin ich nicht sicher. Ich nahm die setchunkedstreamingmode und es hängt sich immer auf den inputstream. Wenn Sie sagen, bug, meinst du ein bug in Tomcat?
  • Die Länge der request-body bekannt ist, ist sb.length(). Sie müssen, um "Content-Length" - header. Siehe Beispiel xyzws.com/Javafaq/...
  • Und Ihre "Content-Type" ist definitiv falsch, obwohl der server scheint es zu ignorieren. Fragen Sie den service, was ist die erwartete Content-Type.
  • Der content-type ist text/plain, aber ja, Sie ignorieren es trotzdem. Ich versuchte, nicht senden es chunked bekam aber die Fehlermeldung "Fehler: Erwartet HTTP-chunked-encoding für lange Befehle ausführen", so dass Sie erwarten, es zu chunked.
  • Ich sehe nichts falsch mit der "0" Länge aufgeteilte block wird angezeigt, dass ist normal, aber möchten Sie vielleicht, um zu versuchen, beenden mit einer leeren Zeile w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
  • Wie würde ich es beenden mit einer leeren Zeile? Ich bin mir auch nicht sicher, warum es das schreiben der null-chunk.
  • ...Ich hatte geschrieben "beendet den string, den Sie übergeben haben, um Ihre StringBuffer() mit \r\n\r\n" aber ich glaube nicht, es würde einen Unterschied machen, weil das Bild, das Sie enthalten, scheint zu zeigen, eine leere Zeile, nach der 0. Nun, die null-chunk ist normal, siehe Abschnitt 3.6.1 klebte ich vor.
  • Ich fand heraus, dass sich der inputstream Ende der ouputstream. Das macht Sinn, denke ich.

InformationsquelleAutor user1309036 | 2013-02-27
Schreibe einen Kommentar