blob: 3fcdb4858c7886c74fd4c513d96eb46540022787 [file] [log] [blame]
Alex Vakulenkobda220a2014-04-18 15:25:44 -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
5#include "buffet/url_utils.h"
6
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
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070020// #fragment - URL fragment string
Alex Vakulenkobda220a2014-04-18 15:25:44 -070021// 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.
24bool GetQueryStringPos(const std::string& url, bool exclude_fragment,
25 size_t* query_pos, size_t* query_len) {
26 size_t query_start = url.find_first_of("?#");
27 if (query_start == std::string::npos) {
28 *query_pos = url.size();
29 if (query_len)
30 *query_len = 0;
31 return false;
32 }
33
34 *query_pos = query_start;
35 if (query_len) {
36 size_t query_end = url.size();
37
38 if (exclude_fragment) {
39 if (url[query_start] == '?') {
40 size_t pos_fragment = url.find('#', query_start);
41 if (pos_fragment != std::string::npos)
42 query_end = pos_fragment;
43 } else {
44 query_end = query_start;
45 }
46 }
47 *query_len = query_end - query_start;
48 }
49 return true;
50}
51} // anonymous namespace
52
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070053namespace buffet {
54
55std::string url::TrimOffQueryString(std::string* url) {
Alex Vakulenkobda220a2014-04-18 15:25:44 -070056 size_t query_pos;
57 if (!GetQueryStringPos(*url, false, &query_pos, nullptr))
58 return std::string();
59 std::string query_string = url->substr(query_pos);
60 url->resize(query_pos);
61 return query_string;
62}
63
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070064std::string url::Combine(
Alex Vakulenkobda220a2014-04-18 15:25:44 -070065 const std::string& url, const std::string& subpath) {
66 return CombineMultiple(url, {subpath});
67}
68
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070069std::string url::CombineMultiple(
Alex Vakulenkobda220a2014-04-18 15:25:44 -070070 const std::string& url, const std::vector<std::string>& parts) {
71 std::string result = url;
72 if (!parts.empty()) {
73 std::string query_string = TrimOffQueryString(&result);
Alex Vakulenkoa0424dd2014-06-13 16:10:17 -070074 for (const auto& part : parts) {
Alex Vakulenkobda220a2014-04-18 15:25:44 -070075 if (!part.empty()) {
76 if (!result.empty() && result.back() != '/')
77 result += '/';
78 size_t non_slash_pos = part.find_first_not_of('/');
79 if (non_slash_pos != std::string::npos)
80 result += part.substr(non_slash_pos);
81 }
82 }
83 result += query_string;
84 }
85 return result;
86}
87
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070088std::string url::GetQueryString(
Alex Vakulenkobda220a2014-04-18 15:25:44 -070089 const std::string& url, bool remove_fragment) {
90 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
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070098data_encoding::WebParamList url::GetQueryStringParameters(
Alex Vakulenkobda220a2014-04-18 15:25:44 -070099 const std::string& url) {
100 // Extract the query string and remove the leading '?'.
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700101 std::string query_string = GetQueryString(url, true);
102 if (!query_string.empty() && query_string.front() == '?')
103 query_string.erase(query_string.begin());
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700104 return data_encoding::WebParamsDecode(query_string);
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700105}
106
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700107std::string url::GetQueryStringValue(
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700108 const std::string& url, const std::string& name) {
109 return GetQueryStringValue(GetQueryStringParameters(url), name);
110}
111
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700112std::string url::GetQueryStringValue(
113 const data_encoding::WebParamList& params,
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700114 const std::string& name) {
Alex Vakulenkoa0424dd2014-06-13 16:10:17 -0700115 for (const auto& pair : params) {
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700116 if (name.compare(pair.first) == 0)
117 return pair.second;
118 }
119 return std::string();
120}
121
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700122std::string url::RemoveQueryString(
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700123 const std::string& url, bool remove_fragment_too) {
124 size_t query_pos, query_len;
125 if (!GetQueryStringPos(url, !remove_fragment_too, &query_pos, &query_len))
126 return url;
127 std::string result = url.substr(0, query_pos);
128 size_t fragment_pos = query_pos + query_len;
129 if (fragment_pos < url.size()) {
130 result += url.substr(fragment_pos);
131 }
132 return result;
133}
134
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700135std::string url::AppendQueryParam(
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700136 const std::string& url, const std::string& name, const std::string& value) {
137 return AppendQueryParams(url, {{name, value}});
138}
139
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700140std::string url::AppendQueryParams(
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700141 const std::string& url,
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700142 const data_encoding::WebParamList& params) {
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700143 if (params.empty())
144 return url;
145 size_t query_pos, query_len;
146 GetQueryStringPos(url, true, &query_pos, &query_len);
147 size_t fragment_pos = query_pos + query_len;
148 std::string result = url.substr(0, fragment_pos);
149 if (query_len == 0) {
150 result += '?';
151 } else if (query_len > 1) {
152 result += '&';
153 }
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700154 result += data_encoding::WebParamsEncode(params);
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700155 if (fragment_pos < url.size()) {
156 result += url.substr(fragment_pos);
157 }
158 return result;
159}
160
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700161bool url::HasQueryString(const std::string& url) {
Alex Vakulenkobda220a2014-04-18 15:25:44 -0700162 size_t query_pos, query_len;
163 GetQueryStringPos(url, true, &query_pos, &query_len);
164 return (query_len > 0);
165}
166
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700167} // namespace buffet