1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <regex>
17 #include <vector>
18 #include "hilog/log.h"
19 #include "string_ex.h"
20 #include "uri.h"
21
22 using std::string;
23 using std::regex;
24 using OHOS::HiviewDFX::HiLog;
25
26 namespace OHOS {
27 namespace {
28 const string NOT_CACHED = "NOT VALID";
29 const string EMPTY = "";
30 const size_t NOT_FOUND = string::npos;
31 const int NOT_CALCULATED = -2;
32 const int PORT_NONE = -1;
33 const char SCHEME_SEPARATOR = ':';
34 const char SCHEME_FRAGMENT = '#';
35 const char LEFT_SEPARATOR = '/';
36 const char RIGHT_SEPARATOR = '\\';
37 const char QUERY_FLAG = '?';
38 const char USER_HOST_SEPARATOR = '@';
39 const char PORT_SEPARATOR = ':';
40 const size_t POS_INC = 1;
41 const size_t POS_INC_MORE = 2;
42 const size_t POS_INC_AGAIN = 3;
43 const regex SCHEME_REGEX("[a-zA-Z][a-zA-Z|\\d|+|-|.]*$");
44 const HiviewDFX::HiLogLabel LABEL = {LOG_CORE, 0xD001800, "URI"};
45 }; // namespace
46
Uri(const string & uriString)47 Uri::Uri(const string& uriString)
48 {
49 cachedSsi_ = NOT_FOUND;
50 cachedFsi_ = NOT_FOUND;
51 port_ = NOT_CALCULATED;
52
53 if (uriString.empty()) {
54 return;
55 }
56
57 uriString_ = uriString;
58 scheme_ = NOT_CACHED;
59 ssp_ = NOT_CACHED;
60 authority_ = NOT_CACHED;
61 host_ = NOT_CACHED;
62 userInfo_ = NOT_CACHED;
63 query_ = NOT_CACHED;
64 path_ = NOT_CACHED;
65 fragment_ = NOT_CACHED;
66
67 if (!CheckScheme()) {
68 uriString_ = EMPTY;
69 HiLog::Error(LABEL, "Scheme wrong!");
70 }
71 }
72
CheckScheme()73 bool Uri::CheckScheme()
74 {
75 scheme_ = ParseScheme();
76 if (scheme_.empty()) {
77 return true;
78 }
79 return regex_match(scheme_, SCHEME_REGEX);
80 }
81
GetScheme()82 string Uri::GetScheme()
83 {
84 if (uriString_.empty()) {
85 return EMPTY;
86 }
87
88 if (scheme_ == NOT_CACHED) {
89 scheme_ = ParseScheme();
90 }
91 return scheme_;
92 }
93
ParseScheme()94 string Uri::ParseScheme()
95 {
96 size_t ssi = FindSchemeSeparator();
97 return (ssi == NOT_FOUND) ? EMPTY : uriString_.substr(0, ssi);
98 }
99
GetSchemeSpecificPart()100 string Uri::GetSchemeSpecificPart()
101 {
102 if (uriString_.empty()) {
103 return EMPTY;
104 }
105
106 return (ssp_ == NOT_CACHED) ? (ssp_ = ParseSsp()) : ssp_;
107 }
108
ParseSsp()109 string Uri::ParseSsp()
110 {
111 size_t ssi = FindSchemeSeparator();
112 size_t fsi = FindFragmentSeparator();
113
114 size_t start = (ssi == NOT_FOUND) ? 0 : (ssi + 1);
115 size_t end = (fsi == NOT_FOUND) ? uriString_.size() : fsi;
116
117 // Return everything between ssi and fsi.
118 string ssp = EMPTY;
119 if (end > start) {
120 ssp = uriString_.substr(start, end - start);
121 }
122
123 return ssp;
124 }
125
GetAuthority()126 string Uri::GetAuthority()
127 {
128 if (uriString_.empty()) {
129 return EMPTY;
130 }
131
132 if (authority_ == NOT_CACHED) {
133 authority_ = ParseAuthority();
134 }
135 return authority_;
136 }
137
ParseAuthority()138 string Uri::ParseAuthority()
139 {
140 size_t ssi = FindSchemeSeparator();
141 if (ssi == NOT_FOUND) {
142 return EMPTY;
143 }
144
145 size_t length = uriString_.length();
146 // If "//" follows the scheme separator, we have an authority.
147 if ((length > (ssi + POS_INC_MORE)) && (uriString_.at(ssi + POS_INC) == LEFT_SEPARATOR) &&
148 (uriString_.at(ssi + POS_INC_MORE) == LEFT_SEPARATOR)) {
149 // Look for the start of the path, query, or fragment, or the end of the string.
150 size_t start = ssi + POS_INC_AGAIN;
151 size_t end = start;
152
153 while (end < length) {
154 char ch = uriString_.at(end);
155 if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR) || (ch == QUERY_FLAG) ||
156 (ch == SCHEME_FRAGMENT)) {
157 break;
158 }
159
160 end++;
161 }
162
163 return uriString_.substr(start, end - start);
164 } else {
165 return EMPTY;
166 }
167 }
168
GetUserInfo()169 string Uri::GetUserInfo()
170 {
171 if (uriString_.empty()) {
172 return EMPTY;
173 }
174
175 if (userInfo_ == NOT_CACHED) {
176 userInfo_ = ParseUserInfo();
177 }
178 return userInfo_;
179 }
180
ParseUserInfo()181 string Uri::ParseUserInfo()
182 {
183 string authority = GetAuthority();
184 if (authority.empty()) {
185 return EMPTY;
186 }
187
188 size_t end = authority.find_last_of(USER_HOST_SEPARATOR);
189 return (end == NOT_FOUND) ? EMPTY : authority.substr(0, end);
190 }
191
GetHost()192 string Uri::GetHost()
193 {
194 if (uriString_.empty()) {
195 return EMPTY;
196 }
197
198 if (host_ == NOT_CACHED) {
199 host_ = ParseHost();
200 }
201 return host_;
202 }
203
ParseHost()204 string Uri::ParseHost()
205 {
206 string authority = GetAuthority();
207 if (authority.empty()) {
208 return EMPTY;
209 }
210
211 // Parse out user info and then port.
212 size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
213 size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
214 size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
215 size_t end = (portSeparator == NOT_FOUND) ? authority.size() : portSeparator;
216
217 string host = EMPTY;
218 if (start < end) {
219 host = authority.substr(start, end - start);
220 }
221
222 return host;
223 }
224
GetPort()225 int Uri::GetPort()
226 {
227 if (uriString_.empty()) {
228 return PORT_NONE;
229 }
230
231 if (port_ == NOT_CALCULATED) {
232 port_ = ParsePort();
233 }
234 return port_;
235 }
236
ParsePort()237 int Uri::ParsePort()
238 {
239 string authority = GetAuthority();
240 if (authority.empty()) {
241 return PORT_NONE;
242 }
243
244 // Make sure we look for the port separtor *after* the user info separator.
245 size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
246 size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
247 size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
248 if (portSeparator == NOT_FOUND) {
249 return PORT_NONE;
250 }
251
252 start = portSeparator + 1;
253 string portString = authority.substr(start);
254
255 int value = PORT_NONE;
256 return StrToInt(portString, value) ? value : PORT_NONE;
257 }
258
GetQuery()259 string Uri::GetQuery()
260 {
261 if (uriString_.empty()) {
262 return EMPTY;
263 }
264
265 if (query_ == NOT_CACHED) {
266 query_ = ParseQuery();
267 }
268 return query_;
269 }
270
ParseQuery()271 string Uri::ParseQuery()
272 {
273 size_t ssi = FindSchemeSeparator();
274 if (ssi == NOT_FOUND) {
275 ssi = 0;
276 }
277 size_t qsi = uriString_.find_first_of(QUERY_FLAG, ssi);
278 if (qsi == NOT_FOUND) {
279 return EMPTY;
280 }
281
282 size_t start = qsi + 1;
283 size_t fsi = FindFragmentSeparator();
284 if (fsi == NOT_FOUND) {
285 return uriString_.substr(start);
286 }
287
288 if (fsi < qsi) {
289 // Invalid.
290 return EMPTY;
291 }
292
293 return uriString_.substr(start, fsi - start);
294 }
295
GetPath()296 string Uri::GetPath()
297 {
298 if (uriString_.empty()) {
299 return EMPTY;
300 }
301
302 if (path_ == NOT_CACHED) {
303 path_ = ParsePath();
304 }
305 return path_;
306 }
307
GetPathSegments(std::vector<std::string> & segments)308 void Uri::GetPathSegments(std::vector<std::string>& segments)
309 {
310 if (uriString_.empty()) {
311 return;
312 }
313 if (path_ == NOT_CACHED) {
314 path_ = ParsePath();
315 }
316
317 size_t previous = 0;
318 size_t current;
319 while ((current = path_.find(LEFT_SEPARATOR, previous)) != std::string::npos) {
320 if (previous < current) {
321 segments.emplace_back(path_.substr(previous, current - previous));
322 }
323 previous = current + POS_INC;
324 }
325 // Add in the final path segment.
326 if (previous < path_.length()) {
327 segments.emplace_back(path_.substr(previous));
328 }
329 }
330
ParsePath()331 string Uri::ParsePath()
332 {
333 size_t ssi = FindSchemeSeparator();
334 // If the URI is absolute.
335 if (ssi != NOT_FOUND) {
336 // Is there anything after the ':'?
337 if ((ssi + 1) == uriString_.length()) {
338 // Opaque URI.
339 return EMPTY;
340 }
341
342 // A '/' after the ':' means this is hierarchical.
343 if (uriString_.at(ssi + 1) != LEFT_SEPARATOR) {
344 // Opaque URI.
345 return EMPTY;
346 }
347 } else {
348 // All relative URIs are hierarchical.
349 }
350
351 return ParsePath(ssi);
352 }
353
ParsePath(size_t ssi)354 string Uri::ParsePath(size_t ssi)
355 {
356 size_t length = uriString_.length();
357
358 // Find start of path.
359 size_t pathStart = (ssi == NOT_FOUND) ? 0 : (ssi + POS_INC);
360 if ((length > (pathStart + POS_INC)) && (uriString_.at(pathStart) == LEFT_SEPARATOR) &&
361 (uriString_.at(pathStart + POS_INC) == LEFT_SEPARATOR)) {
362 // Skip over authority to path.
363 pathStart += POS_INC_MORE;
364
365 while (pathStart < length) {
366 char ch = uriString_.at(pathStart);
367 if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
368 return EMPTY;
369 }
370
371 if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR)) {
372 break;
373 }
374
375 pathStart++;
376 }
377 }
378
379 // Find end of path.
380 size_t pathEnd = pathStart;
381 while (pathEnd < length) {
382 char ch = uriString_.at(pathEnd);
383 if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
384 break;
385 }
386
387 pathEnd++;
388 }
389
390 return uriString_.substr(pathStart, pathEnd - pathStart);
391 }
392
GetFragment()393 string Uri::GetFragment()
394 {
395 if (uriString_.empty()) {
396 return EMPTY;
397 }
398
399 if (fragment_ == NOT_CACHED) {
400 fragment_ = ParseFragment();
401 }
402 return fragment_;
403 }
404
ParseFragment()405 string Uri::ParseFragment()
406 {
407 size_t fsi = FindFragmentSeparator();
408 return (fsi == NOT_FOUND) ? EMPTY : uriString_.substr(fsi + 1);
409 }
410
FindSchemeSeparator()411 size_t Uri::FindSchemeSeparator()
412 {
413 if (cachedSsi_ == NOT_FOUND) {
414 cachedSsi_ = uriString_.find_first_of(SCHEME_SEPARATOR);
415 }
416 return cachedSsi_;
417 }
418
FindFragmentSeparator()419 size_t Uri::FindFragmentSeparator()
420 {
421 if (cachedFsi_ == NOT_FOUND) {
422 cachedFsi_ = uriString_.find_first_of(SCHEME_FRAGMENT, FindSchemeSeparator());
423 }
424 return cachedFsi_;
425 }
426
IsHierarchical()427 bool Uri::IsHierarchical()
428 {
429 if (uriString_.empty()) {
430 return false;
431 }
432
433 size_t ssi = FindSchemeSeparator();
434 if (ssi == NOT_FOUND) {
435 // All relative URIs are hierarchical.
436 return true;
437 }
438
439 if (uriString_.length() == (ssi + 1)) {
440 // No ssp.
441 return false;
442 }
443
444 // If the ssp starts with a '/', this is hierarchical.
445 return (uriString_.at(ssi + 1) == LEFT_SEPARATOR);
446 }
447
IsOpaque()448 bool Uri::IsOpaque()
449 {
450 if (uriString_.empty()) {
451 return false;
452 }
453
454 return !IsHierarchical();
455 }
456
IsAbsolute()457 bool Uri::IsAbsolute()
458 {
459 if (uriString_.empty()) {
460 return false;
461 }
462
463 return !IsRelative();
464 }
465
IsRelative()466 bool Uri::IsRelative()
467 {
468 if (uriString_.empty()) {
469 return false;
470 }
471
472 // Note: We return true if the index is 0
473 return FindSchemeSeparator() == NOT_FOUND;
474 }
475
Equals(const Uri & other) const476 bool Uri::Equals(const Uri& other) const
477 {
478 return ToString() == other.ToString();
479 }
480
CompareTo(const Uri & other) const481 int Uri::CompareTo(const Uri& other) const
482 {
483 return ToString().compare(other.ToString());
484 }
485
ToString() const486 string Uri::ToString() const
487 {
488 return uriString_;
489 }
490
operator ==(const Uri & other) const491 bool Uri::operator==(const Uri& other) const
492 {
493 return ToString() == other.ToString();
494 }
495
Marshalling(Parcel & parcel) const496 bool Uri::Marshalling(Parcel& parcel) const
497 {
498 if (IsAsciiString(uriString_)) {
499 return parcel.WriteString16(Str8ToStr16(uriString_));
500 }
501
502 return false;
503 }
504
Unmarshalling(Parcel & parcel)505 Uri* Uri::Unmarshalling(Parcel& parcel)
506 {
507 return new Uri(Str16ToStr8(parcel.ReadString16()));
508 }
509 } // namespace OHOS
510