vercel-cli 41.1.0__py3-none-any.whl
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.
Potentially problematic release.
This version of vercel-cli might be problematic. Click here for more details.
- vercel_cli/vendor/LICENSE +202 -0
- vercel_cli/vendor/README.md +63 -0
- vercel_cli/vendor/dist/VERCEL_DIR_README.txt +11 -0
- vercel_cli/vendor/dist/builder-worker.js +62 -0
- vercel_cli/vendor/dist/get-latest-worker.js +272 -0
- vercel_cli/vendor/dist/index.js +172693 -0
- vercel_cli/vendor/dist/vc.js +11 -0
- vercel_cli/vendor/node_modules/.package-lock.json +18 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/CHANGELOG.md +488 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/LICENSE +202 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/build.mjs +3 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/clone-env.d.ts +10 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/clone-env.js +43 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/debug.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/debug.js +31 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/default-cache-path-glob.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/default-cache-path-glob.js +28 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/edge-function.d.ts +41 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/edge-function.js +40 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/errors.d.ts +39 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/errors.js +95 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-blob.d.ts +23 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-blob.js +67 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-fs-ref.d.ts +27 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-fs-ref.js +113 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-ref.d.ts +38 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/file-ref.js +147 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/download.d.ts +9 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/download.js +136 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/get-writable-directory.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/get-writable-directory.js +32 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/glob.d.ts +10 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/glob.js +111 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/node-version.d.ts +9 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/node-version.js +180 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/normalize-path.d.ts +4 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/normalize-path.js +31 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/read-config-file.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/read-config-file.js +76 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/rename.d.ts +11 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/rename.js +30 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/run-user-scripts.d.ts +206 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/run-user-scripts.js +944 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/stream-to-buffer.d.ts +3 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/fs/stream-to-buffer.js +87 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-ignore-filter.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-ignore-filter.js +90 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-installed-package-version.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-installed-package-version.js +53 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-platform-env.d.ts +5 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-platform-env.js +45 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-prefixed-env-vars.d.ts +14 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/get-prefixed-env-vars.js +51 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/hard-link-dir.d.ts +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/hard-link-dir.js +104 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/index.d.ts +33 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/index.js +24390 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/lambda.d.ts +81 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/lambda.js +231 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/nodejs-lambda.d.ts +16 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/nodejs-lambda.js +44 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/os.d.ts +3 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/os.js +62 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/prerender.d.ts +37 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/prerender.js +169 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/schemas.d.ts +60 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/schemas.js +87 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/should-serve.d.ts +2 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/should-serve.js +47 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/constants.d.ts +2 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/constants.js +31 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/index.d.ts +3 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/index.js +33 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/trace.d.ts +37 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/trace/trace.js +92 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/types.d.ts +503 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/types.js +51 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/validate-npmrc.d.ts +12 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/dist/validate-npmrc.js +41 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/file-blob.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/file-fs-ref.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/file-ref.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/download.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/get-writable-directory.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/glob.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/rename.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/run-user-scripts.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/fs/stream-to-buffer.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/lambda.js +1 -0
- vercel_cli/vendor/node_modules/@vercel/build-utils/package.json +60 -0
- vercel_cli/vendor/node_modules/@vercel/python/LICENSE +202 -0
- vercel_cli/vendor/node_modules/@vercel/python/dist/index.js +3132 -0
- vercel_cli/vendor/node_modules/@vercel/python/package.json +36 -0
- vercel_cli/vendor/node_modules/@vercel/python/vc_init.py +681 -0
- vercel_cli/vendor/package.json +40 -0
- vercel_cli-41.1.0.dist-info/METADATA +188 -0
- vercel_cli-41.1.0.dist-info/RECORD +99 -0
- vercel_cli-41.1.0.dist-info/WHEEL +4 -0
- vercel_cli-41.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import base64
|
|
3
|
+
import json
|
|
4
|
+
import inspect
|
|
5
|
+
from importlib import util
|
|
6
|
+
from http.server import BaseHTTPRequestHandler
|
|
7
|
+
import socket
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
# Import relative path https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
|
|
11
|
+
__vc_spec = util.spec_from_file_location("__VC_HANDLER_MODULE_NAME", "./__VC_HANDLER_ENTRYPOINT")
|
|
12
|
+
__vc_module = util.module_from_spec(__vc_spec)
|
|
13
|
+
sys.modules["__VC_HANDLER_MODULE_NAME"] = __vc_module
|
|
14
|
+
__vc_spec.loader.exec_module(__vc_module)
|
|
15
|
+
__vc_variables = dir(__vc_module)
|
|
16
|
+
|
|
17
|
+
_use_legacy_asyncio = sys.version_info < (3, 10)
|
|
18
|
+
|
|
19
|
+
def format_headers(headers, decode=False):
|
|
20
|
+
keyToList = {}
|
|
21
|
+
for key, value in headers.items():
|
|
22
|
+
if decode and 'decode' in dir(key) and 'decode' in dir(value):
|
|
23
|
+
key = key.decode()
|
|
24
|
+
value = value.decode()
|
|
25
|
+
if key not in keyToList:
|
|
26
|
+
keyToList[key] = []
|
|
27
|
+
keyToList[key].append(value)
|
|
28
|
+
return keyToList
|
|
29
|
+
|
|
30
|
+
if 'VERCEL_IPC_PATH' in os.environ:
|
|
31
|
+
from http.server import ThreadingHTTPServer
|
|
32
|
+
import http
|
|
33
|
+
import time
|
|
34
|
+
import contextvars
|
|
35
|
+
import functools
|
|
36
|
+
import builtins
|
|
37
|
+
import logging
|
|
38
|
+
|
|
39
|
+
start_time = time.time()
|
|
40
|
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
41
|
+
sock.connect(os.getenv("VERCEL_IPC_PATH", ""))
|
|
42
|
+
|
|
43
|
+
send_message = lambda message: sock.sendall((json.dumps(message) + '\0').encode())
|
|
44
|
+
storage = contextvars.ContextVar('storage', default=None)
|
|
45
|
+
|
|
46
|
+
# Override urlopen from urllib3 (& requests) to send Request Metrics
|
|
47
|
+
try:
|
|
48
|
+
import urllib3
|
|
49
|
+
from urllib.parse import urlparse
|
|
50
|
+
|
|
51
|
+
def timed_request(func):
|
|
52
|
+
fetchId = 0
|
|
53
|
+
@functools.wraps(func)
|
|
54
|
+
def wrapper(self, method, url, *args, **kwargs):
|
|
55
|
+
nonlocal fetchId
|
|
56
|
+
fetchId += 1
|
|
57
|
+
start_time = int(time.time() * 1000)
|
|
58
|
+
result = func(self, method, url, *args, **kwargs)
|
|
59
|
+
elapsed_time = int(time.time() * 1000) - start_time
|
|
60
|
+
parsed_url = urlparse(url)
|
|
61
|
+
context = storage.get()
|
|
62
|
+
if context is not None:
|
|
63
|
+
send_message({
|
|
64
|
+
"type": "metric",
|
|
65
|
+
"payload": {
|
|
66
|
+
"context": {
|
|
67
|
+
"invocationId": context['invocationId'],
|
|
68
|
+
"requestId": context['requestId'],
|
|
69
|
+
},
|
|
70
|
+
"type": "fetch-metric",
|
|
71
|
+
"payload": {
|
|
72
|
+
"pathname": parsed_url.path,
|
|
73
|
+
"search": parsed_url.query,
|
|
74
|
+
"start": start_time,
|
|
75
|
+
"duration": elapsed_time,
|
|
76
|
+
"host": parsed_url.hostname or self.host,
|
|
77
|
+
"statusCode": result.status,
|
|
78
|
+
"method": method,
|
|
79
|
+
"id": fetchId
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
return result
|
|
84
|
+
return wrapper
|
|
85
|
+
urllib3.connectionpool.HTTPConnectionPool.urlopen = timed_request(urllib3.connectionpool.HTTPConnectionPool.urlopen)
|
|
86
|
+
except:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
# Override sys.stdout and sys.stderr to map logs to the correct request
|
|
90
|
+
class StreamWrapper:
|
|
91
|
+
def __init__(self, stream, stream_name):
|
|
92
|
+
self.stream = stream
|
|
93
|
+
self.stream_name = stream_name
|
|
94
|
+
|
|
95
|
+
def write(self, message):
|
|
96
|
+
context = storage.get()
|
|
97
|
+
if context is not None:
|
|
98
|
+
send_message({
|
|
99
|
+
"type": "log",
|
|
100
|
+
"payload": {
|
|
101
|
+
"context": {
|
|
102
|
+
"invocationId": context['invocationId'],
|
|
103
|
+
"requestId": context['requestId'],
|
|
104
|
+
},
|
|
105
|
+
"message": base64.b64encode(message.encode()).decode(),
|
|
106
|
+
"stream": self.stream_name,
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
else:
|
|
110
|
+
self.stream.write(message)
|
|
111
|
+
|
|
112
|
+
def __getattr__(self, name):
|
|
113
|
+
return getattr(self.stream, name)
|
|
114
|
+
|
|
115
|
+
sys.stdout = StreamWrapper(sys.stdout, "stdout")
|
|
116
|
+
sys.stderr = StreamWrapper(sys.stderr, "stderr")
|
|
117
|
+
|
|
118
|
+
# Override the global print to log to stdout
|
|
119
|
+
def print_wrapper(func):
|
|
120
|
+
@functools.wraps(func)
|
|
121
|
+
def wrapper(*args, **kwargs):
|
|
122
|
+
sys.stdout.write(' '.join(map(str, args)) + '\n')
|
|
123
|
+
return wrapper
|
|
124
|
+
builtins.print = print_wrapper(builtins.print)
|
|
125
|
+
|
|
126
|
+
# Override logging to maps logs to the correct request
|
|
127
|
+
def logging_wrapper(func, level="info"):
|
|
128
|
+
@functools.wraps(func)
|
|
129
|
+
def wrapper(*args, **kwargs):
|
|
130
|
+
context = storage.get()
|
|
131
|
+
if context is not None:
|
|
132
|
+
send_message({
|
|
133
|
+
"type": "log",
|
|
134
|
+
"payload": {
|
|
135
|
+
"context": {
|
|
136
|
+
"invocationId": context['invocationId'],
|
|
137
|
+
"requestId": context['requestId'],
|
|
138
|
+
},
|
|
139
|
+
"message": base64.b64encode(f"{args[0]}".encode()).decode(),
|
|
140
|
+
"level": level,
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
else:
|
|
144
|
+
func(*args, **kwargs)
|
|
145
|
+
return wrapper
|
|
146
|
+
|
|
147
|
+
logging.basicConfig(level=logging.INFO)
|
|
148
|
+
logging.debug = logging_wrapper(logging.debug)
|
|
149
|
+
logging.info = logging_wrapper(logging.info)
|
|
150
|
+
logging.warning = logging_wrapper(logging.warning, "warn")
|
|
151
|
+
logging.error = logging_wrapper(logging.error, "error")
|
|
152
|
+
logging.critical = logging_wrapper(logging.critical, "error")
|
|
153
|
+
|
|
154
|
+
class BaseHandler(BaseHTTPRequestHandler):
|
|
155
|
+
# Re-implementation of BaseHTTPRequestHandler's log_message method to
|
|
156
|
+
# log to stdout instead of stderr.
|
|
157
|
+
def log_message(self, format, *args):
|
|
158
|
+
message = format % args
|
|
159
|
+
sys.stdout.write("%s - - [%s] %s\n" %
|
|
160
|
+
(self.address_string(),
|
|
161
|
+
self.log_date_time_string(),
|
|
162
|
+
message.translate(self._control_char_table)))
|
|
163
|
+
|
|
164
|
+
# Re-implementation of BaseHTTPRequestHandler's handle_one_request method
|
|
165
|
+
# to send the end message after the response is fully sent.
|
|
166
|
+
def handle_one_request(self):
|
|
167
|
+
self.raw_requestline = self.rfile.readline(65537)
|
|
168
|
+
if not self.raw_requestline:
|
|
169
|
+
self.close_connection = True
|
|
170
|
+
return
|
|
171
|
+
if not self.parse_request():
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
invocationId = self.headers.get('x-vercel-internal-invocation-id')
|
|
175
|
+
requestId = int(self.headers.get('x-vercel-internal-request-id'))
|
|
176
|
+
del self.headers['x-vercel-internal-invocation-id']
|
|
177
|
+
del self.headers['x-vercel-internal-request-id']
|
|
178
|
+
del self.headers['x-vercel-internal-span-id']
|
|
179
|
+
del self.headers['x-vercel-internal-trace-id']
|
|
180
|
+
|
|
181
|
+
send_message({
|
|
182
|
+
"type": "handler-started",
|
|
183
|
+
"payload": {
|
|
184
|
+
"handlerStartedAt": int(time.time() * 1000),
|
|
185
|
+
"context": {
|
|
186
|
+
"invocationId": invocationId,
|
|
187
|
+
"requestId": requestId,
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
token = storage.set({
|
|
193
|
+
"invocationId": invocationId,
|
|
194
|
+
"requestId": requestId,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
self.handle_request()
|
|
199
|
+
finally:
|
|
200
|
+
storage.reset(token)
|
|
201
|
+
send_message({
|
|
202
|
+
"type": "end",
|
|
203
|
+
"payload": {
|
|
204
|
+
"context": {
|
|
205
|
+
"invocationId": invocationId,
|
|
206
|
+
"requestId": requestId,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
if 'handler' in __vc_variables or 'Handler' in __vc_variables:
|
|
212
|
+
base = __vc_module.handler if ('handler' in __vc_variables) else __vc_module.Handler
|
|
213
|
+
if not issubclass(base, BaseHTTPRequestHandler):
|
|
214
|
+
print('Handler must inherit from BaseHTTPRequestHandler')
|
|
215
|
+
print('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
216
|
+
exit(1)
|
|
217
|
+
|
|
218
|
+
class Handler(BaseHandler, base):
|
|
219
|
+
def handle_request(self):
|
|
220
|
+
mname = 'do_' + self.command
|
|
221
|
+
if not hasattr(self, mname):
|
|
222
|
+
self.send_error(
|
|
223
|
+
http.HTTPStatus.NOT_IMPLEMENTED,
|
|
224
|
+
"Unsupported method (%r)" % self.command)
|
|
225
|
+
return
|
|
226
|
+
method = getattr(self, mname)
|
|
227
|
+
method()
|
|
228
|
+
self.wfile.flush()
|
|
229
|
+
elif 'app' in __vc_variables:
|
|
230
|
+
if (
|
|
231
|
+
not inspect.iscoroutinefunction(__vc_module.app) and
|
|
232
|
+
not inspect.iscoroutinefunction(__vc_module.app.__call__)
|
|
233
|
+
):
|
|
234
|
+
from io import BytesIO
|
|
235
|
+
|
|
236
|
+
string_types = (str,)
|
|
237
|
+
app = __vc_module.app
|
|
238
|
+
|
|
239
|
+
def wsgi_encoding_dance(s, charset="utf-8", errors="replace"):
|
|
240
|
+
if isinstance(s, str):
|
|
241
|
+
s = s.encode(charset)
|
|
242
|
+
return s.decode("latin1", errors)
|
|
243
|
+
|
|
244
|
+
class Handler(BaseHandler):
|
|
245
|
+
def handle_request(self):
|
|
246
|
+
# Prepare WSGI environment
|
|
247
|
+
if '?' in self.path:
|
|
248
|
+
path, query = self.path.split('?', 1)
|
|
249
|
+
else:
|
|
250
|
+
path, query = self.path, ''
|
|
251
|
+
content_length = int(self.headers.get('Content-Length', 0))
|
|
252
|
+
env = {
|
|
253
|
+
'CONTENT_LENGTH': str(content_length),
|
|
254
|
+
'CONTENT_TYPE': self.headers.get('content-type', ''),
|
|
255
|
+
'PATH_INFO': path,
|
|
256
|
+
'QUERY_STRING': query,
|
|
257
|
+
'REMOTE_ADDR': self.headers.get(
|
|
258
|
+
'x-forwarded-for', self.headers.get(
|
|
259
|
+
'x-real-ip')),
|
|
260
|
+
'REQUEST_METHOD': self.command,
|
|
261
|
+
'SERVER_NAME': self.headers.get('host', 'lambda'),
|
|
262
|
+
'SERVER_PORT': self.headers.get('x-forwarded-port', '80'),
|
|
263
|
+
'SERVER_PROTOCOL': 'HTTP/1.1',
|
|
264
|
+
'wsgi.errors': sys.stderr,
|
|
265
|
+
'wsgi.input': BytesIO(self.rfile.read(content_length)),
|
|
266
|
+
'wsgi.multiprocess': False,
|
|
267
|
+
'wsgi.multithread': False,
|
|
268
|
+
'wsgi.run_once': False,
|
|
269
|
+
'wsgi.url_scheme': self.headers.get('x-forwarded-proto', 'http'),
|
|
270
|
+
'wsgi.version': (1, 0),
|
|
271
|
+
}
|
|
272
|
+
for key, value in env.items():
|
|
273
|
+
if isinstance(value, string_types):
|
|
274
|
+
env[key] = wsgi_encoding_dance(value)
|
|
275
|
+
for k, v in self.headers.items():
|
|
276
|
+
env['HTTP_' + k.replace('-', '_').upper()] = v
|
|
277
|
+
# Response body
|
|
278
|
+
body = BytesIO()
|
|
279
|
+
|
|
280
|
+
def start_response(status, headers, exc_info=None):
|
|
281
|
+
self.send_response(int(status.split(' ')[0]))
|
|
282
|
+
for name, value in headers:
|
|
283
|
+
self.send_header(name, value)
|
|
284
|
+
self.end_headers()
|
|
285
|
+
return body.write
|
|
286
|
+
|
|
287
|
+
# Call the application
|
|
288
|
+
response = app(env, start_response)
|
|
289
|
+
try:
|
|
290
|
+
for data in response:
|
|
291
|
+
if data:
|
|
292
|
+
body.write(data)
|
|
293
|
+
finally:
|
|
294
|
+
if hasattr(response, 'close'):
|
|
295
|
+
response.close()
|
|
296
|
+
body = body.getvalue()
|
|
297
|
+
self.wfile.write(body)
|
|
298
|
+
self.wfile.flush()
|
|
299
|
+
else:
|
|
300
|
+
from urllib.parse import urlparse
|
|
301
|
+
from io import BytesIO
|
|
302
|
+
import asyncio
|
|
303
|
+
|
|
304
|
+
app = __vc_module.app
|
|
305
|
+
|
|
306
|
+
class Handler(BaseHandler):
|
|
307
|
+
def handle_request(self):
|
|
308
|
+
# Prepare ASGI scope
|
|
309
|
+
url = urlparse(self.path)
|
|
310
|
+
headers_encoded = []
|
|
311
|
+
for k, v in self.headers.items():
|
|
312
|
+
# Cope with repeated headers in the encoding.
|
|
313
|
+
if isinstance(v, list):
|
|
314
|
+
headers_encoded.append([k.lower().encode(), [i.encode() for i in v]])
|
|
315
|
+
else:
|
|
316
|
+
headers_encoded.append([k.lower().encode(), v.encode()])
|
|
317
|
+
scope = {
|
|
318
|
+
'server': (self.headers.get('host', 'lambda'), self.headers.get('x-forwarded-port', 80)),
|
|
319
|
+
'client': (self.headers.get(
|
|
320
|
+
'x-forwarded-for', self.headers.get(
|
|
321
|
+
'x-real-ip')), 0),
|
|
322
|
+
'scheme': self.headers.get('x-forwarded-proto', 'http'),
|
|
323
|
+
'root_path': '',
|
|
324
|
+
'query_string': url.query.encode(),
|
|
325
|
+
'headers': headers_encoded,
|
|
326
|
+
'type': 'http',
|
|
327
|
+
'http_version': '1.1',
|
|
328
|
+
'method': self.command,
|
|
329
|
+
'path': url.path,
|
|
330
|
+
'raw_path': url.path.encode(),
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if 'content-length' in self.headers:
|
|
334
|
+
content_length = int(self.headers['content-length'])
|
|
335
|
+
body = self.rfile.read(content_length)
|
|
336
|
+
else:
|
|
337
|
+
body = b''
|
|
338
|
+
|
|
339
|
+
if _use_legacy_asyncio:
|
|
340
|
+
loop = asyncio.new_event_loop()
|
|
341
|
+
app_queue = asyncio.Queue(loop=loop)
|
|
342
|
+
else:
|
|
343
|
+
app_queue = asyncio.Queue()
|
|
344
|
+
app_queue.put_nowait({'type': 'http.request', 'body': body, 'more_body': False})
|
|
345
|
+
|
|
346
|
+
# Prepare ASGI receive function
|
|
347
|
+
async def receive():
|
|
348
|
+
message = await app_queue.get()
|
|
349
|
+
return message
|
|
350
|
+
|
|
351
|
+
# Prepare ASGI send function
|
|
352
|
+
response_started = False
|
|
353
|
+
async def send(event):
|
|
354
|
+
nonlocal response_started
|
|
355
|
+
if event['type'] == 'http.response.start':
|
|
356
|
+
self.send_response(event['status'])
|
|
357
|
+
if 'headers' in event:
|
|
358
|
+
for name, value in event['headers']:
|
|
359
|
+
self.send_header(name.decode(), value.decode())
|
|
360
|
+
self.end_headers()
|
|
361
|
+
response_started = True
|
|
362
|
+
elif event['type'] == 'http.response.body':
|
|
363
|
+
self.wfile.write(event['body'])
|
|
364
|
+
if not event.get('more_body', False):
|
|
365
|
+
self.wfile.flush()
|
|
366
|
+
|
|
367
|
+
# Run the ASGI application
|
|
368
|
+
asgi_instance = app(scope, receive, send)
|
|
369
|
+
if _use_legacy_asyncio:
|
|
370
|
+
asgi_task = loop.create_task(asgi_instance)
|
|
371
|
+
loop.run_until_complete(asgi_task)
|
|
372
|
+
else:
|
|
373
|
+
asyncio.run(asgi_instance)
|
|
374
|
+
|
|
375
|
+
if 'Handler' in locals():
|
|
376
|
+
server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
|
|
377
|
+
send_message({
|
|
378
|
+
"type": "server-started",
|
|
379
|
+
"payload": {
|
|
380
|
+
"initDuration": int((time.time() - start_time) * 1000),
|
|
381
|
+
"httpPort": server.server_address[1],
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
server.serve_forever()
|
|
385
|
+
|
|
386
|
+
print('Missing variable `handler` or `app` in file "__VC_HANDLER_ENTRYPOINT".')
|
|
387
|
+
print('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
388
|
+
exit(1)
|
|
389
|
+
|
|
390
|
+
if 'handler' in __vc_variables or 'Handler' in __vc_variables:
|
|
391
|
+
base = __vc_module.handler if ('handler' in __vc_variables) else __vc_module.Handler
|
|
392
|
+
if not issubclass(base, BaseHTTPRequestHandler):
|
|
393
|
+
print('Handler must inherit from BaseHTTPRequestHandler')
|
|
394
|
+
print('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
395
|
+
exit(1)
|
|
396
|
+
|
|
397
|
+
print('using HTTP Handler')
|
|
398
|
+
from http.server import HTTPServer
|
|
399
|
+
import http
|
|
400
|
+
import _thread
|
|
401
|
+
|
|
402
|
+
server = HTTPServer(('127.0.0.1', 0), base)
|
|
403
|
+
port = server.server_address[1]
|
|
404
|
+
|
|
405
|
+
def vc_handler(event, context):
|
|
406
|
+
_thread.start_new_thread(server.handle_request, ())
|
|
407
|
+
|
|
408
|
+
payload = json.loads(event['body'])
|
|
409
|
+
path = payload['path']
|
|
410
|
+
headers = payload['headers']
|
|
411
|
+
method = payload['method']
|
|
412
|
+
encoding = payload.get('encoding')
|
|
413
|
+
body = payload.get('body')
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
(body is not None and len(body) > 0) and
|
|
417
|
+
(encoding is not None and encoding == 'base64')
|
|
418
|
+
):
|
|
419
|
+
body = base64.b64decode(body)
|
|
420
|
+
|
|
421
|
+
request_body = body.encode('utf-8') if isinstance(body, str) else body
|
|
422
|
+
conn = http.client.HTTPConnection('127.0.0.1', port)
|
|
423
|
+
try:
|
|
424
|
+
conn.request(method, path, headers=headers, body=request_body)
|
|
425
|
+
except (http.client.HTTPException, socket.error) as ex:
|
|
426
|
+
print ("Request Error: %s" % ex)
|
|
427
|
+
res = conn.getresponse()
|
|
428
|
+
|
|
429
|
+
return_dict = {
|
|
430
|
+
'statusCode': res.status,
|
|
431
|
+
'headers': format_headers(res.headers),
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
data = res.read()
|
|
435
|
+
|
|
436
|
+
try:
|
|
437
|
+
return_dict['body'] = data.decode('utf-8')
|
|
438
|
+
except UnicodeDecodeError:
|
|
439
|
+
return_dict['body'] = base64.b64encode(data).decode('utf-8')
|
|
440
|
+
return_dict['encoding'] = 'base64'
|
|
441
|
+
|
|
442
|
+
return return_dict
|
|
443
|
+
|
|
444
|
+
elif 'app' in __vc_variables:
|
|
445
|
+
if (
|
|
446
|
+
not inspect.iscoroutinefunction(__vc_module.app) and
|
|
447
|
+
not inspect.iscoroutinefunction(__vc_module.app.__call__)
|
|
448
|
+
):
|
|
449
|
+
print('using Web Server Gateway Interface (WSGI)')
|
|
450
|
+
from io import BytesIO
|
|
451
|
+
from urllib.parse import urlparse
|
|
452
|
+
from werkzeug.datastructures import Headers
|
|
453
|
+
from werkzeug.wrappers import Response
|
|
454
|
+
|
|
455
|
+
string_types = (str,)
|
|
456
|
+
|
|
457
|
+
def to_bytes(x, charset=sys.getdefaultencoding(), errors="strict"):
|
|
458
|
+
if x is None:
|
|
459
|
+
return None
|
|
460
|
+
if isinstance(x, (bytes, bytearray, memoryview)):
|
|
461
|
+
return bytes(x)
|
|
462
|
+
if isinstance(x, str):
|
|
463
|
+
return x.encode(charset, errors)
|
|
464
|
+
raise TypeError("Expected bytes")
|
|
465
|
+
|
|
466
|
+
def wsgi_encoding_dance(s, charset="utf-8", errors="replace"):
|
|
467
|
+
if isinstance(s, str):
|
|
468
|
+
s = s.encode(charset)
|
|
469
|
+
return s.decode("latin1", errors)
|
|
470
|
+
|
|
471
|
+
def vc_handler(event, context):
|
|
472
|
+
payload = json.loads(event['body'])
|
|
473
|
+
|
|
474
|
+
headers = Headers(payload.get('headers', {}))
|
|
475
|
+
|
|
476
|
+
body = payload.get('body', '')
|
|
477
|
+
if body != '':
|
|
478
|
+
if payload.get('encoding') == 'base64':
|
|
479
|
+
body = base64.b64decode(body)
|
|
480
|
+
if isinstance(body, string_types):
|
|
481
|
+
body = to_bytes(body, charset='utf-8')
|
|
482
|
+
|
|
483
|
+
url = urlparse(payload['path'])
|
|
484
|
+
query = url.query
|
|
485
|
+
path = url.path
|
|
486
|
+
|
|
487
|
+
environ = {
|
|
488
|
+
'CONTENT_LENGTH': str(len(body)),
|
|
489
|
+
'CONTENT_TYPE': headers.get('content-type', ''),
|
|
490
|
+
'PATH_INFO': path,
|
|
491
|
+
'QUERY_STRING': query,
|
|
492
|
+
'REMOTE_ADDR': headers.get(
|
|
493
|
+
'x-forwarded-for', headers.get(
|
|
494
|
+
'x-real-ip', payload.get(
|
|
495
|
+
'true-client-ip', ''))),
|
|
496
|
+
'REQUEST_METHOD': payload['method'],
|
|
497
|
+
'SERVER_NAME': headers.get('host', 'lambda'),
|
|
498
|
+
'SERVER_PORT': headers.get('x-forwarded-port', '80'),
|
|
499
|
+
'SERVER_PROTOCOL': 'HTTP/1.1',
|
|
500
|
+
'event': event,
|
|
501
|
+
'context': context,
|
|
502
|
+
'wsgi.errors': sys.stderr,
|
|
503
|
+
'wsgi.input': BytesIO(body),
|
|
504
|
+
'wsgi.multiprocess': False,
|
|
505
|
+
'wsgi.multithread': False,
|
|
506
|
+
'wsgi.run_once': False,
|
|
507
|
+
'wsgi.url_scheme': headers.get('x-forwarded-proto', 'http'),
|
|
508
|
+
'wsgi.version': (1, 0),
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
for key, value in environ.items():
|
|
512
|
+
if isinstance(value, string_types):
|
|
513
|
+
environ[key] = wsgi_encoding_dance(value)
|
|
514
|
+
|
|
515
|
+
for key, value in headers.items():
|
|
516
|
+
key = 'HTTP_' + key.upper().replace('-', '_')
|
|
517
|
+
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
|
|
518
|
+
environ[key] = value
|
|
519
|
+
|
|
520
|
+
response = Response.from_app(__vc_module.app, environ)
|
|
521
|
+
|
|
522
|
+
return_dict = {
|
|
523
|
+
'statusCode': response.status_code,
|
|
524
|
+
'headers': format_headers(response.headers)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if response.data:
|
|
528
|
+
return_dict['body'] = base64.b64encode(response.data).decode('utf-8')
|
|
529
|
+
return_dict['encoding'] = 'base64'
|
|
530
|
+
|
|
531
|
+
return return_dict
|
|
532
|
+
else:
|
|
533
|
+
print('using Asynchronous Server Gateway Interface (ASGI)')
|
|
534
|
+
# Originally authored by Jordan Eremieff and included under MIT license:
|
|
535
|
+
# https://github.com/erm/mangum/blob/b4d21c8f5e304a3e17b88bc9fa345106acc50ad7/mangum/__init__.py
|
|
536
|
+
# https://github.com/erm/mangum/blob/b4d21c8f5e304a3e17b88bc9fa345106acc50ad7/LICENSE
|
|
537
|
+
import asyncio
|
|
538
|
+
import enum
|
|
539
|
+
from urllib.parse import urlparse
|
|
540
|
+
from werkzeug.datastructures import Headers
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
class ASGICycleState(enum.Enum):
|
|
544
|
+
REQUEST = enum.auto()
|
|
545
|
+
RESPONSE = enum.auto()
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class ASGICycle:
|
|
549
|
+
def __init__(self, scope):
|
|
550
|
+
self.scope = scope
|
|
551
|
+
self.body = b''
|
|
552
|
+
self.state = ASGICycleState.REQUEST
|
|
553
|
+
self.app_queue = None
|
|
554
|
+
self.response = {}
|
|
555
|
+
|
|
556
|
+
def __call__(self, app, body):
|
|
557
|
+
"""
|
|
558
|
+
Receives the application and any body included in the request, then builds the
|
|
559
|
+
ASGI instance using the connection scope.
|
|
560
|
+
Runs until the response is completely read from the application.
|
|
561
|
+
"""
|
|
562
|
+
if _use_legacy_asyncio:
|
|
563
|
+
loop = asyncio.new_event_loop()
|
|
564
|
+
self.app_queue = asyncio.Queue(loop=loop)
|
|
565
|
+
else:
|
|
566
|
+
self.app_queue = asyncio.Queue()
|
|
567
|
+
self.put_message({'type': 'http.request', 'body': body, 'more_body': False})
|
|
568
|
+
|
|
569
|
+
asgi_instance = app(self.scope, self.receive, self.send)
|
|
570
|
+
|
|
571
|
+
if _use_legacy_asyncio:
|
|
572
|
+
asgi_task = loop.create_task(asgi_instance)
|
|
573
|
+
loop.run_until_complete(asgi_task)
|
|
574
|
+
else:
|
|
575
|
+
asyncio.run(self.run_asgi_instance(asgi_instance))
|
|
576
|
+
return self.response
|
|
577
|
+
|
|
578
|
+
async def run_asgi_instance(self, asgi_instance):
|
|
579
|
+
await asgi_instance
|
|
580
|
+
|
|
581
|
+
def put_message(self, message):
|
|
582
|
+
self.app_queue.put_nowait(message)
|
|
583
|
+
|
|
584
|
+
async def receive(self):
|
|
585
|
+
"""
|
|
586
|
+
Awaited by the application to receive messages in the queue.
|
|
587
|
+
"""
|
|
588
|
+
message = await self.app_queue.get()
|
|
589
|
+
return message
|
|
590
|
+
|
|
591
|
+
async def send(self, message):
|
|
592
|
+
"""
|
|
593
|
+
Awaited by the application to send messages to the current cycle instance.
|
|
594
|
+
"""
|
|
595
|
+
message_type = message['type']
|
|
596
|
+
|
|
597
|
+
if self.state is ASGICycleState.REQUEST:
|
|
598
|
+
if message_type != 'http.response.start':
|
|
599
|
+
raise RuntimeError(
|
|
600
|
+
f"Expected 'http.response.start', received: {message_type}"
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
status_code = message['status']
|
|
604
|
+
headers = Headers(message.get('headers', []))
|
|
605
|
+
|
|
606
|
+
self.on_request(headers, status_code)
|
|
607
|
+
self.state = ASGICycleState.RESPONSE
|
|
608
|
+
|
|
609
|
+
elif self.state is ASGICycleState.RESPONSE:
|
|
610
|
+
if message_type != 'http.response.body':
|
|
611
|
+
raise RuntimeError(
|
|
612
|
+
f"Expected 'http.response.body', received: {message_type}"
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
body = message.get('body', b'')
|
|
616
|
+
more_body = message.get('more_body', False)
|
|
617
|
+
|
|
618
|
+
# The body must be completely read before returning the response.
|
|
619
|
+
self.body += body
|
|
620
|
+
|
|
621
|
+
if not more_body:
|
|
622
|
+
self.on_response()
|
|
623
|
+
self.put_message({'type': 'http.disconnect'})
|
|
624
|
+
|
|
625
|
+
def on_request(self, headers, status_code):
|
|
626
|
+
self.response['statusCode'] = status_code
|
|
627
|
+
self.response['headers'] = format_headers(headers, decode=True)
|
|
628
|
+
|
|
629
|
+
def on_response(self):
|
|
630
|
+
if self.body:
|
|
631
|
+
self.response['body'] = base64.b64encode(self.body).decode('utf-8')
|
|
632
|
+
self.response['encoding'] = 'base64'
|
|
633
|
+
|
|
634
|
+
def vc_handler(event, context):
|
|
635
|
+
payload = json.loads(event['body'])
|
|
636
|
+
|
|
637
|
+
headers = payload.get('headers', {})
|
|
638
|
+
|
|
639
|
+
body = payload.get('body', b'')
|
|
640
|
+
if payload.get('encoding') == 'base64':
|
|
641
|
+
body = base64.b64decode(body)
|
|
642
|
+
elif not isinstance(body, bytes):
|
|
643
|
+
body = body.encode()
|
|
644
|
+
|
|
645
|
+
url = urlparse(payload['path'])
|
|
646
|
+
query = url.query.encode()
|
|
647
|
+
path = url.path
|
|
648
|
+
|
|
649
|
+
headers_encoded = []
|
|
650
|
+
for k, v in headers.items():
|
|
651
|
+
# Cope with repeated headers in the encoding.
|
|
652
|
+
if isinstance(v, list):
|
|
653
|
+
headers_encoded.append([k.lower().encode(), [i.encode() for i in v]])
|
|
654
|
+
else:
|
|
655
|
+
headers_encoded.append([k.lower().encode(), v.encode()])
|
|
656
|
+
|
|
657
|
+
scope = {
|
|
658
|
+
'server': (headers.get('host', 'lambda'), headers.get('x-forwarded-port', 80)),
|
|
659
|
+
'client': (headers.get(
|
|
660
|
+
'x-forwarded-for', headers.get(
|
|
661
|
+
'x-real-ip', payload.get(
|
|
662
|
+
'true-client-ip', ''))), 0),
|
|
663
|
+
'scheme': headers.get('x-forwarded-proto', 'http'),
|
|
664
|
+
'root_path': '',
|
|
665
|
+
'query_string': query,
|
|
666
|
+
'headers': headers_encoded,
|
|
667
|
+
'type': 'http',
|
|
668
|
+
'http_version': '1.1',
|
|
669
|
+
'method': payload['method'],
|
|
670
|
+
'path': path,
|
|
671
|
+
'raw_path': path.encode(),
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
asgi_cycle = ASGICycle(scope)
|
|
675
|
+
response = asgi_cycle(__vc_module.app, body)
|
|
676
|
+
return response
|
|
677
|
+
|
|
678
|
+
else:
|
|
679
|
+
print('Missing variable `handler` or `app` in file "__VC_HANDLER_ENTRYPOINT".')
|
|
680
|
+
print('See the docs: https://vercel.com/docs/functions/serverless-functions/runtimes/python')
|
|
681
|
+
exit(1)
|