Page 1 of 1

Http client and C++

Posted: Wed Mar 27, 2019 7:58 am
by nicollo
Hi guys!
I'm trying to write a http client using C++ over the Esp-idf http client.
For now I have only one static method to make post requests and one request type: JsonRequest.

Request class definition:

Code: Select all

class JsonRequest: public HttpRequestInterface {
private:
	std::string url;
	std::vector<HttpHeader> headers;
	std::vector<HttpParam> params;

public:
	JsonRequest(std::string url);
	void addHeader(HttpHeader header);
	void addParam(HttpParam param);
	std::vector<HttpHeader> getHeaders();
	std::string getUrl();
	std::string paramsToString();
};
Http client definition

Code: Select all

class HttpClient {
private:
	static HttpResponse * doRequest(esp_http_client_handle_t client, HttpRequestInterface * request);
public:
	static HttpResponse * post(HttpRequestInterface * request);
};
Http client implementation:

Code: Select all

ESP32::HttpResponse * ESP32::HttpClient::post(ESP32::HttpRequestInterface * request) {
	esp_http_client_config_t config = {
		.url = request->getUrl().c_str(),
		.method = HTTP_METHOD_POST
	};
	esp_http_client_handle_t client =  esp_http_client_init(&config);
	std::string params = request->paramsToString();
    	esp_http_client_set_post_field(client, params.c_str(), params.size());
	
	return ESP32::HttpClient::doRequest(client, request);
}

ESP32::HttpResponse * ESP32::HttpClient::doRequest(esp_http_client_handle_t client, ESP32::HttpRequestInterface * request) {
	std::vector<ESP32::HttpHeader>::iterator it;
	for(it = request->getHeaders().begin(); it != request->getHeaders().end(); it++ ) {
		esp_http_client_set_header(client, it->name.c_str(), it->value.c_str());
	}
	esp_err_t err;
	if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
		ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
		// exception
	}
	int content_length =  esp_http_client_fetch_headers(client);
	char *buffer = (char *) malloc(content_length + 1);
	int total_read_len = 0, read_len;

	 if (total_read_len < content_length) {
		 read_len = esp_http_client_read(client, buffer, content_length);
		 if (read_len <= 0) {
			 ESP_LOGE(TAG, "Error read data");
		 }
		 buffer[read_len] = 0;
		 ESP_LOGD(TAG, "read_len = %d", read_len);
	 }
	 esp_http_client_close(client);
	 esp_http_client_cleanup(client);
	 ESP32::HttpResponse * response = new ESP32::HttpResponse(
			 std::string(buffer),
			 esp_http_client_get_status_code(client)
	 );
	 free(buffer);

	 return response;
}
For now a have a problem with url setting and client initialization:

Code: Select all

esp_http_client_config_t config = {
	.url = request->getUrl().c_str(),
	....
};
esp_http_client_handle_t client =  esp_http_client_init(&config);
make monitor tell me:
E (2675) HTTP_CLIENT: Error parse url \��?
E (2675) HTTP_CLIENT: Error while setting default configurations
But if i set client url as:

Code: Select all

esp_http_client_config_t config = {
	.url = "http://someDomain.com",
	....
};
Everything goes OK.

I tried to debug "request->getUrl().c_str()" with ESP_LOG - url was correct.

What can be wrong with that?

Re: Http client and C++

Posted: Wed Mar 27, 2019 11:05 pm
by ESP_Angus
Hi,

What is the implementation of "std::string getUrl();"?

It looks like this it will return a copy of the url string stored in the object. The copy in the result will only be valid until it goes out of scope, and because it's not stored anywhere it goes out of scope almost immediately. Which means the c_str() pointer will also have become invalid, by the time it is used.

When you pass the result to ESP_LOGI(), the copy remains in scope until after the logging call is done, so the c_str() is valid.

Probably what you want is to change the type of this function to "const std::string& getUrl();", so it returns a const reference to the url inside the object. You shouldn't need to change any other part of your code.

Re: Http client and C++

Posted: Thu Mar 28, 2019 5:44 pm
by nicollo
ESP_Angus wrote:
Wed Mar 27, 2019 11:05 pm
It looks like this it will return a copy of the url string stored in the object. The copy in the result will only be valid until it goes out of scope, and because it's not stored anywhere it goes out of scope almost immediately. Which means the c_str() pointer will also have become invalid...
Yep, you're right.

Code: Select all

std::string * ESP32::JsonRequest::getUrl() {
	return &this->url;
}
Fixed the problem.
Thank you!