url.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright 2011-2022 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bnet#license-bsd-2-clause
  4. */
  5. #include <bx/url.h>
  6. namespace bx
  7. {
  8. UrlView::UrlView()
  9. {
  10. }
  11. void UrlView::clear()
  12. {
  13. for (uint32_t ii = 0; ii < Count; ++ii)
  14. {
  15. m_tokens[ii].clear();
  16. }
  17. }
  18. bool UrlView::parse(const StringView& _url)
  19. {
  20. clear();
  21. const char* term = _url.getTerm();
  22. StringView schemeEnd = strFind(_url, "://");
  23. const char* hostStart = !schemeEnd.isEmpty() ? schemeEnd.getTerm() : _url.getPtr();
  24. StringView path = strFind(StringView(hostStart, term), '/');
  25. if (schemeEnd.isEmpty()
  26. && path.isEmpty() )
  27. {
  28. return false;
  29. }
  30. if (!schemeEnd.isEmpty()
  31. && (path.isEmpty() || path.getPtr() > schemeEnd.getPtr() ) )
  32. {
  33. const StringView scheme(_url.getPtr(), schemeEnd.getPtr() );
  34. if (!isAlpha(scheme) )
  35. {
  36. return false;
  37. }
  38. m_tokens[Scheme].set(scheme);
  39. }
  40. if (!path.isEmpty() )
  41. {
  42. path.set(path.getPtr(), term);
  43. const StringView query = strFind(path, '?');
  44. const StringView fragment = strFind(path, '#');
  45. if (!fragment.isEmpty()
  46. && fragment.getPtr() < query.getPtr() )
  47. {
  48. return false;
  49. }
  50. m_tokens[Path].set(path.getPtr()
  51. , !query.isEmpty() ? query.getPtr()
  52. : !fragment.isEmpty() ? fragment.getPtr()
  53. : term
  54. );
  55. if (!query.isEmpty() )
  56. {
  57. m_tokens[Query].set(query.getPtr()+1
  58. , !fragment.isEmpty() ? fragment.getPtr()
  59. : term
  60. );
  61. }
  62. if (!fragment.isEmpty() )
  63. {
  64. m_tokens[Fragment].set(fragment.getPtr()+1, term);
  65. }
  66. term = path.getPtr();
  67. }
  68. const StringView userPassEnd = strFind(StringView(hostStart, term), '@');
  69. const char* userPassStart = !userPassEnd.isEmpty() ? hostStart : NULL;
  70. hostStart = !userPassEnd.isEmpty() ? userPassEnd.getPtr()+1 : hostStart;
  71. const StringView portStart = strFind(StringView(hostStart, term), ':');
  72. m_tokens[Host].set(hostStart, !portStart.isEmpty() ? portStart.getPtr() : term);
  73. if (!portStart.isEmpty())
  74. {
  75. m_tokens[Port].set(portStart.getPtr()+1, term);
  76. }
  77. if (NULL != userPassStart)
  78. {
  79. StringView passStart = strFind(StringView(userPassStart, userPassEnd.getPtr() ), ':');
  80. m_tokens[UserName].set(userPassStart
  81. , !passStart.isEmpty() ? passStart.getPtr()
  82. : userPassEnd.getPtr()
  83. );
  84. if (!passStart.isEmpty() )
  85. {
  86. m_tokens[Password].set(passStart.getPtr()+1, userPassEnd.getPtr() );
  87. }
  88. }
  89. return true;
  90. }
  91. const StringView& UrlView::get(Enum _token) const
  92. {
  93. return m_tokens[_token];
  94. }
  95. static char toHex(char _nible)
  96. {
  97. return "0123456789ABCDEF"[_nible&0xf];
  98. }
  99. // https://secure.wikimedia.org/wikipedia/en/wiki/URL_encoding
  100. void urlEncode(char* _out, uint32_t _max, const StringView& _str)
  101. {
  102. _max--; // need space for zero terminator
  103. const char* str = _str.getPtr();
  104. const char* term = _str.getTerm();
  105. uint32_t ii = 0;
  106. for (char ch = *str++
  107. ; str <= term && ii < _max
  108. ; ch = *str++
  109. )
  110. {
  111. if (isAlphaNum(ch)
  112. || ch == '-'
  113. || ch == '_'
  114. || ch == '.'
  115. || ch == '~')
  116. {
  117. _out[ii++] = ch;
  118. }
  119. else if (ii+3 < _max)
  120. {
  121. _out[ii++] = '%';
  122. _out[ii++] = toHex(ch>>4);
  123. _out[ii++] = toHex(ch);
  124. }
  125. }
  126. _out[ii] = '\0';
  127. }
  128. } // namespace bx