1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 
  9 namespace System.Net.Http
 10 {
 11     public class UriException : Exception
 12     {
 13         public nothrow UriException(const string& message_) : base(message_)
 14         {
 15         }
 16     }
 17     
 18     public class Authority
 19     {
 20         public enum Flags : sbyte
 21         {
 22             none = 0hasUserInfo = 1 << 0
 23         }
 24         public nothrow Authority() : flags(Flags.none)port(-1)
 25         {
 26         }
 27         public inline nothrow Flags operator|(Flags leftFlags right) const
 28         {
 29             return cast<Flags>(cast<sbyte>(left) | cast<sbyte>(right));
 30         }
 31         public inline nothrow Flags operator&(Flags leftFlags right) const
 32         {
 33             return cast<Flags>(cast<sbyte>(left) & cast<sbyte>(right));
 34         }
 35         public inline nothrow Flags operator~(Flags f) const
 36         {
 37             return cast<Flags>(~cast<sbyte>(f));
 38         }
 39         public inline nothrow bool GetFlag(Flags flag) const
 40         {
 41             return (flags & flag) != Flags.none;
 42         }
 43         public inline nothrow void SetFlag(Flags flag)
 44         {
 45             flags = cast<Flags>(flags | flag);
 46         }
 47         public inline nothrow void ResetFlag(Flags flag)
 48         {
 49             flags = cast<Flags>(flags & ~flag);
 50         }
 51         public inline nothrow const string& UserInfo() const
 52         {
 53             return userInfo;
 54         }
 55         public nothrow void SetUserInfo(const string& userInfo_)
 56         {
 57             userInfo = userInfo_;
 58             SetFlag(Flags.hasUserInfo);
 59         }
 60         public inline nothrow const string& Host() const
 61         {
 62             return host;
 63         }
 64         public nothrow void SetHost(const string& host_)
 65         {
 66             host = host_;
 67         }
 68         public inline nothrow int Port() const
 69         {
 70             return port;
 71         }
 72         public nothrow void SetPort(int port_)
 73         {
 74             port = port_;
 75         }
 76         public nothrow string ToString() const
 77         {
 78             string result;
 79             if (GetFlag(Flags.hasUserInfo))
 80             {
 81                 result.Append(userInfo).Append('@');
 82             }
 83             result.Append(host);
 84             if (port != -1)
 85             {
 86                 result.Append(':').Append(ToString(port));
 87             }
 88             return result;
 89         }
 90         private Flags flags;
 91         private string userInfo;
 92         private string host;
 93         private int port;
 94     }
 95     
 96     public class UriReference
 97     {
 98         public enum Flags : sbyte
 99         {
100             none = 0asterisk = 1 << 0hasAuthority = 1 << 1hasQuery = 1 << 2hasFragment = 1 << 3
101         }
102         public nothrow UriReference()
103         {
104         }
105         public UriReference(const string& uri)
106         {
107             UriLexer lexer(ToUtf32(uri)""0);
108             UriReference that = Uri.Parse(lexer);
109             Swap(flagsthat.flags);
110             Swap(schemethat.scheme);
111             Swap(authoritythat.authority);
112             Swap(paththat.path);
113             Swap(querythat.query);
114             Swap(fragmentthat.fragment);
115         }
116         public inline nothrow bool IsAbsolute() const
117         {
118             return !scheme.IsEmpty();
119         }
120         public inline nothrow bool IsRelative() const
121         {
122             return !IsAbsolute();
123         }
124         public static UriReference Combine(const UriReference& baseUriconst UriReference& relativeUri)
125         {
126             if (!baseUri.IsAbsolute())
127             {
128                 throw UriException("base URI not absolute");
129             }
130             if (relativeUri.IsAbsolute())
131             {
132                 return relativeUri;
133             }
134             string basePath = "/";
135             if (!baseUri.Path().IsEmpty())
136             {
137                 if (baseUri.Path().StartsWith("/"))
138                 {
139                     basePath = baseUri.Path();
140                 }
141                 else
142                 {
143                     basePath = "/" + baseUri.Path();
144                 }
145             }
146             UriReference combined;
147             combined.SetFlags(baseUri.GetFlags());
148             combined.SetScheme(baseUri.Scheme());
149             if (baseUri.GetFlag(Flags.hasAuthority))
150             {
151                 combined.SetAuthority(baseUri.GetAuthority());
152             }
153             combined.SetPath(GetFullNetPath(CombineNetPath(basePathrelativeUri.Path())));
154             if (relativeUri.GetFlag(Flags.hasQuery))
155             {
156                 combined.SetQuery(relativeUri.Query());
157             }
158             if (relativeUri.GetFlag(Flags.hasFragment))
159             {
160                 combined.SetFragment(relativeUri.Fragment());
161             }
162             return combined;
163         }
164         public string ToString() const
165         {
166             if (GetFlag(Flags.asterisk))
167             {
168                 return "*";
169             }
170             else
171             {
172                 string result;
173                 if (!scheme.IsEmpty())
174                 {
175                     result.Append(scheme).Append(':');
176                     if (GetFlag(Flags.hasAuthority))
177                     {
178                         result.Append(authority.ToString());
179                     }
180                 }
181                 result.Append(path);
182                 if (GetFlag(Flags.hasQuery))
183                 {
184                     result.Append('?').Append(query);
185                 }
186                 if (GetFlag(Flags.hasFragment))
187                 {
188                     result.Append('#').Append(fragment);
189                 }
190                 return result;
191             }
192         }
193         public inline nothrow Flags GetFlags() const
194         {
195             return flags;
196         }
197         public inline nothrow void SetFlags(Flags flags_)
198         {
199             flags = flags_;
200         }
201         public inline nothrow bool GetFlag(Flags flag) const
202         {
203             return cast<Flags>(cast<sbyte>(flags) & cast<sbyte>(flag)) != Flags.none;
204         }
205         public inline nothrow void SetFlag(Flags flag)
206         {
207             flags = cast<Flags>(cast<sbyte>(flags) | cast<sbyte>(flag));
208         }
209         public inline nothrow void ResetFlag(Flags flag)
210         {
211             flags = cast<Flags>(cast<sbyte>(flags) & ~cast<sbyte>(flag));
212         }
213         public inline nothrow const string& Scheme() const
214         {
215             return scheme;
216         }
217         public nothrow void SetScheme(const string& scheme_)
218         {
219             scheme = scheme_;
220         }
221         public inline nothrow const Authority& GetAuthority() const
222         {
223             return authority;
224         }
225         public nothrow void SetAuthority(const Authority& authority_)
226         {
227             authority = authority_;
228             SetFlag(Flags.hasAuthority);
229         }
230         public inline nothrow const string& Path() const
231         {
232             return path;
233         }
234         public nothrow void SetPath(const string& path_)
235         {
236             path = path_;
237         }
238         public inline nothrow const string& Query() const
239         {
240             return query;
241         }
242         public nothrow void SetQuery(const string& query_)
243         {
244             query = query_;
245             SetFlag(Flags.hasQuery);
246         }
247         public inline nothrow const string& Fragment() const
248         {
249             return fragment;
250         }
251         public nothrow void SetFragment(const string& fragment_)
252         {
253             fragment = fragment_;
254             SetFlag(Flags.hasFragment);
255         }
256         private Flags flags;
257         private string scheme;
258         private Authority authority;
259         private string path;
260         private string query;
261         private string fragment;
262     }
263 
264     public nothrow string CombineNetPath(const string& leftconst string& right)
265     {
266         string combined;
267         if (left.IsEmpty())
268         {
269             combined = right;
270         }
271         else if (right.IsEmpty())
272         {
273             combined = left;
274         }
275         else
276         {
277             if (right.StartsWith("/"))
278             {
279                 combined = right;
280             }
281             else
282             {
283                 combined.Append(left);
284                 if (!left.EndsWith("/"))
285                 {
286                     combined.Append('/');
287                 }
288                 combined.Append(right);
289             }
290         }
291         return combined;
292     }
293 
294     public string GetFullNetPath(const string& path)
295     {
296         List<string> components = path.Split('/');
297         long w = 0;
298         long n = components.Count();
299         for (long i = 0; i < n; ++i;)
300         {
301             string c = components[i];
302             if (i == 0 || !c.IsEmpty() && c != ".")
303             {
304                 if (c == "..")
305                 {
306                     --w;
307                     if (w < 0)
308                     {
309                         throw UriException("invalid path '" + path + "'");
310                     }
311                 }
312                 else
313                 {
314                     if (w != i)
315                     {
316                         components[w] = components[i];
317                     }
318                     ++w;
319                 }
320             }
321         }
322         if (w == 0)
323         {
324             return "/";
325         }
326         else
327         {
328             string result;
329             for (long i = 0; i < w; ++i;)
330             {
331                 if (i != 0)
332                 {
333                     result.Append('/');
334                 }
335                 result.Append(components[i]);
336             }
337             if (result.IsEmpty())
338             {
339                 return "/";
340             }
341             else
342             {
343                 return result;
344             }
345         }
346     }
347 
348 /*                                
349     public class UriReference
350     {
351         public nothrow UriReference()
352         {
353         }
354         public UriReference(const ustring& uri)
355         {
356             TrivialLexer lexer(uri, "", 0);
357             UriParser.Parse(lexer, this);
358         }
359         public static UriReference Combine(const UriReference& baseUri, const UriReference& relativeUri)
360         {
361             if (!baseUri.IsAbsolute())
362             {
363                 throw UriException("base URI not absolute");
364             }
365             if (relativeUri.IsAbsolute())
366             {
367                 return relativeUri;
368             }
369             ustring basePath = u"/";
370             if (!baseUri.Path().IsEmpty())
371             {
372                 if (baseUri.Path().StartsWith(u"/"))
373                 {
374                     basePath = baseUri.Path();
375                 }
376                 else
377                 {
378                     basePath = u"/" + baseUri.Path();
379                 }
380             }
381             UriReference combined;
382             combined.SetScheme(baseUri.Scheme());
383             combined.SetUserInfo(baseUri.UserInfo());
384             combined.SetHost(baseUri.Host());
385             combined.SetPort(baseUri.Port());
386             combined.SetPath(GetFullNetPath(CombineNetPath(basePath, relativeUri.Path())));
387             combined.SetQuery(relativeUri.Query());
388             combined.SetFragment(relativeUri.Fragment());
389             return combined;
390         }
391         public ustring ToString() const
392         {
393             if (asterisk)
394             {
395                 return u"*";
396             }
397             else
398             {
399                 ustring result;
400                 if (!scheme.IsEmpty())
401                 {
402                     result.Append(scheme).Append(u"://");
403                     if (host.IsEmpty())
404                     {
405                         if (!regName.IsEmpty())
406                         {
407                             result.Append(regName);
408                         }
409                     }
410                     else
411                     {
412                         if (!userInfo.IsEmpty())
413                         {
414                             result.Append(userInfo).Append(u'@');
415                         }
416                         if (!host.IsEmpty())
417                         {
418                             result.Append(host);
419                             if (port != 0u)
420                             {
421                                 result.Append(u':').Append(ToUtf32(ToString(port)));
422                             }
423                         }
424                     }
425                 }
426                 if (!path.IsEmpty())
427                 {
428                     result.Append(path);
429                 }
430                 if (!query.IsEmpty())
431                 {
432                     result.Append(u'?').Append(query);
433                 }
434                 if (!fragment.IsEmpty())
435                 {
436                     result.Append(u'#').Append(fragment);
437                 }
438                 return result;
439             }
440         }
441         public bool IsAbsolute() const
442         {
443             return !scheme.IsEmpty() && !Authority().IsEmpty() && !path.IsEmpty();
444         }
445         public bool IsRelative() const
446         {
447             return !IsAbsolute();
448         }
449         public nothrow inline bool Asterisk() const
450         {
451             return asterisk;
452         }
453         public nothrow inline void SetAsterisk(bool asterisk_)
454         {
455             asterisk = asterisk_;
456         }
457         public nothrow inline const ustring& Scheme() const
458         {
459             return scheme;
460         }
461         public nothrow inline void SetScheme(const ustring& scheme_)
462         {
463             scheme = scheme_;
464         }
465         public nothrow inline const ustring& UserInfo() const
466         {
467             return userInfo;
468         }
469         public nothrow inline void SetUserInfo(const ustring& userInfo_)
470         {
471             userInfo = userInfo_;
472         }
473         public nothrow inline const ustring& Host() const
474         {
475             return host;
476         }
477         public nothrow inline void SetHost(const ustring& host_)
478         {
479             host = host_;
480         }
481         public nothrow inline uint Port() const
482         {
483             return port;
484         }
485         public nothrow inline void SetPort(uint port_)
486         {
487             port = port_;
488         }
489         public nothrow inline const ustring& RegName() const
490         {
491             return regName;
492         }
493         public nothrow inline void SetRegName(const ustring& regName_) const
494         {
495             regName = regName_;
496         }
497         public ustring Authority() const
498         {
499             if (host.IsEmpty())
500             {
501                 return regName;
502             }
503             else
504             {
505                 ustring s;
506                 if (!userInfo.IsEmpty())
507                 {
508                     s.Append(userInfo).Append('@');
509                 }
510                 if (!host.IsEmpty())
511                 {
512                     s.Append(host);
513                     if (port != 0u)
514                     {
515                         s.Append(':').Append(ToUtf32(ToString(port)));
516                     }
517                 }
518                 return s;
519             }
520         }
521         public nothrow inline const ustring& Path() const
522         {
523             return path;
524         }
525         public nothrow inline void SetPath(const ustring& path_)
526         {
527             path = path_;
528         }
529         public nothrow inline const ustring& Query() const
530         {
531             return query;
532         }
533         public nothrow inline void SetQuery(const ustring& query_)
534         {
535             query = query_;
536         }
537         public nothrow inline const ustring& Fragment() const
538         {
539             return fragment;
540         }
541         public nothrow inline void SetFragment(const ustring& fragment_)
542         {
543             fragment = fragment_;
544         }
545         public nothrow void Clear()
546         {
547             asterisk = false;
548             scheme.Clear();
549             userInfo.Clear();
550             host.Clear();
551             port = 0u;
552             regName.Clear();
553             path.Clear();
554             query.Clear();
555             fragment.Clear();
556         }
557         private bool asterisk;
558         private ustring scheme;
559         private ustring userInfo;
560         private ustring host;
561         private uint port;
562         private ustring regName;
563         private ustring path;
564         private ustring query;
565         private ustring fragment;
566     }
567     
568     public ustring CombineNetPath(const ustring& path1, const ustring& path2)
569     {
570         if (path1.IsEmpty())
571         {
572             return path2;
573         }
574         else if (path2.IsEmpty())
575         {
576             return path1;
577         }
578         else
579         {
580             if (path2.StartsWith(u"/"))
581             {
582                 return path2;
583             }
584             else
585             {
586                 ustring result = path1;
587                 if (!path1.EndsWith(u"/"))
588                 {
589                     result.Append(u'/');
590                 }
591                 result.Append(path2);
592                 return result;
593             }
594         }
595     }
596 */    
597 }