Can unquoted SoapAction HTTP header cause issues in web service? - web-services

I make a SOAP call to a web service which then shows these errors (info/warning really, I think) in the logs:
INFO: Received WS-I BP non-conformant Unquoted SoapAction HTTP header: myAction
Sep 28, 2016 7:13:54 AM com.sun.xml.ws.transport.http.HttpAdapter fixQuotesAroundSoapAction
Could this warning cause actual issues? Or does the fixQuotesAroundSoapAction method fix the issue when it sees it? I'm wondering if seeing these two lines in the logs should be concerning or can I ignore them?

If you look at com.sun.xml.internal.ws.transport.http.HttpAdapter class, there is a method named "fixQuotesAroundSoapAction" with logic ,
if SOAPAction is not null and it does not start with double quote and end with double quote, it logs the mentioned warning and fix by adding double quote to start and end of the SOAPAction string.
You can ignore or notify the Web Service provider about SOAPAction http header.
Here is the method,
private String fixQuotesAroundSoapAction(String soapAction) {
if(soapAction == null || soapAction.startsWith("\"") && soapAction.endsWith("\"")) {
return soapAction;
} else {
LOGGER.warning("Received WS-I BP non-conformant Unquoted SoapAction HTTP header: " + soapAction);
String fixedSoapAction = soapAction;
if(!soapAction.startsWith("\"")) {
fixedSoapAction = "\"" + soapAction;
}
if(!soapAction.endsWith("\"")) {
fixedSoapAction = fixedSoapAction + "\"";
}
return fixedSoapAction;
}
}

If you uses Log4J, try https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-jul.
Add this to code:
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
Add this to xml configuration file
<Loggers>
...
<Logger name="com.sun.xml.internal.ws.transport.http.HttpAdapter" level="OFF">
</Logger>
...
</Loggers>

