在反向代理 (traefik) 后面使用 phyton 的 http.server 时获取“404”

Getting '404' when using phyton's http.server behind the reverse proxy (traefik)

目前我正在尝试设置一个简单的网络服务。为此,我使用了 phyton3 http.server class。整个东西在 Docker 容器中运行(称为 simple_webservice;暴露端口 8010)。

当 运行 没有 traefik 的容器时,我可以通过调用 http://localhost:8010.

来访问该网站

可以找到我用于实现网络服务器的代码 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()

docker-文件也很简单(通过apt-get下载使用proxy):

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"]

这是我的 docker-compose.yml 文件,用于将我的容器与 traefik 反向代理一起使用:

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

现在,当通过调用 http://localhost/webservice 访问同一个 Web 服务时,它总是 returns:
错误响应
错误代码:404
消息:找不到文件。
错误代码解释:HTTPStatus.NOT_FOUND - 没有匹配给定的 URI

所以我假设我的服务仍然可以访问并且 traefik 行为正确但是我的网络服务器无法处理路径前缀 /webservice? 我该如何解决这个问题?


编辑: 通过发布解决方法来回答。

如果 /webservice 路径只暴露在前端,您可以使用 the stripprefix middleware in Traefik 删除它。请注意,这可能会破坏应用程序中的绝对和某些相对重定向和链接(即 /foo 将不再指向您的前端服务的端点)。

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

您可以根据需要使用 X-Forwarded-Prefix header 作为这些路径的前缀。

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

但是,我的建议是通常在多个端点安装您的 WSGI 应用程序(正如您通常在 Python 中结束的那样),以便它与自己的 URL 根据需要。这样一来,大多数事情都会对前缀和没有前缀(直接访问时)都起作用。你如何做到这一点将取决于所使用的框架。

解决方法

我找到了解决此问题的方法。但是,这不是通用的解决方案。 (正如我的 Dockerfile 所说,我在 /www/ 中操作)。

背景

当(重新)加载网页时 do_GET() method is invoked and this method calls send_head()

现在,send_head()中有一个可变路径;使用 traefik 时,变量 path (在第 66 行分配) path = self.translate_path(self.path) 在我的案例中存储 /www/webservice 这不是有效路径(只有 /www/ 是有效的。)

(坏的)解决方案

所以,我的解决方法是删除 /www/... 之后的内容,只需添加以下行 path = path.replace('/webservice', '')。现在我的路径再次有效。

请注意,这只是一种解决方法。我想有一种更可行的方法可以在不改变 SimpleHTTPServer.py 源文件的情况下做到这一点,但我还没有找到这样做的方法。 send_head(self) 方法如下所示:

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)
    [...]

我也希望能为我的问题提供更通用的解决方案。