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

Side by Side Diff: net/websockets/websocket_job.cc

Issue 601077: Support HttpOnly cookie on Web Socket (Closed)
Patch Set: fix darin's comment Created 10 years, 9 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/websockets/websocket_job.h"
6
7 #include "googleurl/src/gurl.h"
8 #include "net/base/net_errors.h"
9 #include "net/base/cookie_policy.h"
10 #include "net/base/cookie_store.h"
11 #include "net/http/http_util.h"
12 #include "net/url_request/url_request_context.h"
13
14 namespace net {
15
16 // lower-case header names.
17 static const char* const kCookieHeaders[] = {
18 "cookie", "cookie2"
19 };
20 static const char* const kSetCookieHeaders[] = {
21 "set-cookie", "set-cookie2"
22 };
23
24 static SocketStreamJob* WebSocketJobFactory(
25 const GURL& url, SocketStream::Delegate* delegate) {
26 WebSocketJob* job = new WebSocketJob(delegate);
27 job->InitSocketStream(new SocketStream(url, job));
28 return job;
29 }
30
31 class WebSocketJobInitSingleton {
32 private:
33 friend struct DefaultSingletonTraits<WebSocketJobInitSingleton>;
34 WebSocketJobInitSingleton() {
35 SocketStreamJob::RegisterProtocolFactory("ws", WebSocketJobFactory);
36 SocketStreamJob::RegisterProtocolFactory("wss", WebSocketJobFactory);
37 }
38 };
39
40 static void ParseHandshakeMessage(
41 const char* handshake_message, int len,
42 std::string* status_line,
43 std::string* header) {
44 size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
45 if (i == base::StringPiece::npos) {
46 *status_line = std::string(handshake_message, len);
47 *header = "";
48 return;
49 }
50 *status_line = std::string(handshake_message, i + 2);
51 *header = std::string(handshake_message + i + 2, len - i - 2);
52 }
53
54 static void FetchResponseCookies(
55 const char* handshake_message, int len,
56 std::vector<std::string>* response_cookies) {
57 std::string handshake_response(handshake_message, len);
58 HttpUtil::HeadersIterator iter(handshake_response.begin(),
59 handshake_response.end(), "\r\n");
60 while (iter.GetNext()) {
61 for (size_t i = 0; i < arraysize(kSetCookieHeaders); i++) {
62 if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
63 kSetCookieHeaders[i])) {
64 response_cookies->push_back(iter.values());
65 }
66 }
67 }
68 }
69
70 // static
71 void WebSocketJob::EnsureInit() {
72 Singleton<WebSocketJobInitSingleton>::get();
73 }
74
75 WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
76 : delegate_(delegate),
77 state_(INITIALIZED),
78 handshake_request_sent_(0),
79 handshake_response_header_length_(0),
80 response_cookies_save_index_(0),
81 ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
82 this, &WebSocketJob::OnCanGetCookiesCompleted)),
83 ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_(
84 this, &WebSocketJob::OnCanSetCookieCompleted)) {
85 }
86
87 WebSocketJob::~WebSocketJob() {
88 DCHECK_EQ(CLOSED, state_);
89 DCHECK(!delegate_);
90 DCHECK(!socket_.get());
91 }
92
93 void WebSocketJob::Connect() {
94 DCHECK(socket_.get());
95 DCHECK_EQ(state_, INITIALIZED);
96 state_ = CONNECTING;
97 socket_->Connect();
98 }
99
100 bool WebSocketJob::SendData(const char* data, int len) {
101 switch (state_) {
102 case INITIALIZED:
103 return false;
104
105 case CONNECTING:
106 return SendHandshakeRequest(data, len);
107
108 case OPEN:
109 return socket_->SendData(data, len);
110
111 case CLOSED:
112 return false;
113 }
114 return false;
115 }
116
117 void WebSocketJob::Close() {
118 state_ = CLOSED;
119 socket_->Close();
120 }
121
122 void WebSocketJob::RestartWithAuth(
123 const std::wstring& username,
124 const std::wstring& password) {
125 state_ = CONNECTING;
126 socket_->RestartWithAuth(username, password);
127 }
128
129 void WebSocketJob::DetachDelegate() {
130 state_ = CLOSED;
131 delegate_ = NULL;
132 socket_->DetachDelegate();
133 socket_ = NULL;
134 }
135
136 void WebSocketJob::OnConnected(
137 SocketStream* socket, int max_pending_send_allowed) {
138 if (delegate_)
139 delegate_->OnConnected(socket, max_pending_send_allowed);
140 }
141
142 void WebSocketJob::OnSentData(SocketStream* socket, int amount_sent) {
143 if (state_ == CONNECTING) {
144 OnSentHandshakeRequest(socket, amount_sent);
145 return;
146 }
147 if (delegate_)
148 delegate_->OnSentData(socket, amount_sent);
149 }
150
151 void WebSocketJob::OnReceivedData(
152 SocketStream* socket, const char* data, int len) {
153 if (state_ == CONNECTING) {
154 OnReceivedHandshakeResponse(socket, data, len);
155 return;
156 }
157 if (delegate_)
158 delegate_->OnReceivedData(socket, data, len);
159 }
160
161 void WebSocketJob::OnClose(SocketStream* socket) {
162 state_ = CLOSED;
163 SocketStream::Delegate* delegate = delegate_;
164 delegate_ = NULL;
165 socket_ = NULL;
166 if (delegate)
167 delegate->OnClose(socket);
168 }
169
170 void WebSocketJob::OnAuthRequired(
171 SocketStream* socket, AuthChallengeInfo* auth_info) {
172 if (delegate_)
173 delegate_->OnAuthRequired(socket, auth_info);
174 }
175
176 void WebSocketJob::OnError(const SocketStream* socket, int error) {
177 if (delegate_)
178 delegate_->OnError(socket, error);
179 }
180
181 bool WebSocketJob::SendHandshakeRequest(const char* data, int len) {
182 DCHECK_EQ(state_, CONNECTING);
183 if (!handshake_request_.empty()) {
184 // if we're already sending handshake message, don't send any more data
185 // until handshake is completed.
186 return false;
187 }
188 original_handshake_request_.append(data, len);
189 original_handshake_request_header_length_ =
190 HttpUtil::LocateEndOfHeaders(original_handshake_request_.data(),
191 original_handshake_request_.size(), 0);
192 if (original_handshake_request_header_length_ > 0) {
193 // handshake message is completed.
194 AddCookieHeaderAndSend();
195 }
196 // Just buffered in original_handshake_request_.
197 return true;
198 }
199
200 void WebSocketJob::AddCookieHeaderAndSend() {
201 AddRef(); // Balanced in OnCanGetCookiesCompleted
202
203 int policy = OK;
204 if (socket_->context()->cookie_policy()) {
205 GURL url_for_cookies = GetURLForCookies();
206 policy = socket_->context()->cookie_policy()->CanGetCookies(
207 url_for_cookies,
208 url_for_cookies,
209 &can_get_cookies_callback_);
210 if (policy == ERR_IO_PENDING)
211 return; // Wait for completion callback
212 }
213 OnCanGetCookiesCompleted(policy);
214 }
215
216 void WebSocketJob::OnCanGetCookiesCompleted(int policy) {
217 if (socket_ && delegate_ && state_ == CONNECTING) {
218 std::string handshake_request_status_line;
219 std::string handshake_request_header;
220 ParseHandshakeMessage(original_handshake_request_.data(),
221 original_handshake_request_header_length_,
222 &handshake_request_status_line,
223 &handshake_request_header);
224
225 // Remove cookie headers.
226 handshake_request_header = HttpUtil::StripHeaders(
227 handshake_request_header,
228 kCookieHeaders, arraysize(kCookieHeaders));
229
230 if (policy == OK) {
231 // Add cookies, including HttpOnly cookies.
232 if (socket_->context()->cookie_store()) {
233 CookieOptions cookie_options;
234 cookie_options.set_include_httponly();
235 std::string cookie =
236 socket_->context()->cookie_store()->GetCookiesWithOptions(
237 GetURLForCookies(), cookie_options);
238 if (!cookie.empty()) {
239 HttpUtil::AppendHeaderIfMissing("Cookie", cookie,
240 &handshake_request_header);
241 }
242 }
243 }
244
245 // Simply ignore rest data in original request header after
246 // original_handshake_request_header_length_, because websocket protocol
247 // doesn't allow sending message before handshake is completed.
248 // TODO(ukai): report as error?
249 handshake_request_ =
250 handshake_request_status_line + handshake_request_header + "\r\n";
251
252 handshake_request_sent_ = 0;
253 socket_->SendData(handshake_request_.data(),
254 handshake_request_.size());
255 }
256 Release(); // Balance AddRef taken in AddCookieHeaderAndSend
257 }
258
259 void WebSocketJob::OnSentHandshakeRequest(
260 SocketStream* socket, int amount_sent) {
261 DCHECK_EQ(state_, CONNECTING);
262 handshake_request_sent_ += amount_sent;
263 if (handshake_request_sent_ >= handshake_request_.size()) {
264 // handshake request has been sent.
265 // notify original size of handshake request to delegate.
266 if (delegate_)
267 delegate_->OnSentData(socket, original_handshake_request_.size());
268 }
269 }
270
271 void WebSocketJob::OnReceivedHandshakeResponse(
272 SocketStream* socket, const char* data, int len) {
273 DCHECK_EQ(state_, CONNECTING);
274 handshake_response_.append(data, len);
275 handshake_response_header_length_ = HttpUtil::LocateEndOfHeaders(
276 handshake_response_.data(),
277 handshake_response_.size(), 0);
278 if (handshake_response_header_length_ > 0) {
279 // handshake message is completed.
280 SaveCookiesAndNotifyHeaderComplete();
281 }
282 }
283
284 void WebSocketJob::SaveCookiesAndNotifyHeaderComplete() {
285 // handshake message is completed.
286 DCHECK(handshake_response_.data());
287 DCHECK_GT(handshake_response_header_length_, 0);
288
289 response_cookies_.clear();
290 response_cookies_save_index_ = 0;
291
292 FetchResponseCookies(handshake_response_.data(),
293 handshake_response_header_length_,
294 &response_cookies_);
295
296 // Now, loop over the response cookies, and attempt to persist each.
297 SaveNextCookie();
298 }
299
300 void WebSocketJob::SaveNextCookie() {
301 if (response_cookies_save_index_ == response_cookies_.size()) {
302 response_cookies_.clear();
303 response_cookies_save_index_ = 0;
304
305 std::string handshake_response_status_line;
306 std::string handshake_response_header;
307 ParseHandshakeMessage(handshake_response_.data(),
308 handshake_response_header_length_,
309 &handshake_response_status_line,
310 &handshake_response_header);
311 // Remove cookie headers.
312 std::string filtered_handshake_response_header =
313 HttpUtil::StripHeaders(
314 handshake_response_header,
315 kSetCookieHeaders, arraysize(kSetCookieHeaders));
316 std::string remaining_data =
317 std::string(handshake_response_.data() +
318 handshake_response_header_length_,
319 handshake_response_.size() -
320 handshake_response_header_length_);
321 std::string received_data =
322 handshake_response_status_line +
323 filtered_handshake_response_header +
324 "\r\n" +
325 remaining_data;
326 state_ = OPEN;
327 if (delegate_)
328 delegate_->OnReceivedData(socket_,
329 received_data.data(), received_data.size());
330 return;
331 }
332
333 AddRef(); // Balanced in OnCanSetCookieCompleted
334
335 int policy = OK;
336 if (socket_->context()->cookie_policy()) {
337 GURL url_for_cookies = GetURLForCookies();
338 policy = socket_->context()->cookie_policy()->CanSetCookie(
339 url_for_cookies,
340 url_for_cookies,
341 response_cookies_[response_cookies_save_index_],
342 &can_set_cookie_callback_);
343 if (policy == ERR_IO_PENDING)
344 return; // Wait for completion callback
345 }
346
347 OnCanSetCookieCompleted(policy);
348 }
349
350 void WebSocketJob::OnCanSetCookieCompleted(int policy) {
351 if (socket_ && delegate_ && state_ == CONNECTING) {
352 if ((policy == OK || policy == OK_FOR_SESSION_ONLY) &&
353 socket_->context()->cookie_store()) {
354 CookieOptions options;
355 options.set_include_httponly();
356 if (policy == OK_FOR_SESSION_ONLY)
357 options.set_force_session();
358 GURL url_for_cookies = GetURLForCookies();
359 socket_->context()->cookie_store()->SetCookieWithOptions(
360 url_for_cookies, response_cookies_[response_cookies_save_index_],
361 options);
362 }
363 response_cookies_save_index_++;
364 SaveNextCookie();
365 }
366 Release(); // Balance AddRef taken in SaveNextCookie
367 }
368
369 GURL WebSocketJob::GetURLForCookies() const {
370 GURL url = socket_->url();
371 std::string scheme = socket_->is_secure() ? "https" : "http";
372 url_canon::Replacements<char> replacements;
373 replacements.SetScheme(scheme.c_str(),
374 url_parse::Component(0, scheme.length()));
375 return url.ReplaceComponents(replacements);
376 }
377
378 } // namespace net
OLDNEW
« 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