[14,15,16]MIME,DIME和MTOM附件

14  MIME Attachments

The gSOAP toolkit supports MIME attachments as per SOAP with Attachments (SwA) specification (http://www.w3.org/TR/SOAP-attachments). In the following discussion, MIME attachment data is assumed to be resident in memory for sending operations and MIME attachments received will be stored in memory. MTOM and DIME attachments on the other hand can be streamed and therefore MTOM/DIME attachment data does not need to be stored in memory, see Section 15 and 16. Transmitting multipart/related MIME attachments with a SOAP/XML message is accomplished with two functions, soap_set_mime and soap_set_mime_attachment. The first function is for initialization purposes and the latter function is used to specify meta data and content data for each attachment.

14.1  Sending a Collection of MIME Attachments (SwA)

The following functions should be used to set up a collection of multipart/related MIME attachments for transmission with a SOAP/XML message.

Function
void soap_set_mime(struct soap *soap, const char *boundary, const char *start)
This function must be called first to initialize MIME attachment send operations (receives are automatic). The function specifies a MIME boundary and start content ID used for the SOAP message body. When boundary is NULL, an appropriate MIME boundary will be choosen (important: boundaries cannot occur in the SOAP/XML message and cannot occur in any of the MIME attachments content). When a specific boundary value is provided, gSOAP will NOT verify that the boundary is valid. When start is NULL, the start ID of the SOAP message is <SOAP-ENV:Envelope>.
int soap_set_mime_attachment(struct soap *soap, char *ptr, size_t size, enum soap_mime_encoding encoding, const char *type, const char *id, const char *location, const char *description)
This function adds a new attachment to the list of attachments, where ptr and size refer to the block of memory that holds the attachment data. The encoding parameter specifies the content encoding of this block, where the value of encoding is one of SOAP_MIME_7BIT, SOAP_MIME_8BIT, SOAP_MIME_BINARY, SOAP_MIME_QUOTED_PRINTABLE, SOAP_MIME_BASE64, SOAP_MIME_IETF_TOKEN, or SOAP_MIME_X_TOKEN. These constants reflect the content encoding defined in RFC2045 and you MUST adhere to the content encoding rules defined by RFC2045. When in doubt, use SOAP_MIME_BINARY, since this encoding type covers any content. The mandatory type string parameter is the MIME type of the data. The id string parameter is the content ID of the MIME attachment. The optional location string parameter is the content location of the attachment. The optional description string parameter holds a textual description of the attachment (it may not contain any control characters). All parameter values are copied, except ptr which must point to a valid location of the attachment data during the transfer. The value SOAP_OK is returned when the attachment was added. Otherwise a gSOAP error code is returned.
void soap_clr_mime(struct soap *soap)
Disables MIME attachments, e.g. to avoid MIME attachments to be part of a SOAP Fault response message.


When providing a MIME boundary with soap_set_mime, you have to make sure the boundary cannot match any SOAP/XML message content. Or you can simply pass NULL and let gSOAP select a safe boundary for you. The internal list of attachments is destroyed with soap_end, you should call this function sometime after the message exchange was completed (the content of the block of memory referred to by the ptr parameter is unaffected). The following example shows how a multipart/related HTTP message with three MIME attachments is set up and transmitted to a server. The first attachment contains the SOAP message. The second and third attachments contain image data. In this example we let the SOAP message body refer to the attachments using href attributes. The struct claim__form data type includes a definition of a href attribute for this purpose.

struct claim__form form1, form2;
form1.href = "cid:[email protected]";
form2.href = "cid:[email protected]";
/* initialize and enable MIME */
soap_set_mime(soap, "MIME_boundary", "<[email protected]>");
/* add a base64 encoded tiff image (tiffImage points to base64 data) */
soap_set_mime_attachment(soap, tiffImage, tiffLen, SOAP_MIME_BASE64, "image/tiff",
         "<[email protected]>", NULL, NULL);
/* add a raw binary jpeg image (jpegImage points to raw data) */
soap_set_mime_attachment(soap, jpegImage, jpegLen, SOAP_MIME_BINARY, "image/jpeg",
         "<[email protected]>", NULL, NULL);
/* send the forms as MIME attachments with this invocation */
if (soap_call_claim__insurance_claim_auto(soap, form1, form2, ...))
   // an error occurred
else
   // process response


Note: the above example assumes that the boundary MIME_boundary does not match any part of the SOAP/XML message. The claim__form struct is declared in the gSOAP header file as:

struct claim__form
{ @char *href;
};


This data type defines the parameter data of the operation. The claim forms in the SOAP/XML message consist of hrefs to the claim forms attached. The produced message is similar to the last example shown in the SOAP with Attachments specification (http://www.w3.org/TR/SOAP-attachments). Note that the use of href or other attributes for referring to the MIME attachments is optional according to the SwA standard. To associate MIME attachments with the request and response of a service operation in the generated WSDL, please see Section 16.1. The server-side code to transmit MIME attachments back to a client is similar:

int claim__insurance_claim_auto(struct soap *soap, ...)
{
   soap_set_mime(soap, NULL, NULL); // enable MIME
   // add a HTML document (htmlDoc points to data, where the HTML doc is stored in compliance with 7bit encoding RFC2045)
   if (soap_set_mime_attachment(soap, htmlDoc, strlen(htmlDoc), SOAP_MIME_7BIT, "text/html",
         "<[email protected]>", NULL, NULL))
   {
      soap_clr_mime(soap); // don't want fault with attachments
      return soap->error;
   }
   return SOAP_OK;
}


It is also possible to attach data to a SOAP fault message. Caution: DIME in MIME is supported. However, gSOAP will not verify whether the MIME boundary is present in the DIME attachments and therefore will not select a boundary that is guaranteed to be unique. Therefore, you must provide a MIME boundary with soap_set_mime that is unique when using DIME in MIME.

14.2  Retrieving a Collection of MIME Attachments (SwA)

MIME attachments are automatically parsed and stored in memory. After receiving a set of MIME attachments, either at the client-side or the server-side, the list of MIME attachments can be traversed to extract meta data and the attachment content. The first attachment in the collection of MIME attachments always contains meta data about the SOAP message itself (because the SOAP message was processed the attachment does not contain any useful data). To traverse the list of MIME attachments in C, you use a loop similar to:

struct soap_multipart *attachment;
for (attachment = soap.mime.list; attachment; attachment = attachment->next)
{
   printf("MIME attachment:\n");
   printf("Memory=%p\n", (*attachment).ptr);
   printf("Size=%ul\n", (*attachment).size);
   printf("Encoding=%d\n", (int)(*attachment).encoding);
   printf("Type=%s\n", (*attachment).type?(*attachment).type:"null");
   printf("ID=%s\n", (*attachment).id?(*attachment).id:"null");
   printf("Location=%s\n", (*attachment).location?(*attachment).location:"null");
   printf("Description=%s\n", (*attachment).description?(*attachment).description:"null");
}


C++ programmers can use an iterator instead, as in:

for (soap_multipart::iterator attachment = soap.mime.begin(); attachment != soap.mime.end(); ++attachment)
{
   cout << "MIME attachment:" << endl;
   cout << "Memory=" << (void*)(*attachment).ptr << endl;
   cout << "Size=" << (*attachment).size << endl;
   cout << Ëncoding=" << (*attachment).encoding << endl;
   cout << "Type=" << ((*attachment).type?(*attachment).type:"null") << endl;
   cout << "ID=" << ((*attachment).id?(*attachment).id:"null") << endl;
   cout << "Location=" << ((*attachment).location?(*attachment).location:"null") << endl;
   cout << "Description=" << ((*attachment).description?(*attachment).description:"null") << endl;
}


Note: keep in mind that the first attachment is associated with the SOAP message and you may want to ignore it. A call to soap_end removes all of the received MIME data. To preserve an attachment in memory, use soap_unlink on the ptr field of the soap_multipart struct. Recall that the soap_unlink function is commonly used to prevent deallocation of deserialized data.

15  DIME Attachments

The gSOAP toolkit supports DIME attachments as per DIME specification, see http://msdn.microsoft.com/library/en-us/dnglobspec/html/draft-nielsen-dime-02.txt Applications developed with gSOAP can transmit binary DIME attachments with or without streaming messages. Without streaming, all data is stored and retrieved in memory, which can be prohibitive when transmitting large files on small devices. In contrast, with DIME streaming, data handlers are used to pass the data to and from a resource, such as a file or device. With DIME output streaming, raw binary data is send from a data source in chunks on the fly without buffering the entire content to save memory. With DIME input streaming, raw binary data will be passed to data handlers (callbacks).

15.1  Sending a Collection of DIME Attachments

The following functions can be used to explicitly set up a collection of DIME attachments for transmission with a SOAP/XML message body. The attachments can be streamed, as described in Section 15.4. Without streaming, each attachment must refer to a block of data in memory.

Function
void soap_set_dime(struct soap *soap)
This function must be called first to initialize DIME attachment send operations (receives are automatic).
int soap_set_dime_attachment(struct soap *soap, char *ptr, size_t size, const char *type, const char *id, unsigned short optype, const char *option)
This function adds a new attachment to the list of attachments, where ptr and size refer to the block of memory that holds the attachment data (except when DIME streaming callback handlers are used as described in Section 15.4. The type string parameter is the MIME type of the data. The id string parameter is the content ID of the DIME attachment. The option string parameter holds optional text (gSOAP supports DIME options, but it can send only one) and optype is a user-defined option type (as per DIME option specification format). All parameter values are copied, except ptr. The value SOAP_OK is returned when the attachment was added. Otherwise a gSOAP error code is returned.
void soap_clr_mime(struct soap *soap)
Disables DIME attachments, unless the serialized SOAP message contains attachments for transmission.


These functions allow DIME attachments to be added without requiring message body references. This is also referred to as the open DIME attachment style. The closed attachment style requires all DIME attachments to be referenced from the SOAP message body with href (or similar) references. For the closed style, gSOAP supports an automatic binary data serialization method, see Section 15.3.

15.2  Retrieving a Collection of DIME Attachments

DIME attachments are automatically parsed and stored in memory (or passed to the streaming handlers, when applicable). After receiving a set of DIME attachments, either at the client-side or the server-side, the list of DIME attachments can be traversed to extract meta data and the attachment content. To traverse the list of DIME attachments in C, you use a loop similar to:

struct soap_multipart *attachment;
for (attachment = soap.dime.list; attachment; attachment = attachment->next)
{
   printf("DIME attachment:\n");
   printf("Memory=%p\n", (*attachment).ptr);
   printf("Size=%ul\n", (*attachment).size);
   printf("Type=%s\n", (*attachment).type?(*attachment).type:"null");
   printf("ID=%s\n", (*attachment).id?(*attachment).id:"null");
}


C++ programmers can use an iterator instead, as in:

for (soap_multipart::iterator attachment = soap.dime.begin(); attachment != soap.dime.end(); ++attachment)
{
   cout << "DIME attachment:" << endl;
   cout << "Memory=" << (void*)(*attachment).ptr << endl;
   cout << "Size=" << (*attachment).size << endl;
   cout << "Type=" << ((*attachment).type?(*attachment).type:"null") << endl;
   cout << "ID=" << ((*attachment).id?(*attachment).id:"null") << endl;
}


The options field is available as well. The options content is formatted according to the DIME specification: the first two bytes are reserved for the option type, the next two bytes store the size of the option data, followed by the (binary) option data. A call to soap_end removes all of the received DIME data. To preserve an attachment in memory, use soap_unlink on the ptr field of the soap_multipart struct. Recall that the soap_unlink function is commonly used to prevent deallocation of deserialized data.

15.3  Serializing Binary Data in DIME

Binary data stored in extended xsd:base64Binary and xsd:hexBinary types can be serialized and deserialized as DIME attachments. These attachments will be transmitted prior to the sequence of secondary DIME attachments defined by the user with soap_set_dime_attachment as explained in the previous section. The serialization process is automated and DIME attachments will be send even when soap_set_dime or soap_set_dime_attachment are not used. The xsd:base64Binary XSD type is defined in gSOAP as a struct or class by

struct xsd__base64Binary
{
   unsigned char *__ptr; // pointer to raw binary data
   int __size; // size of the block of data
};


To enable serialization of the data in DIME, we extend this type with three additional fields:

struct xsd__base64Binary
{
   unsigned char *__ptr;
   int __size;
   char *id;
   char *type;
   char *options;
};


The three additional fields consist of an id field for attachment referencing (typically a content id (CID) or UUID), a type field to specify the MIME type of the binary data, and an options field to piggy-back additional information with a DIME attachment. The order of the declaration of the fields is significant. In addition, no other fields or methods may be declared before any of these fields in the struct/class, but additional fields and methods may appear after the field declarations. An extended xsd__hexBinary declaration is similar. The id and type fields contain text. The set the DIME-specific options field, you can use the soap_dime_option function:

char *soap_dime_option(struct soap *soap, unsigned short type, const char *option)


returns a string with this encoding. For example

struct xsd__base64Binary image;
image.__ptr = ...;
image.__size = ...;
image.id = "uuid:09233523-345b-4351-b623-5dsf35sgs5d6";
image.type = "image/jpeg";
image.options = soap_dime_option(soap, 0, "My wedding picture");


When either the id or type field values are non-NULL at run time, the data will be serialized as a DIME attachment. The SOAP/XML message refers to the attachments using href attributes. This generally works will with SOAP RPC, because href attributes are permitted. However, with document/literal style the referencing mechanism must be explicitly defined in the schema of the binary type. The gSOAP declaration of an extended binary type is

struct ns__myBinaryDataType
{
   unsigned char *__ptr;
   int __size;
   char *id;
   char *type;
   char *options;
};


C++ programmers can use inheritance instead of textual extension required in C, as in

class xsd__base64Binary
{
   unsigned char *__ptr;
   int __size;
};
class ns__myBinaryDataType : xsd__base64Binary
{
   char *id;
   char *type;
   char *options;
};


This defines an extension of xsd:base64Binary, such that the data can be serialized as DIME attachments using href attributes for referencing. When a different attribute name is in fact used, it must be explicitly defined:

//gsoap WSref schema import: http://schemas.xmlsoap.org/ws/2002/04/reference/
struct ns__myBinaryDataType
{
   unsigned char *__ptr;
   int __size;
   char *id;
   char *type;
   char *options;
   @char *WSref__location;
};


The example above uses the location attribute defined in the content reference schema, as defined in one of the vendor's specific WSDL extensions for DIME (http://www.gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm). When receiving DIME attachments, the DIME meta data and binary data content is stored in binary data types only when the XML parts of the message uses href attributes to refer to these attachments. The gSOAP toolkit may support automatic (de)serialization with other user-defined (or WSDL-defined) attributes in future releases. Messages may contain binary data that references external resources not provided as attachments. In that case, the __ptr field is NULL and the id field refers to the external data source. The dime_id_format attribute of the current gSOAP run-time context can be set to the default format of DIME id fields. The format string MUST contain a %d format specifier (or any other int-based format specifier). The value of this specifier is a non-negative integer, with zero being the value of the DIME attachment id for the SOAP message. For example,

struct soap soap;
soap_init(&soap);
soap.dime_id_format = "uuid:09233523-345b-4351-b623-5dsf35sgs5d6-%x";


As a result, all attachments with a NULL id field will use a gSOAP-generated id value based on the format string. Caution: Care must be taken not to introduce duplicate content id values, when assigning content id values to the id fields of DIME extended binary data types. Content ids must be unique.

15.4  Streaming DIME

Streaming DIME is achieved with callback functions to fetch and store data during transmission. Three function callbacks for streaming DIME output and three callbacks for streaming DIME input are available.

Callback (function pointer)
void *(*soap.fdimereadopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *options)
Called by the gSOAP run-time DIME attachment sender to start reading from a (binary) data source for outbound transmission. The content will be read from the application's data source in chunks using the fdimeread callback and streamed into the SOAP/XML/DIME output stream. The handle contains the value of the __ptr field of an attachment struct/class, which could be a pointer to specific information such as a file descriptor or a pointer to a string to be passed to this callback. Both __ptr and __size fields should have been set by the application prior to the serialization of the content. The id, type, and options arguments are the DIME id, type, and options, respectively. The callback should return handle, or another pointer value which will be passed as a handle to fdimeread and fdimereadclose. The callback should return NULL and set soap->error when an error occurred. The callback should return NULL (and not set soap->error) when this particular DIME attachment is not to be streamed.
size_t (*soap.fdimeread)(struct soap *soap, void *handle, char *buf, size_t len)
Called by the gSOAP run-time DIME attachment sender to read more data from a (binary) data source for streaming into the output stream. The handle contains the value returned by the fdimereadopen callback. The buf argument is the buffer of length len into which a chunk of data should be stored. The actual amount of data stored in the buffer may be less than len and this amount should be returned by the application. A return value of 0 indicates an error (the callback may set soap->errnum to errno). The __size field of the attachment struct/class should have been set by the application prior to the serialization of the content. The value of __size indicates the total size of the content to be transmitted. When the __size is zero then DIME chunked transfers can be used under certain circumstances to stream content without prior determination of attachment size, see Section 15.5 below.
void(*soap.fdimereadclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time DIME attachment sender at the end of the streaming process to close the data source. The handle contains the value returned by the fdimereadopen callback. The fdimewriteclose callback is called after successfully transmitting the data or when an error occurred.
void *(*soap.fdimewriteopen)(struct soap *soap, const char *id, const char *type, const char *options)
Called by the gSOAP run-time DIME attachment receiver to start writing an inbound DIME attachment to an application's data store. The content is streamed into an application data store through multiple fdimewrite calls from the gSOAP attachment receiver. The id, type, and options arguments are the DIME id, type, and options respectively. The callback should return a handle which is passed to the fdimewrite and fdimewriteclose callbacks. The __ptr field of the attachment struct/class is set to the value of this handle. The __size field is set to the total size of the attachment after receiving the entire content. The size is unknown in advance because DIME attachments may be chunked.
int (*soap.fdimewrite)(struct soap *soap, void *handle, const char *buf, size_t len)
Called by the gSOAP run-time DIME attachment receiver to write part of an inbound DIME attachment to an application's data store. The handle contains the value returned by the fdimewriteopen callback. The buf argument contains the data of length len. The callback should return a gSOAP error code (e.g. SOAP_OK when no error occurred).
void(*soap.fdimewriteclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time DIME attachment receiver at the end of the streaming process to close the data store. The fdimewriteclose callback is called after successfully receiving the data or when an error occurred. The handle contains the value returned by the fdimewriteopen callback.


In addition, a void*user field in the struct soap data structure is available to pass user-defined data to the callbacks. This way, you can set soap.user to point to application data that the callbacks need such as a file name for example. The following example illustrates the client-side initialization of an image attachment struct to stream a file into a DIME attachment:

int main()
{
   struct soap soap;
   struct xsd__base64Binary image;
   FILE *fd;
   struct stat sb;
   soap_init(&soap);
   if (!fstat(fileno(fd), &sb) && sb.st_size > 0)
   { // because we can get the length of the file, we can stream it
      soap.fdimereadopen = dime_read_open;
      soap.fdimereadclose = dime_read_close;
      soap.fdimeread = dime_read;
      image.__ptr = (unsigned char*)fd; // must set to non-NULL (this is our fd handle which we need in the callbacks)
      image.__size = sb.st_size; // must set size
   }
   else
   { // don't know the size, so buffer it
      size_t i;
      int c;
      image.__ptr = (unsigned char*)soap_malloc(&soap, MAX_FILE_SIZE);
      for (i = 0; i < MAX_FILE_SIZE; i++)
      {
         if ((c = fgetc(fd)) == EOF)
            break;
         image.__ptr[i] = c;
      }
      fclose(fd);
      image.__size = i;
   }
   image.type = "image/jpeg";
   image.options = soap_dime_option(&soap, 0, "My picture");
   soap_call_ns__method(&soap, ...);
   ...
}
void *dime_read_open(struct soap *soap, void *handle, const char *id, const char *type, const char *options)
{ return handle;
}
void dime_read_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
size_t dime_read(struct soap *soap, void *handle, char *buf, size_t len)
{ return fread(buf, 1, len, (FILE*)handle);
}


The following example illustrates the streaming of a DIME attachment into a file by a client:

int main()
{ struct soap soap;
   soap_init(&soap);
   soap.fdimewriteopen = dime_write_open;
   soap.fdimewriteclose = dime_write_close;
   soap.fdimewrite = dime_write;
   soap_call_ns__method(&soap, ...);
   ...
}
void *dime_write_open(struct soap *soap, const char *id, const char *type, const char *options)
{
   FILE *handle = fopen("somefile", "wb");
   if (!handle)
   {
      soap->error = SOAP_EOF;
      soap->errnum = errno; // get reason
   }
   return (void*)handle;
}
void dime_write_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
int dime_write(struct soap *soap, void *handle, const char *buf, size_t len)
{
   size_t nwritten;
   while (len)
   {
      nwritten = fwrite(buf, 1, len, (FILE*)handle);
      if (!nwritten)
      {
         soap->errnum = errno; // get reason
         return SOAP_EOF;
      }
      len -= nwritten;
      buf += nwritten;
   }
   return SOAP_OK;
}


Note that compression can be used with DIME to compress the entire message. However, compression requires buffering to determine the HTTP content length header, which cancels the benefits of streaming DIME. To avoid this, you should use chunked HTTP (with the output-mode SOAP_IO_CHUNK flag) with compression and streaming DIME. At the server side, when you set SOAP_IO_CHUNK before calling soap_serve, gSOAP will automatically revert to buffering (SOAP_IO_STORE flag is set). You can check this flag with (soap->omode & SOAP_IO) == SOAP_IO_CHUNK to see if the client accepts chunking. More information about streaming chunked DIME can be found in Section 15.5. Caution: The options field is a DIME-specific data structure, consisting of a 4 byte header containing the option type info (hi byte, lo byte), option string length (hi byte, lo byte), followed by a non-'\0' terminated string. The gSOAP DIME handler recognizes one option at most.

15.5  Streaming Chunked DIME

gSOAP automatically handles inbound chunked DIME attachments (streaming or non-streaming). To transmit outbound DIME attachments, the attachment sizes MUST be determined in advance to calculate HTTP message length required to stream DIME over HTTP. However, gSOAP also supports the transmission of outbound chunked DIME attachments without prior determination of DIME attachment sizes when certain conditions are met. These conditions require either non-HTTP transport (use the output-mode SOAP_ENC_XML flag), or chunked HTTP transport (use the output-mode SOAP_IO_CHUNK flag). You can also use the SOAP_IO_STORE flag (which is also used automatically with compression to determine the HTTP content length header) but that cancels the benefits of streaming DIME. To stream chunked DIME, set the __size field of an attachment to zero and enable HTTP chunking. The DIME fdimeread callback then fetches data in chunks and it is important to fill the entire buffer unless the end of the data has been reached and the last chunk is to be send. That is, fdimeread should return the value of the last len parameter and fill the entire buffer buf for all chunks except the last.

15.6  WSDL Bindings for DIME Attachments

The wsdl2h WSDL parser recognizes DIME attachments and produces an annotated header file. Both open and closed layouts are supported for transmitting DIME attachments. For closed formats, all DIME attachments must be referenced from the SOAP message, e.g. using hrefs with SOAP encoding and using the application-specific reference attribute included in the base64Binary struct/class for doc/lit. The gSOAP compiler soapcpp2 does not produce a WSDL with DIME extensions. DIME is an older binary format that has no WSDL protocol support, unlike MIME and MTOM.

16  MTOM Attachments

MTOM (Message Transmission Optimization Mechanism) is a relatively new format for transmitting attachments with SOAP messages (see http://www.w3.org/TR/soap12-mtom). MTOM is a W3C working draft as of this writing. MTOM attachments are essentially MIME attachments with standardized mechanisms for cross referencing attachments from the SOAP body, which is absent in (plain) MIME attachments and optional with DIME attachments. Unlike the name suggests, the speed by which attached data is transmitted is not increased compared to MIME, DIME, or even XML encoded base64 data (at least the performance differences in gSOAP will be small). The advantage of the format is the standardized attachment reference mechanism, which should improve interoperability. The MTOM specification mandates SOAP 1.2 and the use of the XOP namespace. The XOP Include element xop:Include is used to reference attachment(s) from the SOAP message body. Because references from within the SOAP message body to attachments are mandatory with MTOM, the implementation of the serialization and deserialization of MTOM MIME attachments in gSOAP uses the extended binary type comparable to DIME support in gSOAP. This binary type is predefined in the import/xop.h file:

//gsoap xop schema import: http://www.w3.org/2004/08/xop/include
struct _xop__Include
{
   unsigned char *__ptr;
   int __size;
   char *id;
   char *type;
   char *options;
};
typedef struct _xop__Include _xop__Include;


The additional id, type, and option fields enable MTOM attachments for the data pointed to by __ptr of size __size. The process for sending and receiving MTOM XOP attachments is fully automated. The id field references the attachment (typically a content id CID or UUID). When set to NULL, gSOAP assigns a unique CID. The type field specifies the required MIME type of the binary data, and the optional options field can be used to piggy-back descriptive text with an attachment. The order of the declaration of the fields is significant. You can explicitly import the xop.h in your header file to use the MTOM attachments in your service, for example:

#import "import/soap12.h"
/* alternatively, without the import above, use:
//gsoap SOAP-ENV schema namespace: http://www.w3.org/2003/05/soap-envelope
//gsoap SOAP-ENC schema namespace: http://www.w3.org/2003/05/soap-encoding
*/
#import "import/xop.h"
#import "import/xmime5.h"

//gsoap x schema namespace: http://my.first.mtom.net
struct x__myData
{
   _xop__Include xop__Include; // attachment
   @char *xmime5__contentType; // and its contentType
};
int x__myMTOMtest(struct x__myData *in, struct x__myData *out);


As you can see, there is really no difference between the specification of MTOM and DIME attachments in a gSOAP header file. Except that you MUST use SOAP 1.2 and the xop__Include element. When an instance of x__myDataType is serialized and either or both the id and type fields are non-NULL, the data is transmitted as MTOM MIME attachment if the SOAP_ENC_MTOM flag is set in the gSOAP's soap struct context:

struct soap *soap = soap_new1(SOAP_ENC_MTOM);


Without this flag, the attachments will be transmitted in DIME format (Section 15). If your current clients and services are based on non-streaming DIME attachments using the SOAP body reference mechanism (thus, without using the soap_set_dime_attachment function) or plain base64 binary XML data elements, it is very easy to adopt MTOM by renaming the binary types to xop__Include and using the SOAP_ENC_MTOM flag with the SOAP 1.2 namespace.

16.1  Generating MultipartRelated MIME Attachment Bindings in WSDL

To generate multipartRelated bindings in the WSDL file, use the //gsoap ... service method-mime-type directive (see also Section 19.2. The directive can be repeated for each attachment you want to associate with a method's request and response messages. For example:

#import "import/soap12.h"
#import "import/xop.h"
#import "import/xmime5.h"

//gsoap x schema namespace: http://my.first.mtom.net
struct x__myData
{
   _xop__Include xop__Include; // attachment
   @char *xmime5__contentType; // and its contentType
};
//gsoap x service method-mime-type: myMTOMtest text/xml
int x__myMTOMtest(struct x__myData *in, struct x__myData *out);


The //gsoap x service method-mime-type directive indicates that this operation accepts text/xml MIME attachments. See the SOAP-with-Attachment specification for the MIME types to use (for example, */* is a wildcard). If the operation has more than one attachment, just repeat this directive for each attachment you want to bind to the operation. To bind attachments only to the request message of an operation, use //gsoap x service method-input-mime-type. Similarly, to bind attachments only to the response message of an operation, use //gsoap x service method-ouput-mime-type. The wsdl2h WSDL parser recognizes MIME attachments and produces an annotated header file. However, the ordering of MIME parts in the multipartRelated elements is not reflected in the header file. Application developers should adhere the standards and ensure that multipart/related attachments are transmitted in compliance with the WSDL operation declarations.

16.2  Sending and Receiving MTOM Attachments

A receiver must be informed to recognize MTOM attachments by setting the SOAP_ENC_MTOM flag of the gSOAP context. Otherwise, the regular MIME attachment mechanism (SwA) will be used to store attachments. When using wsdl2h to build clients and/or services, you should use the typemap.dat file included in the distribution package. The typemap.dat file defines the XOP namespace and XML MIME namespaces as imported namespaces:

xop = < http://www.w3.org/2004/08/xop/include >
xmime5 = < http://www.w3.org/2005/05/xmlmime >
xmime4 = < http://www.w3.org/2004/11/xmlmime >


The wsdl2h tool uses the typemap.dat file (see also option -t) to convert WSDL into a gSOAP header file. In this case we don't want the wsdl2h tool to read the XOP schema and translate it, since we have a pre-defined _xop__Include element to handle XOP for MTOM. This _xop__Include element is defined in xop.h. Therefore, the bindings shown above will not translate the XOP and XML MIME schemas to code, but generates #import statements instead:

#import "xop.h"
#import "xmime5.h"


The #import statements are only added for those namespaces that are actually used by the service. Let's take a look at an example. The wsdl2h importer generates a header file with #import "xop.h" from a WSDL that references XOP, for example:

#import "xop.h"
#import "xmime5.h"
struct ns__Data
{
   _xop__Include xop__Include;
   @char *xmime5__contentType;
};


Suppose the WSDL defines an operation:

int ns__echoData(struct ns__Data *in, struct ns__Data *out);


After generating the stubs/proxies with the soapcpp2 compiler, we can invoke the stub at the client side with:

struct soap *soap = soap_new1(SOAP_ENC_MTOM);
struct ns__Data data;
data.xop__Include.__ptr = (unsigned char*)"<b>Hello world!</b>";
data.xop__Include.__size = 20;
data.xop__Include.id = NULL; // CID automatically generated by gSOAP engine
data.xop__Include.type = "text/html"; // MIME type
data.xop__Include.options = NULL; // no descriptive info added
data.xmime5__contentType = "text/html"; // MIME type
if (soap_call_ns__echoData(soap, endpoint, action, &data, &data))    soap_print_fault(soap, stderr); else
   printf("Got data\n");
soap_destroy(soap); // remove deserialized class instances
soap_end(soap); // remove temporary and deserialized data
soap_free(soap); // detach and free context


Note that the xop__Include.type field must be set to transmit MTOM attachments, otherwise plain base64 XML will be used. At the server side, we show an example of an operation handler that just copies the input data to output:

int ns__echoData(struct soap *soap, struct ns__Data *in, struct ns__data *out)
{
   *out = *in;
   return SOAP_OK;
}


The server must use the SOAP_ENC_MTOM flag to initialize the soap struct to receive and send MTOM attachments.

16.3  Streaming MTOM/MIME

Streaming MTOM/MIME is achieved with callback functions to fetch and store data during transmission. Three function callbacks for streaming MTOM/MIME output and three callbacks for streaming MTOM/MIME input are available.

Callback (function pointer)
void *(*soap.fmimereadopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *description)
Called by the gSOAP run-time MTOM/MIME attachment sender to start reading from a (binary) data source for outbound transmission. The content will be read from the application's data source in chunks using the fmimeread callback and streamed into the SOAP/XML/MTOM/MIME output stream. The handle contains the value of the __ptr field of an attachment struct/class, which could be a pointer to specific information such as a file descriptor or a pointer to a string to be passed to this callback. Both __ptr and __size fields should have been set by the application prior to the serialization of the content. The id, type, and description arguments are the MTOM/MIME id, type, and description, respectively. The callback should return handle, or another pointer value which will be passed as a handle to fmimeread and fmimereadclose. The callback should return NULL and set soap->error when an error occurred. The callback should return NULL (and not set soap->error) when this particular MTOM/MIME attachment is not to be streamed.
size_t (*soap.fmimeread)(struct soap *soap, void *handle, char *buf, size_t len)
Called by the gSOAP run-time MTOM/MIME attachment sender to read more data from a (binary) data source for streaming into the output stream. The handle contains the value returned by the fmimereadopen callback. The buf argument is the buffer of length len into which a chunk of data should be stored. The actual amount of data stored in the buffer may be less than len and this amount should be returned by the application. A return value of 0 indicates an error (the callback may set soap->errnum to errno). The __size field of the attachment struct/class should have been set by the application prior to the serialization of the content. The value of __size indicates the total size of the content to be transmitted. When the __size is zero then MTOM/MIME chunked transfers can be used under certain circumstances to stream content without prior determination of attachment size, see Section 16.5 below.
void(*soap.fmimereadclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time MTOM/MIME attachment sender at the end of the streaming process to close the data source. The handle contains the value returned by the fmimereadopen callback. The fmimewriteclose callback is called after successfully transmitting the data or when an error occurred.
void *(*soap.fmimewriteopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding)
Called by the gSOAP run-time MTOM/MIME attachment receiver to start writing an inbound MTOM/MIME attachment to an application's data store. The content is streamed into an application data store through multiple fmimewrite calls from the gSOAP attachment receiver. The handle argument is normally NULL, unless soap_get_mime_attachment is used that passes the handle to the callback, see Section 16.4. The id, type, and description arguments are the MTOM/MIME id, type, and description respectively. The encoding enumeration value indicates the MIME content transfer encoding, which is one of SOAP_MIME_NONE, SOAP_MIME_7BIT, SOAP_MIME_8BIT, SOAP_MIME_BINARY, SOAP_MIME_QUOTED_PRINTABLE, SOAP_MIME_BASE64, SOAP_MIME_IETF_TOKEN, SOAP_MIME_X_TOKEN. Content decoding may have to be considered by the application based on this value. The callback should return a non-NULL handle which is passed to the fmimewrite and fmimewriteclose callbacks. The __ptr field of the attachment struct/class is set to the value of this handle. The __size field is set to the total size of the attachment after receiving the entire content. The size is unknown in advance because MTOM/MIME attachments may be chunked.
int (*soap.fmimewrite)(struct soap *soap, void *handle, const char *buf, size_t len)
Called by the gSOAP run-time MTOM/MIME attachment receiver to write part of an inbound MTOM/MIME attachment to an application's data store. The handle contains the value returned by the fmimewriteopen callback. The buf argument contains the data of length len. The callback should return a gSOAP error code (e.g. SOAP_OK when no error occurred).
void(*soap.fmimewriteclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time MTOM/MIME attachment receiver at the end of the streaming process to close the data store. The fmimewriteclose callback is called after successfully receiving the data or when an error occurred. The handle contains the value returned by the fmimewriteopen callback.


In addition, a void*user field in the struct soap data structure is available to pass user-defined data to the callbacks. This way, you can set soap.user to point to application data that the callbacks need such as a file name for example. The following example illustrates the client-side initialization of an image attachment struct to stream a file into a MTOM attachment without HTTP chunking (HTTP streaming chunked MTOM transfer is presented in Section 16.5):

int main()
{
   struct soap soap;
   struct xsd__base64Binary image;
   FILE *fd;
   struct stat sb;
   soap_init1(&soap, SOAP_ENC_MTOM); // mandatory to enable MTOM
   if (!fstat(fileno(fd), &sb) && sb.st_size > 0)
   { // because we can get the length of the file, we can stream it without chunking
      soap.fmimereadopen = mime_read_open;
      soap.fmimereadclose = mime_read_close;
      soap.fmimeread = mime_read;
      image.__ptr = (unsigned char*)fd; // must set to non-NULL (this is our fd handle which we need in the callbacks)
      image.__size = sb.st_size; // must set size
   }
   else
   { // don't know the size, so buffer it
      size_t i;
      int c;
      image.__ptr = (unsigned char*)soap_malloc(&soap, MAX_FILE_SIZE);
      for (i = 0; i < MAX_FILE_SIZE; i++)
      {
         if ((c = fgetc(fd)) == EOF)
            break;
         image.__ptr[i] = c;
      }
      fclose(fd);
      image.__size = i;
   }
   image.type = "image/jpeg"; // MIME type
   image.options = "This is my picture"; // description of object
   soap_call_ns__method(&soap, ...);
   ...
}
void *mime_read_open(struct soap *soap, void *handle, const char *id, const char *type, const char *description)
{ return handle;
}
void mime_read_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
size_t mime_read(struct soap *soap, void *handle, char *buf, size_t len)
{ return fread(buf, 1, len, (FILE*)handle);
}


The following example illustrates the streaming of a MTOM/MIME attachment into a file by a client:

int main()
{ struct soap soap;
   soap_init(&soap);
   soap.fmimewriteopen = mime_write_open;
   soap.fmimewriteclose = mime_write_close;
   soap.fmimewrite = mime_write;
   soap_call_ns__method(&soap, ...);
   ...
}
void *mime_write_open(struct soap *soap, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding)
{
   FILE *handle = fopen("somefile", "wb");
   // We ignore the MIME content transfer encoding here, but should check
   if (!handle)
   {
      soap->error = SOAP_EOF;
      soap->errnum = errno; // get reason
   }
   return (void*)handle;
}
void mime_write_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
int mime_write(struct soap *soap, void *handle, const char *buf, size_t len)
{
   size_t nwritten;
   while (len)
   {
      nwritten = fwrite(buf, 1, len, (FILE*)handle);
      if (!nwritten)
      {
         soap->errnum = errno; // get reason
         return SOAP_EOF;
      }
      len -= nwritten;
      buf += nwritten;
   }
   return SOAP_OK;
}


Note that compression can be used with MTOM/MIME to compress the entire message. However, compression requires buffering to determine the HTTP content length header, which cancels the benefits of streaming MTOM/MIME. To avoid this, you should use chunked HTTP (with the output-mode SOAP_IO_CHUNK flag) with compression and streaming MTOM/MIME. At the server side, when you set SOAP_IO_CHUNK before calling soap_serve, gSOAP will automatically revert to buffering (SOAP_IO_STORE flag is set). You can check this flag with (soap->omode & SOAP_IO) == SOAP_IO_CHUNK to see if the client accepts chunking. More information about streaming chunked MTOM/MIME can be found in Section 16.5.

16.4  Redirecting Inbound MTOM/MIME Streams Based on SOAP Body Content

When it is preferable or required to redirect inbound MTOM/MIME attachment streams based on SOAP message body content, where for example the names of the resources are listed in the SOAP message body, an alternative mechanism must be used. This mechanism can be used both at the client and server side. Because the routing of the streams is accomplished with explicit function calls, this method should only be used when required and should not be considered optional. That is, when you enable this method, you MUST check for pending MTOM/MIME attachments and handle them appropriately. This is true even when you don't expect MTOM/MIME attachments in the payload, because the peer may trick you by sending attachments anyway and you should be prepared to accept or reject them. The explicit MTOM/MIME streaming mechanism consists of three API functions:

Function
void soap_post_check_mime_attachments(struct soap *soap)
Enables post-message body inbound streaming MTOM/MIME attachments. The presence of attachments must be explicitly checked using the function below.
int soap_check_mime_attachments(struct soap *soap)
Should be called after a client-side call (e.g. soap_call_ns__method) to check the presence of attachments. Returns 1 (true) when attachments are present. If present, each attachment MUST be processed with the function below.
struct soap_multipart *soap_get_mime_attachment(struct soap *soap, void *handle)
Parses an attachment and invokes the MIME callbacks (when set). The handle parameter is passed to fmimewriteopen. The handle may contain any data that is extracted from the SOAP message body to guide the redirection of the stream in the callbacks. The return value is a struct with a char *ptr field that contains the handle value returned by the fmimewriteopen callback, and char *id, char *type, and char *description fields with the optional MIME id, type, and description info.


Example client-side code in C:

struct soap *soap = soap_new1(SOAP_ENC_MTOM);
soap_post_check_mime_attachments(soap);
...
if (soap_call_ns__myMethod(soap, ...))
   soap_print_fault(soap, stderr); // an error occurred
else
{
   if (soap_check_mime_attachments(soap))    { // attachments are present, channel is still open
      {
         do
         {
            ... // get data 'handle' from SOAP response and pass to callbacks
            ... // set the fmime callbacks, if needed
            struct soap_multipart *content = soap_get_mime_attachment(soap, (void*)handle);
            printf("Received attachment with id=%s and type=%s\n", content->id?content->id:"", content->type?content->type:"");
         } while (content);
         if (soap->error)
            soap_print_fault(soap, stderr);
      }
   }
}
...
soap_destroy(soap);
soap_end(soap);
soap_free(soap); // detach and free context


The server-side service operations are implemented as usual, but with additional checks for MTOM/MIME attachments:

struct soap *soap = soap_new1(SOAP_ENC_MTOM);
soap_post_check_mime_attachments(soap);
...
soap_serve(soap);
...
int ns__myMethod(struct soap *soap, ...)
{    ... // server-side processing logic
   if (soap_check_mime_attachments(soap))    { // attachments are present, channel is still open
      {
         do
         {
            ... // get data 'handle' from SOAP request and pass to callbacks
            ... // set the fmime callbacks, if needed
            struct soap_multipart *content = soap_get_mime_attachment(soap, (void*)handle);
            printf("Received attachment with id=%s and type=%s\n", content->id?content->id:"", content->type?content->type:"");
         } while (content);
         if (soap->error)
            return soap->error;
      }
   }
   ... // server-side processing logic
   return SOAP_OK;
}

 

16.5  Streaming Chunked MTOM/MIME

gSOAP automatically handles inbound chunked MTOM/MIME attachments (streaming or non-streaming). To transmit outbound MTOM/MIME attachments, the attachment sizes MUST be determined in advance to calculate HTTP message length required to stream MTOM/MIME over HTTP. However, gSOAP also supports the transmission of outbound chunked MTOM/MIME attachments without prior determination of MTOM/MIME attachment sizes when certain conditions are met. These conditions require either non-HTTP transport (use the output-mode SOAP_ENC_XML flag), or chunked HTTP transport (use the output-mode SOAP_IO_CHUNK flag). You can also use the SOAP_IO_STORE flag (which is also used automatically with compression to determine the HTTP content length header) but that cancels the benefits of streaming MTOM/MIME. To stream chunked MTOM/MIME, set the __size field of an attachment to zero and enable HTTP chunking. The MTOM/MIME fmimeread callback then fetches data in chunks of any size between 1 and the value of the len argument. The fmimeread callback should return 0 upon reaching the end of the data stream.

你可能感兴趣的:(DI)