Anvith
Anvith

Reputation: 507

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!

Upvotes: 1

Views: 1257

Answers (2)

Kevin Kopf
Kevin Kopf

Reputation: 14210

Your server listens on localhost and does not respond to any other connections, but from localhost. If you were to connect to your server from within the container, it would have worked. But to connect from the outside, you need to designate for your server to listen for all connections from all available places.

Therefore, you need to change

httpd = MyServer('127.0.0.1', 5000)

to

httpd = MyServer('0.0.0.0', 5000)

Upvotes: 1

ndpu
ndpu

Reputation: 22561

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/

Upvotes: 4

Related Questions