0

I'm attempting to port a working SOAP client (written in Perl) to PowerShell for a different client (who won't allow us to pollute their locked-down windows environment with Perl).

So I try and download the WSDL information to create a proxy, using examples I found on the Web. This is how the existing Perl Code works, so the approach is sound.

$soap = New-WebServiceProxy -Uri $url -UseDefaultCredential

New-WebServiceProxy : The HTML document does not contain Web service discovery information.
At line:1 char:9
+ $soap = New-WebServiceProxy -Uri $uri
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (http://localhos...sx:Uri) [New-WebServiceProxy], InvalidOperationException
    + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.NewWebServiceProxy

But, as I said, this is based on working code, so it is unlikely anything is wrong at the server end. As a test, type

$WebResponse = Invoke-WebRequest -UseDefaultCredential $url

$WebResponse

And I get:

StatusCode        : 200
StatusDescription : OK
Content           : `<?xml version="1.0" encoding="UTF-8"?>

                    <!-- SOAP API definitions -->
                    <definitions name="SOAPAPI"

Which is what I would expect - it's start of the header of the WSDL.

I'm not seeing what's going wrong, does anyone have any suggestions?

mikb
  • 195
  • 13
  • A stab in the dark: does adding `?wsdl` to the value of `$uri` help? – mklement0 Aug 30 '19 at 03:11
  • In this case, no. We know the uri is correct - it is what is generated by the existing Perl code, and we get the WSDL back as content in the Invoke-WebRequest. – mikb Aug 30 '19 at 04:02
  • 1
    Is your variable $uri or $url? The error is different from your code – Scepticalist Aug 30 '19 at 04:20
  • Have you looked at this article? https://stackoverflow.com/questions/27793078/the-html-document-does-not-contain-web-service-discovery-information – Scepticalist Aug 30 '19 at 04:35
  • sorry, there's a typo there. The variable is $url, and it is conisitent in my PS history. – mikb Aug 30 '19 at 06:18
  • I looked at that article. I don't control the application generating the WSDL, and it doesn't follow the ?wsdl convention (because it has an extensive CGI and more than one SOAP API), so no soap (pun intended) – mikb Aug 30 '19 at 06:27
  • I tried dumping the content from `$WebResponse` into a file, and feeding that to `New-WebServiceProxy`, but it fails to validate the XML.I seem to have a strange UTF BOM at the start of the output I can't get rid of, and I wonder if this is what is crashing `New-WebServiceProxy` if I download the WSDL directly. In the Perl case, LWP::UserAgent and SOAP::Lite may be convering the sins of the IIS, so it isn't a problem. I have verified the Perl Code still runs correctly, btw. – mikb Aug 30 '19 at 06:36
  • The BOM is FF FE EF 00 BB 00 BF 00. a 16 bit little endian (UNICODE encoding) of a UTF8 BOM (FEFF EFBBBF)? – mikb Aug 30 '19 at 07:03
  • I'm beginning to think that there's no-one who actually **understands** what `New-WebServiceProxy` does. Here's a more detailed message (via the `$_ | ConvertTo-Json` hack): `The HTML document does not contain Web service discovery information.` I have tried this with both Windows and Linux servers. If I manually put the wsdl in a Here string, set the endpoint, and write that to a local file the cmdlet works. Wireshark tells me the WSDL was downloaded successfully, so why can't the cmdlet process it? – mikb Sep 04 '19 at 01:30

1 Answers1

0

Short answer - give up. It doesn't work. As a workaround, download the WSDL using Invoke-WebRequest -OutFile (that's important) and invoke New-Webserviceproxy with a local file Uri. If you need authentication, set the Credentials or UseDefaultCredentials property manually as appropriate (setting the parameters to New-Webserviceproxy does not have any effect). It now works, but I have to conclude that the underlying .Net c# code was never tested with anything but MS services....

Anyway, this was the solution:


        <#
    -----------------------------------------------------------------
        Phase 1: Download the service description (WSDL)

        We ought to be able to simply invoke the New-WebProxyService cmdlet with
        the URI of the service description, but that consistently fails, possibly because
        this is not a .Net application.

        So the workaround is to download the service description to a temporary file, and
        load that in New-WebServiceProxy as a file::/// URI
    -----------------------------------------------------------------
    #>

        $tmp = New-TemporaryFile
        $tmpName = $tmp.FullName

        # Splatting all the parameters to the cmdlet is more readable than a mix of command line
        # and splatted parameters 
        $wsdlArgs = @{
            Uri = -join ($uri,'?trid=', $service)
            OutFile = $tmpName
            PassThru = $true
            ErrorAction = 'Stop'
        }
        if ($Credential -eq [System.Management.Automation.PSCredential]::Empty) {
            $wsdlArgs['UseDefaultCredentials'] = $true
        }
        else {
            $wsdlArgs['Credential'] = $Credential
        }
        $wsdlResponse = Invoke-WebRequest @wsdlArgs # No try/catch here - if it fails, we fail!

    <#
    -----------------------------------------------------------------
        Phase 2: Create a WebServiceProxy Object

        We use try {} / finally {} to ensure that whether our cmdlet succeeds or fails,
        we clean up our mess...
    -----------------------------------------------------------------
    #>

        # Splatting all the parameters to the cmdlet is more readable than a mix of command line
        # and splatted parameters 
        $soapArgs = @{
            Uri         = ([System.Uri]($tmpName)).AbsoluteUri
            Class       = 'SOAPAPI'
            Namespace   = "www.kambe.com.au"
            ErrorAction = 'Stop'
        }
        try {
            $soap = New-WebServiceProxy @soapArgs # No catch block, if the cmdlet fails, we fail!
        }
        finally { # Whether the cmdlet succeeds or fails, we remove the temp file
            Remove-Item $tmpName -Force
        }

        # It seems the Credential and UseDefaultCredential params are ignored by New-WebServiceProxy if not used?
        # Programming pragmatism: it works this way, and not any other way
        if ($Credential -eq [System.Management.Automation.PSCredential]::Empty) {
            $soap.UseDefaultCredentials = $true
        }
        else {
            $soap.Credentials = $Credential
        }

mikb
  • 195
  • 13