SOAP support in KCML

SOAP, or Simple Object Access Protocol, is the predominant industry standard for performing remote procedure calls across the Web. Originally proposed by DevelopMentor, Microsoft and UserLand, it has been enthusiastically adopted by other manufacturers such as IBM and the standard is now under the control of the W3C. Under the name Web Services it forms a core component of Microsoft .Net. Messages are formatted in XML and sent using standard Internet protocols such as HTTP. KCML implements the SOAP 1.1 specification and should be interoperable with other SOAP implementations including the KCML SOAP server. KCML requires a SOAP server to support the WSDL language for describing Web Services and the server must expose a WSDL file.

Server

KCML can act as a SOAP server exposing KCML functionality as methods on one or more interfaces. Many SOAP servers, including the KCML implementation, follow the convention of exposing the WSDL file at the end point URI with the parameter ?WSDL. The KCML SOAP server will create the WSDL and return it. e.g.

OBJECT s = CREATE "SOAP", "http://www.stockquoteserver.com/stockquote?WSDL"

SOAP calls are normally performed with POST requests but to get documentation on a KCML SOAP server just point your browser at the end point URL and the server will return a page describing the web services available at that endpoint. This is because the server processes GET requests as requests for documentation.

For more information on setting up a KCML SOAP server see this page.

Client

KCML can act as a SOAP client. The client can access methods exposed by other servers using HTTP or HTTPS transport protocols.

To begin accessing the SOAP service we need a URL for the WSDL description. This is in a standard URL format such as "http://www.stockquoteserver.com/stockquote.wsdl". The URL can then be used to create a SOAP client object. The SOAP client object is created using the CREATE statement. The first parameter being "SOAP" and the WSDL URL being passed as the second. e.g.

OBJECT s = CREATE "SOAP", "http://www.stockquoteserver.com/stockquote.wsdl"
a$ = s.GetStockQuote$("AA")
OBJECT s = NULL

When executing the CREATE statement KCML will fetch and parse the WSDL. KCML supports http://, https://, file:// prefixes for the WSDL URL. The file:// prefix allows access to the local file system. Local file access can be handy if the server does not publish WSDL on its site. It is important that you free the SOAP client object when you are finished. This allows KCML to free memory used to hold the WSDL schema.

The SOAP object methods are subject to the same rules as any other object. You can see them here.

Methods and Properties

Inbuilt Methods

MethodArgumentsDescription
_AddAttachment$_AddAttachment$(sFilename$, sType$ [, sId$])Add an attachment
_GetAttachment$_GetAttachment$(sID$, BYREF sType$)Get an attachment
_AddCookie_AddCookie(sName$,sValue$)Set cookie sName$ to value sValue$. This method can be used several times to create several cookies.
_AddHTTPHeader_AddHTTPHeader(sName$,sValue$)Set header sName$ to value sValue$. This method can be used several times to add several headers
_SetProperty_SetProperty(sName$,sValue$)Set internal property in the following table to the specified value
sName$sValue$
"Header$"Insert sValue$ which must be valid XML inside the Header element.
"WSA_To$"Set the <wsa:to> element. This mandatory element is normally set to the url but can be overriden using this property.
"Encoding$"Allow the encoding of the soap XML packet to be something different to the default "UTF-8"
"Location$"Allow a different url endpoint to that specified in the WSDL.
"SAMLAssertion$"The string is embedded as is inside the wsse:Security element. Typically this will be a SAML token acquired by a previous SOAP call. The string should be valid XML and will include the Saml:Asserion element. This property was introduced in KCML 07.19.00.24258 and KCML 06.20.99.24258.
_Call_Call(sName$,sValue$) Call the method named in the sName$ parameter. This is useful where making the call using using object.MethodName() is difficult because the MethodName is held in a variable. The sValue$ is a single string parameter. So soap._Call("Mymethod", "Test") is equivalent to soap.Mymethod("Test"). Not supported in KCML 6.20.
_TraceFile_TraceFile(sFilename$)Open a trace file (this method was added to KCML 07.21.01.25256 and KCML 06.20.99.25256 as an alternative to having to use a config file or CREATE option)

