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 | |
31 | namespace 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 |
52 | class URI { |
53 | public: |
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 | |
133 | private: |
134 | friend 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. |
163 | int ParseURL(const char* url, std::string* scheme, std::string* host, int* port); |
164 | |
165 | inline void URI::SetQuery(const std::string& key, const std::string& value) { |
166 | get_query_map()[key] = value; |
167 | _query_was_modified = true; |
168 | } |
169 | |
170 | inline 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 | |
178 | inline 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 | |
186 | inline 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 | |
195 | inline 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" |
201 | class QuerySplitter : public butil::KeyValuePairsSplitter { |
202 | public: |
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. |
219 | class QueryRemover { |
220 | public: |
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 | |
240 | private: |
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" |
256 | void 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 | |
269 | namespace std { |
270 | template<> |
271 | inline void swap(brpc::URI &lhs, brpc::URI &rhs) { |
272 | lhs.Swap(rhs); |
273 | } |
274 | } // namespace std |
275 | |
276 | #endif //BRPC_URI_H |
277 | |