How to configure index file in CGI server? - python

I have just made a server (only for localhost) in Python to execute by CGI to execute and try my Python scripts. This is the code of the file that executes the server:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import BaseHTTPServer
import CGIHTTPServer
import cgitb
cgitb.enable() ## This line enables CGI error reporting
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
handler.cgi_directories = ["/"]
httpd = server(server_address, handler)
httpd.serve_forever()
When I access some script in the server (http://127.0.0.1:8000/index.py) there is no problem, but when I access the server (http://127.0.0.1:8000/) it shows:
Error response
Error code 403.
Message: CGI script is not executable ('//').
Error code explanation: 403 = Request forbidden -- authorization will not help.
It's like if index files aren't set as default file to access when access a folder instead of a specific file...
I would like to be able to access to http://127.0.0.1/index.py when I access http://127.0.0.1/. Thanks for everything.

Python's built-in HTTP server is extremely basic, so it doesn't include such feature. However you can implement it yourself by subclassing CGIHTTPRequestHandler, probably replacing the is_cgi function.

If you use handler.cgi_directories = ["/cgi"], you can place an index.html file in "/". And of course, if you want a cgi script index.py as default, you can use the index.html for forwarding...

I did try to modify is_cgi function and it's working!
def is_cgi(self):
collapsed_path = _url_collapse_path(self.path)
if collapsed_path == '//':
self.path = '/index.py'
collapsed_path = _url_collapse_path(self.path)
dir_sep = collapsed_path.find('/', 1)
head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep + 1:]
if head in self.cgi_directories:
self.cgi_info = head, tail
return True
return False
I put this method into this following class:
class CGIHandlerOverloadIndex(CGIHTTPServer.CGIHTTPRequestHandler):

Related

Python http.server CGI

Need to run python scrips with CGI options from a local python server
At the moment on my Apache I use CGI to get all the get and post requests anyone does to my python scripts to tell them to do things.
For example if I do a get request to 127.0.0.1:8080?filename=yomomma
My python script should print 'yomomma'
#!/usr/bin/python3
import cgi, os
form = cgi.FieldStorage()
fileitem = form['filename']
print(fileitem)
Heres the server im running in python (I have no idea what im doing apparently)
from http.server import *
from urllib import parse
import os
import cgi
class GetHandler(CGIHTTPRequestHandler):
def do_GET(self):
form = cgi.FieldStorage()
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=utf-8')
self.end_headers()
self.wfile.write('<meta http-equiv="refresh" content=1; URL=http://127.0.0.1:8080" /><pre>'.encode('utf-8'))
self.wfile.write(str(os.popen('python my_site.py').read()).encode('utf-8'))
if __name__ == '__main__':
from http.server import HTTPServer
server = HTTPServer(('localhost', 8080), GetHandler)
print('Starting server, use <Ctrl-C> to stop')
server.serve_forever()
Id like to be able to point this at any python file and get that python file to read the CGI parameters
It's unclear what you're asking to do here.
If you just want to run CGI scripts on HTTPServer, that's very simple. CGIHTTPRequestHandler is not meant to be subclassed, which is what you have done, and you don't need to rewrite the do_X functons. It simply returns the output of a CGI script like any other server, if it's under a cgi_directories folder. Read this.
So in the main function, you would have:
server = HTTPServer(('localhost', 8080), CGIHTTPRequestHandler)
Then just call a CGI script normally:
127.0.0.1:8080/cgi-bin/myscript.cgi?filename=yomomma
If you want to utilise http.server to handle requests, you need to use BaseHTTPRequestHandler, not CGIHTTPRequestHandler. Getting form data is then slightly different but easy. Read the section of this under "HTTP POST"

How to send response headers and status from CGI scripts

