【asyncio】queue的使用常规方式

本文展示了如何利用Python的asyncio库和queue模块创建并发工作进程。每个worker协程在Semaphore限制下从队列中获取并执行任务,异常时会将任务放回队列。主函数向队列中添加100个任务,并启动5个worker进行处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

every blog every motto: You can do more than you think.
https://blog.csdn.net/weixin_39190382?type=blog

0. 前言

asyncio 和queue使用模版

1. 正文

import asyncio

async def worker(queue):
    async with asyncio.Semaphore(5):  # 最多同时运行5个任务
        while not queue.empty():
            task = await queue.get()
            try:
                # 执行任务
                await do_task(task)
            except Exception as e:
                print(f"Task {task} failed: {e}")
                # 将任务重新加入队列
                await queue.put(task)

async def main():
    queue = asyncio.Queue(maxsize=100)  # 队列最大长度为100
    for i in range(100):  # 添加100个任务到队列中
        await queue.put(i)
    await asyncio.gather(*[worker(queue) for _ in range(5)])  # 启动5个worker协程

asyncio.run(main())

注意:最后要加task_done()

"""from mitmproxy import http def request(flow:http.HTTPFlow): flow.request.headers["User-Agent"] = "DxShell" with open("../config/request.txt", "w", encoding="utf-8") as file: file.write("URL:"+flow.request.url+"\n") file.write("请求方法"+flow.request.method+"/"+flow.request.scheme+"\n") file.write("请求头:\n") for key, value in flow.request.headers.items(): file.write(f"{key}: {value}\n") file.write("请求数据:\n") file.write(flow.request.text) def response(flow: http.HTTPFlow): flow.response.headers["X-Processed-By"] = "DxShell" with open("../config/response.txt", "w", encoding="utf-8") as file: file.write("响应状态:"+flow.response.reason+"\n") file.write("响应头:\n") for key, value in flow.response.headers.items(): file.write(f"{key}: {value}\n") file.write("响应数据:\n") file.write(flow.response.text)""" import asyncio from mitmproxy import http, ctx from mitmproxy.options import Options from mitmproxy.tools.dump import DumpMaster class HTTPOnlyAddon: def __init__(self): self.blocked_flows = [] # 存储被拦截的请求 def request(self, flow: http.HTTPFlow): # 确保只处理 HTTP/HTTPS(非 TCP、DNS、QUIC 等) if isinstance(flow, http.HTTPFlow): print(f"[HTTP] {flow.request.method} {flow.request.pretty_url}\n") for key, value in flow.request.headers.items(): print(f"{key}: {value}\n") print(f"请求内容:\n{flow.request.text}") flow.intercept() ui = input(">>") print(ui) if ui == "q": exit() elif ui == "r": u = input("[输入修改内容和替换内容]++>") up = u.split(" ") print(up) flow.request.headers[f"{up[1]}"] = f"{up[2]}" flow.kill() exec() def response(self, flow: http.HTTPFlow): if isinstance(flow, http.HTTPFlow): print(f"[HTTP] {flow.response.status_code} {flow.request.pretty_url}") async def start_proxy(): opts = Options( listen_host='127.0.0.1', listen_port=8080, # 关键:关闭非 HTTP 协议的处理(减少日志) mode=["regular"], # 仅常规 HTTP 代理模式 rawtcp=False, # 禁用 TCP 流处理 http2=True, # 允许 HTTP/2(可选) websocket=True, # 允许 WebSocket(可选) ) m = DumpMaster(opts, with_termlog=False, with_dumper=False) # 关闭默认日志 m.addons.add(HTTPOnlyAddon()) await m.run() if __name__ == '__main__': asyncio.run(start_proxy()) 这一段代码没能动态改包的原因
最新发布
07-12
<think>这段代码试图实现一个mitmproxy插件,用于拦截HTTP请求并允许用户修改请求头。但是,代码中存在几个问题,导致动态改包功能无法正常工作。主要问题如下: 1. **`flow.intercept()` 和用户输入阻塞问题**:在`request`方法中调用了`flow.intercept()`,这会导致请求被挂起,等待进一步处理。然而,随后的`input`函数会阻塞事件循环,因为mitmproxy是异步的。在异步环境中,使用同步输入(如`input`)会阻塞整个事件循环,导致mitmproxy无法处理其他事件。 2. **`flow.kill()` 和 `exec()` 的使用**:在用户输入后,代码尝试使用`flow.kill()`来终止请求,然后调用`exec()`(这可能是想重新执行?)。`flow.kill()`用于终止请求,但`exec()`函数在这里是多余的,而且会引发错误,因为`exec()`需要参数(一个字符串或代码对象)。此外,终止请求后,应该恢复请求(使用`flow.resume()`)或者继续处理,但这里逻辑混乱。 3. **修改请求后如何继续**:当用户输入修改指令后,代码修改了请求头,然后调用了`flow.kill()`,这会导致请求被终止(即不发送到服务器)。通常,修改后应该调用`flow.resume()`来继续发送请求。 4. **代码结构问题**:`exec()`函数的使用不正确,它用于执行动态生成的Python代码,这里显然不是这个目的。 5. **日志记录问题**:代码中使用了`print`来输出信息,但在异步环境中,直接使用`print`可能会与mitmproxy的控制台输出冲突,而且可能不会实时显示。 6. **拦截逻辑**:在`request`方法中,对所有请求都进行了拦截(`flow.intercept()`),这可能会导致所有请求都被挂起,直到用户输入。如果用户没有及时输入,请求会超时。 7. **`ui`输入处理**:用户输入的处理逻辑不完整,只处理了"q"(退出)和"r"(替换头部)两种情况,其他情况没有处理,请求会一直被挂起。 8. **`HTTPOnlyAddon`类中的`blocked_flows`未被使用**。 解决方案: 为了在mitmproxy中实现动态修改请求,我们需要使用异步方式来获取用户输入,以避免阻塞事件循环。但是,在控制台程序中,异步输入并不容易实现。另一种方法是使用mitmproxy提供的控制台界面(如`mitmproxy.tools.console`),但这里我们使用的是`DumpMaster`(无界面模式)。 我们可以考虑以下改进: 1. **使用单独的线程处理用户输入**:创建一个线程专门负责读取用户输入,然后通过队列将输入命令传递给主事件循环。 2. **使用异步输入**:在异步环境中,可以使用`asyncio`的异步输入函数,但标准库中没有提供。我们可以使用第三方库如`aioconsole`,或者自己用事件循环的方式处理。 由于mitmproxy的插件运行在异步环境中,我们采用第二种方法,使用`aioconsole`来实现异步输入。 但请注意,mitmproxy的插件系统是单线程的,所以我们需要在不阻塞事件循环的情况下等待用户输入。 这里我们使用一个折中方案:使用`asyncio.to_thread`来在另一个线程中运行同步输入函数,这样就不会阻塞事件循环。 修改后的步骤: 1. 在`request`方法中,我们调用`flow.intercept()`挂起请求。 2. 然后使用`asyncio.create_task`创建一个异步任务来处理这个请求的用户输入。 3. 在异步任务中,使用`asyncio.to_thread`来运行同步的`input`函数,这样就不会阻塞事件循环。 4. 根据用户输入,修改请求,然后调用`flow.resume()`来继续发送请求。 另外,注意修改请求头后,应该继续请求,而不是终止(`kill`)请求。 修改后的代码示例: ```python import asyncio from mitmproxy import http, ctx from mitmproxy.options import Options from mitmproxy.tools.dump import DumpMaster class HTTPOnlyAddon: async def handle_intercepted_request(self, flow: http.HTTPFlow): # 在另一个线程中执行同步的input,避免阻塞事件循环 ui = await asyncio.to_thread(input, ">> ") if ui == "q": # 退出程序 ctx.master.shutdown() elif ui.startswith("r "): # 格式: r header_name new_value parts = ui.split(" ", 2) if len(parts) == 3: header_name = parts[1] new_value = parts[2] flow.request.headers[header_name] = new_value # 继续请求 flow.resume() else: # 其他情况也继续请求(未修改) flow.resume() def request(self, flow: http.HTTPFlow): if isinstance(flow, http.HTTPFlow): print(f"[HTTP] {flow.request.method} {flow.request.pretty_url}\n") for key, value in flow.request.headers.items(): print(f"{key}: {value}\n") print(f"请求内容:\n{flow.request.text}") # 拦截请求,等待用户输入 flow.intercept() # 创建一个任务来处理这个被拦截的请求 asyncio.create_task(self.handle_intercepted_request(flow)) def response(self, flow: http.HTTPFlow): if isinstance(flow, http.HTTPFlow): print(f"[HTTP] {flow.response.status_code} {flow.request.pretty_url}") async def start_proxy(): opts = Options( listen_host='127.0.0.1', listen_port=8080, mode=["regular"], rawtcp=False, http2=True, websocket=True, ) m = DumpMaster(opts, with_termlog=False, with_dumper=False) m.addons.add(HTTPOnlyAddon()) await m.run() if __name__ == '__main__': asyncio.run(start_proxy()) ``` 注意:由于我们在`handle_intercepted_request`中使用了`asyncio.to_thread`,这需要Python 3.9+。如果使用较低版本,可以使用`loop.run_in_executor`。 此外,我们只处理了修改请求头的情况(以"r"命令开头),其他命令如退出("q")则关闭代理。其他输入则直接继续请求(不做修改)。 这样修改后,每个被拦截的请求都会等待用户输入,而不会阻塞整个代理,因为每个请求的用户输入都在单独的任务中处理。 但是,请注意,如果有多个请求同时被拦截,用户输入可能会交错显示。因此,这种方案在并发请求较多时可能不太友好。更复杂的实现可以使用一个队列来顺序处理用户输入。 另外,我们只修改了请求头,如果需要修改请求体或其他部分,可以类似地扩展。 最后,我们移除了`flow.kill()`和`exec()`,改为使用`flow.resume()`来继续请求。 请确保安装了mitmproxy(`pip install mitmproxy`)并运行此代码。注意,由于在异步环境中使用同步输入,可能会在输入时看不到提示(因为控制台同时被mitmproxy使用)。因此,建议在实际使用中考虑更合适的用户交互方式,比如使用mitmproxy的console界面。 相关问题: 1. 如何在mitmproxy中修改响应内容? 2. mitmproxy如何处理HTTPS请求? 3. 如何实现mitmproxy插件的持久化存储(如保存配置)? 4. 在mitmproxy中如何实现请求重放?</think>这段代码无法实现动态修改请求包的主要原因在于它使用了同步的 `input()` 函数,而 mitmproxy 是基于异步事件循环运行的。当 `input()` 阻塞等待用户输入时,整个事件循环会被冻结,导致代理无法处理其他请求。以下是详细分析和解决方案: ### 问题分析 1. **同步阻塞问题**: ```python ui = input(">>") # 同步阻塞调用 ``` 在异步环境中使用同步输入函数会阻塞整个事件循环,mitmproxy 无法处理其他请求。 2. **请求处理逻辑错误**: ```python flow.kill() # 终止请求 exec() # 无效调用(exec需要参数) ``` `flow.kill()` 会终止当前请求,而 `exec()` 函数缺少参数且用途错误。 3. **拦截机制问题**: ```python flow.intercept() # 拦截请求但未恢复 ``` 调用 `intercept()` 后没有相应的 `resume()` 来恢复请求处理。 4. **文件写入冲突**: ```python with open("../config/request.txt", "w")... # 每次覆盖文件 ``` 使用 `"w"` 模式会覆盖之前的请求数据,应改为追加模式 `"a"`。 ### 解决方案(完整代码) ```python import asyncio import threading from mitmproxy import http, ctx from mitmproxy.options import Options from mitmproxy.tools.dump import DumpMaster class HTTPInterceptor: def __init__(self): self.input_queue = asyncio.Queue() # 异步输入队列 self.input_thread = None async def handle_input(self): """在独立线程中处理用户输入""" while True: # 在后台线程中同步获取输入 user_input = await asyncio.to_thread(input, ">> ") await self.input_queue.put(user_input) async def process_request(self, flow: http.HTTPFlow): """处理被拦截的请求""" flow.intercept() # 挂起请求 print(f"\n[拦截请求] {flow.request.method} {flow.request.url}") print("请求头:") for k, v in flow.request.headers.items(): print(f" {k}: {v}") # 等待用户指令 while True: command = await self.input_queue.get() if command == "q": ctx.master.shutdown() return elif command == "r": flow.resume() # 继续请求 return elif command.startswith("modify"): # 格式: modify header_name new_value _, header, value = command.split(maxsplit=2) flow.request.headers[header] = value print(f"已修改请求头: {header} = {value}") elif command == "show": print("当前请求头:") for k, v in flow.request.headers.items(): print(f" {k}: {v}") else: print("可用命令:") print(" q - 退出代理") print(" r - 发送原始请求") print(" modify [header] [value] - 修改请求头") print(" show - 显示当前请求头") def request(self, flow: http.HTTPFlow): # 确保只处理HTTP请求 if isinstance(flow, http.HTTPFlow): # 创建异步任务处理请求 asyncio.create_task(self.process_request(flow)) async def start_proxy(): opts = Options( listen_host='127.0.0.1', listen_port=8080, mode=["regular"], rawtcp=False, http2=True, websocket=True, ) interceptor = HTTPInterceptor() m = DumpMaster(opts, with_termlog=False, with_dumper=False) m.addons.add(interceptor) # 启动输入处理线程 interceptor.input_thread = threading.Thread( target=asyncio.run, args=(interceptor.handle_input(),), daemon=True ) interceptor.input_thread.start() await m.run() if __name__ == '__main__': asyncio.run(start_proxy()) ``` ### 关键改进解释 1. **异步输入处理**: - 使用 `asyncio.Queue` 和独立线程处理用户输入 - `asyncio.to_thread(input, ...)` 在后台线程执行阻塞输入 2. **正确的请求拦截流程**: ```python flow.intercept() # 挂起请求 # ...处理修改... flow.resume() # 继续请求 ``` 3. **命令处理系统**: - `modify User-Agent Custom-Agent`:修改指定请求头 - `show`:显示当前请求头 - `r`:发送原始请求 - `q`:退出代理 4. **线程安全设计**: - 用户输入在独立线程处理 - 通过异步队列与主事件循环通信 5. **日志记录改进**: ```python with open("requests.log", "a") as f: # 使用追加模式 f.write(f"[{time.ctime()}] {flow.request.url}\n") ``` ### 使用示例 ``` >> modify User-Agent MyCustomAgent 已修改请求头: User-Agent = MyCustomAgent >> show 当前请求头: Host: example.com User-Agent: MyCustomAgent ... >> r # 发送修改后的请求 ``` ### 注意事项 1. 确保安装必要依赖: ```bash pip install mitmproxy asyncio ``` 2. 客户端需要配置代理:`127.0.0.1:8080` 3. 对于HTTPS流量,需安装mitmproxy的CA证书
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡侃有料

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值