CURL returning Bad Request

Hi,
I am trying to get responses using CURL for an API with SOAP.

If I use curl command directly on the terminal I receive the expected answer:

Command:

curl http://www.dneonline.com/calculator.asmx?wsdl -H 'Content-Type: text/xml' -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"><soapenv:Header/><soapenv:Body><tem:Add><tem:intA>1</tem:intA><tem:intB>2</tem:intB></tem:Add></soapenv:Body></soapenv:Envelope>'

Answer:

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><AddResponse xmlns="http://tempuri.org/"><AddResult>3</AddResult></AddResponse></soap:Body></soap:Envelope>

When I use the same command on the extension.conf I get an error:

[crm]
exten => _.,1,NoOp()
 same => n,Verbose(0, ${CURL(http://www.dneonline.com/calculator.asmx?wsdl -H 'Content-Type: text/xml' -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"><soapenv:Header/><soapenv:Body><tem:Add><tem:intA>1</tem:intA><tem:intB>2</tem:intB></tem:Add></soapenv:Body></soapenv:Envelope>')})

The return of the command is Bad Request:

[Nov 10 10:35:23]     -- Executing [1@crm:1] NoOp("Console/dsp", "") in new stack
[Nov 10 10:35:24]     -- Executing [1@crm:2] Verbose("Console/dsp", "0, <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
[Nov 10 10:35:24]     -- <HTML><HEAD><TITLE>Bad Request</TITLE>
[Nov 10 10:35:24]     -- <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
[Nov 10 10:35:24]     -- <BODY><h2>Bad Request</h2>
[Nov 10 10:35:24]     -- <hr><p>HTTP Error 400. The request is badly formed.</p>
[Nov 10 10:35:24]     -- </BODY></HTML>") in new stack
[Nov 10 10:35:24]  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
[Nov 10 10:35:24] <HTML><HEAD><TITLE>Bad Request</TITLE>
[Nov 10 10:35:24] <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
[Nov 10 10:35:24] <BODY><h2>Bad Request</h2>
[Nov 10 10:35:24] <hr><p>HTTP Error 400. The request is badly formed.</p>
[Nov 10 10:35:24] </BODY></HTML>
[Nov 10 10:35:24]     -- Auto fallthrough, channel 'Console/dsp' status is 'UNKNOWN'

What am I doing wrong? Can someone help me?

EXEMPLE: Get Started With SOAP and WSDL Testing in SoapUI | SoapUI

The CURL dialplan function does not take the same input as the curl application. It is documented on the wiki what it takes[1] and there is a separate dialplan function for setting some options[2].

[1] Asterisk 19 Function_CURL - Asterisk Project - Asterisk Project Wiki
[2] Asterisk 19 Function_CURLOPT - Asterisk Project - Asterisk Project Wiki

Hi, thanks for answering, here is the command in a friendly way:

curl http://www.dneonline.com/calculator.asmx?wsdl -H 'Content-Type: text/xml' -d '
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Add>
         <tem:intA>1</tem:intA>
         <tem:intB>2</tem:intB>
      </tem:Add>
   </soapenv:Body>
</soapenv:Envelope>'

The curl command uses the following options:

  • -H

    • -H, --header <header>
    • -H 'Content-Type: text/xml'
    • Extra header to include in the request when sending HTTP to a server
    • I added the follow option:CURLOPT(httpheader)
  • -d

    • -d, --data <data>
    • Sends the specified data in a POST request to the HTTP server, in the same way that a browser does when a user has filled in an HTML form and presses the submit button
    • I didn’t find any option that suits CURLOPT()

For the syntax of CURL:

CURL(url,post-data)

I not sure how I have to use post-data, for POST which one is correct?

CURL(url,HTTP POST)

OR

CURL(url,POST)

Because I tried both and didn’t notice difference, still getting error:

I change my extension to:

[crm]
exten => _.,1,NoOp()
 same => n,Set(CURLOPT(httpheader)=Content-Type: text/xml)
 same => n,Verbose(0, ${CURL(http://www.dneonline.com/calculator.asmx -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"><soapenv:Header/><soapenv:Body><tem:Add><tem:intA>1</tem:intA><tem:intB>2</tem:intB></tem:Add></soapenv:Body></soapenv:Envelope>',HTTP POST)})

CURLOPT(httpheader) is getting an error Unrecognized option: httpheader:

[Nov 10 11:55:37]     -- Executing [1@crm:2] Set("Console/dsp", "CURLOPT(httpheader)=Content-Type: text/xml") in new stack
[2021-11-10 11:55:37] ERROR[25295][C-0000021f]: func_curl.c:411 acf_curlopt_write: Unrecognized option: httpheader

I really dont now what I am doing wrong, and looks like CURLOPT doesnt have an option for -d.
Can you help me?

I don’t have first hand experience with it, I can’t help any further.

The POST Data for SOAP is the actual XML payload, which is the -d parameter in the shell command, less of course, the single quotes, which are stripped by the shell.

Without checking the actual values allowed for CURLOPT(), I note that you have a trailing space. That may well be considered significant.

Thanks!

I change the curl command, and now I put the xml inside of a file /tmp/payload.xml:

curl http://www.dneonline.com/calculator.asmx?wsdl -H 'Content-Type: text/xml' -d /tmp/payload.xml

I didn’t see the “trailing space” that you said, but I believe if I use a file It should minimize problems with spaces, if I understand it correctly.

[crm]
exten => _.,1,NoOp()
 same => n,Set(CURLOPT(httpheader)=Content-Type: text/xml)
 same => n,Verbose(0, ${CURL(http://www.dneonline.com/calculator.asmx?wsdl -d @/tmp/payload.xml)})

The return still the same: Unrecognized option: httpheader and Bad Request:

[Nov 10 13:15:19]     -- Executing [1@crm:1] NoOp("Console/dsp", "") in new stack
[Nov 10 13:15:19]     -- Executing [1@crm:2] Set("Console/dsp", "CURLOPT(httpheader)=Content-Type: text/xml") in new stack
[2021-11-10 13:15:19] ERROR[20862][C-0000022d]: func_curl.c:411 acf_curlopt_write: Unrecognized option: httpheader
[Nov 10 13:15:19]     -- Executing [1@crm:3] Verbose("Console/dsp", "0, <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
[Nov 10 13:15:19]     -- <HTML><HEAD><TITLE>Bad Request</TITLE>
[Nov 10 13:15:19]     -- <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
[Nov 10 13:15:19]     -- <BODY><h2>Bad Request</h2>
[Nov 10 13:15:19]     -- <hr><p>HTTP Error 400. The request is badly formed.</p>
[Nov 10 13:15:19]     -- </BODY></HTML>") in new stack
[Nov 10 13:15:19]  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
[Nov 10 13:15:19] <HTML><HEAD><TITLE>Bad Request</TITLE>
[Nov 10 13:15:19] <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
[Nov 10 13:15:19] <BODY><h2>Bad Request</h2>
[Nov 10 13:15:19] <hr><p>HTTP Error 400. The request is badly formed.</p>
[Nov 10 13:15:19] </BODY></HTML>
[Nov 10 13:15:19]     -- Auto fallthrough, channel 'Console/dsp' status is 'UNKNOWN'

Any ideas? Because I am totally lost, any help would be a great help.

The syntax is CURL(<uri>,<data>)

not CURL(<uri> -d @<file-containing-data>)

and not CURL(<uri> -d <data>)

Sorry, I know that you are trying to help me, but I don’t think that I get it.

But if I understand, what you are trying to say to me is in:

curl http://www.dneonline.com/calculator.asmx?wsdl -H 'Content-Type: text/xml' -d '
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Add>
         <tem:intA>1</tem:intA>
         <tem:intB>2</tem:intB>
      </tem:Add>
   </soapenv:Body>
</soapenv:Envelope>'

http://www.dneonline.com/calculator.asmx?wsdl is the <uri>

and the <data> is:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Add>
         <tem:intA>1</tem:intA>
         <tem:intB>2</tem:intB>
      </tem:Add>
   </soapenv:Body>
</soapenv:Envelope>

So I rewrote the extension:

[crm]
exten => _.,1,NoOp()
 same => n,Set(CURLOPT(httpheader)=Content-Type: text/xml)
 same => n,Verbose(0, ${CURL(http://www.dneonline.com/calculator.asmx,<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"><soapenv:Header/><soapenv:Body><tem:Add><tem:intA>1</tem:intA><tem:intB>2</tem:intB></tem:Add></soapenv:Body></soapenv:Envelope>)})

It give me a huge error:

[Nov 10 15:15:35]     -- Executing [1@crm:1] NoOp("Console/dsp", "") in new stack
[Nov 10 15:15:35]     -- Executing [1@crm:2] Set("Console/dsp", "CURLOPT(httpheader)=Content-Type: text/xml") in new stack
[2021-11-10 15:15:35] ERROR[30878][C-00000241]: func_curl.c:411 acf_curlopt_write: Unrecognized option: httpheader
[Nov 10 15:15:35]     -- Executing [1@crm:3] Verbose("Console/dsp", "0, <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><soap:Code><soap:Value>soap:Receiver</soap:Value></soap:Code><soap:Reason><soap:Text xml:lang="en">System.Web.Services.Protocols.SoapException: Server was unable to process request. ---&gt; System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReaderImpl.Throw(Exception e)
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReaderImpl.Read()
[Nov 10 15:15:35]     --    at System.Xml.XmlTextReader.Read()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.Read()
[Nov 10 15:15:35]     --    at System.Xml.XmlReader.MoveToContent()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.MoveToContent()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.SoapServerProtocolHelper.GetRequestElement()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
[Nov 10 15:15:35]     --    at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean&amp; abortProcessing)
[Nov 10 15:15:35]     --    --- End of inner exception stack trace ---</soap:Text></soap:Reason><soap:Detail /></soap:Fault></soap:Body></soap:Envelope>") in new stack
[Nov 10 15:15:35]  <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><soap:Code><soap:Value>soap:Receiver</soap:Value></soap:Code><soap:Reason><soap:Text xml:lang="en">System.Web.Services.Protocols.SoapException: Server was unable to process request. ---&gt; System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
[Nov 10 15:15:35]    at System.Xml.XmlTextReaderImpl.Throw(Exception e)
[Nov 10 15:15:35]    at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
[Nov 10 15:15:35]    at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()
[Nov 10 15:15:35]    at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
[Nov 10 15:15:35]    at System.Xml.XmlTextReaderImpl.Read()
[Nov 10 15:15:35]    at System.Xml.XmlTextReader.Read()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.Read()
[Nov 10 15:15:35]    at System.Xml.XmlReader.MoveToContent()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.MoveToContent()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.SoapServerProtocolHelper.GetRequestElement()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
[Nov 10 15:15:35]    at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
[Nov 10 15:15:35]    at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
[Nov 10 15:15:35]    at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean&amp; abortProcessing)
[Nov 10 15:15:35]    --- End of inner exception stack trace ---</soap:Text></soap:Reason><soap:Detail /></soap:Fault></soap:Body></soap:Envelope>
[Nov 10 15:15:35]     -- Auto fallthrough, channel 'Console/dsp' status is 'UNKNOWN'

I try it with quotes, it gives the same error:

[crm]
exten => _.,1,NoOp()
 same => n,Set(CURLOPT(httpheader)=Content-Type: text/xml)
 same => n,Verbose(0, ${CURL(http://www.dneonline.com/calculator.asmx,'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/"><soapenv:Header/><soapenv:Body><tem:Add><tem:intA>1</tem:intA><tem:intB>2</tem:intB></tem:Add></soapenv:Body></soapenv:Envelope>')})

And why the CURLOPT(httpheader) still giving an error, it should be right at least.

[Nov 10 15:15:35]     -- Executing [1@crm:2] Set("Console/dsp", "CURLOPT(httpheader)=Content-Type: text/xml") in new stack
[2021-11-10 15:15:35] ERROR[30878][C-00000241]: func_curl.c:411 acf_curlopt_write: Unrecognized option: httpheader

I thought I saw a spurious space, but I was wrong.

What version of Asterisk are you using, so I can find the source code line that is complaining, and check whether that version understands httpheader, and what version of the curl library are you using.

In any case, all bets were off when httpheader was rejected, as the data will be sent as a form submission, but not formatted as one.

Also, if using SOAP, it really would help you to understand how SOAP works and how it relates to http.

I am using:

  • Debian GNU/Linux 9.13 (stretch)
  • Asterisk 16.4.0
  • curl 7.52.1

I dont have knowledge about SOUP, I just followed the “Getting Started” to make an API working, so I can integrate Asterisk with the CRM of a client, and to be honest, I don’t want to learn about SOUP now, they will be migrating soon to a REST API.

You need at least Asterisk 16.7.0, but, in general, if seeking support for Asterisk 16, as of today, you should be on the latest sub-version, which is 16.22.0.

Why don’t you use an existing library in PHP or Python to deal with the curl request to SOAP, then using AGI you can convert the results you expect to Asterisk channel variables. Would be easier for you and a lot of examples on the WWW

I think the only thing that would be easy for the OP would be to shell out to the shell command curl, as they aren’t implementing this from the point of view of it being SOAP, but rather from the point of view of executing a specified, but opaque, shell command.

From their point of view, it is an operation that you perform with /usr/bin/curl, not an operation you perform using the SOAP protocol, or even an operation you perform with an HTTP POST.

There are libraries which make the job easier, but if you have working Linux curl command syntax, just using with Asterisk shell() function