Examples

There are some examples of calling web services from KCML on this page.

Configuration

There are many options that can be set for each client object. They can be set in two ways. In the KCML program or using a separate file that is loaded by the client object.

Programmatically

An optional third parameter to the CREATE statement allows SOAP options to be configured in the program source. See the CREATE documentation for a list of all the available options.

Example 2

If a proxy server must be used to cross a firewall. The proxy location can be specified with the environment variable HTTP_PROXY or with the PROXY keyword in the options string. e.g.

DIM wsdl$ = "http://localhost/soapdemo/services.asp"
DIM options$="PROXY=john:secretpassword@companyproxy:8080"
OBJECT s = CREATE "SOAP", wsdl$, options$
a$ = s.GetServerTime$()
OBJECT s = NULL

Config file

Setting up the SOAP client can involve setting lots of options. To remove these options from the program source we can use a separate configuration file. The config file is an XML document that will allow you to configure all aspects of the SOAP object.

Example 3

The same as example 2 but we have moved the proxy configuration out.

DIM wsdl$ = "http://localhost/soapdemo/services.asp"
DIM options$="CONFIG=c:/tmp/SOAP.config"
OBJECT s = CREATE "SOAP", wsdl$, options$
a$ = s.GetServerTime$()
OBJECT s = NULL

Where the SOAP.config file looks like this;

<Policy xmlns="http://www.kcml.com/soap/policy">
	<Transport>
		<Proxy>
			<Host>companyproxy:8080</Host>
			<Authentication>
				<Username>john</Username>
				<Password>secretpassword</Password>
			</Authentication>
		</Proxy>
	</Transport>
</Policy>

Syntax

<Policy xmlns="http://www.kcml.com/soap/policy" >
	<UseLiteralCalls/>
	<UseInterface name='...'/>
	<TraceFile name='...'/>
	<Transport retry='...' connectTimeout='...' readTimeout='...'>
		<Authentication>
			<Username>...</Username>
			<Password>...</Password>
		</Authentication>
		<Proxy>
			<Host>...</Host>
			<Authentication>
				<Username>...</Username>
				<Password>...</Password>
			</Authentication>
		</Proxy>
	</Transport>
	<Security addressing='...' timestamp='...' derivedKeys='...' mustUnderstand='...' >
		<clientToken>
			<Username passwordType='...'>
				<Username>...</Username>
				<Password>...</Password>
			</Username>
		</clientToken>
		<serviceToken>
			<X509 file='...'/>
		</serviceToken>
		<keyPair>
			<X509 file='...' />
			<privateKey file='...' />
		</keyPair>
		<Request sign="..." encrypt="..." />
		<Response sign="..." encrypt="..." />
	</Security>
</Policy>
Child ElementDescription
UseLiteralCalls

/Policy/UseLiteralCalls

This optional element is used to set literal calling mode. On method calls, in this mode, single scalar strings are passed to and from the server. The strings represent documents that the user is responsible for creating and decoding of received documents. KCML provides only the transport and error handling.

UseInterface

/Policy/UseInterface

Allows you to manually specifiy the service/port to use.

AttributeDescription
@name

/Policy/UseInterface/name

Name of interface

TraceFile

/Policy/TraceFile

Allows writing of output of the XML/HTTP request/responses to a file.

AttributeDescription
@name

/Policy/TraceFile/name

Name of file

Transport

/Policy/Transport

This optional element is used to define the HTTP transport options. We currently only support HTTP(S) transport.

AttributeDescription
@retry

/Policy/Transport/retry

This optional attribute specifies the number of times to retry a HTTP call in the event of getting no response from the server. Integer value between 1 and 100.

@connectTimeout

/Policy/Transport/connectTimeout

This optional attribute specifies the timeout (milliseconds) to allow when connecting to the service. If not set the client will use the TCP timeout.

@readTimeout

/Policy/Transport/readTimeout

This optional attribute specifies the timeout (milliseconds) to allow when reading HTTP response. If not set the client will block indefinitely.

