Skip to content

Commit 540fb88

Browse files
authored
feat: add patches from init error and logging (#219)
* feat: add patches from init error and logging * fix: format * fix: format
1 parent 043a5ed commit 540fb88

3 files changed

Lines changed: 176 additions & 41 deletions

File tree

include/aws/lambda-runtime/runtime.h

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,60 @@ struct invocation_request {
7777
inline std::chrono::milliseconds get_time_remaining() const;
7878
};
7979

80-
class invocation_response {
81-
private:
80+
class runtime_response {
81+
protected:
8282
/**
83-
* The output of the function which is sent to the lambda caller.
83+
* The response payload from the runtime.
8484
*/
8585
std::string m_payload;
8686

8787
/**
8888
* The MIME type of the payload.
89-
* This is always set to 'application/json' in unsuccessful invocations.
9089
*/
9190
std::string m_content_type;
9291

9392
/**
94-
* Flag to distinguish if the contents are for successful or unsuccessful invocations.
93+
* The serialized XRay response header.
9594
*/
96-
bool m_success;
95+
std::string m_xray_response;
9796

9897
/**
99-
* The serialized XRay response header.
98+
* Instantiate an empty response.
10099
*/
101-
std::string m_xray_response;
100+
runtime_response() = default;
101+
102+
public:
103+
/* Create a runtime response with the given payload, content type and xray response. This can be used for
104+
* constructing an initialization error response. For invocation success and failure response, see
105+
* invocation_response.
106+
*/
107+
runtime_response(std::string const& payload, std::string const& content_type, std::string const& xray_response)
108+
: m_payload(payload), m_content_type(content_type), m_xray_response(xray_response)
109+
{
110+
}
111+
112+
/**
113+
* Get the payload string. The string is assumed to be UTF-8 encoded.
114+
*/
115+
std::string const& get_payload() const { return m_payload; }
116+
117+
/**
118+
* Get the MIME type of the payload.
119+
*/
120+
std::string const& get_content_type() const { return m_content_type; }
121+
122+
/**
123+
* Get the XRay response string. The string is assumed to be UTF-8 encoded.
124+
*/
125+
std::string const& get_xray_response() const { return m_xray_response; }
126+
};
127+
128+
class invocation_response : public runtime_response {
129+
private:
130+
/**
131+
* Flag to distinguish if the contents are for successful or unsuccessful invocations.
132+
*/
133+
bool m_success;
102134

103135
/**
104136
* Instantiate an empty response. Used by the static functions 'success' and 'failure' to create a populated
@@ -113,7 +145,7 @@ class invocation_response {
113145
// constructor should be used instead.
114146
// Note: adding an overload to invocation_response::failure is not feasible since the parameter types are the same.
115147
invocation_response(std::string const& payload, std::string const& content_type, bool success)
116-
: m_payload(payload), m_content_type(content_type), m_success(success)
148+
: runtime_response(payload, content_type, ""), m_success(success)
117149
{
118150
}
119151

@@ -122,7 +154,7 @@ class invocation_response {
122154
std::string const& content_type,
123155
bool success,
124156
std::string const& xray_response)
125-
: m_payload(payload), m_content_type(content_type), m_success(success), m_xray_response(xray_response)
157+
: runtime_response(payload, content_type, xray_response), m_success(success)
126158
{
127159
}
128160

@@ -142,25 +174,10 @@ class invocation_response {
142174
std::string const& error_type,
143175
std::string const& xray_response);
144176

145-
/**
146-
* Get the MIME type of the payload.
147-
*/
148-
std::string const& get_content_type() const { return m_content_type; }
149-
150-
/**
151-
* Get the payload string. The string is assumed to be UTF-8 encoded.
152-
*/
153-
std::string const& get_payload() const { return m_payload; }
154-
155177
/**
156178
* Returns true if the payload and content-type are set. Returns false if the error message and error types are set.
157179
*/
158180
bool is_success() const { return m_success; }
159-
160-
/**
161-
* Get the XRay response string. The string isassumed to be UTF-8 encoded.
162-
*/
163-
std::string const& get_xray_response() const { return m_xray_response; }
164181
};
165182

166183
struct no_result {};
@@ -189,13 +206,19 @@ class runtime {
189206
*/
190207
post_outcome post_failure(std::string const& request_id, invocation_response const& handler_response);
191208

209+
/**
210+
* Tells lambda that the runtime has failed during initialization.
211+
*/
212+
post_outcome post_init_error(runtime_response const& init_error_response);
213+
192214
private:
193215
void set_curl_next_options();
194216
static void set_curl_post_result_options();
195217
post_outcome do_post(
196218
std::string const& url,
197-
std::string const& request_id,
198-
invocation_response const& handler_response);
219+
std::string const& content_type,
220+
std::string const& payload,
221+
std::string const& xray_response);
199222
std::string const m_user_agent_header;
200223
std::array<std::string const, 3> const m_endpoints;
201224
};

src/runtime.cpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -338,40 +338,50 @@ runtime::next_outcome runtime::get_next()
338338
runtime::post_outcome runtime::post_success(std::string const& request_id, invocation_response const& handler_response)
339339
{
340340
std::string const url = m_endpoints[Endpoints::RESULT] + request_id + "/response";
341-
return do_post(url, request_id, handler_response);
341+
return do_post(
342+
url, handler_response.get_content_type(), handler_response.get_payload(), handler_response.get_xray_response());
342343
}
343344

344345
runtime::post_outcome runtime::post_failure(std::string const& request_id, invocation_response const& handler_response)
345346
{
346347
std::string const url = m_endpoints[Endpoints::RESULT] + request_id + "/error";
347-
return do_post(url, request_id, handler_response);
348+
return do_post(
349+
url, handler_response.get_content_type(), handler_response.get_payload(), handler_response.get_xray_response());
350+
}
351+
352+
runtime::post_outcome runtime::post_init_error(runtime_response const& init_error_response)
353+
{
354+
std::string const url = m_endpoints[Endpoints::INIT];
355+
return do_post(
356+
url,
357+
init_error_response.get_content_type(),
358+
init_error_response.get_payload(),
359+
init_error_response.get_xray_response());
348360
}
349361

350362
runtime::post_outcome runtime::do_post(
351363
std::string const& url,
352-
std::string const& request_id,
353-
invocation_response const& handler_response)
364+
std::string const& content_type,
365+
std::string const& payload,
366+
std::string const& xray_response)
354367
{
355368
set_curl_post_result_options();
356369
curl_easy_setopt(lambda_runtime::m_curl_handle, CURLOPT_URL, url.c_str());
357370
logging::log_info(LOG_TAG, "Making request to %s", url.c_str());
358371

359372
curl_slist* headers = nullptr;
360-
if (handler_response.get_content_type().empty()) {
373+
if (content_type.empty()) {
361374
headers = curl_slist_append(headers, "content-type: text/html");
362375
}
363376
else {
364-
headers = curl_slist_append(headers, ("content-type: " + handler_response.get_content_type()).c_str());
377+
headers = curl_slist_append(headers, ("content-type: " + content_type).c_str());
365378
}
366379

367-
if (!handler_response.get_xray_response().empty()) {
368-
headers = curl_slist_append(
369-
headers, ("lambda-runtime-function-xray-error-cause: " + handler_response.get_xray_response()).c_str());
370-
}
380+
headers = curl_slist_append(headers, ("lambda-runtime-function-xray-error-cause: " + xray_response).c_str());
371381
headers = curl_slist_append(headers, "Expect:");
372382
headers = curl_slist_append(headers, "transfer-encoding:");
373383
headers = curl_slist_append(headers, m_user_agent_header.c_str());
374-
auto const& payload = handler_response.get_payload();
384+
375385
logging::log_debug(
376386
LOG_TAG, "calculating content length... %s", ("content-length: " + std::to_string(payload.length())).c_str());
377387
headers = curl_slist_append(headers, ("content-length: " + std::to_string(payload.length())).c_str());
@@ -388,10 +398,10 @@ runtime::post_outcome runtime::do_post(
388398
if (curl_code != CURLE_OK) {
389399
logging::log_debug(
390400
LOG_TAG,
391-
"CURL returned error code %d - %s, for invocation %s",
401+
"CURL returned error code %d - %s, when calling %s",
392402
curl_code,
393403
curl_easy_strerror(curl_code),
394-
request_id.c_str());
404+
url.c_str());
395405
return aws::http::response_code::REQUEST_NOT_MADE;
396406
}
397407

@@ -400,7 +410,10 @@ runtime::post_outcome runtime::do_post(
400410

401411
if (!is_success(aws::http::response_code(http_response_code))) {
402412
logging::log_error(
403-
LOG_TAG, "Failed to post handler success response. Http response code: %ld.", http_response_code);
413+
LOG_TAG,
414+
"Failed to post handler success response. Http response code: %ld. %s",
415+
http_response_code,
416+
resp.get_body().c_str());
404417
return aws::http::response_code(http_response_code);
405418
}
406419

tests/unit/unit_tests.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,105 @@ TEST(InvocationResponseTest, constructor_based_failure)
103103
EXPECT_EQ(R"({"custom":"error"})", resp.get_payload());
104104
}
105105

106+
// --- runtime_response tests ---
107+
108+
TEST(RuntimeResponseTest, constructor_sets_all_fields)
109+
{
110+
runtime_response resp("payload data", "application/json", "xray-trace-123");
111+
EXPECT_EQ("payload data", resp.get_payload());
112+
EXPECT_EQ("application/json", resp.get_content_type());
113+
EXPECT_EQ("xray-trace-123", resp.get_xray_response());
114+
}
115+
116+
TEST(RuntimeResponseTest, constructor_with_empty_fields)
117+
{
118+
runtime_response resp("", "", "");
119+
EXPECT_EQ("", resp.get_payload());
120+
EXPECT_EQ("", resp.get_content_type());
121+
EXPECT_EQ("", resp.get_xray_response());
122+
}
123+
124+
TEST(RuntimeResponseTest, constructor_with_empty_xray)
125+
{
126+
runtime_response resp("error body", "application/json", "");
127+
EXPECT_EQ("error body", resp.get_payload());
128+
EXPECT_EQ("application/json", resp.get_content_type());
129+
EXPECT_EQ("", resp.get_xray_response());
130+
}
131+
132+
TEST(RuntimeResponseTest, large_payload)
133+
{
134+
std::string large(10000, 'x');
135+
runtime_response resp(large, "text/plain", "");
136+
EXPECT_EQ(10000u, resp.get_payload().size());
137+
EXPECT_EQ(large, resp.get_payload());
138+
}
139+
140+
TEST(RuntimeResponseTest, can_be_used_for_init_error)
141+
{
142+
runtime_response init_err(
143+
R"({"errorMessage":"module not found","errorType":"ImportError"})", "application/json", "xray-cause-data");
144+
EXPECT_EQ("application/json", init_err.get_content_type());
145+
EXPECT_NE(std::string::npos, init_err.get_payload().find("module not found"));
146+
EXPECT_EQ("xray-cause-data", init_err.get_xray_response());
147+
}
148+
149+
// --- invocation_response inheritance tests ---
150+
151+
TEST(InvocationResponseInheritanceTest, is_a_runtime_response)
152+
{
153+
invocation_response resp("payload", "text/plain", true, "xray");
154+
runtime_response const& base = resp;
155+
EXPECT_EQ("payload", base.get_payload());
156+
EXPECT_EQ("text/plain", base.get_content_type());
157+
EXPECT_EQ("xray", base.get_xray_response());
158+
}
159+
160+
TEST(InvocationResponseInheritanceTest, three_arg_constructor_has_empty_xray)
161+
{
162+
invocation_response resp("data", "text/html", true);
163+
EXPECT_EQ("data", resp.get_payload());
164+
EXPECT_EQ("text/html", resp.get_content_type());
165+
EXPECT_EQ("", resp.get_xray_response());
166+
EXPECT_TRUE(resp.is_success());
167+
}
168+
169+
TEST(InvocationResponseInheritanceTest, four_arg_constructor_preserves_xray)
170+
{
171+
invocation_response resp("err", "application/json", false, "xray-data");
172+
EXPECT_EQ("err", resp.get_payload());
173+
EXPECT_EQ("application/json", resp.get_content_type());
174+
EXPECT_EQ("xray-data", resp.get_xray_response());
175+
EXPECT_FALSE(resp.is_success());
176+
}
177+
178+
TEST(InvocationResponseInheritanceTest, failure_with_xray_response)
179+
{
180+
auto resp = invocation_response::failure("err msg", "ErrType", "xray-cause");
181+
EXPECT_FALSE(resp.is_success());
182+
EXPECT_EQ("application/json", resp.get_content_type());
183+
EXPECT_EQ("xray-cause", resp.get_xray_response());
184+
EXPECT_NE(std::string::npos, resp.get_payload().find("err msg"));
185+
}
186+
187+
TEST(InvocationResponseInheritanceTest, success_has_empty_xray)
188+
{
189+
auto resp = invocation_response::success("ok", "text/plain");
190+
EXPECT_TRUE(resp.is_success());
191+
EXPECT_EQ("", resp.get_xray_response());
192+
}
193+
194+
TEST(InvocationResponseInheritanceTest, can_pass_as_runtime_response_const_ref)
195+
{
196+
invocation_response resp("body", "application/json", false, "xray-123");
197+
auto check = [](runtime_response const& r) {
198+
EXPECT_EQ("body", r.get_payload());
199+
EXPECT_EQ("application/json", r.get_content_type());
200+
EXPECT_EQ("xray-123", r.get_xray_response());
201+
};
202+
check(resp);
203+
}
204+
106205
// --- http::response tests ---
107206

108207
TEST(HttpResponseTest, add_and_retrieve_header)

0 commit comments

Comments
 (0)