Enable access control on simple HTTP server
Asked Answered
P

6

192

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?

Prebo answered 22/2, 2014 at 16:0 Comment(1)
Lolol I love how the simple server, which probably the very point of using it is to serve things up because you can't use local files, requires one to configure this.Weinstein
N
282

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)
Nephron answered 22/2, 2014 at 16:28 Comment(16)
@Nephron The server responds with 501 Unsupported method ('OPTIONS'). I'm running OS X 10.10.1 with Python 2.7.6. Any suggestions? HTTP/1.0 501 Unsupported method ('OPTIONS') Server: SimpleHTTP/0.6 Python/2.7.6 Date: Wed, 21 Jan 2015 23:16:10 GMT Content-Type: text/html Connection: close Access-Control-Allow-Origin: *Drove
@Drove The SimpleHTTPRequestHandler doesn’t support the OPTIONS HTTP method. You could add it if you want (read the Python manual about HTTP servers); or you could just not try to access the server like that.Nephron
this looks great, but is this still working? I just tried and my Chrome keeps complaining in the console.Kym
@RobertoFranceschini You might be running into preflighted requests which require the OPTIONS method to be implemented properly. As for simple requests, the solution of sending just the Access-Control-Allow-Origin header should still work fine.Nephron
@Nephron Are you saying that the particular webpage I am trying to access is harder to get to? if that is the case, can you name a page where the above approach would work? (so that I can test it)Kym
@RobertoFranceschini I’m saying that, depending on what is consuming the files (and how), they might be running non-simple requests. I don’t know why you would want to do that since the simple HTTP server will not run any server-side logic, but that could be the cause. I’m not sure you fully understand what this question is about though, so you might have a different problem instead. You could open a question about that, since this is not really the place to debug other issues.Nephron
For some reason this server is EXTREMELY slow for me. It takes about 10 seconds to send any data.Oilstone
@Oilstone That might be a general behavior with the simple HTTP server though. I had varying results regarding performance before. But for simply running a server for a moment, I still consider it the quickest solution.Nephron
How would you change the default port? I don't know python and I feel like it should be simple... but changing the '8000' to, say, '8001', and running it without arguments doesn't result in the listener binding to port 8001Hardesty
@Hardesty You would add the port as an argument to the call: python simple-cors-http-server.py 8001. The 8000 that’s in the code is just the default value when using Python 3.Nephron
Yep I got that bit, but what I'd like to do is run the script without any arguments and alter the default port that it'll listen on in the code. The reason being I keep forgetting the port numbers!Hardesty
@Hardesty If you’re using Python 3, you can just pass the correct port number to the test/test_orig function. In Python 2, you are out of luck since the test function directly parses the command line arguments. You will have to run the server manually then instead of relying on the built-in test function.Nephron
@Nephron I know this one's a bit old, but it's still helpful. However, I'm still getting some errors with the Access-Control-Allow-Headers. If you have a moment, can you check out the question here? Thanks!Disembogue
Why is test needed here?Pavis
@Friedrich--СлаваУкраїні The original test sets up the actual server and command line handling. It’s the default entry point when you run the http.server module directly. I am just reusing this exact functionality here to avoid having to roll my own implementation.Nephron
@Nephron Thx for the link. test is not explained in the docs. It seems is can be also understood as HTTPServer(('', 8000), CORSRequestHandler).serve_forever().Pavis
N
174

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.

Northcutt answered 20/2, 2015 at 15:43 Comment(10)
This solution was much better for me than the python one, which had intermittent lag.Oilstone
I'm a simple man. I see a solution that requires installing npm on a machine that is only known to have python, I downvote.Critique
@ParthianShot: you might want to learn to use the best tool for the job.Dumanian
I do know how to use the best tool for the job. Which is why I've never installed npm on a production machine, nor do I ever intend to. I especially don't install a new language or package manager every time I want to solve a simple problem like CORS headers on an HTTP server. But either way... OP is comfortable in python, asked for a python solution. Therefore, by definition, the "right tool for the job" is python.Critique
Ah, sorry, wrong link- the leftpad thing wasn't much. There was another, more serious issue with JS infrastructure a few months ago that I distinctly remember... Some idiot pushing a prerelease branch as production with no checks in between... Meh. Point being, some people dislike JS, some people dislike node, there are good reasons for both of those positions, answer the question as asked or don't answer at all and leave a comment instead.Critique
@ParthianShot Many developers already have node/npm installed.and the question title is generic enough to drive a large audience of users that clearly don't care about python or SimpleHTTPServer, which is confirmed by upvotes. It's not because it's not helpful to you that it is for everybody. There are good reasons to not like both Node and Python as well. Things like leftpad/bad publish/bad git usage seems totally unrelated to me.Northcutt
@ParthianShot it's unfortunate you can't consider a Node tool the good tool for the job. Fatal mistakes can be made in any programming language, including Python. Maybe you should consider the tool as an implementation detail to solve your problem instead of digging deeper into its implementation.Northcutt
Adding an additional language and framework incurs technical debt and increases the attack surface of an environment. "Fatal mistakes can be made in any programming language" True, but JS makes that way easier than most other languages. And every language has gotchas; the fewer languages you use, the less likely it is that some developer who is unfamiliar with one of the languages makes a mistake that wouldn't be a mistake in another language.Critique
This is like adopting a child every time you need help around the house; it creates more problems than it solves down the road.Critique
Remember to use http://localhost instead of just localhost to access this server too.Brothel
O
8

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.

Oxalate answered 20/12, 2018 at 9:58 Comment(0)
B
4

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

Bagman answered 29/4, 2021 at 17:14 Comment(0)
P
2

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()
Pediform answered 22/2, 2014 at 16:10 Comment(5)
Thanks for your answer, but I have no Python knowledge what so ever, I am just using the shell script mentioned above as a simple http server for my Emberjs apps. Only when collided with the access control problem, I researched to find that I need to enable it in this simple http server. So after some research I added (enable 'CrossOrigin', origins => '*';) but not-surprisingly it didn't work. If you can please point me to any Python simple http server shell script that include the access control feature that will be highly appreciatedPrebo
On a minor note, I am not trying to be lazy here really but start learning python just to add this feature to the simpleHTTP server doesn't sound logical at this point so I was hoping it will be easy to add OR hopefully I can find an alternative / ready made Python script that can do the job so that I can continue with my dev workPrebo
The SimpleHTTPServer has no options to support access controls. Either you'll need to roll your own code -- or switch to another web server that supports access controls. Think about lighttpd.netPediform
For anyone taking this approach, if you want it to support "non simple" cors requests (ones that require "preflight" permission) you will want to implement a do_OPTIONS method which returns a 204 response with the following headers: 'Access-Control-Allow-Origin', 'Access-Control-Allow-Methods' and 'Access-Control-Allow-Headers'.Closefitting
As @RogerHeathcote noted the do_OPTIONS method would work but be carefull as it is enough to have a missing allowed header to have your request ditched... like when you make a partial request and don't allow for range header (took me a moment to find that out) self.send_header("Access-Control-Allow-Headers", "range, accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with")Aircrew
T
1

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'))
Tannie answered 30/3, 2022 at 10:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.