diff options
author | arcadia-devtools <arcadia-devtools@yandex-team.ru> | 2022-02-17 12:04:09 +0300 |
---|---|---|
committer | arcadia-devtools <arcadia-devtools@yandex-team.ru> | 2022-02-17 12:04:09 +0300 |
commit | 2c8e314f8fff8633fe2cf026badfbf6180845ae0 (patch) | |
tree | c3b650d13934ec1315e3660d60fd2275f09b03a7 /library/cpp | |
parent | a49ae9d891c35087b242c854f69880fd9fecbddd (diff) | |
download | ydb-2c8e314f8fff8633fe2cf026badfbf6180845ae0.tar.gz |
intermediate changes
ref:d5f945ecdc1f5af1ad57e12787c6b8ed1a9f0f12
Diffstat (limited to 'library/cpp')
-rw-r--r-- | library/cpp/case_insensitive_string/case_insensitive_char_traits.cpp | 34 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/case_insensitive_char_traits.h | 30 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/case_insensitive_string.cpp | 22 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/case_insensitive_string.h | 37 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/case_insensitive_string_ut.cpp | 65 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/ut/ya.make | 9 | ||||
-rw-r--r-- | library/cpp/case_insensitive_string/ya.make | 17 | ||||
-rw-r--r-- | library/cpp/digest/murmur/murmur.cpp | 1 | ||||
-rw-r--r-- | library/cpp/digest/murmur/murmur.h | 100 | ||||
-rw-r--r-- | library/cpp/digest/murmur/murmur_ut.cpp | 61 | ||||
-rw-r--r-- | library/cpp/digest/murmur/ut/ya.make | 15 | ||||
-rw-r--r-- | library/cpp/digest/murmur/ya.make | 9 | ||||
-rw-r--r-- | library/cpp/http/misc/httpreqdata.cpp | 211 | ||||
-rw-r--r-- | library/cpp/http/misc/httpreqdata.h | 81 | ||||
-rw-r--r-- | library/cpp/http/misc/httpreqdata_ut.cpp | 42 | ||||
-rw-r--r-- | library/cpp/http/misc/ya.make | 1 |
16 files changed, 555 insertions, 180 deletions
diff --git a/library/cpp/case_insensitive_string/case_insensitive_char_traits.cpp b/library/cpp/case_insensitive_string/case_insensitive_char_traits.cpp new file mode 100644 index 0000000000..14e6d1d51f --- /dev/null +++ b/library/cpp/case_insensitive_string/case_insensitive_char_traits.cpp @@ -0,0 +1,34 @@ +#include "case_insensitive_char_traits.h" +#include "case_insensitive_string.h" + +#include <util/string/escape.h> + +int TCaseInsensitiveCharTraits::compare(const char* s1, const char* s2, std::size_t n) { + while (n-- != 0) { + if (to_upper(*s1) < to_upper(*s2)) { + return -1; + } + if (to_upper(*s1) > to_upper(*s2)) { + return 1; + } + ++s1; + ++s2; + } + return 0; +} + +const char* TCaseInsensitiveCharTraits::find(const char* s, std::size_t n, char a) { + auto const ua(to_upper(a)); + while (n-- != 0) { + if (to_upper(*s) == ua) + return s; + s++; + } + return nullptr; +} + +TCaseInsensitiveString EscapeC(const TCaseInsensitiveString& str) { + const auto result = EscapeC(str.data(), str.size()); + return {result.data(), result.size()}; +} + diff --git a/library/cpp/case_insensitive_string/case_insensitive_char_traits.h b/library/cpp/case_insensitive_string/case_insensitive_char_traits.h new file mode 100644 index 0000000000..2717893c10 --- /dev/null +++ b/library/cpp/case_insensitive_string/case_insensitive_char_traits.h @@ -0,0 +1,30 @@ +#pragma once + +#include <contrib/libs/libc_compat/string.h> + +#include <string> + +struct TCaseInsensitiveCharTraits : private std::char_traits<char> { + static bool eq(char c1, char c2) { + return to_upper(c1) == to_upper(c2); + } + + static bool lt(char c1, char c2) { + return to_upper(c1) < to_upper(c2); + } + + static int compare(const char* s1, const char* s2, std::size_t n); + + static const char* find(const char* s, std::size_t n, char a); + + using std::char_traits<char>::assign; + using std::char_traits<char>::char_type; + using std::char_traits<char>::copy; + using std::char_traits<char>::length; + using std::char_traits<char>::move; + +private: + static char to_upper(char ch) { + return std::toupper((unsigned char)ch); + } +}; diff --git a/library/cpp/case_insensitive_string/case_insensitive_string.cpp b/library/cpp/case_insensitive_string/case_insensitive_string.cpp new file mode 100644 index 0000000000..16c0f5ff7a --- /dev/null +++ b/library/cpp/case_insensitive_string/case_insensitive_string.cpp @@ -0,0 +1,22 @@ +#include "case_insensitive_string.h" + +#include <library/cpp/digest/murmur/murmur.h> + +size_t THash<TCaseInsensitiveStringBuf>::operator()(TCaseInsensitiveStringBuf str) const noexcept { + TMurmurHash2A<size_t> hash; + for (size_t i = 0; i < str.size(); ++i) { + char lower = std::tolower(str[i]); + hash.Update(&lower, 1); + } + return hash.Value(); +} + +template <> +void Out<TCaseInsensitiveString>(IOutputStream& o, const TCaseInsensitiveString& p) { + o.Write(p.data(), p.size()); +} + +template <> +void Out<TCaseInsensitiveStringBuf>(IOutputStream& o, const TCaseInsensitiveStringBuf& p) { + o.Write(p.data(), p.size()); +} diff --git a/library/cpp/case_insensitive_string/case_insensitive_string.h b/library/cpp/case_insensitive_string/case_insensitive_string.h new file mode 100644 index 0000000000..443de3e5f9 --- /dev/null +++ b/library/cpp/case_insensitive_string/case_insensitive_string.h @@ -0,0 +1,37 @@ +#pragma once + +#include "case_insensitive_char_traits.h" + +#include <util/generic/strbuf.h> +#include <util/generic/string.h> +#include <util/generic/hash.h> +#include <util/string/split.h> + +using TCaseInsensitiveString = TBasicString<char, TCaseInsensitiveCharTraits>; +using TCaseInsensitiveStringBuf = TBasicStringBuf<char, TCaseInsensitiveCharTraits>; + +template <> +struct THash<TCaseInsensitiveStringBuf> { + size_t operator()(TCaseInsensitiveStringBuf str) const noexcept; +}; + +template <> +struct THash<TCaseInsensitiveString> : THash<TCaseInsensitiveStringBuf> {}; + +namespace NStringSplitPrivate { + + template<> + struct TStringBufOfImpl<TCaseInsensitiveStringBuf> { + /* + * WARN: + * StringSplitter does not use TCharTraits properly. + * Splitting such strings is explicitly disabled. + */ + // using type = TCaseInsensitiveStringBuf; + }; + + template<> + struct TStringBufOfImpl<TCaseInsensitiveString> : TStringBufOfImpl<TCaseInsensitiveStringBuf> { + }; + +} // namespace NStringSplitPrivate diff --git a/library/cpp/case_insensitive_string/case_insensitive_string_ut.cpp b/library/cpp/case_insensitive_string/case_insensitive_string_ut.cpp new file mode 100644 index 0000000000..49f9c59c95 --- /dev/null +++ b/library/cpp/case_insensitive_string/case_insensitive_string_ut.cpp @@ -0,0 +1,65 @@ +#include "case_insensitive_string.h" + +#include <util/generic/string_ut.h> + +class TCaseInsensitiveStringTest : public TTestBase, private TStringTestImpl<TCaseInsensitiveString, TTestData<char>> { +public: + UNIT_TEST_SUITE(TCaseInsensitiveStringTest); + UNIT_TEST(TestOperators); + UNIT_TEST(TestOperatorsCI); + + UNIT_TEST_SUITE_END(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TCaseInsensitiveStringTest); + +Y_UNIT_TEST_SUITE(TCaseInsensitiveStringTestEx) { + Y_UNIT_TEST(BasicTString) { + TCaseInsensitiveString foo("foo"); + TCaseInsensitiveString FOO("FOO"); + TCaseInsensitiveString Bar("Bar"); + TCaseInsensitiveString bAR("bAR"); + + UNIT_ASSERT_EQUAL(foo, FOO); + UNIT_ASSERT_EQUAL(Bar, bAR); + + constexpr TCaseInsensitiveStringBuf foobar("foobar"); + UNIT_ASSERT(foobar.StartsWith(foo)); + UNIT_ASSERT(foobar.StartsWith(FOO)); + UNIT_ASSERT(foobar.EndsWith(Bar)); + UNIT_ASSERT(foobar.EndsWith(bAR)); + UNIT_ASSERT(foobar.Contains(FOO)); + UNIT_ASSERT(foobar.Contains(Bar)); + } + + Y_UNIT_TEST(BasicStdString) { + using TCaseInsensitiveStdString = std::basic_string<char, TCaseInsensitiveCharTraits>; + using TCaseInsensitiveStringView = std::basic_string_view<char, TCaseInsensitiveCharTraits>; + + TCaseInsensitiveStdString foo("foo"); + TCaseInsensitiveStdString FOO("FOO"); + TCaseInsensitiveStdString Bar("Bar"); + TCaseInsensitiveStdString bAR("bAR"); + + UNIT_ASSERT_EQUAL(foo, FOO); + UNIT_ASSERT_EQUAL(Bar, bAR); + + constexpr TCaseInsensitiveStringView foobar("foobar"); + UNIT_ASSERT(foobar.starts_with(foo)); + UNIT_ASSERT(foobar.starts_with(FOO)); + UNIT_ASSERT(foobar.ends_with(Bar)); + UNIT_ASSERT(foobar.ends_with(bAR)); + //TODO: test contains after C++23 + } + +/* + Y_UNIT_TEST(TestSplit) { + TCaseInsensitiveStringBuf input("splitAmeAbro"); + TVector<TCaseInsensitiveStringBuf> expected{"split", "me", "bro"}; + + TVector<TCaseInsensitiveStringBuf> split = StringSplitter(input).Split('a'); + + UNIT_ASSERT_VALUES_EQUAL(split, expected); + } +*/ +} diff --git a/library/cpp/case_insensitive_string/ut/ya.make b/library/cpp/case_insensitive_string/ut/ya.make new file mode 100644 index 0000000000..b209d4571e --- /dev/null +++ b/library/cpp/case_insensitive_string/ut/ya.make @@ -0,0 +1,9 @@ +OWNER(eeight) + +UNITTEST_FOR(library/cpp/case_insensitive_string) + +SRCS( + case_insensitive_string_ut.cpp +) + +END() diff --git a/library/cpp/case_insensitive_string/ya.make b/library/cpp/case_insensitive_string/ya.make new file mode 100644 index 0000000000..dfca415e6b --- /dev/null +++ b/library/cpp/case_insensitive_string/ya.make @@ -0,0 +1,17 @@ +OWNER(eeight) + +LIBRARY() + +SRCS( + case_insensitive_char_traits.cpp + case_insensitive_string.cpp +) + +PEERDIR( + contrib/libs/libc_compat + library/cpp/digest/murmur +) + +END() + +RECURSE_FOR_TESTS(ut) diff --git a/library/cpp/digest/murmur/murmur.cpp b/library/cpp/digest/murmur/murmur.cpp new file mode 100644 index 0000000000..7c7c1c0ff7 --- /dev/null +++ b/library/cpp/digest/murmur/murmur.cpp @@ -0,0 +1 @@ +#include "murmur.h" diff --git a/library/cpp/digest/murmur/murmur.h b/library/cpp/digest/murmur/murmur.h new file mode 100644 index 0000000000..cbf2886412 --- /dev/null +++ b/library/cpp/digest/murmur/murmur.h @@ -0,0 +1,100 @@ +#pragma once + +#include <util/system/defaults.h> +#include <util/system/unaligned_mem.h> + +/* + * https://sites.google.com/site/murmurhash/ + */ + +namespace NMurmurPrivate { + template <size_t> + struct TMurmurHash2ATraits; + + template <> + struct TMurmurHash2ATraits<32> { + using TValue = ui32; + static const TValue Multiplier = 0x5bd1e995; + enum { R1 = 24, + R2 = 13, + R3 = 15 }; + }; + + template <> + struct TMurmurHash2ATraits<64> { + using TValue = ui64; + static const TValue Multiplier = ULL(0xc6a4a7935bd1e995); + enum { R1 = 47, + R2 = 47, + R3 = 47 }; + }; +} + +template <class T> +class TMurmurHash2A { +private: + using TTraits = typename NMurmurPrivate::TMurmurHash2ATraits<8 * sizeof(T)>; + using TValue = typename TTraits::TValue; + +public: + inline TMurmurHash2A(TValue seed = 0) + : Hash(seed) + { + } + + inline TMurmurHash2A& Update(const void* buf, size_t len) noexcept { + Size += len; + + MixTail(buf, len); + + while (len >= sizeof(TValue)) { + Hash = Mix(Hash, ReadUnaligned<TValue>(buf)); + buf = static_cast<const char*>(buf) + sizeof(TValue); + len -= sizeof(TValue); + } + + MixTail(buf, len); + + return *this; + } + + inline TValue Value() const noexcept { + TValue hash = Mix(Mix(Hash, Tail), (TValue)Size); + + hash ^= hash >> TTraits::R2; + hash *= TTraits::Multiplier; + hash ^= hash >> TTraits::R3; + + return hash; + } + +private: + static inline TValue Mix(TValue h, TValue k) noexcept { + k *= TTraits::Multiplier; + k ^= k >> TTraits::R1; + k *= TTraits::Multiplier; + h *= TTraits::Multiplier; + h ^= k; + return h; + } + + inline void MixTail(const void*& buf, size_t& len) noexcept { + while (len && (len < sizeof(TValue) || Count)) { + Tail |= (TValue) * ((const unsigned char*&)buf)++ << (Count++ * 8); + + --len; + + if (Count == sizeof(TValue)) { + Hash = Mix(Hash, Tail); + Tail = 0; + Count = 0; + } + } + } + +private: + TValue Hash = 0; + TValue Tail = 0; + size_t Count = 0; + size_t Size = 0; +}; diff --git a/library/cpp/digest/murmur/murmur_ut.cpp b/library/cpp/digest/murmur/murmur_ut.cpp new file mode 100644 index 0000000000..3447980c8c --- /dev/null +++ b/library/cpp/digest/murmur/murmur_ut.cpp @@ -0,0 +1,61 @@ +#include "murmur.h" + +#include <library/cpp/testing/unittest/registar.h> + +class TMurmurHashTest: public TTestBase { +private: + UNIT_TEST_SUITE(TMurmurHashTest); + UNIT_TEST(TestHash2A32) + UNIT_TEST(TestHash2A64) + UNIT_TEST(TestUnaligned) + UNIT_TEST_SUITE_END(); + +private: + inline void TestHash2A32() { + ui8 buf[256]; + + for (size_t i = 0; i < 256; ++i) { + buf[i] = i; + } + + Test2A<ui32>(buf, 256, 0, 97, 178525084UL); + Test2A<ui32>(buf, 256, 128, 193, 178525084UL); + Test2A<ui32>(buf, 255, 0, 97, 2459858906UL); + Test2A<ui32>(buf, 255, 128, 193, 2459858906UL); + } + + inline void TestHash2A64() { + ui8 buf[256]; + + for (size_t i = 0; i < 256; ++i) { + buf[i] = i; + } + + Test2A<ui64>(buf, 256, 0, 97, ULL(15099340606808450747)); + Test2A<ui64>(buf, 256, 128, 193, ULL(15099340606808450747)); + Test2A<ui64>(buf, 255, 0, 97, ULL(8331973280124075880)); + Test2A<ui64>(buf, 255, 128, 193, ULL(8331973280124075880)); + } + + inline void TestUnaligned() { + ui8 buf[257]; + for (size_t i = 0; i < 256; ++i) { + buf[i + 1] = i; + } + Test2A<ui64>(buf + 1, 256, 0, 97, ULL(15099340606808450747)); + Test2A<ui64>(buf + 1, 256, 128, 193, ULL(15099340606808450747)); + Test2A<ui64>(buf + 1, 255, 0, 97, ULL(8331973280124075880)); + Test2A<ui64>(buf + 1, 255, 128, 193, ULL(8331973280124075880)); + } + +private: + template <class T> + static inline void Test2A(const ui8* data, size_t len, size_t split1, size_t split2, T expected) { + const T value1 = TMurmurHash2A<T>().Update(data, split1).Update(data + split1, len - split1).Value(); + const T value2 = TMurmurHash2A<T>().Update(data, split2).Update(data + split2, len - split2).Value(); + UNIT_ASSERT_VALUES_EQUAL(value1, value2); + UNIT_ASSERT_VALUES_EQUAL(value1, expected); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TMurmurHashTest); diff --git a/library/cpp/digest/murmur/ut/ya.make b/library/cpp/digest/murmur/ut/ya.make new file mode 100644 index 0000000000..3405399779 --- /dev/null +++ b/library/cpp/digest/murmur/ut/ya.make @@ -0,0 +1,15 @@ +UNITTEST() + +OWNER(elantsev) + +PEERDIR( + ADDINCL library/cpp/digest/murmur +) + +SRCDIR(library/cpp/digest/murmur) + +SRCS( + murmur_ut.cpp +) + +END() diff --git a/library/cpp/digest/murmur/ya.make b/library/cpp/digest/murmur/ya.make new file mode 100644 index 0000000000..5647cc75e1 --- /dev/null +++ b/library/cpp/digest/murmur/ya.make @@ -0,0 +1,9 @@ +LIBRARY() + +OWNER(elantsev) + +SRCS( + murmur.cpp +) + +END() diff --git a/library/cpp/http/misc/httpreqdata.cpp b/library/cpp/http/misc/httpreqdata.cpp index f6951f68cd..f02611bc58 100644 --- a/library/cpp/http/misc/httpreqdata.cpp +++ b/library/cpp/http/misc/httpreqdata.cpp @@ -1,145 +1,123 @@ #include "httpreqdata.h" +#include <library/cpp/case_insensitive_string/case_insensitive_string.h> #include <util/stream/mem.h> TBaseServerRequestData::TBaseServerRequestData(SOCKET s) - : Addr(nullptr) - , Host() - , Port() - , Path(nullptr) - , Search(nullptr) - , SearchLength(0) - , Socket(s) - , BeginTime(MicroSeconds()) + : Socket_(s) + , BeginTime_(MicroSeconds()) { } -TBaseServerRequestData::TBaseServerRequestData(const char* qs, SOCKET s) - : Addr(nullptr) - , Host() - , Port() - , Path(nullptr) - , Search((char*)qs) - , SearchLength(qs ? strlen(qs) : 0) - , OrigSearch(Search, SearchLength) - , Socket(s) - , BeginTime(MicroSeconds()) +TBaseServerRequestData::TBaseServerRequestData(TStringBuf qs, SOCKET s) + : Query_(qs) + , OrigQuery_(Query_) + , Socket_(s) + , BeginTime_(MicroSeconds()) { } -void TBaseServerRequestData::AppendQueryString(const char* str, size_t length) { - if (Y_UNLIKELY(Search)) { - Y_ASSERT(strlen(Search) == SearchLength); - ModifiedQueryString.Reserve(SearchLength + length + 2); - ModifiedQueryString.Assign(Search, SearchLength); - if (SearchLength > 0 && Search[SearchLength - 1] != '&' && - length > 0 && str[0] != '&') { - ModifiedQueryString.Append('&'); - } - ModifiedQueryString.Append(str, length); - } else { - ModifiedQueryString.Reserve(length + 1); - ModifiedQueryString.Assign(str, length); - } - ModifiedQueryString.Append('\0'); - Search = ModifiedQueryString.data(); - SearchLength = ModifiedQueryString.size() - 1; // ignore terminator +void TBaseServerRequestData::AppendQueryString(TStringBuf str) { + if (Y_UNLIKELY(!Query_.empty())) { + TStringBuf separator = !Query_.EndsWith('&') && !str.StartsWith('&') ? "&"sv : ""sv; + ModifiedQueryString_ = TString::Join(Query_, separator, str); + } else { + ModifiedQueryString_ = str; + } + Query_ = ModifiedQueryString_; } void TBaseServerRequestData::SetRemoteAddr(TStringBuf addr) { - TMemoryOutput out(AddrData, Y_ARRAY_SIZE(AddrData) - 1); - out.Write(addr.substr(0, Y_ARRAY_SIZE(AddrData) - 1)); - *out.Buf() = '\0'; - - Addr = AddrData; + Addr_.ConstructInPlace(addr.substr(0, INET6_ADDRSTRLEN - 1)); } -const char* TBaseServerRequestData::RemoteAddr() const { - if (!Addr) { - *AddrData = 0; - GetRemoteAddr(Socket, AddrData, sizeof(AddrData)); - Addr = AddrData; - } - - return Addr; -} +TStringBuf TBaseServerRequestData::RemoteAddr() const { + if (!Addr_) { + auto& addr = Addr_.ConstructInPlace(); + addr.ReserveAndResize(INET6_ADDRSTRLEN); + if (GetRemoteAddr(Socket_, addr.begin(), sizeof(INET6_ADDRSTRLEN))) { + if (auto pos = addr.find('\0'); pos != TString::npos) { + addr.resize(pos); + } + } else { + addr.clear(); + } + } -const char* TBaseServerRequestData::HeaderIn(TStringBuf key) const { - auto it = HeadersIn_.find(key); + return *Addr_; + } - if (it == HeadersIn_.end()) { - return nullptr; - } +const TString* TBaseServerRequestData::HeaderIn(TStringBuf key) const { + return HeadersIn_.FindPtr(key); +} - return it->second.data(); +TStringBuf TBaseServerRequestData::HeaderInOrEmpty(TStringBuf key) const { + const auto* ptr = HeaderIn(key); + return ptr ? TStringBuf{*ptr} : TStringBuf{}; } TString TBaseServerRequestData::HeaderByIndex(size_t n) const noexcept { - if (n >= HeadersCount()) { - return nullptr; + if (n >= HeadersIn_.size()) { + return {}; } - THttpHeadersContainer::const_iterator i = HeadersIn_.begin(); - - while (n) { - ++i; - --n; - } + const auto& [key, value] = *std::next(HeadersIn_.begin(), n); - return TString(i->first) + TStringBuf(": ") + i->second; + return TString::Join(key, ": ", value); } -const char* TBaseServerRequestData::Environment(const char* key) const { - if (stricmp(key, "REMOTE_ADDR") == 0) { - const char* ip = HeaderIn("X-Real-IP"); - if (ip) - return ip; - return RemoteAddr(); - } else if (stricmp(key, "QUERY_STRING") == 0) { - return QueryString(); - } else if (stricmp(key, "SERVER_NAME") == 0) { - return ServerName().data(); - } else if (stricmp(key, "SERVER_PORT") == 0) { - return ServerPort().data(); - } else if (stricmp(key, "SCRIPT_NAME") == 0) { +TStringBuf TBaseServerRequestData::Environment(TStringBuf key) const { + TCaseInsensitiveStringBuf ciKey(key.data(), key.size()); + if (ciKey == "REMOTE_ADDR") { + const auto ip = HeaderIn("X-Real-IP"); + return ip ? *ip : RemoteAddr(); + } else if (ciKey == "QUERY_STRING") { + return Query(); + } else if (ciKey == "SERVER_NAME") { + return ServerName(); + } else if (ciKey == "SERVER_PORT") { + return ServerPort(); + } else if (ciKey == "SCRIPT_NAME") { return ScriptName(); } - return nullptr; + return {}; } -void TBaseServerRequestData::Clear() { + void TBaseServerRequestData::Clear() { HeadersIn_.clear(); - Addr = Path = Search = nullptr; - OrigSearch = {}; - SearchLength = 0; - Host.clear(); - Port.clear(); - CurPage.remove(); - ParseBuf.Clear(); - BeginTime = MicroSeconds(); + Addr_ = Nothing(); + Path_.clear(); + Query_ = {}; + OrigQuery_ = {}; + Host_.clear(); + Port_.clear(); + CurPage_.remove(); + ParseBuf_.clear(); + BeginTime_ = MicroSeconds(); } -const char* TBaseServerRequestData::GetCurPage() const { - if (!CurPage && Host) { - CurPage = "http://"; - CurPage += Host; - if (Port) { - CurPage += ':'; - CurPage += Port; +const TString& TBaseServerRequestData::GetCurPage() const { + if (!CurPage_ && Host_) { + CurPage_ = "http://"; + CurPage_ += Host_; + if (Port_) { + CurPage_ += ':'; + CurPage_ += Port_; } - CurPage += Path; - if (Search) { - CurPage += '?'; - CurPage += Search; + CurPage_ += Path_; + if (Query_) { + CurPage_ += '?'; + CurPage_ += Query_; } } - return CurPage.data(); + return CurPage_; } -bool TBaseServerRequestData::Parse(const char* origReq) { - size_t origReqLength = strlen(origReq); - ParseBuf.Assign(origReq, origReqLength + 1); - char* req = ParseBuf.Data(); +bool TBaseServerRequestData::Parse(TStringBuf origReqBuf) { + ParseBuf_.reserve(origReqBuf.size() + 1); + ParseBuf_.assign(origReqBuf.begin(), origReqBuf.end()); + ParseBuf_.push_back('\0'); + char* req = ParseBuf_.data(); while (*req == ' ' || *req == '\t') req++; @@ -161,20 +139,20 @@ bool TBaseServerRequestData::Parse(const char* origReq) { *fragment = 0; // ignore fragment else fragment = urlEnd; - Path = req; + char* path = req; // calculate Search length without additional strlen-ing - Search = strchr(Path, '?'); - if (Search) { - *Search++ = 0; - ptrdiff_t delta = fragment - Search; + char* query = strchr(path, '?'); + if (query) { + *query++ = 0; + ptrdiff_t delta = fragment - query; // indeed, second case is a parse error - SearchLength = (delta >= 0) ? delta : (urlEnd - Search); - Y_ASSERT(strlen(Search) == SearchLength); + Query_ = {query, static_cast<size_t>(delta >= 0 ? delta : (urlEnd - query))}; } else { - SearchLength = 0; + Query_ = {}; } - OrigSearch = {Search, SearchLength}; + Path_ = path; + OrigQuery_ = Query_; return true; } @@ -185,12 +163,11 @@ void TBaseServerRequestData::AddHeader(const TString& name, const TString& value if (stricmp(name.data(), "Host") == 0) { size_t hostLen = strcspn(value.data(), ":"); if (value[hostLen] == ':') - Port = value.substr(hostLen + 1); - Host = value.substr(0, hostLen); + Port_ = value.substr(hostLen + 1); + Host_ = value.substr(0, hostLen); } } -void TBaseServerRequestData::SetPath(const TString& path) { - PathStorage = TBuffer(path.data(), path.size() + 1); - Path = PathStorage.Data(); +void TBaseServerRequestData::SetPath(TString path) { + Path_ = std::move(path); } diff --git a/library/cpp/http/misc/httpreqdata.h b/library/cpp/http/misc/httpreqdata.h index 16e59c4d78..b5c9e446a9 100644 --- a/library/cpp/http/misc/httpreqdata.h +++ b/library/cpp/http/misc/httpreqdata.h @@ -13,52 +13,52 @@ #include <util/system/yassert.h> #include <util/generic/string.h> #include <util/datetime/base.h> -#include <util/generic/buffer.h> +#include <util/generic/vector.h> +#include <util/generic/maybe.h> using THttpHeadersContainer = THashMap<TString, TString, TCIOps, TCIOps>; class TBaseServerRequestData { public: TBaseServerRequestData(SOCKET s = INVALID_SOCKET); - TBaseServerRequestData(const char* qs, SOCKET s = INVALID_SOCKET); + TBaseServerRequestData(TStringBuf qs, SOCKET s = INVALID_SOCKET); void SetHost(const TString& host, ui16 port) { - Host = host; - Port = ToString(port); + Host_ = host; + Port_ = ToString(port); } const TString& ServerName() const { - return Host; + return Host_; } NAddr::IRemoteAddrPtr ServerAddress() const { - return NAddr::GetSockAddr(Socket); + return NAddr::GetSockAddr(Socket_); } const TString& ServerPort() const { - return Port; + return Port_; } - const char* ScriptName() const { - return Path; + TStringBuf ScriptName() const { + return Path_; } - const char* QueryString() const { - return Search; + TStringBuf Query() const { + return Query_; } - TStringBuf QueryStringBuf() const { - return TStringBuf(Search, SearchLength); + TStringBuf OrigQuery() const { + return OrigQuery_; } - TStringBuf OrigQueryStringBuf() const { - return OrigSearch; - } - - void AppendQueryString(const char* str, size_t length); - const char* RemoteAddr() const; + void AppendQueryString(TStringBuf str); + TStringBuf RemoteAddr() const; void SetRemoteAddr(TStringBuf addr); - const char* HeaderIn(TStringBuf key) const; + // Returns nullptr when the header does not exist + const TString* HeaderIn(TStringBuf key) const; + // Throws on missing header + TStringBuf HeaderInOrEmpty(TStringBuf key) const; const THttpHeadersContainer& HeadersIn() const { return HeadersIn_; @@ -69,39 +69,36 @@ public: } TString HeaderByIndex(size_t n) const noexcept; - const char* Environment(const char* key) const; + TStringBuf Environment(TStringBuf key) const; void Clear(); void SetSocket(SOCKET s) noexcept { - Socket = s; + Socket_ = s; } ui64 RequestBeginTime() const noexcept { - return BeginTime; + return BeginTime_; } - void SetPath(const TString& path); - const char* GetCurPage() const; - bool Parse(const char* req); + void SetPath(TString path); + const TString& GetCurPage() const; + bool Parse(TStringBuf req); void AddHeader(const TString& name, const TString& value); private: - TBuffer PathStorage; - mutable char* Addr; - TString Host; - TString Port; - char* Path; - char* Search; - size_t SearchLength; // length of Search - TStringBuf OrigSearch; + mutable TMaybe<TString> Addr_; + TString Host_; + TString Port_; + TString Path_; + TStringBuf Query_; + TStringBuf OrigQuery_; THttpHeadersContainer HeadersIn_; - mutable char AddrData[INET6_ADDRSTRLEN]; - SOCKET Socket; - ui64 BeginTime; - mutable TString CurPage; - TBuffer ParseBuf; - TBuffer ModifiedQueryString; + SOCKET Socket_; + ui64 BeginTime_; + mutable TString CurPage_; + TVector<char> ParseBuf_; + TString ModifiedQueryString_; }; class TServerRequestData: public TBaseServerRequestData { @@ -110,14 +107,14 @@ public: : TBaseServerRequestData(s) { } - TServerRequestData(const char* qs, SOCKET s = INVALID_SOCKET) + TServerRequestData(TStringBuf qs, SOCKET s = INVALID_SOCKET) : TBaseServerRequestData(qs, s) { Scan(); } void Scan() { - CgiParam.Scan(QueryStringBuf()); + CgiParam.Scan(Query()); } public: diff --git a/library/cpp/http/misc/httpreqdata_ut.cpp b/library/cpp/http/misc/httpreqdata_ut.cpp index e7f16ef27c..3e13046fd0 100644 --- a/library/cpp/http/misc/httpreqdata_ut.cpp +++ b/library/cpp/http/misc/httpreqdata_ut.cpp @@ -13,8 +13,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { sd.AddHeader("x-XxX", "y-yyy"); UNIT_ASSERT_VALUES_EQUAL(sd.HeadersCount(), 2); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("X-XX")), TStringBuf("y-yy")); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("X-XXX")), TStringBuf("y-yyy")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("X-XX"), TStringBuf("y-yy")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("X-XXX"), TStringBuf("y-yyy")); } Y_UNIT_TEST(ComplexHeaders) { @@ -23,21 +23,21 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { sd.AddHeader("x-Xx", "y-yy"); UNIT_ASSERT_VALUES_EQUAL(sd.HeadersCount(), 1); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("X-XX")), TStringBuf("y-yy")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("X-XX"), TStringBuf("y-yy")); sd.AddHeader("x-Xz", "y-yy"); UNIT_ASSERT_VALUES_EQUAL(sd.HeadersCount(), 2); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("X-Xz")), TStringBuf("y-yy")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("X-Xz"), TStringBuf("y-yy")); UNIT_ASSERT_VALUES_EQUAL(sd.ServerName(), "zzz"); UNIT_ASSERT_VALUES_EQUAL(sd.ServerPort(), "1"); sd.AddHeader("Host", "1234"); UNIT_ASSERT_VALUES_EQUAL(sd.HeadersCount(), 3); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("Host")), TStringBuf("1234")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("Host"), TStringBuf("1234")); UNIT_ASSERT_VALUES_EQUAL(sd.ServerName(), "1234"); sd.AddHeader("Host", "12345:678"); UNIT_ASSERT_VALUES_EQUAL(sd.HeadersCount(), 3); - UNIT_ASSERT_VALUES_EQUAL(TStringBuf(sd.HeaderIn("Host")), TStringBuf("12345:678")); + UNIT_ASSERT_VALUES_EQUAL(sd.HeaderInOrEmpty("Host"), TStringBuf("12345:678")); UNIT_ASSERT_VALUES_EQUAL(sd.ServerName(), "12345"); UNIT_ASSERT_VALUES_EQUAL(sd.ServerPort(), "678"); } @@ -51,8 +51,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { // This should work UNIT_ASSERT(rd.Parse(" /yandsearch?>a=fake&haha=da HTTP 1.1 OK")); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), ">a=fake&haha=da"); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), rd.OrigQueryStringBuf()); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), ">a=fake&haha=da"); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), rd.OrigQuery()); rd.Scan(); UNIT_ASSERT(rd.CgiParam.Has("gta", "fake")); @@ -66,8 +66,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { const TString qs("gta=fake&haha=da"); TServerRequestData rd(qs.c_str()); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), qs); - UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQueryStringBuf(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQuery(), qs); UNIT_ASSERT(rd.CgiParam.Has("gta")); UNIT_ASSERT(rd.CgiParam.Has("haha")); @@ -81,8 +81,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { TServerRequestData rd; rd.Parse(header.c_str()); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), qs); - UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQueryStringBuf(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQuery(), qs); rd.Scan(); UNIT_ASSERT(rd.CgiParam.Has("gta")); @@ -94,8 +94,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { TServerRequestData rd; rd.Parse(" /y#ndsearch?>a=fake&haha=da&uberParam=yes&q=? HTTP 1.1 OK"); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), ""); - UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQueryStringBuf(), ""); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), ""); + UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQuery(), ""); rd.Scan(); UNIT_ASSERT(rd.CgiParam.empty()); @@ -105,8 +105,8 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { TServerRequestData rd; rd.Parse(" /yandsearch?#"); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), ""); - UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQueryStringBuf(), ""); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), ""); + UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQuery(), ""); rd.Scan(); UNIT_ASSERT(rd.CgiParam.empty()); @@ -119,14 +119,14 @@ Y_UNIT_TEST_SUITE(TRequestServerDataTest) { UNIT_ASSERT(rd.CgiParam.Has("gta", "fake")); UNIT_ASSERT(rd.CgiParam.Has("haha", "da")); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), qs); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), rd.OrigQueryStringBuf()); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), rd.OrigQuery()); constexpr TStringBuf appendix = "gta=true>a=new"; - rd.AppendQueryString(appendix.data(), appendix.size()); + rd.AppendQueryString(appendix); - UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), qs + '&' + appendix); - UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQueryStringBuf(), qs); + UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), qs + '&' + appendix); + UNIT_ASSERT_STRINGS_EQUAL(rd.OrigQuery(), qs); rd.Scan(); diff --git a/library/cpp/http/misc/ya.make b/library/cpp/http/misc/ya.make index fceb3cf79c..7eb4481218 100644 --- a/library/cpp/http/misc/ya.make +++ b/library/cpp/http/misc/ya.make @@ -15,6 +15,7 @@ SRCS( ) PEERDIR( + library/cpp/case_insensitive_string library/cpp/cgiparam library/cpp/digest/lower_case ) |