@secureProtocol

/Policy/Transport/secureProtocol

Optional attribute, only to be used if there are issues with supported protocols. Only SSLv3 is supported for where old servers support only weaker protocols.

Child ElementDescription
Authentication

/Policy/Transport/Authentication

This optional element allows you to specify HTTP authentication. Only HTTP Basic authentication is support in KCML before 7.16.

Child ElementDescription
Username

/Policy/Transport/Authentication/Username

The elements value is the username string.

Password

/Policy/Transport/Authentication/Password

The elements value is the password string.

X509

/Policy/Transport/Authentication/X509

This element represents the X509 certificate used to identify and secure the web service. Only the web service can decrypt data encrypted with this certificates public key.

AttributeDescription
@file

/Policy/Transport/Authentication/X509/file

This attribute represents the X509 file location. The file must be in PEM or DER format.

oauth

/Policy/Transport/oauth

This optional element allows you to specify Oauth 2.0 authentication. KCML will use this information to obtain an access token with which to authorize requests. KCML will manage the token and add it to HTTP request headers as required.

Child ElementDescription
client_id

/Policy/Transport/oauth/client_id

Required. The public identifier of the application requiring authorization.

client_secret

/Policy/Transport/oauth/client_secret

Required. A secret known only to the application and the authorization server. Effectively a password.

issuer

/Policy/Transport/oauth/issuer

Required. The URL of the authorization server.

audience

/Policy/Transport/oauth/audience

Optional. Defines the intended consumer of the obtained access token. For example the URL of the SOAP service.

scope

/Policy/Transport/oauth/scope

Optional. Details the specific access for which authorization is being requested.

Proxy

/Policy/Transport/Proxy

This optional element allows you to specify a HTTP proxy server to use.

Child ElementDescription
Host

/Policy/Transport/Proxy/Host

This elements value is the proxy hostname string. Is of the form hostname[:port].

Authentication

/Policy/Transport/Proxy/Authentication

Use this optional element to provide details for an authenticating HTTP proxy. We only support HTTP Basic authentication.

Child ElementDescription
Username

/Policy/Transport/Proxy/Authentication/Username

This elements value is the proxy username string.

Password

/Policy/Transport/Proxy/Authentication/Password

This elements value is this proxy password string.

Security

/Policy/Security

This optional element provides configuration details for our WS-Security implementation.

AttributeDescription
@version

/Policy/Security/version

Optional.

@addressing

/Policy/Security/addressing

This optional boolean attribute indicates if WS-Addressing elements should be added when adding security elements.

@addressingVersion

/Policy/Security/addressingVersion

Optional.

@timestamp

/Policy/Security/timestamp

This optional boolean attribute indicates if a timestamp should be added to outgoing SOAP messages.

@mustUnderstand

/Policy/Security/mustUnderstand

This optional boolean attribute indicates if mustUnderstand attribute is set on the SOAP envelope/header. Default is true.

@encryptAlgo

/Policy/Security/encryptAlgo

This optional boolean attribute indicates the encryption algorithm to use.

  • aes128-cbc
  • aes256-cbc. Default.

@encryptKeyAlgo

/Policy/Security/encryptKeyAlgo

This optional boolean attribute indicates the key encryption algorithm to use.

  • rsa-1_5
  • rsa-oaep. Default.

@derivedKeys

/Policy/Security/derivedKeys

Optional boolean attribute. Don't use the shared encrypted key todo encryption and signing. Instead derive keys from it to use.

Child ElementDescription
clientToken

/Policy/Security/clientToken

This optional element encapsulates a list of tokens that the client can identify itself to the server with.

Child ElementDescription
Username

/Policy/Security/clientToken/Username

This element represents a username security token.

AttributeDescription
@passwordType

/Policy/Security/clientToken/Username/passwordType

This optional attribute allows you to specify how the password is encoded:

  • Text - Default. Send password as plain text. Should only be used if the transport is encrypted (SSL/HTTP) or the username token is encrypted.
  • Digest - Send a digest of the password.

Child ElementDescription
Username

/Policy/Security/clientToken/Username/Username

