+
+
+Properties of this class that begin with Http are passed through to the
+%Net.HttpRequest instance used by the class.
+
+The only supported use of this class is as the super class for a SOAP Web Client.
+Parameters, properties and methods may be used by the application.]]>
+1
+%occErrors
+TECH.SOAP.WebBase
+3
+
+
+
+The SOAPVERSION parameter specified the version of SOAP which is supported.
+Possible values for the version are 1.1 and 1.2. The default value "" specifies
+that both SOAP 1.1 and SOAP 1.2 are supported.
+STRING
+,1.1,1.2
+ENUM
+
+
+
+
+ If the web client has the parameter SOAPACTIONQUOTED=1, then the web client will
+quote the SOAPAction value for SOAP 1.1. The default will be SOAPACTIONQUOTED=0
+in order to be compatible with earlier versions of Cache.
+%Boolean
+0
+
+
+
+
+
+For a SOAP web client, SoapVersion specifies the SOAP version that is used
+for the request.
+If SoapVersion="" (the default), then "1.1" is used if SOAPVERSION="1.1" or "" (the default).
+Otherwise "1.2" is used if SOAPVERSION="1.2".
+After the response is received, it is the version of the response.
+This, SoapVersion should be set before each method call.]]>
+%String
+
+
+
+
+Deprecated. %Net.HttpRequester will always be used as the network access layer
+%String
+
+
+
+
+
+If SoapBinary is 1, then the web client will use proprietary binary SOAP protocol.
+The SoapBinary property defaults to the SOAPBINARY parameter.
+%Boolean
+..#SOAPBINARY
+1
+
+
+
+
+Setter method to insure that first not binary message uses session cookie form binary messages
+1
+value:%Boolean
+%Status
+
+
+
+
+
+If set then we keep session cookie even if not binary session.
+%Boolean
+1
+
+
+
+
+Default charset for remote SOAP binary server.
+This parameter is by default used to dertermine if binary message needs to be UTF8 encoded.
+%String
+
+
+
+
+Property to allow override of charset for remote SOAP binary server.
+This parameter is used to dertermine if binary message needs to be UTF8 encoded.
+The default for SoapBinaryCharset is the SOAPBINARYCHARSET parameter.
+%String
+..#SOAPBINARYCHARSET
+1
+
+
+
+
+Property to allow the WS-Addressing namespace to be specified for requests
+%String
+1
+
+
+
+
+PolicyConfiguration is specified as 'Configuration class name':'Configuration name'
+where 'Configuration name' comes from the name attribute of the configuration element
+in the configuration class (i.e. sublcass of %SOAP.Configuration"
+If PolicyConfiguration="" or is not specified, then the policy specified by the
+service classname attribute is attached to this class.]]>
+%String
+1
+
+
+
+
+If specified, the caller assigned %Net.HttpRequest instance is used for
+the web service request.
+%Net.HttpRequest
+1
+
+
+
+
+Content-Type to be used for transport class
+%String
+1
+
+
+
+
+The name of the activated TLS/SSL configuration to use for https requests.
+%String
+1
+
+
+
+
+If request uses an SSL connection and a SSL handshake error has occurred,
+then SSLError contains text describing the SSL error.
+%String
+
+
+
+
+When making an SSL connection check the server identity in the certificate matches the name of the system we are connecting to.
+This defaults to being on and matches based on the rules layed out in section 3.1 of RFC 2818.
+%Boolean
+
+
+
+
+If the GzipOutput property is set to true (1), then the output request
+will be compressed using GZIP.
+The default value for GzipOutput is the GZIPOUTPUT parameter.
+%Boolean
+..#GZIPOUTPUT
+1
+
+
+
+Timeout value.]]>
+%Integer
+1
+
+
+
+
+The HttpResponse property is set to the %Net.HttpResponse instance for the response to the
+web service request. This property is only set when the %Net.HttpRequest object is used to
+make the request, i.e. when HttpRequestor="CACHE" (the default).
+%Net.HttpResponse
+1
+
+
+
+
+The HTTP version we should report to the server when making the request.
+Defaults to '1.1'.
+%String
+"1.1"
+1
+
+
+
+
+If true then automatically follow redirection requests from the web server.
+These are signaled by the HTTP status codes of the form 3xx.
+The default is false.
+%Boolean
+1
+
+
+
+Location and return the response.
+You can specify a default proxy server for this namespace or for this Caché
+by setting ^SYS("HttpRequest","ProxyServer") or ^%SYS("HttpRequest","ProxyServer").]]>
+%String
+1
+
+
+
+
+You can specify a default proxy server for this namespace or for this Caché
+by setting ^SYS("HttpRequest","ProxyPort") or ^%SYS("HttpRequest","ProxyPort").]]>
+%String
+1
+
+
+
+HttpProxyServer and HttpProxyPort. If the endpoint URL
+has the https: protocol, then once the tunnel is established we will negociate the SSL connection.]]>
+%Boolean
+1
+
+
+
+
+Use of SSL to the eventual endpoint is determined by the protocol part of
+web service's location url.]]>
+%Boolean
+1
+
+
+
+
+HttpProxyHTTPS property is ignored since the use of SSL to the end point
+is now determiend from the url.
+If using a proxy server and this is true then it issues a request for an https page
+rather than the normal http page. This allows a proxy server that support https to
+support a secure connection from this %Net.Httprequest class.
+%Boolean
+1
+
+
+
+
+A user agent that wishes to authenticate itself with a proxy--
+usually, but not necessarily, after receiving a 407 response--may do
+so by including an Proxy-Authorization header field with the request. The
+Proxy-Authorization field value consists of credentials containing the
+authentication information of the user agent for the realm of the
+resource being requested.]]>
+%String
+1
+
+
+
+
+The character set to send the HTTP request header in. According to the RFC the HTTP header
+should only contain ASCII characters as the behaviour with characters outside this range
+is unspecified. This class defaults to using UTF-8 as this leaves all the ASCII characters
+unchanged. You should never need to change this parameter.
+%String
+1
+
+
+
+Password are defined then this information
+will be send using Basic authentication to the web server. If you manually set the
+Authorization header this property will be ignored.]]>
+%String
+1
+
+
+
+Username and Password are defined then this information
+will be send using Basic authentication to the web server. If you manually set the
+Authorization header this property will be ignored.]]>
+%String
+1
+
+
+
+
+If HttpAccept202=1, HTTP status 202 will treated just the same as HTTP status 200.
+The HttpResponse.StatusCode property may be checked to see if 202 was actually returned.]]>
+%Boolean
+1
+
+
+
+
+If set to true then this will force the %Net.HttpRequest class to reuse the existing connection
+to the web server if the socket is already open. If there is any error it will be reported to
+the caller. This has been introduced to support reliable SOAP messaging, so should not normally
+be set by other code.
+%Boolean
+1
+0
+
+
+
+
+Internal structure used to store the collection of headers for %Net.HttpRequest.
+%String
+1
+1
+1
+
+
+
+
+Do httprequest.SetHeader("MyHeader","Data to display")
+
+The header name is case insensitive and this class forces it to upper case so
+when the Http request is issued it will output the header as:
+MYHEADER: Data to display
+Note that headers such as Content-Type, Content-Encoding, and Content-Length are
+part of the entity body rather than the http main headers and as such as forwarded
+to the ContentType, ContentEncoding and
+trying to set the Content-Length is just ignored as this is a read only property.
+Also any attempt to set the 'Connection' header is ignored at this request class
+does not support persistent connections.]]>
+name:%String,value:%String
+%Status
+1
+
+
+
+
+
+Clear all previously set Http headers.
+%Status
+1
+
+
+
+
+
+The WSSecurityLogin method adds the WS-Security Security header with /UsernameToken.
+Only the /UsernameToken/Username and /UsernameToken/Password fields are supported.
+Signing and/or encryption as specified by WS-Security is not supported.
+These facilities are expected to be implemented by the use of SSL.
+Username:%String,Password:%String
+%Status
+1
+
+
+
+
+1
+1
+1
+
+
+
+
+
+Test for valid WS-Security 1.1 SignatureConfirmation elements in response message.
+Return true if valid.
+%Boolean
+1
+
+
+
+
+
+
+The SecurityContextToken thus returned will be saved in the SecurityContextToken property.
+Future calls to this client may use this token if needed for the WS-Policy or
+by explicitly adding to SecurityOut by calling AddSecurityElement.
+
+The SecurityOut header needs to be set for proper security for the RequestSecurityToken request.
+The SecurityOut header used for WS-StartSecureConversation will be cleared after when this method is complete.]]>
+RST:%SOAP.WST.RequestSecurityToken
+%Status
+1
+
+
+
+
+
+Cancel the this client's current WS-SecureConversation token specified by the SecurityContextToken property.
+Clear the SecurityContextToken propery.
+If SecurityOut header has not been set, then a minimal SecurityOut using the token for signing is created.
+The SecurityOut header used for WS-StartSecureConversation will be cleared after when this method is complete.
+%Status
+1
+0 {
+ Set target.token=SCT
+ } Else {
+ Do ..SecurityOut.AddSecurityElement(SCT)
+ Set sig=##class(%XML.Security.Signature).Create(SCT,,$$$SOAPWSReferenceSCT)
+ Do ..SecurityOut.AddSecurityElement(sig)
+ Set target.SecurityTokenReference=##class(%SOAP.Security.SecurityTokenReference).GetSCTReference(SCT)
+ }
+
+ Set RST=##class(%SOAP.WST.RequestSecurityToken).%New()
+ Set RST.RequestType=$$$SOAPWSTCancel
+ Set RST.CancelTarget=target
+
+ // Send request for SecurityContextToken
+ Set sc=..SendSOAPRequest($$$SOAPWSSCTokenCancelRequest,0,RST,"RequestSecurityToken")
+
+ // Process response.
+ If $$$ISOK(sc) {
+ Set responseName=$$$xmlGetLocalName(RST.%ResponseDocument.DocumentId,RST.%ResponseNodeId)
+ If responseName="RequestSecurityTokenResponseCollection" {
+ Set element=##class(%SOAP.WST.RequestSecurityTokenResponseCollection).%New()
+ Set sc=element.XMLImport("RequestSecurityTokenResponseCollection","literal",$$$SOAPWSTNS,RST.%ResponseDocument,RST.%ResponseNodeId)
+ If $$$ISOK(sc) {
+ If element.RequestSecurityTokenResponse.Count()<1 {
+ Set sc=$$$ERROR($$$WSTCollectionEmptyResponse)
+ } Else {
+ // Use the first token for following messages
+ Set RSTR=element.RequestSecurityTokenResponse.GetAt(1)
+ If RSTR.RequestedTokenCancelled="" {
+ Set sc=$$$ERROR($$$WSTCancelResponse)
+ }
+ }
+ }
+ } Else {
+ Set sc=$$$ERROR($$$WSTCollectionResponseRequired,responseName)
+ }
+ }
+
+ // Reset SecurityOut and SecurityContextToken after this request
+ Set ..SecurityOut=""
+ Set ..SecurityContextToken=""
+
+ Quit sc
+]]>
+
+
+
+
+
+The SecurityOut header needs to be set for proper security for the CreateSequence request.
+The SecurityOut header used for WS-ReliableMessaging will be cleared after when this method is complete.
+
+If the SecurityContextToken property contains a SecurityContextToken, then this token will be
+associated with the new sequence.]]>
+createSequence:%SOAP.RM.CreateSequence
+%Status
+1
+
+
+
+
+
+Close this client's current WS-ReliableMessaging session specified by the RMSession property.
+Clear the RMSession property.
+%Status
+1
+
+
+
+
+
+ Action is the SOAPAction for the SOAP request.
+ If OneWay is 1 (true) then no response is expected.
+ Request is the %SOAP.RequestMessage subclass instance.
+ MethodName is an optional name of the method being called which is used to select the WS-Policy.
+The response message node will be set in the ResponseDocuemtn and ResponseNodeId properties of %SOAP.RequestMessage.]]>
+action:%String,oneWay:%Boolean=0,request:%SOAP.RequestMessage,methodName:%String
+%Status
+1
+
+
+
+
+
+1
+proxy:%SOAP.ProxyDescriptor,method:%String,Action:%String,OneWay:%Boolean=0
+%Status
+1
+1.0 {
+ // Chunked output for HTTP/1.1
+ Set stream=binwriter
+ } Else {
+ Set io=$io
+ Set stream=##class(%FileBinaryStream).%New()
+ Set sc=stream.Write("") ; force stream's file to open
+ If $$$ISERR(sc) Goto SOAPError
+ Set file=stream.Filename ; get filename and make current device
+ Use file:(/NOXY)
+ $$$SETIO("RAW")
+ Set binwriter.Chunked=0
+ Do binwriter.OutputStream()
+ Use io
+ kill binwriter
+ If $$$ISERR(sc) Goto SOAPError
+ }
+
+ #; Use transport to get response.
+ If transport="" {
+ Set transport=$this
+ // Try to use string for response if our built-in transport
+ Set responseStream="string"
+ } Else {
+ Set responseStream=##class(%GlobalBinaryStream).%New()
+ }
+
+ $$$SOAPTimer("creq")
+ #; Make the request
+ Set sc=transport.DoSOAPRequest($this,Action,OneWay,stream,.responseStream)
+
+ #; Log the response
+ $$$SOAPTimer("cres")
+ Do ##class(%SOAP.Binary).LogMessage(0,Action,responseStream,sc)
+
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; If one-way, check for empty response
+ If OneWay {
+ If $isobject(responseStream) {
+ Do responseStream.Rewind()
+ Set len=1
+ Set data=responseStream.Read(.len,.sc)
+ If $$$ISERR(sc) Goto SOAPError
+ Do responseStream.Rewind()
+ If len<=0 Set sc=$$$OK Goto SOAPExit
+ } Else {
+ If responseStream="" Set sc=$$$OK Goto SOAPExit
+ }
+ }
+
+ If 'OneWay {
+ #; Reset any INOUT properties
+ Do proxy.Reset()
+ #; Import the SOAP response.
+ #; Deserialize the message class
+ if '$isobject(responseStream) {
+ set responseStream=##class(%SOAP.BufferedStream).%New(responseStream)
+ }
+ Set sc=..ReadBinaryMessage(proxy,.asUTF8,.sessionFlag,responseStream)
+ If $$$ISERR(sc) Goto SOAPError
+ #; return no session cookie, if session not wanted
+ If 'sessionFlag,..HttpRequest'="" Do ..HttpRequest.DeleteSessionCookie()
+ }
+ $$$SOAPTimer("cret")
+ Goto SOAPExit
+ }
+
+ #; If in a SOAP session then add the SOAP session header
+ If ..SessionCookie'="" {
+ Set sessionHeader=##class(%SOAP.SessionHeader).%New()
+ Set sessionHeader.SessionCookie=..SessionCookie
+ Do ..HeadersOut.SetAt(sessionHeader,"CSPCHD")
+ }
+
+ #; Add Sequence and SequenceAcknowledgement headers if RM session
+ If $isobject(..RMSession),'..RMSession.Terminated {
+ If ..RMSession.ResponseIdentifier'="",..RMSession.MessageNumber>0 {
+ Do ..HeadersOut.SetAt(##class(%SOAP.RM.SequenceAcknowledgement).CreateHeader(..RMSession,1),"SequenceAcknowledgement")
+ }
+ Set ..RMSession.MessageNumber=..RMSession.MessageNumber+1
+ Do ..HeadersOut.SetAt(##class(%SOAP.RM.Sequence).CreateHeader(..RMSession,1),"Sequence")
+ }
+
+ #; Find WS-Policy alternative
+ Set ..Action=Action
+ Set ..OneWay=OneWay
+ Set originalSecurityOut=r%SecurityOut
+ Set sc=##class(%SOAP.Policy).ProcessSendAlternative($this,..MethodName,.sendAlternative)
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Get list of SOAP Headers
+ Set ..SoapHeaders=..#SOAPHEADERS
+
+ #; Create WS-Security header with UsernameToken, if neeeded
+ If ..Username'="" Do ..MakeSecurityHeader()
+
+ #; Setup WS-Addressing if required.
+ If '$data(sendAlternative),..AddressingOut="",$zcvt(..#WSADDRESSING,"U")="AUTO" {
+ Set action=##class(%SOAP.WebParameters).GetAction($classname(),..MethodName,"request")
+ If action="" Set action=Action
+ Set ..AddressingOut=##class(%SOAP.Addressing.Properties).GetDefaultRequestProperties(..Location,action,..WSANamespace)
+ }
+
+ Set ..IsMTOM=0
+ Set allowedVersion=..#SOAPVERSION
+ If allowedVersion="" {
+ If ..SoapVersion="" Set ..SoapVersion="1.1"
+ } ElseIf ..SoapVersion="" {
+ Set ..SoapVersion=allowedVersion
+ } Else {
+ If allowedVersion'=..SoapVersion {
+ Set sc=$$$ERROR($$$SOAPBadVersion,..SoapVersion)
+ Goto SOAPError
+ }
+ }
+
+ #; Initialize any WS-Security operations.
+ Set wsRequired=..InitializeSecurity(.sc)
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Check SoapVersion
+ If $case(..SoapVersion,"":0,"1.1":0,"1.2":0,:1) {
+ Set sc=$$$ERROR($$$SOAPBadVersion,..SoapVersion)
+ Goto SOAPError
+ }
+
+ #; Create SOAP request
+ $$$XMLUseGlobal
+ Set sc=..WriteHTTPContent(proxy,proxy.%RequestName,0,wsRequired,,"")
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Send SOAP request and get response
+ $$$SOAPTimer("creq")
+ If (..RMSession="") || (..RMSession.Terminated) {
+ Set sc=..SOAPRequestResponse(proxy,method,.retryAllowed)
+
+ Set sequence=..HeadersIn.GetAt("Sequence")
+ If $classname(sequence)'="%SOAP.RM.Sequence" Set sequence=""
+ Set ack=..HeadersIn.GetAt("SequenceAcknowledgement")
+ If $classname(ack)'="%SOAP.RM.SequenceAcknowledgement" Set ack=""
+
+ If $$$ISOK(sc), (sequence'="") || ((ack'="") && (..Action'=($$$SOAPWSRMns_"/CloseSequence")) && (..Action'=($$$SOAPWSRMns_"/TerminateSequence"))) {
+ Set sc=$$$ERROR($$$RMUnexpectedHeader,$select(sequence'="":"Sequence",1:"SequenceAcknowledgement"))
+
+ }
+ } Else {
+ $$$XMLSave(streamSave)
+ Set ..HttpForceReuseDevice=..RMSession.SSLSecurity
+ For i=1:1:..RMSession.MaxRetryCount {
+ Set sc=..SOAPRequestResponse(proxy,method,.retryAllowed)
+
+ If 'retryAllowed Quit
+
+ If $$$ISOK(sc) {
+ Set sequence=..HeadersIn.GetAt("Sequence")
+ If sequence'="",$classname(sequence)'="%SOAP.RM.Sequence" Set sequence=""
+
+ If ..RMSession.ResponseIdentifier'="" {
+ If '$isobject(sequence) {
+ Set sc=$$$ERROR($$$RMExpectedHeader)
+ Quit
+ }
+ If '..RMSession.CheckSCT($this) {
+ Set sc=$$$ERROR($$$RMUnexpectedHeader,"No SecurityContextToken, "_..RMSession.SCTIdentifier_", for Sequence with Identifier = "_sequence.Identifier)
+ Quit
+ }
+ If sequence.Identifier'=..RMSession.ResponseIdentifier {
+ Set sc=$$$ERROR($$$RMUnexpectedHeader,"Sequence with Identifier = "_sequence.Identifier)
+ Quit
+ }
+ If sequence.MessageNumber'=..RMSession.MessageNumber {
+ Set sc=$$$ERROR($$$RMUnexpectedMessageNumber)
+ Quit
+ }
+ $$$SOAPLogSecurity("Receive Sequence. MessageNumber="_sequence.MessageNumber)
+ } ElseIf $isobject(sequence) {
+ Set sc=$$$ERROR($$$RMUnexpectedHeader,"Sequence")
+ Quit
+ }
+ }
+
+ Set ack=..HeadersIn.GetAt("SequenceAcknowledgement")
+ If $classname(ack)'="%SOAP.RM.SequenceAcknowledgement" Set ack=""
+ If $isobject(ack) {
+ If ack.Identifier'=..RMSession.RequestIdentifier {
+ Set sc=$$$ERROR($$$RMUnexpectedHeader,"SequenceAcknowledgement for Identifier "_ack.Identifier)
+ Quit
+ }
+ Do ack.UpdateSession(..RMSession)
+ $$$SOAPLogSecurity("Receive SequenceAcknowledgement.")
+ If ..RMSession.GetMessageState(..RMSession.MessageNumber) {
+ Do ..RMSession.MessageOK()
+ Quit
+ }
+ // Final indicates sequence terminated
+ If ack.Final=$c(0) Quit
+ } ElseIf $$$ISOK(sc) {
+ Do ..RMSession.MessageOK()
+ Quit
+ }
+
+ Hang ..RMSession.RetryInterval
+ $$$XMLRestore(streamSave)
+ }
+ }
+
+ $$$SOAPTimer("cret")
+ If $$$ISERR(sc) Goto SOAPError
+
+SOAPExit
+ If io'="" Use io
+ // Restore SecurityOut to a state that it can be reused for next call
+ If $get(wsRequired) || $isobject(r%SecurityOut) {
+ If $data(sendAlternative) {
+ // If WS-Policy created SecurityOut, Restore caller's SecurityOut
+ Set ..SecurityOut=originalSecurityOut
+ } Else {
+ // Just reset code created SecurityOut
+ Do ..ResetSecurity()
+ }
+ }
+ Set ..KeepSessionCookie=0
+ Do ..HeadersOut.Clear()
+ Set ..AddressingOut=""
+ Set ..NamespacesOut=""
+ If ..Username'="" {
+ Set (..Username,..Password)=""
+ If $isobject(r%SecurityOut) {
+ Do r%SecurityOut.RemoveElement("UsernameToken")
+ }
+ }
+ Set ..ContentType=""
+ If error {
+ Set %objlasterror=sc
+ If $$$CheckSOAPLogInput || $$$CheckSOAPLogSecurity {
+ Kill err
+ Do $system.Status.DecomposeStatus(sc,.err)
+ $$$SOAPLogText("**** SOAP client return error. method="_method_", action="_Action)
+ $$$SOAPLogText(" "_err(1)_$c(13,10)_$c(13,10))
+ }
+ if method'="" Ztrap "SOAP"
+ }
+ Quit sc
+
+SOAPError
+ Set error=1
+ Goto SOAPExit
+]]>
+
+
+
+
+1
+proxy:%SOAP.ProxyDescriptor,method:%String,*retryAllowed:%Boolean
+%Status
+1
+
+
+
+
+
+1
+
+%Status
+1
+1 Set request.Port=$piece(host,":",2)
+ If $length(Location,"?")>1 {
+ Set Location=##class(%CSP.Page).EscapeURL($piece(Location,"?",1),"UTF8")_"?"_$piece(Location,"?",2,$length(Location,"?"))
+ } Else {
+ Set Location=##class(%CSP.Page).EscapeURL(Location,"UTF8")
+ }
+ Set sc=request.Post($piece(Location,"/",2,$length(Location,"/")))
+ If $$$ISERR(sc) Set ..SSLError=request.SSLError Quit sc
+ Set response=request.HttpResponse
+ Set ..HttpResponse=response
+ Set responseStream=response.Data
+ Set responseStatus=response.StatusCode
+
+ #; 202 response OK for one-way messages.
+ If OneWay,(responseStatus="202") || (responseStatus="200") Quit $$$OK
+
+ #; If HttpAccept202, treat status 202 as 200.
+ If ..HttpAccept202,responseStatus=202 Set responseStatus=200
+
+ #; Check response
+ If $case(responseStatus,"200":0,"201":0,"400":0,"500":0,:1) {
+ Quit $$$ERROR($$$SOAPUnexpectedStatus,response.StatusCode)
+ }
+
+ If responseStream="" Quit $$$ERROR($$$SOAPNoResponseBody)
+
+ Set responseContentType=$zcvt($piece(response.ContentType,";",1),"L")
+ If ..SoapBinary {
+ If (responseContentType'="application/octet-stream") Quit $$$ERROR($$$SOAPUnexpectedType,response.ContentType)
+ } Else {
+ If (responseContentType'="text/xml") &&
+ (responseContentType'="application/soap+xml") &&
+ (responseContentType'="multipart/related") {
+ Quit $$$ERROR($$$SOAPUnexpectedType,response.ContentType)
+ }
+ }
+
+ Quit $$$OK
+]]>
+
+
+
+
+