I have the following code for a simple BaseHTTPServer based server.
class myHandler(BaseHTTPRequestHandler):
#Handler for the GET requests
def do_GET(self):
# Parse the query_str
query_str = self.path.strip().lower()
if query_str.startswith("/download?"):
query_str = query_str[10:]
opts = urlparse.parse_qs(query_str)
# Send the html message and download file
self.protocol_version = 'HTTP/1.1'
self.send_response(200)
self.send_header("Content-type", 'text/html')
self.send_header("Content-length", 1)
self.end_headers()
self.wfile.write("0")
# Some code to do some processing
# ...
# -----------
self.wfile.write("1")
I was expecting the HTML page to show "1", but it shows "0". How can I update the response through keep alive?
I believe you are setting self.protocol_version to 'HTTP/1.1' too late. You are doing it in your do_GET() method, at which point your request handler has already been instantiated, and the server has already inspected that instance's protocol_version property.
Better to set it on the class:
class myHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
Not sure what you are trying to accomplish, but if you want the 1 to be sent, you need to set your content-length to 2 or remove it entirely. The 1 is not going to overwrite the 0, so you will see 01.
https://docs.python.org/2/library/basehttpserver.html
protocol_version
This specifies the HTTP protocol version used in responses. If set to 'HTTP/1.1', the server will permit HTTP persistent connections; however, your server must then include an accurate Content-Length header (using send_header()) in all of its responses to clients. For backwards compatibility, the setting defaults to 'HTTP/1.0'.
I faced same question. I tried set protocol_version in my do_METHOD() function which doesn't work.
My code look like this.
def _handle(self, method):
self.protocol_version = "HTTP/1.1"
# some code here
def do_GET(self):
self._handle("GET")
I used ss and tcpdump to detect network and finally find server will reset connection after send response although it use http/1.1.
So I try set protocol_version just under my class which inherited from standard library class and it works. Because of cost of time, I don't dive into source code. Hope it works for others.
Related
I wrote a HTTP server using python, but I do not know how to get the HTTP body. what should I do to get the HTTP body?
here is my code:
from http.server import HTTPServer,BaseHTTPRequestHandler
class MyHTTPHandler(BaseHTTPRequestHandler):
def do_GET(self):
print("connect from ",self.client_address)
print(self.headers)
length = self.headers['Content-Length']
print(length)
addr = ('',21567)
server = HTTPServer(addr,MyHTTPHandler)
server.serve_forever()
Having a request body in a GET request is not a good practice, as its discussed here: HTTP GET with request body
Instead, you can change your method to POST and check there the BaseHTTPRequestHandler documentation: https://docs.python.org/2/library/basehttpserver.html
Especially this part:
rfile
Contains an input stream, positioned at the start of the optional input data.
Is there anyway to get suds returning the SoapRequest (in XML) without sending it?
The idea is that the upper levels of my program can call my API with an additional boolean argument (simulation).
If simulation == false then process the other params and send the request via suds
If simulation == false then process the other params, create the XML using suds (or any other way) and return it to the caller without sending it to the host.
I already implemented a MessagePlugin follwing https://fedorahosted.org/suds/wiki/Documentation#MessagePlugin, but I am not able to get the XML, stop the request and send back the XML to the caller...
Regards
suds uses a "transport" class called HttpAuthenticated by default. That is where the actual send occurs. So theoretically you could try subclassing that:
from suds.client import Client
from suds.transport import Reply
from suds.transport.https import HttpAuthenticated
class HttpAuthenticatedWithSimulation(HttpAuthenticated):
def send(self, request):
is_simulation = request.headers.pop('simulation', False)
if is_simulation:
# don't actually send the SOAP request, just return its XML
return Reply(200, request.headers.dict, request.msg)
return HttpAuthenticated(request)
...
sim_transport = HttpAuthenticatedWithSimulation()
client = Client(url, transport=sim_transport,
headers={'simulation': is_simulation})
It's a little hacky. (For example, this relies on HTTP headers to pass the boolean simulation option down to the transport level.) But I hope this illustrates the idea.
The solution that I implemented is:
class CustomTransportClass(HttpTransport):
def __init__(self, *args, **kwargs):
HttpTransport.__init__(self, *args, **kwargs)
self.opener = MutualSSLHandler() # I use a special opener to enable a mutual SSL authentication
def send(self,request):
print "===================== 1-* request is going ===================="
is_simulation = request.headers['simulation']
if is_simulation == "true":
# don't actually send the SOAP request, just return its XML
print "This is a simulation :"
print request.message
return Reply(200, request.headers, request.message )
return HttpTransport.send(self,request)
sim_transport = CustomTransportClass()
client = Client(url, transport=sim_transport,
headers={'simulation': is_simulation})
Thanks for your help,
The default Python xmlrpc.client.Transport (can be used with xmlrpc.client.ServerProxy) does not retain cookies, which are sometimes needed for cookie based logins.
For example, the following proxy, when used with the TapaTalk API (for which the login method uses cookies for authentication), will give a permission error when trying to modify posts.
proxy = xmlrpc.client.ServerProxy(URL, xmlrpc.client.Transport())
There are some solutions for Python 2 on the net, but they aren't compatible with Python 3.
How can I use a Transport that retains cookies?
Existing answer from GermainZ works only for HTTP. After a lot of time fighting with it, there is HTTPS adaptation. Note the context option which is crucial.
class CookiesTransport(xmlrpc.client.SafeTransport):
"""A SafeTransport (HTTPS) subclass that retains cookies over its lifetime."""
# Note context option - it's required for success
def __init__(self, context=None):
super().__init__(context=context)
self._cookies = []
def send_headers(self, connection, headers):
if self._cookies:
connection.putheader("Cookie", "; ".join(self._cookies))
super().send_headers(connection, headers)
def parse_response(self, response):
# This check is required if in some responses we receive no cookies at all
if response.msg.get_all("Set-Cookie"):
for header in response.msg.get_all("Set-Cookie"):
cookie = header.split(";", 1)[0]
self._cookies.append(cookie)
return super().parse_response(response)
The reason for it is that ServerProxy doesn't respect context option related to transport, if transport is specified, so we need to use it directly in Transport constructor.
Usage:
import xmlrpc.client
import ssl
transport = CookiesTransport(context=ssl._create_unverified_context())
# Note the closing slash in address as well, very important
server = xmlrpc.client.ServerProxy("https://<api_link>/", transport=transport)
# do stuff with server
server.myApiFunc({'param1': 'x', 'param2': 'y'})
This is a simple Transport subclass that will retain all cookies:
class CookiesTransport(xmlrpc.client.Transport):
"""A Transport subclass that retains cookies over its lifetime."""
def __init__(self):
super().__init__()
self._cookies = []
def send_headers(self, connection, headers):
if self._cookies:
connection.putheader("Cookie", "; ".join(self._cookies))
super().send_headers(connection, headers)
def parse_response(self, response):
for header in response.msg.get_all("Set-Cookie"):
cookie = header.split(";", 1)[0]
self._cookies.append(cookie)
return super().parse_response(response)
Usage:
proxy = xmlrpc.client.ServerProxy(URL, CookiesTransport())
Since xmlrpc.client in Python 3 has better suited hooks for this, it's much simpler than an equivalent Python 2 version.
There is some way to remove HTTP Header 'Server: TwistedWeb/13.1.0' from responses from a Twisted based web application?
You can rewrite any header by calling the request.setHeader method.
class RootPage(Resource):
def getChild(self, name, request):
request.setHeader('server', 'MyVeryOwnServer/1.0')
return OtherResource(name)
The change applies to any resources on your site; you could put it in your Site class. You want that 404 or 500 errors would also return correct header; so you should set it as earlier as possible but not before it is set by twisted itself (in order to overwrite it):
#!/usr/bin/env python
import sys
from twisted.web import server, resource
from twisted.internet import reactor
from twisted.python import log
class Site(server.Site):
def getResourceFor(self, request):
request.setHeader('server', 'Server/1.9E377')
return server.Site.getResourceFor(self, request)
# example from http://twistedmatrix.com/
class HelloResource(resource.Resource):
isLeaf = True
numberRequests = 0
def render_GET(self, request):
self.numberRequests += 1
request.setHeader("content-type", "text/plain")
return "I am request #" + str(self.numberRequests) + "\n"
log.startLogging(sys.stderr)
reactor.listenTCP(8080, Site(HelloResource()))
reactor.run()
Default Server http header is specified in t.w.server.version.
I know this is an old question, but if you would like to remove the server http header. I am talking about the
request.setHeader('Server', 'SomeServer')
This is set by Twisted Web automagically if you don't specify a value. You can remove it by using the inner Headers class. For example,
request.responseHeaders.removeHeader('Server')
This will remove the Server Http Header.
I need a library that can only format a correct HTTP response (without creating a web server because I already have this one).
I have to pass a body of an http response and a content type to this library.
The following function does not work properly if I try to send AJAX - jQuery does not find any response. However if I type a corresponding URL in the URL string of browser then a page is displayed.
def response( data ):
return "HTTP/1.0 200 OK\r\nContent-Type:application/json\r\nConnection:close\r\n\r\n{0}\r\n".format( data )
Details. The data variable is string of json. I also use the SocketServer class, call self.request.sendall( result_response ) in child's handle() function.
# the 'request handler' class
class StateRequestHandler( SocketServer.BaseRequestHandler ):
def handle( self ):
...
self.request.sendall( response( some_json ) )
# the configured server class
class StateServer( SocketServer.ThreadingMixIn, SocketServer.TCPServer ):
pass
Solved. The reason was in cross-domain requests.
Try the following:
Although the HTTP 1.0 and 1.1 spec say that a white space is optional between the header name and header value, the specs do say "a single SP[ace] is preferred".
Ensure that you do not have any Cross Domain issues so ensure your HTML page is served from exactly the domain,scheme and port as your JSON response (Unless you've correctly configured CORS.
Use Chrome Dev Tools / Firebug / Fiddler to see what request they're making and check the Javascript console.
Use Wireshark to see exactly what's on the wire.