Web services has made the communication between heterogeneous environments (say PHP with .NET  or Java) a reality. It has defines standards for communicate not only with texts but also with binaries. And more importantly you can keep these communication confidential using encrypted messages according to your requirement. In this post, we will look at how we can implement such a system with PHP in one side.

In web services we can send/receive binary messages in two basic forms.

  1. Setting the binary inside the SOAP message. – Binary should be converted to base64 to make sure the SOAP body contains only texts. Since base64 converted data span longer than the binary data, we call this form as non-optimized way of sending binaries.
  2. Setting the binary outside the SOAP message – Binary would be sent as a MIME part in the message. And some element inside SOAP body keeps a reference to the binary using the MIME id. MTOM is a standard for referencing the MIME from inside the SOAP body. Since the binary is encoded, this will keep the message optimum with the binaries.

In WSF/PHP you can use any of these methods as you prefer. Lets forget about the encryption for now. We will check how we can send binaries in both of the above mentioned forms.

// first the request xml. Note tht xop:Include element that is referring the attachment with the id "myid1".
$reqPayloadString = <<<XML
<ns1:upload xmlns:ns1="http://wso2.org/wsfphp/samples/mtom">
               <ns1:fileName>test.jpg</ns1:fileName>
               <ns1:image xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
                  <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:myid1"></xop:Include>
               </ns1:image>
</ns1:upload>
XML;

try {
    $f = file_get_contents("my_binary_file.jpg");

    // here in the attachments option we define the binaries
    // corresponding to the id defined in the above XML
    $reqMessage = new WSMessage($reqPayloadString,
                                array("to" => "http://localhost/simple_upload_service.php",
                                      "action" => "http://wso2.org/upload",
                                      "attachments" => array("myid1" => $f)));

    // creating the WSClient
    // here the option useMTOM will decide whether the
    // attachment is set MTOM or base64
    $client = new WSClient(array("useWSA" => TRUE,
                                 "useMTOM" => TRUE));

    // sending the message and retrieving the response
    $resMessage = $client->request($reqMessage);

    printf("Response = %s \\n", $resMessage->str);

} catch (Exception $e) {

    if ($e instanceof WSFault) {
        printf("Soap Fault: %s\\n", $e->Reason);
    } else {
        printf("Message = %s\\n",$e->getMessage());
    }
}

As mentioned in the inline comment we can choose the preferred form of sending binary using the “useMTOM” option. if it is true, the binary is set as a MTOM, (referencing from the body) or if it is set false, the binary will be set as a base64 binary within the SOAP body.
To encrypt the message you only need to write few additional lines. First you define your policy that you need to encrypt this message using a WSPolicy object. Then the security token including the service public key and your private key. You need to give these two option as a constructor argument in WSClient. Here is that little additional code you need to write to add the encryption.

    // loading the keys
    $rec_cert = ws_get_cert_from_file("receiving_server.cert");
    $pvt_key = ws_get_key_from_file("my_private_key.pem");

    // here we defines the policies and create WSPolicy object
    $sec_array = array("encrypt" => TRUE,
                       "algorithmSuite" => "Basic256Rsa15",
                       "securityTokenReference" => "IssuerSerial");

    $policy = new WSPolicy(array("security" => $sec_array));

    // defining Security Tokens
    $sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
                                           "receiverCertificate" => $rec_cert));

    // modifing WSClient with adding WSPolicy and WSSecurityToken object
    $client = new WSClient(array("useWSA" => TRUE,
                                 "useMTOM" => TRUE,
                                 "policy" => $policy,
                                 "securityToken" => $sec_token));

You can implement the receiving side of the message similar to the sending side that we just described above. The most important thing is it doesn’t need to be written in PHP. It can be a Java code or .NET code.If you already have web services that use encrypted binary messaging, the above php code can be use out of the box to communicate with it.

In PHP you have several ways of sending binary data. It can be primarily categorized in to non-optimized method (send as base64 binary) and optimized method (send as MTOM or SWA). Here I m talking about how to send binaries in above mentioned methods starting from a WSDL.

WSDL

Think you have a WSDL with the following XML Schema.

            <!-- Here is my submitPerson method->

            <xs:element name="submitPerson">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="name" type="xs:string"/>
                        <xs:element name="age" type="xs:int"/>
                        <xs:element name="photo" type="xs:base64Binary"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>

