From 9331eabd7e2bf974dfbd8123f545d11b80ab8c67 Mon Sep 17 00:00:00 2001 From: Christian Blichmann Date: Wed, 23 Sep 2020 07:59:42 -0700 Subject: [PATCH] Copybara import of the project: Including changes for GitHub -> internal migration -- b5d7e43ddeff9c087d0f67949bea6ac795c5474a by Federico Stazi <34340238+FedericoStazi@users.noreply.github.com>: Initial curl commit -- 24786c44d89b4a6817204aaacd84fc1aa2747434 by Federico Stazi : Added gitignore and curl submodule -- 6d5cfd575abd05c387f93be060c8fc88fd39e482 by Federico Stazi : Added new line at the end of files -- c7423c5f8a8d460655d0fafa198758c39d5270d1 by Federico Stazi : Remove SHARED from add_sapi_library -- 05c0a4b004feba1c0ae1ba6bf519966f48589ba6 by Federico Stazi : Fix includes -- 5be51fabbef7e7eab032dbfb94239654e44008c3 by Federico Stazi : Improve comments -- 34338411b845d438a5b7615d990d6539771152eb by Federico Stazi : Improve style -- 8c68ac221ff158aab3b285d8b2d6158a895ddbf2 by Federico Stazi : Address review comments -- ac1112ae4de6f5f520054b5608d202a57c296ac4 by Federico Stazi : Minor fix -- f47e1cc6aceb0365cb2e5352d61980628af7f954 by Federico Stazi : Implement all curl methods -- 929123127532589ef19f12114b8e450cc2c976a1 by Federico Stazi : Address reviews and improve code style -- 1b0a8edfd4cdffdc76f3e979a5e1b42cbe289e73 by Federico Stazi : Minor fix -- cea046d3e29b86e04bd6ce7821ee1409cea2db37 by Federico Stazi : Implement stricter policy -- cf23888b88b71add3e60524f3db3604f0ab6c386 by Federico Stazi : Improve and extend examples -- 6167cafbdec1355588c073baa8cdf17fad1fcb9e by Federico Stazi : Implement tests -- 9fed2ec09798e656cd5c518bc13f45eea1abef2e by Federico Stazi : Improved error handling -- e446ec81a13d3c567bdebe00285211d9df9dbed1 by Federico Stazi : Address review comments -- cf41ec4701a6a47ecee3af6765623ca020cebfcd by Federico Stazi <34340238+FedericoStazi@users.noreply.github.com>: Fix project name -- 9a4293a3cfd87b9b13b46a36d5eeee9d575ea519 by Federico Stazi : Fix project name -- bbebeee1a69fed2c70afc6afa2aa79aad990a778 by Federico Stazi : Fix test mock server -- eb783de3f5fc35877db5f08fd53c9a33207a416e by Federico Stazi : Address review comments -- cf6cb89bca2b0275652509afdb4d4e20e9e851ba by Federico Stazi : Minor mock server fix -- b52d9e6e4fa1f9c07a3027b4b4d564457e7a648f by Federico Stazi : Address review comments PiperOrigin-RevId: 333292204 Change-Id: I9ff27348028d9f22486492dc92c0859ff8f44d68 --- .gitmodules | 5 +- oss-internship-2020/curl/.gitignore | 1 + oss-internship-2020/curl/CMakeLists.txt | 144 +++++++++ oss-internship-2020/curl/README.md | 112 +++++++ .../curl/callbacks/callbacks.cc | 37 +++ .../curl/callbacks/callbacks.h | 24 ++ .../curl/curl_wrapper/CMakeLists.txt | 37 +++ .../curl/curl_wrapper/curl_wrapper.cc | 86 +++++ .../curl/curl_wrapper/curl_wrapper.h | 82 +++++ .../curl/examples/CMakeLists.txt | 76 +++++ oss-internship-2020/curl/examples/README.md | 38 +++ oss-internship-2020/curl/examples/example1.cc | 78 +++++ oss-internship-2020/curl/examples/example2.cc | 107 +++++++ oss-internship-2020/curl/examples/example3.cc | 166 ++++++++++ oss-internship-2020/curl/examples/example4.cc | 128 ++++++++ oss-internship-2020/curl/examples/example5.cc | 102 ++++++ oss-internship-2020/curl/examples/example6.cc | 72 +++++ oss-internship-2020/curl/sandbox.h | 67 ++++ oss-internship-2020/curl/tests/CMakeLists.txt | 28 ++ oss-internship-2020/curl/tests/test_utils.cc | 301 ++++++++++++++++++ oss-internship-2020/curl/tests/test_utils.h | 55 ++++ oss-internship-2020/curl/tests/tests.cc | 138 ++++++++ 22 files changed, 1883 insertions(+), 1 deletion(-) create mode 100644 oss-internship-2020/curl/.gitignore create mode 100644 oss-internship-2020/curl/CMakeLists.txt create mode 100644 oss-internship-2020/curl/README.md create mode 100644 oss-internship-2020/curl/callbacks/callbacks.cc create mode 100644 oss-internship-2020/curl/callbacks/callbacks.h create mode 100644 oss-internship-2020/curl/curl_wrapper/CMakeLists.txt create mode 100644 oss-internship-2020/curl/curl_wrapper/curl_wrapper.cc create mode 100644 oss-internship-2020/curl/curl_wrapper/curl_wrapper.h create mode 100644 oss-internship-2020/curl/examples/CMakeLists.txt create mode 100644 oss-internship-2020/curl/examples/README.md create mode 100644 oss-internship-2020/curl/examples/example1.cc create mode 100644 oss-internship-2020/curl/examples/example2.cc create mode 100644 oss-internship-2020/curl/examples/example3.cc create mode 100644 oss-internship-2020/curl/examples/example4.cc create mode 100644 oss-internship-2020/curl/examples/example5.cc create mode 100644 oss-internship-2020/curl/examples/example6.cc create mode 100644 oss-internship-2020/curl/sandbox.h create mode 100644 oss-internship-2020/curl/tests/CMakeLists.txt create mode 100644 oss-internship-2020/curl/tests/test_utils.cc create mode 100644 oss-internship-2020/curl/tests/test_utils.h create mode 100644 oss-internship-2020/curl/tests/tests.cc diff --git a/.gitmodules b/.gitmodules index 7449ecf..ad0eddc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,7 @@ url = https://github.com/uclouvain/openjpeg.git [submodule "oss-internship-2020/pffft/master"] path = oss-internship-2020/pffft/master - url = https://bitbucket.org/jpommier/pffft/src/master/ \ No newline at end of file + url = https://bitbucket.org/jpommier/pffft/src/master/ +[submodule "oss-internship-2020/curl/curl_wrapper/curl"] + path = oss-internship-2020/curl/curl_wrapper/curl + url = https://github.com/curl/curl diff --git a/oss-internship-2020/curl/.gitignore b/oss-internship-2020/curl/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/oss-internship-2020/curl/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/oss-internship-2020/curl/CMakeLists.txt b/oss-internship-2020/curl/CMakeLists.txt new file mode 100644 index 0000000..8d2be38 --- /dev/null +++ b/oss-internship-2020/curl/CMakeLists.txt @@ -0,0 +1,144 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.16) + +project(libcurl_sandbox) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +option(CURL_SAPI_ENABLE_EXAMPLES "" ON) +option(CURL_SAPI_ENABLE_TESTS "" ON) + +# Add callbacks used by examples and tests +if (CURL_SAPI_ENABLE_EXAMPLES OR CURL_SAPI_ENABLE_TESTS) + list(APPEND CURL_SAPI_CALLBACKS + "${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.h" + "${CMAKE_CURRENT_SOURCE_DIR}/callbacks/callbacks.cc" + ) +endif() + +# Add folder containing the non-sandboxed custom curl library +add_subdirectory(curl_wrapper) + +# Setup Sandboxed API +set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree") +set(SAPI_ENABLE_EXAMPLES ${CURL_SAPI_ENABLE_EXAMPLES} CACHE BOOL "" FORCE) +set(SAPI_ENABLE_TESTS ${CURL_SAPI_ENABLE_TESTS} CACHE BOOL "" FORCE) +add_subdirectory( + "${SAPI_ROOT}" + "${CMAKE_BINARY_DIR}/sandboxed-api-build" + EXCLUDE_FROM_ALL +) + +# Generate SAPI header +add_sapi_library(curl_sapi + + # List of all the methods in https://curl.haxx.se/libcurl/c/allfuncs.html + # Some are added or modified because the original ones are not supported + # by Sandboxed API (details can be found in curl_wrapper.h) + FUNCTIONS curl_easy_cleanup + curl_easy_duphandle + curl_easy_escape + curl_easy_getinfo + curl_easy_getinfo_ptr + curl_easy_init + curl_easy_pause + curl_easy_perform + curl_easy_recv + curl_easy_reset + curl_easy_send + curl_easy_setopt + curl_easy_setopt_ptr + curl_easy_setopt_long + curl_easy_setopt_curl_off_t + curl_easy_strerror + curl_easy_unescape + curl_easy_upkeep + curl_free + curl_getdate_sapi + curl_global_cleanup + curl_global_init + curl_global_init_mem + curl_global_sslset + curl_mime_addpart + curl_mime_data + curl_mime_data_cb + curl_mime_encoder + curl_mime_filedata + curl_mime_filename + curl_mime_free + curl_mime_headers + curl_mime_init + curl_mime_name + curl_mime_subparts + curl_mime_type + curl_multi_add_handle + curl_multi_assign + curl_multi_cleanup + curl_multi_fdset_sapi + curl_multi_info_read + curl_multi_init + curl_multi_perform + curl_multi_remove_handle + curl_multi_setopt + curl_multi_setopt_ptr + curl_multi_setopt_long + curl_multi_setopt_curl_off_t + curl_multi_socket_action + curl_multi_strerror + curl_multi_timeout + curl_multi_poll_sapi + curl_multi_wait_sapi + curl_multi_wakeup + curl_share_init + curl_share_setopt + curl_share_setopt_ptr + curl_share_setopt_long + curl_share_strerror + curl_slist_append + curl_slist_free_all + curl_url + curl_url_cleanup + curl_url_dup + curl_url_get + curl_url_set + curl_version + curl_version_info + + INPUTS curl_wrapper/curl/include/curl/curl.h + curl_wrapper/curl_wrapper.h + + LIBRARY curl_wrapper_and_callbacks + + LIBRARY_NAME Curl + + NAMESPACE "" +) + +# Include generated SAPI header +target_include_directories(curl_sapi INTERFACE + "${PROJECT_BINARY_DIR}" +) + +# Add examples +if (CURL_SAPI_ENABLE_EXAMPLES) + add_subdirectory(examples) +endif() + +# Add tests +if (CURL_SAPI_ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/oss-internship-2020/curl/README.md b/oss-internship-2020/curl/README.md new file mode 100644 index 0000000..3000838 --- /dev/null +++ b/oss-internship-2020/curl/README.md @@ -0,0 +1,112 @@ +# LibCurl Sandbox + +This library is a sandboxed version of curl's C API, +[libcurl](https://curl.haxx.se/libcurl/c/), implemented using Sandboxed API. + +## Setup + +The repository can be cloned using: `git clone --recursive [URL to this repo]` +The `--recursive` flag ensures that submodules are also cloned. + +Alternatively, if the repository has already been cloned but the submodules have +not, these can be cloned using: `git submodule update --init --recursive` + +The full list of Sandboxed API dependencies can be found on +[Sandboxed API Getting Started page](https://developers.google.com/sandboxed-api/docs/getting-started). + +The following commands, used from the current `curl/` directory, build the +library: `mkdir -p build cd build cmake .. -G Ninja -D SAPI_ROOT=[path to +sandboxed-api] cmake --build .` + +## Implementation details + +All of libcurl's methods are supported by the library. However, a few of these +have different signatures defined in the sandboxed header `custom_curl.h`, which +wraps and extends libcurl. + +This is necessary because Sandboxed API sandboxes most of libcurl correctly, but +encounters some issues when sandboxing a few methods. The simplest solution is +wrapping these methods into wrapper methods that accomplish the same tasks but +can also be sandboxed. + +The next sections describe the issues encountered and contain some information +on the signatures of the wrapper methods solving these issues. + +#### Variadic methods + +Variadic methods are currently not supported by Sandboxed API. To solve this, +these methods are defined with an additional explicit parameter in +`custom_curl.h`. + +The methods are: - `curl_easy_setopt`. Use `curl_easy_setopt_ptr`, +`curl_easy_setopt_long` or `curl_easy_setopt_curl_off_t` instead. - +`curl_easy_getinfo`. Use `curl_easy_getinfo_ptr` instead. - `curl_multi_setopt`. +Use `curl_multi_setopt_ptr`, `curl_multi_setopt_long` or +`curl_multi_setopt_curl_off_t` instead. - `curl_share_setopt`. Use +`curl_share_setopt_ptr` or `curl_share_setopt_long` instead + +#### Methods with incomplete array arguments + +Incomplete array arguments are currently not supported by Sandboxed API. To +solve this, methods taking an incomplete array argument have a wrapper in +`custom_curl.h`, and take a pointer as the argument. + +The methods are: - `curl_multi_poll`. Use `curl_multi_poll_sapi` instead. - +`curl_multi_wait`. Use `curl_multi_wait_sapi` instead. + +#### Methods with conflicts on the generated header + +Some methods create conflicts on the generated header because of redefined +`#define` directives from files included by the header. To solve this, the +conflicting types and methods are redefined in `custom_curl.h`. + +The types are: - `time_t`. Use `time_t_sapi` instead. - `fd_set`. Use +`fd_set_sapi` instead. + +The methods are: - `curl_getdate`. Use `curl_getdate_sapi` instead. - +`curl_multi_fdset`. Use `curl_multi_fdset_sapi` instead. + +#### Function pointers + +The functions whose pointers will be passed to the library's methods +(*callbacks*) can't be implemented in the files making use of the library, but +must be in other files. These files must be compiled together with the library, +and this is done by adding their absolute path to the cmake variable +`CURL_SAPI_CALLBACKS`. + +The pointers can then be obtained using an `RPCChannel` object, as shown in +`example2.cc`. + +## Examples + +The `examples` directory contains the sandboxed versions of example source codes +taken from [this page](https://curl.haxx.se/libcurl/c/example.html) on curl's +website. More information about each example can be found in the examples' +[README](examples/README.md). + +To build these examples when building the library, the cmake variable +`CURL_SAPI_ENABLE_EXAMPLES` must be set to `ON`. This enables Sandboxed API +examples as well. + +## Policy + +The `sandbox.h` file contains a policy allowing all is necessary for libcurl to +perform simple requests. It is used by all the examples, except by example3. +This example needs some additional policies and files in its namespace (since it +uses HTTPS), and the file `example3.cc` shows how to easily extend an existing +policy. + +## Testing + +The `tests` folder contains some test cases created using Google Test. The class +`CurlTestUtils` is used to facilitate some tasks that all test cases need, +including the setup of a mock local server on which test requests are performed. + +To build these tests when building the library, the cmake variable +`CURL_SAPI_ENABLE_TESTS` must be set to `ON`. This enables Sandboxed API tests +as well. + +## Callbacks + +The `callbacks.h` and `callbacks.cc` files implement all the callbacks used by +examples and tests. diff --git a/oss-internship-2020/curl/callbacks/callbacks.cc b/oss-internship-2020/curl/callbacks/callbacks.cc new file mode 100644 index 0000000..015e66c --- /dev/null +++ b/oss-internship-2020/curl/callbacks/callbacks.cc @@ -0,0 +1,37 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "callbacks.h" // NOLINT(build/include) + +#include +#include + +#include "sandboxed_api/vars.h" + +size_t WriteToMemory(char* contents, size_t size, size_t num_bytes, + void* userp) { + size_t real_size = size * num_bytes; + auto* mem = static_cast(userp); + + char* ptr = static_cast(realloc(mem->data, mem->size + real_size + 1)); + if (ptr == nullptr) return 0; + + mem->data = ptr; + auto data = static_cast(mem->data); + memcpy(&(data[mem->size]), contents, real_size); + mem->size += real_size; + data[mem->size] = 0; + + return real_size; +} diff --git a/oss-internship-2020/curl/callbacks/callbacks.h b/oss-internship-2020/curl/callbacks/callbacks.h new file mode 100644 index 0000000..306e2eb --- /dev/null +++ b/oss-internship-2020/curl/callbacks/callbacks.h @@ -0,0 +1,24 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_CALLBACKS_H +#define TESTS_CALLBACKS_H + +#include + +// Append contents to the string stored by userp, which is a sapi::LenValStruct* +extern "C" size_t WriteToMemory(char* contents, size_t size, size_t num_bytes, + void* userp); + +#endif // TESTS_CALLBACKS_H diff --git a/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt b/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt new file mode 100644 index 0000000..78d2526 --- /dev/null +++ b/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Wrapper library including curl, wrappers for some methods and callbacks +# The CURL_SAPI_CALLBACKS variable should contain the absolute paths of +# all the files implementing the callbacks +add_library(curl_wrapper_and_callbacks OBJECT + curl_wrapper.h + curl_wrapper.cc + "${CURL_SAPI_CALLBACKS}" +) +set_target_properties(curl_wrapper_and_callbacks + PROPERTIES LINKER_LANGUAGE C +) + +# Flags needed to build curl statically +set(CURL_HIDDEN_SYMBOLS OFF) +set(BUILD_SHARED_LIBS OFF) + +# Link the wrapper to the original curl library (testing is disabled in curl) +set(BUILD_TESTING OFF) +add_subdirectory(curl) +target_link_libraries(curl_wrapper_and_callbacks + CURL::libcurl + sapi::sapi +) diff --git a/oss-internship-2020/curl/curl_wrapper/curl_wrapper.cc b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.cc new file mode 100644 index 0000000..6105fbe --- /dev/null +++ b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.cc @@ -0,0 +1,86 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "curl_wrapper.h" // NOLINT(build/include) + +CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, + void* parameter) { + return curl_easy_setopt(handle, option, parameter); +} + +CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, + long parameter) { + return curl_easy_setopt(handle, option, parameter); +} + +CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option, + curl_off_t parameter) { + return curl_easy_setopt(handle, option, parameter); +} + +CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, void* parameter) { + return curl_easy_getinfo(handle, option, parameter); +} + +time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now) { + return curl_getdate(datestring, now); +} + +CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, fd_set_sapi* read_fd_set, + fd_set_sapi* write_fd_set, + fd_set_sapi* exc_fd_set, int* max_fd) { + return curl_multi_fdset(multi_handle, read_fd_set, write_fd_set, exc_fd_set, + max_fd); +} + +CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option, + void* parameter) { + return curl_multi_setopt(handle, option, parameter); +} + +CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option, + long parameter) { + return curl_multi_setopt(handle, option, parameter); +} + +CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMoption option, + curl_off_t parameter) { + return curl_multi_setopt(handle, option, parameter); +} + +CURLMcode curl_multi_poll_sapi(CURLM* multi_handle, + struct curl_waitfd* extra_fds, + unsigned int extra_nfds, int timeout_ms, + int* numfds) { + return curl_multi_poll(multi_handle, extra_fds, extra_nfds, timeout_ms, + numfds); +} + +CURLMcode curl_multi_wait_sapi(CURLM* multi_handle, + struct curl_waitfd* extra_fds, + unsigned int extra_nfds, int timeout_ms, + int* numfds) { + return curl_multi_wait(multi_handle, extra_fds, extra_nfds, timeout_ms, + numfds); +} + +CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option, + void* parameter) { + return curl_share_setopt(handle, option, parameter); +} + +CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option, + long parameter) { + return curl_share_setopt(handle, option, parameter); +} diff --git a/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h new file mode 100644 index 0000000..4ef612b --- /dev/null +++ b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h @@ -0,0 +1,82 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Wrapper for curl library + +#ifndef CURL_WRAPPER_H +#define CURL_WRAPPER_H + +#include + +extern "C" { + +// The wrapper method is needed to make the variadic argument explicit +CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, void* parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, long parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option, + curl_off_t parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, void* parameter); + +// The typedef and wrapper method are needed because the original method has +// some conflicts in curl_sapi.sapi.h +typedef time_t time_t_sapi; +time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now); + +// The typedef and wrapper method are needed because the original method has +// some conflicts in curl_sapi.sapi.h +typedef fd_set fd_set_sapi; +CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, fd_set_sapi* read_fd_set, + fd_set_sapi* write_fd_set, + fd_set_sapi* exc_fd_set, int* max_fd); + +// The wrapper method is needed to make the variadic argument explicit +CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option, + void* parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option, + long parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMoption option, + curl_off_t parameter); + +// The wrapper method is needed because incomplete array type is not supported +CURLMcode curl_multi_poll_sapi(CURLM* multi_handle, + struct curl_waitfd* extra_fds, + unsigned int extra_nfds, int timeout_ms, + int* numfds); + +// The wrapper method is needed because incomplete array type is not supported +CURLMcode curl_multi_wait_sapi(CURLM* multi_handle, + struct curl_waitfd* extra_fds, + unsigned int extra_nfds, int timeout_ms, + int* numfds); + +// The wrapper method is needed to make the variadic argument explicit +CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option, + void* parameter); + +// The wrapper method is needed to make the variadic argument explicit +CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option, + long parameter); +} + +#endif // CURL_WRAPPER_H diff --git a/oss-internship-2020/curl/examples/CMakeLists.txt b/oss-internship-2020/curl/examples/CMakeLists.txt new file mode 100644 index 0000000..9193109 --- /dev/null +++ b/oss-internship-2020/curl/examples/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# All the examples are sandboxed versions of curl's examples +# (https://curl.haxx.se/libcurl/c/example.html) + +# Example 1: simple.c +add_executable(example1 + example1.cc + ../sandbox.h +) +target_link_libraries(example1 PRIVATE + curl_sapi + sapi::sapi +) + +# Example 2: getinmemory.c +add_executable(example2 + example2.cc + ../sandbox.h +) +target_link_libraries(example2 PRIVATE + curl_sapi + sapi::sapi +) + +# Example 3: simplessl.c +add_executable(example3 + example3.cc + ../sandbox.h +) +target_link_libraries(example3 PRIVATE + curl_sapi + sapi::sapi +) + +# Example 4: multi-poll.c +add_executable(example4 + example4.cc + ../sandbox.h +) +target_link_libraries(example4 PRIVATE + curl_sapi + sapi::sapi +) + +# Example 5: multithread.c +add_executable(example5 + example5.cc + ../sandbox.h +) +target_link_libraries(example5 PRIVATE + curl_sapi + sapi::sapi +) + +# Example 6: simple.c (using transactions) +add_executable(example6 + example6.cc + ../sandbox.h +) +target_link_libraries(example6 PRIVATE + curl_sapi + sapi::sapi +) diff --git a/oss-internship-2020/curl/examples/README.md b/oss-internship-2020/curl/examples/README.md new file mode 100644 index 0000000..d653a47 --- /dev/null +++ b/oss-internship-2020/curl/examples/README.md @@ -0,0 +1,38 @@ +# LibCurl Sandbox Examples + +Each example in this folder is the sandboxed version of a code snippet from +[this page](https://curl.haxx.se/libcurl/c/example.html) on curl's website. +These examples perform some basic tasks using libcurl, and can be useful both to +understand how to use LibCurl Sandbox, but also to get an idea of how regular +and sandboxed code compare to each other. + +This is the list of the examples: + +- **example1**: sandboxed version of + [simple.c](https://curl.haxx.se/libcurl/c/simple.html). Really simple HTTP + request, downloads and prints out the page at + [example.com](http://example.com). +- **example2**: sandboxed version of + [getinmemory.c](https://curl.haxx.se/libcurl/c/getinmemory.html). Same HTTP + request as example1. The difference is that this example uses a callback to + save the page directly in memory. Only the page size is printed out. +- **example3**: sandboxed version of + [simplessl.c](https://curl.haxx.se/libcurl/c/simplessl.html). HTTPS request + of the [example.com](https://example.com) page, using SSL authentication. + This script takes 4 arguments (SSL certificates file, SSL keys file, SSL + keys password and CA certificates files), and prints out the page. +- **example4**: sandboxed version of + [multi-poll.c](https://curl.haxx.se/libcurl/c/multi-poll.html). Same HTTP + request as example1, with the addition of a polling method that can be used + to track the status of the request. The page is printed out after it is + downloaded. +- **example5**: sandboxed version of + [multithread.c](https://curl.haxx.se/libcurl/c/multithread.html). Four HTTP + request of the pages [example.com](http://example.com), + [example.edu](http://example.edu), [example.net](http://example.net) and + [example.org](http://example.org), performed at the same time using + libcurl's multithreading methods. The pages are printed out. +- **example6**: sandboxed version of + [simple.c](https://curl.haxx.se/libcurl/c/simple.html). Performs the same + tasks as example1, but Sandbox API Transactions are used to show how they + can be used to perform a simple request. diff --git a/oss-internship-2020/curl/examples/example1.cc b/oss-internship-2020/curl/examples/example1.cc new file mode 100644 index 0000000..e5ee5fe --- /dev/null +++ b/oss-internship-2020/curl/examples/example1.cc @@ -0,0 +1,78 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of simple.c +// Simple HTTP GET request + +#include + +#include "../sandbox.h" // NOLINT(build/include) + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + absl::Status status; + + // Initialize sandbox2 and sapi + CurlSapiSandbox sandbox; + status = sandbox.Init(); + if (!status.ok()) { + LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; + } + CurlApi api(&sandbox); + + // Initialize the curl session + absl::StatusOr curl_handle = api.curl_easy_init(); + if (!curl_handle.ok()) { + LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status(); + } + sapi::v::RemotePtr curl(curl_handle.value()); + if (!curl.GetValue()) LOG(FATAL) << "curl_easy_init failed: curl is NULL"; + + absl::StatusOr curl_code; + + // Specify URL to get + sapi::v::ConstCStr url("http://example.com"); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the library to follow a redirection + curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status(); + } + + // Disable authentication of peer certificate + curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status(); + } + + // Perform the request + curl_code = api.curl_easy_perform(&curl); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status(); + } + + // Cleanup curl + status = api.curl_easy_cleanup(&curl); + if (!status.ok()) { + LOG(FATAL) << "curl_easy_cleanup failed: " << status; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/examples/example2.cc b/oss-internship-2020/curl/examples/example2.cc new file mode 100644 index 0000000..c7abe0c --- /dev/null +++ b/oss-internship-2020/curl/examples/example2.cc @@ -0,0 +1,107 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of getinmemory.c +// HTTP GET request using callbacks + +#include +#include + +#include "../sandbox.h" // NOLINT(build/include) + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + absl::Status status; + + // Initialize sandbox2 and sapi + CurlSapiSandbox sandbox; + status = sandbox.Init(); + if (!status.ok()) { + LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; + } + CurlApi api(&sandbox); + + // Generate pointer to WriteMemoryCallback function + void* function_ptr; + status = sandbox.rpc_channel()->Symbol("WriteToMemory", &function_ptr); + if (!status.ok()) { + LOG(FATAL) << "sapi::Sandbox::rpc_channel().Symbol failed: " << status; + } + sapi::v::RemotePtr remote_function_ptr(function_ptr); + + // Initialize the curl session + absl::StatusOr curl_handle = api.curl_easy_init(); + if (!curl_handle.ok()) { + LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status(); + } + sapi::v::RemotePtr curl(curl_handle.value()); + if (!curl.GetValue()) { + LOG(FATAL) << "curl_easy_init failed: curl is NULL"; + } + + absl::StatusOr curl_code; + + // Specify URL to get + sapi::v::ConstCStr url("http://example.com"); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set WriteMemoryCallback as the write function + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION, + &remote_function_ptr); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Pass 'chunk' struct to the callback function + sapi::v::LenVal chunk(0); + curl_code = + api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set a user agent + sapi::v::ConstCStr user_agent("libcurl-agent/1.0"); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT, + user_agent.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Perform the request + curl_code = api.curl_easy_perform(&curl); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status(); + } + + // Retrieve memory size + status = sandbox.TransferFromSandboxee(&chunk); + if (!status.ok()) { + LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status; + } + std::cout << "memory size: " << chunk.GetDataSize() << " bytes" << std::endl; + + // Cleanup curl + status = api.curl_easy_cleanup(&curl); + if (!status.ok()) { + LOG(FATAL) << "curl_easy_cleanup failed: " << status; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/examples/example3.cc b/oss-internship-2020/curl/examples/example3.cc new file mode 100644 index 0000000..1a73c93 --- /dev/null +++ b/oss-internship-2020/curl/examples/example3.cc @@ -0,0 +1,166 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of simplessl.c +// HTTPS GET request + +#include + +#include "../sandbox.h" // NOLINT(build/include) + +class CurlSapiSandboxEx3 : public CurlSapiSandbox { + public: + CurlSapiSandboxEx3(std::string ssl_certificate, std::string ssl_key, + std::string ca_certificates) + : ssl_certificate(ssl_certificate), + ssl_key(ssl_key), + ca_certificates(ca_certificates) {} + + private: + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder*) override { + // Add the syscalls and files missing in CurlSandbox to a new PolicyBuilder + auto policy_builder = std::make_unique(); + (*policy_builder) + .AllowFutexOp(FUTEX_WAIT_PRIVATE) + .AllowGetPIDs() + .AllowGetRandom() + .AllowHandleSignals() + .AllowSyscall(__NR_sysinfo) + .AddFile(ssl_certificate) + .AddFile(ssl_key) + .AddFile(ca_certificates); + // Provide the new PolicyBuilder to ModifyPolicy in CurlSandbox + return CurlSapiSandbox::ModifyPolicy(policy_builder.get()); + } + + std::string ssl_certificate; + std::string ssl_key; + std::string ca_certificates; +}; + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + absl::Status status; + + // Get input parameters (should be absolute paths) + if (argc != 5) { + LOG(FATAL) << "wrong number of arguments (4 expected)"; + } + std::string ssl_certificate = argv[1]; + std::string ssl_key = argv[2]; + std::string ssl_key_password = argv[3]; + std::string ca_certificates = argv[4]; + + // Initialize sandbox2 and sapi + CurlSapiSandboxEx3 sandbox(ssl_certificate, ssl_key, ca_certificates); + status = sandbox.Init(); + if (!status.ok()) { + LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; + } + CurlApi api(&sandbox); + + absl::StatusOr curl_code; + + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) + curl_code = api.curl_global_init(3l); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_global_init failed: " << curl_code.status(); + } + + // Initialize curl easy handle + absl::StatusOr curl_handle = api.curl_easy_init(); + if (!curl_handle.ok()) { + LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status(); + } + sapi::v::RemotePtr curl(curl_handle.value()); + if (!curl.GetValue()) { + LOG(FATAL) << "curl_easy_init failed: curl is NULL"; + } + + // Specify URL to get (using HTTPS) + sapi::v::ConstCStr url("https://example.com"); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the SSL certificate type to "PEM" + sapi::v::ConstCStr ssl_cert_type("PEM"); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE, + ssl_cert_type.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the certificate for client authentication + sapi::v::ConstCStr sapi_ssl_certificate(ssl_certificate.c_str()); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT, + sapi_ssl_certificate.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the private key for client authentication + sapi::v::ConstCStr sapi_ssl_key(ssl_key.c_str()); + curl_code = + api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLKEY, sapi_ssl_key.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the password used to protect the private key + sapi::v::ConstCStr sapi_ssl_key_password(ssl_key_password.c_str()); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD, + sapi_ssl_key_password.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Set the file with the certificates vaildating the server + sapi::v::ConstCStr sapi_ca_certificates(ca_certificates.c_str()); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO, + sapi_ca_certificates.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Verify the authenticity of the server + curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status(); + } + + // Perform the request + curl_code = api.curl_easy_perform(&curl); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status(); + } + + // Cleanup curl easy handle + status = api.curl_easy_cleanup(&curl); + if (!status.ok()) { + LOG(FATAL) << "curl_easy_cleanup failed: " << status; + } + + // Cleanup curl + status = api.curl_global_cleanup(); + if (!status.ok()) { + LOG(FATAL) << "curl_global_cleanup failed: " << status; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/examples/example4.cc b/oss-internship-2020/curl/examples/example4.cc new file mode 100644 index 0000000..9103866 --- /dev/null +++ b/oss-internship-2020/curl/examples/example4.cc @@ -0,0 +1,128 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of multi-poll.c +// HTTP GET request with polling + +#include + +#include "../sandbox.h" // NOLINT(build/include) +#include "curl_sapi.sapi.h" // NOLINT(build/include) +#include "sandboxed_api/util/flag.h" + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + absl::Status status; + + // Initialize sandbox2 and sapi + CurlSapiSandbox sandbox; + status = sandbox.Init(); + if (!status.ok()) { + LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; + } + CurlApi api(&sandbox); + + // Number of running handles + sapi::v::Int still_running(1); + + absl::StatusOr curl_code; + + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) + curl_code = api.curl_global_init(3l); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_global_init failed: " << curl_code.status(); + } + + // Initialize http_handle + absl::StatusOr curl_handle = api.curl_easy_init(); + if (!curl_handle.ok()) { + LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status(); + } + sapi::v::RemotePtr http_handle(curl_handle.value()); + if (!http_handle.GetValue()) { + LOG(FATAL) << "curl_easy_init failed: http_handle is NULL"; + } + + // Specify URL to get + sapi::v::ConstCStr url("http://example.com"); + curl_code = + api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Initialize multi_handle + absl::StatusOr curlm_handle = api.curl_multi_init(); + if (!curlm_handle.ok()) { + LOG(FATAL) << "curl_multi_init failed: " << curlm_handle.status(); + } + sapi::v::RemotePtr multi_handle(curlm_handle.value()); + if (!multi_handle.GetValue()) { + LOG(FATAL) << "curl_multi_init failed: multi_handle is NULL"; + } + + // Add http_handle to the multi stack + curl_code = api.curl_multi_add_handle(&multi_handle, &http_handle); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_multi_add_handle failed: " << curl_code.status(); + } + + while (still_running.GetValue()) { + sapi::v::Int numfds(0); + + // Perform the request + curl_code = api.curl_multi_perform(&multi_handle, still_running.PtrBoth()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_mutli_perform failed: " << curl_code.status(); + } + + if (still_running.GetValue()) { + // Wait for an event or timeout + sapi::v::NullPtr null_ptr; + curl_code = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, 1000, + numfds.PtrBoth()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_multi_poll_sapi failed: " << curl_code.status(); + } + } + } + + // Remove http_handle from the multi stack + curl_code = api.curl_multi_remove_handle(&multi_handle, &http_handle); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_multi_remove_handle failed: " << curl_code.status(); + } + + // Cleanup http_handle + status = api.curl_easy_cleanup(&http_handle); + if (!status.ok()) { + LOG(FATAL) << "curl_easy_cleanup failed: " << status; + } + + // Cleanup multi_handle + curl_code = api.curl_multi_cleanup(&multi_handle); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_multi_cleanup failed: " << curl_code.status(); + } + + // Cleanup curl + status = api.curl_global_cleanup(); + if (!status.ok()) { + LOG(FATAL) << "curl_global_cleanup failed: " << status; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/examples/example5.cc b/oss-internship-2020/curl/examples/example5.cc new file mode 100644 index 0000000..8cc6a16 --- /dev/null +++ b/oss-internship-2020/curl/examples/example5.cc @@ -0,0 +1,102 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of multithread.c +// Multithreaded HTTP GET requests + +#include + +#include + +#include "../sandbox.h" // NOLINT(build/include) + +void pull_one_url(const std::string& url, CurlApi& api) { + // Initialize the curl session + absl::StatusOr curl_handle = api.curl_easy_init(); + if (!curl_handle.ok()) { + LOG(FATAL) << "curl_easy_init failed: " << curl_handle.status(); + } + sapi::v::RemotePtr curl(curl_handle.value()); + if (!curl.GetValue()) { + LOG(FATAL) << "curl_easy_init failed: curl is NULL"; + } + + absl::StatusOr curl_code; + + // Specify URL to get + sapi::v::ConstCStr sapi_url(url.c_str()); + curl_code = + api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore()); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); + } + + // Perform the request + curl_code = api.curl_easy_perform(&curl); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status(); + } + + // Cleanup curl + absl::Status status = api.curl_easy_cleanup(&curl); + if (!status.ok()) { + LOG(FATAL) << "curl_easy_cleanup failed: " << status; + } +} + +const std::vector urls = { + "http://example.com", "http://example.edu", "http://example.net", + "http://example.org"}; + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + + absl::Status status; + + // Initialize sandbox2 and sapi + CurlSapiSandbox sandbox; + status = sandbox.Init(); + if (!status.ok()) { + LOG(FATAL) << "Couldn't initialize Sandboxed API: " << status; + } + CurlApi api(&sandbox); + + absl::StatusOr curl_code; + + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) + curl_code = api.curl_global_init(3l); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_global_init failed: " << curl_code.status(); + } + + // Create the threads + std::vector threads; + for (auto& url : urls) { + threads.emplace_back(pull_one_url, std::ref(url), std::ref(api)); + } + + // Join the threads + for (auto& thread : threads) { + thread.join(); + } + + // Cleanup curl + status = api.curl_global_cleanup(); + if (!status.ok()) { + LOG(FATAL) << "curl_global_cleanup failed: " << status; + } + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/examples/example6.cc b/oss-internship-2020/curl/examples/example6.cc new file mode 100644 index 0000000..15fb1c2 --- /dev/null +++ b/oss-internship-2020/curl/examples/example6.cc @@ -0,0 +1,72 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Sandboxed version of simple.c using transactions +// Simple HTTP GET request + +#include + +#include "../sandbox.h" // NOLINT(build/include) +#include "sandboxed_api/transaction.h" + +class CurlTransaction : public sapi::Transaction { + public: + explicit CurlTransaction(std::unique_ptr sandbox) + : sapi::Transaction(std::move(sandbox)) { + sapi::Transaction::SetTimeLimit(kTimeOutVal); + } + + private: + // Default timeout value for each transaction run. + static constexpr time_t kTimeOutVal = 2; + + // The main processing function. + absl::Status Main() override; +}; + +absl::Status CurlTransaction::Main() { + CurlApi api(sandbox()); + + // Initialize the curl session + SAPI_ASSIGN_OR_RETURN(void* curl_remote, api.curl_easy_init()); + sapi::v::RemotePtr curl(curl_remote); + TRANSACTION_FAIL_IF_NOT(curl.GetValue(), "curl_easy_init failed"); + + // Specify URL to get + sapi::v::ConstCStr url("http://example.com"); + SAPI_ASSIGN_OR_RETURN( + int setopt_url_code, + api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore())); + TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK, + "curl_easy_setopt_ptr failed"); + + // Perform the request + SAPI_ASSIGN_OR_RETURN(int perform_code, api.curl_easy_perform(&curl)); + TRANSACTION_FAIL_IF_NOT(setopt_url_code == CURLE_OK, + "curl_easy_perform failed"); + + // Cleanup curl + TRANSACTION_FAIL_IF_NOT(api.curl_easy_cleanup(&curl).ok(), + "curl_easy_cleanup failed"); + + return absl::OkStatus(); +} + +int main(int argc, char* argv[]) { + CurlTransaction curl{std::make_unique()}; + absl::Status status = curl.Run(); + CHECK(status.ok()) << "CurlTransaction failed"; + + return EXIT_SUCCESS; +} diff --git a/oss-internship-2020/curl/sandbox.h b/oss-internship-2020/curl/sandbox.h new file mode 100644 index 0000000..914617f --- /dev/null +++ b/oss-internship-2020/curl/sandbox.h @@ -0,0 +1,67 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SANDBOX_H_ +#define SANDBOX_H_ + +#include +#include // For mmap arguments +#include + +#include + +#include "curl_sapi.sapi.h" // NOLINT(build/include) +#include "sandboxed_api/sandbox2/util/bpf_helper.h" + +class CurlSapiSandbox : public CurlSandbox { + protected: + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder* policy_builder) override { + // Return a new policy + return (*policy_builder) + .AllowDynamicStartup() + .AllowExit() + .AllowFork() + .AllowFutexOp(FUTEX_WAKE_PRIVATE) + .AllowMmap() + .AllowOpen() + .AllowRead() + .AllowSafeFcntl() + .AllowWrite() + .AllowSyscalls({ + __NR_accept, + __NR_access, + __NR_bind, + __NR_connect, + __NR_getpeername, + __NR_getsockname, + __NR_getsockopt, + __NR_ioctl, + __NR_listen, + __NR_madvise, + __NR_poll, + __NR_recvfrom, + __NR_recvmsg, + __NR_sendmmsg, + __NR_sendto, + __NR_setsockopt, + __NR_socket, + }) + .AllowUnrestrictedNetworking() + .AddDirectory("/lib") + .BuildOrDie(); + } +}; + +#endif // SANDBOX_H_ diff --git a/oss-internship-2020/curl/tests/CMakeLists.txt b/oss-internship-2020/curl/tests/CMakeLists.txt new file mode 100644 index 0000000..6fd4556 --- /dev/null +++ b/oss-internship-2020/curl/tests/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(GoogleTest) + +add_executable(tests + test_utils.h + test_utils.cc + tests.cc +) + +target_link_libraries(tests + curl_sapi sapi::sapi + gtest gmock gtest_main +) + +gtest_discover_tests(tests) diff --git a/oss-internship-2020/curl/tests/test_utils.cc b/oss-internship-2020/curl/tests/test_utils.cc new file mode 100644 index 0000000..8ea2897 --- /dev/null +++ b/oss-internship-2020/curl/tests/test_utils.cc @@ -0,0 +1,301 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test_utils.h" // NOLINT(build/include) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include // NOLINT(build/c++11) + +int CurlTestUtils::port_; +std::thread CurlTestUtils::server_thread_; + +absl::Status CurlTestUtils::CurlTestSetUp() { + // Initialize sandbox2 and sapi + sandbox_ = std::make_unique(); + absl::Status init = sandbox_->Init(); + if (!init.ok()) { + return init; + } + api_ = std::make_unique(sandbox_.get()); + + // Initialize curl + absl::StatusOr curl_handle = api_->curl_easy_init(); + if (!curl_handle.ok()) { + return curl_handle.status(); + } + if (!curl_handle.value()) { + return absl::UnavailableError("curl_easy_init returned NULL "); + } + curl_ = std::make_unique(curl_handle.value()); + + absl::StatusOr curl_code; + + // Specify request URL + sapi::v::ConstCStr sapi_url(kUrl.data()); + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL, + sapi_url.PtrBefore()); + if (!curl_code.ok()) { + return curl_code.status(); + } + if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_ptr returned with the error code " + + curl_code.value()); + } + + // Set port + curl_code = api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_); + if (!curl_code.ok()) { + return curl_code.status(); + } + if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_long returned with the error code " + + curl_code.value()); + } + + // Generate pointer to the WriteToMemory callback + void* function_ptr; + absl::Status symbol = + sandbox_->rpc_channel()->Symbol("WriteToMemory", &function_ptr); + if (!symbol.ok()) { + return symbol; + } + sapi::v::RemotePtr remote_function_ptr(function_ptr); + + // Set WriteToMemory as the write function + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION, + &remote_function_ptr); + if (!curl_code.ok()) { + return curl_code.status(); + } + if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_ptr returned with the error code " + + curl_code.value()); + } + + // Pass memory chunk object to the callback + chunk_ = std::make_unique(0); + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA, + chunk_->PtrBoth()); + if (!curl_code.ok()) { + return curl_code.status(); + } + if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_ptr returned with the error code " + + curl_code.value()); + } + + return absl::OkStatus(); +} + +absl::Status CurlTestUtils::CurlTestTearDown() { + // Cleanup curl + return api_->curl_easy_cleanup(curl_.get()); +} + +absl::StatusOr CurlTestUtils::PerformRequest() { + // Perform the request + absl::StatusOr curl_code = api_->curl_easy_perform(curl_.get()); + if (!curl_code.ok()) { + return curl_code.status(); + } + if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_perform returned with the error code " + curl_code.value()); + } + + // Get pointer to the memory chunk + absl::Status status = sandbox_->TransferFromSandboxee(chunk_.get()); + if (!status.ok()) { + return status; + } + + return std::string{reinterpret_cast(chunk_->GetData())}; +} + +namespace { + +// Read the socket until str is completely read +std::string ReadUntil(const int socket, const std::string& str, + const size_t max_request_size) { + std::string str_read; + str_read.reserve(max_request_size); + + // Read one char at a time until str is suffix of buf + while (!absl::EndsWith(str_read, str)) { + char next_char; + if (str_read.size() >= max_request_size || + read(socket, &next_char, 1) < 1) { + return ""; + } + str_read += next_char; + } + + return str_read; +} + +// Parse HTTP headers to return the Content-Length +ssize_t GetContentLength(const std::string& headers) { + // Find the Content-Length header + std::string::size_type length_header_start = headers.find("Content-Length: "); + + // There is no Content-Length field + if (length_header_start == std::string::npos) { + return 0; + } + + // Find Content-Length string + std::string::size_type length_start = + length_header_start + std::string{"Content-Length: "}.size(); + std::string::size_type length_bytes = + headers.find("\r\n", length_start) - length_start; + + // length_bytes exceeds maximum + if (length_bytes >= 64) { + return -1; + } + + // Convert string to int and return + return std::stoi(headers.substr(length_start, length_bytes)); +} + +// Read exactly content_bytes from the socket +std::string ReadExact(int socket, size_t content_bytes) { + std::string str_read; + str_read.reserve(content_bytes); + + // Read one char at a time until all chars are read + while (str_read.size() < content_bytes) { + char next_char; + if (read(socket, &next_char, 1) < 1) { + return ""; + } + str_read += next_char; + } + + return str_read; +} + +// Listen on the socket and answer back to requests +void ServerLoop(int listening_socket, sockaddr_in socket_address) { + socklen_t socket_address_size = sizeof(socket_address); + + // Listen on the socket (maximum 1 connection) + if (listen(listening_socket, 1) == -1) { + return; + } + + // Keep accepting connections until the thread is terminated + // (i.e. server_thread_ is assigned to a new thread or destroyed) + for (;;) { + // File descriptor to the connection socket + // This blocks the thread until a connection is established + int accepted_socket = + accept(listening_socket, reinterpret_cast(&socket_address), + reinterpret_cast(&socket_address_size)); + if (accepted_socket == -1) { + return; + } + + constexpr int kMaxRequestSize = 4096; + + // Read until the end of the headers + std::string headers = + ReadUntil(accepted_socket, "\r\n\r\n", kMaxRequestSize); + + if (headers == "") { + close(accepted_socket); + return; + } + + // Get the length of the request content + ssize_t content_length = GetContentLength(headers); + if (content_length > kMaxRequestSize - headers.size() || + content_length < 0) { + close(accepted_socket); + return; + } + + // Read the request content + std::string content = ReadExact(accepted_socket, content_length); + + // Prepare a response for the request + std::string http_response = + "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: "; + + if (headers.substr(0, 3) == "GET") { + http_response += "2\r\n\r\nOK"; + + } else if (headers.substr(0, 4) == "POST") { + http_response += + std::to_string(content.size()) + "\r\n\r\n" + std::string{content}; + + } else { + close(accepted_socket); + return; + } + + // Ignore any errors, the connection will be closed anyway + write(accepted_socket, http_response.c_str(), http_response.size()); + + // Close the socket + close(accepted_socket); + } +} + +} // namespace + +void CurlTestUtils::StartMockServer() { + // Get the socket file descriptor + int listening_socket = socket(AF_INET, SOCK_STREAM, 0); + + // Create the socket address object + // The port is set to 0, meaning that it will be auto assigned + // Only local connections can access this socket + sockaddr_in socket_address{AF_INET, 0, htonl(INADDR_LOOPBACK)}; + socklen_t socket_address_size = sizeof(socket_address); + if (listening_socket == -1) { + return; + } + + // Bind the file descriptor to the socket address object + if (bind(listening_socket, reinterpret_cast(&socket_address), + socket_address_size) == -1) { + return; + } + + // Assign an available port to the socket address object + if (getsockname(listening_socket, + reinterpret_cast(&socket_address), + &socket_address_size) == -1) { + return; + } + + // Get the port number + port_ = ntohs(socket_address.sin_port); + + // Set server_thread_ operation to socket listening + server_thread_ = std::thread(ServerLoop, listening_socket, socket_address); +} diff --git a/oss-internship-2020/curl/tests/test_utils.h b/oss-internship-2020/curl/tests/test_utils.h new file mode 100644 index 0000000..fa74769 --- /dev/null +++ b/oss-internship-2020/curl/tests/test_utils.h @@ -0,0 +1,55 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_H_ +#define TESTS_H_ + +#include "../sandbox.h" // NOLINT(build/include) +#include "curl_sapi.sapi.h" // NOLINT(build/include) +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/util/status_matchers.h" + +// Helper class that can be used to test Curl Sandboxed +class CurlTestUtils { + protected: + // Initialize and set up the curl handle + absl::Status CurlTestSetUp(); + // Clean up the curl handle + absl::Status CurlTestTearDown(); + + // Perform a request to the mock server, return the response + absl::StatusOr PerformRequest(); + + // Start a mock server (only once) that will manage connections for the tests + // The server listens on a port asynchronously by creating a thread + // The port number is stored in port_ + // Responds with "OK" to a GET request + // Responds with the POST request fields to a POST request + static void StartMockServer(); + + std::unique_ptr sandbox_; + std::unique_ptr api_; + std::unique_ptr curl_; + + static std::thread server_thread_; + + static constexpr absl::string_view kUrl = "http://127.0.0.1/"; + static int port_; + + private: + std::unique_ptr chunk_; +}; + +#endif // TESTS_H_ diff --git a/oss-internship-2020/curl/tests/tests.cc b/oss-internship-2020/curl/tests/tests.cc new file mode 100644 index 0000000..c786169 --- /dev/null +++ b/oss-internship-2020/curl/tests/tests.cc @@ -0,0 +1,138 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test_utils.h" // NOLINT(build/include) + +class CurlTest : public CurlTestUtils, public ::testing::Test { + protected: + void SetUp() override { + // Start mock server, get port number and check for any error + StartMockServer(); + ASSERT_TRUE(server_thread_.joinable()); + ASSERT_TRUE(CurlTestSetUp().ok()); + } + + void TearDown() override { + ASSERT_TRUE(CurlTestTearDown().ok()); + // Detach the server thread + server_thread_.detach(); + } +}; + +TEST_F(CurlTest, EffectiveUrl) { + sapi::v::RemotePtr effective_url_ptr(nullptr); + + ASSERT_TRUE(PerformRequest().ok()); + + // Get effective URL + SAPI_ASSERT_OK_AND_ASSIGN( + int getinfo_code, + api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_EFFECTIVE_URL, + effective_url_ptr.PtrBoth())); + ASSERT_EQ(getinfo_code, CURLE_OK); + + // Store effective URL in a string + SAPI_ASSERT_OK_AND_ASSIGN(std::string effective_url, + sandbox_->GetCString(sapi::v::RemotePtr( + effective_url_ptr.GetPointedVar()))); + + // Compare effective URL with original URL + ASSERT_EQ(effective_url, kUrl); +} + +TEST_F(CurlTest, EffectivePort) { + sapi::v::Int effective_port; + + ASSERT_TRUE(PerformRequest().ok()); + + // Get effective port + SAPI_ASSERT_OK_AND_ASSIGN(int getinfo_code, api_->curl_easy_getinfo_ptr( + curl_.get(), CURLINFO_PRIMARY_PORT, + effective_port.PtrBoth())); + ASSERT_EQ(getinfo_code, CURLE_OK); + + // Compare effective port with port set by the mock server + ASSERT_EQ(effective_port.GetValue(), port_); +} + +TEST_F(CurlTest, ResponseCode) { + sapi::v::Int response_code; + + ASSERT_TRUE(PerformRequest().ok()); + + // Get response code + SAPI_ASSERT_OK_AND_ASSIGN( + int getinfo_code, + api_->curl_easy_getinfo_ptr(curl_.get(), CURLINFO_RESPONSE_CODE, + response_code.PtrBoth())); + ASSERT_EQ(getinfo_code, CURLE_OK); + + // Check response code + ASSERT_EQ(response_code.GetValue(), 200); +} + +TEST_F(CurlTest, ContentType) { + sapi::v::RemotePtr content_type_ptr(nullptr); + + ASSERT_TRUE(PerformRequest().ok()); + + // Get effective URL + SAPI_ASSERT_OK_AND_ASSIGN(int getinfo_code, api_->curl_easy_getinfo_ptr( + curl_.get(), CURLINFO_CONTENT_TYPE, + content_type_ptr.PtrBoth())); + ASSERT_EQ(getinfo_code, CURLE_OK); + + // Store content type in a string + SAPI_ASSERT_OK_AND_ASSIGN(std::string content_type, + sandbox_->GetCString(sapi::v::RemotePtr( + content_type_ptr.GetPointedVar()))); + + // Compare content type with "text/plain" + ASSERT_EQ(content_type, "text/plain"); +} + +TEST_F(CurlTest, GETResponse) { + SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest()); + + // Compare response with expected response + ASSERT_EQ(response, "OK"); +} + +TEST_F(CurlTest, POSTResponse) { + sapi::v::ConstCStr post_fields("postfields"); + + // Set request method to POST + SAPI_ASSERT_OK_AND_ASSIGN(int setopt_post, api_->curl_easy_setopt_long( + curl_.get(), CURLOPT_POST, 1l)); + ASSERT_EQ(setopt_post, CURLE_OK); + + // Set the size of the POST fields + SAPI_ASSERT_OK_AND_ASSIGN( + int setopt_post_fields_size, + api_->curl_easy_setopt_long(curl_.get(), CURLOPT_POSTFIELDSIZE, + post_fields.GetSize())); + ASSERT_EQ(setopt_post_fields_size, CURLE_OK); + + // Set the POST fields + SAPI_ASSERT_OK_AND_ASSIGN( + int setopt_post_fields, + api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_POSTFIELDS, + post_fields.PtrBefore())); + ASSERT_EQ(setopt_post_fields, CURLE_OK); + + SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest()); + + // Compare response with expected response + ASSERT_EQ(std::string(post_fields.GetData()), response); +}