It is actually a simple problem where you do not give a quote (") String in your SOAPAction HTTPHeader. Just put String SOAPAction = "\"myAction\"" . This " will add quotes to your HTTPHeader and the irritating warning will gone forever.

Related

Using REST Assured library for testing SOAP webservices

Is there a possibility of using REST Assured library for testing SOAP webservices?
I have a bunch of Test Suites in SOAP UI and I need to check if there is possibility of using REST Assured.
Can anyone suggest if this is possible?
Many thanks for any comments.
Here I show you an example
Headers SOAPAction and Content-Type are mandatory. You need to find wich is the SOAPAction header in your case, sometimes is the next part of the last "/" in the url
import static io.restassured.RestAssured.given;
import io.restassured.RestAssured;
import io.restassured.path.xml.XmlPath;
import io.restassured.response.Response;
XmlPath xmlPath;
Response response;
RestAssured.baseURI = "http://url";
response =
given()
.request().body("xml_text").headers("SOAPAction", "findSoapAction", "Content-Type", "text/xml").
when()
.post("/path").
then()
.assertThat()
.statusCode(200).extract().response();
System.out.println(response.asString());
//next we get the xmlPath of the response
xmlPath = response.xmlPath();
//and get the value of a node in the xml
String nodeValue= xmlPath.get("fatherNode.childNode");
System.out.println(nodeValue);
Elements in the code you should set:
RestAssured.baseURI = "http://url";
Is the url where to make the request
given().request().body("xml_text")
The argument o body() is a string with the xml of the request
headers("SOAPAction", "findSoapAction", "Content-Type", "text/xml")
"findSoapAction" is a string with the value of the SOAPAction header you should guess, and "text/xml" is what you should set as Content-Type header.
xmlPath.get("fatherNode.childNode");
returns the value of the node. Example:
<fatherNode>
<childNode>value of the node</childNode>
</fatherNode>
get("fatherNode.childNode") returns "value of the node"
REST-assured does not have direct support for testing SOAP services, but it is possible by manually setting the SOAPAction and Content-Type headers and doing an HTTP POST etc. Then you can run XPath assertions on the response like you do for normal REST services in REST-assured.
I suggest you also evaluate Karate as it has built-in support for SOAP, and also makes XML manipulation a lot easier.

Can libcurl.net be used to post a new page to confluence?

I'm just getting started using the REST API to create pages.
I'm trying to configure a basic example and I thought of using libcurl.net to do it.
Does anyone see any reason why that wouldn't work?
UPDATE:
Here is what I have so far adapted from the curllib.net "bookpost" example
Option Explicit On
Imports System.IO
Imports System.Net
Imports SeasideResearch.LibCurlNet
Public Class CurlOperations
Public Shared Sub CurlPost()
Try
Dim credUserName As String = "username"
Dim credPassword As String = "password"
Dim response As String = Nothing
Dim outputStdErr As Stream = Nothing
Curl.GlobalInit(CURLinitFlag.CURL_GLOBAL_ALL)
Dim easy As Easy
easy = New Easy
' Set up write delegate
Dim wf As Easy.WriteFunction
wf = New Easy.WriteFunction(AddressOf OnWriteData)
easy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf)
'Authentication
easy.SetOpt(CURLoption.CURLOPT_HTTPAUTH, CURLhttpAuth.CURLAUTH_BASIC)
easy.SetOpt(CURLoption.CURLOPT_USERPWD, credUserName & ":" & credPassword)
'disable ssl peer verification
easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, False)
'Header
easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, "Content-Type: application/json; charset=utf-8")
' Simple post - with a string
easy.SetOpt(CURLoption.CURLOPT_POSTFIELDS, WikiTools.CommREST.WebToCF.PostCurl())
' and the rest of the cURL options
easy.SetOpt(CURLoption.CURLOPT_USERAGENT, ".NET Framework Client")
easy.SetOpt(CURLoption.CURLOPT_URL, "https://domain.com/confluence/rest/api/content/")
easy.SetOpt(CURLoption.CURLOPT_POST, True)
response = easy.Perform().ToString
LoggingAndActivites.WriteLog("Response: " & response, GetFunctionName.GetCallerName, True, True)
Catch ex As Exception
LoggingAndActivites.WriteLog("Exception: " & ex.ToString, GetFunctionName.GetCallerName, True, True)
End Try
Curl.GlobalCleanup()
End Sub
' Called by libcURL.NET when it has data for your program
Public Shared Function OnWriteData(ByVal buf() As Byte, ByVal size As Int32, ByVal nmemb As Int32, ByVal extraData As Object) As Int32
LoggingAndActivites.WriteLog(System.Text.Encoding.UTF8.GetString(buf), GetFunctionName.GetCallerName, True, True)
Return size * nmemb
End Function
End Class
I am getting connected because if I remove the username and password I get a response through the "onWriteData" function as follows:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>401 Unauthorized</title>
</head>
<body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
<hr>
<address>Apache Server at domain.com Port 7080</address>
</body>
</html>
The problem now is that if I correctly log on I'm not getting any response other than the "CURLE_OK" from the "easy.Perform()" function.
It's good because I know it's working to some degree.
According to the libcurl.net docs : http://www.libcurl.org/
libcurl also supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, and user+password authentication.
So I guess you should be able to make a REST API call with it. I have used curl (the linux version) to create and update pages, using something like this:
curl --globoff --insecure --silent -u username:password -X PUT -H 'Content-Type: application/json' --data #body.json confluenceRestAPIURL/pageId
where body.json is a file containing the data to update the page.
I wrote a blog about this here: https://javamemento.blogspot.no/2016/05/jira-confluence-3.html
You can get the code here: https://github.com/somaiah/jira-confluence-graphs
So it does work
Here is what I added/changed to make the code above work
'I added an Slist to store the header items (I only had one)
Dim slistHeaders As New Slist
slistHeaders.Append("Content-Type: application/json")
'Then I added the slist to the HTTPHEADER
easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, slistHeaders)
THINGS TO WATCH FOR:
(1) The URL of course is the number one thing
In my case I was using was https://domain.com/confluence/rest/api/content/ because the Confluence Documentation assumes you would be using the root domain name (as did I)
However, what I didn't know was that the URL I was given to test was already directing me into the "confluence" folder.
So my URI needed to be https://domain.com/rest/api/content/ instead
(2) An indicator that your HTTPHEADER needs to be put into an Slist is this return from the server: 415 Unsupported Media Type
(3) Be sure NOT to use the CURLOPT_HEADER property. If you are seeing this header in your responses you need to make sure it's not used:
HTTP/1.1 500 Internal Server Error
Date: Sun, 22 May 2016 17:50:32 GMT
Server: Apache
Content-Location: 500.en-GB.html
Vary: negotiate,accept-language
TCN: choice
Accept-Ranges: bytes
Content-Length: 7575
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Language: en-gb
Refer to my post here for an explanation of why: CURLOPT_HEADER
(4) Lastly, when you build your app if you receive this error:
An unhandled exception of type 'System.AccessViolationException' occurred in libcurl.NET.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
This is caused by libcurl.net process not being cleaned up.
The "cleanup()" method isn't available in the DLL, despite being lsited in the examples.
So instead use EASY.Dispose() at the end of your procedure and this error will stop. (I kept the "GlobalCleanup()" method in just for good measure)
(5) Ironically I went the way of using CURL because I thought that the Confluence interface might require it.
But it appears now that it doesn't and that you can simply use the "HttpWebRequest" Class in .NET to get the same results.
However, the Jury is still out because the "lightweight" test server I was assigned to crashed and I'm waiting for them to fix it so I can verify this.
Either Way I hope all this helps someone
M

