(self)
| 783 | self._send_json(404, {"type": "error", "error": {"type": "not_found_error", "message": self.path}}) |
| 784 | |
| 785 | def do_POST(self): |
| 786 | if not self._auth_ok(): |
| 787 | return |
| 788 | # Content-Length 解析放在保护内:畸形头(如 "oops" / 负数)应回规范 400, |
| 789 | # 不能让 int() 抛 ValueError 击穿 handler、给客户端一个空响应。 |
| 790 | try: |
| 791 | n = int(self.headers.get("Content-Length") or 0) |
| 792 | if n < 0: |
| 793 | raise ValueError("negative length") |
| 794 | except (ValueError, TypeError): |
| 795 | self._send_json(400, {"type": "error", "error": { |
| 796 | "type": "invalid_request_error", "message": "invalid Content-Length"}}) |
| 797 | return |
| 798 | raw = self.rfile.read(n) if n else b"{}" |
| 799 | if not self.path.startswith("/v1/messages"): |
| 800 | self._send_json(404, {"type": "error", "error": {"type": "not_found_error", "message": self.path}}) |
| 801 | return |
| 802 | try: |
| 803 | areq = json.loads(raw) |
| 804 | except Exception as e: |
| 805 | self._send_json(400, {"type": "error", "error": {"type": "invalid_request_error", "message": str(e)}}) |
| 806 | return |
| 807 | # 结构校验(修 P1 GPT 复审):顶层必须是对象且 messages 是数组,否则回规范 400。 |
| 808 | # 否则 []/"hello"/{"messages":null} 会在下游 .get / 迭代处抛 AttributeError/TypeError, |
| 809 | # 击穿线程 → 客户端拿到空响应而非 400。 |
| 810 | if not isinstance(areq, dict) or not isinstance(areq.get("messages"), list): |
| 811 | self._send_json(400, {"type": "error", "error": { |
| 812 | "type": "invalid_request_error", |
| 813 | "message": "request body must be a JSON object with a 'messages' array"}}) |
| 814 | return |
| 815 | _dd = os.environ.get("PROXY_DUMP_REQ") |
| 816 | if _dd: |
| 817 | try: |
| 818 | with open(os.path.join(_dd, f"req_{areq.get('model','x')}_{len(raw)}.json"), "w") as _f: |
| 819 | json.dump({"model": areq.get("model"), "thinking": areq.get("thinking"), |
| 820 | "tool_choice": areq.get("tool_choice"), |
| 821 | "n_tools": len(areq.get("tools") or [])}, _f, ensure_ascii=False, indent=2) |
| 822 | except Exception: |
| 823 | pass |
| 824 | if PROV["mode"] == "anthropic": |
| 825 | self._handle_anthropic(areq) |
| 826 | else: |
| 827 | self._handle_openai(areq) |
| 828 | |
| 829 | # ---- HTTP CONNECT 隧道:Anthropic 域名 fast-fail、其余透传(修 #3) ---- |
| 830 | def do_CONNECT(self): |
nothing calls this directly
no test coverage detected