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.

November 11th, 2008WSF/PHP Test Cases Explained

WSO2 WSF/PHP comes with a comprehensive set of test cases. It covers the most of the basic/concrete scenarios supported by WSF/PHP. You can find these test cases inside the “src/tests” directory of WSF/PHP source package. Or you can find the latest test-suite from the SVN location.

Here are some aspects covered in the test-suit.

Scenario Test Cases For Client Test Cases For Service
Basic Functionality echo_client*.phptmath_*.phpt samples/echo_service*.php
samples/math_service.php
Basic Schema Types BasicDataTypes/*.phpt services/BasicTypesDoclitBSvc/*.php
Complex Schema Types cmplxDataTypes/*.phpt services/ComplexDataTypesWSvc/*.php
services/ComplexDataTypesBSvc/*.php
WSDL/Schema Variations wsdl_mode/*.phpt services/wsdl_mode/*.php
WSDL Generation with Annotations wsdl_generation/*.phpt services/wsdl_generation/*.php
Reliable Messaging echo_rm*.phpt samples/echo_service_rm*.php
Security echo_encrypt_client*.phptecho_signing_client*.phpt

echo_timestamp_client*.phpt

echo_username_token_client*.phpt

encrypt_service*.phpsigning_service*.php

timestamp_service*.php

username_token_service*.php

MTOM mtom_*.phpt samples/mtom/*.php

(Note that Here ‘*’ is used as a wild card represent 0 or many characters)

Steps to Run Tests

  • First you need to install WSF/PHP correctly. Please read the WSF/PHP installation manual for that.
  • You have to have the ‘pear’ utility tool comes with PHP. And add this to the PATH environment variable.
  • Copy the samples and src/tests/samples/services directory (paths are relative to the root directory of the wsf/php package) to the web root directory.
  • Then go to the src/tests directory and execute the following command.
    pear run-tests -r

    This will execute all the test cases under the ‘tests’ directory and finally give a summery of the test results.

  • You can run individual test cases separately by providing the relative path to the test case from the ‘test’ directory. E.g. To run the echo_client.phpt test case, you may type
    pear run-tests samples/echo_client.phpt

If you like to add test cases for the WSF/PHP scenarios, follow this comprehensive guideline titled Writing Simple phpt Test Scripts For PHP Web Services.

November 7th, 2008WSF/PHP Samples Explained

Here is a simple categorization of the WSF/PHP samples. You can access all the wsf/php samples from http://labs.wso2.org/wsf/php/solutions/samples/index.html.

Sample Category Example Client Source Code Example Service Source Code Online Demo
Beginners echo_client.php echo_service.php Demo
REST echo_client_rest.php echo_service_with_rest.php Demo
WSDL Mode (Contract First) wsdl_11_client.php wsdl_11_service.php Demo
WSDL Generation (Code First) doclit_client.php doclit_service.php Demo
MTOM Attachments mtom_download_client.php mtom_download_service.php Demo
Security encryption_client.php encryption_service.php Demo
Reliable Messaging echo_client_rm.php echo_service_rm.php Demo
Data Services CustomerDetailsClient.php CustomerDetailsService.php Demo

If you have downloaded the WSF/PHP binaries or souce code package you can find all these samples, inside the ‘samples’ directory

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