bluewinliang commited on
Commit
41a07df
·
verified ·
1 Parent(s): 21b7e38

Update proxy_handler.py

Browse files
Files changed (1) hide show
  1. proxy_handler.py +12 -22
proxy_handler.py CHANGED
@@ -22,7 +22,6 @@ class ProxyHandler:
22
  limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
23
  http2=True,
24
  )
25
- # The primary secret key from the reference code.
26
  self.primary_secret = "junjie".encode('utf-8')
27
 
28
  async def aclose(self):
@@ -33,24 +32,21 @@ class ProxyHandler:
33
  return int(time.time() * 1000)
34
 
35
  def _parse_jwt_token(self, token: str) -> Dict[str, str]:
36
- """A simple JWT payload decoder to get user ID ('sub' claim)."""
37
  try:
38
  parts = token.split('.')
39
  if len(parts) != 3: return {"user_id": ""}
40
  payload_b64 = parts[1]
41
- payload_b64 += '=' * (-len(payload_b64) % 4) # Add padding if needed
42
  payload_json = base64.urlsafe_b64decode(payload_b64).decode('utf-8')
43
  payload = json.loads(payload_json)
44
  return {"user_id": payload.get("sub", "")}
45
  except Exception:
46
- # It's okay if this fails; we'll proceed with an empty user_id.
47
  return {"user_id": ""}
48
 
49
- # MODIFICATION START: The function now accepts the timestamp to ensure consistency.
50
  def _generate_signature(self, e_payload: str, t_payload: str, timestamp_ms: int) -> Dict[str, Any]:
51
  """
52
  Generates the signature based on the logic from the reference JS code.
53
- A consistent timestamp is now passed into this function.
54
 
55
  Args:
56
  e_payload (str): The simplified payload string (e.g., "requestId,...,timestamp,...").
@@ -61,20 +57,23 @@ class ProxyHandler:
61
  A dictionary with 'signature' and 'timestamp'.
62
  """
63
  b64_encoded_t = base64.b64encode(t_payload.encode("utf-8")).decode("utf-8")
64
-
65
- # Use the passed-in timestamp instead of generating a new one.
66
  message_string = f"{e_payload}|{b64_encoded_t}|{timestamp_ms}"
67
-
68
  n = timestamp_ms // (5 * 60 * 1000)
69
 
 
 
 
70
  msg1 = str(n).encode("utf-8")
71
- intermediate_key = hmac.new(self.primary_secret, msg1, hashlib.sha256).hexdigest()
72
 
 
 
73
  msg2 = message_string.encode("utf-8")
74
- final_signature = hmac.new(intermediate_key.encode("utf-8"), msg2, hashlib.sha256).hexdigest()
 
 
75
 
76
  return {"signature": final_signature, "timestamp": timestamp_ms}
77
- # MODIFICATION END
78
 
79
  def _clean_thinking_content(self, text: str) -> str:
80
  if not text: return ""
@@ -88,7 +87,7 @@ class ProxyHandler:
88
  def _clean_answer_content(self, text: str) -> str:
89
  if not text: return ""
90
  cleaned_text = re.sub(r'<details[^>]*>.*?</details>', '', text, flags=re.DOTALL)
91
- cleaned_text = re.sub(r'<glm_block.*?</glm_block>|<summary>.*?</summary>', '', cleaned_text, flags=re.DOTALL)
92
  cleaned_text = re.sub(r'\s*duration="\d+"[^>]*>', '', cleaned_text)
93
  return cleaned_text
94
 
@@ -102,36 +101,28 @@ class ProxyHandler:
102
  return out
103
 
104
  async def _prep_upstream(self, req: ChatCompletionRequest) -> Tuple[Dict[str, Any], Dict[str, str], str, str]:
105
- """Prepares the request body, headers, cookie, and URL for the upstream API."""
106
  ck = await cookie_manager.get_next_cookie()
107
  if not ck: raise HTTPException(503, "No available cookies")
108
 
109
  model = settings.UPSTREAM_MODEL if req.model == settings.MODEL_NAME else req.model
110
 
111
- # MODIFICATION START: Generate timestamp ONCE and reuse it everywhere.
112
  timestamp_ms = self._get_timestamp_millis()
113
  payload_timestamp = str(timestamp_ms)
114
- # MODIFICATION END
115
 
116
  payload_user_id = str(uuid.uuid4())
117
  payload_request_id = str(uuid.uuid4())
118
 
119
- # e: The simplified payload for the signature, now using the single consistent timestamp.
120
  e_payload = f"requestId,{payload_request_id},timestamp,{payload_timestamp},user_id,{payload_user_id}"
121
 
122
- # t: The last message content
123
  t_payload = ""
124
  if req.messages:
125
  last_message = req.messages[-1]
126
  if isinstance(last_message.content, str):
127
  t_payload = last_message.content
128
 
129
- # MODIFICATION START: Pass the consistent timestamp into the signature generator.
130
  signature_data = self._generate_signature(e_payload, t_payload, timestamp_ms)
131
- # MODIFICATION END
132
 
133
  signature = signature_data["signature"]
134
- # This timestamp now correctly matches the one in the URL params.
135
  signature_timestamp = signature_data["timestamp"]
136
 
137
  url_params = {
@@ -288,7 +279,6 @@ class ProxyHandler:
288
  logger.exception("Non-stream processing failed"); raise
289
 
290
  async def handle_chat_completion(self, req: ChatCompletionRequest):
291
- """Determines whether to stream or not and handles the request."""
292
  stream = bool(req.stream) if req.stream is not None else settings.DEFAULT_STREAM
293
  if stream:
294
  return StreamingResponse(self.stream_proxy_response(req), media_type="text/event-stream",
 
22
  limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
23
  http2=True,
24
  )
 
25
  self.primary_secret = "junjie".encode('utf-8')
26
 
27
  async def aclose(self):
 
32
  return int(time.time() * 1000)
33
 
34
  def _parse_jwt_token(self, token: str) -> Dict[str, str]:
 
35
  try:
36
  parts = token.split('.')
37
  if len(parts) != 3: return {"user_id": ""}
38
  payload_b64 = parts[1]
39
+ payload_b64 += '=' * (-len(payload_b64) % 4)
40
  payload_json = base64.urlsafe_b64decode(payload_b64).decode('utf-8')
41
  payload = json.loads(payload_json)
42
  return {"user_id": payload.get("sub", "")}
43
  except Exception:
 
44
  return {"user_id": ""}
45
 
 
46
  def _generate_signature(self, e_payload: str, t_payload: str, timestamp_ms: int) -> Dict[str, Any]:
47
  """
48
  Generates the signature based on the logic from the reference JS code.
49
+ This version corrects the HMAC chaining issue by using .digest() for the intermediate key.
50
 
51
  Args:
52
  e_payload (str): The simplified payload string (e.g., "requestId,...,timestamp,...").
 
57
  A dictionary with 'signature' and 'timestamp'.
58
  """
59
  b64_encoded_t = base64.b64encode(t_payload.encode("utf-8")).decode("utf-8")
 
 
60
  message_string = f"{e_payload}|{b64_encoded_t}|{timestamp_ms}"
 
61
  n = timestamp_ms // (5 * 60 * 1000)
62
 
63
+ # --- MODIFICATION START: Correct HMAC Chaining ---
64
+
65
+ # 1. First HMAC: Calculate intermediate key as RAW BYTES using .digest()
66
  msg1 = str(n).encode("utf-8")
67
+ intermediate_key_bytes = hmac.new(self.primary_secret, msg1, hashlib.sha256).digest()
68
 
69
+ # 2. Second HMAC: Use the raw bytes of the intermediate key directly.
70
+ # The final result is converted to a hex string for the header.
71
  msg2 = message_string.encode("utf-8")
72
+ final_signature = hmac.new(intermediate_key_bytes, msg2, hashlib.sha256).hexdigest()
73
+
74
+ # --- MODIFICATION END ---
75
 
76
  return {"signature": final_signature, "timestamp": timestamp_ms}
 
77
 
78
  def _clean_thinking_content(self, text: str) -> str:
79
  if not text: return ""
 
87
  def _clean_answer_content(self, text: str) -> str:
88
  if not text: return ""
89
  cleaned_text = re.sub(r'<details[^>]*>.*?</details>', '', text, flags=re.DOTALL)
90
+ cleaned_text = re.sub(r'<glm_block.*?</glm_block>|<summary>.*?</summary>', '', text, flags=re.DOTALL)
91
  cleaned_text = re.sub(r'\s*duration="\d+"[^>]*>', '', cleaned_text)
92
  return cleaned_text
93
 
 
101
  return out
102
 
103
  async def _prep_upstream(self, req: ChatCompletionRequest) -> Tuple[Dict[str, Any], Dict[str, str], str, str]:
 
104
  ck = await cookie_manager.get_next_cookie()
105
  if not ck: raise HTTPException(503, "No available cookies")
106
 
107
  model = settings.UPSTREAM_MODEL if req.model == settings.MODEL_NAME else req.model
108
 
 
109
  timestamp_ms = self._get_timestamp_millis()
110
  payload_timestamp = str(timestamp_ms)
 
111
 
112
  payload_user_id = str(uuid.uuid4())
113
  payload_request_id = str(uuid.uuid4())
114
 
 
115
  e_payload = f"requestId,{payload_request_id},timestamp,{payload_timestamp},user_id,{payload_user_id}"
116
 
 
117
  t_payload = ""
118
  if req.messages:
119
  last_message = req.messages[-1]
120
  if isinstance(last_message.content, str):
121
  t_payload = last_message.content
122
 
 
123
  signature_data = self._generate_signature(e_payload, t_payload, timestamp_ms)
 
124
 
125
  signature = signature_data["signature"]
 
126
  signature_timestamp = signature_data["timestamp"]
127
 
128
  url_params = {
 
279
  logger.exception("Non-stream processing failed"); raise
280
 
281
  async def handle_chat_completion(self, req: ChatCompletionRequest):
 
282
  stream = bool(req.stream) if req.stream is not None else settings.DEFAULT_STREAM
283
  if stream:
284
  return StreamingResponse(self.stream_proxy_response(req), media_type="text/event-stream",