Merge lp:~pete-woods/net-cpp/uri-builder into lp:net-cpp

Proposed by Pete Woods
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
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

To post a comment you must log in.
Revision history for this message
Thomas Voß (thomas-voss) wrote :

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.

review: Needs Fixing
Revision history for this message
Pete Woods (pete-woods) wrote :

Before I go changing the rest of the test suite, is this more what you wanted?

Revision history for this message
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.

Revision history for this message
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://127.0.0.1:5000"
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::make_uri(httpbin::host, {httpbin::resources::get()})
then it will URI escape the slash characters, which is not what we want at all.

The alternative would be to do this:
    net::make_uri(httpbin::host + httpbin::resources::get())
but I'm not sure if that's any better than just using a string?

lp:~pete-woods/net-cpp/uri-builder updated
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

Revision history for this message
Thomas Voß (thomas-voss) wrote :

Fair point, let's leave the httpbin tests as they are then.

review: Needs Fixing
lp:~pete-woods/net-cpp/uri-builder updated
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

Revision history for this message
Pete Woods (pete-woods) wrote :

Hopefully I should have addressed your remaining concerns now. :)

lp:~pete-woods/net-cpp/uri-builder updated
42. By Pete Woods

Make docs "compile"

43. By Pete Woods

Rename Uri components

Revision history for this message
Pete Woods (pete-woods) wrote :

Okay, I've renamed the URI components to host, path, and query_parameters now.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

LGTM, thanks for the changes.

review: Approve
lp:~pete-woods/net-cpp/uri-builder updated
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 ));

Subscribers

People subscribed via source and target branches