This optional element value is the username string for the username token.

Password

/Policy/Security/clientToken/Username/Password

This optional element value is the password string for the username token.

serviceToken

/Policy/Security/serviceToken

The web service identifier. Must be a X509 certificate. This must be present if you wish to encrypt messages.

Child ElementDescription
X509

/Policy/Security/serviceToken/X509

This element represents the X509 certificate used to identify and secure the web service. Only the web service can decrypt data encrypted with this certificates public key.

AttributeDescription
@file

/Policy/Security/serviceToken/X509/file

This attribute represents the X509 file location. The file must be in PEM or DER format.

@reftype

/Policy/Security/serviceToken/X509/reftype

Optional. Not documented.

keyPair

/Policy/Security/keyPair

This optional element encapsulates a public private key pair. The public key is part of a X509 certificate. The client will use the private key to sign messages and pass the certificate so the service can validate this signature.

Child ElementDescription
X509

/Policy/Security/keyPair/X509

This element represents the X509 certificate used to validate messages.

AttributeDescription
@file

/Policy/Security/keyPair/X509/file

This attribute represents the X509 file location. The file must be in PEM or DER format.

@file

/Policy/Security/keyPair/X509/file

The private key file location. The file must be in PEM or DER format.

Request

/Policy/Security/Request

SOAP request security options.

AttributeDescription
@sign

/Policy/Security/Request/sign

Comma separated list of SOAP envelope elements to sign. The Timestamp is signed by default iff the message contains any other signing.

  • Body - SOAP envelope body
  • Token - client security token

@encrypt

/Policy/Security/Request/encrypt

Comma separated list of SOAP envelope elements to encrypt.

  • Body - SOAP envelope body
  • Token - client security token

Response

/Policy/Security/Response

SOAP response security options.

AttributeDescription
@sign

/Policy/Security/Response/sign

List of SOAP envelope elements that should be signed.

@encrypt

/Policy/Security/Response/encrypt

List of SOAP envelope elements that should be encrypted.

WS-* support

The OASIS consortium publish many specifications relating to web services.

This is an area of constant development so it might be worthwhile contacting the KCML development team if you need help with a particular specification.

We implement partial compliance as documented below.

Security

The SOAP client has partial support of the OASIS Web Services Security (Version 1.0) specification. This provides a security framework for SOAP messages.

Support includes

and requires OpenSSL to be installed, see Operating System Requirements.

All the options must be set via the config file.

Example 4

DIM wsdl$ = "http://localhost/soapdemo/services.asp"
DIM options$="CONFIG=c:/tmp/SOAP.config,WSS=(username:'roy', password:'pass123')"
OBJECT s = CREATE "SOAP", wsdl$, options$
a$ = s.GetServerTime$()
OBJECT s = NULL

Where the SOAP.config file looks like this;

<Policy xmlns="http://www.kcml.com/soap/policy">
	<Transport>
		<Proxy>
			<Host>companyproxy:8080</Host>
			<Authentication>
				<Username>john</Username>
				<Password>secretpassword</Password>
			</Authentication>
		</Proxy>
	</Transport>
	<Security timestamp='true'>
		<clientToken>
			<Username passwordType='text'/>
		</clientToken>
		<serviceToken>
			<X509 file='/tmp/certificate.pem'/>
		</serviceToken>
		<Request sign="Body, Token" encrypt="Body, Token" />
		<Response sign="Body" encrypt="Body" />
	</Security>
</Policy>

This example will

Legacy implementation

This support is limited to outgoing messages signed using RSA keys with X509 certificates as the security token.

Each outgoing message has a header added to the SOAP envelopes header section. This security header contains the certificate and a signature value generated from the SOAP message. The header information can then used by the message recipient to confirm the source of the message.

This support is controlled via the CREATE "WSS" keyword. The value following the WSS keyword is a comma separated list of tokens and values that must be enclosed in brackets. The general format is shown below:

WSS=(keyword:'value', keyword:'value', ...)

Supported keywords:

