blob: eba7db84df1a7b106326ab49aaaa2553186fc4ea [file] [log] [blame]
Alex Vakulenkoa629bed2014-08-20 16:16:34 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenko9ed0cab2015-10-12 15:21:28 -07005#include <brillo/url_utils.h>
Alex Vakulenkoa629bed2014-08-20 16:16:34 -07006
7#include <algorithm>
8
9namespace {
10// Given a URL string, determine where the query string starts and ends.
11// URLs have schema, domain and path (along with possible user name, password
12// and port number which are of no interest for us here) which could optionally
13// have a query string that is separated from the path by '?'. Finally, the URL
14// could also have a '#'-separated URL fragment which is usually used by the
15// browser as a bookmark element. So, for example:
16// http://server.com/path/to/object?k=v&foo=bar#fragment
17// Here:
18// http://server.com/path/to/object - is the URL of the object,
19// ?k=v&foo=bar - URL query string
20// #fragment - URL fragment string
21// If |exclude_fragment| is true, the function returns the start character and
22// the length of the query string alone. If it is false, the query string length
23// will include both the query string and the fragment.
Alex Vakulenko05d29042015-01-13 09:39:25 -080024bool GetQueryStringPos(const std::string& url,
25 bool exclude_fragment,
26 size_t* query_pos,
27 size_t* query_len) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -070028 size_t query_start = url.find_first_of("?#");
29 if (query_start == std::string::npos) {
30 *query_pos = url.size();
31 if (query_len)
32 *query_len = 0;
33 return false;
34 }
35
36 *query_pos = query_start;
37 if (query_len) {
38 size_t query_end = url.size();
39
40 if (exclude_fragment) {
41 if (url[query_start] == '?') {
42 size_t pos_fragment = url.find('#', query_start);
43 if (pos_fragment != std::string::npos)
44 query_end = pos_fragment;
45 } else {
46 query_end = query_start;
47 }
48 }
49 *query_len = query_end - query_start;
50 }
51 return true;
52}
53} // anonymous namespace
54
Alex Vakulenko9ed0cab2015-10-12 15:21:28 -070055namespace brillo {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -070056
57std::string url::TrimOffQueryString(std::string* url) {
58 size_t query_pos;
59 if (!GetQueryStringPos(*url, false, &query_pos, nullptr))
60 return std::string();
61 std::string query_string = url->substr(query_pos);
62 url->resize(query_pos);
63 return query_string;
64}
65
Alex Vakulenko05d29042015-01-13 09:39:25 -080066std::string url::Combine(const std::string& url, const std::string& subpath) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -070067 return CombineMultiple(url, {subpath});
68}
69
Alex Vakulenko05d29042015-01-13 09:39:25 -080070std::string url::CombineMultiple(const std::string& url,
71 const std::vector<std::string>& parts) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -070072 std::string result = url;
73 if (!parts.empty()) {
74 std::string query_string = TrimOffQueryString(&result);
75 for (const auto& part : parts) {
76 if (!part.empty()) {
77 if (!result.empty() && result.back() != '/')
78 result += '/';
79 size_t non_slash_pos = part.find_first_not_of('/');
80 if (non_slash_pos != std::string::npos)
81 result += part.substr(non_slash_pos);
82 }
83 }
84 result += query_string;
85 }
86 return result;
87}
88
Alex Vakulenko05d29042015-01-13 09:39:25 -080089std::string url::GetQueryString(const std::string& url, bool remove_fragment) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -070090 std::string query_string;
91 size_t query_pos, query_len;
92 if (GetQueryStringPos(url, remove_fragment, &query_pos, &query_len)) {
93 query_string = url.substr(query_pos, query_len);
94 }
95 return query_string;
96}
97
98data_encoding::WebParamList url::GetQueryStringParameters(
99 const std::string& url) {
100 // Extract the query string and remove the leading '?'.
101 std::string query_string = GetQueryString(url, true);
102 if (!query_string.empty() && query_string.front() == '?')
103 query_string.erase(query_string.begin());
104 return data_encoding::WebParamsDecode(query_string);
105}
106
Alex Vakulenko05d29042015-01-13 09:39:25 -0800107std::string url::GetQueryStringValue(const std::string& url,
108 const std::string& name) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -0700109 return GetQueryStringValue(GetQueryStringParameters(url), name);
110}
111
112std::string url::GetQueryStringValue(const data_encoding::WebParamList& params,
113 const std::string& name) {
114 for (const auto& pair : params) {
115 if (name.compare(pair.first) == 0)
116 return pair.second;
117 }
118 return std::string();
119}
120
Alex Vakulenko05d29042015-01-13 09:39:25 -0800121std::string url::RemoveQueryString(const std::string& url,
122 bool remove_fragment_too) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -0700123 size_t query_pos, query_len;
124 if (!GetQueryStringPos(url, !remove_fragment_too, &query_pos, &query_len))
125 return url;
126 std::string result = url.substr(0, query_pos);
127 size_t fragment_pos = query_pos + query_len;
128 if (fragment_pos < url.size()) {
129 result += url.substr(fragment_pos);
130 }
131 return result;
132}
133
Alex Vakulenko05d29042015-01-13 09:39:25 -0800134std::string url::AppendQueryParam(const std::string& url,
135 const std::string& name,
136 const std::string& value) {
Alex Vakulenkoa629bed2014-08-20 16:16:34 -0700137 return AppendQueryParams(url, {{name, value}});
138}
139
140std::string url::AppendQueryParams(const std::string& url,
141 const data_encoding::WebParamList& params) {
142 if (params.empty())
143 return url;
144 size_t query_pos, query_len;
145 GetQueryStringPos(url, true, &query_pos, &query_len);
146 size_t fragment_pos = query_pos + query_len;
147 std::string result = url.substr(0, fragment_pos);
148 if (query_len == 0) {
149 result += '?';
150 } else if (query_len > 1) {
151 result += '&';
152 }
153 result += data_encoding::WebParamsEncode(params);
154 if (fragment_pos < url.size()) {
155 result += url.substr(fragment_pos);
156 }
157 return result;
158}
159
160bool url::HasQueryString(const std::string& url) {
161 size_t query_pos, query_len;
162 GetQueryStringPos(url, true, &query_pos, &query_len);
163 return (query_len > 0);
164}
165
Alex Vakulenko9ed0cab2015-10-12 15:21:28 -0700166} // namespace brillo