xref: /aosp_15_r20/external/aws-crt-java/src/native/http_request_utils.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include "http_request_utils.h"
7 
8 #include "crt.h"
9 #include "java_class_ids.h"
10 
11 #include <aws/common/byte_order.h>
12 #include <aws/http/http.h>
13 #include <aws/http/request_response.h>
14 #include <aws/io/stream.h>
15 
16 #if _MSC_VER
17 #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
18 #endif
19 
20 struct aws_http_request_body_stream_impl {
21     struct aws_input_stream base;
22     struct aws_allocator *allocator;
23     JavaVM *jvm;
24     jobject http_request_body_stream;
25     bool body_done;
26     bool is_valid;
27 };
28 
s_aws_input_stream_seek(struct aws_input_stream * stream,int64_t offset,enum aws_stream_seek_basis basis)29 static int s_aws_input_stream_seek(struct aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis) {
30     struct aws_http_request_body_stream_impl *impl =
31         AWS_CONTAINER_OF(stream, struct aws_http_request_body_stream_impl, base);
32 
33     if (!impl->is_valid) {
34         return aws_raise_error(AWS_ERROR_HTTP_INVALID_BODY_STREAM);
35     }
36 
37     int result = AWS_OP_SUCCESS;
38     if (impl->http_request_body_stream != NULL) {
39         if (basis != AWS_SSB_BEGIN || offset != 0) {
40             return AWS_OP_ERR;
41         }
42 
43         /********** JNI ENV ACQUIRE **********/
44         JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
45         if (env == NULL) {
46             /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
47             return AWS_OP_ERR;
48         }
49 
50         if (!(*env)->CallBooleanMethod(
51                 env, impl->http_request_body_stream, http_request_body_stream_properties.reset_position)) {
52             result = aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
53         }
54 
55         if (aws_jni_check_and_clear_exception(env)) {
56             result = aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
57         }
58 
59         aws_jni_release_thread_env(impl->jvm, env);
60         /********** JNI ENV RELEASE **********/
61     }
62 
63     if (result == AWS_OP_SUCCESS) {
64         impl->body_done = false;
65     }
66 
67     return result;
68 }
69 
s_aws_input_stream_read(struct aws_input_stream * stream,struct aws_byte_buf * dest)70 static int s_aws_input_stream_read(struct aws_input_stream *stream, struct aws_byte_buf *dest) {
71     struct aws_http_request_body_stream_impl *impl =
72         AWS_CONTAINER_OF(stream, struct aws_http_request_body_stream_impl, base);
73 
74     if (!impl->is_valid) {
75         return aws_raise_error(AWS_ERROR_HTTP_INVALID_BODY_STREAM);
76     }
77 
78     if (impl->http_request_body_stream == NULL) {
79         impl->body_done = true;
80         return AWS_OP_SUCCESS;
81     }
82 
83     if (impl->body_done) {
84         return AWS_OP_SUCCESS;
85     }
86 
87     /********** JNI ENV ACQUIRE **********/
88     JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
89     if (env == NULL) {
90         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
91         return AWS_OP_ERR;
92     }
93 
94     size_t out_remaining = dest->capacity - dest->len;
95 
96     jobject direct_buffer = aws_jni_direct_byte_buffer_from_raw_ptr(env, dest->buffer + dest->len, out_remaining);
97 
98     impl->body_done = (*env)->CallBooleanMethod(
99         env, impl->http_request_body_stream, http_request_body_stream_properties.send_outgoing_body, direct_buffer);
100 
101     int result = AWS_OP_SUCCESS;
102     if (aws_jni_check_and_clear_exception(env)) {
103         result = aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
104     } else {
105         size_t amt_written = aws_jni_byte_buffer_get_position(env, direct_buffer);
106         dest->len += amt_written;
107     }
108 
109     (*env)->DeleteLocalRef(env, direct_buffer);
110 
111     aws_jni_release_thread_env(impl->jvm, env);
112     /********** JNI ENV RELEASE **********/
113 
114     return result;
115 }
116 
s_aws_input_stream_get_status(struct aws_input_stream * stream,struct aws_stream_status * status)117 static int s_aws_input_stream_get_status(struct aws_input_stream *stream, struct aws_stream_status *status) {
118     struct aws_http_request_body_stream_impl *impl =
119         AWS_CONTAINER_OF(stream, struct aws_http_request_body_stream_impl, base);
120 
121     status->is_end_of_stream = impl->body_done;
122     status->is_valid = impl->is_valid;
123 
124     return AWS_OP_SUCCESS;
125 }
126 
s_aws_input_stream_get_length(struct aws_input_stream * stream,int64_t * length)127 static int s_aws_input_stream_get_length(struct aws_input_stream *stream, int64_t *length) {
128     AWS_FATAL_ASSERT(length && "NULL length out param passed to JNI aws_input_stream_get_length");
129     struct aws_http_request_body_stream_impl *impl =
130         AWS_CONTAINER_OF(stream, struct aws_http_request_body_stream_impl, base);
131 
132     if (impl->http_request_body_stream != NULL) {
133 
134         /********** JNI ENV ACQUIRE **********/
135         JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
136         if (env == NULL) {
137             /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
138             return AWS_OP_ERR;
139         }
140 
141         *length =
142             (*env)->CallLongMethod(env, impl->http_request_body_stream, http_request_body_stream_properties.get_length);
143 
144         int result = AWS_OP_SUCCESS;
145         if (aws_jni_check_and_clear_exception(env)) {
146             result = aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
147         }
148 
149         aws_jni_release_thread_env(impl->jvm, env);
150         /********** JNI ENV RELEASE **********/
151 
152         return result;
153     }
154 
155     return AWS_OP_ERR;
156 }
157 
s_aws_input_stream_destroy(struct aws_http_request_body_stream_impl * impl)158 static void s_aws_input_stream_destroy(struct aws_http_request_body_stream_impl *impl) {
159 
160     /********** JNI ENV ACQUIRE **********/
161     JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
162     if (env == NULL) {
163         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
164         return;
165     }
166 
167     if (impl->http_request_body_stream != NULL) {
168         (*env)->DeleteGlobalRef(env, impl->http_request_body_stream);
169     }
170 
171     aws_jni_release_thread_env(impl->jvm, env);
172     /********** JNI ENV RELEASE **********/
173 
174     aws_mem_release(impl->allocator, impl);
175 }
176 
177 static struct aws_input_stream_vtable s_aws_input_stream_vtable = {
178     .seek = s_aws_input_stream_seek,
179     .read = s_aws_input_stream_read,
180     .get_status = s_aws_input_stream_get_status,
181     .get_length = s_aws_input_stream_get_length,
182 };
183 
aws_input_stream_new_from_java_http_request_body_stream(struct aws_allocator * allocator,JNIEnv * env,jobject http_request_body_stream)184 struct aws_input_stream *aws_input_stream_new_from_java_http_request_body_stream(
185     struct aws_allocator *allocator,
186     JNIEnv *env,
187     jobject http_request_body_stream) {
188     struct aws_http_request_body_stream_impl *impl =
189         aws_mem_calloc(allocator, 1, sizeof(struct aws_http_request_body_stream_impl));
190 
191     impl->allocator = allocator;
192     impl->base.vtable = &s_aws_input_stream_vtable;
193     aws_ref_count_init(&impl->base.ref_count, impl, (aws_simple_completion_callback *)s_aws_input_stream_destroy);
194 
195     jint jvmresult = (*env)->GetJavaVM(env, &impl->jvm);
196     AWS_FATAL_ASSERT(jvmresult == 0);
197 
198     impl->is_valid = true;
199     if (http_request_body_stream != NULL) {
200         impl->http_request_body_stream = (*env)->NewGlobalRef(env, http_request_body_stream);
201         if (impl->http_request_body_stream == NULL) {
202             goto on_error;
203         }
204     } else {
205         impl->body_done = true;
206     }
207 
208     return &impl->base;
209 
210 on_error:
211 
212     aws_input_stream_release(&impl->base);
213 
214     return NULL;
215 }
216 
s_marshal_http_header_to_buffer(struct aws_byte_buf * buf,const struct aws_byte_cursor * name,const struct aws_byte_cursor * value)217 static inline int s_marshal_http_header_to_buffer(
218     struct aws_byte_buf *buf,
219     const struct aws_byte_cursor *name,
220     const struct aws_byte_cursor *value) {
221     if (aws_byte_buf_reserve_relative(buf, sizeof(int) + sizeof(int) + name->len + value->len)) {
222         return AWS_OP_ERR;
223     }
224 
225     /* This will append to the buffer without overwriting anything */
226     aws_byte_buf_write_be32(buf, (uint32_t)name->len);
227     aws_byte_buf_write_from_whole_cursor(buf, *name);
228     aws_byte_buf_write_be32(buf, (uint32_t)value->len);
229     aws_byte_buf_write_from_whole_cursor(buf, *value);
230     return AWS_OP_SUCCESS;
231 }
232 
aws_marshal_http_headers_array_to_dynamic_buffer(struct aws_byte_buf * buf,const struct aws_http_header * header_array,size_t num_headers)233 int aws_marshal_http_headers_array_to_dynamic_buffer(
234     struct aws_byte_buf *buf,
235     const struct aws_http_header *header_array,
236     size_t num_headers) {
237     for (size_t i = 0; i < num_headers; ++i) {
238         if (s_marshal_http_header_to_buffer(buf, &header_array[i].name, &header_array[i].value)) {
239             return AWS_OP_ERR;
240         }
241     }
242 
243     return AWS_OP_SUCCESS;
244 }
245 
aws_marshal_http_headers_to_dynamic_buffer(struct aws_byte_buf * buf,const struct aws_http_headers * headers)246 int aws_marshal_http_headers_to_dynamic_buffer(struct aws_byte_buf *buf, const struct aws_http_headers *headers) {
247     for (size_t i = 0; i < aws_http_headers_count(headers); ++i) {
248         struct aws_http_header header;
249         aws_http_headers_get_index(headers, i, &header);
250         if (s_marshal_http_header_to_buffer(buf, &header.name, &header.value)) {
251             return AWS_OP_ERR;
252         }
253     }
254 
255     return AWS_OP_SUCCESS;
256 }
257 
258 /**
259  * Unmarshal the request from java.
260  *
261  * Version is as int: [4-bytes BE]
262  *
263  * Each string field is: [4-bytes BE] [variable length bytes specified
264  *          by the previous field]
265  *
266  * Each request is like: [version][method][path][header name-value
267  *          pairs]
268  *
269  * s_unmarshal_http_request_to_get_version to get the version field, which is a 4 byte int.
270  * s_unmarshal_http_request_without_version to get the whole request without version field.
271  */
s_unmarshal_http_request_to_get_version(struct aws_byte_cursor * request_blob)272 static inline enum aws_http_version s_unmarshal_http_request_to_get_version(struct aws_byte_cursor *request_blob) {
273     uint32_t version = 0;
274     if (!aws_byte_cursor_read_be32(request_blob, &version)) {
275         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
276     }
277     return version;
278 }
279 
s_unmarshal_http_request_without_version(struct aws_http_message * message,struct aws_byte_cursor * request_blob)280 static inline int s_unmarshal_http_request_without_version(
281     struct aws_http_message *message,
282     struct aws_byte_cursor *request_blob) {
283     uint32_t field_len = 0;
284     if (aws_http_message_get_protocol_version(message) != AWS_HTTP_VERSION_2) {
285         /* HTTP/1 puts method and path first, but those are empty in HTTP/2 */
286         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
287             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
288         }
289 
290         struct aws_byte_cursor method = aws_byte_cursor_advance(request_blob, field_len);
291 
292         int result = aws_http_message_set_request_method(message, method);
293         if (result != AWS_OP_SUCCESS) {
294             return AWS_OP_ERR;
295         }
296 
297         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
298             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
299         }
300 
301         struct aws_byte_cursor path = aws_byte_cursor_advance(request_blob, field_len);
302 
303         result = aws_http_message_set_request_path(message, path);
304         if (result != AWS_OP_SUCCESS) {
305             return AWS_OP_ERR;
306         }
307     } else {
308         /* Read empty method and path from the marshalled request */
309         if (!aws_byte_cursor_read_be32(request_blob, &field_len) || field_len) {
310             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
311         }
312         if (!aws_byte_cursor_read_be32(request_blob, &field_len) || field_len) {
313             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
314         }
315     }
316     while (request_blob->len) {
317         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
318             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
319         }
320 
321         struct aws_byte_cursor header_name = aws_byte_cursor_advance(request_blob, field_len);
322 
323         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
324             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
325         }
326 
327         struct aws_byte_cursor header_value = aws_byte_cursor_advance(request_blob, field_len);
328 
329         struct aws_http_header header = {
330             .name = header_name,
331             .value = header_value,
332         };
333 
334         aws_http_message_add_header(message, header);
335     }
336 
337     return AWS_OP_SUCCESS;
338 }
339 
s_unmarshal_http_headers(struct aws_http_headers * headers,struct aws_byte_cursor * request_blob)340 static inline int s_unmarshal_http_headers(struct aws_http_headers *headers, struct aws_byte_cursor *request_blob) {
341     uint32_t field_len = 0;
342     while (request_blob->len) {
343         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
344             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
345         }
346 
347         struct aws_byte_cursor header_name = aws_byte_cursor_advance(request_blob, field_len);
348 
349         if (!aws_byte_cursor_read_be32(request_blob, &field_len)) {
350             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
351         }
352 
353         struct aws_byte_cursor header_value = aws_byte_cursor_advance(request_blob, field_len);
354 
355         struct aws_http_header header = {
356             .name = header_name,
357             .value = header_value,
358         };
359 
360         aws_http_headers_add_header(headers, &header);
361     }
362     return AWS_OP_SUCCESS;
363 }
364 
aws_apply_java_http_request_changes_to_native_request(JNIEnv * env,jbyteArray marshalled_request,jobject jni_body_stream,struct aws_http_message * message)365 int aws_apply_java_http_request_changes_to_native_request(
366     JNIEnv *env,
367     jbyteArray marshalled_request,
368     jobject jni_body_stream,
369     struct aws_http_message *message) {
370 
371     /* come back to this when we decide we need to. */
372     (void)jni_body_stream;
373 
374     struct aws_http_headers *headers = aws_http_message_get_headers(message);
375     aws_http_headers_clear(headers);
376     int result = AWS_OP_SUCCESS;
377 
378     const size_t marshalled_request_length = (*env)->GetArrayLength(env, marshalled_request);
379 
380     uint8_t *marshalled_request_data = (*env)->GetPrimitiveArrayCritical(env, marshalled_request, NULL);
381     struct aws_byte_cursor marshalled_cur =
382         aws_byte_cursor_from_array((uint8_t *)marshalled_request_data, marshalled_request_length);
383 
384     enum aws_http_version version = s_unmarshal_http_request_to_get_version(&marshalled_cur);
385     if (version != aws_http_message_get_protocol_version(message)) {
386         result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
387     } else {
388         result = s_unmarshal_http_request_without_version(message, &marshalled_cur);
389     }
390     (*env)->ReleasePrimitiveArrayCritical(env, marshalled_request, marshalled_request_data, 0);
391 
392     if (result) {
393         aws_jni_throw_runtime_exception(
394             env, "HttpRequest.applyChangesToNativeRequest: %s\n", aws_error_debug_str(aws_last_error()));
395         return result;
396     }
397 
398     if (jni_body_stream) {
399         struct aws_input_stream *body_stream =
400             aws_input_stream_new_from_java_http_request_body_stream(aws_jni_get_allocator(), env, jni_body_stream);
401 
402         aws_http_message_set_body_stream(message, body_stream);
403         /* request controls the lifetime of body stream fully */
404         aws_input_stream_release(body_stream);
405     }
406 
407     return result;
408 }
409 
aws_http_request_new_from_java_http_request(JNIEnv * env,jbyteArray marshalled_request,jobject jni_body_stream)410 struct aws_http_message *aws_http_request_new_from_java_http_request(
411     JNIEnv *env,
412     jbyteArray marshalled_request,
413     jobject jni_body_stream) {
414     const char *exception_message = NULL;
415     const size_t marshalled_request_length = (*env)->GetArrayLength(env, marshalled_request);
416 
417     jbyte *marshalled_request_data = (*env)->GetPrimitiveArrayCritical(env, marshalled_request, NULL);
418     struct aws_byte_cursor marshalled_cur =
419         aws_byte_cursor_from_array((uint8_t *)marshalled_request_data, marshalled_request_length);
420     enum aws_http_version version = s_unmarshal_http_request_to_get_version(&marshalled_cur);
421     struct aws_http_message *request = version == AWS_HTTP_VERSION_2
422                                            ? aws_http2_message_new_request(aws_jni_get_allocator())
423                                            : aws_http_message_new_request(aws_jni_get_allocator());
424 
425     int result = AWS_OP_SUCCESS;
426     if (version != aws_http_message_get_protocol_version(request)) {
427         result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
428     } else {
429         result = s_unmarshal_http_request_without_version(request, &marshalled_cur);
430     }
431     (*env)->ReleasePrimitiveArrayCritical(env, marshalled_request, marshalled_request_data, 0);
432 
433     if (result) {
434         exception_message = "aws_http_request_new_from_java_http_request: Invalid marshalled request data.";
435         goto on_error;
436     }
437 
438     if (jni_body_stream != NULL) {
439         struct aws_input_stream *body_stream =
440             aws_input_stream_new_from_java_http_request_body_stream(aws_jni_get_allocator(), env, jni_body_stream);
441         if (body_stream == NULL) {
442             exception_message = "aws_fill_out_request: Error building body stream";
443             goto on_error;
444         }
445 
446         aws_http_message_set_body_stream(request, body_stream);
447         /* request controls the lifetime of body stream fully */
448         aws_input_stream_release(body_stream);
449     }
450 
451     return request;
452 
453 on_error:
454     if (exception_message) {
455         aws_jni_throw_runtime_exception(env, exception_message);
456     }
457 
458     /* Don't need to destroy input stream since it's the last thing created */
459     aws_http_message_destroy(request);
460 
461     return NULL;
462 }
463 
aws_http_headers_new_from_java_http_headers(JNIEnv * env,jbyteArray marshalled_headers)464 struct aws_http_headers *aws_http_headers_new_from_java_http_headers(JNIEnv *env, jbyteArray marshalled_headers) {
465     struct aws_http_headers *headers = aws_http_headers_new(aws_jni_get_allocator());
466     if (headers == NULL) {
467         aws_jni_throw_runtime_exception(env, "aws_http_headers_new_from_java_http_headers: Unable to allocate headers");
468         return NULL;
469     }
470     const size_t marshalled_headers_length = (*env)->GetArrayLength(env, marshalled_headers);
471 
472     jbyte *marshalled_headers_data = (*env)->GetPrimitiveArrayCritical(env, marshalled_headers, NULL);
473     struct aws_byte_cursor marshalled_cur =
474         aws_byte_cursor_from_array((uint8_t *)marshalled_headers_data, marshalled_headers_length);
475     int result = s_unmarshal_http_headers(headers, &marshalled_cur);
476     (*env)->ReleasePrimitiveArrayCritical(env, marshalled_headers, marshalled_headers_data, 0);
477 
478     if (result) {
479         aws_jni_throw_runtime_exception(
480             env, "aws_http_headers_new_from_java_http_headers: Invalid marshalled headers data.");
481         goto on_error;
482     }
483 
484     return headers;
485 
486 on_error:
487     aws_http_headers_release(headers);
488     return NULL;
489 }
490 
s_marshall_http_request(const struct aws_http_message * message,struct aws_byte_buf * request_buf)491 static inline int s_marshall_http_request(const struct aws_http_message *message, struct aws_byte_buf *request_buf) {
492     struct aws_byte_cursor method;
493     AWS_ZERO_STRUCT(method);
494 
495     AWS_FATAL_ASSERT(!aws_http_message_get_request_method(message, &method));
496 
497     struct aws_byte_cursor path;
498     AWS_ZERO_STRUCT(path);
499 
500     AWS_FATAL_ASSERT(!aws_http_message_get_request_path(message, &path));
501 
502     if (aws_byte_buf_reserve_relative(request_buf, sizeof(int) + sizeof(int) + sizeof(int) + method.len + path.len)) {
503         return AWS_OP_ERR;
504     }
505 
506     aws_byte_buf_write_be32(request_buf, (uint32_t)aws_http_message_get_protocol_version(message));
507     aws_byte_buf_write_be32(request_buf, (uint32_t)method.len);
508     aws_byte_buf_write_from_whole_cursor(request_buf, method);
509     aws_byte_buf_write_be32(request_buf, (uint32_t)path.len);
510     aws_byte_buf_write_from_whole_cursor(request_buf, path);
511 
512     const struct aws_http_headers *headers = aws_http_message_get_const_headers(message);
513     AWS_FATAL_ASSERT(headers);
514     size_t header_count = aws_http_message_get_header_count(message);
515     for (size_t i = 0; i < header_count; ++i) {
516         struct aws_http_header header;
517         AWS_ZERO_STRUCT(header);
518 
519         AWS_FATAL_ASSERT(!aws_http_headers_get_index(headers, i, &header));
520         if (s_marshal_http_header_to_buffer(request_buf, &header.name, &header.value)) {
521             return AWS_OP_ERR;
522         }
523     }
524 
525     return AWS_OP_SUCCESS;
526 }
527 
aws_java_http_request_from_native(JNIEnv * env,struct aws_http_message * message,jobject request_body_stream)528 jobject aws_java_http_request_from_native(JNIEnv *env, struct aws_http_message *message, jobject request_body_stream) {
529     jobject jni_request_blob = NULL;
530     jobject j_request = NULL;
531     struct aws_byte_buf marshaling_buf;
532 
533     if (aws_byte_buf_init(&marshaling_buf, aws_jni_get_allocator(), 1024)) {
534         aws_jni_throw_runtime_exception(env, "aws_java_http_request_from_native: allocation failed");
535         return NULL;
536     }
537 
538     if (s_marshall_http_request(message, &marshaling_buf)) {
539         aws_jni_throw_runtime_exception(
540             env, "aws_java_http_request_from_native: %s.", aws_error_debug_str(aws_last_error()));
541         goto done;
542     }
543 
544     jni_request_blob = aws_jni_direct_byte_buffer_from_raw_ptr(env, marshaling_buf.buffer, marshaling_buf.len);
545 
546     /* Currently our only use case for this does not involve a body stream. We should come back and handle this
547        when it's not time sensitive to do so. */
548     j_request = (*env)->NewObject(
549         env,
550         http_request_properties.http_request_class,
551         http_request_properties.constructor_method_id,
552         jni_request_blob,
553         request_body_stream);
554 
555     if (aws_jni_check_and_clear_exception(env)) {
556         aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
557         goto done;
558     }
559 
560 done:
561     if (jni_request_blob) {
562         (*env)->DeleteLocalRef(env, jni_request_blob);
563     }
564 
565     aws_byte_buf_clean_up(&marshaling_buf);
566     return j_request;
567 }
568