Why does SimpleHTTPServer redirect to ?querystring/ when I request ?querystring?
Asked Answered
S

3

13

I like to use Python's SimpleHTTPServer for local development of all kinds of web applications which require loading resources via Ajax calls etc.

When I use query strings in my URLs, the server always redirects to the same URL with a slash appended.

For example /folder/?id=1 redirects to /folder/?id=1/ using a HTTP 301 response.

I simply start the server using python -m SimpleHTTPServer.

Any idea how I could get rid of the redirecting behaviour? This is Python 2.7.2.

Sadomasochism answered 18/10, 2012 at 11:26 Comment(1)
SimpleHTTPServer serves files only. Use something else to deal with parameters in requests.Kayak
S
3

Okay. With the help of Morten I've come up with this, which seems to be all I need: Simply ignoring the query strings if they are there and serving the static files.

import SimpleHTTPServer
import SocketServer

PORT = 8000


class CustomHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

    def __init__(self, req, client_addr, server):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, req, client_addr, server)

    def do_GET(self):
        # cut off a query string
        if '?' in self.path:
            self.path = self.path.split('?')[0]
        SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)


class MyTCPServer(SocketServer.ThreadingTCPServer):
    allow_reuse_address = True

if __name__ == '__main__':
    httpd = MyTCPServer(('localhost', PORT), CustomHandler)
    httpd.allow_reuse_address = True
    print "Serving at port", PORT
    httpd.serve_forever()
Sadomasochism answered 19/10, 2012 at 15:22 Comment(1)
This does not seem to respect the url params. It still appends the '/' after the query params.Gayegayel
G
5

The right way to do this, to ensure that the query parameters remain as they should, is to make sure you do a request to the filename directly instead of letting SimpleHTTPServer redirect to your index.html

For example http://localhost:8000/?param1=1 does a redirect (301) and changes the url to http://localhost:8000/?param=1/ which messes with the query parameter.

However http://localhost:8000/index.html?param1=1 (making the index file explicit) loads correctly.

So just not letting SimpleHTTPServer do a url redirection solves the problem.

Gayegayel answered 11/9, 2014 at 11:43 Comment(0)
S
3

Okay. With the help of Morten I've come up with this, which seems to be all I need: Simply ignoring the query strings if they are there and serving the static files.

import SimpleHTTPServer
import SocketServer

PORT = 8000


class CustomHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

    def __init__(self, req, client_addr, server):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, req, client_addr, server)

    def do_GET(self):
        # cut off a query string
        if '?' in self.path:
            self.path = self.path.split('?')[0]
        SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)


class MyTCPServer(SocketServer.ThreadingTCPServer):
    allow_reuse_address = True

if __name__ == '__main__':
    httpd = MyTCPServer(('localhost', PORT), CustomHandler)
    httpd.allow_reuse_address = True
    print "Serving at port", PORT
    httpd.serve_forever()
Sadomasochism answered 19/10, 2012 at 15:22 Comment(1)
This does not seem to respect the url params. It still appends the '/' after the query params.Gayegayel
G
2

I'm not sure how the redirect is generated... I've tried implementing a very basic SimpleHTTPServer, and I don't get any redirects when using query string params.

Just do something like self.path.split("/") and process the path before handling the request? This code does what you want I think:

import SocketServer
import SimpleHTTPServer
import os


class CustomHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def folder(self):
        fid = self.uri[-1].split("?id=")[-1].rstrip()
        return "FOLDER ID: %s" % fid

    def get_static_content(self):
        # set default root to cwd
        root = os.getcwd()
        # look up routes and set root directory accordingly
        for pattern, rootdir in ROUTES:
            if path.startswith(pattern):
                # found match!
                path = path[len(pattern):]  # consume path up to pattern len
                root = rootdir
                break
        # normalize path and prepend root directory
        path = path.split('?',1)[0]
        path = path.split('#',1)[0]
        path = posixpath.normpath(urllib.unquote(path))
        words = path.split('/')
        words = filter(None, words)
        path = root
        for word in words:
            drive, word = os.path.splitdrive(word)
            head, word = os.path.split(word)
            if word in (os.curdir, os.pardir):
                continue
            path = os.path.join(path, word)
            return path

    def do_GET(self):
        path = self.path
        self.uri = path.split("/")[1:]

        actions = {
            "folder": self.folder,
        }
        resource = self.uri[0]
        if not resource:
            return self.get_static_content()
        action = actions.get(resource)
        if action:
            print "action from looking up '%s' is:" % resource, action
            return self.wfile.write(action())
        SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)


class MyTCPServer(SocketServer.ThreadingTCPServer):
    allow_reuse_address = True

httpd = MyTCPServer(('localhost', 8080), CustomHandler)
httpd.allow_reuse_address = True
print "serving at port", 8080
httpd.serve_forever()

Try it out:

HTTP GET /folder/?id=500x -> "FOLDER ID: 500x"

EDIT:

Okay so if you haven't used the SimpleHTTPServer-stuff before, you basically implement the base request handler, implement do_GET(), do_PUT(), do_POST() etc.

What I usually do then is parse the request string (using re), pattern match and see if I can find a request-handler and if not, handle the request as a request for static content if possible.

You say you want to serve static content if at all possible, then you should flip this pattern matching around, and FIRST see if the request matches the file-store and if not, then match against handlers :)

Graehl answered 18/10, 2012 at 12:19 Comment(6)
First, thanks! It shows that it's possible to prevent the redirect. However it seems as if this handler doesn't serve static files as I'm used to. What I basically want is to be able to serve any path that's available as static file, completely ignoring the query string part.Sadomasochism
Just add the logic in the do_GET() method, I've made an update that does just thatGraehl
have you considered using web.py ? it's really lightweight and would be a fraction of the code above.Mongolism
I wouldn't have considered web.py for this purpose, since as far as I know it serves static files only from /static/ by default. Since all I want to do is to serve static files, I didn't take the trouble.Sadomasochism
You can usually tweak such small factors as where the static content resides, so I wouldn't rule out a framework just by that :)Graehl
Morten, I tried your updated example. Thanks again! It seems if the example doesn't accomplish what I really want to do: ignore query strings, have no redirects when they are there and simply serve static files. It fails with the root URL (/) and still produces redirects with arbitrary query strings. I added another answer to the question showing the code that actually does what I needed.Sadomasochism

© 2022 - 2024 — McMap. All rights reserved.