2013年5月21日 星期二

MTOM (Message Transmission Optimization Mechanism)


當我們要在 xml 中傳送一組binary資料時,一般的作法是將此資料先使用Base64編碼,再送出,如此會使得此資料大小增加為原本的 4/3 倍。

為此,W3C定義了MTOM方法,當傳送大量的資料時,可以減少所需的資料傳輸量。
MTOM 的原理是使用 XOP (XML-binary Optimized Packaging) 來封裝 binary 資料,目前 XOP 只定義使用一種封裝方法,就是 MIME Multipart/Related。

以下將介紹一個 MTOM 的例子,並介紹如何利用 gSOAP 取得 MTOM 資料



一、MTOM 例子

我們從一個簡單的例子來了解 XOP 的作法,(此例子來源為http://www.w3.org/TR/2005/REC-xop10-20050125/) 
原始的 XML,此處 binary 資料使用 base64 編碼
 1 <m:data xmlns:m='http://example.org/stuff'>
 2   <m:photo>/aWKKapGGyQ=</m:photo>
 3 </m:data>

封裝為 XOP package,此處 binary 資料使用 MIME 封裝
 1 MIME-Version: 1.0
 2 Content-Type: Multipart/Related;boundary=MIME_boundary;
 3     type="application/xop+xml";
 4     start="<mymessage.xml@example.org>";
 5     start-info="text/xml"
 6 Content-Description: An XML document with my pic and sig in it
 7 
 8 --MIME_boundary
 9 Content-Type: application/xop+xml; 
10     charset=UTF-8; 
11     type="text/xml"
12 Content-Transfer-Encoding: 8bit
13 Content-ID: <mymessage.xml@example.org>
14 
15 <m:data xmlns:m='http://example.org/stuff'>
16   <m:photo><xop:Include 
17   xmlns:xop='http://www.w3.org/2004/08/xop/include' 
18   href='cid:http://example.org/me.png'/></m:photo>
19 </m:data>
20 
21 --MIME_boundary
22 
23 Content-Type: image/png
24 Content-Transfer-Encoding: binary
25 Content-ID: <http://example.org/me.png>
26 
27 // binary octets for png
28 
29 --MIME_boundary
封裝後,第一個 multipart 為 "application/xop+xml",並且此處的 "start-info" 要指向 XML,後續其他的multipart就可以用來放置各種資料,然後透過 cid 來找到正確的 multipart。 
注意: 就我個人的認知,此處的 cid 因為要符合 URI[RFC 2392]的定義,所以應該要將"cid:http://example.org/me.png" 編碼為 "cid:http%3a%2f%2fexample.org%2fme.png"才對


二、使用 gSOAP 接收 MTOM 資料

GSOAP 的支援,原文請參考 http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc16,以下幫忙做簡單翻譯。 
當使用 wsdl2h 進行 code generation 時,需要在 typemap.dat 中加入下列 namespace
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 > 
之後在產生的 header file中,將可以看到類似以下的 XOP 資料結構,或者使用者也可以自行進行定義新的資料結構
 1 #import "import/xop.h"
 2 struct x__myData 
 3 { 
 4    _xop__Include xop__Include; // attachment 
 5    @char *xmime5__contentType; // and its contentType 
 6 }; 
當要使用 GSOAP 時,需要告知將使用 MTOM
struct soap *soap = soap_new1(SOAP_ENC_MTOM);

注意:若沒有正確的設定 MIME Type, 則 GSOAP 會直接回 base64 而非使用 MTOM方式封裝資料。
Note that the xop__Include.type field must be set to transmit MTOM attachments, otherwise plain base64 XML will be used.



三、ONVIF的使用

參考 http://www.cs.fsu.edu/~engelen/soap.html 定義的 ONVIF 方式,code generation之後可以產生以下的資料結構,可用來放置 XOP 。
 1 #import "import/xop.h"
 2 struct tt__AttachmentData
 3 { 
 4    //xop__Include xop__Include; // attachment 
 5    _xop__Includexop__Include; // attachment
 6    @char *xmime__contentType; // and its contentType 
 7 }; 
此時,我們可以使用免費的 ONVIF Device Manager 工具,透過利用 ONVIF 定義的 UpgradeSystemFirmware command 命令來測試是否 MTOM 有正常運作。 
測試結果發現程式可以正確的呼叫 __tds__UpgradeSystemFirmware() stub function, 但是 _xop__Include_ 並沒有正確的產生。 
此時可以參考 gSOAP source code 內的 \samples\mtom-stream\mtom-stream-test.c,新增下列三個 callback
 1 void *mime_server_write_open(struct soap *soap, void *handle, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding);
 2 void mime_server_write_close(struct soap *soap, void *handle);
 3 int mime_server_write(struct soap *soap, void *handle, const char *buf, size_t len);
並且在 init soap 之後,設定對應的Callback
 1 soap_init1(&soap, SOAP_ENC_MTOM); 
 2 soap.fmimewriteopen = mime_server_write_open;
 3 soap.fmimewriteclose = mime_server_write_close;
 4 soap.fmimewrite = mime_server_write;
重新編譯後再次測試,此時便可以發現 _xop__Include_ 已經正確的產生,並且對應的物件也已經存入系統磁碟中

注意:
測試時發現,若檔案過小,例如只有128bytes,則ONVIF Device Manager會直接將資料使用base64編碼後,放入XML內。若檔案稍大,例如2048bytes,便會使用 MTOM的方法。

參考資料

  1. http://www.w3.org/TR/soap12-mtom/
  2. http://www.w3.org/TR/xop10/ (XML-binary Optimized Packaging)
  3. mtom-stream-test.c
  4. http://www.geekhideout.com/urlcode.shtml