Address review comments

This commit is contained in:
Federico Stazi 2020-09-16 15:51:34 +00:00
parent bbebeee1a6
commit eb783de3f5
15 changed files with 438 additions and 459 deletions

View File

@ -33,4 +33,5 @@ set(BUILD_TESTING OFF)
add_subdirectory(curl) add_subdirectory(curl)
target_link_libraries(curl_wrapper_and_callbacks target_link_libraries(curl_wrapper_and_callbacks
CURL::libcurl CURL::libcurl
sapi::sapi
) )

View File

@ -19,68 +19,64 @@
#include <curl/curl.h> #include <curl/curl.h>
// The wrapper method is needed to make the variadic argument explicit extern "C" {
extern "C" CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option,
void* parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, CURLcode curl_easy_setopt_ptr(CURL* handle, CURLoption option, void* parameter);
long parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option, CURLcode curl_easy_setopt_long(CURL* handle, CURLoption option, long parameter);
curl_off_t parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLcode curl_easy_getinfo_ptr(CURL* handle, CURLINFO option, CURLcode curl_easy_setopt_curl_off_t(CURL* handle, CURLoption option,
void* parameter); 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 // The typedef and wrapper method are needed because the original method has
// some conflicts in curl_sapi.sapi.h // some conflicts in curl_sapi.sapi.h
extern "C" typedef time_t time_t_sapi; typedef time_t time_t_sapi;
extern "C" time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now); time_t_sapi curl_getdate_sapi(char* datestring, time_t_sapi* now);
// The typedef and wrapper method are needed because the original method has // The typedef and wrapper method are needed because the original method has
// some conflicts in curl_sapi.sapi.h // some conflicts in curl_sapi.sapi.h
extern "C" typedef fd_set fd_set_sapi; typedef fd_set fd_set_sapi;
extern "C" CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, CURLMcode curl_multi_fdset_sapi(CURLM* multi_handle, fd_set_sapi* read_fd_set,
fd_set_sapi* read_fd_set, fd_set_sapi* write_fd_set,
fd_set_sapi* write_fd_set, fd_set_sapi* exc_fd_set, int* max_fd);
fd_set_sapi* exc_fd_set,
int* max_fd);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option, CURLMcode curl_multi_setopt_ptr(CURLM* handle, CURLMoption option,
void* parameter); void* parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option, CURLMcode curl_multi_setopt_long(CURLM* handle, CURLMoption option,
long parameter); long parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMcode curl_multi_setopt_curl_off_t(CURLM* handle, CURLMoption option,
CURLMoption option, curl_off_t parameter);
curl_off_t parameter);
// The wrapper method is needed because incomplete array type is not supported // The wrapper method is needed because incomplete array type is not supported
extern "C" CURLMcode curl_multi_poll_sapi(CURLM* multi_handle, CURLMcode curl_multi_poll_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds, struct curl_waitfd* extra_fds,
unsigned int extra_nfds, unsigned int extra_nfds, int timeout_ms,
int timeout_ms, int* numfds); int* numfds);
// The wrapper method is needed because incomplete array type is not supported // The wrapper method is needed because incomplete array type is not supported
extern "C" CURLMcode curl_multi_wait_sapi(CURLM* multi_handle, CURLMcode curl_multi_wait_sapi(CURLM* multi_handle,
struct curl_waitfd* extra_fds, struct curl_waitfd* extra_fds,
unsigned int extra_nfds, unsigned int extra_nfds, int timeout_ms,
int timeout_ms, int* numfds); int* numfds);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option, CURLSHcode curl_share_setopt_ptr(CURLSH* handle, CURLSHoption option,
void* parameter); void* parameter);
// The wrapper method is needed to make the variadic argument explicit // The wrapper method is needed to make the variadic argument explicit
extern "C" CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHcode curl_share_setopt_long(CURLSH* handle, CURLSHoption option,
CURLSHoption option, long parameter);
long parameter); }
#endif // CURL_WRAPPER_H #endif // CURL_WRAPPER_H

View File

@ -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. - **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. - **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. - **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. - **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.

View File

