CXF
  1. CXF
  2. CXF-4348

Content-Type is broken in multipart serialization

    Details

    • Estimated Complexity:
      Unknown

      Description

      Multiparts are serialized incorrectly.
      Imagine the response with two attachments:
      a) "filename1.doc" with attachment-id "Attachment_id1" and Content-Type "application/msword"
      b) "filename2.xls" with attachment-id "Attachment_id2" and Content-Type "application/vnd.ms-excel"
      Serialization of this Multipart is ({} are used for text reduction):

      HTTP/1.1 200 OK
      Server: ...
      Date: ...
      Content-Type: application/octet-stream; type="application/msword"; boundary="uuid:

      {UUID}"; start="<Attachment_id1>"; start-info="application/msword"
      Transfer-Encoding: chunked


      --uuid:{UUID}

      Content-Type: text/xml; charset=UTF-8; type="application/msword";
      Content-Transfer-Encoding: binary
      Content-ID: <Attachment_id1>
      Content-Disposition: attachment; filename=filename1.doc

      {CONTENT1}
      -uuid:{UUID}
      Content-Type: application/vnd.ms-excel
      Content-Transfer-Encoding: binary
      Content-ID: <Attachment_id2>
      Content-Disposition: attachment; filename=filename2.xls
      {CONTENT2}
      --uuid:{UUID}
      -

      While we are expecting kind of this:

      HTTP/1.1 200 OK
      Server: ...
      Date: ...
      Content-Type: multipart/related
      Transfer-Encoding: chunked


      --uuid:{UUID}
      Content-Type: application/msword;
      Content-Transfer-Encoding: binary
      Content-ID: <Attachment_id1>
      Content-Disposition: attachment; filename=filename1.doc
      {CONTENT1}

      --uuid:

      {UUID}
      Content-Type: application/vnd.ms-excel
      Content-Transfer-Encoding: binary
      Content-ID: <Attachment_id2>
      Content-Disposition: attachment; filename=filename2.xls
      {CONTENT2}
      --uuid:{UUID}

      So the Content-Type of the whole response and of the first part are incorrect.
      The starting point of the bug searching is org.apache.cxf.jaxrs.ext.MessageContextImpl.convertToAttachments(Object) method, which has at least these sub-bugs:
      1) First attachment is handled other way than all subsequent ones -> All attachments must be handled in the same way.
      2) AttachmentOutputInterceptor is created with some default Contetnt-Type, which is "application/octet-stream" -> The type must be "multipart/related" (or other multipart).
      3) Content-Type of outMessage is changed to the first attachment's Content-Type and then new type is used at least in org.apache.cxf.attachment.AttachmentSerializer.writeProlog() method -> The same type must be used.

        Issue Links

          Activity

          Hide
          Sergey Beryozkin added a comment -

          I've just confirmed that AttachmentSerializer on 2.4.x has not been updated to get the optional parameters dropped. I think I did not update 2.4.x to avoid the possible regressions due to some sensitivity of the change, given 2.4.9 is now actually the last tag in the 2.4.x line. So unfortunately the optional parameters will be there indeed on 2.4.x.

          Please migrate to 2.5.x at least where only "multipart/related" will have an optional "start" parameter included (as confirmed by the tests).
          Also, as I said I'd consider updating the code to get 'start' dropped for non-XOP "multipart/related", RS endpoint bound payloads - please open a minor issue if you'd like to get it optimized further, cheers

          Show
          Sergey Beryozkin added a comment - I've just confirmed that AttachmentSerializer on 2.4.x has not been updated to get the optional parameters dropped. I think I did not update 2.4.x to avoid the possible regressions due to some sensitivity of the change, given 2.4.9 is now actually the last tag in the 2.4.x line. So unfortunately the optional parameters will be there indeed on 2.4.x. Please migrate to 2.5.x at least where only "multipart/related" will have an optional "start" parameter included (as confirmed by the tests). Also, as I said I'd consider updating the code to get 'start' dropped for non-XOP "multipart/related", RS endpoint bound payloads - please open a minor issue if you'd like to get it optimized further, cheers
          Hide
          Ivan Bondarenko added a comment -

          I have tested on 2.4.10 version. Cannot switch upper than 2.4.x right now, but looks like multipart methods are nearly the same. Also probably "multipart/mixed" must be used as a default type, not "multipart/related".

          Show
          Ivan Bondarenko added a comment - I have tested on 2.4.10 version. Cannot switch upper than 2.4.x right now, but looks like multipart methods are nearly the same. Also probably "multipart/mixed" must be used as a default type, not "multipart/related".
          Hide
          Sergey Beryozkin added a comment -

          OK, thanks - I'm not seeing it at the dev list - you may need to register too.

          FYI, the latest update has been committed to 2.5.x, 2.6.x, trunk. As I said, it is only multipart/related where I see 3 parameters, two of them are required (boundary, type) plus one optional parameter ("start"). For other multipart media types I expect only a single parameter ("boundary"). Can you test with the latest released CXFs ?

          Show
          Sergey Beryozkin added a comment - OK, thanks - I'm not seeing it at the dev list - you may need to register too. FYI, the latest update has been committed to 2.5.x, 2.6.x, trunk. As I said, it is only multipart/related where I see 3 parameters, two of them are required (boundary, type) plus one optional parameter ("start"). For other multipart media types I expect only a single parameter ("boundary"). Can you test with the latest released CXFs ?
          Hide
          Ivan Bondarenko added a comment -

          I have created a thread here too (guess it is CXF Dev list): http://cxf.547215.n5.nabble.com/Multipart-serialization-td5717660.html So looks like the bug is more connected to non-"related" multiparts. Probably this should be another report?!

          Show
          Ivan Bondarenko added a comment - I have created a thread here too (guess it is CXF Dev list): http://cxf.547215.n5.nabble.com/Multipart-serialization-td5717660.html So looks like the bug is more connected to non-"related" multiparts. Probably this should be another report?!
          Show
          Sergey Beryozkin added a comment - FYI: http://svn.apache.org/viewvc?rev=1404308&view=rev
          Hide
          Sergey Beryozkin added a comment -

          Ivan, I'm adding one more test. Optional parameters are not there for types like "multipart/mixed", "multipart/form-data". For "multipart/related" I'm seeing:

          "Content-Type: multipart/related; type="application/xml"; boundary="uuid:cb4e262c-8fa7-4d2a-8744-2801c61680fd"; start="<root.message@cxf.apache.org>"".

          Note, the only extra parameter is 'start', which is clearly defined in RFC2387 and it is the ContentId of the root part. It is optional all right and I guess is redundant, but it fits OK for multipart/related. "start-info" is not included by default and will only be there for XOP payloads.

          Thus I do not think we have an issue to be honest. However, please feel free to open a new Minor issue to get even "start" not reported for 'simple' multipart/related, I'm open to optimizing it even further though I'd consider it a minor issue.

          Thanks

          Show
          Sergey Beryozkin added a comment - Ivan, I'm adding one more test. Optional parameters are not there for types like "multipart/mixed", "multipart/form-data". For "multipart/related" I'm seeing: "Content-Type: multipart/related; type="application/xml"; boundary="uuid:cb4e262c-8fa7-4d2a-8744-2801c61680fd"; start="<root.message@cxf.apache.org>"". Note, the only extra parameter is 'start', which is clearly defined in RFC2387 and it is the ContentId of the root part. It is optional all right and I guess is redundant, but it fits OK for multipart/related. "start-info" is not included by default and will only be there for XOP payloads. Thus I do not think we have an issue to be honest. However, please feel free to open a new Minor issue to get even "start" not reported for 'simple' multipart/related, I'm open to optimizing it even further though I'd consider it a minor issue. Thanks
          Hide
          Sergey Beryozkin added a comment -

          Fixed earlier - Not a problem related to the latest reopening of this issue

          Show
          Sergey Beryozkin added a comment - Fixed earlier - Not a problem related to the latest reopening of this issue
          Hide
          Sergey Beryozkin added a comment -

          Well, http://tools.ietf.org/html/rfc2387 states that 'type' is required, two other parameters are optional.

          Therefore I'm going to close this issue. Please do not hesitate to move this discussion to CXF Dev list if you do not agree. Thank you

          Show
          Sergey Beryozkin added a comment - Well, http://tools.ietf.org/html/rfc2387 states that 'type' is required, two other parameters are optional. Therefore I'm going to close this issue. Please do not hesitate to move this discussion to CXF Dev list if you do not agree. Thank you
          Hide
          Ivan Bondarenko added a comment -

          Not only overload, they just shouldn't be there. But this part of bug has less priority than the fixed part.

          Show
          Ivan Bondarenko added a comment - Not only overload, they just shouldn't be there. But this part of bug has less priority than the fixed part.
          Hide
          Sergey Beryozkin added a comment -

          Are you reopening it because you think those parameters which are accepted for multipart/related overload the traffic ?

          Show
          Sergey Beryozkin added a comment - Are you reopening it because you think those parameters which are accepted for multipart/related overload the traffic ?
          Hide
          Ivan Bondarenko added a comment -

          We haven't used this feature for some time, so I couldn't do a testing. Looks like global Content-Type is still broken:
          Content-Type: multipart/related; type="application/msword"; boundary="uuid:

          {UUID}

          "; start="<Attachment_id1>"; start-info="application/msword"
          The necessity of 'type', 'start' and 'start-info' params in not described in spec. At least it is traffic overloading.

          Show
          Ivan Bondarenko added a comment - We haven't used this feature for some time, so I couldn't do a testing. Looks like global Content-Type is still broken: Content-Type: multipart/related; type="application/msword"; boundary="uuid: {UUID} "; start="<Attachment_id1>"; start-info="application/msword" The necessity of 'type', 'start' and 'start-info' params in not described in spec. At least it is traffic overloading.
          Hide
          Sergey Beryozkin added a comment -

          Optional start-info parameter is also dropped for simple multipart/related payloads (excluding XOP/MTOP), multipart/mixed, multipart/form-data will only have a boundary parameter

          Show
          Sergey Beryozkin added a comment - Optional start-info parameter is also dropped for simple multipart/related payloads (excluding XOP/MTOP), multipart/mixed, multipart/form-data will only have a boundary parameter
          Hide
          Sergey Beryozkin added a comment -

          The fix for the root part's Content-Type is on the trunk, 2.5.x

          Show
          Sergey Beryozkin added a comment - The fix for the root part's Content-Type is on the trunk, 2.5.x
          Hide
          Sergey Beryozkin added a comment - - edited

          just add LoggingFeature or LoggingInInterceptor to see the actual Accept.

          re: type="application/msword"; boundary="uuid:

          {UUID}

          "; start="<Attachment_id1>"; start-info="application/msword"

          this is a bit broken, it should only occur for XOP payloads - will be fixing that. However it should not affect the consumers expecting no XOP, those are just media type parameters and thus can be safely ignored

          Show
          Sergey Beryozkin added a comment - - edited just add LoggingFeature or LoggingInInterceptor to see the actual Accept. re: type="application/msword"; boundary="uuid: {UUID} "; start="<Attachment_id1>"; start-info="application/msword" this is a bit broken, it should only occur for XOP payloads - will be fixing that. However it should not affect the consumers expecting no XOP, those are just media type parameters and thus can be safely ignored
          Hide
          Ivan Bondarenko added a comment -

          I cannot check "application/octet-stream" right now, will do it a bit later.
          But what spec reflects this in global Content-Type?: type="application/msword"; boundary="uuid:

          {UUID}

          "; start="<Attachment_id1>"; start-info="application/msword"
          As I know the first part should have no impact on the whole content.

          Show
          Ivan Bondarenko added a comment - I cannot check "application/octet-stream" right now, will do it a bit later. But what spec reflects this in global Content-Type?: type="application/msword"; boundary="uuid: {UUID} "; start="<Attachment_id1>"; start-info="application/msword" As I know the first part should have no impact on the whole content.
          Hide
          Sergey Beryozkin added a comment - - edited

          Ivan,

          > 2) AttachmentOutputInterceptor is created with some default Content-Type, which is "application/octet-stream" -> The type must be "multipart/related" (or other multipart).

          note that the method in your code @Produces("multipart/*"). I can reproduce that the response message's Content-Type is set to "application/octet-stream", but only if no "Accept: multipart-related" exists. If I update the client to set "Accept: multipart-related", then the message Content-Type is set correctly to multipart-related, with other parameters like start-info, etc, included.

          Can you confirm that you have a specific multipart media type listed in Accept ?

          I can confirm that the Content-Type of the root part only is broken for outbound attachments. Will be fixing it

          Show
          Sergey Beryozkin added a comment - - edited Ivan, > 2) AttachmentOutputInterceptor is created with some default Content-Type, which is "application/octet-stream" -> The type must be "multipart/related" (or other multipart). note that the method in your code @Produces("multipart/*"). I can reproduce that the response message's Content-Type is set to "application/octet-stream", but only if no "Accept: multipart-related" exists. If I update the client to set "Accept: multipart-related", then the message Content-Type is set correctly to multipart-related, with other parameters like start-info, etc, included. Can you confirm that you have a specific multipart media type listed in Accept ? I can confirm that the Content-Type of the root part only is broken for outbound attachments. Will be fixing it
          Hide
          Sergey Beryozkin added a comment -

          I suspect it's a duplicate of https://issues.apache.org/jira/browse/CXF-3479. Sending multiple attachments from the client code works fine, but I guess there could be some issues when writing the multiple attachments from the server side, I'll check

          Show
          Sergey Beryozkin added a comment - I suspect it's a duplicate of https://issues.apache.org/jira/browse/CXF-3479 . Sending multiple attachments from the client code works fine, but I guess there could be some issues when writing the multiple attachments from the server side, I'll check
          Hide
          Ivan Bondarenko added a comment -

          I cannot type the exact code, because DataSource objects are constructed in a complicated internal way, but pseudo-code looks like this:

          @POST
          @Path("")
          @Consumes("multipart/*")
          @Produces("multipart/*")
          public MultipartBody translateDocumentPost()

          { List<Attachment> retAttachments = new LinkedList<Attachment>(); MultivaluedMap<String, String> headers; headers = new MetadataMap<String, String>(false, true); headers.putSingle("Content-Disposition", "attachment; filename=filename1.doc"); retAttachments.add(new Attachment("Attachment_id1", new FileDataSource("d:/filename1.doc", "application/msword"), headers)); headers = new MetadataMap<String, String>(false, true); headers.putSingle("Content-Disposition", "attachment; filename=filename2.xls"); retAttachments.add(new Attachment("Attachment_id2", new FileDataSource("d:/filename2.xls", "application/vnd.ms-excel"), headers)); return new MultipartBody(retAttachments, true); }

          The code
          new FileDataSource("d:/filename1.doc", "application/msword")
          you can consider as following:
          a) getInputStream() = the file's content
          b) getName() = "filename1.doc"
          c) getContentType() = "application/msword"

          Show
          Ivan Bondarenko added a comment - I cannot type the exact code, because DataSource objects are constructed in a complicated internal way, but pseudo-code looks like this: @POST @Path("") @Consumes("multipart/*") @Produces("multipart/*") public MultipartBody translateDocumentPost() { List<Attachment> retAttachments = new LinkedList<Attachment>(); MultivaluedMap<String, String> headers; headers = new MetadataMap<String, String>(false, true); headers.putSingle("Content-Disposition", "attachment; filename=filename1.doc"); retAttachments.add(new Attachment("Attachment_id1", new FileDataSource("d:/filename1.doc", "application/msword"), headers)); headers = new MetadataMap<String, String>(false, true); headers.putSingle("Content-Disposition", "attachment; filename=filename2.xls"); retAttachments.add(new Attachment("Attachment_id2", new FileDataSource("d:/filename2.xls", "application/vnd.ms-excel"), headers)); return new MultipartBody(retAttachments, true); } The code new FileDataSource("d:/filename1.doc", "application/msword") you can consider as following: a) getInputStream() = the file's content b) getName() = "filename1.doc" c) getContentType() = "application/msword"
          Hide
          Sergey Beryozkin added a comment -

          Please type the code which is used to post these attachments

          Show
          Sergey Beryozkin added a comment - Please type the code which is used to post these attachments

            People

            • Assignee:
              Sergey Beryozkin
              Reporter:
              Ivan Bondarenko
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development