-
Notifications
You must be signed in to change notification settings - Fork 68
/
Copy pathhttp.py
158 lines (129 loc) · 5.54 KB
/
http.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import logging
import sys
import typing
from http.cookies import SimpleCookie
from azure.functions import _abc as azf_abc
from azure.functions import _json as json
from azure.functions import _http as azf_http
from . import meta
from ._thirdparty.werkzeug.datastructures import Headers
class HttpRequest(azf_http.HttpRequest):
"""An HTTP request object."""
__body_bytes: typing.Optional[bytes]
__body_str: typing.Optional[str]
def __init__(self,
method: str,
url: str, *,
headers: typing.Mapping[str, str],
params: typing.Mapping[str, str],
route_params: typing.Mapping[str, str],
body_type: str,
body: typing.Union[str, bytes]) -> None:
body_str: typing.Optional[str] = None
body_bytes: typing.Optional[bytes] = None
if isinstance(body, str):
body_str = body
body_bytes = body_str.encode('utf-8')
elif isinstance(body, bytes):
body_bytes = body
else:
raise TypeError(
f'unexpected HTTP request body type: {type(body).__name__}')
super().__init__(method=method, url=url, headers=headers,
params=params, route_params=route_params,
body=body_bytes)
self.__body_type = body_type
self.__body_str = body_str
self.__body_bytes = body_bytes
def get_body(self) -> bytes:
if self.__body_bytes is None:
assert self.__body_str is not None
self.__body_bytes = self.__body_str.encode('utf-8')
return self.__body_bytes
def get_json(self) -> typing.Any:
if self.__body_type in ('json', 'string'):
assert self.__body_str is not None
return json.loads(self.__body_str)
elif self.__body_bytes is not None:
try:
return json.loads(self.__body_bytes.decode('utf-8'))
except ValueError as e:
raise ValueError(
'HTTP request does not contain valid JSON data') from e
else:
raise ValueError(
'Request body cannot be empty in JSON deserialization')
class HttpResponseConverter(meta.OutConverter, binding='http'):
@classmethod
def check_output_type_annotation(cls, pytype: type) -> bool:
return issubclass(pytype, (azf_abc.HttpResponse, str))
@classmethod
def encode(cls, obj: typing.Any, *,
expected_type: typing.Optional[type]) -> meta.Datum:
if isinstance(obj, str):
return meta.Datum(type='string', value=obj)
if isinstance(obj, azf_abc.HttpResponse):
status = obj.status_code
headers: Headers = obj.headers
if 'content-type' not in headers:
if obj.mimetype.startswith('text/'):
ct = f'{obj.mimetype}; charset={obj.charset}'
else:
ct = f'{obj.mimetype}'
headers['content-type'] = ct
body = obj.get_body()
if body is not None:
datum_body = meta.Datum(type='bytes', value=body)
else:
datum_body = meta.Datum(type='bytes', value=b'')
cookies = None
if sys.version_info.major == 3 and sys.version_info.minor <= 7:
# SimpleCookie api in http.cookies - Python Standard Library
# is not supporting 'samesite' in cookie attribute in python
# 3.7 or below and would cause cookie parsing error
# https://docs.python.org/3/library/http.cookies.html
# ?msclkid=d78849ddcd7311ecadd81f2f51d08b8e
logging.warning(
"Setting multiple 'Set-Cookie' response headers is not "
"supported in Azure Python Function with python version "
"3.7, please upgrade to python 3.8 or above.")
else:
if "Set-Cookie" in headers:
cookies = [SimpleCookie(cookie) for cookie in
headers.get_all('Set-Cookie')]
headers.pop("Set-Cookie")
return meta.Datum(
type='http',
value=dict(
status_code=meta.Datum(type='string', value=str(status)),
headers={
n: meta.Datum(type='string', value=h)
for n, h in headers.items()
},
cookies=cookies,
body=datum_body,
)
)
raise NotImplementedError
class HttpRequestConverter(meta.InConverter,
binding='httpTrigger', trigger=True):
@classmethod
def check_input_type_annotation(cls, pytype: type) -> bool:
return issubclass(pytype, azf_abc.HttpRequest)
@classmethod
def decode(cls, data: meta.Datum, *,
trigger_metadata) -> typing.Any:
if data.type != 'http':
raise NotImplementedError
val = data.value
return HttpRequest(
method=val['method'].value,
url=val['url'].value,
headers={n: v.value for n, v in val['headers'].items()},
params={n: v.value for n, v in val['query'].items()},
route_params={n: v.value for n, v in val['params'].items()},
body_type=val['body'].type,
body=val['body'].value,
)