@ -18,22 +18,25 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include "sandboxed_api/vars.h"
// Function taken from curl's getinmemory.c // Function taken from curl's getinmemory.c
size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb,
void* userp) { void* userp) {
size_t realsize = size * nmemb; size_t real_size = size * nmemb;
struct MemoryStruct* mem = (struct MemoryStruct*)userp; auto* mem = static_cast<sapi::LenValStruct*>(userp);
char* ptr = (char*)realloc(mem->memory, mem->size + realsize + 1); char* ptr = static_cast<char*>(realloc(mem->data, mem->size + real_size + 1));
if (ptr == NULL) { // Out of memory if (ptr == nullptr) { // Out of memory
std::cout << "not enough memory (realloc returned NULL)\n"; std::cout << "not enough memory (realloc returned NULL)\n";
return 0; return 0;
} }
mem->memory = ptr; mem->data = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize); auto data = static_cast<char*>(mem->data);
mem->size += realsize; memcpy(&(data[mem->size]), contents, real_size);
mem->memory[mem->size] = 0; mem->size += real_size;
data[mem->size] = 0;
return realsize; return real_size;
} }

View File

@ -17,11 +17,6 @@
#include <curl/curl.h> #include <curl/curl.h>
extern "C" struct MemoryStruct {
char* memory;
size_t size;
};
extern "C" size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, extern "C" size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb,
void* userp); void* userp);

View File

@ -24,8 +24,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
absl::Status status; absl::Status status;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<int> status_or_int;
// Initialize sandbox2 and sapi // Initialize sandbox2 and sapi
CurlSapiSandbox sandbox; CurlSapiSandbox sandbox;
@ -36,36 +34,38 @@ int main(int argc, char* argv[]) {
CurlApi api(&sandbox); CurlApi api(&sandbox);
// Initialize the curl session // Initialize the curl session
status_or_curl = api.curl_easy_init(); absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!status_or_curl.ok()) { if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); 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"; if (!curl.GetValue()) LOG(FATAL) << "curl_easy_init failed: curl is NULL";
absl::StatusOr<int> curl_code;
// Specify URL to get // Specify URL to get
sapi::v::ConstCStr url("http://example.com"); sapi::v::ConstCStr url("http://example.com");
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the library to follow a redirection // Set the library to follow a redirection
status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l); curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_FOLLOWLOCATION, 1l);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
} }
// Disable authentication of peer certificate // Disable authentication of peer certificate
status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l); curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 0l);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
} }
// Perform the request // Perform the request
status_or_int = api.curl_easy_perform(&curl); curl_code = api.curl_easy_perform(&curl);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
} }
// Cleanup curl // Cleanup curl

View File

