Search code examples
scalacordovanettyfinagle

Changing DynamicChannelBuffer in Netty to String and back to ChannelBuffer


My web server is written in Scala using Twitter's Finagle library, which in turn relies on Netty. As such, the request content is returned as a DynamicChannelBuffer. If I upload an image to the server using curl from the Terminal like this:

 curl -T "abc.jpg" http://127.0.0.1:8080/test/image

Then I can read, and forward the image to a backend webserver using a SOAP packet that looks like this:

      <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Header>
          <AuthHeader xmlns="http://www.testtesttest.co.za/">
            <LogonID>testtesttest</LogonID>
            <Password>testtesttest</Password>
          </AuthHeader>
        </soap:Header>
        <soap:Body>
          <uploadFile xmlns="http://www.testtesttest.co.za/">
            <FileDetails>
               <FileName>image.jpg</FileName>
              <FileContents>
                 {(Base64.encode(request.getContent())).toString(UTF_8)
              </FileContents>
            </FileDetails>
          </uploadFile>
        </soap:Body>
      </soap:Envelope>

In the example above, the code: (Base64.encode(request.getContent())).toString(UTF_8) converts the request content to a base 64 encoded string.

The problem is that I need to read the image content from Multipart Http request that is sent from a PhoneGap mobile app. PhoneGap gives me no option, to send only the image, and insists in doing the file upload as a multipart request.

To break the multipart request apart, I change the request.getContent() result into a string using toString(UTF_8), and then getting the image data part by splitting the http multipart message into it's separate chunks:

 var requestParts = request.content.toString(UTF_8).split("\\Q--*****org.apache.cordova.formBoundary\\E")
 val imageParts = requestParts(3).split("\\n\\s*\\n")
 val imageHeader = imageParts(0)
 val imageBody = imageParts(1)

This is crappy, I know (I'll improve later), but does the trick for now. imageBody now has the image content as a string.

Now, if I put the imageBody back into the SOAP packet, I have to encode it again using:

 val encoder = new BASE64Encoder();
 val encodedImage = encoder.encode(imageBody)

At this point the image is just garble. It's size looks right, but I'm messing something up with the string conversion or encoding. For the first example, I'm using Netty's encoder, but for the second example I'm using the standard java encoder. The reason is that Netty's encoder can only encode objects of type ChannelBuffer.

I don't want to say this too loud, but I've been struggling with this for more than a day. Any help here will be GREATLY appreciated.


Solution

  • So this works:

    image --> [curl] ------> post1 -->  [your code] --> soap msg 1 --> [back-end]
    

    This does not:

    image --> [phonegap] --> post2 -->  [your code] --> soap msg 2 --> [back-end]
    

    To solve this type of problem reliably you need to understand which encoding is used in each step.

    Assuming you can use the same image, can you check the raw encoded content in post1 and post2 and infer which encoding is being used? Then when you understand that, log the content in your code as you decode and recode the message. That way you can ensure it's the same in soap msg1 and soap msg2.