BaseHTTPServer 库中如何获取客户端请求 server 端的域名是什么?

2019-09-03 22:23:49 +08:00
 vast0906

在 py2 下找到了

self.path
self.client_address
self.command
self.requestline
self.request_version

请问我该如何在代码中获取 client 访问 server 的域名是什么? 比如我访问的是 v2ex.com/test。怎么能获取到 v2ex.com 这段域名?

历史原因暂时只能用 py2

2157 次点击
所在节点    Python
14 条回复
vast0906
2019-09-03 22:34:36 +08:00
目前在群里根据大佬提示通过查找 headers 找到的一种解法,还有其他的办法吗?
lcdtyph
2019-09-03 22:48:06 +08:00
@vast0906
只有 http 的话没有了,就算是查找 header 也不是一定能拿到域名的,因为 http 协议没有规定必须专递那几个 header,也没有要求传输域名。
但是如果有 tls 的话可以在 tls 这一层拿到访问的域名,因为 client hello 的 sni 部分携带了请求的域名。
ochatokori
2019-09-03 23:23:38 +08:00
http 头里面的 host 就是啊
also24
2019-09-03 23:41:28 +08:00
@lcdtyph #2
HTTP 1.1 的标准里,header 里的 host 字段是必须的

参见 RFC: https://tools.ietf.org/html/rfc2616#page-129

A client MUST include a Host header field in all HTTP/1.1 request messages .
lcdtyph
2019-09-03 23:46:41 +08:00
@also24
SimpleHttpServer 只实现了 HTTP 1.0 吧,至少我本地这个版本是这样的,如果发送 1.1 的请求,得到的回复第一行也是
HTTP/1.0 200 OK
binux
2019-09-04 00:03:40 +08:00
@lcdtyph #5 即使 SimpleHTTPServer 只实现了 1.0 这和发送方有什么关系呢?发送的时候他又不知道对方是 1.0 的。
also24
2019-09-04 00:07:33 +08:00
@lcdtyph #5
根据我从下面的代码看到的情况,是可以支持 1.1 的(确切来说,是支持 1.1 的 keepalive 特性),
不过需要手动设置一下 protocol_version ( # Set this to HTTP/1.1 to enable automatic keepalive )
https://github.com/python/cpython/blob/2.7/Lib/BaseHTTPServer.py#L270
https://github.com/python/cpython/blob/2.7/Lib/BaseHTTPServer.py#L513

另外,host 字段是否存在,其实主要取决于你发起请求的客户端使用的版本啊,你 1.1 的请求已经自带 host header 了。


当然,假如你一定要考虑 1.0 的请求,其实也有一定可能能从 URI 里读取到的:
https://tools.ietf.org/html/rfc1945#page-24
翻到这里发现一件事,那就是 1.0 时,如果这样读不到,其它的 Web Server 也读不到,不用纠结。
lcdtyph
2019-09-04 00:14:47 +08:00
@binux @also24
但是一个恶意的客户端是可以发送非正常请求的,这种情况下 simple server 就是拿不到 header 而且不会在 handle 请求之前返回错误。
如果只使用 SimpleHTTPServer 那么就是没有“一定”能拿到域名的方法,一定要拿到域名我只能想到套一层 tls。要么就在 handle 请求的时候拿不到域名就手动返回错误好了。
binux
2019-09-04 00:22:10 +08:00
@lcdtyph 你考虑非正常请求干嘛?读不到 host 就返回 400 就好了呗。
如果我在前面放个 cdn,你不发送 host,难不成还要靠猜吗?
also24
2019-09-04 00:25:47 +08:00
@lcdtyph #8
一个 “恶意” 的客户端,如何做到:
让 Web Server 拿到正确的域名(从而正常分发 vhost )
却又
不让 Web Application 拿到正确的域名(从而无法取出 host 字段)

请注意:
只传递正确的 SNI,但传递错误的 host 字段,不但会欺骗 Web Application,
事实上在 Web Server 也会被欺骗,此时就已经跑到错误的 vhost 去了。

另外如果采取 SNI 的方式,你的 SSL 负载要靠 Web Server 来卸载 还是靠 SimpleHTTPServer 来卸载呢?
靠 Web Server 来卸载的话,哪儿来的 SNI 信息呢?靠 SimpleHTTPServer 卸载的话,好像不支持吧?
以及,我甚至不需要考虑恶意的客户端,对于 IE6 IE7 这种不支持 SNI 的浏览器,是不是又变成了拿不到了呢?

其实并不是:SimpleHTTPServer 没有“一定”能拿到域名的方法。
而应当是:任何的 Web Server 都没有 没有“一定”能拿到域名的方法。


在当前的环境下,我认为 HTTP 1.1 可以当做事实上的最优选择。
lcdtyph
2019-09-04 00:46:22 +08:00
@also24
只支持 http1.1 的 server 的确是可以的。我本来的想法是,本应在下层就可以 abort 掉的错误就不要扔给上层处理,比如这种场景下只使用 SimpleHTTPServer 是做不到这点的,是我莫名预设了条件,my bad。
---
只传递正确的 SNI,但是不传递 host 字段,这种错误在 web server 反代时直接就返回 400 了,不需要 http 的上层来处理这种问题;另外 ssl 卸载靠 web server,反代的时候直接把 sni 信息塞进一个 header 转发给 simplehttpserver 就行了,因为 webserver 是可信的。我以为 lz 就是不想在 web app 里 handle 这种事情才来问的,所以以为需求是如果进入了 web app 就一定要能拿到域名。
also24
2019-09-04 01:11:19 +08:00
@lcdtyph #11
首先我觉得你预设了楼主认为 “一定拿到真实 host ” 的需求,我没有看到楼主要求这个。
楼主问这个的原因,我猜测是因为 BaseHTTPServer 没有 self.host,让楼主有些懵如何自己获取。

相对应的,例如 flask 有 flask.Request.host,django 有 django.http.HttpRequest.get_host,都可以直接取。
https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request.host
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.get_host

那么他们都是怎么取的呢?我比较懒,只翻了 flask,层层追溯可以找到这里:
https://github.com/pallets/werkzeug/blob/master/src/werkzeug/wsgi.py#L145

而 django,其实上面贴的文档里已经讲了,相比直接去 host 字段,还考虑了反代的场景。


至于:ssl 卸载靠 web server,反代的时候直接把 sni 信息塞进一个 header 转发给 simplehttpserver 就行了。
不妨调研一下,想要实现这样的效果,应该怎样去具体实现,怕不是要写个 nginx 插件才行?


最后,关于 “ webserver 是可信的”,不妨看一下著名的 WebServer Nginx 是怎样处理请求的:
http://nginx.org/en/docs/http/request_processing.html

我想,你应该能看到大量的 ' “ Host ” header field ' 这样的字眼,我就不多说了。
conn4575
2019-09-04 03:58:10 +08:00
根据 http 协议标准,这个只能从 header 里获取,虽然绝大部分情况都能拿的到,但是协议本身没有强制要求,例如如果用 curl 请求的话就没有
vast0906
2019-09-04 12:27:26 +08:00
@also24

@binux

@lcdtyph

@conn4575

睡过头了。谢谢各位的回复。提问的时候大部分原因是 @also24 猜的那样因为 BaseHTTPServer 没有 self.host,让我有些懵如何自己获取。后来发现 headers 里面去获取,觉得不够优雅。所以就提问了。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/597684

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX