CheeseAPI 2.0.8b4__tar.gz → 2.0.8b5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/app.py +1 -1
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/cors.py +9 -9
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/request.py +18 -18
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/response.py +49 -49
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/websocket.py +6 -6
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/PKG-INFO +1 -1
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/pyproject.toml +1 -1
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/.gitignore +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/__init__.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/file.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/printer.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/route.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/scheduler.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/signal.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/static.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/CheeseAPI/validator.py +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/LICENSE +0 -0
- {cheeseapi-2.0.8b4 → cheeseapi-2.0.8b5}/README.md +0 -0
|
@@ -361,7 +361,7 @@ class AppProxy:
|
|
|
361
361
|
'request': request
|
|
362
362
|
})
|
|
363
363
|
|
|
364
|
-
if request._proxy.protocol is None or not self.app.keep_alive or (request._proxy.protocol == 'HTTP/1.0' and request.headers.get('
|
|
364
|
+
if request._proxy.protocol is None or not self.app.keep_alive or (request._proxy.protocol == 'HTTP/1.0' and request.headers.get('connection') != 'keep-alive') or (request._proxy.protocol == 'HTTP/1.1' and request.headers.get('connection') == 'close'):
|
|
365
365
|
break
|
|
366
366
|
|
|
367
367
|
client_socket, addr = await self.before_request(client_socket, addr)
|
|
@@ -19,31 +19,31 @@ class CORS:
|
|
|
19
19
|
self.max_age: int | None = max_age
|
|
20
20
|
|
|
21
21
|
def get_response(self, request: 'Request'):
|
|
22
|
-
origin = request.headers.get('
|
|
22
|
+
origin = request.headers.get('origin', '')
|
|
23
23
|
if '*' not in self.allow_origins and origin not in self.allow_origins:
|
|
24
24
|
return Response(status = 403)
|
|
25
25
|
|
|
26
26
|
headers = {}
|
|
27
27
|
|
|
28
28
|
if origin in self.allow_origins:
|
|
29
|
-
headers['
|
|
29
|
+
headers['access-control-allow-origin'] = origin
|
|
30
30
|
elif '*' in self.allow_origins and not self.allow_credentials:
|
|
31
|
-
headers['
|
|
31
|
+
headers['access-control-allow-origin'] = '*'
|
|
32
32
|
|
|
33
|
-
headers['
|
|
33
|
+
headers['access-control-allow-methods'] = ', '.join(self.allow_methods)
|
|
34
34
|
|
|
35
35
|
if '*' in self.allow_headers:
|
|
36
|
-
headers['
|
|
36
|
+
headers['access-control-allow-headers'] = request.headers.get('access-control-request-headers') or '*'
|
|
37
37
|
else:
|
|
38
|
-
headers['
|
|
38
|
+
headers['access-control-allow-headers'] = ', '.join(self.allow_headers)
|
|
39
39
|
|
|
40
40
|
if self.allow_credentials:
|
|
41
|
-
headers['
|
|
41
|
+
headers['access-control-allow-credentials'] = 'true'
|
|
42
42
|
|
|
43
43
|
if self.expose_headers:
|
|
44
|
-
headers['
|
|
44
|
+
headers['access-control-expose-headers'] = ', '.join(self.expose_headers)
|
|
45
45
|
|
|
46
46
|
if self.max_age is not None:
|
|
47
|
-
headers['
|
|
47
|
+
headers['access-control-max-age'] = str(self.max_age)
|
|
48
48
|
|
|
49
49
|
return Response(status = 204, headers = headers)
|
|
@@ -150,34 +150,34 @@ class RequestProxy:
|
|
|
150
150
|
self.request._headers = {}
|
|
151
151
|
for line in lines[1:]:
|
|
152
152
|
key, value = line.split(': ', 1)
|
|
153
|
-
self.request.headers[key] = value
|
|
153
|
+
self.request.headers[key.lower()] = value
|
|
154
154
|
|
|
155
|
-
if '
|
|
155
|
+
if 'cookie' in self.request.headers:
|
|
156
156
|
self.request._cookies = {}
|
|
157
|
-
for cookie in self.request.headers['
|
|
157
|
+
for cookie in self.request.headers['cookie'].split(';'):
|
|
158
158
|
key, value = cookie.strip().split('=', 1)
|
|
159
159
|
self.request.cookies[key] = value
|
|
160
160
|
|
|
161
|
-
if '
|
|
162
|
-
self.request._ip = self.request.headers['
|
|
163
|
-
elif '
|
|
164
|
-
self.request._ip = self.request.headers['
|
|
161
|
+
if 'x-real-ip' in self.request.headers:
|
|
162
|
+
self.request._ip = self.request.headers['x-real-ip']
|
|
163
|
+
elif 'x-forwarded-for' in self.request.headers:
|
|
164
|
+
self.request._ip = self.request.headers['x-forwarded-for'].split(',')[0].strip()
|
|
165
165
|
|
|
166
|
-
if '
|
|
166
|
+
if 'range' in self.request.headers:
|
|
167
167
|
self.request._ranges = []
|
|
168
|
-
for range_part in self.request.headers['
|
|
168
|
+
for range_part in self.request.headers['range'][6:].split(','):
|
|
169
169
|
range_part = range_part.strip()
|
|
170
170
|
if '-' in range_part:
|
|
171
171
|
start, end = range_part.split('-', 1)
|
|
172
172
|
self.request.ranges.append((int(start) if start else 0, int(end) if end else None))
|
|
173
173
|
|
|
174
|
-
if '
|
|
174
|
+
if 'upgrade' in self.request.headers and self.request.headers['upgrade'] == 'websocket':
|
|
175
175
|
self.request._method = 'WEBSOCKET'
|
|
176
176
|
|
|
177
177
|
async def recv_body(self, get_all: bool = False) -> bool | Response | None:
|
|
178
178
|
loop = asyncio.get_event_loop()
|
|
179
179
|
|
|
180
|
-
content_length = self.request.headers.get('
|
|
180
|
+
content_length = self.request.headers.get('content-length')
|
|
181
181
|
if content_length:
|
|
182
182
|
self.request._body = self.buffer
|
|
183
183
|
self.buffer = b''
|
|
@@ -193,7 +193,7 @@ class RequestProxy:
|
|
|
193
193
|
raise ConnectionAbortedError()
|
|
194
194
|
return True
|
|
195
195
|
|
|
196
|
-
is_chunked = self.request.headers.get('
|
|
196
|
+
is_chunked = self.request.headers.get('transfer-encoding') == 'chunked'
|
|
197
197
|
if is_chunked:
|
|
198
198
|
if self.request._body is None:
|
|
199
199
|
self.request._body = b''
|
|
@@ -213,7 +213,7 @@ class RequestProxy:
|
|
|
213
213
|
return Response(status = 400)
|
|
214
214
|
|
|
215
215
|
if chunk_size == 0:
|
|
216
|
-
if self.request.headers.get('
|
|
216
|
+
if self.request.headers.get('trailer'):
|
|
217
217
|
while b'\r\n\r\n' not in self.buffer:
|
|
218
218
|
try:
|
|
219
219
|
data = await asyncio.wait_for(loop.sock_recv(self.client_socket, self.app.socket_receive_buffer_size), self.app.request_timeout)
|
|
@@ -224,7 +224,7 @@ class RequestProxy:
|
|
|
224
224
|
self.buffer += data
|
|
225
225
|
|
|
226
226
|
trailer, self.buffer = self.buffer.split(b'\r\n\r\n', 1)
|
|
227
|
-
if trailer and trailer.startswith('
|
|
227
|
+
if trailer and trailer.startswith('content-md5: ') and trailer[13:] not in (base64.b64encode(hashlib.md5(self.request._body).digest()).decode(), hashlib.md5(self.request._body).hexdigest()):
|
|
228
228
|
return Response(status = 400)
|
|
229
229
|
|
|
230
230
|
self.buffer = b''
|
|
@@ -246,7 +246,7 @@ class RequestProxy:
|
|
|
246
246
|
if self.request.body is None:
|
|
247
247
|
return
|
|
248
248
|
|
|
249
|
-
content_type = self.request.headers.get('
|
|
249
|
+
content_type = self.request.headers.get('content-type')
|
|
250
250
|
if content_type == 'text/plain' or content_type is None:
|
|
251
251
|
self.request._body = self.request.body.decode()
|
|
252
252
|
elif content_type == 'application/json':
|
|
@@ -267,7 +267,7 @@ class RequestProxy:
|
|
|
267
267
|
name = None
|
|
268
268
|
filename = None
|
|
269
269
|
for line in headers.decode().strip().split('\r\n'):
|
|
270
|
-
if line.startswith('
|
|
270
|
+
if line.startswith('content-disposition:'):
|
|
271
271
|
name_match = re.search(r'name="([^"]*)"', line)
|
|
272
272
|
if name_match:
|
|
273
273
|
name = name_match.group(1)
|
|
@@ -287,7 +287,7 @@ class RequestProxy:
|
|
|
287
287
|
self.request._form = {}
|
|
288
288
|
self.request._form[name] = data.decode()
|
|
289
289
|
|
|
290
|
-
if self.request.headers.get('
|
|
291
|
-
match = re.search(r'filename="([^"]*)"', self.request.headers['
|
|
290
|
+
if self.request.headers.get('content-disposition'):
|
|
291
|
+
match = re.search(r'filename="([^"]*)"', self.request.headers['content-disposition'])
|
|
292
292
|
if match:
|
|
293
293
|
self.request._file = File(match.group(1), self.request.body)
|
|
@@ -125,7 +125,7 @@ class RedirectResponse(Response):
|
|
|
125
125
|
def __init__(self, location: str, status: Literal[301, 302, 303, 307, 308] = 302, headers: dict[str, str] | None = None, body: bytes | str | list | dict | None = None):
|
|
126
126
|
if headers is None:
|
|
127
127
|
headers = {}
|
|
128
|
-
headers['
|
|
128
|
+
headers['location'] = location
|
|
129
129
|
|
|
130
130
|
super().__init__(status, body, headers)
|
|
131
131
|
|
|
@@ -173,14 +173,14 @@ class ResponseProxy:
|
|
|
173
173
|
bytes.extend(['', ''])
|
|
174
174
|
bytes = '\r\n'.join(bytes).encode()
|
|
175
175
|
if not no_body:
|
|
176
|
-
if self.response.headers.get('
|
|
176
|
+
if self.response.headers.get('transfer-encoding') == 'chunked':
|
|
177
177
|
bytes += hex(len(data)).encode() + b'\r\n' + data + b'\r\n'
|
|
178
178
|
else:
|
|
179
179
|
bytes += data
|
|
180
180
|
await loop.sock_sendall(client_socket, bytes)
|
|
181
181
|
|
|
182
182
|
if not no_body:
|
|
183
|
-
if self.response.headers.get('
|
|
183
|
+
if self.response.headers.get('transfer-encoding') == 'chunked':
|
|
184
184
|
async for _, _, data in gen:
|
|
185
185
|
await loop.sock_sendall(client_socket, hex(len(data)).encode() + b'\r\n' + data + b'\r\n')
|
|
186
186
|
await loop.sock_sendall(client_socket, b'0\r\n\r\n')
|
|
@@ -193,7 +193,7 @@ class ResponseProxy:
|
|
|
193
193
|
|
|
194
194
|
async def get_status(self, status: int, headers: dict[str, str], body: dict | list | str | bytes | None) -> tuple[int, dict[str, str], dict | list | str | bytes | None]:
|
|
195
195
|
if isinstance(self.response, FileResponse):
|
|
196
|
-
if self.request.headers.get('
|
|
196
|
+
if self.request.headers.get('range') is not None:
|
|
197
197
|
max_range = -1
|
|
198
198
|
for range in self.request.ranges:
|
|
199
199
|
if range[1] is not None:
|
|
@@ -229,36 +229,36 @@ class ResponseProxy:
|
|
|
229
229
|
elif self.response.transmission_type == 'CHUNKED':
|
|
230
230
|
body = self.file_response_chunked_body()
|
|
231
231
|
|
|
232
|
-
if '
|
|
232
|
+
if 'content-type' not in headers and 'content-disposition' not in headers:
|
|
233
233
|
mime_type = mimetypes.guess_type(self.response.file.name)[0] or 'application/octet-stream'
|
|
234
234
|
if mime_type in MERGE_TYPES:
|
|
235
235
|
mime_type = MERGE_TYPES[mime_type]
|
|
236
|
-
headers['
|
|
237
|
-
headers['
|
|
236
|
+
headers['content-type'] = f'{mime_type}; charset=utf-8'
|
|
237
|
+
headers['content-disposition'] = f'{"inline" if self.response.preview and mime_type in PREVIEWABLE_TYPES else "attachment"}; filename="{self.response.file.name}"'
|
|
238
238
|
|
|
239
239
|
if isinstance(self.response.body, AsyncIterable):
|
|
240
|
-
if '
|
|
241
|
-
headers['
|
|
240
|
+
if 'transfer-encoding' not in headers:
|
|
241
|
+
headers['transfer-encoding'] = 'chunked'
|
|
242
242
|
|
|
243
|
-
if '
|
|
243
|
+
if 'date' not in headers:
|
|
244
244
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
245
|
-
headers['
|
|
245
|
+
headers['date'] = (now.strftime('%a, %d %b %Y %H:%M:%S.') + f'{now.microsecond:06d} GMT') if self.response.high_precision_date else now.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
246
246
|
|
|
247
|
-
if '
|
|
248
|
-
if self.app.keep_alive and self.request and ((self.request._proxy.protocol == 'HTTP/1.1' and self.request.headers.get('
|
|
249
|
-
headers['
|
|
250
|
-
headers['
|
|
247
|
+
if 'connection' not in headers:
|
|
248
|
+
if self.app.keep_alive and self.request and ((self.request._proxy.protocol == 'HTTP/1.1' and self.request.headers.get('connection', '') != 'close') or (self.request._proxy.protocol == 'HTTP/1.0' and self.request.headers.get('connection', '') == 'keep-alive')):
|
|
249
|
+
headers['connection'] = 'keep-alive'
|
|
250
|
+
headers['keep-alive'] = f'timeout={self.app.keep_alive_timeout}, max={self.app.keep_alive_max_requests}'
|
|
251
251
|
else:
|
|
252
|
-
headers['
|
|
252
|
+
headers['connection'] = 'close'
|
|
253
253
|
|
|
254
254
|
if self.request.ranges:
|
|
255
255
|
if status == 206:
|
|
256
|
-
headers.setdefault('
|
|
256
|
+
headers.setdefault('accept-ranges', 'bytes')
|
|
257
257
|
else:
|
|
258
258
|
encodings = []
|
|
259
259
|
encoding_quality = False
|
|
260
|
-
if self.app.compress and self.request and self.request.headers and self.request.headers.get('
|
|
261
|
-
for encoding in self.request.headers.get('
|
|
260
|
+
if self.app.compress and self.request and self.request.headers and self.request.headers.get('accept-encoding'):
|
|
261
|
+
for encoding in self.request.headers.get('accept-encoding').split(','):
|
|
262
262
|
encoding_split = encoding.strip().split(';')
|
|
263
263
|
if len(encoding_split) == 1:
|
|
264
264
|
encoding_split.append(1)
|
|
@@ -270,26 +270,26 @@ class ResponseProxy:
|
|
|
270
270
|
encodings = [encoding[0] for encoding in encodings]
|
|
271
271
|
|
|
272
272
|
if self.response.compress is not None:
|
|
273
|
-
headers.setdefault('
|
|
273
|
+
headers.setdefault('content-encoding', self.response.compress)
|
|
274
274
|
elif encodings:
|
|
275
275
|
if encoding_quality is True:
|
|
276
276
|
for encoding in encodings:
|
|
277
277
|
if encoding in self.app.compress:
|
|
278
|
-
headers.setdefault('
|
|
278
|
+
headers.setdefault('content-encoding', encoding)
|
|
279
279
|
break
|
|
280
280
|
if encoding == '*':
|
|
281
|
-
headers.setdefault('
|
|
281
|
+
headers.setdefault('content-encoding', self.app.compress[0])
|
|
282
282
|
break
|
|
283
283
|
else:
|
|
284
284
|
if encodings[0] == '*':
|
|
285
|
-
headers.setdefault('
|
|
285
|
+
headers.setdefault('content-encoding', self.app.compress[0])
|
|
286
286
|
else:
|
|
287
287
|
for encoding in self.app.compress:
|
|
288
288
|
if encoding in encodings:
|
|
289
|
-
headers.setdefault('
|
|
289
|
+
headers.setdefault('content-encoding', encoding)
|
|
290
290
|
break
|
|
291
291
|
|
|
292
|
-
if self.response.cookies and '
|
|
292
|
+
if self.response.cookies and 'set-cookie' not in headers:
|
|
293
293
|
cookies = []
|
|
294
294
|
for key, cookie in self.response.cookies.items():
|
|
295
295
|
_cookie = f'{key}={cookie["value"]}'
|
|
@@ -304,7 +304,7 @@ class ResponseProxy:
|
|
|
304
304
|
if cookie['http_only']:
|
|
305
305
|
_cookie += '; HttpOnly'
|
|
306
306
|
cookies.append(_cookie)
|
|
307
|
-
headers['
|
|
307
|
+
headers['set-cookie'] = ', '.join(cookies)
|
|
308
308
|
|
|
309
309
|
return status, headers, body
|
|
310
310
|
|
|
@@ -321,13 +321,13 @@ class ResponseProxy:
|
|
|
321
321
|
handler.seek(self.request.ranges[0][0] or 0)
|
|
322
322
|
data = handler.read((self.request.ranges[0][1] or size) - (self.request.ranges[0][0]))
|
|
323
323
|
handler.close()
|
|
324
|
-
headers['
|
|
325
|
-
headers['
|
|
324
|
+
headers['content-length'] = str(len(data))
|
|
325
|
+
headers['content-range'] = f'bytes {self.request.ranges[0][0]}-{(self.request.ranges[0][1] or size) - 1}/{size}'
|
|
326
326
|
yield status, headers, data
|
|
327
327
|
else:
|
|
328
328
|
boundary = uuid.uuid4().hex
|
|
329
|
-
content_type = headers['
|
|
330
|
-
headers['
|
|
329
|
+
content_type = headers['content-type']
|
|
330
|
+
headers['content-type'] = f'multipart/byteranges; boundary={boundary}'
|
|
331
331
|
if self.response.file._data is not None:
|
|
332
332
|
size = len(self.response.file.data)
|
|
333
333
|
else:
|
|
@@ -336,10 +336,10 @@ class ResponseProxy:
|
|
|
336
336
|
content_length = 0
|
|
337
337
|
for range in self.request.ranges:
|
|
338
338
|
content_length += 2 + 32 + 2 + 14 + len(content_type) + 2 + 21 + (1 if range[0] == 0 else int(math.log10(range[0]))) + 1 + 1 + int(math.log10(range[1] or size)) + 1 + 1 + int(math.log10(size)) + 1 + 4 + (range[1] or size) - range[0] + 1
|
|
339
|
-
headers['
|
|
339
|
+
headers['content-length'] = str(content_length)
|
|
340
340
|
|
|
341
341
|
for range in self.request.ranges:
|
|
342
|
-
data = [b'--', boundary.encode(), b'\r\n', b'
|
|
342
|
+
data = [b'--', boundary.encode(), b'\r\n', b'content-type: ', content_type.encode(), b'\r\n', b'content-range: bytes ', str(range[0]).encode(), b'-', str(range[1] or size).encode(), b'/', str(size).encode() + b'\r\n\r\n']
|
|
343
343
|
if self.response.file._data is not None:
|
|
344
344
|
data.append(self.response.file.data[range[0]:range[1] or size + 1])
|
|
345
345
|
else:
|
|
@@ -357,22 +357,22 @@ class ResponseProxy:
|
|
|
357
357
|
|
|
358
358
|
if isinstance(data, (dict, list)):
|
|
359
359
|
data = json.dumps(data).encode()
|
|
360
|
-
headers.setdefault('
|
|
360
|
+
headers.setdefault('content-type', 'application/json; charset=utf-8')
|
|
361
361
|
elif isinstance(data, str):
|
|
362
362
|
data = data.encode()
|
|
363
|
-
headers.setdefault('
|
|
363
|
+
headers.setdefault('content-type', 'text/plain; charset=utf-8')
|
|
364
364
|
elif data is None:
|
|
365
365
|
data = HTTP_STATUS[status].encode()
|
|
366
|
-
headers.setdefault('
|
|
366
|
+
headers.setdefault('content-type', 'text/plain; charset=utf-8')
|
|
367
367
|
elif isinstance(data, bytes):
|
|
368
|
-
headers.setdefault('
|
|
368
|
+
headers.setdefault('content-type', 'application/octet-stream; charset=utf-8')
|
|
369
369
|
|
|
370
370
|
if isinstance(body, AsyncIterable) is False:
|
|
371
|
-
headers['
|
|
371
|
+
headers['content-length'] = str(len(data))
|
|
372
372
|
|
|
373
373
|
status, headers, data = await self.get_encode_body(status, headers, data)
|
|
374
374
|
if isinstance(body, AsyncIterable) is False:
|
|
375
|
-
headers['
|
|
375
|
+
headers['content-length'] = str(len(data))
|
|
376
376
|
yield status, headers, data
|
|
377
377
|
|
|
378
378
|
if isinstance(body, AsyncIterable):
|
|
@@ -385,25 +385,25 @@ class ResponseProxy:
|
|
|
385
385
|
yield status, headers, data
|
|
386
386
|
|
|
387
387
|
async def get_encode_body(self, status: int, headers: dict[str, str], body: bytes) -> tuple[int, dict[str, str], bytes]:
|
|
388
|
-
content_length = headers.get('
|
|
388
|
+
content_length = headers.get('content-length')
|
|
389
389
|
if content_length and int(content_length) < self.app.compress_min_length:
|
|
390
|
-
if '
|
|
391
|
-
del headers['
|
|
390
|
+
if 'content-encoding' in headers:
|
|
391
|
+
del headers['content-encoding']
|
|
392
392
|
|
|
393
|
-
if '
|
|
393
|
+
if 'content-encoding' in headers and 'content-length' in headers and (int(headers['content-length']) > self.app.compress_min_length or self.response.compress is not None):
|
|
394
394
|
compress_level = self.response.compress_level if self.response.compress_level is not None else self.app.compress_level
|
|
395
|
-
if headers['
|
|
395
|
+
if headers['content-encoding'] == 'gzip':
|
|
396
396
|
body = gzip.compress(body, compress_level)
|
|
397
|
-
elif headers['
|
|
397
|
+
elif headers['content-encoding'] == 'deflate':
|
|
398
398
|
body = zlib.compress(body, level = compress_level)
|
|
399
|
-
elif headers['
|
|
399
|
+
elif headers['content-encoding'] == 'br':
|
|
400
400
|
body = brotli.compress(body, quality = compress_level)
|
|
401
|
-
elif headers['
|
|
401
|
+
elif headers['content-encoding'] == 'zstd':
|
|
402
402
|
body = zstandard.ZstdCompressor(level = compress_level).compress(body)
|
|
403
403
|
|
|
404
|
-
headers['
|
|
404
|
+
headers['content-length'] = str(len(body))
|
|
405
405
|
else:
|
|
406
|
-
if '
|
|
407
|
-
del headers['
|
|
406
|
+
if 'content-encoding' in headers:
|
|
407
|
+
del headers['content-encoding']
|
|
408
408
|
|
|
409
409
|
return status, headers, body
|
|
@@ -48,8 +48,8 @@ class Websocket:
|
|
|
48
48
|
self._request: 'Request' = request
|
|
49
49
|
|
|
50
50
|
self._proxy: 'WebsocketProxy' = request._proxy.app.WebsocketProxy_Class(request._proxy.app, self)
|
|
51
|
-
self._key: str = self.request.headers.get('
|
|
52
|
-
subprotocols = self.request.headers.get('
|
|
51
|
+
self._key: str = self.request.headers.get('sec-websocket-key')
|
|
52
|
+
subprotocols = self.request.headers.get('sec-websocket-protocol')
|
|
53
53
|
self._subprotocols: list[str] | None = subprotocols.strip().split(',') if subprotocols else None
|
|
54
54
|
self._subprotocol: str | None = None
|
|
55
55
|
self.response: Response | None = None
|
|
@@ -227,15 +227,15 @@ class WebsocketProxy:
|
|
|
227
227
|
|
|
228
228
|
async def get_response(self) -> Response:
|
|
229
229
|
headers = {
|
|
230
|
-
'
|
|
231
|
-
'
|
|
232
|
-
'
|
|
230
|
+
'upgrade': 'websocket',
|
|
231
|
+
'connection': 'upgrade',
|
|
232
|
+
'sec-websocket-accept': base64.b64encode(hashlib.sha1(f'{self.websocket.key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11'.encode('utf-8')).digest()).decode('utf-8')
|
|
233
233
|
}
|
|
234
234
|
if self.websocket.subprotocols:
|
|
235
235
|
subprotocol = await self.websocket.on_subprotocol(self.websocket.subprotocols)
|
|
236
236
|
if subprotocol:
|
|
237
237
|
self.websocket._subprotocol = subprotocol
|
|
238
|
-
headers['
|
|
238
|
+
headers['sec-websocket-protocol'] = subprotocol
|
|
239
239
|
response = self.app.ResponseProxy_Class(self.app, Response(status = 101, headers = headers)).response
|
|
240
240
|
response._proxy.websocket = self.websocket
|
|
241
241
|
return response
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|