The submit Person method submit the name,age and a photo which is a type of base64Binary.

Generated Class

After generating php class for this piece of code using wsdl2php you will have  the following class.

class submitPerson {

    /**
     * @var string
     */
    public $name;

    /**
     * @var int
     */
    public $age;

    // You need to set only one from the following two vars

    /**
     * @var Plain Binary
     */
    public $photo;

    /**
     * @var base64Binary
     */
    public $photo_encoded;

}

So it is very easy to fill the class with your own data. Note that you only need to fill one of the ‘photo’ or ‘photo_encoded’ fields. If you have binary already converted to base64 then you can use the ‘photo_encoded’ field where as if you only have the row binary just use the ‘photo’ field. Here are my values for this particular example.

$person = new submitPerson();
$person->name = "xxxx yyy";
$person->age = 35;
$person->photo = file_get_contents("/photo/xxxyyy");

Sending Binary as MTOM

Here is the code you need to send the above structure as a MTOM message.

$client = new WSClient(array("useMTOM" => TRUE)); // anyway useMTOM is default to TRUE for WSClient
$proxy = $client->getProxy();

$response = $proxy->submitPerson($person);

Sending Binary as Base64 Encoded string

You only need to change one option. That is setting “useMTOM” to FALSE will send the binary as Base64.

$client = new WSClient(array("useMTOM" => FALSE));
$proxy = $client->getProxy();

$response = $proxy->submitPerson($person);

SWA (SOAP With Attachments)
You can send the binary data as SWA by setting the “useMTOM” option to “SWA”. SWA is also a binary optimized method of sending attachments, but unlike with MTOM you can’t integrate security or reliability with this approach.

In a valid XML you can only have text and you can not have binary characters. SOAP which in fact an XML is also having this issue. So it is not staright forward to send binary data in SOAP. But with WSO2 WSF/PHP 1.3.2 It is really easy to send binaries. Check the online WSF/PHP Mtom Sample sources to understand the few lines that needed to send and serve binaries.

To understand what is happening inside WSF/PHP to send your message we will first take a simple demo. To understand the messages I will be sending text data rather than binary so you can clearly see how your data goes through the wire.

Here is my code to send the binary. (in fact a test in this percular case)

<?php
$requestPayloadString = <<<XML
<ns1:upload xmlns:ns1="http://php.axis2.org/samples/mtom">
  <ns1:fileName>test.txt</ns1:fileName>
  <ns1:image xmlmime:contentType="text/txt" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">

    <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:myid1"></xop:Include>
  </ns1:image>
</ns1:upload>
XML;

try {
    $f = file_get_contents("test.txt");

    $requestMessage = new WSMessage($requestPayloadString,
        array("to" => "http://localhost/my_mtom_service.php",
        "attachments" => array("myid1" => $f)));

    $client = new WSClient(array("useMTOM" => "FALSE"));

    $responseMessage = $client->request($requestMessage);

    echo $responseMessage->str;

} catch (Exception $e) {
    if ($e instanceof WSFault) {

        printf("Soap Fault: %s\\n", $e->Reason);
    } else {

        printf("Message = %s\\n",$e->getMessage());
    }
}

?>

And test.txt has the following text

TEXT as BINARY

In here the ‘xop:include’ element is where you going to send either the binary data or some reference to binary data.
“useMTOM” => FALSE
Note that in there in the “useMTOM” option for the WSClient I have given the value FALSE. That mean don’t use the MTOM(Message Transmission Optimization Mechanism) to serialize the message.There the generated soap message (over HTTP) will look like this,

POST /samples/mtom/mtom_upload_service.php HTTP/1.1
User-Agent: Axis2C/1.5.0
Content-Length: 404
Content-Type: application/soap+xml;charset=UTF-8
Host: 127.0.0.1:8080

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
   <soapenv:Header/>
   <soapenv:Body>
      <ns1:upload xmlns:ns1="http://php.axis2.org/samples/mtom">
         <ns1:fileName>test.jpg</ns1:fileName>
         <ns1:image xmlmime:contentType="text/txt" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">VEVYVCBhcyBCSU5BUlkK</ns1:image>
      </ns1:upload>
   </soapenv:Body>
</soapenv:Envelope>

The value “VEVYVCBhcyBCSU5BUlkK” is base64 encoded value for the “TEXT as BINARY” text.

<?php
echo base64_encode("TEXT as BINARY");
?>

