diff options
author | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
---|---|---|
committer | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
commit | 22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch) | |
tree | bffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/http/client/request.cpp | |
parent | 332b99e2173f0425444abb759eebcb2fafaa9209 (diff) | |
download | ydb-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.cpp | 249 |
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(); + } + +} |