Skip to content

Commit

Permalink
URL encode location when requesting Geocoding API
Browse files Browse the repository at this point in the history
Refs: #55
  • Loading branch information
orontee committed Oct 6, 2023
1 parent 8f84cc1 commit 1db558e
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 19 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Changed

- URL encode location when requesting Geocoding API
[#55](https://github.com/orontee/taranis/issues/55)

- Fix possible crash on missing attributes in OpenWeather API payload
[#52](https://github.com/orontee/taranis/issues/52)

Expand Down
42 changes: 30 additions & 12 deletions src/http.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@

#include "errors.h"

Json::Value taranis::HttpClient::get(const std::string &url) {
namespace taranis {

std::string
HttpClient::encode_query_parameter(const std::string &parameter) const {
std::shared_ptr<char> escaped{
curl_easy_escape(nullptr, parameter.c_str(), parameter.size()),
&curl_free};
if (escaped == nullptr) {
return "";
}

std::string value{escaped.get()};
return value;
}

Json::Value HttpClient::get(const std::string &url) {
BOOST_LOG_TRIVIAL(debug) << "Sending GET request " << url;

this->ensure_network();

auto curl = this->preprare_curl();
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
this->response_data.clear();
this->response_data.reserve(10 * CURLOPT_BUFFERSIZE);

std::string response_data;
response_data.reserve(10 * CURLOPT_BUFFERSIZE);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_data);
auto curl = this->prepare_curl();
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &this->response_data);

const CURLcode code = curl_easy_perform(curl.get());
if (code != CURLE_OK) {
Expand All @@ -31,13 +46,14 @@ Json::Value taranis::HttpClient::get(const std::string &url) {
throw HttpError{response_code};
}

BOOST_LOG_TRIVIAL(debug) << "Received " << response_data.size() << " bytes";
BOOST_LOG_TRIVIAL(debug) << "Received " << this->response_data.size()
<< " bytes";

Json::Value root;
Json::CharReaderBuilder reader;
reader["collectComments"] = false;
std::string json_errors;
std::stringstream input_stream{response_data};
std::stringstream input_stream{this->response_data};
try {
if (not Json::parseFromStream(reader, input_stream, &root, &json_errors)) {
BOOST_LOG_TRIVIAL(error)
Expand All @@ -49,7 +65,7 @@ Json::Value taranis::HttpClient::get(const std::string &url) {
return root;
}

std::unique_ptr<CURL, void (*)(CURL *)> taranis::HttpClient::preprare_curl() {
std::unique_ptr<CURL, void (*)(CURL *)> HttpClient::prepare_curl() {
std::unique_ptr<CURL, void (*)(CURL *)> curl{curl_easy_init(),
&curl_easy_cleanup};

Expand All @@ -60,6 +76,7 @@ std::unique_ptr<CURL, void (*)(CURL *)> taranis::HttpClient::preprare_curl() {

curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl.get(), CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(curl.get(), CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "taranis/0.0.1");
curl_easy_setopt(curl.get(), CURLOPT_MAXREDIRS, 50L);
Expand All @@ -72,7 +89,7 @@ std::unique_ptr<CURL, void (*)(CURL *)> taranis::HttpClient::preprare_curl() {
return curl;
}

void taranis::HttpClient::ensure_network() {
void HttpClient::ensure_network() const {
iv_netinfo *netinfo = NetInfo();
if (netinfo == nullptr or not netinfo->connected) {
BOOST_LOG_TRIVIAL(debug) << "Will try to establish connection";
Expand All @@ -97,10 +114,11 @@ void taranis::HttpClient::ensure_network() {
}
}

size_t taranis::HttpClient::write_callback(void *contents, size_t size,
size_t nmemb, void *userp) {
size_t HttpClient::write_callback(void *contents, size_t size, size_t nmemb,
void *userp) {
BOOST_LOG_TRIVIAL(debug) << "Writing " << size * nmemb << " bytes to buffer";
static_cast<std::string *>(userp)->append(static_cast<char *>(contents),
size * nmemb);
return size * nmemb;
}
} // namespace taranis
8 changes: 6 additions & 2 deletions src/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ namespace taranis {

class HttpClient {
public:
std::string encode_query_parameter(const std::string &parameter) const;

Json::Value get(const std::string &url);

private:
std::unique_ptr<CURL, void (*)(CURL *)> preprare_curl();
std::string response_data;

std::unique_ptr<CURL, void (*)(CURL *)> prepare_curl();

void ensure_network();
void ensure_network() const;

static size_t write_callback(void *contents, size_t size, size_t nmemb,
void *userp);
Expand Down
17 changes: 12 additions & 5 deletions src/service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Service::identify_lonlat(const std::string &town, const std::string &country) {
}
if (not this->lonlat) {
this->request_lonlat();
BOOST_LOG_TRIVIAL(debug) << "Longitude and latitude taken from cache";
}
return this->lonlat.value();
}
Expand All @@ -89,11 +90,7 @@ void Service::request_lonlat() {

std::stringstream url;
url << openweather::url << openweather::geo_path << "?"
<< "q=" << this->town;
if (not this->country.empty()) {
url << "," << this->country;
}
url << "&"
<< "q=" << this->encode_location() << "&"
<< "appid=" << this->api_key;

auto returned_value = this->send_get_request(url.str());
Expand Down Expand Up @@ -132,6 +129,16 @@ Json::Value Service::request_onecall_api(const std::string &town,
return returned_value;
}

std::string Service::encode_location() const {
std::string location = this->client.encode_query_parameter(this->town);
if (not this->country.empty()) {
location += "," + this->client.encode_query_parameter(this->country);
}
// ⚠️ this is not the usual way to encode query parameters, in
// particular the comma must not be encoded...
return location;
}

Json::Value Service::send_get_request(const std::string &url) {
try {
return this->client.get(url);
Expand Down
2 changes: 2 additions & 0 deletions src/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class Service {
const std::string &language,
const std::string &units);

std::string encode_location() const;

Json::Value send_get_request(const std::string &url);

static Condition extract_condition(const Json::Value &value);
Expand Down

0 comments on commit 1db558e

Please sign in to comment.