I'm pretty sure this is just my fault as a programmer, but I cannot see the flaw. I'm using libcurl's multi interface to talk to a server, emulating multiple simultaneous clients. Each client is represented by a C++ "request" object. One member of the object is a ostringstream into which the libcurl write function will put its data. However, after multi_perform is finished, no data is in the ostringstream.
Code:
class request {
private:
std::string* url;
std::ostringstream* content_stream;
std::string* content;
long code;
double time;
CURL* easy_handle;
CURLM* multi_handle;
struct curl_slist* headers;
public:
request(CURLM* multi_handle, const char* method) {
this->url = new std::string((boost::format("%s?method=%s") % JSON_RPC_TUNNEL % method).str());
this->multi_handle = multi_handle;
this->headers = NULL;
}
/*
* CURL callback function to write output into a stream.
*/
static size_t write_function(void* buffer, size_t size, size_t count, void* stream) {
static_cast<std::ostream*>(stream)->write(static_cast<const char*>(buffer), count);
return count;
}
void do_request(const char* json) {
this->easy_handle = curl_easy_init();
this->content_stream = new std::ostringstream();
// handle the content-type header
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(this->easy_handle, CURLOPT_HTTPHEADER, headers);
// set the data to upload
curl_easy_setopt(this->easy_handle, CURLOPT_POSTFIELDS, json);
curl_easy_setopt(this->easy_handle, CURLOPT_POSTFIELDSIZE, std::strlen(json));
// set more options
curl_easy_setopt(this->easy_handle, CURLOPT_URL, url->c_str());
curl_easy_setopt(this->easy_handle, CURLOPT_VERBOSE, true);
// set the write-back information
curl_easy_setopt(this->easy_handle, CURLOPT_WRITEFUNCTION, &request::write_function);
curl_easy_setopt(this->easy_handle, CURLOPT_WRITEDATA, content_stream);
// curl_easy_perform(easy_handle);
curl_multi_add_handle(this->multi_handle, easy_handle);
}
void extract_results() {
this->content = new std::string(content_stream->str());
curl_easy_getinfo(this->easy_handle, CURLINFO_RESPONSE_CODE, &(this->code));
curl_easy_getinfo(this->easy_handle, CURLINFO_TOTAL_TIME, &(this->time));
}
~request() {
delete this->url;
delete this->content_stream;
delete this->content;
curl_slist_free_all(this->headers);
curl_easy_cleanup(this->easy_handle);
}
};
Code:
class client {
private:
unsigned int id;
request* r;
public:
client(int id) {
this->id = id;
}
~client() {
delete this->r;
}
unsigned int get_id() {
return this->id;
}
void do_request(CURLM* multi_handle, const char* method, const char* json) {
this->r = new request(multi_handle, method);
this->r->do_request(json);
}
request* get_request() {
return this->r;
}
};
Code:
int main(int argc, char** argv) {
curl_global_init(CURL_GLOBAL_SSL);
client* c[MAX_THREADS];
for (unsigned int i = 0; i < MAX_THREADS; i += 1) {
c[i] = new client(i);
}
CURLM* multi_handle = curl_multi_init();
for (unsigned int i = 0; i < MAX_THREADS; i += 1) {
phase_one(multi_handle, c[i]);
}
int completed = 0;
int completed_total = 0;
while (completed_total < MAX_THREADS) {
curl_multi_perform(multi_handle, &completed);
completed_total += completed;
completed = 0;
}
for (unsigned int i = 0; i < MAX_THREADS; i += 1) {
result r;
c[i]->get_request()->extract_results();
phase_two(c[i], &r);
std::cerr << r << std::endl;
}
curl_multi_cleanup(multi_handle);
curl_global_cleanup();
return 0;
}
Here, the function phase_one() sets up the data that will be sent to the server, calling do_request on the client object, which sets up the request and registers to the multi handle. Then the multi is performed. Lastly, phase_two() has the request take the data (like HTTP response code and content) from its easy handle and from the ostringstream. Only, nothing ever comes back, and the values of the HTTP code and time remain unchanged.
So, in short, it seems not to work! The data that should be available is not, and I can assure that the curl request is correct as it works fine when performing the easy handle. I think, therefore, it must be a scope or memory problem, perhaps. If anybody has any ideas, I greatly appreciate it.