In fact it is a standard that map each 6 bit of your binary file to an 8 bit (valid character to send through XML), so you can send the encoded value in the soap message. But no doubt you see the drawback. The encoded value is 4/3rd of the original data size. So this is not the most optimized way to send binary over SOAP.
“useMTOM” => TRUE
Setting this option TRUE will generate the following soap message over http.

POST /samples/mtom/mtom_upload_service.php HTTP/1.1
User-Agent: Axis2C/1.5.0
Transfer-Encoding: chunked
Content-Type: multipart/related; boundary=MIMEBoundary594038a6-6d95-1dd1-2b72-00197e732d4d; type="application/xop+xml"; start="<0.594038e2-6d95-1dd1-2b73-00197e732d4d@apache.org>"; start-info="application/soap+xml"; charset="UTF-8"
Host: 127.0.0.1:8080

32
--MIMEBoundary594038a6-6d95-1dd1-2b72-00197e732d4d
2

b2
content-transfer-encoding: binary
content-id: <0.594038e2-6d95-1dd1-2b73-00197e732d4d@apache.org>content-type: application/xop+xml;charset=UTF-8;type="application/soap+xml";21ff
   <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
      <soapenv:Header/>
      <soapenv:Body>
         <ns1:upload xmlns:ns1="http://php.axis2.org/samples/mtom">
            <ns1:fileName>test.jpg</ns1:fileName>
            <ns1:image xmlmime:contentType="text/txt" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
               <xop:Include href="cid:1.594037a2-6d95-1dd1-2b71-00197e732d4d@apache.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
            </ns1:image>
         </ns1:upload>
      </soapenv:Body>
   </soapenv:Envelope>32--MIMEBoundary594038a6-6d95-1dd1-2b72-00197e732d4d27econtent-transfer-encoding: binarycontent-id:
   <1.594037a2-6d95-1dd1-2b71-00197e732d4d@apache.org>content-type: text/txtfTEXT as BINARY234--MIMEBoundary594038a6-6d95-1dd1-2b72-00197e732d4d--0

Note that in the last line you see the data you send as it is. Here the binary is sent as an attachment and the element which suppose to keep the binary data refers to the attachment using the content id (check “href” attribute). It is obvious for a large binary data this is really optimized way of sending binaries.

“useMTOM” => “SWA”

WSF/PHP supports 1.3.2 supports SWA (Soap with Attachment) as well. you can enable this by setting “useMTOM” => “swa”. It is also send the binary as an attachment.  But it is not a recommended approach since you can’t secure the message when using SWA, because it can not be represented in the XML infoset. Here is the message generated enabling the “SWA”,

POST /samples/mtom/mtom_upload_service.php HTTP/1.1
User-Agent: Axis2C/1.5.0
Transfer-Encoding: chunked
Content-Type: multipart/related; boundary=MIMEBoundarybc636bdc-6d96-1dd1-31c0-00197e732d4d; type="application/xop+xml"; start="<0.bc636c40-6d96-1dd1-31c1-00197e732d4d@apache.org>"; start-info="application/soap+xml"; charset="UTF-8"
Host: 127.0.0.1:8080

32
--MIMEBoundarybc636bdc-6d96-1dd1-31c0-00197e732d4d
2

b2
content-transfer-encoding: binary
content-id: <0.bc636c40-6d96-1dd1-31c1-00197e732d4d@apache.org>content-type: application/xop+xml;charset=UTF-8;type="application/soap+xml";21b3
   <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
      <soapenv:Header/>
      <soapenv:Body>
         <ns1:upload xmlns:ns1="http://php.axis2.org/samples/mtom">
            <ns1:fileName>test.jpg</ns1:fileName>
            <ns1:image xmlmime:contentType="text/txt" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">1.bc6368a8-6d96-1dd1-31bf-00197e732d4d@apache.org</ns1:image>
         </ns1:upload>
      </soapenv:Body>
   </soapenv:Envelope>

32
--MIMEBoundarybc636bdc-6d96-1dd1-31c0-00197e732d4d
2

7e
content-transfer-encoding: binary
content-id:
   <1.bc6368a8-6d96-1dd1-31bf-00197e732d4d@apache.org>content-type: text/txtfTEXT as BINARY234--MIMEBoundarybc636bdc-6d96-1dd1-31c0-00197e732d4d--0

© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress