Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add one shot mode to valhalla_service #2624

Merged
merged 4 commits into from Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -170,6 +170,7 @@
* ADDED: Costing option to ignore_closures when routing with current flow [#2615](https://github.com/valhalla/valhalla/pull/2615)
* ADDED: Defines the incident tile schema and incident metadata [#2620](https://github.com/valhalla/valhalla/pull/2620)
* ADDED: Moves incident serializer logic into a generic serializer [#2621](https://github.com/valhalla/valhalla/pull/2621)
* ADDED: One shot mode to valhalla_service so you can run a single request of any type without starting a server [#2624](https://github.com/valhalla/valhalla/pull/2624)

## Release Date: 2019-11-21 Valhalla 3.0.9
* **Bug Fix**
Expand Down
9 changes: 4 additions & 5 deletions CMakeLists.txt
Expand Up @@ -254,16 +254,15 @@ endfunction()
## Valhalla programs
set(valhalla_programs valhalla_run_map_match valhalla_benchmark_loki valhalla_benchmark_skadi
valhalla_run_isochrone valhalla_run_route valhalla_benchmark_adjacency_list valhalla_run_matrix
valhalla_path_comparison valhalla_export_edges valhalla_expand_bounding_box)
valhalla_path_comparison valhalla_export_edges valhalla_expand_bounding_box valhalla_service)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build it no matter what


## Valhalla data tools
set(valhalla_data_tools valhalla_build_statistics valhalla_ways_to_edges valhalla_validate_transit
valhalla_benchmark_admins valhalla_build_connectivity valhalla_build_tiles
valhalla_build_admins valhalla_convert_transit valhalla_fetch_transit valhalla_query_transit
valhalla_add_predicted_traffic)
valhalla_benchmark_admins valhalla_build_connectivity valhalla_build_tiles valhalla_build_admins
valhalla_convert_transit valhalla_fetch_transit valhalla_query_transit valhalla_add_predicted_traffic)

## Valhalla services
set(valhalla_services valhalla_service valhalla_loki_worker valhalla_odin_worker valhalla_thor_worker)
set(valhalla_services valhalla_loki_worker valhalla_odin_worker valhalla_thor_worker)

if(ENABLE_TOOLS)
foreach(program ${valhalla_programs})
Expand Down
88 changes: 86 additions & 2 deletions src/valhalla_service.cc
Expand Up @@ -12,29 +12,112 @@
#include "baldr/rapidjson_utils.h"
#include <boost/property_tree/ptree.hpp>

#ifdef HAVE_HTTP
#include <prime_server/http_protocol.hpp>
#include <prime_server/prime_server.hpp>
using namespace prime_server;
#endif

#include "midgard/logging.h"

#include "loki/worker.h"
#include "odin/worker.h"
#include "thor/worker.h"
#include "tyr/actor.h"

int main(int argc, char** argv) {

if (argc < 2) {
#ifdef HAVE_HTTP
if (argc < 2 || argc > 4) {
LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json [concurrency]");
LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json action json_request");
return 1;
}
#else
if (argc < 4) {
LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json action json_request");
return 1;
}
#endif

// config file
// TODO: validate the config
std::string config_file(argv[1]);
boost::property_tree::ptree config;
rapidjson::read_json(config_file, config);

// one shot direct request mode
kevinkreiser marked this conversation as resolved.
Show resolved Hide resolved
if (argc == 4) {
// because we want the program output to go only to stdout we force any logging to be stderr
valhalla::midgard::logging::Configure({{"type", "std_err"}});

// setup an object that can answer the request
valhalla::tyr::actor_t actor(config);

// figure out which action
valhalla::Options::Action action;
if (!valhalla::Options_Action_Enum_Parse(argv[2], &action)) {
std::cerr << "Unknown action" << std::endl;
return 1;
}

// do the right action
valhalla::Api request;
try {
switch (action) {
case valhalla::Options::route:
std::cout << actor.route(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::locate:
std::cout << actor.locate(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::sources_to_targets:
std::cout << actor.matrix(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::optimized_route:
std::cout << actor.optimized_route(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::isochrone:
std::cout << actor.isochrone(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::trace_route:
std::cout << actor.trace_route(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::trace_attributes:
std::cout << actor.trace_attributes(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::height:
std::cout << actor.height(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::transit_available:
std::cout << actor.transit_available(argv[3], nullptr, &request) << std::endl;
break;
case valhalla::Options::expansion:
std::cout << actor.expansion(argv[3], nullptr, &request) << std::endl;
break;
default:
std::cerr << "Unknown action" << std::endl;
return 1;
}
} // request processing error specific error condition
catch (const valhalla::valhalla_exception_t& ve) {
std::cout << valhalla::jsonify_error(ve, request) << std::endl;
return 1;
} // it was a regular exception!?
catch (const std::exception& e) {
std::cout << jsonify_error({599, std::string(e.what())}, request) << std::endl;
return 1;
} // anything else
catch (...) {
std::cout << jsonify_error({599, std::string("Unknown exception thrown")}, request)
<< std::endl;
return 1;
}

// we are done
return 0;
}

#ifdef HAVE_HTTP
// grab the endpoints
std::string listen = config.get<std::string>("httpd.service.listen");
std::string loopback = config.get<std::string>("httpd.service.loopback");
Expand Down Expand Up @@ -110,6 +193,7 @@ int main(int argc, char** argv) {

// wait forever (or for interrupt)
server_thread.join();
#endif

return 0;
}
52 changes: 28 additions & 24 deletions src/worker.cc
Expand Up @@ -961,6 +961,32 @@ void ParseApi(const std::string& request, Options::Action action, valhalla::Api&
from_json(document, *api.mutable_options());
}

std::string jsonify_error(const valhalla_exception_t& exception, const Api& request) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was pulled out of the service only method below and is now used when the actor methods throw, which is what the workers do. we could move the try catch inside the actor and have it handle it all but taht would require rewriting some unit tests and i didn twant to bite that off yet. we can leave that for the future

// get the http status
std::stringstream body;

// overwrite with osrm error response
if (request.options().format() == Options::osrm) {
auto found = OSRM_ERRORS_CODES.find(exception.code);
if (found == OSRM_ERRORS_CODES.cend()) {
found = OSRM_ERRORS_CODES.find(199);
}
body << (request.options().has_jsonp() ? request.options().jsonp() + "(" : "") << found->second
<< (request.options().has_jsonp() ? ")" : "");
} // valhalla error response
else {
// build up the json map
auto json_error = baldr::json::map({});
json_error->emplace("status", exception.http_message);
json_error->emplace("status_code", static_cast<uint64_t>(exception.http_code));
json_error->emplace("error", std::string(exception.message));
json_error->emplace("error_code", static_cast<uint64_t>(exception.code));
body << (request.options().has_jsonp() ? request.options().jsonp() + "(" : "") << *json_error
<< (request.options().has_jsonp() ? ")" : "");
}
return body.str();
}

#ifdef HAVE_HTTP
void ParseApi(const http_request_t& request, valhalla::Api& api) {
api.Clear();
Expand Down Expand Up @@ -1032,31 +1058,9 @@ const headers_t::value_type ATTACHMENT{"Content-Disposition", "attachment; filen
worker_t::result_t jsonify_error(const valhalla_exception_t& exception,
http_request_info_t& request_info,
const Api& request) {
// get the http status
std::stringstream body;

// overwrite with osrm error response
if (request.options().format() == Options::osrm) {
auto found = OSRM_ERRORS_CODES.find(exception.code);
if (found == OSRM_ERRORS_CODES.cend()) {
found = OSRM_ERRORS_CODES.find(199);
}
body << (request.options().has_jsonp() ? request.options().jsonp() + "(" : "") << found->second
<< (request.options().has_jsonp() ? ")" : "");
} // valhalla error response
else {
// build up the json map
auto json_error = baldr::json::map({});
json_error->emplace("status", exception.http_message);
json_error->emplace("status_code", static_cast<uint64_t>(exception.http_code));
json_error->emplace("error", std::string(exception.message));
json_error->emplace("error_code", static_cast<uint64_t>(exception.code));
body << (request.options().has_jsonp() ? request.options().jsonp() + "(" : "") << *json_error
<< (request.options().has_jsonp() ? ")" : "");
}

worker_t::result_t result{false, std::list<std::string>(), ""};
http_response_t response(exception.http_code, exception.http_message, body.str(),
http_response_t response(exception.http_code, exception.http_message,
jsonify_error(exception, request),
headers_t{CORS, request.options().has_jsonp() ? worker::JS_MIME
: worker::JSON_MIME});
response.from_info(request_info);
Expand Down
18 changes: 9 additions & 9 deletions valhalla/worker.h
Expand Up @@ -151,16 +151,8 @@ void ParseApi(const std::string& json_request, Options::Action action, Api& api)
void ParseApi(const prime_server::http_request_t& http_request, Api& api);
#endif

std::string jsonify_error(const valhalla_exception_t& exception, const Api& options);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only real change. just moved the other thing down so the jsonify methods would be closer together

#ifdef HAVE_HTTP

namespace worker {
using content_type = prime_server::headers_t::value_type;
const content_type JSON_MIME{"Content-type", "application/json;charset=utf-8"};
const content_type JS_MIME{"Content-type", "application/javascript;charset=utf-8"};
const content_type XML_MIME{"Content-type", "text/xml;charset=utf-8"};
const content_type GPX_MIME{"Content-type", "application/gpx+xml;charset=utf-8"};
} // namespace worker

prime_server::worker_t::result_t jsonify_error(const valhalla_exception_t& exception,
prime_server::http_request_info_t& request_info,
const Api& options);
Expand All @@ -170,6 +162,14 @@ prime_server::worker_t::result_t to_response(const baldr::json::ArrayPtr& array,
prime_server::worker_t::result_t to_response(const baldr::json::MapPtr& map,
prime_server::http_request_info_t& request_info,
const Api& options);
namespace worker {
using content_type = prime_server::headers_t::value_type;
const content_type JSON_MIME{"Content-type", "application/json;charset=utf-8"};
const content_type JS_MIME{"Content-type", "application/javascript;charset=utf-8"};
const content_type XML_MIME{"Content-type", "text/xml;charset=utf-8"};
const content_type GPX_MIME{"Content-type", "application/gpx+xml;charset=utf-8"};
} // namespace worker

prime_server::worker_t::result_t
to_response(const std::string& data,
prime_server::http_request_info_t& request_info,
Expand Down