Skip to content

Commit 9f9aea5

Browse files
authored
Merge pull request #25 from adam-p/cookies
add cookie support
2 parents 66c6733 + f6a2f4e commit 9f9aea5

File tree

7 files changed

+115
-9
lines changed

7 files changed

+115
-9
lines changed

psicash.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ static constexpr const char* kLandingPageParamKey = "psicash";
6868
static constexpr const char* kMethodGET = "GET";
6969
static constexpr const char* kMethodPOST = "POST";
7070

71-
static constexpr const char* kDateHeaderKey = "Date";
72-
7371
//
7472
// PsiCash class implementation
7573
//
@@ -584,16 +582,15 @@ Result<HTTPResult> PsiCash::MakeHTTPRequestWithRetry(
584582
return WrapError(req_params.error(), "BuildRequestParams failed");
585583
}
586584

587-
588585
http_result = make_http_request_fn_(*req_params);
589586

590587
// Error state sanity check
591588
if (http_result.code < 0 && http_result.error.empty()) {
592589
return MakeCriticalError("HTTP result code is negative but no error message provided");
593590
}
594591

595-
// We just got a fresh server timestamp, so set the server time diff
596-
auto date_header = utils::FindHeaderValue(http_result.headers, kDateHeaderKey);
592+
// We just got a fresh server timestamp (Date header), so set the server time diff
593+
auto date_header = utils::FindHeaderValue(http_result.headers, "Date");
597594
if (!date_header.empty()) {
598595
datetime::DateTime server_datetime;
599596
if (server_datetime.FromRFC7231(date_header)) {
@@ -603,6 +600,11 @@ Result<HTTPResult> PsiCash::MakeHTTPRequestWithRetry(
603600
// else: we're not going to raise the error
604601
}
605602

603+
// Store/update any cookies we received, to send in the next request.
604+
// We're not going to cause a general error if the cookies fail to save but
605+
// everything else is successful.
606+
(void)user_data_->SetCookies(utils::GetCookies(http_result.headers));
607+
606608
if (http_result.code < 0) {
607609
// Something happened that prevented the request from nominally succeeding.
608610
// If the native code indicates that this is a "recoverable error" (such as
@@ -654,6 +656,7 @@ Result<HTTPParams> PsiCash::BuildRequestParams(
654656
params.headers = additional_headers;
655657
params.headers["Accept"] = "application/json";
656658
params.headers["User-Agent"] = user_agent_;
659+
params.headers["Cookie"] = user_data_->GetCookies();
657660

658661
if (include_auth_tokens) {
659662
params.headers["X-PsiCash-Auth"] = CommaDelimitTokens({});

userdata.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ static constexpr const char* LAST_TRANSACTION_ID = "lastTransactionID";
6464
static const auto kLastTransactionIDPtr = kUserPtr / LAST_TRANSACTION_ID;
6565
static const char* REQUEST_METADATA = "requestMetadata";
6666
const json::json_pointer kRequestMetadataPtr = kUserPtr / REQUEST_METADATA; // used in header, so not static
67+
static constexpr const char* COOKIES = "cookies";
68+
static const auto kCookiesPtr = kUserPtr / COOKIES;
6769

6870

6971
// These are the possible token types.
@@ -500,6 +502,18 @@ error::Error UserData::SetLocale(const std::string& v) {
500502
return PassError(datastore_.Set(kLocalePtr, v));
501503
}
502504

505+
std::string UserData::GetCookies() const {
506+
auto v = datastore_.Get<string>(kCookiesPtr);
507+
if (!v) {
508+
return "";
509+
}
510+
return *v;
511+
}
512+
513+
error::Error UserData::SetCookies(const std::string& v) {
514+
return PassError(datastore_.Set(kCookiesPtr, v));
515+
}
516+
503517
json UserData::GetStashedRequestMetadata() const {
504518
SYNCHRONIZE(stashed_request_metadata_mutex_);
505519
auto stashed = stashed_request_metadata_;

userdata.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ class UserData {
148148
std::string GetLocale() const;
149149
error::Error SetLocale(const std::string& v);
150150

151+
std::string GetCookies() const;
152+
error::Error SetCookies(const std::string& v);
153+
151154
protected:
152155
/// Modifies the purchases in the argument.
153156
void UpdatePurchasesLocalTimeExpiry(Purchases& purchases) const;

userdata_test.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,26 @@ TEST_F(TestUserData, Locale)
803803
ASSERT_EQ(v, "");
804804
}
805805

806+
TEST_F(TestUserData, Cookies)
807+
{
808+
UserData ud;
809+
auto err = ud.Init(GetTempDir().c_str(), dev);
810+
ASSERT_FALSE(err);
811+
812+
auto v = ud.GetCookies();
813+
ASSERT_THAT(v, IsEmpty());
814+
815+
err = ud.SetCookies("x=y; a=b; m=n");
816+
ASSERT_FALSE(err);
817+
v = ud.GetCookies();
818+
ASSERT_EQ(v, "x=y; a=b; m=n");
819+
820+
err = ud.SetCookies("");
821+
ASSERT_FALSE(err);
822+
v = ud.GetCookies();
823+
ASSERT_EQ(v, "");
824+
}
825+
806826
TEST_F(TestUserData, Transaction)
807827
{
808828
UserData ud;

utils.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,38 @@ static string ToLowerASCII(const string& s) {
9494
return ss.str();
9595
}
9696

97-
string FindHeaderValue(const map<string, vector<string>>& headers, const string& key) {
98-
auto lower_key = ToLowerASCII(key);
97+
static vector<string> FindHeaderValues(const map<string, vector<string>>& headers, const string& key) {
98+
const auto lower_key = ToLowerASCII(key);
9999
for (const auto& entry : headers) {
100100
if (lower_key == ToLowerASCII(entry.first)) {
101-
return entry.second.empty() ? "" : entry.second.front();
101+
return entry.second;
102+
}
103+
}
104+
return {};
105+
}
106+
107+
string FindHeaderValue(const map<string, vector<string>>& headers, const string& key) {
108+
auto vec = FindHeaderValues(headers, key);
109+
return vec.empty() ? "" : vec.front();
110+
}
111+
112+
string GetCookies(const map<string, vector<string>>& headers) {
113+
// Set-Cookie header values are of the form:
114+
// AWSALB=abcxyz; Expires=Tue, 03 May 2022 19:47:19 GMT; Path=/
115+
// We only care about the cookie name and the value.
116+
117+
stringstream res;
118+
bool first = true;
119+
for (const auto& c : FindHeaderValues(headers, "Set-Cookie")) {
120+
if (!first) {
121+
res << "; ";
102122
}
123+
first = false;
124+
125+
auto semi = c.find_first_of(';');
126+
res << TrimCopy(c.substr(0, semi));
103127
}
104-
return "";
128+
return res.str();
105129
}
106130

107131
// Adapted from https://stackoverflow.com/a/22986486/729729

utils.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ std::string RandomID();
6666
/// If there are multiple header values for the key, the first one is returned.
6767
std::string FindHeaderValue(const std::map<std::string, std::vector<std::string>>& headers, const std::string& key);
6868

69+
/// Returns all cookie name=values in the Set-Cookie headers in a single
70+
/// semicolon-separated string (suitable for sending in a request Cookie header).
71+
std::string GetCookies(const std::map<std::string, std::vector<std::string>>& headers);
72+
6973
// From https://stackoverflow.com/a/5289170/729729
7074
/// note: delimiter cannot contain NUL characters
7175
template <typename Range, typename Value = typename Range::value_type>

utils_test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
*/
1919

2020
#include "gtest/gtest.h"
21+
#include "gmock/gmock.h"
2122
#include "utils.hpp"
2223

2324
using namespace std;
2425
using namespace utils;
26+
using namespace testing;
2527

2628
TEST(TestStringer, SingleValue) {
2729
auto s = Stringer("s");
@@ -55,4 +57,40 @@ TEST(TestFindHeaderValue, Simple) {
5557
headers = {{"a", {"xyz"}}, {"c", {"abc", "def"}}, {"DATE", {"expected", "second"}}};
5658
s = FindHeaderValue(headers, "Date");
5759
ASSERT_EQ(s, "expected");
60+
61+
headers = {{"a", {"xyz"}}, {"c", {"abc", "def"}}, {"DATE", {"expected", "second"}}};
62+
s = FindHeaderValue(headers, "Nope");
63+
ASSERT_EQ(s, "");
64+
}
65+
66+
TEST(TestGetCookies, Simple) {
67+
map<string, vector<string>> headers;
68+
69+
headers = {{"a", {"xyz"}}, {"set-COOKIE", {"AWSALBCORS=qxg5PeVRnxutG8kvdnISQvQM+PWqFzqoVZGJcyZh9c6su3O+u1121WEFwZ6DAEtVaKq6ufOzUIfAL8qRmUuSya5ODUxJOC9m3+006HBi71pSk6T88oiMgva0IOvi; Expires=Mon, 02 May 2022 20:53:02 GMT; Path=/; SameSite=None; Secure", "k1=v1", "k2=v2;"}}};
70+
auto v = GetCookies(headers);
71+
ASSERT_EQ(v, "AWSALBCORS=qxg5PeVRnxutG8kvdnISQvQM+PWqFzqoVZGJcyZh9c6su3O+u1121WEFwZ6DAEtVaKq6ufOzUIfAL8qRmUuSya5ODUxJOC9m3+006HBi71pSk6T88oiMgva0IOvi; k1=v1; k2=v2");
72+
73+
headers = {{"a", {"xyz"}}};
74+
v = GetCookies(headers);
75+
ASSERT_EQ(v, "");
76+
77+
headers = {{"a", {"xyz"}}, {"Set-Cookie", {}}};
78+
v = GetCookies(headers);
79+
ASSERT_EQ(v, "");
80+
81+
headers = {{"a", {"xyz"}}, {"Set-Cookie", {""}}};
82+
v = GetCookies(headers);
83+
ASSERT_EQ(v, "");
84+
85+
headers = {{"a", {"xyz"}}, {"Set-Cookie", {";"}}};
86+
v = GetCookies(headers);
87+
ASSERT_EQ(v, "");
88+
89+
headers = {{"a", {"xyz"}}, {"Set-Cookie", {"!;!;!"}}};
90+
v = GetCookies(headers);
91+
ASSERT_EQ(v, "!");
92+
93+
headers = {{"a", {"xyz"}}, {"Set-Cookie", {" x=y "}}};
94+
v = GetCookies(headers);
95+
ASSERT_EQ(v, "x=y");
5896
}

0 commit comments

Comments
 (0)