在反向代理 (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)
[...]
我也希望能为我的问题提供更通用的解决方案。
目前我正在尝试设置一个简单的网络服务。为此,我使用了 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)
[...]
我也希望能为我的问题提供更通用的解决方案。