aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/http/client/request.cpp
diff options
context:
space:
mode:
authorqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
committerqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
commit22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch)
treebffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/http/client/request.cpp
parent332b99e2173f0425444abb759eebcb2fafaa9209 (diff)
downloadydb-22f8ae0e3f5d68b92aecccdf96c1d841a0334311.tar.gz
validate canons without yatest_common
Diffstat (limited to 'library/cpp/http/client/request.cpp')
-rw-r--r--library/cpp/http/client/request.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/library/cpp/http/client/request.cpp b/library/cpp/http/client/request.cpp
new file mode 100644
index 0000000000..08cf73da7b
--- /dev/null
+++ b/library/cpp/http/client/request.cpp
@@ -0,0 +1,249 @@
+#include "request.h"
+
+#include <library/cpp/http/client/fetch/codes.h>
+#include <library/cpp/uri/location.h>
+
+#include <util/string/ascii.h>
+
+namespace NHttp {
+ static const ui64 URI_PARSE_FLAGS =
+ (NUri::TFeature::FeaturesRecommended | NUri::TFeature::FeatureConvertHostIDN | NUri::TFeature::FeatureEncodeExtendedDelim | NUri::TFeature::FeatureEncodePercent) & ~NUri::TFeature::FeatureHashBangToEscapedFragment;
+
+ /// Generates sequence of unique identifiers of requests.
+ static TAtomic RequestCounter = 0;
+
+ TFetchRequest::TRedirects::TRedirects(bool parseCookie) {
+ if (parseCookie) {
+ CookieStore.Reset(new NHttp::TCookieStore);
+ }
+ }
+
+ size_t TFetchRequest::TRedirects::Level() const {
+ return this->size();
+ }
+
+ void TFetchRequest::TRedirects::ParseCookies(const TString& url,
+ const THttpHeaders& headers) {
+ if (CookieStore) {
+ NUri::TUri uri;
+ if (uri.Parse(url, URI_PARSE_FLAGS) != NUri::TUri::ParsedOK) {
+ return;
+ }
+ if (!uri.IsValidGlobal()) {
+ return;
+ }
+
+ for (THttpHeaders::TConstIterator it = headers.Begin(); it != headers.End(); it++) {
+ if (AsciiEqualsIgnoreCase(it->Name(), TStringBuf("Set-Cookie"))) {
+ CookieStore->SetCookie(uri, it->Value());
+ }
+ }
+ }
+ }
+
+ TFetchRequest::TFetchRequest(const TString& url, const TFetchOptions& options)
+ : Url_(url)
+ , Options_(options)
+ , Id_(AtomicIncrement(RequestCounter))
+ , RetryAttempts_(options.RetryCount)
+ , RetryDelay_(options.RetryDelay)
+ , Cancel_(false)
+ , CurrentUrl_(url)
+ {
+ }
+
+ TFetchRequest::TFetchRequest(const TString& url, TVector<TString> headers, const TFetchOptions& options)
+ : TFetchRequest(url, options)
+ {
+ Headers_ = std::move(headers);
+ }
+
+ void TFetchRequest::Cancel() {
+ AtomicSet(Cancel_, 1);
+ }
+
+ NHttpFetcher::TRequestRef TFetchRequest::GetRequestImpl() const {
+ NHttpFetcher::TRequestRef req(new NHttpFetcher::TRequest(CurrentUrl_));
+
+ req->UnixSocketPath = Options_.UnixSocketPath;
+ req->Login = Options_.Login;
+ req->Password = Options_.Password;
+ req->OAuthToken = Options_.OAuthToken;
+ req->OnlyHeaders = Options_.OnlyHeaders;
+ req->CustomHost = Options_.CustomHost;
+ req->Method = Options_.Method;
+ req->OAuthToken = Options_.OAuthToken;
+ req->ContentType = Options_.ContentType;
+ req->PostData = Options_.PostData;
+ req->UserAgent = Options_.UserAgent;
+ req->ExtraHeaders.assign(Headers_.begin(), Headers_.end());
+ req->NeedDataCallback = OnPartialRead_;
+ req->Deadline = TInstant::Now() + Options_.Timeout;
+
+ if (Options_.ConnectTimeout) {
+ req->ConnectTimeout = Options_.ConnectTimeout;
+ }
+
+ if (Options_.MaxHeaderSize) {
+ req->MaxHeaderSize = Options_.MaxHeaderSize.GetRef();
+ }
+ if (Options_.MaxBodySize) {
+ req->MaxBodySize = Options_.MaxBodySize.GetRef();
+ }
+
+ if (Redirects_ && Redirects_->CookieStore) {
+ NUri::TUri uri;
+ if (uri.Parse(CurrentUrl_, URI_PARSE_FLAGS) == NUri::TUri::ParsedOK) {
+ if (TString cookies = Redirects_->CookieStore->GetCookieString(uri)) {
+ req->ExtraHeaders.push_back("Cookie: " + cookies + "\r\n");
+ }
+ }
+ }
+
+ return req;
+ }
+
+ bool TFetchRequest::IsValid() const {
+ auto g(Guard(Lock_));
+ return IsValidNoLock();
+ }
+
+ bool TFetchRequest::IsCancelled() const {
+ return AtomicGet(Cancel_);
+ }
+
+ bool TFetchRequest::GetForceReconnect() const {
+ return Options_.ForceReconnect;
+ }
+
+ NHttpFetcher::TResultRef TFetchRequest::MakeResult() const {
+ if (Exception_) {
+ std::rethrow_exception(Exception_);
+ }
+
+ return Result_;
+ }
+
+ void TFetchRequest::SetException(std::exception_ptr ptr) {
+ Exception_ = ptr;
+ }
+
+ void TFetchRequest::SetCallback(NHttpFetcher::TCallBack cb) {
+ Cb_ = cb;
+ }
+
+ void TFetchRequest::SetOnFail(TOnFail cb) {
+ OnFail_ = cb;
+ }
+
+ void TFetchRequest::SetOnRedirect(TOnRedirect cb) {
+ OnRedirect_ = cb;
+ }
+
+ void TFetchRequest::SetOnPartialRead(NHttpFetcher::TNeedDataCallback cb) {
+ OnPartialRead_ = cb;
+ }
+
+ bool TFetchRequest::WaitT(TDuration timeout) {
+ TCondVar c;
+
+ {
+ auto g(Guard(Lock_));
+
+ if (IsValidNoLock()) {
+ THolder<TWaitState> state(new TWaitState(&c));
+ Awaitings_.PushBack(state.Get());
+ if (!c.WaitT(Lock_, timeout)) {
+ AtomicSet(Cancel_, 1);
+ // Удаляем элемент из очереди ожидания в случае, если
+ // истёк установленный период времени.
+ state->Unlink();
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool TFetchRequest::IsValidNoLock() const {
+ return !Result_ && !Exception_ && !AtomicGet(Cancel_);
+ }
+
+ void TFetchRequest::Reply(NHttpFetcher::TResultRef result) {
+ NHttpFetcher::TCallBack cb;
+
+ {
+ auto g(Guard(Lock_));
+
+ cb.swap(Cb_);
+ Result_.Swap(result);
+
+ if (Redirects_) {
+ Result_->Redirects.assign(Redirects_->begin(), Redirects_->end());
+ }
+ }
+
+ if (cb) {
+ try {
+ cb(Result_);
+ } catch (...) {
+ SetException(std::current_exception());
+ }
+ }
+
+ {
+ auto g(Guard(Lock_));
+ while (!Awaitings_.Empty()) {
+ Awaitings_.PopFront()->Signal();
+ }
+ }
+ }
+
+ TDuration TFetchRequest::OnResponse(NHttpFetcher::TResultRef result) {
+ if (AtomicGet(Cancel_)) {
+ goto finish;
+ }
+
+ if (NHttpFetcher::IsRedirectCode(result->Code)) {
+ auto location = NUri::ResolveRedirectLocation(CurrentUrl_, result->Location);
+
+ if (!Redirects_) {
+ Redirects_.Reset(new TRedirects(true));
+ }
+ result->ResolvedUrl = location;
+ Redirects_->push_back(result);
+
+ if (Options_.UseCookie) {
+ Redirects_->ParseCookies(CurrentUrl_, result->Headers);
+ }
+
+ if (Redirects_->Level() < Options_.RedirectDepth) {
+ if (OnRedirect_) {
+ if (!OnRedirect_(CurrentUrl_, location)) {
+ goto finish;
+ }
+ }
+
+ CurrentUrl_ = location;
+ return TDuration::Zero();
+ }
+ } else if (!NHttpFetcher::IsSuccessCode(result->Code)) {
+ bool again = RetryAttempts_ > 0;
+
+ if (OnFail_ && !OnFail_(result)) {
+ again = false;
+ }
+ if (again) {
+ RetryAttempts_--;
+ return RetryDelay_;
+ }
+ }
+
+ finish:
+ Reply(result);
+
+ return TDuration::Zero();
+ }
+
+}