From 9c9422c21f8aa824c6c433ad8a1597d0206a8eb5 Mon Sep 17 00:00:00 2001 From: Justin Boswell Date: Thu, 25 Mar 2021 10:14:07 -0700 Subject: [PATCH] HTTP message API (#19) * HTTP message API * Added MacOS CI * Grabbed fixes for mac from aws-crt-ffi --- .github/workflows/ci.yml | 19 ++--- config.m4 | 8 ++- crt/aws-crt-ffi | 2 +- ext/awscrt.stub.php | 6 ++ ext/awscrt_arginfo.h | 20 +++++- ext/crt.c | 112 +++++++++++++++++++----------- ext/php_aws_crt.h | 2 +- src/AWS/CRT/CRT.php | 8 +++ src/AWS/CRT/HTTP/Headers.php | 47 +++++++++++++ src/AWS/CRT/HTTP/Message.php | 91 +++++++++++++++++++----- src/AWS/CRT/HTTP/Request.php | 24 +++++++ src/AWS/CRT/HTTP/Response.php | 24 +++++++ src/AWS/CRT/Internal/Encoding.php | 31 +++++++++ src/tests/HttpMessageTest.php | 96 +++++++++++++++++++++++++ 14 files changed, 421 insertions(+), 69 deletions(-) create mode 100644 src/AWS/CRT/HTTP/Headers.php create mode 100644 src/AWS/CRT/HTTP/Request.php create mode 100644 src/AWS/CRT/HTTP/Response.php create mode 100644 src/AWS/CRT/Internal/Encoding.php create mode 100644 src/tests/HttpMessageTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aeb9e68..cbcc0d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,11 +81,14 @@ jobs: # python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" # python builder.pyz build -p ${{ env.PACKAGE_NAME }} downstream - # osx: - # runs-on: macos-latest - # steps: - # - name: Build ${{ env.PACKAGE_NAME }} + consumers - # run: | - # python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" - # chmod a+x builder - # ./builder build -p ${{ env.PACKAGE_NAME }} --spec=downstream + macos: + runs-on: macos-${{ matrix.version }} + strategy: + matrix: + version: [10.15] + steps: + - name: Build PHP 8 extension and test + run: | + python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" + chmod a+x builder + ./builder build -p ${{ env.PACKAGE_NAME }} --spec=downstream diff --git a/config.m4 b/config.m4 index 9f0b1fe..b056b33 100644 --- a/config.m4 +++ b/config.m4 @@ -8,8 +8,14 @@ PHP_ARG_WITH(awscrt) if test "$PHP_AWSCRT" != "no"; then # force lib paths to be absolute, or PHP will mangle them cwd=`pwd` + # Enable s2n and libcrypto for non-darwin UNIX + if uname -a | grep -i darwin > /dev/null 2>&1; then + platform_tls_libs="" + else + platform_tls_libs="-ls2n -l:libcrypto.a" + fi CRT_LIBPATHS="-L${cwd}/build/install/lib -L${cwd}/build/install/lib64" - CRT_LIBS="-laws-crt-ffi -laws-c-auth -laws-c-http -laws-c-io -laws-c-cal -laws-c-compression -laws-c-common -ls2n -l:libcrypto.a" + CRT_LIBS="-laws-crt-ffi -laws-c-auth -laws-c-http -laws-c-io -laws-c-cal -laws-c-compression -laws-c-common ${platform_tls_libs}" PHP_ADD_INCLUDE(${cwd}/build/install/include) PHP_EVAL_LIBLINE([$CRT_LIBPATHS $CRT_LIBS], AWSCRT_SHARED_LIBADD) diff --git a/crt/aws-crt-ffi b/crt/aws-crt-ffi index 37c2cbe..d5631ad 160000 --- a/crt/aws-crt-ffi +++ b/crt/aws-crt-ffi @@ -1 +1 @@ -Subproject commit 37c2cbe22f711529cd96f4ec697c0662b0b2b2b3 +Subproject commit d5631ad166b7389204cff9ce0aeb92b533afec3d diff --git a/ext/awscrt.stub.php b/ext/awscrt.stub.php index c6d1999..d3a79cf 100644 --- a/ext/awscrt.stub.php +++ b/ext/awscrt.stub.php @@ -38,6 +38,12 @@ function aws_crt_input_stream_eof(int $stream): bool {} function aws_crt_input_stream_get_length(int $stream): int {} +function aws_crt_http_message_new_from_blob(string $blob): int {} + +function aws_crt_http_message_to_blob(int $message): string {} + +function aws_crt_http_message_release(int $message): void {} + function aws_crt_credentials_options_new(): object {} function aws_crt_credentials_options_release(object $options): void {} diff --git a/ext/awscrt_arginfo.h b/ext/awscrt_arginfo.h index 9c02315..7ba89ef 100644 --- a/ext/awscrt_arginfo.h +++ b/ext/awscrt_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5549e1214a9a419075f5cb561adb2d7a340c5916 */ + * Stub hash: bd274f6b280b27aa5fb7d3be2ce85ad065936943 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_last_error, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -68,6 +68,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_get_length, ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_new_from_blob, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, blob, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_to_blob, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, message, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_release, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, message, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_aws_crt_credentials_options_new arginfo_aws_crt_input_stream_options_new #define arginfo_aws_crt_credentials_options_release arginfo_aws_crt_input_stream_options_release @@ -133,6 +145,9 @@ ZEND_FUNCTION(aws_crt_input_stream_seek); ZEND_FUNCTION(aws_crt_input_stream_read); ZEND_FUNCTION(aws_crt_input_stream_eof); ZEND_FUNCTION(aws_crt_input_stream_get_length); +ZEND_FUNCTION(aws_crt_http_message_new_from_blob); +ZEND_FUNCTION(aws_crt_http_message_to_blob); +ZEND_FUNCTION(aws_crt_http_message_release); ZEND_FUNCTION(aws_crt_credentials_options_new); ZEND_FUNCTION(aws_crt_credentials_options_release); ZEND_FUNCTION(aws_crt_credentials_options_set_access_key_id); @@ -169,6 +184,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(aws_crt_input_stream_read, arginfo_aws_crt_input_stream_read) ZEND_FE(aws_crt_input_stream_eof, arginfo_aws_crt_input_stream_eof) ZEND_FE(aws_crt_input_stream_get_length, arginfo_aws_crt_input_stream_get_length) + ZEND_FE(aws_crt_http_message_new_from_blob, arginfo_aws_crt_http_message_new_from_blob) + ZEND_FE(aws_crt_http_message_to_blob, arginfo_aws_crt_http_message_to_blob) + ZEND_FE(aws_crt_http_message_release, arginfo_aws_crt_http_message_release) ZEND_FE(aws_crt_credentials_options_new, arginfo_aws_crt_credentials_options_new) ZEND_FE(aws_crt_credentials_options_release, arginfo_aws_crt_credentials_options_release) ZEND_FE(aws_crt_credentials_options_set_access_key_id, arginfo_aws_crt_credentials_options_set_access_key_id) diff --git a/ext/crt.c b/ext/crt.c index 1f51dfd..8ddf71a 100644 --- a/ext/crt.c +++ b/ext/crt.c @@ -61,7 +61,7 @@ PHP_FUNCTION(aws_crt_last_error) { PHP_FUNCTION(aws_crt_error_str) { zend_ulong error_code = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &error_code) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error_code) == FAILURE) { RETURN_NULL(); } @@ -72,7 +72,7 @@ PHP_FUNCTION(aws_crt_error_str) { PHP_FUNCTION(aws_crt_error_name) { zend_ulong error_code = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &error_code) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error_code) == FAILURE) { RETURN_NULL(); } @@ -83,7 +83,7 @@ PHP_FUNCTION(aws_crt_error_name) { PHP_FUNCTION(aws_crt_error_debug_str) { zend_ulong error_code = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &error_code) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error_code) == FAILURE) { RETURN_NULL(); } @@ -98,7 +98,7 @@ PHP_FUNCTION(aws_crt_event_loop_group_options_new) { PHP_FUNCTION(aws_crt_event_loop_group_options_release) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { return; } @@ -110,7 +110,7 @@ PHP_FUNCTION(aws_crt_event_loop_group_options_set_max_threads) { zend_ulong php_options = 0; zend_ulong num_threads = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &php_options, &num_threads) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &php_options, &num_threads) == FAILURE) { RETURN_NULL(); } @@ -121,7 +121,7 @@ PHP_FUNCTION(aws_crt_event_loop_group_options_set_max_threads) { PHP_FUNCTION(aws_crt_event_loop_group_new) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } @@ -133,7 +133,7 @@ PHP_FUNCTION(aws_crt_event_loop_group_new) { PHP_FUNCTION(aws_crt_event_loop_group_release) { zend_ulong php_elg = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_elg) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_elg) == FAILURE) { return; } @@ -153,7 +153,7 @@ PHP_FUNCTION(aws_crt_input_stream_options_new) { PHP_FUNCTION(aws_crt_input_stream_options_release) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { return; } @@ -170,7 +170,7 @@ PHP_FUNCTION(aws_crt_input_stream_options_set_user_data) { zend_ulong php_options = 0; zval *user_data = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &php_options, &user_data) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &php_options, &user_data) == FAILURE) { return; } @@ -187,7 +187,7 @@ static int s_php_stream_seek(void *user_data, int64_t offset, aws_crt_input_stre static int s_php_stream_read(void *user_data, uint8_t *dest, size_t dest_length) { php_stream *stream = user_data; - return php_stream_read(stream, dest, dest_length) != 0; + return php_stream_read(stream, (char *)dest, dest_length) != 0; } static int s_php_stream_get_length(void *user_data, int64_t *out_length) { @@ -204,7 +204,8 @@ static int s_php_stream_get_status(void *user_data, aws_crt_input_stream_status out_status->is_valid = stream != NULL; /* We would like to use php_stream_eof here, but certain streams (notably php://memory) * are not actually capable of EOF, so we get to do it the hard way */ - size_t length = 0, pos = 0; + int64_t length = 0; + int64_t pos = 0; s_php_stream_get_length(stream, &length); pos = php_stream_tell(stream); out_status->is_end_of_stream = pos == length; @@ -219,7 +220,7 @@ static void s_php_stream_destroy(void *user_data) { PHP_FUNCTION(aws_crt_input_stream_new) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } @@ -236,7 +237,7 @@ PHP_FUNCTION(aws_crt_input_stream_new) { PHP_FUNCTION(aws_crt_input_stream_release) { zend_ulong php_stream = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_stream) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_stream) == FAILURE) { RETURN_NULL(); } @@ -249,7 +250,7 @@ PHP_FUNCTION(aws_crt_input_stream_seek) { zend_ulong offset = 0; zend_ulong basis = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &php_stream, &offset, &basis) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &php_stream, &offset, &basis) == FAILURE) { RETURN_NULL(); } @@ -261,21 +262,21 @@ PHP_FUNCTION(aws_crt_input_stream_read) { zend_ulong php_stream = 0; zend_ulong length = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &php_stream, &length) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &php_stream, &length) == FAILURE) { RETURN_NULL(); } aws_crt_input_stream *stream = (void *)php_stream; uint8_t *buf = emalloc(length); int ret = aws_crt_input_stream_read(stream, buf, length); - AWS_RETURN_STRINGL(buf, length); + AWS_RETURN_STRINGL((const char *)buf, length); efree(buf); } PHP_FUNCTION(aws_crt_input_stream_eof) { zend_ulong php_stream = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_stream) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_stream) == FAILURE) { RETURN_NULL(); } @@ -288,16 +289,54 @@ PHP_FUNCTION(aws_crt_input_stream_eof) { PHP_FUNCTION(aws_crt_input_stream_get_length) { zend_ulong php_stream = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_stream) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_stream) == FAILURE) { RETURN_NULL(); } aws_crt_input_stream *stream = (void *)php_stream; - size_t length = 0; + int64_t length = 0; aws_crt_input_stream_get_length(stream, &length); RETURN_LONG(length); } +PHP_FUNCTION(aws_crt_http_message_new_from_blob) { + const char *blob = NULL; + size_t blob_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &blob, &blob_len) == FAILURE) { + RETURN_NULL(); + } + + aws_crt_http_message *message = aws_crt_http_message_new_from_blob((uint8_t *)blob, blob_len); + RETURN_LONG((zend_ulong)message); +} + +PHP_FUNCTION(aws_crt_http_message_to_blob) { + zend_ulong php_msg = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_msg) == FAILURE) { + RETURN_NULL(); + } + + aws_crt_http_message *message = (void *)php_msg; + uint8_t *blob = NULL; + size_t blob_len = 0; + aws_crt_http_message_to_blob(message, &blob, &blob_len); + AWS_RETURN_STRINGL((const char *)blob, blob_len); + aws_crt_mem_release(blob); +} + +PHP_FUNCTION(aws_crt_http_message_release) { + zend_ulong php_msg = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_msg) == FAILURE) { + RETURN_NULL(); + } + + aws_crt_http_message *message = (void *)php_msg; + aws_crt_http_message_release(message); +} + PHP_FUNCTION(aws_crt_credentials_options_new) { aws_crt_credentials_options *options = aws_crt_credentials_options_new(); RETURN_LONG((zend_ulong)options); @@ -306,7 +345,7 @@ PHP_FUNCTION(aws_crt_credentials_options_new) { PHP_FUNCTION(aws_crt_credentials_options_release) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } @@ -319,8 +358,7 @@ PHP_FUNCTION(aws_crt_credentials_options_set_access_key_id) { const char *access_key_id = NULL; size_t access_key_id_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &access_key_id, &access_key_id_len) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &access_key_id, &access_key_id_len) == FAILURE) { RETURN_NULL(); } @@ -333,8 +371,8 @@ PHP_FUNCTION(aws_crt_credentials_options_set_secret_access_key) { const char *secret_access_key = NULL; size_t secret_access_key_len = 0; - if (zend_parse_parameters( - ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &secret_access_key, &secret_access_key_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == + FAILURE) { RETURN_NULL(); } @@ -347,8 +385,7 @@ PHP_FUNCTION(aws_crt_credentials_options_set_session_token) { const char *session_token = NULL; size_t session_token_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &session_token, &session_token_len) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &session_token, &session_token_len) == FAILURE) { RETURN_NULL(); } @@ -359,8 +396,7 @@ PHP_FUNCTION(aws_crt_credentials_options_set_session_token) { PHP_FUNCTION(aws_crt_credentials_options_set_expiration_timepoint_seconds) { zend_ulong php_options = 0; zend_ulong expiration_timepoint_seconds = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &php_options, &expiration_timepoint_seconds) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &php_options, &expiration_timepoint_seconds) == FAILURE) { RETURN_NULL(); } @@ -371,7 +407,7 @@ PHP_FUNCTION(aws_crt_credentials_options_set_expiration_timepoint_seconds) { PHP_FUNCTION(aws_crt_credentials_new) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } @@ -383,7 +419,7 @@ PHP_FUNCTION(aws_crt_credentials_new) { PHP_FUNCTION(aws_crt_credentials_release) { zend_ulong php_credentials = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_credentials) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_credentials) == FAILURE) { RETURN_NULL(); } @@ -394,7 +430,7 @@ PHP_FUNCTION(aws_crt_credentials_release) { PHP_FUNCTION(aws_crt_credentials_provider_release) { zend_ulong php_creds_provider = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_creds_provider) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_creds_provider) == FAILURE) { RETURN_NULL(); } @@ -410,7 +446,7 @@ PHP_FUNCTION(aws_crt_credentials_provider_static_options_new) { PHP_FUNCTION(aws_crt_credentials_provider_static_options_release) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } @@ -423,8 +459,7 @@ PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_access_key_id) { const char *access_key_id = NULL; size_t access_key_id_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &access_key_id, &access_key_id_len) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &access_key_id, &access_key_id_len) == FAILURE) { RETURN_NULL(); } @@ -437,8 +472,8 @@ PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_secret_access_key) const char *secret_access_key = NULL; size_t secret_access_key_len = 0; - if (zend_parse_parameters( - ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &secret_access_key, &secret_access_key_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == + FAILURE) { RETURN_NULL(); } @@ -452,8 +487,7 @@ PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_session_token) { const char *session_token = NULL; size_t session_token_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &php_options, &session_token, &session_token_len) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &session_token, &session_token_len) == FAILURE) { RETURN_NULL(); } @@ -464,7 +498,7 @@ PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_session_token) { PHP_FUNCTION(aws_crt_credentials_provider_static_new) { zend_ulong php_options = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &php_options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &php_options) == FAILURE) { RETURN_NULL(); } diff --git a/ext/php_aws_crt.h b/ext/php_aws_crt.h index c2723f0..56eafd6 100644 --- a/ext/php_aws_crt.h +++ b/ext/php_aws_crt.h @@ -19,7 +19,7 @@ # error "PHP >= 5.6 is required" #endif -#define AWS_PHP_AT_LEAST_7 defined(ZEND_EXTENSION_API_NO_7_0_X) || (ZEND_EXTENSION_API_NO >= 320151012) +#define AWS_PHP_AT_LEAST_7 (ZEND_EXTENSION_API_NO >= 320151012) ZEND_BEGIN_MODULE_GLOBALS(awscrt) long log_level; diff --git a/src/AWS/CRT/CRT.php b/src/AWS/CRT/CRT.php index cbf3582..58f4a85 100644 --- a/src/AWS/CRT/CRT.php +++ b/src/AWS/CRT/CRT.php @@ -203,4 +203,12 @@ final class CRT { function input_stream_get_length($stream) { return self::$impl->aws_crt_input_stream_get_length($stream); } + + function http_message_new_from_blob($blob) { + return self::$impl->aws_crt_http_message_new_from_blob($blob); + } + + function http_message_release($message) { + self::$impl->aws_crt_http_message_release($message); + } } diff --git a/src/AWS/CRT/HTTP/Headers.php b/src/AWS/CRT/HTTP/Headers.php new file mode 100644 index 0000000..9a04bd3 --- /dev/null +++ b/src/AWS/CRT/HTTP/Headers.php @@ -0,0 +1,47 @@ +headers = $headers; + } + + public static function marshall($headers) { + $buf = ""; + foreach ($headers->headers as $header => $value) { + $buf .= Encoding::encodeString($header); + $buf .= Encoding::encodeString($value); + } + return $buf; + } + + public static function unmarshall($buf) { + $strings = Encoding::readStrings($buf); + $headers = []; + for ($idx = 0; $idx < count($strings);) { + $headers[$strings[$idx++]] = $strings[$idx++]; + } + return new Headers($headers); + } + + public function count() { + return count($this->headers); + } + + public function get($header) { + return isset($this->headers[$header]) ? $this->headers[$header] : null; + } + + public function set($header, $value) { + $this->headers[$header] = $value; + } + + public function toArray() { + return array_merge($this->headers); + } +} diff --git a/src/AWS/CRT/HTTP/Message.php b/src/AWS/CRT/HTTP/Message.php index f5a3fd3..e186daf 100644 --- a/src/AWS/CRT/HTTP/Message.php +++ b/src/AWS/CRT/HTTP/Message.php @@ -2,32 +2,87 @@ namespace AWS\CRT\HTTP; -abstract class Message { - private $path = ""; - private $query = []; - private $headers = []; +use AWS\CRT\NativeResource; - public function __construct($path, $query = [], $headers = []) { +use AWS\CRT\Internal\Encoding; + +abstract class Message extends NativeResource { + private $method; + private $path; + private $query; + private $headers; + + public function __construct($method, $path, $query = [], $headers = null) { + $this->method = $method; $this->path = $path; $this->query = $query; - $this->headers = $headers; + $this->headers = !is_null($headers) ? $headers : new Headers(); + $this->acquire(self::$crt->http_message_new_from_blob(self::marshall($this))); } -} -class Request extends Message { - private $body_stream = null; + public function __destruct() { + self::$crt->http_message_release($this->release()); + parent::__destruct(); + } + + protected static function marshall($msg) { + $buf = ""; + $buf .= Encoding::encodeString($msg->method); + $buf .= Encoding::encodeString($msg->pathAndQuery()); + $buf .= Headers::marshall($msg->headers); + return $buf; + } + + protected static function _unmarshall($buf, $class=Message::class) { + $method = Encoding::readString($buf); + $path_and_query = Encoding::readString($buf); + $parts = explode("?", $path_and_query, 2); + $path = isset($parts[0]) ? $parts[0] : ""; + $query = isset($parts[1]) ? $parts[1] : ""; + $headers = Headers::unmarshall($buf); + + // Turn query params back into a dictionary + if (strlen($query)) { + $query = rawurldecode($query); + $query = explode("&", $query); + $query = array_reduce($query, function($params, $pair) { + list($param, $value) = explode("=", $pair, 2); + $params[$param] = $value; + return $params; + }, []); + } else { + $query = []; + } + + return new $class($method, $path, $query, $headers); + } + + public function pathAndQuery() { + $path = $this->path; + $queries = []; + foreach ($this->query as $param => $value) { + $queries []= urlencode($param) . "=" . urlencode($value); + } + $query = implode("&", $queries); + if (strlen($query)) { + $path = implode("?", [$path, $query]); + } + return $path; + } - public function __construct($path, $query = [], $headers = [], $body_stream = null) { - parent::__construct($path, $query, $headers); - $this->body_stream = $body_stream; + public function method() { + return $this->method; } -} -class Response extends Message { - private $status_code; + public function path() { + return $this->path; + } + + public function query() { + return $this->query; + } - public function __construct($path, $headers, $status_code) { - parent::__construct($path, [], $headers); - $this->status_code = $status_code; + public function headers() { + return $this->headers; } } diff --git a/src/AWS/CRT/HTTP/Request.php b/src/AWS/CRT/HTTP/Request.php new file mode 100644 index 0000000..18d908e --- /dev/null +++ b/src/AWS/CRT/HTTP/Request.php @@ -0,0 +1,24 @@ +body_stream = $body_stream; + } + + public static function marshall($request) { + return parent::marshall($request); + } + + public static function unmarshall($buf) { + return parent::_unmarshall($buf, Request::class); + } + + public function body_stream() { + return $this->body_stream; + } +} diff --git a/src/AWS/CRT/HTTP/Response.php b/src/AWS/CRT/HTTP/Response.php new file mode 100644 index 0000000..a0f97e4 --- /dev/null +++ b/src/AWS/CRT/HTTP/Response.php @@ -0,0 +1,24 @@ +status_code = $status_code; + } + + public static function marshall($response) { + return parent::marshall($response); + } + + public static function unmarshall($buf) { + return parent::_unmarshall($buf, Response::class); + } + + public function status_code() { + return $this->status_code; + } +} diff --git a/src/AWS/CRT/Internal/Encoding.php b/src/AWS/CRT/Internal/Encoding.php new file mode 100644 index 0000000..87e07ac --- /dev/null +++ b/src/AWS/CRT/Internal/Encoding.php @@ -0,0 +1,31 @@ +assertSame(0, $headers->count()); + } + + public function testHeadersMarshalling() { + $headers_array = [ + "host" => "s3.amazonaws.com", + "test" => "this is a test header value" + ]; + $headers = new Headers($headers_array); + $this->assertSame(2, $headers->count()); + $this->assertSame($headers_array['host'], $headers->get('host')); + $this->assertSame($headers_array['test'], $headers->get('test')); + $buffer = Headers::marshall($headers); + $headers_copy = Headers::unmarshall($buffer); + $this->assertSame(2, $headers_copy->count()); + $this->assertSame($headers_array['host'], $headers_copy->get('host')); + $this->assertSame($headers_array['test'], $headers_copy->get('test')); + } + + private function assertMessagesMatch($a, $b) { + $this->assertSame($a->method(), $b->method()); + $this->assertSame($a->path(), $b->path()); + $this->assertSame($a->query(), $b->query()); + $this->assertSame($a->headers()->toArray(), $b->headers()->toArray()); + } + + public function testRequestMarshalling() { + $headers_array = [ + "host" => "s3.amazonaws.com", + "test" => "this is a test header value" + ]; + $headers = new Headers($headers_array); + $method = "GET"; + $path = "/index.php"; + $query = []; + + $msg = new Request($method, $path, $query, $headers); + $msg_buf = Request::marshall($msg); + $msg_copy = Request::unmarshall($msg_buf); + + $this->assertMessagesMatch($msg, $msg_copy); + } + + public function testRequestMarshallingWithQueryParams() { + $headers_array = [ + "host" => "s3.amazonaws.com", + "test" => "this is a test header value" + ]; + $headers = new Headers($headers_array); + $method = "GET"; + $path = "/index.php"; + $query = [ + 'request' => '1', + 'test' => 'true', + 'answer' => '42', + 'foo' => 'bar', + ]; + + $msg = new Request($method, $path, $query, $headers); + $msg_buf = Request::marshall($msg); + $msg_copy = Request::unmarshall($msg_buf); + + $this->assertMessagesMatch($msg, $msg_copy); + } + + public function testResponseMarshalling() { + $headers_array = [ + "content-length" => "42", + "test" => "this is a test header value" + ]; + $headers = new Headers($headers_array); + $method = "GET"; + $path = "/index.php"; + $query = [ + 'response' => '1' + ]; + + $msg = new Response($method, $path, $query, $headers, 400); + $msg_buf = Request::marshall($msg); + $msg_copy = Request::unmarshall($msg_buf); + + $this->assertMessagesMatch($msg, $msg_copy); + } +}