diff options
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/http_auth.h | 3 | ||||
-rw-r--r-- | net/http/http_cache.cc | 7 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 53 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 20 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 78 | ||||
-rw-r--r-- | net/http/http_transaction.h | 8 | ||||
-rw-r--r-- | net/http/http_transaction_unittest.h | 4 | ||||
-rw-r--r-- | net/http/http_util.cc | 25 | ||||
-rw-r--r-- | net/http/http_util.h | 8 | ||||
-rw-r--r-- | net/http/http_util_unittest.cc | 23 |
10 files changed, 180 insertions, 49 deletions
diff --git a/net/http/http_auth.h b/net/http/http_auth.h index 8f594e2..5557694 100644 --- a/net/http/http_auth.h +++ b/net/http/http_auth.h @@ -21,6 +21,9 @@ class HttpAuth { // Http authentication can be done the the proxy server, origin server, // or both. This enum tracks who the target is. enum Target { + AUTH_NONE = -1, + // We depend on the valid targets (!= AUTH_NONE) being usable as indexes + // in an array, so start from 0. AUTH_PROXY = 0, AUTH_SERVER = 1, }; diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index 38a8f23..46bb64c 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -166,6 +166,7 @@ class HttpCache::Transaction virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, CompletionCallback* callback); + virtual bool IsReadyToRestartForAuth(); virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback*); virtual const HttpResponseInfo* GetResponseInfo() const; virtual LoadState GetLoadState() const; @@ -402,6 +403,12 @@ int HttpCache::Transaction::RestartWithAuth( return rv; } +bool HttpCache::Transaction::IsReadyToRestartForAuth() { + if (!network_trans_.get()) + return false; + return network_trans_->IsReadyToRestartForAuth(); +} + int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(buf); diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index fc43066..77a5e19 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -35,7 +35,8 @@ namespace net { HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session, ClientSocketFactory* csf) - : ALLOW_THIS_IN_INITIALIZER_LIST( + : pending_auth_target_(HttpAuth::AUTH_NONE), + ALLOW_THIS_IN_INITIALIZER_LIST( io_callback_(this, &HttpNetworkTransaction::OnIOComplete)), user_callback_(NULL), session_(session), @@ -92,20 +93,24 @@ int HttpNetworkTransaction::RestartWithAuth( const std::wstring& username, const std::wstring& password, CompletionCallback* callback) { + HttpAuth::Target target = pending_auth_target_; + if (target == HttpAuth::AUTH_NONE) { + NOTREACHED(); + return ERR_UNEXPECTED; + } - DCHECK(NeedAuth(HttpAuth::AUTH_PROXY) || - NeedAuth(HttpAuth::AUTH_SERVER)); + pending_auth_target_ = HttpAuth::AUTH_NONE; - // Figure out whether this username password is for proxy or server. - // Proxy gets set first, then server. - HttpAuth::Target target = NeedAuth(HttpAuth::AUTH_PROXY) ? - HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER; + DCHECK(auth_identity_[target].invalid || + (username.empty() && password.empty())); - // Update the username/password. - auth_identity_[target].source = HttpAuth::IDENT_SRC_EXTERNAL; - auth_identity_[target].invalid = false; - auth_identity_[target].username = username; - auth_identity_[target].password = password; + if (auth_identity_[target].invalid) { + // Update the username/password. + auth_identity_[target].source = HttpAuth::IDENT_SRC_EXTERNAL; + auth_identity_[target].invalid = false; + auth_identity_[target].username = username; + auth_identity_[target].password = password; + } PrepareForAuthRestart(target); @@ -1097,8 +1102,6 @@ int HttpNetworkTransaction::DidReadResponseHeaders() { } int rv = HandleAuthChallenge(); - if (rv == WILL_RESTART_TRANSACTION) - return OK; if (rv != OK) return rv; @@ -1182,6 +1185,7 @@ int HttpNetworkTransaction::HandleIOError(int error) { } void HttpNetworkTransaction::ResetStateForRestart() { + pending_auth_target_ = HttpAuth::AUTH_NONE; header_buf_.reset(); header_buf_capacity_ = 0; header_buf_len_ = 0; @@ -1473,11 +1477,10 @@ int HttpNetworkTransaction::HandleAuthChallenge() { return OK; } - bool has_identity_to_try; if (auth_handler_[target]->NeedsIdentity()) { // Pick a new auth identity to try, by looking to the URL and auth cache. // If an identity to try is found, it is saved to auth_identity_[target]. - has_identity_to_try = SelectNextAuthIdentityToTry(target); + SelectNextAuthIdentityToTry(target); } else { // Proceed with a null identity. // @@ -1487,19 +1490,17 @@ int HttpNetworkTransaction::HandleAuthChallenge() { auth_identity_[target].invalid = false; auth_identity_[target].username.clear(); auth_identity_[target].password.clear(); - has_identity_to_try = true; } - DCHECK(has_identity_to_try == !auth_identity_[target].invalid); - if (has_identity_to_try) { - DCHECK(user_callback_); - PrepareForAuthRestart(target); - return WILL_RESTART_TRANSACTION; - } + // Make a note that we are waiting for auth. This variable is inspected + // when the client calls RestartWithAuth() to pick up where we left off. + pending_auth_target_ = target; - // We have exhausted all identity possibilities, all we can do now is - // pass the challenge information back to the client. - PopulateAuthChallenge(target); + if (auth_identity_[target].invalid) { + // We have exhausted all identity possibilities, all we can do now is + // pass the challenge information back to the client. + PopulateAuthChallenge(target); + } return OK; } diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 088a090..939a900 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -41,6 +41,11 @@ class HttpNetworkTransaction : public HttpTransaction { virtual int RestartWithAuth(const std::wstring& username, const std::wstring& password, CompletionCallback* callback); + virtual bool IsReadyToRestartForAuth() { + return pending_auth_target_ != HttpAuth::AUTH_NONE && + HaveAuth(pending_auth_target_); + } + virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); virtual const HttpResponseInfo* GetResponseInfo() const; virtual LoadState GetLoadState() const; @@ -152,10 +157,8 @@ class HttpNetworkTransaction : public HttpTransaction { void AddAuthorizationHeader(HttpAuth::Target target); // Handles HTTP status code 401 or 407. - // HandleAuthChallenge() returns a network error code, or OK, or - // WILL_RESTART_TRANSACTION. The latter indicates that the state machine has - // been updated to restart the transaction with a new auth attempt. - enum { WILL_RESTART_TRANSACTION = 1 }; + // HandleAuthChallenge() returns a network error code, or OK on success. + // May update |pending_auth_target_| or |response_.auth_challenge|. int HandleAuthChallenge(); // Populates response_.auth_challenge with the challenge information, so that @@ -177,10 +180,6 @@ class HttpNetworkTransaction : public HttpTransaction { // auth_handler_[target] with the cache entry's data and returns true. bool SelectPreemptiveAuth(HttpAuth::Target target); - bool NeedAuth(HttpAuth::Target target) const { - return auth_handler_[target].get() && auth_identity_[target].invalid; - } - bool HaveAuth(HttpAuth::Target target) const { return auth_handler_[target].get() && !auth_identity_[target].invalid; } @@ -206,6 +205,11 @@ class HttpNetworkTransaction : public HttpTransaction { // a number of places (url, cache, prompt). HttpAuth::Identity auth_identity_[2]; + // Whether this transaction is waiting for proxy auth, server auth, or is + // not waiting for any auth at all. |pending_auth_target_| is read and + // cleared by RestartWithAuth(). + HttpAuth::Target pending_auth_target_; + CompletionCallbackImpl<HttpNetworkTransaction> io_callback_; CompletionCallback* user_callback_; diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index a9fa974..4e8209c 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -1651,7 +1651,7 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { }; MockWrite data_writes2[] = { - // After automatically restarting with a null identity, this is the + // After restarting with a null identity, this is the // request we should be issuing -- the final header line contains a Type // 1 message. MockWrite("GET /kids/login.aspx HTTP/1.1\r\n" @@ -1715,6 +1715,14 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { rv = callback1.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback2); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const net::HttpResponseInfo* response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); @@ -1726,12 +1734,12 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); - TestCompletionCallback callback2; + TestCompletionCallback callback3; - rv = trans->RestartWithAuth(L"testing-ntlm", L"testing-ntlm", &callback2); + rv = trans->RestartWithAuth(L"testing-ntlm", L"testing-ntlm", &callback3); EXPECT_EQ(net::ERR_IO_PENDING, rv); - rv = callback2.WaitForResult(); + rv = callback3.WaitForResult(); EXPECT_EQ(net::OK, rv); response = trans->GetResponseInfo(); @@ -1773,7 +1781,7 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { }; MockWrite data_writes2[] = { - // After automatically restarting with a null identity, this is the + // After restarting with a null identity, this is the // request we should be issuing -- the final header line contains a Type // 1 message. MockWrite("GET /kids/login.aspx HTTP/1.1\r\n" @@ -1824,7 +1832,7 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { }; MockWrite data_writes3[] = { - // After automatically restarting with a null identity, this is the + // After restarting with a null identity, this is the // request we should be issuing -- the final header line contains a Type // 1 message. MockWrite("GET /kids/login.aspx HTTP/1.1\r\n" @@ -1892,6 +1900,14 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { rv = callback1.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback2); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const net::HttpResponseInfo* response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); @@ -1903,14 +1919,22 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); - TestCompletionCallback callback2; + TestCompletionCallback callback3; // Enter the wrong password. - rv = trans->RestartWithAuth(L"testing-ntlm", L"wrongpassword", &callback2); + rv = trans->RestartWithAuth(L"testing-ntlm", L"wrongpassword", &callback3); EXPECT_EQ(net::ERR_IO_PENDING, rv); - rv = callback2.WaitForResult(); + rv = callback3.WaitForResult(); + EXPECT_EQ(net::OK, rv); + + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback4; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback4); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback4.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); @@ -1923,13 +1947,13 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); - TestCompletionCallback callback3; + TestCompletionCallback callback5; // Now enter the right password. - rv = trans->RestartWithAuth(L"testing-ntlm", L"testing-ntlm", &callback3); + rv = trans->RestartWithAuth(L"testing-ntlm", L"testing-ntlm", &callback5); EXPECT_EQ(net::ERR_IO_PENDING, rv); - rv = callback3.WaitForResult(); + rv = callback5.WaitForResult(); EXPECT_EQ(net::OK, rv); response = trans->GetResponseInfo(); @@ -2187,6 +2211,14 @@ TEST_F(HttpNetworkTransactionTest, AuthIdentityInUrl) { rv = callback1.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback2); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const net::HttpResponseInfo* response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); @@ -2490,6 +2522,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { rv = callback1.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback2); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const net::HttpResponseInfo* response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -2577,6 +2617,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { rv = callback1.WaitForResult(); EXPECT_EQ(net::OK, rv); + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(std::wstring(), std::wstring(), &callback2); + EXPECT_EQ(net::ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(net::OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const net::HttpResponseInfo* response = trans->GetResponseInfo(); EXPECT_FALSE(response == NULL); @@ -2589,12 +2637,12 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm); EXPECT_EQ(L"basic", response->auth_challenge->scheme); - TestCompletionCallback callback2; + TestCompletionCallback callback3; - rv = trans->RestartWithAuth(L"foo3", L"bar3", &callback2); + rv = trans->RestartWithAuth(L"foo3", L"bar3", &callback3); EXPECT_EQ(net::ERR_IO_PENDING, rv); - rv = callback2.WaitForResult(); + rv = callback3.WaitForResult(); EXPECT_EQ(net::OK, rv); response = trans->GetResponseInfo(); diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h index 2c857ed..cc7dd92 100644 --- a/net/http/http_transaction.h +++ b/net/http/http_transaction.h @@ -55,6 +55,14 @@ class HttpTransaction { const std::wstring& password, CompletionCallback* callback) = 0; + // Returns true if auth is ready to be continued. Callers should check + // this value anytime Start() completes: if it is true, the transaction + // can be resumed with RestartWithAuth(L"", L"", callback) to resume + // the automatic auth exchange. This notification gives the caller a + // chance to process the response headers from all of the intermediate + // restarts needed for authentication. + virtual bool IsReadyToRestartForAuth() = 0; + // Once response info is available for the transaction, response data may be // read by calling this method. // diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h index e530423..d0a51b0 100644 --- a/net/http/http_transaction_unittest.h +++ b/net/http/http_transaction_unittest.h @@ -240,6 +240,10 @@ class MockNetworkTransaction : public net::HttpTransaction { return net::ERR_FAILED; } + virtual bool IsReadyToRestartForAuth() { + return false; + } + virtual int Read(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback) { int data_len = static_cast<int>(data_.size()); diff --git a/net/http/http_util.cc b/net/http/http_util.cc index 7094b03..500222a 100644 --- a/net/http/http_util.cc +++ b/net/http/http_util.cc @@ -218,6 +218,31 @@ bool HttpUtil::HasHeader(const std::string& headers, const char* name) { } // static +std::string HttpUtil::StripHeaders(const std::string& headers, + const char* const headers_to_remove[], + size_t headers_to_remove_len) { + std::string stripped_headers; + net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); + + while (it.GetNext()) { + bool should_remove = false; + for (size_t i = 0; i < headers_to_remove_len; ++i) { + if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(), + headers_to_remove[i])) { + should_remove = true; + break; + } + } + if (!should_remove) { + // Assume that name and values are on the same line. + stripped_headers.append(it.name_begin(), it.values_end()); + stripped_headers.append("\r\n"); + } + } + return stripped_headers; +} + +// static bool HttpUtil::IsNonCoalescingHeader(string::const_iterator name_begin, string::const_iterator name_end) { // NOTE: "set-cookie2" headers do not support expires attributes, so we don't diff --git a/net/http/http_util.h b/net/http/http_util.h index 8bf055f..ade6241 100644 --- a/net/http/http_util.h +++ b/net/http/http_util.h @@ -47,6 +47,14 @@ class HttpUtil { // TODO(darin): kill this static bool HasHeader(const std::string& headers, const char* name); + // Strips all header lines from |headers| whose name matches + // |headers_to_remove|. |headers_to_remove| is a list of null-terminated + // lower-case header names, with array length |headers_to_remove_len|. + // Returns the stripped header lines list, separated by "\r\n". + static std::string StripHeaders(const std::string& headers, + const char* const headers_to_remove[], + size_t headers_to_remove_len); + // Multiple occurances of some headers cannot be coalesced into a comma- // separated list since their values are (or contain) unquoted HTTP-date // values, which may contain a comma (see RFC 2616 section 3.3.1). diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc index 098468f..87ae3cc 100644 --- a/net/http/http_util_unittest.cc +++ b/net/http/http_util_unittest.cc @@ -34,6 +34,29 @@ TEST(HttpUtilTest, HasHeader) { } } +TEST(HttpUtilTest, StripHeaders) { + static const char* headers = + "Origin: origin\r\n" + "Content-Type: text/plain\r\n" + "Cookies: foo1\r\n" + "Custom: baz\r\n" + "COOKIES: foo2\r\n" + "Server: Apache\r\n" + "OrIGin: origin2\r\n"; + + static const char* header_names[] = { + "origin", "content-type", "cookies" + }; + + static const char* expected_stripped_headers = + "Custom: baz\r\n" + "Server: Apache\r\n"; + + EXPECT_EQ(expected_stripped_headers, + HttpUtil::StripHeaders(headers, header_names, + arraysize(header_names))); +} + TEST(HttpUtilTest, HeadersIterator) { std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n"; |