RT,我这边原先使用的 socket 去获取的完整响应包,包括 header 的那种,这样方便复现数据包。 现在有其他需求需要直接使用 http 库,比如 requests 或者 urllib2 之类的,不知道能否获取像 socket 这样的效果。 这样方便和以前的数据处理机制做兼容,因为想要改的话,代码量是比较多的。 原先的 socket 发送完整 http 包,可获得类似下面的返回( header 和 body 是在一块的),我昨天试了好几个库好像都不能模拟获取 socket 这种返回方式:
HTTP/1.1 200
Server: ADSSERVER/45863
Date: Fri, 13 Oct 2017 06:48:23 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
Location: https://sec.douban.com/b?r=https%3A%2F%2Fwww.douban.com%2Fsubject%2F27076001%2F
Strict-Transport-Security: max-age=15552000;
Set-Cookie: __ads_session=uY8l3pLW/AjCKJ8Y4wA=; domain=.douban.com; path=/
X-Powered-By-ADS: uni-jnads-1-02
<html>
#这里是完整的 html resp 内容
...
</html>
求大腿指教,有没有办法,让我获取像 socket 的完整 html 响应包?
1
jxxz 2019-10-09 10:06:18 +08:00
要不自己拼接下
用 rsp.status_code、rsp.headers 和 rsp.text 拼接下 不过 headers 获取到的是 dict,需要再处理下 |
2
arrow8899 2019-10-09 10:13:22 +08:00
r = requests.get('https://www.example.com', stream=True)
r.raw.read(10) # 可以读到原始的数据流,但是需要自己处理各种编码问题 |
3
akmonde OP |
4
littlespider89 2019-10-09 10:22:54 +08:00
那不叫完整的 html 响应包,那是 http
|
5
jxxz 2019-10-09 11:00:48 +08:00 1
@akmonde 看了下源码,requests 用 urllib3 的 response 封装的响应,具体位置在 requests --> adapters.py --> HTTPAdapter --> build_response()这个方法里,你可以看下能不能改源码
``` def build_response(self, req, resp): """Builds a :class:`Response <requests.Response>` object from a urllib3 response. This should not be called from user code, and is only exposed for use when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>` :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response. :param resp: The urllib3 response object. :rtype: requests.Response ``` |
6
akmonde OP |
7
wwqgtxx 2019-10-09 11:56:24 +08:00 via iPhone
自己给系统库的 socket.socket 打一个 monkey patch ?拦截所有的 recv 函数,记录下来再转发给上层
|
8
est 2019-10-09 14:08:54 +08:00
@jxxz requests 调用的 urllib3.HTTPResponse 最为返回,实例化里读取头用的是 httplib.HTTPMessage 这个类。继承自 rfc822.Message,所以最简单的办法是:
a=requests.get('http://jd.com') print a.raw._fp.msg |
9
est 2019-10-09 14:15:37 +08:00 1
然后需要自己处理一下 HTTPResponse.begin 里的 version, status, reason 部分。
if version == 'HTTP/1.0': self.version = 10 elif version.startswith('HTTP/1.'): self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 elif version == 'HTTP/0.9': self.version = 9 |
10
akmonde OP |
11
zhuangzhuang1988 2019-10-09 17:30:12 +08:00
```python
import socket from contextlib import contextmanager import requests from socket import SocketIO as _SocketIO @contextmanager def hook_socket_context(): buffer = [] class SocketIO(_SocketIO): def readinto(self, b): print('hooking') res = super().readinto(b) if res > 0: buffer.append(bytes(b[:res])) return res socket.SocketIO = SocketIO yield buffer socket.SocketIO = _SocketIO with hook_socket_context() as buf: requests.get('https://v2ex.com/t/607316#reply10') print(b''.join(buf)) print('with out hook') requests.get('https://v2ex.com/t/607316#reply10') print('done!') ``` python3.7 测试无问题 |
12
zhuangzhuang1988 2019-10-09 17:36:48 +08:00 1
https://bitbucket.org/snippets/supermouse/onRK7x
高亮问题, 放在 bitbucket 上了 |
13
cz5424 2019-10-09 18:41:28 +08:00
|
14
wwqgtxx 2019-10-09 20:14:10 +08:00 1
@akmonde 无论是 http.client 还是 requests 底层都是调用系统 socket 库的(除非你用 libcurl 这种 C 库的封装),所以你可以按照 @zhuangzhuang1988 给出的方法给 socket 库打 monkey patch,从而获得原始 socket 流的所有数据包
|
15
akmonde OP |
16
ClericPy 2019-10-09 23:23:10 +08:00
楼上好多 talk is cheap 的大佬....... 学到老活到老
python 的最大乐趣就是管你是不是猴子, 只要不是 built-ins 我就给你屁股上镶补丁 requests 库已经被魔改过好几种变种了 |
18
ClericPy 2019-10-09 23:51:25 +08:00
@wwqgtxx 它是它 我是我...
我只用过替换的方式 import builtins 然后改, 直接在已有对象上打当场报错... |
19
walleL 2019-10-10 01:20:40 +08:00
我比较好奇为什么需要改用 http client 库,然后还要获取原始的 tcp 数据
楼主方便详细说下需求吗?可能从原始需求出发更好解决呢 |
20
akmonde OP @walleL 是这样的,主要我这边是给 QA 做数据复现,然后 QA 那边做的分析规则,原本是分析流量得到的完整的 http 响应包的。
我这边的话,是需要原始 url 等分散的数据,重新去获取,所以拿到的不是完整的 http 响应包,而是分组的数据,做规则分析的时候,可能会形成差异,所以我这边需要做兼容。 |
21
walleL 2019-10-10 15:05:56 +08:00
@akmonde
> 原本是分析流量得到的完整的 http 响应包的 你分析的流量来自哪里呢?是如何抓取的呢? 我的理解是之前你直接分析流量得到 http 包,现在需要自已发起 http 请求来生成流量。 这样的话是不是可以分析流量的程序不变,抓取方案也不变,只是用另一个程序来发起 http 请求产生流量就行了? |
22
akmonde OP @walleL 分析的流量原来是用 burpsuite 抓的包,做代理获取的完整响应包,是需要用另一个程序或者脚本发起请求,但发起二次请求已经不是抓取了,是直接获取 http 返回包,所以获取的内容形式跟原来用代理工具抓到的包是不太一样的。
|