@ -30,8 +30,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
absl::Status status; absl::Status status;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<int> status_or_int;
// Initialize sandbox2 and sapi // Initialize sandbox2 and sapi
CurlSapiSandbox sandbox; CurlSapiSandbox sandbox;
@ -42,63 +40,64 @@ int main(int argc, char* argv[]) {
CurlApi api(&sandbox); CurlApi api(&sandbox);
// Generate pointer to WriteMemoryCallback function // Generate pointer to WriteMemoryCallback function
sapi::RPCChannel rpcc(sandbox.comms()); void* function_ptr;
size_t (*_function_ptr)(void*, size_t, size_t, void*); status = sandbox.rpc_channel()->Symbol("WriteMemoryCallback", &function_ptr);
status = rpcc.Symbol("WriteMemoryCallback", (void**)&_function_ptr);
if (!status.ok()) { 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 // Initialize the curl session
status_or_curl = api.curl_easy_init(); absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!status_or_curl.ok()) { if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); 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()) { if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL"; LOG(FATAL) << "curl_easy_init failed: curl is NULL";
} }
absl::StatusOr<int> curl_code;
// Specify URL to get // Specify URL to get
sapi::v::ConstCStr url("http://example.com"); sapi::v::ConstCStr url("http://example.com");
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set WriteMemoryCallback as the write function // Set WriteMemoryCallback as the write function
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEFUNCTION,
&remote_function_ptr); &remote_function_ptr);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Pass 'chunk' struct to the callback function // Pass 'chunk' struct to the callback function
sapi::v::Struct<MemoryStruct> chunk; sapi::v::Struct<MemoryStruct> chunk;
status_or_int = curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth()); api.curl_easy_setopt_ptr(&curl, CURLOPT_WRITEDATA, chunk.PtrBoth());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set a user agent // Set a user agent
sapi::v::ConstCStr user_agent("libcurl-agent/1.0"); sapi::v::ConstCStr user_agent("libcurl-agent/1.0");
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_USERAGENT,
user_agent.PtrBefore()); user_agent.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Perform the request // Perform the request
status_or_int = api.curl_easy_perform(&curl); curl_code = api.curl_easy_perform(&curl);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
} }
// Retrieve memory size // Retrieve memory size
sapi::v::Int size; sapi::v::Int size;
size.SetRemote(&((MemoryStruct*)chunk.GetRemote())->size); size.SetRemote(&(static_cast<sapi::LenValStruct*>(chunk.GetRemote()))->size);
status = sandbox.TransferFromSandboxee(&size); status = sandbox.TransferFromSandboxee(&size);
if (!status.ok()) { if (!status.ok()) {
LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status; LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status;

View File

@ -55,8 +55,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
absl::Status status; absl::Status status;
sapi::StatusOr<int> status_or_int;
sapi::StatusOr<CURL*> status_or_curl;
// Get input parameters (should be absolute paths) // Get input parameters (should be absolute paths)
if (argc != 5) { if (argc != 5) {
@ -75,79 +73,81 @@ int main(int argc, char* argv[]) {
} }
CurlApi api(&sandbox); CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3) // Initialize curl (CURL_GLOBAL_DEFAULT = 3)
status_or_int = api.curl_global_init(3l); curl_code = api.curl_global_init(3l);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
} }
// Initialize curl easy handle // Initialize curl easy handle
status_or_curl = api.curl_easy_init(); absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!status_or_curl.ok()) { if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); 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()) { if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL"; LOG(FATAL) << "curl_easy_init failed: curl is NULL";
} }
// Specify URL to get (using HTTPS) // Specify URL to get (using HTTPS)
sapi::v::ConstCStr url("https://example.com"); sapi::v::ConstCStr url("https://example.com");
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore()); curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, url.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the SSL certificate type to "PEM" // Set the SSL certificate type to "PEM"
sapi::v::ConstCStr ssl_cert_type("PEM"); sapi::v::ConstCStr ssl_cert_type("PEM");
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERTTYPE,
ssl_cert_type.PtrBefore()); ssl_cert_type.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the certificate for client authentication // Set the certificate for client authentication
sapi::v::ConstCStr sapi_ssl_certificate(ssl_certificate.c_str()); sapi::v::ConstCStr sapi_ssl_certificate(ssl_certificate.c_str());
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLCERT,
sapi_ssl_certificate.PtrBefore()); sapi_ssl_certificate.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the private key for client authentication // Set the private key for client authentication
sapi::v::ConstCStr sapi_ssl_key(ssl_key.c_str()); 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()); api.curl_easy_setopt_ptr(&curl, CURLOPT_SSLKEY, sapi_ssl_key.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the password used to protect the private key // Set the password used to protect the private key
sapi::v::ConstCStr sapi_ssl_key_password(ssl_key_password.c_str()); sapi::v::ConstCStr sapi_ssl_key_password(ssl_key_password.c_str());
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_KEYPASSWD,
sapi_ssl_key_password.PtrBefore()); sapi_ssl_key_password.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Set the file with the certificates vaildating the server // Set the file with the certificates vaildating the server
sapi::v::ConstCStr sapi_ca_certificates(ca_certificates.c_str()); sapi::v::ConstCStr sapi_ca_certificates(ca_certificates.c_str());
status_or_int = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO, curl_code = api.curl_easy_setopt_ptr(&curl, CURLOPT_CAINFO,
sapi_ca_certificates.PtrBefore()); sapi_ca_certificates.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Verify the authenticity of the server // Verify the authenticity of the server
status_or_int = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_code = api.curl_easy_setopt_long(&curl, CURLOPT_SSL_VERIFYPEER, 1L);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_long failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_long failed: " << curl_code.status();
} }
// Perform the request // Perform the request
status_or_int = api.curl_easy_perform(&curl); curl_code = api.curl_easy_perform(&curl);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
} }
// Cleanup curl easy handle // Cleanup curl easy handle

View File

@ -26,9 +26,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
absl::Status status; absl::Status status;
sapi::StatusOr<int> status_or_int;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<CURLM*> status_or_curlm;
// Initialize sandbox2 and sapi // Initialize sandbox2 and sapi
CurlSapiSandbox sandbox; CurlSapiSandbox sandbox;
@ -41,71 +38,72 @@ int main(int argc, char* argv[]) {
// Number of running handles // Number of running handles
sapi::v::Int still_running(1); sapi::v::Int still_running(1);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3) // Initialize curl (CURL_GLOBAL_DEFAULT = 3)
status_or_int = api.curl_global_init(3l); curl_code = api.curl_global_init(3l);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
} }
// Initialize http_handle // Initialize http_handle
status_or_curl = api.curl_easy_init(); absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!status_or_curl.ok()) { if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); 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()) { if (!http_handle.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: http_handle is NULL"; LOG(FATAL) << "curl_easy_init failed: http_handle is NULL";
} }
// Specify URL to get // Specify URL to get
sapi::v::ConstCStr url("http://example.com"); sapi::v::ConstCStr url("http://example.com");
status_or_int = curl_code =
api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore()); api.curl_easy_setopt_ptr(&http_handle, CURLOPT_URL, url.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Initialize multi_handle // Initialize multi_handle
status_or_curlm = api.curl_multi_init(); absl::StatusOr<CURLM*> curlm_handle = api.curl_multi_init();
if (!status_or_curlm.ok()) { if (!curlm_handle.ok()) {
LOG(FATAL) << "curl_multi_init failed: " << status_or_curlm.status(); 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()) { if (!multi_handle.GetValue()) {
LOG(FATAL) << "curl_multi_init failed: multi_handle is NULL"; LOG(FATAL) << "curl_multi_init failed: multi_handle is NULL";
} }
// Add http_handle to the multi stack // Add http_handle to the multi stack
status_or_int = api.curl_multi_add_handle(&multi_handle, &http_handle); curl_code = api.curl_multi_add_handle(&multi_handle, &http_handle);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_add_handle failed: " << status_or_int.status(); LOG(FATAL) << "curl_multi_add_handle failed: " << curl_code.status();
} }
while (still_running.GetValue()) { while (still_running.GetValue()) {
sapi::v::Int numfds(0); sapi::v::Int numfds(0);
// Perform the request // Perform the request
status_or_int = curl_code = api.curl_multi_perform(&multi_handle, still_running.PtrBoth());
api.curl_multi_perform(&multi_handle, still_running.PtrBoth()); if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { LOG(FATAL) << "curl_mutli_perform failed: " << curl_code.status();
LOG(FATAL) << "curl_mutli_perform failed: " << status_or_int.status();
} }
if (still_running.GetValue()) { if (still_running.GetValue()) {
// Wait for an event or timeout // Wait for an event or timeout
sapi::v::NullPtr null_ptr; sapi::v::NullPtr null_ptr;
status_or_int = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, curl_code = api.curl_multi_poll_sapi(&multi_handle, &null_ptr, 0, 1000,
1000, numfds.PtrBoth()); numfds.PtrBoth());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_poll_sapi failed: " << status_or_int.status(); LOG(FATAL) << "curl_multi_poll_sapi failed: " << curl_code.status();
} }
} }
} }
// Remove http_handle from the multi stack // Remove http_handle from the multi stack
status_or_int = api.curl_multi_remove_handle(&multi_handle, &http_handle); curl_code = api.curl_multi_remove_handle(&multi_handle, &http_handle);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_remove_handle failed: " << status_or_int.status(); LOG(FATAL) << "curl_multi_remove_handle failed: " << curl_code.status();
} }
// Cleanup http_handle // Cleanup http_handle
@ -115,9 +113,9 @@ int main(int argc, char* argv[]) {
} }
// Cleanup multi_handle // Cleanup multi_handle
status_or_int = api.curl_multi_cleanup(&multi_handle); curl_code = api.curl_multi_cleanup(&multi_handle);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_multi_cleanup failed: " << status_or_int.status(); LOG(FATAL) << "curl_multi_cleanup failed: " << curl_code.status();
} }
// Cleanup curl // Cleanup curl

View File

