Address review comments

pull/48/head
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)
target_link_libraries(curl_wrapper_and_callbacks
CURL::libcurl
sapi::sapi
)

View File

@ -19,68 +19,64 @@
#include <curl/curl.h>
// 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

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

View File

@ -18,22 +18,25 @@
#include <cstring>
#include <iostream>
#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<sapi::LenValStruct*>(userp);
char* ptr = (char*)realloc(mem->memory, mem->size + realsize + 1);
if (ptr == NULL) { // Out of memory
char* ptr = static_cast<char*>(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<char*>(mem->data);
memcpy(&(data[mem->size]), contents, real_size);
mem->size += real_size;
data[mem->size] = 0;
return realsize;
return real_size;
}

View File

@ -17,11 +17,6 @@
#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,
void* userp);

View File

@ -24,8 +24,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
absl::Status status;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<int> 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*> 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<int> 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

View File

@ -30,8 +30,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
absl::Status status;
sapi::StatusOr<CURL*> status_or_curl;
sapi::StatusOr<int> 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*> 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<int> 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<MemoryStruct> 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<sapi::LenValStruct*>(chunk.GetRemote()))->size);
status = sandbox.TransferFromSandboxee(&size);
if (!status.ok()) {
LOG(FATAL) << "sandbox.TransferFromSandboxee failed: " << status;

View File

@ -55,8 +55,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
absl::Status status;
sapi::StatusOr<int> status_or_int;
sapi::StatusOr<CURL*> 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<int> 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*> 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

View File

@ -26,9 +26,6 @@ int main(int argc, char* argv[]) {
google::InitGoogleLogging(argv[0]);
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
CurlSapiSandbox sandbox;
@ -41,71 +38,72 @@ int main(int argc, char* argv[]) {
// Number of running handles
sapi::v::Int still_running(1);
absl::StatusOr<int> 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*> 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*> 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

View File

@ -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<CURL*> status_or_curl;
sapi::StatusOr<int> 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*> 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<int> 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<std::string> 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<int> status_or_int;
// Initialize sandbox2 and sapi
CurlSapiSandbox sandbox;
@ -89,29 +73,23 @@ int main(int argc, char* argv[]) {
}
CurlApi api(&sandbox);
absl::StatusOr<int> 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<std::thread> 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

View File

@ -17,19 +17,21 @@
#include <cstdlib>
#include <cstring>
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<MemoryStruct*>(userp);
#include "sandboxed_api/vars.h"
char* ptr =
static_cast<char*>(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<sapi::LenValStruct*>(userp);
char* ptr = static_cast<char*>(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<char*>(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;
}

View File

@ -17,13 +17,8 @@
#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*)
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

View File

@ -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<CurlSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), sapi::IsOk());
absl::Status init = sandbox_->Init();
if (!init.ok()) {
return init;
}
api_ = std::make_unique<CurlApi>(sandbox_.get());
// Initialize curl
SAPI_ASSERT_OK_AND_ASSIGN(void* curl_raw_ptr, api_->curl_easy_init());
curl_ = std::make_unique<sapi::v::RemotePtr>(curl_raw_ptr);
absl::StatusOr<CURL*> 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<sapi::v::RemotePtr>(curl_handle.value());
absl::StatusOr<int> 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<std::string> 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<int> 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<sapi::LenValStruct*>(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<std::string> 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(&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);
}
}
// 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);
});
}

View File

@ -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<std::string> 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<MemoryStruct> chunk_;
sapi::v::LenVal chunk_{0};
};
#endif // TESTS_H

View File

@ -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);