aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-02-17 12:04:09 +0300
committerarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-02-17 12:04:09 +0300
commit2c8e314f8fff8633fe2cf026badfbf6180845ae0 (patch)
treec3b650d13934ec1315e3660d60fd2275f09b03a7 /library/cpp
parenta49ae9d891c35087b242c854f69880fd9fecbddd (diff)
downloadydb-2c8e314f8fff8633fe2cf026badfbf6180845ae0.tar.gz
intermediate changes
ref:d5f945ecdc1f5af1ad57e12787c6b8ed1a9f0f12
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/case_insensitive_string/case_insensitive_char_traits.cpp34
-rw-r--r--library/cpp/case_insensitive_string/case_insensitive_char_traits.h30
-rw-r--r--library/cpp/case_insensitive_string/case_insensitive_string.cpp22
-rw-r--r--library/cpp/case_insensitive_string/case_insensitive_string.h37
-rw-r--r--library/cpp/case_insensitive_string/case_insensitive_string_ut.cpp65
-rw-r--r--library/cpp/case_insensitive_string/ut/ya.make9
-rw-r--r--library/cpp/case_insensitive_string/ya.make17
-rw-r--r--library/cpp/digest/murmur/murmur.cpp1
-rw-r--r--library/cpp/digest/murmur/murmur.h100
-rw-r--r--library/cpp/digest/murmur/murmur_ut.cpp61
-rw-r--r--library/cpp/digest/murmur/ut/ya.make15
-rw-r--r--library/cpp/digest/murmur/ya.make9
-rw-r--r--library/cpp/http/misc/httpreqdata.cpp211
-rw-r--r--library/cpp/http/misc/httpreqdata.h81
-rw-r--r--library/cpp/http/misc/httpreqdata_ut.cpp42
-rw-r--r--library/cpp/http/misc/ya.make1
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?&gta=fake&haha=da HTTP 1.1 OK"));
- UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), "&gta=fake&haha=da");
- UNIT_ASSERT_STRINGS_EQUAL(rd.QueryStringBuf(), rd.OrigQueryStringBuf());
+ UNIT_ASSERT_STRINGS_EQUAL(rd.Query(), "&gta=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?&gta=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&gta=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
)