Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-5573

http-request is sent before PhaseInterceptorChain gets to send-phase if http-request is chunked (because its too long)

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Not A Problem
    • Affects Version/s: 2.7.5
    • Fix Version/s: None
    • Component/s: Bus, Core, JAX-RS, Transports
    • Labels:
      None
    • Environment:

      Spring
      Windows 7
      Tomcat 7.0.50
      JDK 1.7_51

    • Estimated Complexity:
      Unknown

      Description

      While I wrote some REST-Webservices that communicate among each other I came across some surprising behaviour during the PhaseInterceptor invocations.

      I wrote a PhaseInterceptor for the phase "MARSHAL" that attaches a custom HTTP header to each outgoing message. But it seemed that in some cases (in most others it worked fine) he failed to attach this header to outgoing HTTP-requests. After some investigating I saw that only HTTP messages which where chunked (because the body was too long) seemed to be missing the custom header. On HTTP messages that were not chunked (i.e. simply had a content-length header) everything worked as expected.

      Then I had a look at the cxf debug log messages and saw the problem.

      Log
      DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Chain org.apache.cxf.phase.PhaseInterceptorChain@4d184e was modified. Current flow:
        prepare-send [MessageSenderInterceptor]
        >>>write [ClientRequestFilterInterceptor, BodyWriter]
        >>>marshal [MyInterceptor]
        prepare-send-ending [MessageSenderEndingInterceptor]
      
      DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.jaxrs.client.spec.ClientRequestFilterInterceptor@14c8a89
      >>>DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter@11fdaab
      DEBUG org.apache.cxf.transport.http.Headers - Accept: text/plain
      DEBUG org.apache.cxf.transport.http.Headers - Connection: Keep-Alive
      DEBUG org.apache.cxf.transport.http.Headers - Content-Type: text/plain
      DEBUG org.apache.cxf.transport.http.HTTPConduit - No Trust Decider for Conduit '{http://<myRestService>.http-conduit'. An afirmative Trust Decision is assumed.
      >>>DEBUG org.apache.cxf.transport.http.HTTPConduit - Sending POST Message with Headers to http://localhost:8080/<mypath>/test/something Conduit :{http://<myRestService>.http-conduit
      
      >>>DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor mypackage.MyInterceptor@d5940e
      DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor@176e488
      

      For some reason the HTTP message was sent before my Interceptor got the chance to attach the custom header. The PhaseInterceptorChain only invoked my Interceptor after it was sent and added the custom header too late.

      I simply worked around this problem by setting the phase of the interceptor to "PRE_PROTOCOL" which is before the "WRITE" phase (where the HTTP message seems to be sent).

      While you can rather easily work around this problem, it is still something that should be fixed, so the next developer doesn't end up spending hours searching for the problem like I did.

      My Spring configuration contains something like this:

      Spring config
      <jaxrs:server address="/myservices">
          <jaxrs:serviceBeans>
            <bean class="mypackage.TestRestServiceImpl" />
          </jaxrs:serviceBeans>
      </jaxrs:server>
      
      ...
      
      
      <http-conf:conduit name=".*">
          <http-conf:client ConnectionTimeout="0" ReceiveTimeout="0" />
      </http-conf:conduit>
      
      <bean id="myInterceptor" class="mypackage.MyInterceptor" />
      
      <cxf:bus>
          <cxf:outInterceptors>
            <ref bean="myInterceptor" />
          </cxf:outInterceptors>
      </cxf:bus>
      

      Example code that shows the basics of my REST-Service

      MyInterceptor.java
      public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
        @Inject
        private MyContext myContext;
      
        public MyInterceptor() {
          super(Phase.MARSHAL);
        }
      
        @Override
        public void handleMessage(Message m) throws Fault {
      
          @SuppressWarnings("unchecked")
          MultivaluedMap<String, Object> headers = (MultivaluedMap<String, Object>) m.get(Message.PROTOCOL_HEADERS);
      
          if (headers == null) {
            headers = new MetadataMap<String, Object>();
            m.put(Message.PROTOCOL_HEADERS, headers);
          }
      
          headers.putSingle("X-myheader", myContext.generateHeader());
          m.put(Message.PROTOCOL_HEADERS, headers);
        }
      }
      
      TestRestService.java
      @Path("/test")
      @Consumes(MediaType.TEXT_PLAIN)
      public interface TestRestService {
      
        @Produces(value = MediaType.TEXT_PLAIN)
        @POST
        @Path("/something")
        public String doSomething(String order);
      }
      
      TestRestServiceImpl.java
      public class TestRestServiceImpl implements TestRestService {
        @Override
        public String doSomething(String msg) {
          return "i got your message '"+msg+"', thanks.";
        }
      }
      
      And here an example of how I send the HTTP request:
      
      
      ClientTest.java
      public class ClientTest {  
        public static void main(String [] args){
          TestRestService client = JAXRSClientFactory.create("http://localhost:8080/<mypath>/test/something", TestRestService.class);
          StringBuilder sb= new StringBuilder(1024*16);
          
          for(int i=1; i <= 16; i++){            
            sb.append(StringUtils.repeat("X", 1024));      
          }
          
          try{
            client.doSomething(sb.toString());
          }catch(Exception e){
            //just need to send it
          }
        }
      }
      

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              mikelman Michael Svoboda
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: