2
votes

Currently I'm trying to setup a simple web service. For this I'm using the phyton3 http.server class. The whole thing runs in a Docker container (called simple_webservice; exposing port 8010).

When running the container without traefik I'm able to access the website by calling http://localhost:8010.

The code I used for implementing my webserver can be found here:

import http.server
import socketserver

PORT = 8010
Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

The docker-file is also simple (proxy is used for downloading via apt-get):

FROM ubuntu:latest

ENV http_proxy 'http://proxy:port'
ENV https_proxy 'http://proxy:port'
ENV no_proxy 'company.net'

RUN apt-get update &&   apt-get install -y \
                        build-essential \
                        python3 
RUN mkdir /www
COPY ext/ /www
ADD ./entrypoint.sh /entrypoint.sh
ADD server.py /www/server.py
RUN chmod +x /entrypoint.sh

ENTRYPOINT [ "./entrypoint.sh"]

Here is my docker-compose.yml file for using my container with the traefik reverse proxy:

version: '3'

services:
  simple_webservice:
    build: .
    image: "simple_webservice"
    expose:
      - 8010
    networks:
      - internal_network
      - default
    labels:
      - traefik.passHostHeader=true
      - traefik.docker.network=internal_network
      - traefik.enable=true
      - traefik.backend=simple_webservice
      - traefik.frontend.rule=PathPrefix:/webservice
      - traefik.port=8010

networks:
  internal_network:
    external: true

Now when accessing the very same web service by calling http://localhost/webservice it always returns:
Error response
Error code: 404
Message: File not found.
Error code explanation: HTTPStatus.NOT_FOUND - Nothing matches the given URI

So I assume my service is still reachable and traefik behaves correctly but my webserver can't handle the Path Prefix /webservice? How can I fix this?


Edit: Answered by posting a workaround.

2
I think people will need your dockerfile for the image simple_webservice. - ndclt
also try to map 8010 port in the docker-compose and expose it in dockerfile. - TreantBG
Try to move the files that you want to serve to a folder called webservice, relative to the Python file that runs the web server. - bellackn
@ndclt: I have added my Dockerfile as well. I thought it would be to trivial to add. My entrypoint script is doing the following: cd /www && python3 server.py - agentsmith
the traefik.port should be another port I think why you use the same port as your app ? - LinPy

2 Answers

0
votes

You can use the stripprefix middleware in Traefik to remove the /webservice path if it's only exposed in the front end. Be aware that this may break absolute and some relative redirects and links in your application (i.e. /foo will no longer be pointing to the endpoint served by your frontend).

# Strip prefix /foobar and /fiibar
labels:
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar, /fiibar"

You can use the X-Forwarded-Prefix header to prefix those paths as necessary.

The X-Forwarded-Prefix header can be queried to build such URLs dynamically.

However, my recommendation is to usually mount your WSGI application (as you're usually ending up with in Python) in multiple endpoints, so that it'll have a proper relationship with its own URL as necessary. That way most things will Just Work both for the prefix and without (when accessing directly). How you do that will depend on the framework used.

0
votes

Workaround

I found a workaround for this problem. However, this is not a general solution. (As my Dockerfile tells, I'm operating in /www/).

Background

When (re)loading a webpage the do_GET() method is invoked and this method calls send_head()

Now, there is a variable path in send_head(); When working with traefik the variable path (assigned in line 66) path = self.translate_path(self.path) stores in my case /www/webservice which is not a valid path (only /www/ would be valid.)

The (bad) solution

So, my workaround is just deleting the content after /www/... by simply adding the line path = path.replace('/webservice', ''). Now my path is valid again.

Note that this is just a workaround. I guess there is a more practicable way of doing that without altering SimpleHTTPServer.py source files but I haven't found a way of doing this. The method send_head(self) looks like this:

def send_head(self):
  path = self.translate_path(self.path)
  path = path.replace('/webservice', '')
  f = None
  if os.path.isdir(path):
    parts = urllib.parse.urlsplit(self.path)
    [...]

I would also appreciate a more general solution for my problem.