Receiving SOAP notifications from eBay api into ASP variable?

I am trying to receive ebay api transaction notifications into an ASP hosted on a web server. The notifications are sent as SOAP messages and can be sent to a URL with a query string. Notifications must be responded to with HTTP 200 OK. I would like the notification to land inside a variable so that I can parse it and send it on to the next part of the system.
http://developer.ebay.com/DevZone/guides/ebayfeatures/Notifications/Notifications.html#ReceivingPlatformNotifications
In the documentation they mention that this is possible, but the sample they give goes the route of subscribing to an email server. This ASP would not necessarily need to make SOAP requests, just accept SOAP messages from the ebay servers.
I am studying ASP, SOAP, and query strings, but a little guidance would be truly appreciated. Thanks!
This should be pretty straight forward, your Classic ASP page becomes the endpoint for the eBay Notification API (as long as you have configured it to send notifications and what URL to send them to).
You should be able to test this with a simple Classic ASP page
<%
Dim isPost: isPost = (UCase(Request.ServerVariables("REQUEST_METHOD") & "") = "POST")
Dim hasSoapAction
'Is it a HTTP POST?
If isPost Then
'Do we have a SOAPACTION header (check both because
'it can be either HTTP_ or HEADER_ depending on IIS version)?
hasSoapAction = ( _
Len(Request.ServerVariables("HEADER_SOAPACTION") & "") > 0 Or _
Len(Request.ServerVariables("HTTP_SOAPACTION") & "") > 0 _
)
If hasSoapAction Then
'Process the notification here.
'Use Request.BinaryRead to read the SOAP
End If
'Let eBay know we have received and processing the message.
Response.Status = "200 OK"
Else
'Return method not allowed
Response.Status = "405 Method Not Allowed"
End If
Response.End
%>
You might also want to check REMOTE_HOST to make sure that you are only getting sent messages for the expected source (this isn't bulletproof though as the information can be spoofed).
Useful Links
Accessing a request's body (great existing answer that explains how to use Request.BinaryRead() to read the content and convert it to a string which you can then use in a variable or for parsing with XMLDocument.LoadXML()).
How to generate MD5 using VBScript in classic ASP? (If you want to look at a way of verifying the MD5 signature)
This is what i have so far in my notifications.asp. When I try to send it a basic SOAP post through Postman nothing is happening. Does this look like it should work?
I tested this without the If statements checking for SOAP headers, and I posted just regular string data and it works. So the binary to string conversion and output to file is all good. Now I just need to test it with actual ebay api notifications. ;-)
<%
Function BytesToStr(bytes)
Dim Stream
Set Stream = Server.CreateObject("Adodb.Stream")
Stream.Type = 1 'adTypeBinary
Stream.Open
Stream.Write bytes
Stream.Position = 0
Stream.Type = 2 'adTypeText
Stream.Charset = "iso-8859-1"
BytesToStr = Stream.ReadText
Stream.Close
Set Stream = Nothing
End Function
Dim isPost: isPost = (UCase(Request.ServerVariables("REQUEST_METHOD") & "") = "POST")
Dim hasSoapAction
'Is it a HTTP POST?
If isPost Then
'Do we have a SOAPACTION header?
hasSoapAction = (Len(Request.ServerVariables("HEADER_SOAPACTION") & "") > 0)
If hasSoapAction Then
'Process the notification here.
'Use Request.BinaryRead to read the SOAP
If Request.TotalBytes > 0 Then
Dim lngBytesCount, text
lngBytesCount = Request.TotalBytes
text = BytesToStr(Request.BinaryRead(lngBytesCount))
dim fs, tfile
set fs=Server.CreateObject("Scripting.FileSystemObject")
set tfile=fs.CreateTextFile("C:\inetpub\wwwroot\ASPtest\notifications.txt")
tfile.WriteLine(text)
tfile.Close
set tfile=nothing
set fs=nothing
End If
End If
'Let eBay know we have received and processing the message.
Response.Status = "200 OK"
Else
'Return method not allowed
Response.Status = "405 Method Not Allowed"
End If
Response.End
%>