Token Description
cfile X509 Certificate file. The file can be in either PEM or a DER format.
pkfile Private RSA key file. The file can be in either PEM or a DER format.

Example of use in a CREATE statement:

OBJECT oSOAP = CREATE "SOAP", "myService.wsdl", "PROXY=user:passwd@proxy, WSS=(cfile:'myCertificate.x509', pkfile:'myPrivateKey.key')"

The role of WSDL

WSDL or Web Services Description Language is a W3C standard defining how Web Services expose their methods. The KCML SOAP client uses WSDL to find the server and to discover how to frame the SOAP requests for the available method. The SOAP object browser in the KCML workbench uses WSDL to list the available methods and to enumerate their parameters and parameter types. The formal definition of the language is available here but it is not necessary to know the language in order to be able to use a web service. Most SOAP implementations, including the KCML server implementation, will automatically generate a WSDL file describing their services. This file can be manually fetched and stored on the KCML client machine or it can be dynamically fetched when the CREATE statement instantiates a SOAP object. The former method will be faster but is less flexible.

If you are using the KCML client to access a Web Service that does not publish WSDL but does document the methods in some ad hoc fashion then you can use KCML's own SOAP server support to generate WSDL by writing stub DEFSUBs for the methods and using the KCMLObjectExports routine with the services actual endpoint to generate a WSDL file which can then be used on the client.

Message formats

WSDL specifies two distinct styles of SOAP messages: the original Remote Procedure Call or RPC where the message is defined by its parameters and the later Document form where the message has at most one input and one output parameter consisting of XML defined in a schema.

KCML converts both styles to a method call. This is a simple mapping for an RPC style but it is more complicated if the message is defined as Document and the document schema is complex. The corresponding method for a document style message will have zero or one input parameters and zero or one results. These parameters might be simple datatype, such as a string, a floating point number or a date, which can be mapped onto a KCML variable or they might be a complex structure which may or may not map onto a KCML DEFRECORD.

The original SOAP specification defined Encoding rules for the types of the parameters which are not enforced by any schema but many implementations prefer to let the schema define the types so that it can be used to validate the message as an XML document. This has led to 4 types of SOAP messages

  1. RE or RPC/encoded
  2. RL or RPC/literal
  3. DE or Document/encoded
  4. DL or Document/literal

Only RPC/encoded and Document/Literal are common and in fact most modern SOAP server implementations define messages as Document/Literal. The SOAP object browser in the workbench prefixes the method name with its message style.

Data types

KCML supports the following XSD datatypes and maps them to equivalent KCML types

XSD typeKCML typeComment
stringString
base64BinaryString
hexBinaryString
floatNumeric
doubleNumeric
decimalNumeric
integerNumeric
positiveIntegerNumeric
negativeIntegerNumeric
nonPositiveIntegerNumeric
nonNegativeIntegerNumeric
byteNumeric
shortNumeric
intNumeric
longNumeric
unsignedByteNumeric
unsignedShortNumeric
unsignedIntNumeric
unsignedLongNumeric
booleanNumeric0 for false and 1 for true
dateNumericJulian day number
timeNumericMilliseconds since midnight
dateTimeNumeric

Literal mode

Support for a document based exchange is supported in the KCML SOAP client. The KCML application can build the input as an XML document and pass it as the single argument to the method and get back an XML document as the result of the method call. This will require knowledge of the WSDL schema and the resulting document will have to be parsed using XML tools such as the DOM parser. However it gives the greatest flexibility and allows complex nested documents to be parsed. Many web services do not fit the RPC parameter passing model as they can return documents of unknown complexity and literal mode is the best choice for them.

KCML takes care of wrapping and unwrapping the soap envelopes and will handle server fault messages and convert them to errors.

DIM wsdl$ = "http://localhost/soapdemo/services.asp"
OBJECT s = CREATE "SOAP", wsdl$, "LIT=Y"
a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>")
OBJECT s = NULL

There is an example on this page.

The SOAP object supports the writing and reading of a SOAP Header section for each request and response respectively. The .Header$ property can be used to set the SOAP Header before a request is made. The value given will be enclosed in the relevant SOAP Header tags. This will need to be set before each request that requires it. The .Header$ is filled in with the response SOAP Header after a request is made.

