Posting a SOAP request

Find quick help here to get you started with Hollywood
Post Reply
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

Posting a SOAP request

Post by mrupp »

Hi everybody

I'm trying to figure out how to post a SOAP request with Hollywood. I figured I should do this using the hURL plugin. My overall goal is to write Sonos controller app for the Amiga, but let's take one step at a time, shall we? ;)
This is want I'd like to achieve:

Method: POST
URL: http://192.168.1.227:1400/MediaRenderer ... rt/Control
Headers:
Content-Type = text/xml
SOAPACTION = urn:schemas-upnp-org:service:AVTransport:1#Play
XML-Data:

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
        <u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
            <InstanceID>0</InstanceID>
            <Speed>1</Speed>
        </u:Play>
    </s:Body>
</s:Envelope> 
I testet this request with Postman (https://www.postman.com/), where it works just as expected.

And this is what I tried with Hollywood:

Code: Select all

Function Play()

	Local body$ = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:Play xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>"

	form = hurl.Form();
	form:addcontent("BODY", body$, "text/xml")

	e = hurl.Easy()
	e:setopt_url("http://192.168.1.227:1400/MediaRenderer/AVTransport/Control")
	e:setopt_httpheader({ "SOAPACTION" = "urn:schemas-upnp-org:service:AVTransport:1#Play"});
	e:setopt_httppost(form);
	e:perform()
	
	form:free();

EndFunction
Hopefully, somebody more experienced than me now just laughs and can point me to what I did wrong. :D I'm new to Hollywood programming, so I'm not sure at all, if I did the right approach using hurl.Easy() and hurl.Form(), there might me another, better way to do this...

This piece of code does run without errors but doesn't show any effect on my Sonos device at 192.168.1.227.

I'm grateful for any advice anyone can give me.
Greetings,
Michael
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

Re: Posting a SOAP request

Post by mrupp »

I might be a step further now... maybe... but have run into trouble again.

I figured I have to send raw data and not form data. Sending form data doesn't work with Postman either, the data is marked as raw there. The only method I found in the hURL doc to do so is easy:Send(), therefore I tried this:

Code: Select all

Function Play()
	Local body$ = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:Play xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>"

	Local e = hurl.Easy()
	e:setopt_url("http://192.168.1.227:1400/MediaRenderer/AVTransport/Control")
	e:setopt_verbose(True)
	e:setopt_post(1)
	e:setopt_httpheader({ "SOAPACTION" = "urn:schemas-upnp-org:service:AVTransport:1#Play"});
	e:send(body$)
	e:close()
	
EndFunction
Unfortunately, when running this code, there's an error on "e:send(body$)" --> Unsupported protocol! (1)
Any ideas how to fix this?
p-OS
Posts: 167
Joined: Mon Nov 01, 2010 11:56 pm

Re: Posting a SOAP request

Post by p-OS »

Maybe you could try sth. like this:

Code: Select all

url=http://192.168.1.227:1400/MediaRenderer/AVTransport/Control
mimetype="text/xml"

request=[[
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
        <u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
            <InstanceID>0</InstanceID>
            <Speed>1</Speed>
        </u:Play>
    </s:Body>
</s:Envelope> 
]]


response=DownloadFile(url,{Post=request,PostType=mimetype})
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

Re: Posting a SOAP request

Post by mrupp »

Thanks p-OS, that's a really good idea.
I tried excactly as you wrote (except to enclose the url in double quotes), but the code wouldn't run, the error output is:

Wrong usage/parameters for this command!
Read the documentation!

Hmm... I read the documentation for this command and can't see anything wrong with it.
User avatar
airsoftsoftwair
Posts: 5425
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Posting a SOAP request

Post by airsoftsoftwair »

mrupp wrote: Tue Feb 02, 2021 7:57 pm Hmm... I read the documentation for this command and can't see anything wrong with it.
Sorry, it's another Hollywood bug. It has been reported here already: viewtopic.php?p=12409#p12409 :)
User avatar
airsoftsoftwair
Posts: 5425
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Posting a SOAP request

Post by airsoftsoftwair »

Coming back to your original problem, I know nothing about SOAP requests but if all you want to do is send a certain string using HTTP POST and add a custom header, it should be possible like this with hURL:

Code: Select all

Function p_Dump(v)
        DebugPrint(v)
EndFunction
        
Function Play()
	Local postdata$ = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:Play xmlns:u=\"urn:schemas-upnp-org:service:AVTransport:1\"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>"

	Local e = hurl.Easy()
	e:setopt_url("http://192.168.1.227:1400/MediaRenderer/AVTransport/Control")
	e:setopt_verbose(True)
	e:setopt_post(1)
	e:setopt_postfields(postdata$)
	e:setopt_httpheader({"SOAPAction: urn:schemas-upnp-org:service:AVTransport:1#Play"});
	e:setopt_writefunction(p_Dump)
	e:perform()
	e:close()
	
EndFunction
You should definitely use easy:Perform() instead of easy:Send(). Also, I think you used easy:SetOpt_HTTPHeader() in the wrong way. It needs a list of strings, so something like:

Code: Select all

easy:SetOpt_HTTPHeader({"Custom-Header1: Test", "Custom-Header2: Test"})
I've also added a call to easy:SetOpt_WriteFunction() so that you can see the data you get back from the server.
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

Re: Posting a SOAP request

Post by mrupp »

YES, IT WORKS!
Your code works just like that. By all the things I tried, in the end it was mostly the wrong formatting of the header.
THANKS!
One step closer to the Sonos conroller app for Amiga systems!

Maybe this or something similar would be a good example to add to the hURL docs, don't you think?
Also it would be nice to add an example for setopt_httpheader() in the docs, that would be very helpful.
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

Re: Posting a SOAP request

Post by mrupp »

Just in case somebody looks for this is in the future, here's my final solution to post a SOAP request and getting back the result. Thanks to everybody helping me with this:

Code: Select all

Function p_SoapRequest(host$, path$, soapBody$, soapAction$, timeout)
	Local request$ = [[
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
		<s:Body>]] .. soapBody$ .. [[</s:Body>
</s:Envelope>
]]
	Local e = hurl.Easy()
	Local result$ = ""
	If Not IsNil(timeout)
		e:setopt_timeout(timeout)
	EndIf
	e:setopt_url(host$ .. path$)
	;e:setopt_verbose(True)
	e:setopt_post(1)
	e:setopt_postfields(request$)
	e:setopt_httpheader({"CONTENT-TYPE: text/xml", "SOAPACTION: " .. soapAction$});
	e:setopt_writefunction(Function(output$) result$ = result$ .. output$ EndFunction)
	e:perform()
	e:close()

	;DebugPrint("result: " .. result$)
	Return(result$)
EndFunction
Usage, following the example of the original thread:

Code: Select all

Function p_Play()
	Local host$ = "http://192.168.1.227:1400"
	Local soapPath$ = "/MediaRenderer/AVTransport/Control"
	Local soapBody$ = [[
		<u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
			<InstanceID>0</InstanceID>
			<Speed>1</Speed>
		</u:Play>
	]]
	Local soapAction$ = "urn:schemas-upnp-org:service:AVTransport:1#Play"
	Local result$ = p_SoapRequest(host$, soapPath$, soapBody$, soapAction$)
	Return(result$)
EndFunction
Cheers,
Michael
Post Reply