cgiparam.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include "cgiparam.h"
  2. #include <library/cpp/string_utils/scan/scan.h>
  3. #include <library/cpp/string_utils/quote/quote.h>
  4. #include <util/generic/singleton.h>
  5. TCgiParameters::TCgiParameters(std::initializer_list<std::pair<TString, TString>> il) {
  6. for (const auto& item : il) {
  7. insert(item);
  8. }
  9. }
  10. const TString& TCgiParameters::Get(const TStringBuf name, size_t numOfValue) const noexcept Y_LIFETIME_BOUND {
  11. const auto it = Find(name, numOfValue);
  12. return end() == it ? Default<TString>() : it->second;
  13. }
  14. const TString& TCgiParameters::GetLast(const TStringBuf name) const noexcept {
  15. if (auto it = this->upper_bound(name); it != this->begin()) {
  16. --it;
  17. if (it->first == name) {
  18. return it->second;
  19. }
  20. }
  21. return Default<TString>();
  22. }
  23. bool TCgiParameters::Erase(const TStringBuf name, size_t pos) {
  24. const auto pair = equal_range(name);
  25. for (auto it = pair.first; it != pair.second; ++it, --pos) {
  26. if (0 == pos) {
  27. erase(it);
  28. return true;
  29. }
  30. }
  31. return false;
  32. }
  33. bool TCgiParameters::Erase(const TStringBuf name, const TStringBuf val) {
  34. const auto pair = equal_range(name);
  35. bool found = false;
  36. for (auto it = pair.first; it != pair.second;) {
  37. if (val == it->second) {
  38. it = erase(it);
  39. found = true;
  40. } else {
  41. ++it;
  42. }
  43. }
  44. return found;
  45. }
  46. bool TCgiParameters::ErasePattern(const TStringBuf name, const TStringBuf pat) {
  47. const auto pair = equal_range(name);
  48. bool found = false;
  49. for (auto it = pair.first; it != pair.second;) {
  50. bool startsWith = it->second.StartsWith(pat);
  51. if (startsWith) {
  52. it = erase(it);
  53. found = true;
  54. } else {
  55. ++it;
  56. }
  57. }
  58. return found;
  59. }
  60. size_t TCgiParameters::EraseAll(const TStringBuf name) {
  61. size_t num = 0;
  62. const auto pair = equal_range(name);
  63. for (auto it = pair.first; it != pair.second; erase(it++), ++num)
  64. ;
  65. return num;
  66. }
  67. void TCgiParameters::JoinUnescaped(const TStringBuf key, char sep, TStringBuf val) {
  68. const auto pair = equal_range(key);
  69. auto it = pair.first;
  70. if (it == pair.second) { // not found
  71. if (val.IsInited()) {
  72. emplace_hint(it, TString(key), TString(val));
  73. }
  74. } else {
  75. TString& dst = it->second;
  76. for (++it; it != pair.second; erase(it++)) {
  77. dst += sep;
  78. dst.AppendNoAlias(it->second.data(), it->second.size());
  79. }
  80. if (val.IsInited()) {
  81. dst += sep;
  82. dst += val;
  83. }
  84. }
  85. }
  86. static inline TString DoUnescape(const TStringBuf s) {
  87. TString res;
  88. res.ReserveAndResize(CgiUnescapeBufLen(s.size()));
  89. res.resize(CgiUnescape(res.begin(), s).size());
  90. return res;
  91. }
  92. void TCgiParameters::InsertEscaped(const TStringBuf name, const TStringBuf value) {
  93. InsertUnescaped(DoUnescape(name), DoUnescape(value));
  94. }
  95. template <bool addAll, class F>
  96. static inline void DoScan(const TStringBuf s, F& f) {
  97. ScanKeyValue<addAll, '&', '='>(s, f);
  98. }
  99. struct TAddEscaped {
  100. TCgiParameters* C;
  101. inline void operator()(const TStringBuf key, const TStringBuf val) {
  102. C->InsertEscaped(key, val);
  103. }
  104. };
  105. void TCgiParameters::Scan(const TStringBuf query, bool form) {
  106. Flush();
  107. form ? ScanAdd(query) : ScanAddAll(query);
  108. }
  109. void TCgiParameters::ScanAdd(const TStringBuf query) {
  110. TAddEscaped f = {this};
  111. DoScan<false>(query, f);
  112. }
  113. void TCgiParameters::ScanAddUnescaped(const TStringBuf query) {
  114. auto f = [this](const TStringBuf key, const TStringBuf val) {
  115. this->InsertUnescaped(key, val);
  116. };
  117. DoScan<false>(query, f);
  118. }
  119. void TCgiParameters::ScanAddAllUnescaped(const TStringBuf query) {
  120. auto f = [this](const TStringBuf key, const TStringBuf val) {
  121. this->InsertUnescaped(key, val);
  122. };
  123. DoScan<true>(query, f);
  124. }
  125. void TCgiParameters::ScanAddAll(const TStringBuf query) {
  126. TAddEscaped f = {this};
  127. DoScan<true>(query, f);
  128. }
  129. TString TCgiParameters::Print() const {
  130. TString res;
  131. res.ReserveAndResize(PrintSize());
  132. const char* end = Print(res.begin());
  133. res.ReserveAndResize(end - res.data());
  134. return res;
  135. }
  136. char* TCgiParameters::Print(char* res) const {
  137. if (empty()) {
  138. return res;
  139. }
  140. for (auto i = begin();;) {
  141. res = CGIEscape(res, i->first);
  142. *res++ = '=';
  143. res = CGIEscape(res, i->second);
  144. if (++i == end()) {
  145. break;
  146. }
  147. *res++ = '&';
  148. }
  149. return res;
  150. }
  151. size_t TCgiParameters::PrintSize() const noexcept {
  152. size_t res = size(); // for '&'
  153. for (const auto& i : *this) {
  154. res += CgiEscapeBufLen(i.first.size() + i.second.size()); // extra zero will be used for '='
  155. }
  156. return res;
  157. }
  158. TString TCgiParameters::QuotedPrint(const char* safe) const {
  159. if (empty()) {
  160. return TString();
  161. }
  162. TString res;
  163. res.ReserveAndResize(PrintSize());
  164. char* ptr = res.begin();
  165. for (auto i = begin();;) {
  166. ptr = Quote(ptr, i->first, safe);
  167. *ptr++ = '=';
  168. ptr = Quote(ptr, i->second, safe);
  169. if (++i == end()) {
  170. break;
  171. }
  172. *ptr++ = '&';
  173. }
  174. res.ReserveAndResize(ptr - res.data());
  175. return res;
  176. }
  177. TCgiParameters::const_iterator TCgiParameters::Find(const TStringBuf name, size_t pos) const noexcept Y_LIFETIME_BOUND {
  178. const auto pair = equal_range(name);
  179. for (auto it = pair.first; it != pair.second; ++it, --pos) {
  180. if (0 == pos) {
  181. return it;
  182. }
  183. }
  184. return end();
  185. }
  186. bool TCgiParameters::Has(const TStringBuf name, const TStringBuf value) const noexcept {
  187. const auto pair = equal_range(name);
  188. for (auto it = pair.first; it != pair.second; ++it) {
  189. if (value == it->second) {
  190. return true;
  191. }
  192. }
  193. return false;
  194. }
  195. TQuickCgiParam::TQuickCgiParam(const TStringBuf cgiParamStr) {
  196. UnescapeBuf.ReserveAndResize(CgiUnescapeBufLen(cgiParamStr.size()));
  197. char* buf = UnescapeBuf.begin();
  198. auto f = [this, &buf](const TStringBuf key, const TStringBuf val) {
  199. TStringBuf name = CgiUnescapeBuf(buf, key);
  200. buf += name.size() + 1;
  201. TStringBuf value = CgiUnescapeBuf(buf, val);
  202. buf += value.size() + 1;
  203. Y_ASSERT(buf <= UnescapeBuf.begin() + UnescapeBuf.capacity() + 1 /*trailing zero*/);
  204. emplace(name, value);
  205. };
  206. DoScan<false>(cgiParamStr, f);
  207. if (buf != UnescapeBuf.begin()) {
  208. UnescapeBuf.ReserveAndResize(buf - UnescapeBuf.begin() - 1 /*trailing zero*/);
  209. }
  210. }
  211. TStringBuf TQuickCgiParam::Get(const TStringBuf name, size_t pos) const noexcept Y_LIFETIME_BOUND {
  212. const auto pair = equal_range(name);
  213. for (auto it = pair.first; it != pair.second; ++it, --pos) {
  214. if (0 == pos) {
  215. return it->second;
  216. }
  217. }
  218. return TStringBuf{};
  219. }
  220. bool TQuickCgiParam::Has(const TStringBuf name, const TStringBuf value) const noexcept {
  221. const auto pair = equal_range(name);
  222. for (auto it = pair.first; it != pair.second; ++it) {
  223. if (value == it->second) {
  224. return true;
  225. }
  226. }
  227. return false;
  228. }