I am using CGIHTTPServer.py for creating simple CGI server. I want my CGI script to take care of response code if some operation goes wrong . How can I do that?
Code snippet from my CGI script.
if authmxn.authenticate():
stats = Stats()
print "Content-Type: application/json"
print 'Status: 200 OK'
print
print json.dumps(stats.getStats())
else:
print 'Content-Type: application/json'
print 'Status: 403 Forbidden'
print
print json.dumps({'msg': 'request is not authenticated'})
Some of the snippet from request handler,
def run_cgi(self):
'''
rest of code
'''
if not os.path.exists(scriptfile):
self.send_error(404, "No such CGI script (%s)" % `scriptname`)
return
if not os.path.isfile(scriptfile):
self.send_error(403, "CGI script is not a plain file (%s)" %
`scriptname`)
return
ispy = self.is_python(scriptname)
if not ispy:
if not (self.have_fork or self.have_popen2):
self.send_error(403, "CGI script is not a Python script (%s)" %
`scriptname`)
return
if not self.is_executable(scriptfile):
self.send_error(403, "CGI script is not executable (%s)" %
`scriptname`)
return
if not self.have_fork:
# Since we're setting the env in the parent, provide empty
# values to override previously set values
for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
'HTTP_USER_AGENT', 'HTTP_COOKIE'):
env.setdefault(k, "")
self.send_response(200, "Script output follows") # overrides the headers
decoded_query = query.replace('+', ' ')
It is possible to implement support for a Status: code message header that overrides the HTTP status line (first line of HTTP response, e.g. HTTP/1.0 200 OK). This requires:
sub-classing CGIHTTPRequestHandler in order to trick it into writing the CGI script's output into a StringIO object instead of directly to a socket.
Then, once the CGI script is complete, update the HTTP status line with the value provided in the Status: header.
It's a hack, but it's not too bad and no standard library code needs to be patched.
import BaseHTTPServer
import SimpleHTTPServer
from CGIHTTPServer import CGIHTTPRequestHandler
from cStringIO import StringIO
class BufferedCGIHTTPRequestHandler(CGIHTTPRequestHandler):
def setup(self):
"""
Arrange for CGI response to be buffered in a StringIO rather than
sent directly to the client.
"""
CGIHTTPRequestHandler.setup(self)
self.original_wfile = self.wfile
self.wfile = StringIO()
# prevent use of os.dup(self.wfile...) forces use of subprocess instead
self.have_fork = False
def run_cgi(self):
"""
Post-process CGI script response before sending to client.
Override HTTP status line with value of "Status:" header, if set.
"""
CGIHTTPRequestHandler.run_cgi(self)
self.wfile.seek(0)
headers = []
for line in self.wfile:
headers.append(line) # includes new line character
if line.strip() == '': # blank line signals end of headers
body = self.wfile.read()
break
elif line.startswith('Status:'):
# Use status header to override premature HTTP status line.
# Header format is: "Status: code message"
status = line.split(':')[1].strip()
headers[0] = '%s %s' % (self.protocol_version, status)
self.original_wfile.write(''.join(headers))
self.original_wfile.write(body)
def test(HandlerClass = BufferedCGIHTTPRequestHandler,
ServerClass = BaseHTTPServer.HTTPServer):
SimpleHTTPServer.test(HandlerClass, ServerClass)
if __name__ == '__main__':
test()
Needless to say, this is probably not the best way to go and you should look at a non-CGIHTTPServer solution, e.g. a micro-framework such as bottle, a proper web-server (from memory, CGIHTTPServer is not recommended for production use), fastcgi, or WSGI - just to name a few options.
With the standard library HTTP server you cannot do this. From the library documentation:
Note CGI scripts run by the CGIHTTPRequestHandler class cannot execute redirects (HTTP code 302), because code 200 (script output follows) is sent prior to execution of the CGI script. This pre-empts the status code.
This means that the server does not support the Status: <status-code> <reason> header from the script. You correctly identified the portion in the code that shows this support does not exist: The server sends status code 200 before even running the script. There is no way you can change this from within the script.
There are several tickets related to this in the Python bugtracker, some with patches, see e.g., issue13893. So one option for you would be to patch the standard library to add this feature.
However, I would strongly suggest you switch to another technology instead of CGI (or run a real web server).

Enable access control on simple HTTP server

