1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18
19#ifndef BRPC_URI_H
20#define BRPC_URI_H
21
22#include <string> // std::string
23#include "butil/containers/flat_map.h"
24#include "butil/status.h"
25#include "butil/string_splitter.h"
26
27// To brpc developers: This is a class exposed to end-user. DON'T put impl.
28// details in this header, use opaque pointers instead.
29
30
31namespace brpc {
32
33// The class for URI scheme : http://en.wikipedia.org/wiki/URI_scheme
34//
35// foo://username:[email protected]:8042/over/there/index.dtb?type=animal&name=narwhal#nose
36// \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
37// | | | | | | | |
38// | userinfo host port | | query fragment
39// | \________________________________/\_____________|____|/ \__/ \__/
40// scheme | | | | | |
41// authority | | | | |
42// path | | interpretable as keys
43// | |
44// \_______________________________________________|____|/ \____/ \_____/
45// | | | | |
46// hierarchical part | | interpretable as values
47// | |
48// interpretable as filename |
49// |
50// |
51// interpretable as extension
52class URI {
53public:
54 static const size_t QUERY_MAP_INITIAL_BUCKET = 16;
55 typedef butil::FlatMap<std::string, std::string> QueryMap;
56 typedef QueryMap::const_iterator QueryIterator;
57
58 // You can copy a URI.
59 URI();
60 ~URI();
61
62 // Exchange internal fields with another URI.
63 void Swap(URI &rhs);
64
65 // Reset internal fields as if they're just default-constructed.
66 void Clear();
67
68 // Decompose `url' and set into corresponding fields.
69 // heading and trailing spaces are allowed and skipped.
70 // Returns 0 on success, -1 otherwise and status() is set.
71 int SetHttpURL(const char* url);
72 int SetHttpURL(const std::string& url) { return SetHttpURL(url.c_str()); }
73 // syntactic sugar of SetHttpURL
74 void operator=(const char* url) { SetHttpURL(url); }
75 void operator=(const std::string& url) { SetHttpURL(url); }
76
77 // Status of previous SetHttpURL or opreator=.
78 const butil::Status& status() const { return _st; }
79
80 // Sub fields. Empty string if the field is not set.
81 const std::string& scheme() const { return _scheme; }
82 BAIDU_DEPRECATED const std::string& schema() const { return scheme(); }
83 const std::string& host() const { return _host; }
84 int port() const { return _port; } // -1 on unset.
85 const std::string& path() const { return _path; }
86 const std::string& user_info() const { return _user_info; }
87 const std::string& fragment() const { return _fragment; }
88 // NOTE: This method is not thread-safe because it may re-generate the
89 // query-string if SetQuery()/RemoveQuery() were successfully called.
90 const std::string& query() const;
91 // Put path?query#fragment into `h2_path'
92 void GenerateH2Path(std::string* h2_path) const;
93
94 // Overwrite parts of the URL.
95 // NOTE: The input MUST be guaranteed to be valid.
96 void set_scheme(const std::string& scheme) { _scheme = scheme; }
97 BAIDU_DEPRECATED void set_schema(const std::string& s) { set_scheme(s); }
98 void set_path(const std::string& path) { _path = path; }
99 void set_host(const std::string& host) { _host = host; }
100 void set_port(int port) { _port = port; }
101 void SetHostAndPort(const std::string& host_and_optional_port);
102 // Set path/query/fragment with the input in form of "path?query#fragment"
103 void SetH2Path(const char* h2_path);
104 void SetH2Path(const std::string& path) { SetH2Path(path.c_str()); }
105
106 // Get the value of a CASE-SENSITIVE key.
107 // Returns pointer to the value, NULL when the key does not exist.
108 const std::string* GetQuery(const char* key) const
109 { return get_query_map().seek(key); }
110 const std::string* GetQuery(const std::string& key) const
111 { return get_query_map().seek(key); }
112
113 // Add key/value pair. Override existing value.
114 void SetQuery(const std::string& key, const std::string& value);
115
116 // Remove value associated with `key'.
117 // Returns 1 on removed, 0 otherwise.
118 size_t RemoveQuery(const char* key);
119 size_t RemoveQuery(const std::string& key);
120
121 // Get query iterators which are invalidated after calling SetQuery()
122 // or SetHttpURL().
123 QueryIterator QueryBegin() const { return get_query_map().begin(); }
124 QueryIterator QueryEnd() const { return get_query_map().end(); }
125 // #queries
126 size_t QueryCount() const { return get_query_map().size(); }
127
128 // Print this URI to the ostream.
129 // PrintWithoutHost only prints components including and after path.
130 void PrintWithoutHost(std::ostream& os) const;
131 void Print(std::ostream& os) const;
132
133private:
134friend class HttpMessage;
135
136 void InitializeQueryMap() const;
137
138 QueryMap& get_query_map() const {
139 if (!_initialized_query_map) {
140 InitializeQueryMap();
141 }
142 return _query_map;
143 }
144
145 // Iterate _query_map and append all queries to `query'
146 void AppendQueryString(std::string* query, bool append_question_mark) const;
147
148 butil::Status _st;
149 int _port;
150 mutable bool _query_was_modified;
151 mutable bool _initialized_query_map;
152 std::string _host;
153 std::string _path;
154 std::string _user_info;
155 std::string _fragment;
156 std::string _scheme;
157 mutable std::string _query;
158 mutable QueryMap _query_map;
159};
160
161// Parse host/port/scheme from `url' if the corresponding parameter is not NULL.
162// Returns 0 on success, -1 otherwise.
163int ParseURL(const char* url, std::string* scheme, std::string* host, int* port);
164
165inline void URI::SetQuery(const std::string& key, const std::string& value) {
166 get_query_map()[key] = value;
167 _query_was_modified = true;
168}
169
170inline size_t URI::RemoveQuery(const char* key) {
171 if (get_query_map().erase(key)) {
172 _query_was_modified = true;
173 return 1;
174 }
175 return 0;
176}
177
178inline size_t URI::RemoveQuery(const std::string& key) {
179 if (get_query_map().erase(key)) {
180 _query_was_modified = true;
181 return 1;
182 }
183 return 0;
184}
185
186inline const std::string& URI::query() const {
187 if (_initialized_query_map && _query_was_modified) {
188 _query_was_modified = false;
189 _query.clear();
190 AppendQueryString(&_query, false);
191 }
192 return _query;
193}
194
195inline std::ostream& operator<<(std::ostream& os, const URI& uri) {
196 uri.Print(os);
197 return os;
198}
199
200// Split query in the format of "key1=value1&key2&key3=value3"
201class QuerySplitter : public butil::KeyValuePairsSplitter {
202public:
203 inline QuerySplitter(const char* str_begin, const char* str_end)
204 : KeyValuePairsSplitter(str_begin, str_end, '&', '=')
205 {}
206
207 inline QuerySplitter(const char* str_begin)
208 : KeyValuePairsSplitter(str_begin, '&', '=')
209 {}
210
211 inline QuerySplitter(const butil::StringPiece &sp)
212 : KeyValuePairsSplitter(sp, '&', '=')
213 {}
214};
215
216// A class to remove some specific keys in a query string,
217// when removal is over, call modified_query() to get modified
218// query.
219class QueryRemover {
220public:
221 QueryRemover(const std::string* str);
222
223 butil::StringPiece key() { return _qs.key();}
224 butil::StringPiece value() { return _qs.value(); }
225 butil::StringPiece key_and_value() { return _qs.key_and_value(); }
226
227 // Move splitter forward.
228 QueryRemover& operator++();
229 QueryRemover operator++(int);
230
231 operator const void*() const { return _qs; }
232
233 // After this function is called, current query will be removed from
234 // modified_query(), calling this function more than once has no effect.
235 void remove_current_key_and_value();
236
237 // Return the modified query string
238 std::string modified_query();
239
240private:
241 const std::string* _query;
242 QuerySplitter _qs;
243 std::string _modified_query;
244 size_t _iterated_len;
245 bool _removed_current_key_value;
246 bool _ever_removed;
247};
248
249// This function can append key and value to *query_string
250// in consideration of all possible format of *query_string
251// for example:
252// "" -> "key=value"
253// "key1=value1" -> "key1=value1&key=value"
254// "/some/path?" -> "/some/path?key=value"
255// "/some/path?key1=value1" -> "/some/path?key1=value1&key=value"
256void append_query(std::string *query_string,
257 const butil::StringPiece& key,
258 const butil::StringPiece& value);
259
260} // namespace brpc
261
262
263#if __cplusplus < 201103L // < C++11
264#include <algorithm> // std::swap until C++11
265#else
266#include <utility> // std::swap since C++11
267#endif // __cplusplus < 201103L
268
269namespace std {
270template<>
271inline void swap(brpc::URI &lhs, brpc::URI &rhs) {
272 lhs.Swap(rhs);
273}
274} // namespace std
275
276#endif //BRPC_URI_H
277