@ -21,54 +21,41 @@
#include "../sandbox.h" #include "../sandbox.h"
struct thread_args { void pull_one_url(const std::string& url, CurlApi& api) {
const char* url;
CurlApi* api;
};
constexpr int kThreadsnumber = 4;
void* pull_one_url(void* args) {
absl::Status status;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<int> status_or_int;
CurlApi& api = *((thread_args*)args)->api;
// Initialize the curl session // Initialize the curl session
status_or_curl = api.curl_easy_init(); absl::StatusOr<CURL*> curl_handle = api.curl_easy_init();
if (!status_or_curl.ok()) { if (!curl_handle.ok()) {
LOG(FATAL) << "curl_easy_init failed: " << status_or_curl.status(); 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()) { if (!curl.GetValue()) {
LOG(FATAL) << "curl_easy_init failed: curl is NULL"; LOG(FATAL) << "curl_easy_init failed: curl is NULL";
} }
absl::StatusOr<int> curl_code;
// Specify URL to get // Specify URL to get
sapi::v::ConstCStr sapi_url(((thread_args*)args)->url); sapi::v::ConstCStr sapi_url(url.c_str());
status_or_int = curl_code =
api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore()); api.curl_easy_setopt_ptr(&curl, CURLOPT_URL, sapi_url.PtrBefore());
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_setopt_ptr failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_setopt_ptr failed: " << curl_code.status();
} }
// Perform the request // Perform the request
status_or_int = api.curl_easy_perform(&curl); curl_code = api.curl_easy_perform(&curl);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_easy_perform failed: " << status_or_int.status(); LOG(FATAL) << "curl_easy_perform failed: " << curl_code.status();
} }
// Cleanup curl // Cleanup curl
status = api.curl_easy_cleanup(&curl); absl::Status status = api.curl_easy_cleanup(&curl);
if (!status.ok()) { if (!status.ok()) {
LOG(FATAL) << "curl_easy_cleanup failed: " << status; LOG(FATAL) << "curl_easy_cleanup failed: " << status;
} }
return NULL;
} }
const char* const urls[kThreadsnumber] = { const std::vector<std::string> urls = {
"http://example.com", "http://example.edu", "http://example.net", "http://example.com", "http://example.edu", "http://example.net",
"http://example.org"}; "http://example.org"};
@ -76,10 +63,7 @@ int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true); gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
pthread_t tid[kThreadsnumber];
absl::Status status; absl::Status status;
sapi::StatusOr<int> status_or_int;
// Initialize sandbox2 and sapi // Initialize sandbox2 and sapi
CurlSapiSandbox sandbox; CurlSapiSandbox sandbox;
@ -89,29 +73,23 @@ int main(int argc, char* argv[]) {
} }
CurlApi api(&sandbox); CurlApi api(&sandbox);
absl::StatusOr<int> curl_code;
// Initialize curl (CURL_GLOBAL_DEFAULT = 3) // Initialize curl (CURL_GLOBAL_DEFAULT = 3)
status_or_int = api.curl_global_init(3l); curl_code = api.curl_global_init(3l);
if (!status_or_int.ok() or status_or_int.value() != CURLE_OK) { if (!curl_code.ok() || curl_code.value() != CURLE_OK) {
LOG(FATAL) << "curl_global_init failed: " << status_or_int.status(); LOG(FATAL) << "curl_global_init failed: " << curl_code.status();
} }
// Create the threads // Create the threads
for (int i = 0; i < kThreadsnumber; ++i) { std::vector<std::thread> threads;
thread_args args = {urls[i], &api}; for (auto& url : urls) {
int error = pthread_create(&tid[i], NULL, pull_one_url, (void*)&args); threads.emplace_back(pull_one_url, std::ref(url), std::ref(api));
if (error) {
LOG(FATAL) << "pthread_create failed";
}
std::cout << "Thread " << i << " gets " << urls[i] << std::endl;
} }
// Join the threads // Join the threads
for (int i = 0; i < kThreadsnumber; ++i) { for (auto& thread : threads) {
int error = pthread_join(tid[i], NULL); thread.join();
if (error) {
LOG(FATAL) << "pthread_join failed";
}
std::cout << "Thread " << i << " terminated" << std::endl;
} }
// Cleanup curl // Cleanup curl

View File

@ -17,19 +17,21 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
size_t WriteToMemory(char* contents, size_t size, size_t num_bytes, #include "sandboxed_api/vars.h"
void* userp) {
size_t real_size = size * num_bytes;
auto* mem = static_cast<MemoryStruct*>(userp);
char* ptr = size_t WriteToMemoryTests(char* contents, size_t size, size_t num_bytes,
static_cast<char*>(realloc(mem->memory, mem->size + real_size + 1)); void* userp) {
size_t real_size = size * num_bytes;
auto* mem = static_cast<sapi::LenValStruct*>(userp);
char* ptr = static_cast<char*>(realloc(mem->data, mem->size + real_size + 1));
if (ptr == nullptr) return 0; if (ptr == nullptr) return 0;
mem->memory = ptr; mem->data = ptr;
memcpy(&(mem->memory[mem->size]), contents, real_size); auto data = static_cast<char*>(mem->data);
memcpy(&(data[mem->size]), contents, real_size);
mem->size += real_size; mem->size += real_size;
mem->memory[mem->size] = 0; data[mem->size] = 0;
return real_size; return real_size;
} }

