1 #include "server/api/url_parser.hpp"
2 #include "engine/polyline_compressor.hpp"
3 
4 #include <boost/fusion/include/adapt_struct.hpp>
5 #include <boost/spirit/include/phoenix.hpp>
6 #include <boost/spirit/include/qi.hpp>
7 #include <boost/spirit/repository/include/qi_iter_pos.hpp>
8 
9 #include <string>
10 #include <type_traits>
11 
12 BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL,
13                           (std::string, service)(unsigned, version)(std::string,
14                                                                     profile)(std::string, query))
15 
16 // Keep impl. TU local
17 namespace
18 {
19 namespace ph = boost::phoenix;
20 namespace qi = boost::spirit::qi;
21 
22 template <typename Iterator, typename Into> //
23 struct URLParser final : qi::grammar<Iterator, Into>
24 {
URLParser__anoncd203f340111::URLParser25     URLParser() : URLParser::base_type(start)
26     {
27         using boost::spirit::repository::qi::iter_pos;
28 
29         identifier = qi::char_("a-zA-Z0-9_.~:-");
30         percent_encoding =
31             qi::char_('%') > qi::uint_parser<unsigned char, 16, 2, 2>()[qi::_val = qi::_1];
32         polyline_chars = qi::char_("a-zA-Z0-9_[]{}@?|\\~`^") | percent_encoding;
33         all_chars = polyline_chars | qi::char_("=,;:&().-");
34 
35         service = +identifier;
36         version = qi::uint_;
37         profile = +identifier;
38         query = +all_chars;
39 
40         // Example input: /route/v1/driving/7.416351,43.731205;7.420363,43.736189
41 
42         start = qi::lit('/') > service > qi::lit('/') > qi::lit('v') > version > qi::lit('/') >
43                 profile > qi::lit('/') >
44                 qi::omit[iter_pos[ph::bind(&osrm::server::api::ParsedURL::prefix_length, qi::_val) =
45                                       qi::_1 - qi::_r1]] > query;
46 
47         BOOST_SPIRIT_DEBUG_NODES((start)(service)(version)(profile)(query))
48     }
49 
50     qi::rule<Iterator, Into> start;
51 
52     qi::rule<Iterator, std::string()> service;
53     qi::rule<Iterator, unsigned()> version;
54     qi::rule<Iterator, std::string()> profile;
55     qi::rule<Iterator, std::string()> query;
56 
57     qi::rule<Iterator, char()> identifier;
58     qi::rule<Iterator, char()> all_chars;
59     qi::rule<Iterator, char()> polyline_chars;
60     qi::rule<Iterator, char()> percent_encoding;
61 };
62 
63 } // namespace
64 
65 namespace osrm
66 {
67 namespace server
68 {
69 namespace api
70 {
71 
parseURL(std::string::iterator & iter,const std::string::iterator end)72 boost::optional<ParsedURL> parseURL(std::string::iterator &iter, const std::string::iterator end)
73 {
74     using It = std::decay<decltype(iter)>::type;
75 
76     static URLParser<It, ParsedURL(It)> const parser;
77     ParsedURL out;
78 
79     try
80     {
81         const auto ok = boost::spirit::qi::parse(iter, end, parser(boost::phoenix::val(iter)), out);
82 
83         if (ok && iter == end)
84             return boost::make_optional(out);
85     }
86     catch (const qi::expectation_failure<It> &failure)
87     {
88         // The grammar above using expectation parsers ">" does not automatically increment the
89         // iterator to the failing position. Extract the position from the exception ourselves.
90         iter = failure.first;
91     }
92 
93     return boost::none;
94 }
95 
96 } // namespace api
97 } // namespace server
98 } // namespace osrm
99