Skip to content

Commit a42ea43

Browse files
rdallmandenismakogon
authored andcommitted
attempt fixing content type issue (#83)
buggy headers look like: ``` {Fn-Http-Status=[200], Opc-Request-Id=[/01DBJASGQY1BT1AHGZJ000PC8D/01DBJASGQY1BT1AHGZJ000PC8E], Connection=[keep-alive], Fn-Call-Id=[01DBJASGRW1BT1AHGZJ000PC8F], Fn-Http-H-Content-Type=[application/json], Content-Length=[462], Date=[Thu, 23 May 2019 12:31:35 GMT], Content-Type=[None]} ``` but should have `Content-Type: [application/json]` and not `Content-Type:[None]` nor `Fn-Http-H-Content-Type` * removes content type from encap headers, which was just ripping content type out of the headers bucket anyway - just leave in the headers bucket, since it wasn't removing it from the header bucket for usage, it was a confusing contract. * actually use the gateway headers if Fn-Intent=httprequest ("the contract"), we weren't checking this at all or stripping gateway headers when we need to * only encap the headers for gateway responses instead of for both invoke and gateway responses * no more default of `Content-Type: text/plain` * fix up test fixtures we should probably have separate types of contexts like things were before this, but we should maybe use inheritance - I tried this, but didn't get it working, though now I think it was a separate issue why I didn't get it working. anyway, this fixes the bug(s) in invoke. gateway headers were not being used nor well tested anyway (well, because it was defaulting to them confusingly), so we could patch later.
1 parent da2184f commit a42ea43

8 files changed

Lines changed: 101 additions & 118 deletions

File tree

fdk/async_http/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ def serve(
186186

187187
# Register signals for graceful termination
188188
if register_sys_signals:
189-
_singals = (SIGTERM,) if run_multiple else (SIGINT, SIGTERM)
190-
for _signal in _singals:
189+
_signals = (SIGTERM,) if run_multiple else (SIGINT, SIGTERM)
190+
for _signal in _signals:
191191
try:
192192
loop.add_signal_handler(_signal, loop.stop)
193193
except NotImplementedError:

fdk/constants.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
ASYNC_IO_READ_BUFFER = 65536
1818
DEFAULT_DEADLINE = 30
1919
HTTPSTREAM = "http-stream"
20+
INTENT_HTTP_REQUEST = "httprequest"
2021
FN_FORMAT = "FN_FORMAT"
2122
FN_LISTENER = "FN_LISTENER"
23+
FN_INTENT = "Fn-Intent"
2224
FN_HTTP_PREFIX = "Fn-Http-H-"
2325
FN_HTTP_STATUS = "Fn-Http-Status"
2426
FN_DEADLINE = "fn-deadline"
@@ -27,7 +29,7 @@
2729
FN_HTTP_METHOD = "fn-http-method"
2830
FN_APP_ID = "FN_APP_ID"
2931
FN_ID = "FN_FN_ID"
30-
CONTENT_TYPE = "content-type"
32+
CONTENT_TYPE = "Content-Type"
3133
CONTENT_LENGTH = "Content-Length"
3234
FN_ENFORCED_RESPONSE_CODES = [200, 502, 504]
3335
FN_DEFAULT_RESPONSE_CODE = 200

fdk/context.py

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,6 @@
2020
from fdk import headers as hs
2121

2222

23-
def set_response_headers(
24-
current_headers: dict, new_headers: dict,
25-
status_code: int, content_type: str=None):
26-
"""
27-
Sets Fn-readable headers
28-
:param current_headers: current HTTP headers
29-
:type current_headers: dict
30-
:param new_headers: new HTTP headers to add
31-
:type new_headers: dict
32-
:param status_code: HTTP status code
33-
:type status_code: int
34-
:param content_type: response content type
35-
:type content_type: str
36-
:return: updated headers
37-
:rtype: dict
38-
"""
39-
new_headers = hs.encap_headers(
40-
new_headers,
41-
status=status_code,
42-
content_type=content_type
43-
)
44-
for k, v in new_headers.items():
45-
current_headers[k] = v
46-
47-
return current_headers
48-
49-
5023
class InvokeContext(object):
5124

5225
def __init__(self, app_id, fn_id, call_id,
@@ -90,6 +63,9 @@ def __init__(self, app_id, fn_id, call_id,
9063
self.__response_headers = {}
9164
self.__fn_format = fn_format
9265

66+
if self.__is_gateway():
67+
self.__headers = hs.decap_headers(self.__headers)
68+
9369
def AppID(self):
9470
return self.__app_id
9571

@@ -115,48 +91,26 @@ def Deadline(self):
11591
return now.isoformat()
11692
return self.__deadline
11793

118-
def SetResponseHeaders(self, headers, status_code, content_type=None):
119-
self.__response_headers = set_response_headers(
120-
self.GetResponseHeaders(), headers, status_code,
121-
content_type=content_type)
94+
def SetResponseHeaders(self, headers, status_code):
95+
if self.__is_gateway():
96+
headers = hs.encap_headers(headers, status=status_code)
97+
98+
for k, v in headers.items():
99+
self.__response_headers[k] = v
122100

123101
def GetResponseHeaders(self):
124102
return self.__response_headers
125103

126-
def HTTPContext(self):
127-
return HTTPGatewayContext(self)
128-
129-
130-
class HTTPGatewayContext(object):
131-
def __init__(self, invoke_context: InvokeContext):
132-
"""
133-
Creates an instance of an HTTP context
134-
:param invoke_context: invoke context:
135-
:type invoke_context: fdk.context.InvokeContext
136-
"""
137-
self.__headers = hs.decap_headers(invoke_context.Headers())
138-
self.__invoke_context = invoke_context
139-
self.__response_headers = {}
140-
141104
def RequestURL(self):
142105
return self.__invoke_context._request_url
143106

144107
def Method(self):
145108
return self.__invoke_context._method
146109

147-
def Headers(self):
148-
return self.__headers
149-
150-
def SetResponseHeaders(self, headers, status_code, content_type=None):
151-
self.__response_headers = set_response_headers(
152-
self.GetResponseHeaders(), headers, status_code,
153-
content_type=content_type)
154-
155-
def GetResponseHeaders(self):
156-
return self.__response_headers
157-
158-
def Format(self):
159-
return self.__invoke_context.Format()
110+
def __is_gateway(self):
111+
return (constants.FN_INTENT in self.__headers and
112+
self.__headers.get(constants.FN_INTENT) ==
113+
constants.INTENT_HTTP_REQUEST)
160114

161115

162116
def context_from_format(format_def: str, **kwargs) -> (

fdk/fixtures.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,24 @@
1515
import datetime as dt
1616

1717
from fdk import constants
18-
from fdk import headers as hs
1918
from fdk import runner
19+
from fdk import headers as hs
2020

2121

2222
async def process_response(fn_call_coro):
23-
new_headers = {}
2423
resp = await fn_call_coro
2524
response_data = resp.body()
2625
response_status = resp.status()
2726
response_headers = resp.context().GetResponseHeaders()
28-
for k, v in response_headers.items():
29-
new_headers.update({
30-
k.lstrip(constants.FN_HTTP_PREFIX): v
31-
})
3227
print(response_headers)
33-
resp_headers = hs.decap_headers(response_headers)
34-
del resp_headers[constants.FN_HTTP_STATUS]
3528

36-
return response_data, response_status, resp_headers
29+
return response_data, response_status, response_headers
3730

3831

3932
class fake_request(object):
4033

41-
def __init__(self):
42-
self.headers = setup_headers()
34+
def __init__(self, gateway=False):
35+
self.headers = setup_headers(gateway=gateway)
4336
self.body = b''
4437

4538

@@ -53,35 +46,43 @@ def handler(self):
5346

5447

5548
def setup_headers(deadline=None, headers=None,
56-
request_url="/", method="POST"):
49+
request_url="/", method="POST", gateway=False):
5750
new_headers = {}
58-
if headers is not None:
51+
52+
if gateway:
53+
new_headers = hs.encap_headers(headers)
54+
new_headers.update({
55+
constants.FN_INTENT: constants.INTENT_HTTP_REQUEST,
56+
constants.FN_HTTP_REQUEST_URL: request_url,
57+
constants.FN_HTTP_METHOD: method,
58+
})
59+
elif headers is not None:
5960
for k, v in headers.items():
60-
new_headers.update({constants.FN_HTTP_PREFIX + k: v})
61+
new_headers.update({k: v})
6162

6263
if deadline is None:
6364
now = dt.datetime.now(dt.timezone.utc).astimezone()
6465
now += dt.timedelta(0, float(constants.DEFAULT_DEADLINE))
6566
deadline = now.isoformat()
6667

67-
new_headers.update({
68-
constants.FN_DEADLINE: deadline,
69-
constants.FN_HTTP_REQUEST_URL: request_url,
70-
constants.FN_HTTP_METHOD: method,
71-
})
68+
new_headers.update({constants.FN_DEADLINE: deadline})
7269
return new_headers
7370

7471

7572
async def setup_fn_call(
7673
handle_func, request_url="/",
7774
method="POST", headers=None,
78-
content=None, deadline=None):
75+
content=None, deadline=None,
76+
gateway=False):
7977

8078
new_headers = setup_headers(
8179
deadline=deadline, headers=headers,
82-
method=method, request_url=request_url
80+
method=method, request_url=request_url,
81+
gateway=gateway
8382
)
8483

84+
# don't decap headers, so we can test them
85+
# (just like they come out of fdk)
8586
return process_response(runner.handle_request(
8687
code(handle_func), constants.HTTPSTREAM,
8788
headers=new_headers, data=content,

fdk/headers.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,25 @@
1717

1818
def decap_headers(hdsr):
1919
ctx_headers = {}
20-
for k, v in hdsr.items():
21-
if k.startswith(constants.FN_HTTP_PREFIX):
22-
ctx_headers[k.lstrip(constants.FN_HTTP_PREFIX)] = v
23-
else:
24-
ctx_headers[k] = v
20+
if hdsr is not None:
21+
for k, v in hdsr.items():
22+
if k.startswith(constants.FN_HTTP_PREFIX):
23+
ctx_headers[k.lstrip(constants.FN_HTTP_PREFIX)] = v
24+
else:
25+
ctx_headers[k] = v
2526
return ctx_headers
2627

2728

28-
def encap_headers(headers, status=None, content_type=None):
29+
def encap_headers(headers, status=None):
2930
new_headers = {}
30-
for k, v in headers.items():
31-
if (not k.startswith(constants.CONTENT_TYPE) and
32-
not k.startswith(constants.CONTENT_TYPE)):
33-
new_headers[constants.FN_HTTP_PREFIX + k] = v
31+
if headers is not None:
32+
for k, v in headers.items():
33+
if (k == constants.CONTENT_TYPE or
34+
k.startswith(constants.FN_HTTP_PREFIX)):
35+
new_headers[k] = v
36+
else:
37+
new_headers[constants.FN_HTTP_PREFIX + k] = v
3438

3539
if status is not None:
3640
new_headers[constants.FN_HTTP_STATUS] = str(status)
37-
if content_type is not None:
38-
new_headers[constants.CONTENT_TYPE] = content_type
3941
return new_headers

fdk/response.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# under the License.
1414

1515
from fdk import context
16-
from fdk import constants
1716

1817

1918
class Response(object):
@@ -38,11 +37,7 @@ def __init__(self, ctx: context.InvokeContext,
3837
self.response_data = response_data if response_data else ""
3938
if headers is None:
4039
headers = {}
41-
ctx.SetResponseHeaders(
42-
headers, status_code,
43-
content_type=headers.get(
44-
constants.CONTENT_TYPE, "text/plain")
45-
)
40+
ctx.SetResponseHeaders(headers, status_code)
4641
self.ctx = ctx
4742

4843
def status(self):

fdk/tests/funcs.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,15 @@ def dummy_func(ctx, data=None):
5151

5252

5353
def encaped_header(ctx, **kwargs):
54-
httpctx = ctx.HTTPContext()
55-
hs = httpctx.Headers()
56-
v = hs.get("custom-header-maybe")
5754
return response.Response(
58-
httpctx, response_data="OK", status_code=200,
59-
headers={"Content-Type": "text/plain",
60-
"custom-header-maybe": v})
55+
ctx, response_data="OK", status_code=200,
56+
headers=ctx.Headers())
6157

6258

6359
def content_type(ctx, data=None):
6460
return response.Response(
6561
ctx, response_data="OK", status_code=200,
66-
headers={"Content-Type": "text/plain"})
62+
headers={"Content-Type": "application/json"})
6763

6864

6965
def custom_response(ctx, data=None):
@@ -118,12 +114,11 @@ def verify_request_headers(ctx, **kwargs):
118114

119115

120116
def access_request_url(ctx, **kwargs):
121-
httpctx = ctx.HTTPContext()
122-
hs = httpctx.Headers()
123-
method = httpctx.Method()
117+
hs = ctx.Headers()
118+
method = ctx.Method()
124119
request_url = hs.get("Fn-Http-Request-Url")
125120
return response.Response(
126-
httpctx, response_data="OK", headers={
121+
ctx, response_data="OK", headers={
127122
"Response-Request-URL": request_url,
128123
"Request-Method": method,
129124
}

0 commit comments

Comments
 (0)