View File

@ -17,13 +17,8 @@
#include <curl/curl.h> #include <curl/curl.h>
extern "C" struct MemoryStruct {
char* memory;
size_t size;
};
// Append contents to the string stored by userp (userp is a MemoryStruct*) // 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, extern "C" size_t WriteToMemoryTests(char* contents, size_t size,
void* userp); size_t num_bytes, void* userp);
#endif // TESTS_CALLBACKS_H #endif // TESTS_CALLBACKS_H

View File

@ -27,243 +27,261 @@
long int CurlTestUtils::port_; long int CurlTestUtils::port_;
std::thread CurlTestUtils::server_thread_; std::thread CurlTestUtils::server_thread_;
void CurlTestUtils::CurlTestSetUp() { absl::Status CurlTestUtils::CurlTestSetUp() {
// Initialize sandbox2 and sapi // Initialize sandbox2 and sapi
sandbox_ = std::make_unique<CurlSapiSandbox>(); sandbox_ = std::make_unique<CurlSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), sapi::IsOk()); absl::Status init = sandbox_->Init();
if (!init.ok()) {
return init;
}
api_ = std::make_unique<CurlApi>(sandbox_.get()); api_ = std::make_unique<CurlApi>(sandbox_.get());
// Initialize curl // Initialize curl
SAPI_ASSERT_OK_AND_ASSIGN(void* curl_raw_ptr, api_->curl_easy_init()); absl::StatusOr<CURL*> curl_handle = api_->curl_easy_init();
curl_ = std::make_unique<sapi::v::RemotePtr>(curl_raw_ptr); 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<sapi::v::RemotePtr>(curl_handle.value());
absl::StatusOr<int> curl_code;
// Specify request URL // Specify request URL
sapi::v::ConstCStr sapi_url(kUrl.data()); sapi::v::ConstCStr sapi_url(kUrl.data());
SAPI_ASSERT_OK_AND_ASSIGN(int setopt_url_code, curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL,
api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_URL, sapi_url.PtrBefore());
sapi_url.PtrBefore())); if (!curl_code.ok()) {
ASSERT_EQ(setopt_url_code, CURLE_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 // Set port
SAPI_ASSERT_OK_AND_ASSIGN( curl_code = api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_);
int setopt_port_code, if (!curl_code.ok()) {
api_->curl_easy_setopt_long(curl_.get(), CURLOPT_PORT, port_)); return curl_code.status();
ASSERT_EQ(setopt_port_code, CURLE_OK); } 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 // Generate pointer to the WriteToMemory callback
sapi::RPCChannel rpcc(sandbox_->comms()); void* function_ptr;
size_t (*_function_ptr)(char*, size_t, size_t, void*); absl::Status symbol =
ASSERT_THAT(rpcc.Symbol("WriteToMemory", (void**)&_function_ptr), sandbox_->rpc_channel()->Symbol("WriteToMemoryTests", &function_ptr);
sapi::IsOk()); if (!symbol.ok()) {
sapi::v::RemotePtr remote_function_ptr((void*)_function_ptr); return symbol;
}
sapi::v::RemotePtr remote_function_ptr(function_ptr);
// Set WriteToMemory as the write function // Set WriteToMemory as the write function
SAPI_ASSERT_OK_AND_ASSIGN( curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION,
int setopt_write_function, &remote_function_ptr);
api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEFUNCTION, if (!curl_code.ok()) {
&remote_function_ptr)); return curl_code.status();
ASSERT_EQ(setopt_write_function, CURLE_OK); } 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 // Pass memory chunk object to the callback
SAPI_ASSERT_OK_AND_ASSIGN( curl_code = api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA,
int setopt_write_data, chunk_.PtrBoth());
api_->curl_easy_setopt_ptr(curl_.get(), CURLOPT_WRITEDATA, if (!curl_code.ok()) {
chunk_.PtrBoth())); return curl_code.status();
ASSERT_EQ(setopt_write_data, CURLE_OK); } 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 // 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<std::string> CurlTestUtils::PerformRequest() {
// Perform the request // Perform the request
SAPI_ASSERT_OK_AND_ASSIGN(int perform_code, absl::StatusOr<int> curl_code = api_->curl_easy_perform(curl_.get());
api_->curl_easy_perform(curl_.get())); if (!curl_code.ok()) {
ASSERT_EQ(perform_code, CURLE_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 // Get pointer to the memory chunk
sapi::v::GenericPtr remote_ptr; sapi::v::GenericPtr remote_ptr;
remote_ptr.SetRemote(&((MemoryStruct*)chunk_.GetRemote())->memory); remote_ptr.SetRemote(
ASSERT_THAT(sandbox_->TransferFromSandboxee(&remote_ptr), sapi::IsOk()); &(static_cast<sapi::LenValStruct*>(chunk_.GetRemote()))->data);
absl::Status transfer = sandbox_->TransferFromSandboxee(&remote_ptr);
if (!transfer.ok()) {
return transfer;
}
void* chunk_ptr = (void*)remote_ptr.GetValue(); void* chunk_ptr = (void*)remote_ptr.GetValue();
// Get the string and store it in response // Get the string
SAPI_ASSERT_OK_AND_ASSIGN( absl::StatusOr<std::string> response =
response, sandbox_->GetCString(sapi::v::RemotePtr(chunk_ptr))); sandbox_->GetCString(sapi::v::RemotePtr(chunk_ptr));
if (!response.ok()) {
return response.status();
}
return response.value();
} }
void CurlTestUtils::PerformRequest() { // Read the socket until str is completely read
// If the response is not needed, pass a string that will be discarded std::string ReadUntil(const int socket, const std::string& str,
std::string discarded_response; const size_t max_request_size) {
PerformRequest(discarded_response); 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() { void CurlTestUtils::StartMockServer() {
// Get ai_list, a list of addrinfo structures, the port will be set later // Get the socket file descriptor
addrinfo hints{AI_PASSIVE, AF_INET, SOCK_STREAM}; int listening_socket = socket(AF_INET, SOCK_STREAM, 0);
addrinfo* ai_list;
if (getaddrinfo("127.0.0.1", NULL, &hints, &ai_list) < 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; return;
} }
// Loop over ai_list, until a socket is created // Bind the file descriptor to the socket address object
int listening_socket; if (bind(listening_socket, (sockaddr*)&socket_address, socket_address_size) ==
for (addrinfo* p = ai_list;; p = p->ai_next) { -1) {
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) {
return; 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 // Set server_thread_ operation to socket listening
server_thread_ = std::thread([=] { server_thread_ = std::thread([=] {
// Create the master fd_set containing listening_socket // Listen on the socket (maximum 1 connection)
fd_set master_fd_set; if (listen(listening_socket, 1) == -1) {
FD_ZERO(&master_fd_set); return;
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(&copy_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, &copy_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, &copy_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);
}
}
} }
// 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);
}); });
} }

