Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 1.0 RC2
    • Fix Version/s: 1.0.1
    • Component/s: Core
    • Labels:
      None
    • Environment:
      JUnit class testing a remote soap web service.

      Description

      When trying to use gzip compression with a spring-ws client, i get this exception:

      2007-08-10 16:06:40,375 INFO [org.springframework.ws.soap.saaj.SaajSoapMessageFactory::afterPropertiesSet] - <Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol>
      java.io.IOException: Not in GZIP format
      at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:132)
      at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:58)
      at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:68)
      at org.springframework.ws.transport.http.AbstractHttpSenderConnection.getResponseInputStream(AbstractHttpSenderConnection.java:78)
      at org.springframework.ws.transport.AbstractSenderConnection$ResponseTransportInputStream.createInputStream(AbstractSenderConnection.java:100)
      at org.springframework.ws.transport.TransportInputStream.getInputStream(TransportInputStream.java:42)
      at org.springframework.ws.transport.TransportInputStream.read(TransportInputStream.java:79)
      at com.sun.xml.messaging.saaj.util.ByteOutputStream.write(ByteOutputStream.java:80)
      at com.sun.xml.messaging.saaj.util.JAXMStreamSource.<init>(JAXMStreamSource.java:50)
      at com.sun.xml.messaging.saaj.soap.SOAPPartImpl.setContent(SOAPPartImpl.java:227)
      at com.sun.xml.messaging.saaj.soap.MessageImpl.init(MessageImpl.java:364)
      at com.sun.xml.messaging.saaj.soap.MessageImpl.<init>(MessageImpl.java:273)
      at com.sun.xml.messaging.saaj.soap.ver1_1.Message1_1Impl.<init>(Message1_1Impl.java:68)
      at com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl.createMessage(SOAPMessageFactory1_1Impl.java:62)
      at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:163)
      at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:52)
      at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:408)
      at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:357)
      at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:305)
      at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:296)
      ....

      I tried to debug a little. This is my client code:

      DOMResult responseResult = new DOMResult(documentResponse);
      boolean err = !getWebServiceTemplate().sendSourceAndReceiveToResult(
      new DOMSource(document), new SoapActionCallback("findByIdOperation"),
      responseResult);
      if (err)
      throw new Exception("blah blah blah");

      What happens (approximatively) is:

      • the soap request is sent to the remote server
      • response comes back with a Content-Encoding http header set to "gzip" and payload is gzipped
      • in org.springframework.ws.transport.http.AbstractHttpSenderConnection, getResponseInputStream() is called:

      protected final InputStream getResponseInputStream() throws IOException {
      InputStream inputStream;
      if (responseBuffer != null)

      { inputStream = new ByteArrayInputStream(responseBuffer); }
      else { inputStream = getRawResponseInputStream(); }
      return isGzipResponse() ? new GZIPInputStream(inputStream) : inputStream;
      }

      /** Determine whether the given response is a GZIP response. */
      private boolean isGzipResponse() throws IOException {
      for (Iterator iterator = getResponseHeaders(HttpTransportConstants.HEADER_CONTENT_ENCODING);
      iterator.hasNext() { String encodingHeader = (String) iterator.next(); return encodingHeader.toLowerCase().indexOf(HttpTransportConstants.CONTENT_ENCODING_GZIP) != -1; }
      return false;
      }

      The first time the function is called, responseBuffer is null, so a raw input stream is created and wrapped into a gzip input stream (since isGzipResponse() return true). Next time function is called, responseBuffer (which holds the now unzipped payload) is wrapped into a bytearray input stream ... but also into a gzip input stream (since isGzipResponse() still returns true). Gzip complains about not finding the magic bytes at the beginning of the stream (which in fact starts by "<?xml version=").

      At first glance, getResponseInputStream() should be (but couldn't test it so don't know about side effects :-O):

      if (responseBuffer != null) { inputStream = new ByteArrayInputStream(responseBuffer); }

      else

      { inputStream = getRawResponseInputStream(); if(isGzipResponse()) inputStream = new GZIPInputStream(inputStream); }

      return inputStream;

      Best regards.

        Activity

        Hide
        arjen.poutsma Arjen Poutsma added a comment -

        I'm having a hard time reproducing this with Jetty. Could you tell me which Web container this bug occurs on?

        Basically, the responseBuffer is only filled when the response content length is -1:

        protected final boolean hasResponse() throws IOException {
        if (getResponseCode() == HttpTransportConstants.STATUS_ACCEPTED)

        { return false; }

        long contentLength = getResponseContentLength();
        if (contentLength < 0) {
        if (responseBuffer == null)

        { responseBuffer = FileCopyUtils.copyToByteArray(getResponseInputStream()); }

        contentLength = responseBuffer.length;
        }
        return contentLength > 0;
        }

        Note that responseBuffer contains the raw, zipped bytes.

        So either the server returns -1, and we have a responseBuffer with the raw bytes, or it returns the proper length of the response, and we can use whatever getRawResponseInputStream() returns. This behavior is the same every time you call getResponseInputStream(): the first call would do the same as the second call (either the buffered result or the raw stream). However, Spring-WS will invoke it only once.

        Could you please debug your unit test and see what responseBuffer contains?

        Show
        arjen.poutsma Arjen Poutsma added a comment - I'm having a hard time reproducing this with Jetty. Could you tell me which Web container this bug occurs on? Basically, the responseBuffer is only filled when the response content length is -1: protected final boolean hasResponse() throws IOException { if (getResponseCode() == HttpTransportConstants.STATUS_ACCEPTED) { return false; } long contentLength = getResponseContentLength(); if (contentLength < 0) { if (responseBuffer == null) { responseBuffer = FileCopyUtils.copyToByteArray(getResponseInputStream()); } contentLength = responseBuffer.length; } return contentLength > 0; } Note that responseBuffer contains the raw, zipped bytes. So either the server returns -1, and we have a responseBuffer with the raw bytes, or it returns the proper length of the response, and we can use whatever getRawResponseInputStream() returns. This behavior is the same every time you call getResponseInputStream(): the first call would do the same as the second call (either the buffered result or the raw stream). However, Spring-WS will invoke it only once. Could you please debug your unit test and see what responseBuffer contains?
        Hide
        arjen.poutsma Arjen Poutsma added a comment -

        Moving to 1.0.1 for now.

        Show
        arjen.poutsma Arjen Poutsma added a comment - Moving to 1.0.1 for now.
        Hide
        jay54 Jay Bertrand added a comment -

        Sorry for the delay, day(s) off Well, I'm working with Tomcat 5.5. When i run in debug mode, here's what i get:

        • hasResponse() is called, contentLength (=getResponseContentLength()) is -1 and responseBuffer is null
          -> responseBuffer = FileCopyUtils.copyToByteArray(getResponseInputStream())
          ---> getResponseInputStream() returns new GZIPInputStream(getRawResponseInputStream())
          -> responseBuffer holds the uncompressed payload (<?xml version="1.0" ...)
        • getResponseInputStream() is called for the second time, responseBuffer is not null
          -> inputStream = new ByteArrayInputStream(responseBuffer)
          -> isGzipResponse() is still true so the function returns new GZIPInputStream(inputStream)
        • java.io.IOException is thrown because the buffer is not in gzip (already uncompressed)

        Hope this will help.

        Show
        jay54 Jay Bertrand added a comment - Sorry for the delay, day(s) off Well, I'm working with Tomcat 5.5. When i run in debug mode, here's what i get: hasResponse() is called, contentLength (=getResponseContentLength()) is -1 and responseBuffer is null -> responseBuffer = FileCopyUtils.copyToByteArray(getResponseInputStream()) ---> getResponseInputStream() returns new GZIPInputStream(getRawResponseInputStream()) -> responseBuffer holds the uncompressed payload (<?xml version="1.0" ...) getResponseInputStream() is called for the second time, responseBuffer is not null -> inputStream = new ByteArrayInputStream(responseBuffer) -> isGzipResponse() is still true so the function returns new GZIPInputStream(inputStream) java.io.IOException is thrown because the buffer is not in gzip (already uncompressed) Hope this will help.
        Hide
        arjen.poutsma Arjen Poutsma added a comment -

        Fixed, the response buffer now really contains the raw, zipped content. I've added a unit testcase for this, and it now works when the response is compressed, and the content size is -1.

        Show
        arjen.poutsma Arjen Poutsma added a comment - Fixed, the response buffer now really contains the raw, zipped content. I've added a unit testcase for this, and it now works when the response is compressed, and the content size is -1.
        Hide
        arjen.poutsma Arjen Poutsma added a comment -

        Closing 1.0.1 issues.

        Show
        arjen.poutsma Arjen Poutsma added a comment - Closing 1.0.1 issues.

          People

          • Assignee:
            arjen.poutsma Arjen Poutsma
            Reporter:
            jay54 Jay Bertrand
          • Votes:
            1 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: