Python client for free SOAP service using socket giving error? - python

This is the code that I'm trying to use:
import os
import socket
import httplib
packet='''
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetCitiesByCountry xmlns="http://www.webserviceX.NET">
<CountryName>India</CountryName>
</GetCitiesByCountry>
</soap12:Body>
</soap12:Envelope>'''
lent=len(packet)
msg="""
POST "www.webservicex.net/globalweather.asmx" HTTP/1.1
Host: www.webservicex.net
Content-Type: application/soap+xml; charset=utf-8
Content-Length: {length}
SOAPAction:"http://www.webserviceX.NET/GetCitiesByCountry"
Connection: Keep-Alive
{xml}""".format(length=lent,xml=packet)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect( ("www.webservicex.net",80) )
if bytes != str: # Testing if is python2 or python3
msg = bytes(msg, 'utf-8')
client.send(msg)
client.settimeout(10)
print(client.type)
res=client.recv(4096)
print(res)
#res = res.replace("<","<") -- only for stack overflow post
#res = res.replace(">",">") -- only for stack overflow post
The output is:
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 24 Aug 2016 11:40:02 GMT
Connection: close
Content-Length: 324
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">;
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request - Invalid URL</h2>
<hr><p>HTTP Error 400. The request URL is invalid.</p>
</BODY></HTML>
Any ideas?

You ought to use suds to consume SOAP web service.
EDIT: Example
import suds
import suds.transport
from suds.transport.http import HttpAuthenticated
class MyException(Exception):
pass
service_url = "http://my/service/url"
GET the available services:
try:
transport = HttpAuthenticated(username='elmer', password='fudd')
wsdl = suds.client.Client(service_url, faults=False, transport=transport)
except IOError as exc:
fmt = "Can initialize my service: {reason}"
raise MyException(fmt.format(reason=exc))
except suds.transport.TransportError as exc:
fmt = "HTTP error -- Is it a bad URL: {service_url}? {reason}"
raise MyException(fmt.format(service_url=service_url, raison=exc))
Run a given service (here, its name is RunScript):
# add required options in headers
http_headers = {'sessionID': 1}
wsdl.set_options(soapheaders=http_headers)
params = {"scriptLanguage": "javascript", "scriptFile": "...",
"scriptArgs": [{"name": "...", "value": "..."}]}
try:
exit_code, response = wsdl.service.RunScript([params])
# ...
except suds.MethodNotFound as reason:
raise MyException("No method found: {reason}".format(reason=reason))
except suds.WebFault as reason:
raise MyException("Error running the script: {reason}".format(reason=reason))
except Exception as reason:
err_msg = "{0}".format(reason)
if err_msg == "timed out":
raise MyException("Timeout: {reason}".format(reason=reason))
raise
Of course, it is not required to use an error manager here. But I give an example. The last with the "timed out" is a kind of hack I used to detect a time out in my application.

Related

Posting file to Flask with cURL returns 400 error

I want to post a file using cURL to a Flask view. However, I get a 400 error in response. Why does the command below fail?
#app.route('/deploy/partial', methods=['POST'])
def postfile():
try:
file = request.files['file']
filename = secure_filename(file.filename)
file.save(os.path.join('/home/test/test.txt', filename))
return 'OK', 200
except:
print("Unexpected error:", sys.exc_info()[0])
raise
curl -X POST -H "Content-Type: multipart/form-data" -F "file=#FILEPATH" http://127.0.0.1:25252/deploy/partial
Unexpected error: <class 'werkzeug.exceptions.HTTPException.wrap.<locals>.newcls'>
127.0.0.1 - - [12/Oct/2016 22:57:43] "POST /deploy/partial HTTP/1.1" 400
HTTP/1.1 100 Continue
HTTP/1.0 400 BAD REQUEST
Content-Type: text/html
Content-Length: 192
Server: Werkzeug/0.11.11 Python/3.4.3
Date: Wed, 12 Oct 2016 19:31:07 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
I got the
Unexpected error: .newcls'>
after I accidentally renamed request.form['name'] to request.form['ame']
(note the wrong 'ame') while my form field is
name = StringField(...)
I changed request.form['ame'] to request.form['name'] and the error disappeared, i.e. flask works
Not sure if this is your case too.