s.Header$ = "<headerInfo><sessionID>123456</sessionID></headerInfo>"
a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>")

Character Encoding

The SOAP object doesn't support any character encoding conversion. That is left to the application.

For new applications that run KCML in UTF-8 mode the HTTP content headers default to UTF8. This is the character encoding a SOAP application should be using and should be changed.

Old 6.20 KCMLs didn't set any indicator for character encoding. Which caused some confusion in Web Services / Applications processing the requests/responses. This was changed so that KCML indicated what character encoding it was using for the application. It based this on the value of $OPTIONS RUN 61. It is possible to override these defaults using the Encoding property.

oSOAP.Encoding$ = "utf-8"

will result in the HTTP header. Content-Type=text/xml;charset='utf-8'

Cookies

HTTP Cookies generated by the server and sent as part of a response will be automatically added to any subsequent requests. They will persist as long as the SOAP Object. Path and Expiry times will be ignored.

The SOAP object supports the adding HTTP Cookies to requests. This is done using the method _AddCookie. The method takes two arguments. A name/value pair for the cookie. The cookie will be generated for all subsequent requests. Using a blank string for value will remove an already set cookie.

REM Add cookie
s._AddCookie("name", "value")
a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>")
REM Remove Cookie
s._AddCookie("name", "")

MTOM with XOP

The SOAP object includes a very basic mechanism for supporting SOAP Message Transmission Optimization Mechanism with XML-binary Optimized Packaging in document literal mode. This is achieved using two methods _AddAttachment and _GetAttachment

_AddAttachment$(sFilename$, sType$)

This takes two arguments and returns the XOP id to be used in the XML request document. The file will be automatically opened and attached to the outgoing HTTP request as a MIME part.

REDIM sID$ = s._addattachment$("filename.ext", "text")
REDIM sRequest$ = "<MethodRequest><fileName>myFilename</fileName><binaryData><xop:Include xmlns:xop=""http://www.w3.org/2004/08/xop/include"" href=""cid:" & sID$ & """/></binaryData></MethodRequest>"
REDIM sResponse$ = s.method$(sRequest$)

_GetAttachment$(sID$, BYREF sType$)

This takes two arguments and returns a filename of a file that contains the attachment. The file will be deleted automtically before the next request. The ID will need to be extracted by the application from the XML response document.

REDIM sFilename$ = s._getattachment$(sID$, BYREF sType$)

Debugging

KCML adds two properties for each SOAP client object to aid debugging. These are .Response$ and .Request$. These two scalar string properties provide access to the internal messages sent between the SOAP Client and Server. The .Request$ property contains the data sent when making a request of the SOAP server i.e. Invoke a method. The .Response$ is the reply received from a SOAP server. The length of each string property can be accessed by calling its numeric equivalent thus the length of the .Response$ string can be obtained by referencing .Response.

Error messages

All the SOAP $DECLARE calls will return TRUE if they are successful and FALSE if they fail. It is good programming practice to check the return codes from all subroutines, however in the interests of brevity none are checked in the examples on this page.

In the event of a problem there are three $DECLARES implemented for error reporting.

$DECLARE 'KCMLObjectGetLastError(STR(), RETURN INT())="*"
$DECLARE 'KCMLObjectGetLastErrorString(STR(), RETURN STR())="*"
$DECLARE 'KCMLObjectGetErrorString(STR(), INT(), RETURN STR())="*"
...
'KCMLObjectGetLastError("SOAP", error_number)
Returns the last SOAP error number.
'KCMLObjectGetLastErrorString("SOAP", error_text$)
Returns the last SOAP error text.
'KCMLObjectGetErrorString("SOAP", error_number, error_text$)

Returns the error text for a given error number.

Restrictions

There are many inconsistencies in the SOAP and WSDL specifications that have lead to incompatibilities between SOAP implementations. In general KCML follows the SOAP 1.1 and WSDL 1.1 specifications but it defers to the WS-I interoperability specification where there are conflicts.