How can i set encoded httplogin and httppass in the http request with CAMEL ROUTE?

I try to send request to a webservice with camel and i dont found how to set httplogin and httppass
with simple java i can do this:
final String s = my_httpLogin+":"+my_httpPwd;
final byte[] authBytes = s.getBytes(StandardCharsets.UTF_8);
final String encoded = Base64.getEncoder().encodeToString(authBytes);
and i set it in the header like this:
con.setRequestProperty("Authorization", "Basic " + encoded);
How can i do this with Camel ?
this is the blueprint.xml:
<setHeader headerName="CamelHttpQuery">
<constant>
my_url_partams
</constant>
</setHeader>
<setHeader headerName="Authorization">
<constant>my_encoded_string</constant>
</setHeader>
<setBody>
<method ref="configsms" method="convertparam(Exchange)"></method>
</setBody>
<to uri="http://my_link"/>
in the convertparam(Exchange) i just add HTTP_METHOD, CONTENT_TYPE..like this
public void convertparam(Exchange exchange) throws Exception {
exchange.getIn().setHeader(Exchange.HTTP_METHOD,"POST" );
exchange.getIn().setHeader(Exchange.CONTENT_TYPE,"application/x-www-form-urlencoded" );
}
You have to set the Authorization information in exchange header. If you have encoded string already in the exchange, you set as follow in spring/bluprint DSL just before your http request
<setHeader headerName="Authorization">
<!-- here you can use simple language to get the encoded string from body -->
</setHeader>
if you want to do some java logic to get the appropriate username and password, just have a camel processor before http request. In Camel processor you can get username and password, encode and set in the header as below,
exchange.getIn().setHeader("Authorization", "Basic " + encoded);

Adding headers to HTTP request using cfhttp's addParam method

I'm new to Coldfusion and can't understand why I'm having so much trouble adding a couple of headers to an http request that I'm making with cfhttp. I'm trying to write the request using cfscript, and from everything I've read I should be able to simply do:
httpService.addParam(type="header", name="Content-Type", value="application/json");
or
httpService.addParam(type="header", name="Authorization", value=local.authPasscode);
But the outgoing request doesn't seem to contain these headers. I can tell it's not working because:
I'm getting errors back in my response
I'm doing a writeDump(GetHttpRequestData()), which shows all the details of the request (and this output does not show the Authorization header at all and shows the Content-Type header as:
multipart/form-data; boundary=----WebKitFormBoundaryZs9TyOQV02N3fQop
I also seem to have difficulty adding the body of the message in a similar way so I'm thinking I'm doing something wrong with addParam(). I'm attempting that like so:
httpService.addParam(type="body", value="hello");
Again, the response I get back has a bunch of missing field errors and the output of writeDump(GetHttpRequestData()) shows a missing body.
Am I supposed to be able to use addParam() in this way?
Full code:
httpService = new http();
httpService.setMethod("POST");
httpService.setUrl(application.config.beanstream.postURL);
local.authPasscode = "Passcode " & ToBase64(application.config.beanstream.merchantid & ":" & application.config.beanstream.APIPasscode, "utf-8");
// Set headers.
httpService.addParam(type="header", name="Authorization", value=local.authPasscode);
httpService.addParam(type="header", name="Content-Type", value="application/json");
// Construct the message body.
local.body = {
"test": "hello"
};
httpService.addParam(type="body", value=SerializeJSON(local.body));
writeDump(GetHttpRequestData());
local.result = httpService.send().getPrefix();
Update:
Okay, so I changed my postURL to that of another local page and on that page I'm using GetHttpRequestData() to log the method, protocol, headers and content all out to a file. Here's what I get:
POST
HTTP/1.1
{host={www.mysite.com},user-agent={ColdFusion},connection={close},content-length={16},authorization={Passcode Mjc1ODMwMDAwOjIwMTI5NGUwMjI2MzQxMzlBZjBFMDE2RmViRjg0RDAz},content-type={application/json}}
{"test":"hello"}
So it looks to me like I am indeed correctly sending a POST to the correct URL and the headers are getting set correctly as well. My content even seems like it's there so this is starting to feel like I'm sending invalid body data to the API. Unless anybody can think of anything else to check I'm going to investigate that further.
instead of
local.body = {
"test": "hello"
};
try
local.body = {};
local.body["test"] = "hello";
then you can
SerializeJSON(local.body)