httplib - http not accepting content length

Problem
When I switched Macbooks, all of the sudden I am getting an HTTP 411: Length Required (I wasn't getting this using a different Mac) trying to use a POST request with httplib. I cannot seem to find a work around for this.
Code Portion 1: from a supporting class; retrieves data and other things,
class Data(object):
def __init__(self, value):
self.company_id = None
self.host = settings.CONSUMER_URL
self.body = None
self.headers = {"clienttype": "Cloud-web", "Content-Type": "application/json", "ErrorLogging": value}
def login(self):
'''Login and store auth token'''
path = "/Security/Login"
body = self.get_login_info()
status_code, resp = self.submit_request("POST", path, json.dumps(body))
self.info = json.loads(resp)
company_id = self.get_company_id(self.info)
self.set_token(self.info["token"])
return company_id
def submit_request(self, method, path, body=None, header=None):
'''Submit requests for API tests'''
conn = httplib.HTTPSConnection(self.host)
conn.set_debuglevel(1)
conn.request(method, path, body, self.headers)
resp = conn.getresponse()
return resp.status, resp.read()
Code Portion 2: my unittests,
# logging in
cls.api = data.Data(False) # initializing the data class from Code Portion 1
cls.company_id = cls.api.login()
...
# POST Client/Register
def test_client_null_body(self):
'''Null body object - 501'''
status, resp = self.api.submit_request('POST', '/Client/register')
if status != 500:
log.log_warning('POST /Client/register: %s, %s' % (str(status), str(resp)))
self.assertEqual(status, 500)
Code Portion 3: example of the data I send from a settings file,
API_ACCOUNT = {
"userName": "account#account.com",
"password": "password",
"companyId": 107
}
From Logging
WARNING:root: POST /Client/register: 411, <!DOCTYPE HTML PUBLIC "-//W3C//DTD
HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Length Required</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Length Required</h2>
<hr><p>HTTP Error 411. The request must be chunked or have a content length.</p>
</BODY></HTML>
Additional Info: I was using a 2008 Macbook Pro without issue. Switched to a 2013 Macbook Pro and this keeps occurring.
I took a look at this post:
Python httplib and POST and it seems that at the time httplib did not automatically generate the content length.
Now https://docs.python.org/2/library/httplib.html:
If one is not provided in headers, a Content-Length header is added automatically for all methods if the length of the body can be determined, either from the length of the str representation, or from the reported size of the file on disk.
when using conn.set_debuglevel(1) we see that httplib is sending a header
reply: 'HTTP/1.1 411 Length Required\r\n'
header: Content-Type: text/html; charset=us-ascii
header: Server: Microsoft-HTTPAPI/2.0
header: Date: Thu, 26 May 2016 17:08:46 GMT
header: Connection: close
header: Content-Length: 344
Edit
Unittest Failure:
======================================================================
FAIL: test_client_null_body (__main__.NegApi)
Null body object - 501
----------------------------------------------------------------------
Traceback (most recent call last):
File "API_neg.py", line 52, in test_client_null_body
self.assertEqual(status, 500)
AssertionError: 411 != 500
.send: 'POST /Client/register HTTP/1.1\r\nHost: my.host\r\nAccept-Encoding: identity\r\nAuthorizationToken: uhkGGpJ4aQxm8BKOCH5dt3bMcwsHGCHs1p+OJvtf9mHKa/8pTEnKyYeJr+boBr8oUuvWvZLr1Fd+Og2xJP3xVw==\r\nErrorLogging: False\r\nContent-Type: application/json\r\nclienttype: Cloud-web\r\n\r\n'
reply: 'HTTP/1.1 411 Length Required\r\n'
header: Content-Type: text/html; charset=us-ascii
header: Server: Microsoft-HTTPAPI/2.0
header: Date: Thu, 26 May 2016 17:08:27 GMT
header: Connection: close
header: Content-Length: 344
Any ideas as to why this was working on a previous Mac and is currently not working here? It's the same code, same operating systems. Let me know if I can provide any more information.
Edit 2
The issue seemed to be with OSX 10.10.4, after upgrading to 10.10.5 all is well. I still would like to get some insight on why I was having this issue.
The only change from 10.10.4 to 10.10.5, that seems close, would have been the python update from 2.7.6 to 2.7.10 which includes this bug fix: http://bugs.python.org/issue22417

'urllib2.urlopen' adding Host header

I'm using Observium to pull Nginx stats on localhost however it returns '405 Not Allowed':
# curl -I localhost/nginx_status
HTTP/1.1 405 Not Allowed
Server: nginx
Date: Wed, 19 Jun 2013 22:12:37 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 166
Connection: keep-alive
Keep-Alive: timeout=5
# curl -I -H "Host: example.com" localhost/nginx_status
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 19 Jun 2013 22:12:43 GMT
Content-Type: text/plain
Connection: keep-alive
Keep-Alive: timeout=5
Could you please advise how to add Host header with 'urllib2.urlopen' in Python (Python 2.6.6
):
Current script:
#!/usr/bin/env python
import urllib2
import re
data = urllib2.urlopen('http://localhost/nginx_status').read()
params = {}
for line in data.split("\n"):
smallstat = re.match(r"\s?Reading:\s(.*)\sWriting:\s(.*)\sWaiting:\s(.*)$", line)
req = re.match(r"\s+(\d+)\s+(\d+)\s+(\d+)", line)
if smallstat:
params["Reading"] = smallstat.group(1)
params["Writing"] = smallstat.group(2)
params["Waiting"] = smallstat.group(3)
elif req:
params["Requests"] = req.group(3)
else:
pass
dataorder = [
"Active",
"Reading",
"Writing",
"Waiting",
"Requests"
]
print "<<<nginx>>>\n";
for param in dataorder:
if param == "Active":
Active = int(params["Reading"]) + int(params["Writing"]) + int(params["Waiting"])
print Active
else:
print params[param]
You might want to check out the urllib2 missing manual for more information, but basically you create a dictionary of your header labels and values and pass it to the urllib2.Request method. A (slightly) modified version of the code from the linked manual:
from urllib import urlencode
from urllib2 import Request urlopen
# Define values that we'll pass to our urllib and urllib2 methods
url = 'http://www.something.com/blah'
user_host = 'example.com'
values = {'name' : 'Engineero', # dict of keys and values for our POST data
'location' : 'Interwebs',
'language' : 'Python' }
headers = { 'Host' : user_host } # dict of keys and values for our header
# Set up our request, execute, and read
data = urlencode(values) # encode for sending URL request
req = Request(url, data, headers) # make POST request to url with data and headers
response = urlopen(req) # get the response from the server
the_page = response.read() # read the response from the server
# Do other stuff with the response

Problems to authenticate correctly with pysimplesoap

I'm trying to use pysimplesoap to communicate with the Websitepanel SOAP-API.
The WebsitePanel API Introduction says:
For interacting with WebsitePanel API you should use Basic Authentication. WebsitePanel recognizes “Authorization” header with the user credentials provided in the following format: username:password
My first try was the following:
client = SoapClient(wsdl=endpoint_url, trace=True)
client['Authorization'] = "%s:%s" % (username, password)
which returns a 401 "Unauthorized".
Second try was:
client = SoapClient(wsdl=endpoint_url, trace=True)
client['wsse:Security'] = {
'wsse:UsernameToken': {
'wsse:Username': username,
'wsse:Password': password,
}
}
which works as expected but returns the following:
status: 500
content-length: 924
x-aspnet-version: 4.0.30319
x-powered-by: ASP.NET
server: Microsoft-IIS/7.5
cache-control: private
date: Tue, 12 Feb 2013 14:23:56 GMT
content-type: text/xml; charset=utf-8
And
pysimplesoap.client.SoapFault: q0:Security: SOAP response should be signed.
Why does client['Authorization'] not work and what is meant by the Response should be signed error message?
Thanks in advance.
I figured it out: To authenticate correctly with pysimplesoap you have to call
client = SoapClient(wsdl=u, trace=True,
http_headers={'Authorization': 'Basic %s' % encoded})
with encodedbeeing the base64-encoded string username:password

Authenticated HTTP POST with XML payload using Python urllib2

I'm trying to send a POST message with a purely XML payload (I think) using urllib2 in IronPython. However, everytime I send it, it returns Error code 400 (Bad Request).
I'm actually trying to mimick a Boxee remove queue item call for which the actual data packets looks like this (from WireShark):
POST /action/add HTTP/1.1
User-Agent: curl/7.16.3 (Windows build 7600; en-US; beta) boxee/0.9.21.11487
Host: app.boxee.tv
Accept: */*
Accept-Encoding: deflate, gzip
Cookie: boxee_ping_version=9; X-Mapping-oompknoc=76D730BC9E858725098BF13AEFE32EB5; boxee_app=e01e36e85d368d4112fe4d1b6587b1fd
Connection: keep-alive
Content-Type: text/xml
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: en-us,en;q=0.5
Keep-Alive: 300
Connection: keep-alive
Content-Length: 53
<message type="dequeue" referral="3102296"></message>
I'm using the following python code to send the POST:
def PostProtectedPage(theurl, username, password, postdata):
req = urllib2.Request(theurl, data=postdata)
req.add_header('Content-Type', 'text/xml')
try:
handle = urllib2.urlopen(req)
except IOError, e: # here we are assuming we fail
pass
else: # If we don't fail then the page isn't protected
print "This page isn't protected by authentication."
sys.exit(1)
if not hasattr(e, 'code') or e.code != 401: # we got an error - but not a 401 error
print "This page isn't protected by authentication."
print 'But we failed for another reason.'
sys.exit(1)
authline = e.headers.get('www-authenticate', '') # this gets the www-authenticat line from the headers - which has the authentication scheme and realm in it
if not authline:
print 'A 401 error without an authentication response header - very weird.'
sys.exit(1)
authobj = re.compile(r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"](\w+)['"]''', re.IGNORECASE) # this regular expression is used to extract scheme and realm
matchobj = authobj.match(authline)
if not matchobj: # if the authline isn't matched by the regular expression then something is wrong
print 'The authentication line is badly formed.'
sys.exit(1)
scheme = matchobj.group(1)
realm = matchobj.group(2)
if scheme.lower() != 'basic':
print 'This example only works with BASIC authentication.'
sys.exit(1)
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
authheader = "Basic %s" % base64string
req.add_header("Authorization", authheader)
try:
handle = urllib2.urlopen(req)
except IOError, e: # here we shouldn't fail if the username/password is right
print "It looks like the username or password is wrong."
print e
sys.exit(1)
thepage = handle.read()
return thepage
However, whenever I run this, it returns Error 400 (Bad Request)
I know the authentication is correct because I use it elsewhere to fetch the queue (and I can't imagine it's not used, otherwise how would it now which account to apply the change to?)
Looking at the network capture, could I simply be missing adding some headers to the request? Probably something simple, but I just don't know enough about python or HTTP requests to know what's what.
Edit: BTW, I'm calling the code as follows (it's actually dynamic, but this is the basic idea):
PostProtectedPage("http://app.boxee.tv/action/add", "user", "pass", "<message type=\"dequeue\" referral=\"3102296\"></message>")
This worked fine for me:
curl -v -A 'curl/7.16.3 (Windows build 7600; en-US; beta) boxee/0.9.21.11487' \
-H 'Content-Type: text/xml' -u "USER:PASS" \
--data '<message type="dequeue" referral="12573293"></message>' \
'http://app.boxee.tv/action/add'
But I get 400 Bad Request if I try to remove a referral ID that isn't currently in the queue. If you're using the same referral ID as you detected from Wireshark, that's very likely what's happening for you, too. Use
wget -nv -m -nd --user=USER --password=PASS http://app.boxee.tv/api/get_queue
to make sure what you're trying to remove is actually in the queue.

Categories