Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(444)

Unified Diff: net/websockets/websocket_job.cc

Issue 601077: Support HttpOnly cookie on Web Socket (Closed)
Patch Set: fix darin's comment Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/websockets/websocket_job.h ('k') | net/websockets/websocket_job_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/websockets/websocket_job.cc
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3ba0c36d6eb8b8fb9caa87f53a20f3f26861ca8a
--- /dev/null
+++ b/net/websockets/websocket_job.cc
@@ -0,0 +1,378 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_job.h"
+
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/base/cookie_policy.h"
+#include "net/base/cookie_store.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request_context.h"
+
+namespace net {
+
+// lower-case header names.
+static const char* const kCookieHeaders[] = {
+ "cookie", "cookie2"
+};
+static const char* const kSetCookieHeaders[] = {
+ "set-cookie", "set-cookie2"
+};
+
+static SocketStreamJob* WebSocketJobFactory(
+ const GURL& url, SocketStream::Delegate* delegate) {
+ WebSocketJob* job = new WebSocketJob(delegate);
+ job->InitSocketStream(new SocketStream(url, job));
+ return job;
+}
+
+class WebSocketJobInitSingleton {
+ private:
+ friend struct DefaultSingletonTraits<WebSocketJobInitSingleton>;
+ WebSocketJobInitSingleton() {
+ SocketStreamJob::RegisterProtocolFactory("ws", WebSocketJobFactory);
+ SocketStreamJob::RegisterProtocolFactory("wss", WebSocketJobFactory);
+ }
+};
+
+static void ParseHandshakeMessage(
+ const char* handshake_message, int len,
+ std::string* status_line,
+ std::string* header) {
+ size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
+ if (i == base::StringPiece::npos) {
+ *status_line = std::string(handshake_message, len);
+ *header = "";
+ return;
+ }
+ *status_line = std::string(handshake_message, i + 2);
+ *header = std::string(handshake_message + i + 2, len - i - 2);
+}
+
+static void FetchResponseCookies(
+ const char* handshake_message, int len,
+ std::vector<std::string>* response_cookies) {
+ std::string handshake_response(handshake_message, len);
+ HttpUtil::HeadersIterator iter(handshake_response.begin(),
+ handshake_response.end(), "\r\n");
+ while (iter.GetNext()) {
+ for (size_t i = 0; i < arraysize(kSetCookieHeaders); i++) {
+ if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+ kSetCookieHeaders[i])) {
+ response_cookies->push_back(iter.values());
+ }
+ }
+ }
+}
+
+// static
+void WebSocketJob::EnsureInit() {
+ Singleton<WebSocketJobInitSingleton>::get();
+}
+
+WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
+ : delegate_(delegate),
+ state_(INITIALIZED),
+ handshake_request_sent_(0),
+ handshake_response_header_length_(0),
+ response_cookies_save_index_(0),
+ ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
+ this, &WebSocketJob::OnCanGetCookiesCompleted)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_(
+ this, &WebSocketJob::OnCanSetCookieCompleted)) {
+}
+
+WebSocketJob::~WebSocketJob() {
+ DCHECK_EQ(CLOSED, state_);
+ DCHECK(!delegate_);
+ DCHECK(!socket_.get());
+}
+
+void WebSocketJob::Connect() {
+ DCHECK(socket_.get());
+ DCHECK_EQ(state_, INITIALIZED);
+ state_ = CONNECTING;
+ socket_->Connect();
+}
+
+bool WebSocketJob::SendData(const char* data, int len) {
+ switch (state_) {
+ case INITIALIZED:
+ return false;
+
+ case CONNECTING:
+ return SendHandshakeRequest(data, len);
+
+ case OPEN:
+ return socket_->SendData(data, len);
+
+ case CLOSED:
+ return false;
+ }
+ return false;
+}
+
+void WebSocketJob::Close() {
+ state_ = CLOSED;
+ socket_->Close();
+}
+
+void WebSocketJob::RestartWithAuth(
+ const std::wstring& username,
+ const std::wstring& password) {
+ state_ = CONNECTING;
+ socket_->RestartWithAuth(username, password);
+}
+
+void WebSocketJob::DetachDelegate() {
+ state_ = CLOSED;
+ delegate_ = NULL;
+ socket_->DetachDelegate();
+ socket_ = NULL;
+}
+
+void WebSocketJob::OnConnected(
+ SocketStream* socket, int max_pending_send_allowed) {
+ if (delegate_)
+ delegate_->OnConnected(socket, max_pending_send_allowed);
+}
+
+void WebSocketJob::OnSentData(SocketStream* socket, int amount_sent) {
+ if (state_ == CONNECTING) {
+ OnSentHandshakeRequest(socket, amount_sent);
+ return;
+ }
+ if (delegate_)
+ delegate_->OnSentData(socket, amount_sent);
+}
+
+void WebSocketJob::OnReceivedData(
+ SocketStream* socket, const char* data, int len) {
+ if (state_ == CONNECTING) {
+ OnReceivedHandshakeResponse(socket, data, len);
+ return;
+ }
+ if (delegate_)
+ delegate_->OnReceivedData(socket, data, len);
+}
+
+void WebSocketJob::OnClose(SocketStream* socket) {
+ state_ = CLOSED;
+ SocketStream::Delegate* delegate = delegate_;
+ delegate_ = NULL;
+ socket_ = NULL;
+ if (delegate)
+ delegate->OnClose(socket);
+}
+
+void WebSocketJob::OnAuthRequired(
+ SocketStream* socket, AuthChallengeInfo* auth_info) {
+ if (delegate_)
+ delegate_->OnAuthRequired(socket, auth_info);
+}
+
+void WebSocketJob::OnError(const SocketStream* socket, int error) {
+ if (delegate_)
+ delegate_->OnError(socket, error);
+}
+
+bool WebSocketJob::SendHandshakeRequest(const char* data, int len) {
+ DCHECK_EQ(state_, CONNECTING);
+ if (!handshake_request_.empty()) {
+ // if we're already sending handshake message, don't send any more data
+ // until handshake is completed.
+ return false;
+ }
+ original_handshake_request_.append(data, len);
+ original_handshake_request_header_length_ =
+ HttpUtil::LocateEndOfHeaders(original_handshake_request_.data(),
+ original_handshake_request_.size(), 0);
+ if (original_handshake_request_header_length_ > 0) {
+ // handshake message is completed.
+ AddCookieHeaderAndSend();
+ }
+ // Just buffered in original_handshake_request_.
+ return true;
+}
+
+void WebSocketJob::AddCookieHeaderAndSend() {
+ AddRef(); // Balanced in OnCanGetCookiesCompleted
+
+ int policy = OK;
+ if (socket_->context()->cookie_policy()) {
+ GURL url_for_cookies = GetURLForCookies();
+ policy = socket_->context()->cookie_policy()->CanGetCookies(
+ url_for_cookies,
+ url_for_cookies,
+ &can_get_cookies_callback_);
+ if (policy == ERR_IO_PENDING)
+ return; // Wait for completion callback
+ }
+ OnCanGetCookiesCompleted(policy);
+}
+
+void WebSocketJob::OnCanGetCookiesCompleted(int policy) {
+ if (socket_ && delegate_ && state_ == CONNECTING) {
+ std::string handshake_request_status_line;
+ std::string handshake_request_header;
+ ParseHandshakeMessage(original_handshake_request_.data(),
+ original_handshake_request_header_length_,
+ &handshake_request_status_line,
+ &handshake_request_header);
+
+ // Remove cookie headers.
+ handshake_request_header = HttpUtil::StripHeaders(
+ handshake_request_header,
+ kCookieHeaders, arraysize(kCookieHeaders));
+
+ if (policy == OK) {
+ // Add cookies, including HttpOnly cookies.
+ if (socket_->context()->cookie_store()) {
+ CookieOptions cookie_options;
+ cookie_options.set_include_httponly();
+ std::string cookie =
+ socket_->context()->cookie_store()->GetCookiesWithOptions(
+ GetURLForCookies(), cookie_options);
+ if (!cookie.empty()) {
+ HttpUtil::AppendHeaderIfMissing("Cookie", cookie,
+ &handshake_request_header);
+ }
+ }
+ }
+
+ // Simply ignore rest data in original request header after
+ // original_handshake_request_header_length_, because websocket protocol
+ // doesn't allow sending message before handshake is completed.
+ // TODO(ukai): report as error?
+ handshake_request_ =
+ handshake_request_status_line + handshake_request_header + "\r\n";
+
+ handshake_request_sent_ = 0;
+ socket_->SendData(handshake_request_.data(),
+ handshake_request_.size());
+ }
+ Release(); // Balance AddRef taken in AddCookieHeaderAndSend
+}
+
+void WebSocketJob::OnSentHandshakeRequest(
+ SocketStream* socket, int amount_sent) {
+ DCHECK_EQ(state_, CONNECTING);
+ handshake_request_sent_ += amount_sent;
+ if (handshake_request_sent_ >= handshake_request_.size()) {
+ // handshake request has been sent.
+ // notify original size of handshake request to delegate.
+ if (delegate_)
+ delegate_->OnSentData(socket, original_handshake_request_.size());
+ }
+}
+
+void WebSocketJob::OnReceivedHandshakeResponse(
+ SocketStream* socket, const char* data, int len) {
+ DCHECK_EQ(state_, CONNECTING);
+ handshake_response_.append(data, len);
+ handshake_response_header_length_ = HttpUtil::LocateEndOfHeaders(
+ handshake_response_.data(),
+ handshake_response_.size(), 0);
+ if (handshake_response_header_length_ > 0) {
+ // handshake message is completed.
+ SaveCookiesAndNotifyHeaderComplete();
+ }
+}
+
+void WebSocketJob::SaveCookiesAndNotifyHeaderComplete() {
+ // handshake message is completed.
+ DCHECK(handshake_response_.data());
+ DCHECK_GT(handshake_response_header_length_, 0);
+
+ response_cookies_.clear();
+ response_cookies_save_index_ = 0;
+
+ FetchResponseCookies(handshake_response_.data(),
+ handshake_response_header_length_,
+ &response_cookies_);
+
+ // Now, loop over the response cookies, and attempt to persist each.
+ SaveNextCookie();
+}
+
+void WebSocketJob::SaveNextCookie() {
+ if (response_cookies_save_index_ == response_cookies_.size()) {
+ response_cookies_.clear();
+ response_cookies_save_index_ = 0;
+
+ std::string handshake_response_status_line;
+ std::string handshake_response_header;
+ ParseHandshakeMessage(handshake_response_.data(),
+ handshake_response_header_length_,
+ &handshake_response_status_line,
+ &handshake_response_header);
+ // Remove cookie headers.
+ std::string filtered_handshake_response_header =
+ HttpUtil::StripHeaders(
+ handshake_response_header,
+ kSetCookieHeaders, arraysize(kSetCookieHeaders));
+ std::string remaining_data =
+ std::string(handshake_response_.data() +
+ handshake_response_header_length_,
+ handshake_response_.size() -
+ handshake_response_header_length_);
+ std::string received_data =
+ handshake_response_status_line +
+ filtered_handshake_response_header +
+ "\r\n" +
+ remaining_data;
+ state_ = OPEN;
+ if (delegate_)
+ delegate_->OnReceivedData(socket_,
+ received_data.data(), received_data.size());
+ return;
+ }
+
+ AddRef(); // Balanced in OnCanSetCookieCompleted
+
+ int policy = OK;
+ if (socket_->context()->cookie_policy()) {
+ GURL url_for_cookies = GetURLForCookies();
+ policy = socket_->context()->cookie_policy()->CanSetCookie(
+ url_for_cookies,
+ url_for_cookies,
+ response_cookies_[response_cookies_save_index_],
+ &can_set_cookie_callback_);
+ if (policy == ERR_IO_PENDING)
+ return; // Wait for completion callback
+ }
+
+ OnCanSetCookieCompleted(policy);
+}
+
+void WebSocketJob::OnCanSetCookieCompleted(int policy) {
+ if (socket_ && delegate_ && state_ == CONNECTING) {
+ if ((policy == OK || policy == OK_FOR_SESSION_ONLY) &&
+ socket_->context()->cookie_store()) {
+ CookieOptions options;
+ options.set_include_httponly();
+ if (policy == OK_FOR_SESSION_ONLY)
+ options.set_force_session();
+ GURL url_for_cookies = GetURLForCookies();
+ socket_->context()->cookie_store()->SetCookieWithOptions(
+ url_for_cookies, response_cookies_[response_cookies_save_index_],
+ options);
+ }
+ response_cookies_save_index_++;
+ SaveNextCookie();
+ }
+ Release(); // Balance AddRef taken in SaveNextCookie
+}
+
+GURL WebSocketJob::GetURLForCookies() const {
+ GURL url = socket_->url();
+ std::string scheme = socket_->is_secure() ? "https" : "http";
+ url_canon::Replacements<char> replacements;
+ replacements.SetScheme(scheme.c_str(),
+ url_parse::Component(0, scheme.length()));
+ return url.ReplaceComponents(replacements);
+}
+
+} // namespace net
« no previous file with comments | « net/websockets/websocket_job.h ('k') | net/websockets/websocket_job_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698