From eb783de3f5fc35877db5f08fd53c9a33207a416e Mon Sep 17 00:00:00 2001 From: Federico Stazi Date: Wed, 16 Sep 2020 15:51:34 +0000 Subject: [PATCH] Address review comments --- .../curl/curl_wrapper/CMakeLists.txt | 1 + .../curl/curl_wrapper/curl_wrapper.h | 70 ++- oss-internship-2020/curl/examples/README.md | 2 +- .../curl/examples/callbacks.cc | 21 +- oss-internship-2020/curl/examples/callbacks.h | 5 - oss-internship-2020/curl/examples/example1.cc | 36 +- oss-internship-2020/curl/examples/example2.cc | 57 ++- oss-internship-2020/curl/examples/example3.cc | 74 ++-- oss-internship-2020/curl/examples/example4.cc | 66 ++- oss-internship-2020/curl/examples/example5.cc | 74 ++-- oss-internship-2020/curl/tests/callbacks.cc | 20 +- oss-internship-2020/curl/tests/callbacks.h | 9 +- oss-internship-2020/curl/tests/test_utils.cc | 412 +++++++++--------- oss-internship-2020/curl/tests/test_utils.h | 12 +- oss-internship-2020/curl/tests/tests.cc | 38 +- 15 files changed, 438 insertions(+), 459 deletions(-) diff --git a/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt b/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt index 0ef04ec..bdaf053 100644 --- a/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt +++ b/oss-internship-2020/curl/curl_wrapper/CMakeLists.txt @@ -33,4 +33,5 @@ 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.h b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h index ff93354..4ef612b 100644 --- a/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h +++ b/oss-internship-2020/curl/curl_wrapper/curl_wrapper.h @@ -19,68 +19,64 @@ #include -// The wrapper method is needed to make the variadic argument explicit -extern "C" CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, - void* parameter); +extern "C" { // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, - long parameter); +CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, void* parameter); // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option, - curl_off_t parameter); +CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, long parameter); // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, - void* parameter); +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 -extern "C" typedef time_t time_t_sapi; -extern "C" time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now); +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 -extern "C" typedef fd_set fd_set_sapi; -extern "C" 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); +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 -extern "C" CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option, - void* parameter); +CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option, + void* parameter); // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option, - long parameter); +CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option, + long parameter); // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, - CURLMoption option, - curl_off_t parameter); +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 -extern "C" CURLMcode curl_multi_poll_sapi(CURLM* multi_handle, - struct curl_waitfd* extra_fds, - unsigned int extra_nfds, - int timeout_ms, int* numfds); +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 -extern "C" CURLMcode curl_multi_wait_sapi(CURLM* multi_handle, - struct curl_waitfd* extra_fds, - unsigned int extra_nfds, - int timeout_ms, int* numfds); +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 -extern "C" CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option, - void* parameter); +CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option, + void* parameter); // The wrapper method is needed to make the variadic argument explicit -extern "C" CURLSHcode curl_share_setopt_long(CURLSH* handle, - CURLSHoption option, - long parameter); +CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option, + long parameter); +} #endif // CURL_WRAPPER_H diff --git a/oss-internship-2020/curl/examples/README.md b/oss-internship-2020/curl/examples/README.md index 8db577c..4df51bd 100644 --- a/oss-internship-2020/curl/examples/README.md +++ b/oss-internship-2020/curl/examples/README.md @@ -8,5 +8,5 @@ This is the list of the examples: - **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 threads' status and the pages are printed out. +- **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. \ No newline at end of file diff --git a/oss-internship-2020/curl/examples/callbacks.cc b/oss-internship-2020/curl/examples/callbacks.cc index c731ce7..8da644d 100644 --- a/oss-internship-2020/curl/examples/callbacks.cc +++ b/oss-internship-2020/curl/examples/callbacks.cc @@ -18,22 +18,25 @@ #include #include +#include "sandboxed_api/vars.h" + // Function taken from curl's getinmemory.c size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) { - size_t realsize = size * nmemb; - struct MemoryStruct* mem = (struct MemoryStruct*)userp; + size_t real_size = size * nmemb; + auto* mem = static_cast(userp); - char* ptr = (char*)realloc(mem->memory, mem->size + realsize + 1); - if (ptr == NULL) { // Out of memory + char* ptr = static_cast(realloc(mem->data, mem->size + real_size + 1)); + if (ptr == nullptr) { // Out of memory std::cout << "not enough memory (realloc returned NULL)\n"; return 0; } - mem->memory = ptr; - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 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 realsize; + return real_size; } diff --git a/oss-internship-2020/curl/examples/callbacks.h b/oss-internship-2020/curl/examples/callbacks.h index b89f31f..da163f6 100644 --- a/oss-internship-2020/curl/examples/callbacks.h +++ b/oss-internship-2020/curl/examples/callbacks.h @@ -17,11 +17,6 @@ #include -extern "C" struct MemoryStruct { - char* memory; - size_t size; -}; - extern "C" size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp); diff --git a/oss-internship-2020/curl/examples/example1.cc b/oss-internship-2020/curl/examples/example1.cc index 77171b5..9866f36 100644 --- a/oss-internship-2020/curl/examples/example1.cc +++ b/oss-internship-2020/curl/examples/example1.cc @@ -24,8 +24,6 @@ int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); absl::Status status; - sapi::StatusOr status_or_curl; - sapi::StatusOr status_or_int; // Initialize sandbox2 and sapi CurlSapiSandbox sandbox; @@ -36,36 +34,38 @@ int main(int argc, char* argv[]) { CurlApi api(&sandbox); // Initialize the curl session - status_or_curl = api.curl_easy_init(); - if (!status_or_curl.ok()) { - LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); + 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(status_or_curl.value()); + 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"); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_perform(&curl); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); + 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 diff --git a/oss-internship-2020/curl/examples/example2.cc b/oss-internship-2020/curl/examples/example2.cc index 6aa196d..6a200ba 100644 --- a/oss-internship-2020/curl/examples/example2.cc +++ b/oss-internship-2020/curl/examples/example2.cc @@ -30,8 +30,6 @@ int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); absl::Status status; - sapi::StatusOr status_or_curl; - sapi::StatusOr status_or_int; // Initialize sandbox2 and sapi CurlSapiSandbox sandbox; @@ -42,63 +40,64 @@ int main(int argc, char* argv[]) { CurlApi api(&sandbox); // Generate pointer to WriteMemoryCallback function - sapi::RPCChannel rpcc(sandbox.comms()); - size_t (*_function_ptr)(void*, size_t, size_t, void*); - status = rpcc.Symbol("WriteMemoryCallback", (void**)&_function_ptr); + void* function_ptr; + status = sandbox.rpc_channel()->Symbol("WriteMemoryCallback", &function_ptr); if (!status.ok()) { - LOG(FATAL) << "rpcc.Symbol failed: " << status; + LOG(FATAL) << "sapi::Sandbox::rpc_channel().Symbol failed: " << status; } - sapi::v::RemotePtr remote_function_ptr((void*)_function_ptr); + sapi::v::RemotePtr remote_function_ptr(function_ptr); // Initialize the curl session - status_or_curl = api.curl_easy_init(); - if (!status_or_curl.ok()) { - LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); + 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(status_or_curl.value()); + 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"); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION, - &remote_function_ptr); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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::Struct chunk; - status_or_int = + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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"); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT, - user_agent.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_perform(&curl); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); + 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 sapi::v::Int size; - size.SetRemote(&((MemoryStruct*)chunk.GetRemote())->size); + size.SetRemote(&(static_cast(chunk.GetRemote()))->size); status = sandbox.TransferFromSandboxee(&size); if (!status.ok()) { LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status; diff --git a/oss-internship-2020/curl/examples/example3.cc b/oss-internship-2020/curl/examples/example3.cc index 4db690c..0bac0c7 100644 --- a/oss-internship-2020/curl/examples/example3.cc +++ b/oss-internship-2020/curl/examples/example3.cc @@ -55,8 +55,6 @@ int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); absl::Status status; - sapi::StatusOr status_or_int; - sapi::StatusOr status_or_curl; // Get input parameters (should be absolute paths) if (argc != 5) { @@ -75,79 +73,81 @@ int main(int argc, char* argv[]) { } CurlApi api(&sandbox); + absl::StatusOr curl_code; + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) - status_or_int = api.curl_global_init(3l); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); + 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 - status_or_curl = api.curl_easy_init(); - if (!status_or_curl.ok()) { - LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); + 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(status_or_curl.value()); + 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"); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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"); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE, - ssl_cert_type.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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()); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT, - sapi_ssl_certificate.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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()); - status_or_int = + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLKEY, sapi_ssl_key.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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()); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD, - sapi_ssl_key_password.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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()); - status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO, - sapi_ca_certificates.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_easy_perform(&curl); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); + 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 diff --git a/oss-internship-2020/curl/examples/example4.cc b/oss-internship-2020/curl/examples/example4.cc index d4cbd1e..8386dd4 100644 --- a/oss-internship-2020/curl/examples/example4.cc +++ b/oss-internship-2020/curl/examples/example4.cc @@ -26,9 +26,6 @@ int main(int argc, char* argv[]) { google::InitGoogleLogging(argv[0]); absl::Status status; - sapi::StatusOr status_or_int; - sapi::StatusOr status_or_curl; - sapi::StatusOr status_or_curlm; // Initialize sandbox2 and sapi CurlSapiSandbox sandbox; @@ -41,71 +38,72 @@ int main(int argc, char* argv[]) { // Number of running handles sapi::v::Int still_running(1); + absl::StatusOr curl_code; + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) - status_or_int = api.curl_global_init(3l); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); + 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 - status_or_curl = api.curl_easy_init(); - if (!status_or_curl.ok()) { - LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); + 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(status_or_curl.value()); + 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"); - status_or_int = + curl_code = api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); } // Initialize multi_handle - status_or_curlm = api.curl_multi_init(); - if (!status_or_curlm.ok()) { - LOG(FATAL) << "curl_multi_init failed: " << status_or_curlm.status(); + 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(status_or_curlm.value()); + 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 - status_or_int = api.curl_multi_add_handle(&multi_handle, &http_handle); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_multi_add_handle failed: " << status_or_int.status(); + 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 - status_or_int = - api.curl_multi_perform(&multi_handle, still_running.PtrBoth()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_mutli_perform failed: " << status_or_int.status(); + 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; - status_or_int = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, - 1000, numfds.PtrBoth()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_multi_poll_sapi failed: " << status_or_int.status(); + 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 - status_or_int = api.curl_multi_remove_handle(&multi_handle, &http_handle); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_multi_remove_handle failed: " << status_or_int.status(); + 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 @@ -115,9 +113,9 @@ int main(int argc, char* argv[]) { } // Cleanup multi_handle - status_or_int = api.curl_multi_cleanup(&multi_handle); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_multi_cleanup failed: " << status_or_int.status(); + 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 diff --git a/oss-internship-2020/curl/examples/example5.cc b/oss-internship-2020/curl/examples/example5.cc index 9d61dd0..e3218c2 100644 --- a/oss-internship-2020/curl/examples/example5.cc +++ b/oss-internship-2020/curl/examples/example5.cc @@ -21,54 +21,41 @@ #include "../sandbox.h" -struct thread_args { - const char* url; - CurlApi* api; -}; - -constexpr int kThreadsnumber = 4; - -void* pull_one_url(void* args) { - absl::Status status; - sapi::StatusOr status_or_curl; - sapi::StatusOr status_or_int; - - CurlApi& api = *((thread_args*)args)->api; - +void pull_one_url(const std::string& url, CurlApi& api) { // Initialize the curl session - status_or_curl = api.curl_easy_init(); - if (!status_or_curl.ok()) { - LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); + 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(status_or_curl.value()); + 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(((thread_args*)args)->url); - status_or_int = + sapi::v::ConstCStr sapi_url(url.c_str()); + curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore()); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); + if (!curl_code.ok() || curl_code.value() != CURLE_OK) { + LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status(); } // Perform the request - status_or_int = api.curl_easy_perform(&curl); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); + 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); + absl::Status status = api.curl_easy_cleanup(&curl); if (!status.ok()) { LOG(FATAL) << "curl_easy_cleanup failed: " << status; } - - return NULL; } -const char* const urls[kThreadsnumber] = { +const std::vector urls = { "http://example.com", "http://example.edu", "http://example.net", "http://example.org"}; @@ -76,10 +63,7 @@ int main(int argc, char* argv[]) { gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); - pthread_t tid[kThreadsnumber]; - absl::Status status; - sapi::StatusOr status_or_int; // Initialize sandbox2 and sapi CurlSapiSandbox sandbox; @@ -89,29 +73,23 @@ int main(int argc, char* argv[]) { } CurlApi api(&sandbox); + absl::StatusOr curl_code; + // Initialize curl (CURL_GLOBAL_DEFAULT = 3) - status_or_int = api.curl_global_init(3l); - if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { - LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); + 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 - for (int i = 0; i < kThreadsnumber; ++i) { - thread_args args = {urls[i], &api}; - int error = pthread_create(&tid[i], NULL, pull_one_url, (void*)&args); - if (error) { - LOG(FATAL) << "pthread_create failed"; - } - std::cout << "Thread " << i << " gets " << urls[i] << std::endl; + std::vector threads; + for (auto& url : urls) { + threads.emplace_back(pull_one_url, std::ref(url), std::ref(api)); } // Join the threads - for (int i = 0; i < kThreadsnumber; ++i) { - int error = pthread_join(tid[i], NULL); - if (error) { - LOG(FATAL) << "pthread_join failed"; - } - std::cout << "Thread " << i << " terminated" << std::endl; + for (auto& thread : threads) { + thread.join(); } // Cleanup curl diff --git a/oss-internship-2020/curl/tests/callbacks.cc b/oss-internship-2020/curl/tests/callbacks.cc index 45d7328..98af317 100644 --- a/oss-internship-2020/curl/tests/callbacks.cc +++ b/oss-internship-2020/curl/tests/callbacks.cc @@ -17,19 +17,21 @@ #include #include -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); +#include "sandboxed_api/vars.h" - char* ptr = - static_cast(realloc(mem->memory, mem->size + real_size + 1)); +size_t WriteToMemoryTests(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->memory = ptr; - memcpy(&(mem->memory[mem->size]), contents, real_size); + mem->data = ptr; + auto data = static_cast(mem->data); + memcpy(&(data[mem->size]), contents, real_size); mem->size += real_size; - mem->memory[mem->size] = 0; + data[mem->size] = 0; return real_size; } diff --git a/oss-internship-2020/curl/tests/callbacks.h b/oss-internship-2020/curl/tests/callbacks.h index 21b4c9f..8eebc65 100644 --- a/oss-internship-2020/curl/tests/callbacks.h +++ b/oss-internship-2020/curl/tests/callbacks.h @@ -17,13 +17,8 @@ #include -extern "C" struct MemoryStruct { - char* memory; - size_t size; -}; - // Append contents to the string stored by userp (userp is a MemoryStruct*) -extern "C" size_t WriteToMemory(char* contents, size_t size, size_t num_bytes, - void* userp); +extern "C" size_t WriteToMemoryTests(char* contents, size_t size, + size_t num_bytes, void* userp); #endif // TESTS_CALLBACKS_H diff --git a/oss-internship-2020/curl/tests/test_utils.cc b/oss-internship-2020/curl/tests/test_utils.cc index deb9bad..cbf6fe7 100644 --- a/oss-internship-2020/curl/tests/test_utils.cc +++ b/oss-internship-2020/curl/tests/test_utils.cc @@ -27,243 +27,261 @@ long int CurlTestUtils::port_; std::thread CurlTestUtils::server_thread_; -void CurlTestUtils::CurlTestSetUp() { +absl::Status CurlTestUtils::CurlTestSetUp() { // Initialize sandbox2 and sapi sandbox_ = std::make_unique(); - ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); + absl::Status init = sandbox_->Init(); + if (!init.ok()) { + return init; + } api_ = std::make_unique(sandbox_.get()); // Initialize curl - SAPI_ASSERT_OK_AND_ASSIGN(void* curl_raw_ptr, api_->curl_easy_init()); - curl_ = std::make_unique(curl_raw_ptr); + absl::StatusOr curl_handle = api_->curl_easy_init(); + if (!curl_handle.ok()) { + return curl_handle.status(); + } else 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()); - SAPI_ASSERT_OK_AND_ASSIGN(int setopt_url_code, - api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL, - sapi_url.PtrBefore())); - ASSERT_EQ(setopt_url_code, CURLE_OK); + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL, + sapi_url.PtrBefore()); + if (!curl_code.ok()) { + return curl_code.status(); + } else if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_ptr returned with the error code " + + curl_code.value()); + } // Set port - SAPI_ASSERT_OK_AND_ASSIGN( - int setopt_port_code, - api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_)); - ASSERT_EQ(setopt_port_code, CURLE_OK); + curl_code = api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_); + if (!curl_code.ok()) { + return curl_code.status(); + } else 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 - sapi::RPCChannel rpcc(sandbox_->comms()); - size_t (*_function_ptr)(char*, size_t, size_t, void*); - ASSERT_THAT(rpcc.Symbol("WriteToMemory", (void**)&_function_ptr), - sapi::IsOk()); - sapi::v::RemotePtr remote_function_ptr((void*)_function_ptr); + void* function_ptr; + absl::Status symbol = + sandbox_->rpc_channel()->Symbol("WriteToMemoryTests", &function_ptr); + if (!symbol.ok()) { + return symbol; + } + sapi::v::RemotePtr remote_function_ptr(function_ptr); // Set WriteToMemory as the write function - SAPI_ASSERT_OK_AND_ASSIGN( - int setopt_write_function, - api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION, - &remote_function_ptr)); - ASSERT_EQ(setopt_write_function, CURLE_OK); + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION, + &remote_function_ptr); + if (!curl_code.ok()) { + return curl_code.status(); + } else 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 - SAPI_ASSERT_OK_AND_ASSIGN( - int setopt_write_data, - api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA, - chunk_.PtrBoth())); - ASSERT_EQ(setopt_write_data, CURLE_OK); + curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA, + chunk_.PtrBoth()); + if (!curl_code.ok()) { + return curl_code.status(); + } else if (curl_code.value() != CURLE_OK) { + return absl::UnavailableError( + "curl_easy_setopt_ptr returned with the error code " + + curl_code.value()); + } + + return absl::OkStatus(); } -void CurlTestUtils::CurlTestTearDown() { +absl::Status CurlTestUtils::CurlTestTearDown() { // Cleanup curl - ASSERT_THAT(api_->curl_easy_cleanup(curl_.get()), sapi::IsOk()); + return api_->curl_easy_cleanup(curl_.get()); } -void CurlTestUtils::PerformRequest(std::string& response) { +absl::StatusOr CurlTestUtils::PerformRequest() { // Perform the request - SAPI_ASSERT_OK_AND_ASSIGN(int perform_code, - api_->curl_easy_perform(curl_.get())); - ASSERT_EQ(perform_code, CURLE_OK); + absl::StatusOr curl_code = api_->curl_easy_perform(curl_.get()); + if (!curl_code.ok()) { + return curl_code.status(); + } else 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 sapi::v::GenericPtr remote_ptr; - remote_ptr.SetRemote(&((MemoryStruct*)chunk_.GetRemote())->memory); - ASSERT_THAT(sandbox_->TransferFromSandboxee(&remote_ptr), sapi::IsOk()); + remote_ptr.SetRemote( + &(static_cast(chunk_.GetRemote()))->data); + absl::Status transfer = sandbox_->TransferFromSandboxee(&remote_ptr); + if (!transfer.ok()) { + return transfer; + } void* chunk_ptr = (void*)remote_ptr.GetValue(); - // Get the string and store it in response - SAPI_ASSERT_OK_AND_ASSIGN( - response, sandbox_->GetCString(sapi::v::RemotePtr(chunk_ptr))); + // Get the string + absl::StatusOr response = + sandbox_->GetCString(sapi::v::RemotePtr(chunk_ptr)); + if (!response.ok()) { + return response.status(); + } + + return response.value(); } -void CurlTestUtils::PerformRequest() { - // If the response is not needed, pass a string that will be discarded - std::string discarded_response; - PerformRequest(discarded_response); +// Read the socket until str is completely read +std::string ReadUntil(const int socket, const std::string& str, + const size_t max_request_size) { + char buf[max_request_size] = {}; + size_t read_bytes = 0; + + // Read one char at a time until str is suffix of buf + do { + if (read_bytes >= max_request_size || + read(socket, buf + read_bytes, 1) == -1) { + return ""; + } + ++read_bytes; + } while (std::string{buf + std::max(size_t{0}, read_bytes - str.size())} != + str); + + buf[read_bytes] = '\0'; + return std::string{buf}; +} + +// Parse HTTP headers to return the Content-Length +size_t GetContentLength(const std::string& headers) { + // Find the Content-Length header + const char* length_header_start = strstr(headers.c_str(), "Content-Length: "); + + // There is no Content-Length field + if (!length_header_start) { + return 0; + } + + // Find Content-Length string + const char* length_start = length_header_start + strlen("Content-Length: "); + size_t length_bytes = strstr(length_start, "\r\n") - length_start; + if (length_bytes >= 64) { + return 0; + } + + // Convert Content-Length string value to int + char content_length_string[64]; + strncpy(content_length_string, length_start, length_bytes); + + return atoi(content_length_string); +} + +// Read exactly content_bytes from the socket +std::string ReadExact(int socket, size_t content_bytes) { + char buf[content_bytes + 1] = {}; + size_t read_bytes = 0; + + // Read until content_bytes chars are read + do { + int num_bytes; + if ((num_bytes = read(socket, buf + read_bytes, + sizeof(buf) - read_bytes - 1)) == -1) { + return ""; + } + read_bytes += num_bytes; + } while (read_bytes < content_bytes); + + buf[content_bytes] = '\0'; + + return std::string{buf}; } void CurlTestUtils::StartMockServer() { - // Get ai_list, a list of addrinfo structures, the port will be set later - addrinfo hints{AI_PASSIVE, AF_INET, SOCK_STREAM}; - addrinfo* ai_list; - if (getaddrinfo("127.0.0.1", NULL, &hints, &ai_list) < 0) { + // 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; } - // Loop over ai_list, until a socket is created - int listening_socket; - for (addrinfo* p = ai_list;; p = p->ai_next) { - if (p == nullptr) { - return; - } - - // Try creating a socket - listening_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (listening_socket >= 0) { - // Try binding the socket to the address - if (bind(listening_socket, p->ai_addr, p->ai_addrlen) >= 0) { - // Assign an arbitrary available port to the socket address object - if (getsockname(listening_socket, p->ai_addr, &p->ai_addrlen) == -1) { - return; - } - port_ = ntohs(((struct sockaddr_in*)p->ai_addr)->sin_port); - - break; - - } else { - close(listening_socket); - } - } - } - - freeaddrinfo(ai_list); - - // Listen on the socket - if (listen(listening_socket, 256) == -1) { + // Bind the file descriptor to the socket address object + if (bind(listening_socket, (sockaddr*)&socket_address, socket_address_size) == + -1) { return; } + // Assign an available port to the socket address object + if (getsockname(listening_socket, (sockaddr*)&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([=] { - // Create the master fd_set containing listening_socket - fd_set master_fd_set; - FD_ZERO(&master_fd_set); - FD_SET(listening_socket, &master_fd_set); - - // Create an empty fd_set, will be used for making copies of master_fd_set - fd_set copy_fd_set; - FD_ZERO(©_fd_set); - - int max_fd = listening_socket; - - // Keep calling select and block after a new event happens - // Doesn't stop until the process doing tests is terminated - for (;;) { - copy_fd_set = master_fd_set; - - // Block and wait for a file descriptor to be ready - if (select(max_fd + 1, ©_fd_set, nullptr, nullptr, nullptr) == -1) { - close(listening_socket); - return; - } - - // A file descriptor is ready, loop over all the fds to find it - for (int i = 0; i <= max_fd; ++i) { - // i is not a file desciptor in the set, skip it - if (!FD_ISSET(i, ©_fd_set)) { - continue; - } - - if (i == listening_socket) { // CASE 1: a new connection - - sockaddr_storage remote_address; - socklen_t remote_address_size = sizeof(remote_address); - - // Accept the connection - int accepted_socket = - accept(listening_socket, (sockaddr*)&remote_address, - &remote_address_size); - if (accepted_socket == -1) { - if (errno == ECONNABORTED) - continue; - else { - close(listening_socket); - return; - } - } - - // Set the socket's nonblocking flag to true - int flag = fcntl(accepted_socket, F_GETFL, nullptr); - fcntl(accepted_socket, F_SETFL, flag | O_NONBLOCK); - - // Add the new socket to the fd_set and update max_fd - FD_SET(accepted_socket, &master_fd_set); - max_fd = std::max(max_fd, accepted_socket); - - } else { // CASE 2: a request from an existing connection - - constexpr int kMaxRequestSize = 4096; - char buf[kMaxRequestSize] = {}; - - ssize_t num_bytes = 0; - size_t read_bytes = 0; - bool close_socket = false; - - // Read from the nonblocking socket until recv returns -1 and - // errno is EAGAIN or EWOULDBLOCK (busy wait) - do { - num_bytes = recv(i, buf + read_bytes, sizeof(buf) - read_bytes, 0); - - // An error happened, close the socket - if (num_bytes == -1 and errno != EAGAIN and errno != EWOULDBLOCK) { - close_socket = true; - break; - } - - // The connection was closed, close the socket - if (num_bytes == 0) { - close_socket = true; - break; - } - - read_bytes += num_bytes; - - } while (num_bytes > 0); - - if (close_socket) { - // Close the connection and remove it from fd_set - close(i); - FD_CLR(i, &master_fd_set); - continue; - } - - // Prepare a response for the request - std::string http_response = - "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: "; - - if (strncmp(buf, "GET", 3) == 0) { - http_response += std::to_string(kSimpleResponse.size()) + "\n\n" + - std::string{kSimpleResponse}; - - } else if (strncmp(buf, "POST", 4) == 0) { - char* post_fields = strstr(buf, "\r\n\r\n"); - post_fields += 4; // Points to the first char after HTTP header - http_response += std::to_string(strlen(post_fields)) + "\n\n" + - std::string(post_fields); - - } else { - // Close the connection and remove it from fd_set - close(i); - FD_CLR(i, &master_fd_set); - continue; - } - - // Ignore any errors, the connection will be closed anyway - write(i, http_response.c_str(), http_response.size()); - - // Close the connection and remove it from fd_set - close(i); - FD_CLR(i, &master_fd_set); - } - } + // Listen on the socket (maximum 1 connection) + if (listen(listening_socket, 1) == -1) { + return; } + + // File descriptor to the connection socket + // This blocks the thread until a connection is established + int accepted_socket = accept(listening_socket, (sockaddr*)&socket_address, + (socklen_t*)&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); + + // Get the length of the request content + size_t content_length = GetContentLength(headers); + if (content_length > kMaxRequestSize - headers.size()) { + 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 += std::to_string(kSimpleResponse.size()) + "\r\n\r\n" + + std::string{kSimpleResponse}; + + } 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); }); } diff --git a/oss-internship-2020/curl/tests/test_utils.h b/oss-internship-2020/curl/tests/test_utils.h index 70adba1..5f31b4d 100644 --- a/oss-internship-2020/curl/tests/test_utils.h +++ b/oss-internship-2020/curl/tests/test_utils.h @@ -30,14 +30,12 @@ class CurlTestUtils { }; // Initialize and set up the curl handle - void CurlTestSetUp(); + absl::Status CurlTestSetUp(); // Clean up the curl handle - void CurlTestTearDown(); + absl::Status CurlTestTearDown(); - // Perform a request to the mock server - // Optionally, store the response in a string - void PerformRequest(std::string& response); - void PerformRequest(); + // 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 @@ -58,7 +56,7 @@ class CurlTestUtils { static constexpr absl::string_view kSimpleResponse = "OK"; private: - sapi::v::Struct chunk_; + sapi::v::LenVal chunk_{0}; }; #endif // TESTS_H diff --git a/oss-internship-2020/curl/tests/tests.cc b/oss-internship-2020/curl/tests/tests.cc index 2c04a99..99d68fc 100644 --- a/oss-internship-2020/curl/tests/tests.cc +++ b/oss-internship-2020/curl/tests/tests.cc @@ -14,28 +14,26 @@ #include "test_utils.h" -class Curl_Test : public CurlTestUtils, public ::testing::Test { +class CurlTest : public CurlTestUtils, public ::testing::Test { protected: - static void SetUpTestSuite() { + 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 SetUp() override { CurlTestSetUp(); } - - static void TearDownTestSuite() { + void TearDown() override { + ASSERT_TRUE(CurlTestTearDown().ok()); // Detach the server thread server_thread_.detach(); } - - void TearDown() override { CurlTestTearDown(); } }; -TEST_F(Curl_Test, EffectiveUrl) { +TEST_F(CurlTest, EffectiveUrl) { sapi::v::RemotePtr effective_url_ptr(nullptr); - PerformRequest(); + ASSERT_TRUE(PerformRequest().ok()); // Get effective URL SAPI_ASSERT_OK_AND_ASSIGN( @@ -53,10 +51,10 @@ TEST_F(Curl_Test, EffectiveUrl) { ASSERT_EQ(effective_url, kUrl); } -TEST_F(Curl_Test, EffectivePort) { +TEST_F(CurlTest, EffectivePort) { sapi::v::Int effective_port; - PerformRequest(); + ASSERT_TRUE(PerformRequest().ok()); // Get effective port SAPI_ASSERT_OK_AND_ASSIGN( @@ -69,10 +67,10 @@ TEST_F(Curl_Test, EffectivePort) { ASSERT_EQ(effective_port.GetValue(), port_); } -TEST_F(Curl_Test, ResponseCode) { +TEST_F(CurlTest, ResponseCode) { sapi::v::Int response_code; - PerformRequest(); + ASSERT_TRUE(PerformRequest().ok()); // Get response code SAPI_ASSERT_OK_AND_ASSIGN( @@ -85,10 +83,10 @@ TEST_F(Curl_Test, ResponseCode) { ASSERT_EQ(response_code.GetValue(), 200); } -TEST_F(Curl_Test, ContentType) { +TEST_F(CurlTest, ContentType) { sapi::v::RemotePtr content_type_ptr(nullptr); - PerformRequest(); + ASSERT_TRUE(PerformRequest().ok()); // Get effective URL SAPI_ASSERT_OK_AND_ASSIGN( @@ -106,15 +104,14 @@ TEST_F(Curl_Test, ContentType) { ASSERT_EQ(content_type, "text/plain"); } -TEST_F(Curl_Test, GETResponse) { - std::string response; - PerformRequest(response); +TEST_F(CurlTest, GETResponse) { + SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest()); // Compare response with expected response ASSERT_EQ(response, kSimpleResponse); } -TEST_F(Curl_Test, POSTResponse) { +TEST_F(CurlTest, POSTResponse) { sapi::v::ConstCStr post_fields("postfields"); // Set request method to POST @@ -137,8 +134,7 @@ TEST_F(Curl_Test, POSTResponse) { post_fields.PtrBefore())); ASSERT_EQ(setopt_post_fields, CURLE_OK); - std::string response; - PerformRequest(response); + SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest()); // Compare response with expected response ASSERT_EQ(std::string(post_fields.GetData()), response);