Monday, December 1, 2008

Take care when propagating transport headers through Camel!

Last week I worked with a fellow FUSE enthusiast on a very cool proof-of-concept showing how to route XML messages containing partially encrypted message payload. We used FUSE Services Framework (Apache CXF) to create a SOAP service and consumer, and used the nifty WSS4J interceptors (thanks to Glen Mazza's excellent blog entry on the subject) to encrypt selected elements of the SOAP message. CXF is such a great project - we got our service running really quickly without any trouble at all.

Feeling boisterous and buoyant with the taste of success, we forged ahead to put an intermediary Camel route between the service and the consumer, to do some content-based routing based on the non-encrypted part of the payload. This kind of X-Path routing is really easy to do with FUSE Medation Router (Apache Camel). Thinking that our coffee break was only minutes away we ran a quick test only to witness an exception on the SOAP consumer, deep in some HTTP-commons code: "bad chunk charachter '60'". Very strange, we thought: we knew that the unencrypted version worked fine. And, we know that it worked fine when the consumer talked directly with the service.

We investigated. A cursory look at the ASCII table shows that '60' is the code for '<'. Hmmm. Instantly this stank of something going on with the XML payload. But what? Our hopes for fresh coffee were now overrun by a burning desire to figure this out. We looked at all the angles, and eventually after an hour or so of scatching our heads, saw the light. The CXF consumer (the client!) was transmitting the payload using HTTP chunking: this has two effects at a payload level. First, a transport attribute, Transfer-Encoding, is set as "chunked"; second, the payload is then transmitted "chunked" by breaking it up into smaller chunks and transmitting each chunk preceded with a line containing a number indicating the size of the chunk.

CXF provides support for chunking, as do the Camel HTTP and Jetty components; so, the problem was not the chunking per se. Here's what was going on: the Camel route was receiving the payload and reassembling (or "unchunking") the content; however, our route was passing on the Transfer-Encoding header intact. So, when the payload arrived at the target service, the payload was unchunked, but the header suggested the opposite. The server read the first line of the payload to get the chunk size, got a '<', and correctly argued that this is an invalid character to describe the chunk size.

We disabled chunking on the client side in CXF, and then everything worked fine - effectively this just removes the problem by cutting it off at source. In general though, I think it raises a word of caution: be mindful of the headers you propagate through integration flows.

1 comment:

Gary Tully said...

This is a good argument for adding "Transfer-Encoding" to the default list of excluded headers in the camel http component. The current default values are listed in the HttpHeaderFilterStrategy.
I guess you could try supplying your own DefaultHeaderFilterStrategy as an alternative solution.