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
~Uri()73 Uri::~Uri()
74 {
75 HiLog::Debug(LABEL, "Uri has been destroyed");
76 }
77
CheckScheme()78 bool Uri::CheckScheme()
79 {
80 scheme_ = ParseScheme();
81 if (scheme_.empty()) {
82 return true;
83 }
84 return regex_match(scheme_, SCHEME_REGEX);
85 }
86
GetScheme()87 string Uri::GetScheme()
88 {
89 if (uriString_.empty()) {
90 return EMPTY;
91 }
92
93 if (scheme_ == NOT_CACHED) {
94 scheme_ = ParseScheme();
95 }
96 return scheme_;
97 }
98
ParseScheme()99 string Uri::ParseScheme()
100 {
101 size_t ssi = FindSchemeSeparator();
102 return (ssi == NOT_FOUND) ? EMPTY : uriString_.substr(0, ssi);
103 }
104
GetSchemeSpecificPart()105 string Uri::GetSchemeSpecificPart()
106 {
107 if (uriString_.empty()) {
108 return EMPTY;
109 }
110
111 return (ssp_ == NOT_CACHED) ? (ssp_ = ParseSsp()) : ssp_;
112 }
113
ParseSsp()114 string Uri::ParseSsp()
115 {
116 size_t ssi = FindSchemeSeparator();
117 size_t fsi = FindFragmentSeparator();
118
119 size_t start = (ssi == NOT_FOUND) ? 0 : (ssi + 1);
120 size_t end = (fsi == NOT_FOUND) ? uriString_.size() : fsi;
121
122 // Return everything between ssi and fsi.
123 string ssp = EMPTY;
124 if (end > start) {
125 ssp = uriString_.substr(start, end - start);
126 }
127
128 return ssp;
129 }
130
GetAuthority()131 string Uri::GetAuthority()
132 {
133 if (uriString_.empty()) {
134 return EMPTY;
135 }
136
137 if (authority_ == NOT_CACHED) {
138 authority_ = ParseAuthority();
139 }
140 return authority_;
141 }
142
ParseAuthority()143 string Uri::ParseAuthority()
144 {
145 size_t ssi = FindSchemeSeparator();
146 if (ssi == NOT_FOUND) {
147 return EMPTY;
148 }
149
150 size_t length = uriString_.length();
151 // If "//" follows the scheme separator, we have an authority.
152 if ((length > (ssi + POS_INC_MORE)) && (uriString_.at(ssi + POS_INC) == LEFT_SEPARATOR) &&
153 (uriString_.at(ssi + POS_INC_MORE) == LEFT_SEPARATOR)) {
154 // Look for the start of the path, query, or fragment, or the end of the string.
155 size_t start = ssi + POS_INC_AGAIN;
156 size_t end = start;
157
158 while (end < length) {
159 char ch = uriString_.at(end);
160 if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR) || (ch == QUERY_FLAG) ||
161 (ch == SCHEME_FRAGMENT)) {
162 break;
163 }
164
165 end++;
166 }
167
168 return uriString_.substr(start, end - start);
169 } else {
170 return EMPTY;
171 }
172 }
173
GetUserInfo()174 string Uri::GetUserInfo()
175 {
176 if (uriString_.empty()) {
177 return EMPTY;
178 }
179
180 if (userInfo_ == NOT_CACHED) {
181 userInfo_ = ParseUserInfo();
182 }
183 return userInfo_;
184 }
185
ParseUserInfo()186 string Uri::ParseUserInfo()
187 {
188 string authority = GetAuthority();
189 if (authority.empty()) {
190 return EMPTY;
191 }
192
193 size_t end = authority.find_last_of(USER_HOST_SEPARATOR);
194 return (end == NOT_FOUND) ? EMPTY : authority.substr(0, end);
195 }
196
GetHost()197 string Uri::GetHost()
198 {
199 if (uriString_.empty()) {
200 return EMPTY;
201 }
202
203 if (host_ == NOT_CACHED) {
204 host_ = ParseHost();
205 }
206 return host_;
207 }
208
ParseHost()209 string Uri::ParseHost()
210 {
211 string authority = GetAuthority();
212 if (authority.empty()) {
213 return EMPTY;
214 }
215
216 // Parse out user info and then port.
217 size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
218 size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
219 size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
220 size_t end = (portSeparator == NOT_FOUND) ? authority.size() : portSeparator;
221
222 string host = EMPTY;
223 if (start < end) {
224 host = authority.substr(start, end - start);
225 }
226
227 return host;
228 }
229
GetPort()230 int Uri::GetPort()
231 {
232 if (uriString_.empty()) {
233 return PORT_NONE;
234 }
235
236 if (port_ == NOT_CALCULATED) {
237 port_ = ParsePort();
238 }
239 return port_;
240 }
241
ParsePort()242 int Uri::ParsePort()
243 {
244 string authority = GetAuthority();
245 if (authority.empty()) {
246 return PORT_NONE;
247 }
248
249 // Make sure we look for the port separtor *after* the user info separator.
250 size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
251 size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
252 size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
253 if (portSeparator == NOT_FOUND) {
254 return PORT_NONE;
255 }
256
257 start = portSeparator + 1;
258 string portString = authority.substr(start);
259
260 int value = PORT_NONE;
261 return StrToInt(portString, value) ? value : PORT_NONE;
262 }
263
GetQuery()264 string Uri::GetQuery()
265 {
266 if (uriString_.empty()) {
267 return EMPTY;
268 }
269
270 if (query_ == NOT_CACHED) {
271 query_ = ParseQuery();
272 }
273 return query_;
274 }
275
ParseQuery()276 string Uri::ParseQuery()
277 {
278 size_t ssi = FindSchemeSeparator();
279 if (ssi == NOT_FOUND) {
280 ssi = 0;
281 }
282 size_t qsi = uriString_.find_first_of(QUERY_FLAG, ssi);
283 if (qsi == NOT_FOUND) {
284 return EMPTY;
285 }
286
287 size_t start = qsi + 1;
288 size_t fsi = FindFragmentSeparator();
289 if (fsi == NOT_FOUND) {
290 return uriString_.substr(start);
291 }
292
293 if (fsi < qsi) {
294 // Invalid.
295 return EMPTY;
296 }
297
298 return uriString_.substr(start, fsi - start);
299 }
300
GetPath()301 string Uri::GetPath()
302 {
303 if (uriString_.empty()) {
304 return EMPTY;
305 }
306
307 if (path_ == NOT_CACHED) {
308 path_ = ParsePath();
309 }
310 return path_;
311 }
312
GetPathSegments(std::vector<std::string> & segments)313 void Uri::GetPathSegments(std::vector<std::string>& segments)
314 {
315 if (uriString_.empty()) {
316 return;
317 }
318 if (path_ == NOT_CACHED) {
319 path_ = ParsePath();
320 }
321
322 size_t previous = 0;
323 size_t current;
324 while ((current = path_.find(LEFT_SEPARATOR, previous)) != std::string::npos) {
325 if (previous < current) {
326 segments.emplace_back(path_.substr(previous, current - previous));
327 }
328 previous = current + POS_INC;
329 }
330 // Add in the final path segment.
331 if (previous < path_.length()) {
332 segments.emplace_back(path_.substr(previous));
333 }
334 }
335
ParsePath()336 string Uri::ParsePath()
337 {
338 size_t ssi = FindSchemeSeparator();
339 // If the URI is absolute.
340 if (ssi != NOT_FOUND) {
341 // Is there anything after the ':'?
342 if ((ssi + 1) == uriString_.length()) {
343 // Opaque URI.
344 return EMPTY;
345 }
346
347 // A '/' after the ':' means this is hierarchical.
348 if (uriString_.at(ssi + 1) != LEFT_SEPARATOR) {
349 // Opaque URI.
350 return EMPTY;
351 }
352 } else {
353 // All relative URIs are hierarchical.
354 }
355
356 return ParsePath(ssi);
357 }
358
ParsePath(size_t ssi)359 string Uri::ParsePath(size_t ssi)
360 {
361 size_t length = uriString_.length();
362
363 // Find start of path.
364 size_t pathStart = (ssi == NOT_FOUND) ? 0 : (ssi + POS_INC);
365 if ((length > (pathStart + POS_INC)) && (uriString_.at(pathStart) == LEFT_SEPARATOR) &&
366 (uriString_.at(pathStart + POS_INC) == LEFT_SEPARATOR)) {
367 // Skip over authority to path.
368 pathStart += POS_INC_MORE;
369
370 while (pathStart < length) {
371 char ch = uriString_.at(pathStart);
372 if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
373 return EMPTY;
374 }
375
376 if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR)) {
377 break;
378 }
379
380 pathStart++;
381 }
382 }
383
384 // Find end of path.
385 size_t pathEnd = pathStart;
386 while (pathEnd < length) {
387 char ch = uriString_.at(pathEnd);
388 if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
389 break;
390 }
391
392 pathEnd++;
393 }
394
395 return uriString_.substr(pathStart, pathEnd - pathStart);
396 }
397
GetFragment()398 string Uri::GetFragment()
399 {
400 if (uriString_.empty()) {
401 return EMPTY;
402 }
403
404 if (fragment_ == NOT_CACHED) {
405 fragment_ = ParseFragment();
406 }
407 return fragment_;
408 }
409
ParseFragment()410 string Uri::ParseFragment()
411 {
412 size_t fsi = FindFragmentSeparator();
413 return (fsi == NOT_FOUND) ? EMPTY : uriString_.substr(fsi + 1);
414 }
415
FindSchemeSeparator()416 size_t Uri::FindSchemeSeparator()
417 {
418 if (cachedSsi_ == NOT_FOUND) {
419 cachedSsi_ = uriString_.find_first_of(SCHEME_SEPARATOR);
420 }
421 return cachedSsi_;
422 }
423
FindFragmentSeparator()424 size_t Uri::FindFragmentSeparator()
425 {
426 if (cachedFsi_ == NOT_FOUND) {
427 cachedFsi_ = uriString_.find_first_of(SCHEME_FRAGMENT, FindSchemeSeparator());
428 }
429 return cachedFsi_;
430 }
431
IsHierarchical()432 bool Uri::IsHierarchical()
433 {
434 if (uriString_.empty()) {
435 return false;
436 }
437
438 size_t ssi = FindSchemeSeparator();
439 if (ssi == NOT_FOUND) {
440 // All relative URIs are hierarchical.
441 return true;
442 }
443
444 if (uriString_.length() == (ssi + 1)) {
445 // No ssp.
446 return false;
447 }
448
449 // If the ssp starts with a '/', this is hierarchical.
450 return (uriString_.at(ssi + 1) == LEFT_SEPARATOR);
451 }
452
IsOpaque()453 bool Uri::IsOpaque()
454 {
455 if (uriString_.empty()) {
456 return false;
457 }
458
459 return !IsHierarchical();
460 }
461
IsAbsolute()462 bool Uri::IsAbsolute()
463 {
464 if (uriString_.empty()) {
465 return false;
466 }
467
468 return !IsRelative();
469 }
470
IsRelative()471 bool Uri::IsRelative()
472 {
473 if (uriString_.empty()) {
474 return false;
475 }
476
477 // Note: We return true if the index is 0
478 return FindSchemeSeparator() == NOT_FOUND;
479 }
480
Equals(const Uri & other) const481 bool Uri::Equals(const Uri& other) const
482 {
483 return ToString() == other.ToString();
484 }
485
CompareTo(const Uri & other) const486 int Uri::CompareTo(const Uri& other) const
487 {
488 return ToString().compare(other.ToString());
489 }
490
ToString() const491 string Uri::ToString() const
492 {
493 return uriString_;
494 }
495
operator ==(const Uri & other) const496 bool Uri::operator==(const Uri& other) const
497 {
498 return ToString() == other.ToString();
499 }
500
Marshalling(Parcel & parcel) const501 bool Uri::Marshalling(Parcel& parcel) const
502 {
503 if (IsAsciiString(uriString_)) {
504 return parcel.WriteString16(Str8ToStr16(uriString_));
505 }
506
507 return false;
508 }
509
Unmarshalling(Parcel & parcel)510 Uri* Uri::Unmarshalling(Parcel& parcel)
511 {
512 return new Uri(Str16ToStr8(parcel.ReadString16()));
513 }
514 } // namespace OHOS
515