pythonpython-3.xdockerbasehttpserver

Unable to run BaseHTTPServer in Docker


I have the following python code, where I have written a simple HTTP server by extending HTTPBaseServer:

class ApiError(Exception):
    def __init__(self, code, msg=None, desc=None):
        self.code = code
        self.msg = msg
        self.desc = desc

    def __str__(self):
        return f"ApiError({self.code}, {self.msg})"


def ApiRoute(path):
    def outer(func):
        if not hasattr(func, "_routes"):
            setattr(func, "_routes", [])
        func._routes += [path]
        return func
    return outer


class ApiServer(HTTPServer):
    def __init__(self, addr, port):
        server_address = (addr, port)
        self.__addr = addr

        class handler_class(ApiHandler):
            pass

        self.handler_class = handler_class

        # routed methods map into handler
        for meth in type(self).__dict__.values():
            if hasattr(meth, "_routes"):
                for route in meth._routes:
                    self.add_route(route, meth)

        super().__init__(server_address, handler_class)

    def add_route(self, path, meth):
        self.handler_class._routes[path] = meth

    def port(self):
        "Get my port"
        sa = self.socket.getsockname()
        return sa[1]

    def address(self):
        "Get my ip address"
        sa = self.socket.getsockname()
        return sa[0]

    def uri(self, path):
        "Make a URI pointing at myself"
        if path[0] == "/":
            path = path[1:]
        return "http://"+self.__addr + ":"+ str(self.port()) + "/" + path

    def shutdown(self):
        super().shutdown()
        self.socket.close()


class ApiHandler(BaseHTTPRequestHandler):
    _routes={}

    def do_GET(self):
        self.do_XXX()

    def do_XXX(self, info={}):
        url=urlparse.urlparse(self.path)

        handler = self._routes.get(url.path)

        if handler:
            response=handler(info)
            self.send_response(200)
            response = json.dumps(response)
            response = bytes(str(response),"utf-8")
            self.send_header("Content-Length", len(response))
            self.end_headers()
            self.wfile.write(response)


class MyServer(ApiServer):
    @ApiRoute("/popup")
    def popup(req):
        return "HERE"


httpd = MyServer('127.0.0.1', 5000)
print("serving on ", httpd.address(), httpd.port())
threading.Thread(target=httpd.serve_forever).start()

The above works perfectly when I do,

python serv.py

on the terminal.

However, I want to run this in a docker container. The Dockerfile looks like this:

FROM python:3.6-alpine3.7

COPY . /serv

WORKDIR /serv

ENTRYPOINT ["python"]
CMD ["serv.py"]

Then I build the Docker Image using:

docker build . -t custom-serv

And run it as container using:

docker run --rm -p 5000:5000 custom-serv

I do get the log message indicating successful start of server, however the curl on the url returns the following:

curl: (56) Recv failure: Connection reset by peer

I realise that, there are many frameworks available to make life simpler, however this is more of an experiment I was doing and it'll be great if someone could point me in the right direction!


Solution

  • Change that line:

    httpd = MyServer('127.0.0.1', 5000)
    

    to:

    httpd = MyServer('0.0.0.0', 5000)
    

    About difference: https://www.howtogeek.com/225487/what-is-the-difference-between-127.0.0.1-and-0.0.0.0/