http.server not serving pages as per example from python.org - python

I'm trying to start a simple dir server on a local network But I am getting this error
Error response
Error code: 501
Message: Unsupported method ('GET').
Error code explanation: HTTPStatus.NOT_IMPLEMENTED - Server does not
support this operation.
This is the example given at https://docs.python.org/3/library/http.server.html If I run it from the command line it works python3 -m http.server. I need to control this server over time so I need to turn it on for a while and turn it off automatically
from http.server import BaseHTTPRequestHandler, HTTPServer
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('0.0.0.0', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()

The answer is in the documentation that you linked to:
The HTTPServer must be given a RequestHandlerClass on
instantiation, of which this module provides three different variants:
class http.server.BaseHTTPRequestHandler(request, client_address, server)
This class is used to handle the HTTP requests that arrive at the
server. By itself, it cannot respond to any actual HTTP requests; it
must be subclassed to handle each request method (e.g. GET or
POST). ...
For your case you should use http.server.SimpleHTTPRequestHandler instead:
class http.server.SimpleHTTPRequestHandler(request, client_address, server)
This class serves files from the current directory and below, directly
mapping the directory structure to HTTP requests.

Related

TCPServer vs HTTPServer

I am writing a simple python3 webserver. I have tried various tutorials and all work pretty well. Nevertheless there is a difference that I don't understand.
In one tutorial, they use HTTPServer as follows:
server = HTTPServer(('', PORT_NUMBER), myHandler)
server.serve_forever()
In another tutorial, they use socketserver.TCPServer as follows:
with socketserver.TCPServer(('', PORT_NUMBER), myHandler) as httpd:
httpd.serve_forever()
What is the difference between both methods? All I need is a simple webserver that is able to receive JSON files through POSTs and aswer with another JSON. In both cases, I would use the same handler:
class myHandler(BaseHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
def do_GET(self):
self._set_headers()
self.wfile.write("{dummy:'dummy'}")
def do_POST(self):
# Doesn't do anything with posted data for this example
self._set_headers()
self.wfile.write("{dummy:'dummy'}")
Does one of the methods suit my needs better? Is there an even better way of writing this server for my needs?
Thanks for your help and time!
TCP is an OSI Layer 4 transport protocol, whereas HTTP is a higher Layer 7 application protocol, built on top of TCP.
In fact, it looks like HTTPServer inherits directly from socketserver.TCPServer, according to the documentation:
One class, HTTPServer, is a socketserver.TCPServer subclass. It
creates and listens at the HTTP socket, dispatching the requests to a
handler.
For example, TCP will take care of establishing a connection (the three-way handshake with SYN, SYN-ACK and ACK) but it doesn't really prescribe much in terms of the structure of the data interaction (request/response). If you use the TCP protocol, you'll generally need to write all your own data processing code.
All I need is a simple webserver that is able to receive JSON files
through POSTs and aswer with another JSON
This suggests the HTTP server is better suited for your need here, as the concept of a POST, and your description of an answer with another JSON is probably going to include a response (with a response body, response headers, and a response status). All of these will be HTTP concepts. As the documentation for http.server. BaseHTTPRequestHandler states, it will include an instance variable called command that is your request method (GET, for instance).
I can't tell exactly how myhandler is assigned in your example code, but looking at some of the other documentation examples, it looks like a http.server.SimpleHTTPRequestHandler:
import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
A frequently pattern of instantiating work for a server appears to be
To instantiate a lower-level protocol server (ie. TCP) and pass in a higher level handler (like http.server.SimpleHTTPRequestHandler), hence the socketserver.TCPServer(("", PORT), myhandler).
To use it within a context manager (with keyword), most likely because you need to tear down / deallocate resources after the server finishes execution.

Add https redirect in Python3 Mockserver

we have this mockserver that is now serving https:// requests, and if we remove the ssl wrapping (ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True), the server only serves http:// requests. Is there any way where we can make this server to support both requests. Our objective is when the server receives an http:// request, it will automatically convert it as https:// and process the request.
Thanks in advance for the support
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
class Mock(BaseHTTPRequestHandler):
-------------------
-------------------
def main():
global hostname, port
hostname = "127.0.0.1"
port = 8000
myServer = HTTPServer((hostname, port), Mock)
myServer.socket = ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True)
myServer.serve_forever()
if __name__ =="__main__":
main()
If the HTTP and HTTPS servers need different functionality, then it makes sense to make them two different instances. Why not make a second HTTPServer that is only HTTP that simply returns a 302 status with the Location header pointing to the HTTPS mock server (but using the same path).

How to redirect HTTP requests on a Python App on Bluemix to HTTPS only?

I have python app on Bluemix and would like to make it accessible over https only. By default I can connect both via http and https. Want to restrict access via https only. So what is the best way to disable http access, or redirect request to https only?
As ralphearle mentioned in his answer, Bluemix proxy server terminates the SSL, so you can look into the X-Forwarded-Proto header to find out if request comes from http or https.
See below a simple example based on the Python starter code from Bluemix. I added the RedirectHandler class to check for the X-Forwarded-Proto header value and redirects the request to https if it not https.
import os
try:
from SimpleHTTPServer import SimpleHTTPRequestHandler as Handler
from SocketServer import TCPServer as Server
except ImportError:
from http.server import SimpleHTTPRequestHandler as Handler
from http.server import HTTPServer as Server
class RedirectHandler(Handler):
def do_HEAD(self):
if ('X-Forwarded-Proto' in self.headers and
self.headers['X-Forwarded-Proto'] != 'https'):
self.send_response(301)
self.send_header("Location", 'https://' + self.headers['Host'] + self.path)
self.end_headers()
def do_GET(self):
self.do_HEAD()
Handler.do_GET(self)
# Read port selected by the cloud for our application
PORT = int(os.getenv('PORT', 8000))
# Change current directory to avoid exposure of control files
os.chdir('static')
httpd = Server(("", PORT), RedirectHandler)
try:
print("Start serving at port %i" % PORT)
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
The Bluemix proxy server terminates the SSL, so that all traffic looks like HTTP to your app. However, the proxy also adds a special HTTP header named $WSSC with a value that can be either http or https. Check this header and, if the value is set to http, then change it to https.
As Adam pointed out in his comment, the IBM forum has further discussion of this approach: https://developer.ibm.com/answers/questions/16016/how-do-i-enforce-ssl-for-my-bluemix-application.html

Hosting CGI and WSGI servers on the same port?

I've got two python servers locally hosting both the dynamic and static pages of a site.
I'm showing the dynamic page in an iframe on a static page, and want to send data to the parent static page using the javascript parent.functionx(data) approach, but I get error:
Uncaught SecurityError: Blocked a frame with origin "http://localhost:8001" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
Can I host these both on the same port, and ideally run a single script to launch both static and dynamic pages?
A WSGI server on port 8001 to host a dynamic python page, accessed by the path /meas
cgiserver.py
import subprocess
from cherrypy import wsgiserver
def application(env, start_response):
if '/meas' in env['PATH_INFO']:
start_response('200 OK', [('Content-Type', 'text/html')])
pathargs = env['PATH_INFO']
args = pathargs.split('/')
participantid = args[2]
studyid = args[3]
repeatid = args[4]
proc = subprocess.Popen(['python3', 'cgi-bin/script-meas.py', participantid, studyid, repeatid], stdout=subprocess.PIPE)
line = proc.stdout.readline()
while line:
yield line
line = proc.stdout.readline()
wsgi_server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 8001), application)
wsgi_server.start()
A CGI server on port 8000 to host static files (of which there are many and complex folder structures)
wsgiserver.py
from http.server import CGIHTTPRequestHandler, HTTPServer
handler = CGIHTTPRequestHandler
handler.cgi_directories = ['/cgi-bin', '/htbin'] # this is the default
server = HTTPServer(('localhost', 8000), handler)
print('Serving on localhost:8000');
server.serve_forever()
Thank you for clarifying your needs.
In your use-case to have multiple processes using the same IP/port, you should put them behind a third process (apache/nginx/lighttpd/...).
Considering your question, I suppose you are in a developpment environment and it might be hard to setup and configure a webserver, so you could use a HTTP proxy (found SimpleHTTPProxy and ProxyHTTPServer and the related post "seriously simple python HTTP proxy?", but check the web for more); overall, this solution would not be easier...
The general idea is to configure the third program to listen to a port (ie: 80) and to serve the request to either of the backends depending of the requested path; ie:
When the process (say apache) listening to a port (let's say port 80) get a request to /meas it will redirect it to the wsgi server (either on a Unix socket or on the port 8081), if it is a request to /cgi-bin or /htbin it will redirect it to the CGI handler (actually Apache have a cgi-bin module and you could remove this backend).

How to very SIMPLY deploy a Twisted Server on OPenshift

I've got the right environment setup, python 2.7.5, Twisted installed and imports work in the Python Shell.
I have a very simple Server instance to display a landing page that Works on local machine fine.
from twisted.web import http
class MyRequestHandler(http.Request):
pages={
'/': '<h1>Geo-Address Server</h1>Twisted Server is Up and Running..',
'/test': '<h1>Test</h1>Test page',
}
def process(self):
print self.path
if self.pages.has_key(self.path):
self.write(self.pages[self.path])
else:
self.setResponseCode(http.NOT_FOUND)
self.write("<h1>Not Found</h1>Sorry, page does not exist")
self.finish()
class MyHttp(http.HTTPChannel):
requestFactory=MyRequestHandler
class MyHttpFactory(http.HTTPFactory):
protocol=MyHttp
if __name__=='__main__':
from twisted.internet import reactor
reactor.listenTCP(8080, MyHttpFactory())
reactor.run()
However, deploying this on the Openshift Server fails to run. If I try to run the script
python script.py &
I get:
reactor.listenTCP(8080, MyHttpFactory()) File
"/var/lib/openshift/5378ea844382ec89da000432/python/virtenv/lib/python2.7/site-packages/twisted/internet/posixbase.py",
line 495, in listenTCP
p.startListening() File "/var/lib/openshift/5378ea844382ec89da000432/python/virtenv/lib/python2.7/site-packages/twisted/internet/tcp.py",
line 979, in startListening
raise CannotListenError(self.interface, self.port, le) twisted.internet.error.CannotListenError: Couldn't listen on any:8080:
[Errno 13] Permission denied.
Reading through SO, most people just say to bind to port 8080(which I have done), but still I get the same error.
As the kb says
Please note: We don't allow arbitrary binding of ports on the
externally accessible IP address.
It is possible to bind to the internal IP with port range: 15000 -
35530. All other ports are reserved for specific processes to avoid conflicts. Since we're binding to the internal IP, you will need to
use port forwarding to access it:
https://openshift.redhat.com/community/blogs/getting-started-with-port-forwarding-on-openshift
Therefor, just find out the $OPENSHIFT_PYTHON_IP
echo $OPENSHIFT_PYTHON_IP
Then add that address to the reactor listener's interface
reactor.listenTCP(15000, MyHttpFactory(), interface='127.X.X.X')
Alternative(and the best) way to do it is by picking the value in the code. That way, if the IP changes dynamically, you still get the current IP
import os
local_hostname =os.getenv("OPENSHIFT_INTERNAL_IP")
..
..
reactor.listenTCP(15000, MyHttpFactory(), interface=local_hostname)
Note: You can only bind to the port range (15000-35530)

Categories