View File

@ -30,14 +30,12 @@ class CurlTestUtils {
}; };
// Initialize and set up the curl handle // Initialize and set up the curl handle
void CurlTestSetUp(); absl::Status CurlTestSetUp();
// Clean up the curl handle // Clean up the curl handle
void CurlTestTearDown(); absl::Status CurlTestTearDown();
// Perform a request to the mock server // Perform a request to the mock server, return the response
// Optionally, store the response in a string absl::StatusOr<std::string> PerformRequest();
void PerformRequest(std::string& response);
void PerformRequest();
// Start a mock server (only once) that will manage connections for the tests // 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 server listens on a port asynchronously by creating a thread
@ -58,7 +56,7 @@ class CurlTestUtils {
static constexpr absl::string_view kSimpleResponse = "OK"; static constexpr absl::string_view kSimpleResponse = "OK";
private: private:
sapi::v::Struct<MemoryStruct> chunk_; sapi::v::LenVal chunk_{0};
}; };
#endif // TESTS_H #endif // TESTS_H

View File

@ -14,28 +14,26 @@
#include "test_utils.h" #include "test_utils.h"
class Curl_Test : public CurlTestUtils, public ::testing::Test { class CurlTest : public CurlTestUtils, public ::testing::Test {
protected: protected:
static void SetUpTestSuite() { void SetUp() override {
// Start mock server, get port number and check for any error // Start mock server, get port number and check for any error
StartMockServer(); StartMockServer();
ASSERT_TRUE(server_thread_.joinable()); ASSERT_TRUE(server_thread_.joinable());
ASSERT_TRUE(CurlTestSetUp().ok());
} }
void SetUp() override { CurlTestSetUp(); } void TearDown() override {
ASSERT_TRUE(CurlTestTearDown().ok());
static void TearDownTestSuite() {
// Detach the server thread // Detach the server thread
server_thread_.detach(); server_thread_.detach();
} }
void TearDown() override { CurlTestTearDown(); }
}; };
TEST_F(Curl_Test, EffectiveUrl) { TEST_F(CurlTest, EffectiveUrl) {
sapi::v::RemotePtr effective_url_ptr(nullptr); sapi::v::RemotePtr effective_url_ptr(nullptr);
PerformRequest(); ASSERT_TRUE(PerformRequest().ok());
// Get effective URL // Get effective URL
SAPI_ASSERT_OK_AND_ASSIGN( SAPI_ASSERT_OK_AND_ASSIGN(
@ -53,10 +51,10 @@ TEST_F(Curl_Test, EffectiveUrl) {
ASSERT_EQ(effective_url, kUrl); ASSERT_EQ(effective_url, kUrl);
} }
TEST_F(Curl_Test, EffectivePort) { TEST_F(CurlTest, EffectivePort) {
sapi::v::Int effective_port; sapi::v::Int effective_port;
PerformRequest(); ASSERT_TRUE(PerformRequest().ok());
// Get effective port // Get effective port
SAPI_ASSERT_OK_AND_ASSIGN( SAPI_ASSERT_OK_AND_ASSIGN(
@ -69,10 +67,10 @@ TEST_F(Curl_Test, EffectivePort) {
ASSERT_EQ(effective_port.GetValue(), port_); ASSERT_EQ(effective_port.GetValue(), port_);
} }
TEST_F(Curl_Test, ResponseCode) { TEST_F(CurlTest, ResponseCode) {
sapi::v::Int response_code; sapi::v::Int response_code;
PerformRequest(); ASSERT_TRUE(PerformRequest().ok());
// Get response code // Get response code
SAPI_ASSERT_OK_AND_ASSIGN( SAPI_ASSERT_OK_AND_ASSIGN(
@ -85,10 +83,10 @@ TEST_F(Curl_Test, ResponseCode) {
ASSERT_EQ(response_code.GetValue(), 200); ASSERT_EQ(response_code.GetValue(), 200);
} }
TEST_F(Curl_Test, ContentType) { TEST_F(CurlTest, ContentType) {
sapi::v::RemotePtr content_type_ptr(nullptr); sapi::v::RemotePtr content_type_ptr(nullptr);
PerformRequest(); ASSERT_TRUE(PerformRequest().ok());
// Get effective URL // Get effective URL
SAPI_ASSERT_OK_AND_ASSIGN( SAPI_ASSERT_OK_AND_ASSIGN(
@ -106,15 +104,14 @@ TEST_F(Curl_Test, ContentType) {
ASSERT_EQ(content_type, "text/plain"); ASSERT_EQ(content_type, "text/plain");
} }
TEST_F(Curl_Test, GETResponse) { TEST_F(CurlTest, GETResponse) {
std::string response; SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest());
PerformRequest(response);
// Compare response with expected response // Compare response with expected response
ASSERT_EQ(response, kSimpleResponse); ASSERT_EQ(response, kSimpleResponse);
} }
TEST_F(Curl_Test, POSTResponse) { TEST_F(CurlTest, POSTResponse) {
sapi::v::ConstCStr post_fields("postfields"); sapi::v::ConstCStr post_fields("postfields");
// Set request method to POST // Set request method to POST
@ -137,8 +134,7 @@ TEST_F(Curl_Test, POSTResponse) {
post_fields.PtrBefore())); post_fields.PtrBefore()));
ASSERT_EQ(setopt_post_fields, CURLE_OK); ASSERT_EQ(setopt_post_fields, CURLE_OK);
std::string response; SAPI_ASSERT_OK_AND_ASSIGN(std::string response, PerformRequest());
PerformRequest(response);
// Compare response with expected response // Compare response with expected response
ASSERT_EQ(std::string(post_fields.GetData()), response); ASSERT_EQ(std::string(post_fields.GetData()), response);