+
+
+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
+%systemInclude
+1
+%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
+
+
+
+
+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
+
+
+
+
+The name of method that is to be called
+%String
+1
+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
+
+
+
+
+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
+true.
+%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 jus tthe same as HTTP status 200.
+The HttpResponse.StatusCode property may be checked to see if 202 was actually returned.]]>
+%Boolean
+1
+
+
+
+
+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
+
+
+
+
+
+Clear all previously set Http headers.
+%Status
+
+
+
+
+
+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
+
+
+
+
+
+Test for valid WS-Security 1.1 SignatureConfirmation elements in response message.
+Return true if valid.
+%Boolean
+
+
+
+
+
+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()
+ Close file
+ 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()
+ }
+
+ #; If no action specified, use the method name from the proxy class.
+ If $tr(Action,"""","")="" Set Action=$$$ClassShortName($zobjclass(proxy))
+
+ #; Always use the same %Net.HttpRequest to maintain a session if
+ #; the session cookie is returned for SOAPSESSION=1
+ If ..HttpRequest="" Set ..HttpRequest=##class(%Net.HttpRequest).%New()
+
+ #; Make the request
+ Set sc=transport.DoSOAPRequest($this,Action,OneWay,stream,.responseStream)
+
+ #; Log the response
+ 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
+ If 'sessionFlag Set ..HttpRequest=""
+ }
+ 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")
+ }
+
+ #; Find WS-Policy alternative
+ Set ..Action=Action
+ Set ..OneWay=OneWay
+ Set ..policyAlternative=""
+ Set sc=##class(%SOAP.Policy).ProcessSendAlternative($this,..MethodName,.alternative)
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Create WS-Security header with UsernameToken, if neeeded
+ If ..Username'="" Do ..MakeSecurityHeader()
+
+ #; Setup WS-Addressing if required.
+ If '$data(alternative),..AddressingOut="",$zcvt(..#WSADDRESSING,"U")="AUTO" {
+ Set ..AddressingOut=##class(%SOAP.Addressing.Properties).GetDefaultRequestProperties(..Location,Action)
+ }
+
+ Set responseAttachments=""
+ 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.
+ If $isobject(r%SecurityOut) {
+ // wsRequired is true if any signing or encryption is to be done.
+ Set wsRequired=r%SecurityOut.Initialize($this,.sc)
+ If $$$ISERR(sc) Goto SOAPError
+ } Else {
+ Set wsRequired=0
+ }
+
+ #; Check SoapVersion
+ If $case(..SoapVersion,"":0,"1.1":0,"1.2":0,:1) {
+ Set sc=$$$ERROR($$$SOAPBadVersion,..SoapVersion)
+ Goto SOAPError
+ }
+
+ #; Create SOAP request
+ Set sc=..WriteHTTPContent(proxy,proxy.%RequestName,0,wsRequired,,"",.stream)
+ 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()
+ }
+
+ Set sc=transport.DoSOAPRequest($this,Action,OneWay,stream,.responseStream)
+
+ #; Log the response
+ Do ..LogInput(0,Action,responseStream,sc)
+
+ #; Return error if detected
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; If one-way, check for empty response
+ If $isobject(responseStream) {
+ Do responseStream.Rewind()
+ If OneWay {
+ 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
+ }
+
+ #; Get an entity resolver
+ Set tResolver=##Class(%XML.SAX.EntityResolver).%New()
+ If '$isObject(tResolver) Set sc=$$$ERROR($$$CannotCreateObject,"%XML.SAX.EntityResolver") Goto SOAPError
+
+ #; Parse the message into XML DOM
+ if '$isobject(responseStream) {
+ set responseStream=##class(%SOAP.BufferedStream).%New(responseStream)
+ }
+
+ // Process response with content-type=multipart/related
+ Set response=..HttpResponse
+ If $isobject(response) && ($zcvt($piece(response.ContentType,";",1),"L")="multipart/related") {
+ If ##class(%Net.HttpRequest).ParseContent(response.ContentInfo,.tmparray) {
+ Set sc=$$$ERROR($$$CSPInvalidContentType,response.ContentType)
+ Goto SOAPError
+ }
+
+ If $case($get(tmparray("type")),"text/xml":0,"application/soap+xml":0,"application/xop+xml":0,:1) {
+ Set sc=$$$ERROR($$$SOAPBadMultipart,response.ContentType)
+ Goto SOAPError
+ }
+
+ If tmparray("type")="application/xop+xml" {
+ Set ..IsMTOM=1
+ Set responseAttachments=..ResponseAttachments
+ Set start=##class(%Net.MIMEPart).NormalizeContentId(tmparray("start"))
+ Set startindex=$select(start="":1,1:0)
+ } Else {
+ Set start=""
+ Set startindex=1
+ }
+
+ Set msg=##class(%Net.MIMEPart).%New()
+ do msg.SetHeader("Content-Type",response.ContentType)
+ Set reader=##class(%Net.MIMEReader).%New()
+ Set sc=reader.OpenStream(responseStream)
+ If $$$ISERR(sc) Goto SOAPError
+ Set sc=reader.ReadMIMEBody(msg)
+ If $$$ISERR(sc) Goto SOAPError
+ If msg.Parts.Count()<1 {
+ Set sc=$$$ERROR($$$SOAPBadMultipart,response.ContentType)
+ Goto SOAPError
+ }
+
+ For index=1:1:msg.Parts.Count() {
+ Set part=msg.Parts.GetAt(index)
+ Do ..ResponseAttachments.Insert(part)
+ If (startindex=0) && (start=part.ContentId) {
+ Set startindex=index
+ }
+ }
+
+ If startindex=0 {
+ Set sc=$$$ERROR($$$SOAPBadMultipart,response.ContentType)
+ Goto SOAPError
+ }
+
+ Set part=msg.Parts.GetAt(startindex)
+ If ..IsMTOM {
+ If $zcvt($piece(part.ContentType,";",1),"L")'="application/xop+xml" {
+ Set sc=$$$ERROR($$$SOAPBadMultipart,part.ContentType)
+ Goto SOAPError
+ }
+ } Else {
+ If $case($zcvt($piece(part.ContentType,";",1),"L"),"text/xml":0,"application/soap+xml":0,"application/xop+xml":0,:1) {
+ Set sc=$$$ERROR($$$SOAPBadMultipart,part.ContentType)
+ Goto SOAPError
+ }
+ }
+ Set responseStream=part.Body
+ Set ..ResponseContentId=part.ContentId
+ Set ..ResponseContentLocation=part.ContentLocation
+ Do ..ResponseAttachments.RemoveAt(startindex)
+ }
+
+ #; Get an XML content handler that parses message into XML DOM
+ Set tHandler=##Class(%XML.Document).%New()
+ If '$isObject(tHandler) Set sc=$$$ERROR($$$CannotCreateObject,"%XML.Document") Goto SOAPError
+ Set tHandler.KeepWhitespace=1 // Need whitespace for XMLImport
+
+
+
+
+ #; Parse message
+ Set sc=##Class(APPS.TRANSP.XML.SAX.Parser).ParseStream(responseStream,tHandler,tResolver,..SAXFlags)
+
+
+ If $$$ISERR(sc) Goto SOAPError
+
+
+
+
+ #; Make sure to process session and security headers
+ Set headers=..SoapHeaders
+ If headers="" {
+ Set headers="Security:%SOAP.Security.Header,CSPCHD:%SOAP.SessionHeader"
+ } Else {
+ Set headers=","_headers
+ If headers'[",Security:" Set headers=headers_",Security:%SOAP.Security.Header"
+ If headers'[",CSPCHD:" Set headers=headers_",CSPCHD:%SOAP.SessionHeader"
+ Set headers=$extract(headers,2,*)
+ }
+ Set ..SoapHeaders=headers
+ $$$SOAPLogSecurity($c(13,10)_"Security action="_Action_", MethodName="_..MethodName)
+
+ #; Validate the SOAP envelope.
+ Do ..HeadersIn.Clear()
+ Set sc=..ProcessSOAPEnvelope(.tHandler,OneWay,.message,.versionMismatch)
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Save the WS-Security header
+ Set header=..HeadersIn.GetAt("Security")
+ If (header'="") && ($zobjclass(header)="%SOAP.Security.Header") {
+ Set ..SecurityIn=header
+ Set ..SecurityNamespace=header.Namespace
+ } Else {
+ If $zcvt(..#SECURITYIN,"L")="require" {
+ Set sc=$$$ERROR($$$SOAPWSSECURITYRequired)
+ Goto SOAPError
+ }
+ Set ..SecurityIn=""
+ Set ..SecurityNamespace=""
+ }
+
+ #; Process session header
+ Set header=..HeadersIn.GetAt("CSPCHD")
+ If (header'="") && ($zobjclass(header)="%SOAP.SessionHeader") {
+ Set sc=header.ProcessClient($this)
+ If $$$ISERR(sc) Goto SOAPError
+ }
+
+ #; Validate the WS-Policy policy that applies to this service
+ Set sc=##class(%SOAP.Policy).ProcessReceiveAlternative($this,..MethodName,.alternative)
+ If $$$ISERR(sc) Goto SOAPError
+
+ #; Import SOAP body.
+ If 'OneWay {
+ If method'="" {
+ #; Reset any INOUT properties
+ Do proxy.Reset()
+ #; Import the SOAP response.
+ Set sc=proxy.ImportSOAPMessage(..MethodName_"Response",..MethodName_"Result",tHandler,message,responseAttachments)
+ If $$$ISERR(sc) Goto SOAPError
+ } Else {
+ // method="" for %SOAP.WebRequest
+ Set sc=proxy.ReturnResponse(tHandler,message)
+ }
+ }
+
+SOAPExit
+ If io'="" Use io
+ Do ..HeadersOut.Clear()
+ Set ..AddressingOut=""
+ If $isobject(r%SecurityOut) do r%SecurityOut.Reset()
+ If ..Username'="" {
+ Set (..Username,..Password)=""
+ If $isobject(r%SecurityOut) {
+ Do r%SecurityOut.RemoveElement("UsernameToken")
+ }
+ }
+ Set ..ContentType=""
+ If error {
+ Set %objlasterror=sc
+ if method'="" Ztrap "SOAP"
+ }
+ Quit sc
+
+SOAPError
+ Set error=1
+ Goto SOAPExit
+]]>
+
+
+
+
+1
+
+%Status
+1
+1 Set request.Port=$piece(host,":",2)
+ If $length(Location,"?")>1 {
+ Set Location=##class(%CSP.Page).EscapeURL($piece(Location,"?",1))_"?"_$piece(Location,"?",2,$length(Location,"?"))
+ } Else {
+ Set Location=##class(%CSP.Page).EscapeURL(Location)
+ }
+ 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") Quit $$$OK
+
+ #; If HttpAccept202, treat status 202 as 200.
+ If ..HttpAccept202,responseStatus=202 Set responseStatus=200
+
+ #; Check response
+ If $case(responseStatus,"200":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
+]]>
+
+
+
+
+