Merge lp:~pete-woods/net-cpp/uri-builder into lp:net-cpp
- uri-builder
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Thomas Voß | ||||
Approved revision: | 43 | ||||
Merged at revision: | 38 | ||||
Proposed branch: | lp:~pete-woods/net-cpp/uri-builder | ||||
Merge into: | lp:net-cpp | ||||
Diff against target: |
369 lines (+230/-14) 10 files modified
CMakeLists.txt (+1/-1) debian/changelog (+7/-0) debian/libnet-cpp1.symbols (+2/-0) include/core/net/http/client.h (+5/-0) include/core/net/uri.h (+94/-0) src/CMakeLists.txt (+1/-0) src/core/net/http/client.cpp (+38/-1) src/core/net/http/impl/curl/client.h (+1/-0) src/core/net/uri.cpp (+27/-0) tests/http_client_test.cpp (+54/-12) |
||||
To merge this branch: | bzr merge lp:~pete-woods/net-cpp/uri-builder | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomas Voß (community) | Approve | ||
Ubuntu Phablet Team | Pending | ||
Review via email: mp+222614@code.launchpad.net |
Commit message
Add URI building API
Description of the change
Add URI building API
Pete Woods (pete-woods) wrote : | # |
Before I go changing the rest of the test suite, is this more what you wanted?
Thomas Voß (thomas-voss) wrote : | # |
> Before I go changing the rest of the test suite, is this more what you wanted?
Yes, that matches my mental model :) thanks.
Pete Woods (pete-woods) wrote : | # |
Hmm, just started looking at this, and the httpbin paths aren't really in a useful form for this.
i.e. they provide:
base = "http://
path = "/get/banana"
To put these in a useful form for the API, I'd first have to split them into their components, then pass those into the URI builder. Seems like a lot of effort to go through.
If I just ran them through like this:
net:
then it will URI escape the slash characters, which is not what we want at all.
The alternative would be to do this:
net:
but I'm not sure if that's any better than just using a string?
- 31. By PS Jenkins bot
-
Empty MP for landing.
- 32. By PS Jenkins bot
-
Update symbols
- 33. By PS Jenkins bot
-
Releasing 0.0.1+14.
10.20140611- 0ubuntu1
Thomas Voß (thomas-voss) wrote : | # |
Fair point, let's leave the httpbin tests as they are then.
- 35. By Pete Woods
-
Clean up
- 36. By Pete Woods
-
URL escape the endpoint
- 37. By Pete Woods
-
Extract out Uri class
- 38. By Pete Woods
-
Add some documentation to Uri
- 39. By Pete Woods
-
Rename endpoints -> endpoint
- 40. By Pete Woods
-
Bump version number for new API method
- 41. By Pete Woods
-
Remove un-necessary include
Pete Woods (pete-woods) wrote : | # |
Hopefully I should have addressed your remaining concerns now. :)
- 42. By Pete Woods
-
Make docs "compile"
- 43. By Pete Woods
-
Rename Uri components
Pete Woods (pete-woods) wrote : | # |
Okay, I've renamed the URI components to host, path, and query_parameters now.
Thomas Voß (thomas-voss) wrote : | # |
LGTM, thanks for the changes.
- 44. By Pete Woods
-
Merge trunk
- 45. By Pete Woods
-
Merge trunk
- 46. By Pete Woods
-
Add new symbols
- 47. By Pete Woods
-
Increment the minor revision, instead of the patch one
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-06-27 08:16:12 +0000 |
3 | +++ CMakeLists.txt 2014-07-30 15:16:26 +0000 |
4 | @@ -27,7 +27,7 @@ |
5 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") |
6 | |
7 | set(NET_CPP_VERSION_MAJOR 1) |
8 | -set(NET_CPP_VERSION_MINOR 0) |
9 | +set(NET_CPP_VERSION_MINOR 1) |
10 | set(NET_CPP_VERSION_PATCH 0) |
11 | |
12 | include(CTest) |
13 | |
14 | === modified file 'debian/changelog' |
15 | --- debian/changelog 2014-07-29 10:18:40 +0000 |
16 | +++ debian/changelog 2014-07-30 15:16:26 +0000 |
17 | @@ -1,3 +1,10 @@ |
18 | +net-cpp (1.1.0-0ubuntu1) UNRELEASED; urgency=medium |
19 | + |
20 | + [ Pete Woods ] |
21 | + * Add Uri class and corresponding string conversion. |
22 | + |
23 | + -- Pete Woods <pete.woods@canonical.com> Wed, 30 Jul 2014 14:35:58 +0100 |
24 | + |
25 | net-cpp (1.0.0+14.10.20140729.1-0ubuntu1) utopic; urgency=low |
26 | |
27 | [ Pete Woods ] |
28 | |
29 | === modified file 'debian/libnet-cpp1.symbols' |
30 | --- debian/libnet-cpp1.symbols 2014-06-27 08:16:12 +0000 |
31 | +++ debian/libnet-cpp1.symbols 2014-07-30 15:16:26 +0000 |
32 | @@ -1,5 +1,7 @@ |
33 | libnet-cpp.so.1 libnet-cpp1 #MINVER# |
34 | (c++)"core::net::http::make_client()@Base" 0.0.1+14.10.20140611 |
35 | + (c++)"core::net::make_uri(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&)@Base" 0replaceme |
36 | + (c++)"core::net::http::Client::uri_to_string(core::net::Uri const&) const@Base" 0replaceme |
37 | (c++)"core::net::http::Client::Errors::HttpMethodNotSupported::HttpMethodNotSupported(core::net::http::Method, core::Location const&)@Base" 0.0.1+14.10.20140611 |
38 | (c++)"core::net::http::Client::Errors::HttpMethodNotSupported::~HttpMethodNotSupported()@Base" 0.0.1+14.10.20140611 |
39 | (c++)"core::net::http::Client::post_form(core::net::http::Request::Configuration const&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&)@Base" 0.0.1+14.10.20140611 |
40 | |
41 | === modified file 'include/core/net/http/client.h' |
42 | --- include/core/net/http/client.h 2014-06-04 14:59:34 +0000 |
43 | +++ include/core/net/http/client.h 2014-07-30 15:16:26 +0000 |
44 | @@ -31,6 +31,9 @@ |
45 | { |
46 | namespace net |
47 | { |
48 | + |
49 | +struct Uri; |
50 | + |
51 | namespace http |
52 | { |
53 | class ContentType; |
54 | @@ -95,6 +98,8 @@ |
55 | Client& operator=(const Client&) = delete; |
56 | bool operator==(const Client&) const = delete; |
57 | |
58 | + virtual std::string uri_to_string (const core::net::Uri& uri) const; |
59 | + |
60 | /** @brief Percent-encodes the given string. */ |
61 | virtual std::string url_escape(const std::string& s) const = 0; |
62 | |
63 | |
64 | === added file 'include/core/net/uri.h' |
65 | --- include/core/net/uri.h 1970-01-01 00:00:00 +0000 |
66 | +++ include/core/net/uri.h 2014-07-30 15:16:26 +0000 |
67 | @@ -0,0 +1,94 @@ |
68 | +/* |
69 | + * Copyright © 2014 Canonical Ltd. |
70 | + * |
71 | + * This program is free software: you can redistribute it and/or modify it |
72 | + * under the terms of the GNU Lesser General Public License version 3, |
73 | + * as published by the Free Software Foundation. |
74 | + * |
75 | + * This program is distributed in the hope that it will be useful, |
76 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
77 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
78 | + * GNU Lesser General Public License for more details. |
79 | + * |
80 | + * You should have received a copy of the GNU Lesser General Public License |
81 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
82 | + * |
83 | + * Authored by: Pete Woods <pete.woods@canonical.com> |
84 | + */ |
85 | + |
86 | +#ifndef CORE_NET_URI_H_ |
87 | +#define CORE_NET_URI_H_ |
88 | + |
89 | +#include <string> |
90 | +#include <vector> |
91 | + |
92 | +#include <core/net/visibility.h> |
93 | + |
94 | +namespace core |
95 | +{ |
96 | +namespace net |
97 | +{ |
98 | + |
99 | + |
100 | +/** |
101 | + * @brief The Uri class encapsulates the components of a URI |
102 | + */ |
103 | +struct Uri |
104 | +{ |
105 | + typedef std::string Host; |
106 | + |
107 | + typedef std::vector<std::string> Path; |
108 | + |
109 | + typedef std::vector<std::pair<std::string, std::string>> QueryParameters; |
110 | + |
111 | + /** |
112 | + * @brief The host is the first part of the URI, including the protocol |
113 | + * |
114 | + * e.g. |
115 | + * \code{.cpp} |
116 | + * "http://www.ubuntu.com" |
117 | + * \endcode |
118 | + */ |
119 | + Host host; |
120 | + |
121 | + /** |
122 | + * @brief the path components |
123 | + * |
124 | + * e.g. |
125 | + * \code{.cpp} |
126 | + * {"api", "v3", "search"} |
127 | + * \endcode |
128 | + */ |
129 | + Path path; |
130 | + |
131 | + /** |
132 | + * @brief The CGI query parameters as ordered key-value pairs |
133 | + * |
134 | + * e.g. |
135 | + * \code{.cpp} |
136 | + * {{"key1", "value1"}, {"key2", "value2"}} |
137 | + * \endcode |
138 | + */ |
139 | + QueryParameters query_parameters; |
140 | +}; |
141 | + |
142 | +/** |
143 | + * @brief Build a URI from its components |
144 | + * |
145 | + * e.g. |
146 | + * \code{.cpp} |
147 | + * std::string query = "banana"; |
148 | + * auto uri = core::net::make_uri("https://api.mydomain.com", {"api", "v3", "search"}, {{"query", query}}); |
149 | + * \endcode |
150 | + * |
151 | + * When converted to a std::string with core::net::http::client::uri_to_string() |
152 | + * the endpoint and parameters will be URL-escaped. |
153 | + */ |
154 | +CORE_NET_DLL_PUBLIC |
155 | +Uri make_uri (const Uri::Host& host, const Uri::Path& path = Uri::Path(), |
156 | + const Uri::QueryParameters& query_parameters = Uri::QueryParameters()); |
157 | + |
158 | +} |
159 | +} |
160 | + |
161 | +#endif // CORE_NET_URI_H_ |
162 | |
163 | === modified file 'src/CMakeLists.txt' |
164 | --- src/CMakeLists.txt 2014-06-04 14:59:34 +0000 |
165 | +++ src/CMakeLists.txt 2014-07-30 15:16:26 +0000 |
166 | @@ -25,6 +25,7 @@ |
167 | core/location.cpp |
168 | |
169 | core/net/error.cpp |
170 | + core/net/uri.cpp |
171 | |
172 | core/net/http/client.cpp |
173 | core/net/http/error.cpp |
174 | |
175 | === modified file 'src/core/net/http/client.cpp' |
176 | --- src/core/net/http/client.cpp 2014-05-06 11:05:04 +0000 |
177 | +++ src/core/net/http/client.cpp 2014-07-30 15:16:26 +0000 |
178 | @@ -16,8 +16,8 @@ |
179 | * Authored by: Thomas Voß <thomas.voss@canonical.com> |
180 | */ |
181 | |
182 | +#include <core/net/uri.h> |
183 | #include <core/net/http/client.h> |
184 | - |
185 | #include <core/net/http/content_type.h> |
186 | |
187 | #include <sstream> |
188 | @@ -49,3 +49,40 @@ |
189 | |
190 | return post(configuration, ss.str(), http::ContentType::x_www_form_urlencoded); |
191 | } |
192 | + |
193 | +std::string http::Client::uri_to_string(const core::net::Uri& uri) const |
194 | +{ |
195 | + std::ostringstream s; |
196 | + |
197 | + // Start with the host of the URI |
198 | + s << uri.host; |
199 | + |
200 | + // Append each of the components of the path |
201 | + for (const std::string& part : uri.path) |
202 | + { |
203 | + s << "/" << url_escape(part); |
204 | + } |
205 | + |
206 | + // Append the parameters |
207 | + bool first = true; |
208 | + for (const std::pair<std::string, std::string>& query_parameter : uri.query_parameters) |
209 | + { |
210 | + if (first) |
211 | + { |
212 | + // The first parameter needs a ? |
213 | + s << "?"; |
214 | + first = false; |
215 | + } |
216 | + else |
217 | + { |
218 | + // The rest are separated with a & |
219 | + s << "&"; |
220 | + } |
221 | + |
222 | + // URL escape the parameters |
223 | + s << url_escape(query_parameter.first) << "=" << url_escape(query_parameter.second); |
224 | + } |
225 | + |
226 | + // We're done |
227 | + return s.str(); |
228 | +} |
229 | |
230 | === modified file 'src/core/net/http/impl/curl/client.h' |
231 | --- src/core/net/http/impl/curl/client.h 2014-06-04 14:59:34 +0000 |
232 | +++ src/core/net/http/impl/curl/client.h 2014-07-30 15:16:26 +0000 |
233 | @@ -38,6 +38,7 @@ |
234 | Client(); |
235 | |
236 | // From core::net::http::Client |
237 | + |
238 | std::string url_escape(const std::string& s) const; |
239 | |
240 | std::string base64_encode(const std::string& s) const override; |
241 | |
242 | === added file 'src/core/net/uri.cpp' |
243 | --- src/core/net/uri.cpp 1970-01-01 00:00:00 +0000 |
244 | +++ src/core/net/uri.cpp 2014-07-30 15:16:26 +0000 |
245 | @@ -0,0 +1,27 @@ |
246 | +/* |
247 | + * Copyright © 2014 Canonical Ltd. |
248 | + * |
249 | + * This program is free software: you can redistribute it and/or modify it |
250 | + * under the terms of the GNU Lesser General Public License version 3, |
251 | + * as published by the Free Software Foundation. |
252 | + * |
253 | + * This program is distributed in the hope that it will be useful, |
254 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
255 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
256 | + * GNU Lesser General Public License for more details. |
257 | + * |
258 | + * You should have received a copy of the GNU Lesser General Public License |
259 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
260 | + * |
261 | + * Authored by: Pete Woods <pete.woods@canonical.com> |
262 | + */ |
263 | + |
264 | +#include <core/net/uri.h> |
265 | + |
266 | +namespace net = core::net; |
267 | + |
268 | +net::Uri net::make_uri (const net::Uri::Host& host, const net::Uri::Path& path, |
269 | + const net::Uri::QueryParameters& query_parameters) |
270 | +{ |
271 | + return net::Uri{ host, path, query_parameters }; |
272 | +} |
273 | |
274 | === modified file 'tests/http_client_test.cpp' |
275 | --- tests/http_client_test.cpp 2014-07-29 10:16:36 +0000 |
276 | +++ tests/http_client_test.cpp 2014-07-30 15:16:26 +0000 |
277 | @@ -17,6 +17,7 @@ |
278 | */ |
279 | |
280 | #include <core/net/error.h> |
281 | +#include <core/net/uri.h> |
282 | #include <core/net/http/client.h> |
283 | #include <core/net/http/content_type.h> |
284 | #include <core/net/http/request.h> |
285 | @@ -55,6 +56,24 @@ |
286 | static const bool is_initialized = init(); |
287 | } |
288 | |
289 | +TEST(HttpClient, uri_to_string) |
290 | +{ |
291 | + // We obtain a default client instance, dispatching to the default implementation. |
292 | + auto client = http::make_client(); |
293 | + |
294 | + EXPECT_EQ("http://baz.com", client->uri_to_string(net::make_uri("http://baz.com"))); |
295 | + |
296 | + EXPECT_EQ("http://foo.com/foo%20bar/baz%20boz", |
297 | + client->uri_to_string(net::make_uri("http://foo.com", |
298 | + { "foo bar", "baz boz" }))); |
299 | + |
300 | + EXPECT_EQ( |
301 | + "http://banana.fruit/my/endpoint?hello%20there=good%20bye&happy=sad", |
302 | + client->uri_to_string(net::make_uri("http://banana.fruit", |
303 | + { "my", "endpoint" }, |
304 | + { { "hello there", "good bye" }, { "happy", "sad" } }))); |
305 | +} |
306 | + |
307 | TEST(HttpClient, head_request_for_existing_resource_succeeds) |
308 | { |
309 | // We obtain a default client instance, dispatching to the default implementation. |
310 | @@ -550,9 +569,9 @@ |
311 | response.status); |
312 | } |
313 | |
314 | -typedef std::pair<std::string, std::string> Base64TestParams; |
315 | +typedef std::pair<std::string, std::string> StringPairTestParams; |
316 | |
317 | -class HttpClientBase64Test : public ::testing::TestWithParam<Base64TestParams> { |
318 | +class HttpClientBase64Test : public ::testing::TestWithParam<StringPairTestParams> { |
319 | }; |
320 | |
321 | TEST_P(HttpClientBase64Test, encoder) |
322 | @@ -581,14 +600,37 @@ |
323 | |
324 | INSTANTIATE_TEST_CASE_P(Base64Fixtures, HttpClientBase64Test, |
325 | ::testing::Values( |
326 | - Base64TestParams("", ""), |
327 | - Base64TestParams("M", "TQ=="), |
328 | - Base64TestParams("Ma", "TWE="), |
329 | - Base64TestParams("Man", "TWFu"), |
330 | - Base64TestParams("pleasure.", "cGxlYXN1cmUu"), |
331 | - Base64TestParams("leasure.", "bGVhc3VyZS4="), |
332 | - Base64TestParams("easure.", "ZWFzdXJlLg=="), |
333 | - Base64TestParams("asure.", "YXN1cmUu"), |
334 | - Base64TestParams("sure.", "c3VyZS4="), |
335 | - Base64TestParams("bananas are tasty", "YmFuYW5hcyBhcmUgdGFzdHk=") |
336 | + StringPairTestParams("", ""), |
337 | + StringPairTestParams("M", "TQ=="), |
338 | + StringPairTestParams("Ma", "TWE="), |
339 | + StringPairTestParams("Man", "TWFu"), |
340 | + StringPairTestParams("pleasure.", "cGxlYXN1cmUu"), |
341 | + StringPairTestParams("leasure.", "bGVhc3VyZS4="), |
342 | + StringPairTestParams("easure.", "ZWFzdXJlLg=="), |
343 | + StringPairTestParams("asure.", "YXN1cmUu"), |
344 | + StringPairTestParams("sure.", "c3VyZS4="), |
345 | + StringPairTestParams("bananas are tasty", "YmFuYW5hcyBhcmUgdGFzdHk=") |
346 | + )); |
347 | + |
348 | +class HttpClientUrlEscapeTest : public ::testing::TestWithParam<StringPairTestParams> { |
349 | +}; |
350 | + |
351 | +TEST_P(HttpClientUrlEscapeTest, url_escape) |
352 | +{ |
353 | + // We obtain a default client instance, dispatching to the default implementation. |
354 | + auto client = http::make_client(); |
355 | + |
356 | + // Get our encoding parameters |
357 | + auto param = GetParam(); |
358 | + |
359 | + // Try the url_escape out |
360 | + EXPECT_EQ(param.second, client->url_escape(param.first)); |
361 | +} |
362 | + |
363 | +INSTANTIATE_TEST_CASE_P(UrlEscapeFixtures, HttpClientUrlEscapeTest, |
364 | + ::testing::Values( |
365 | + StringPairTestParams("", ""), |
366 | + StringPairTestParams("Hello Günter", "Hello%20G%C3%BCnter"), |
367 | + StringPairTestParams("That costs £20", "That%20costs%20%C2%A320"), |
368 | + StringPairTestParams("Microsoft®", "Microsoft%C2%AE") |
369 | )); |
Thanks for getting started on this. I have a few comments:
(1.) I would propose to have a
struct Uri
{
struct Endpoint
{
};
struct Parameters
{
};
};
Where Endpoint and Parameters could just be appropriate typedefs. We could put it into core/net/uri.h, being ready for consumption by core/net/ http/client. h.
(2.) We should provide a default implementation of build_uri, that can be reused by any implementation that provides implementations for url_escape.
(3.) There are some manual uri setups in the test-cases. I would appreciate it if you could change those to leverage the new interface.