I have the following shell script for a very simple HTTP server:
#!/bin/sh
echo "Serving at http://localhost:3000"
python -m SimpleHTTPServer 3000
I was wondering how I can enable or add a CORS header like Access-Control-Allow-Origin: * to this server?
Unfortunately, the simple HTTP server is really that simple that it does not allow any customization, especially not for the headers it sends. You can however create a simple HTTP server yourself, using most of SimpleHTTPRequestHandler, and just add that desired header.
For that, simply create a file simple-cors-http-server.py (or whatever) and, depending on the Python version you are using, put one of the following codes inside.
Then you can do python simple-cors-http-server.py and it will launch your modified server which will set the CORS header for every response.
With the shebang at the top, make the file executable and put it into your PATH, and you can just run it using simple-cors-http-server.py too.
Python 3 solution
Python 3 uses SimpleHTTPRequestHandler and HTTPServer from the http.server module to run the server:
#!/usr/bin/env python3
from http.server import HTTPServer, SimpleHTTPRequestHandler, test
import sys
class CORSRequestHandler (SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Access-Control-Allow-Origin', '*')
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
test(CORSRequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
Python 2 solution
Python 2 uses SimpleHTTPServer.SimpleHTTPRequestHandler and the BaseHTTPServer module to run the server.
#!/usr/bin/env python2
from SimpleHTTPServer import SimpleHTTPRequestHandler
import BaseHTTPServer
class CORSRequestHandler (SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Access-Control-Allow-Origin', '*')
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
BaseHTTPServer.test(CORSRequestHandler, BaseHTTPServer.HTTPServer)
Python 2 & 3 solution
If you need compatibility for both Python 3 and Python 2, you could use this polyglot script that works in both versions. It first tries to import from the Python 3 locations, and otherwise falls back to Python 2:
#!/usr/bin/env python
try:
# Python 3
from http.server import HTTPServer, SimpleHTTPRequestHandler, test as test_orig
import sys
def test (*args):
test_orig(*args, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
except ImportError: # Python 2
from BaseHTTPServer import HTTPServer, test
from SimpleHTTPServer import SimpleHTTPRequestHandler
class CORSRequestHandler (SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Access-Control-Allow-Origin', '*')
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
test(CORSRequestHandler, HTTPServer)
Try an alternative like http-server
As SimpleHTTPServer is not really the kind of server you deploy to production, I'm assuming here that you don't care that much about which tool you use as long as it does the job of exposing your files at http://localhost:3000 with CORS headers in a simple command line
# install (it requires nodejs/npm)
npm install http-server -g
#run
http-server -p 3000 --cors
Need HTTPS?
If you need https in local you can also try caddy or certbot
Edit 2022: my favorite solution is now serve, used internally by Next.js.
Just run npx serve --cors
Some related tools you might find useful
ngrok: when running ngrok http 3000, it creates an url https://$random.ngrok.com that permits anyone to access your http://localhost:3000 server. It can expose to the world what runs locally on your computer (including local backends/apis)
localtunnel: almost the same as ngrok
now: when running now, it uploads your static assets online and deploy them to https://$random.now.sh. They remain online forever unless you decide otherwise. Deployment is fast (except the first one) thanks to diffing. Now is suitable for production frontend/SPA code deployment It can also deploy Docker and NodeJS apps. It is not really free, but they have a free plan.
I had the same problem and came to this solution:
class Handler(SimpleHTTPRequestHandler):
def send_response(self, *args, **kwargs):
SimpleHTTPRequestHandler.send_response(self, *args, **kwargs)
self.send_header('Access-Control-Allow-Origin', '*')
I simply created a new class inheriting from SimpleHTTPRequestHandler that only changes the send_response method.
try this: https://github.com/zk4/livehttp. support CORS.
python3 -m pip install livehttp
goto your folder, and run livehttp. that`s all.
http://localhost:5000
You'll need to provide your own instances of do_GET() (and do_HEAD() if choose to support HEAD operations). something like this:
class MyHTTPServer(SimpleHTTPServer):
allowed_hosts = (('127.0.0.1', 80),)
def do_GET(self):
if self.client_address not in allowed_hosts:
self.send_response(401, 'request not allowed')
else:
super(MyHTTPServer, self).do_Get()
My working code:
self.send_response(200)
self.send_header( "Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write( bytes(json.dumps( answ ), 'utf-8'))

Localhost fails to display images

I am attempting to display an image on localhost. As a first step, I have created a sever script in python
#!/usr/bin/env python
import BaseHTTPServer
import CGIHTTPServer
import cgitb; cgitb.enable() ## This line enables CGI error reporting
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
handler.cgi_directories = ["/"]
httpd = server(server_address, handler)
httpd.serve_forever()
The image is placed within the same directory where this script is executing.
Subsequently http://localhost:8000/test.jpg is typed in the browser.
The browser fails to render the image with an error:
The image "http://localhost:8000/test.jpg" cannot be displayed because it contains errors.
The server script throws an error like
File "/usr/lib/python2.7/CGIHTTPServer.py", line 253, in run_cgi
os.execve(scriptfile, args, env)
OSError: [Errno 8] Exec format error
I have tried displaying text and the server works fine with lot of examples. Except that it fails to load images. Where am I going wrong?
The problem was solved. I moved the test.jpg into a sub-directory within the
server directory.
Your code is attempting to execute the test.jpg as a cgi script. If you remove the CGIHttpRequestHandler and instead use SimpleHTTPServer.SimpleHTTPRequestHandler you will get your image back. If you need both, then you need to put the image somewhere else.
CGI is used to execute server-side scripts, not to serve static content. It will therefore attempt to execute files that are being served (in this case, attempting to execute an image): os.execve(scriptfile, args, env).
Execution is equivalent to running the file at your shell.
Use SimpleHTTPServer for static content, such as images.
I believe the error is because you have no html page setup.
Your python script although is running a server on your localhost, but you need html/css pages
to actually display pictures and words.

Python CGIHTTPServer Default Directories

I've got the following minimal code for a CGI-handling HTTP server, derived from several examples on the inner-tubes:
#!/usr/bin/env python
import BaseHTTPServer
import CGIHTTPServer
import cgitb;
cgitb.enable() # Error reporting
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
handler.cgi_directories = [""]
httpd = server(server_address, handler)
httpd.serve_forever()
Yet, when I execute the script and try to run a test script in the same directory via CGI using http://localhost:8000/test.py, I see the text of the script rather than the results of the execution.
Permissions are all set correctly, and the test script itself is not the problem (as I can run it fine using python -m CGIHTTPServer, when the script resides in cgi-bin). I suspect the problem has something to do with the default CGI directories.
How can I get the script to execute?
My suspicions were correct. The examples from which this code is derived showed the wrong way to set the default directory to be the same directory in which the server script resides. To set the default directory in this way, use:
handler.cgi_directories = ["/"]
Caution: This opens up potentially huge security holes if you're not behind any kind of a firewall. This is only an instructive example. Use only with extreme care.
The solution doesn't seem to work (at least for me) if the .cgi_directories requires multiple layers of subdirectories ( ['/db/cgi-bin'] for instance). Subclassing the server and changing the is_cgi def seemed to work. Here's what I added/substituted in your script:
from CGIHTTPServer import _url_collapse_path
class MyCGIHTTPServer(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
collapsed_path = _url_collapse_path(self.path)
for path in self.cgi_directories:
if path in collapsed_path:
dir_sep_index = collapsed_path.rfind(path) + len(path)
head, tail = collapsed_path[:dir_sep_index], collapsed_path[dir_sep_index + 1:]
self.cgi_info = head, tail
return True
return False
server = BaseHTTPServer.HTTPServer
handler = MyCGIHTTPServer
Here is how you would make every .py file on the server a cgi file (you probably don't want that for production/a public server ;):
import BaseHTTPServer
import CGIHTTPServer
import cgitb; cgitb.enable()
server = BaseHTTPServer.HTTPServer
# Treat everything as a cgi file, i.e.
# `handler.cgi_directories = ["*"]` but that is not defined, so we need
class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
self.cgi_info = '', self.path[1:]
return True
httpd = server(("", 9006), Handler)
httpd.serve_forever()

Categories

Resources