1// Copyright (c) 2012 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 "chrome/test/chromedriver/net/sync_websocket_impl.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/location.h"
10#include "base/single_thread_task_runner.h"
11#include "base/synchronization/waitable_event.h"
12#include "net/base/net_errors.h"
13#include "net/url_request/url_request_context_getter.h"
14#include "url/gurl.h"
15
16SyncWebSocketImpl::SyncWebSocketImpl(
17    net::URLRequestContextGetter* context_getter)
18    : core_(new Core(context_getter)) {}
19
20SyncWebSocketImpl::~SyncWebSocketImpl() {}
21
22bool SyncWebSocketImpl::IsConnected() {
23  return core_->IsConnected();
24}
25
26bool SyncWebSocketImpl::Connect(const GURL& url) {
27  return core_->Connect(url);
28}
29
30bool SyncWebSocketImpl::Send(const std::string& message) {
31  return core_->Send(message);
32}
33
34SyncWebSocket::StatusCode SyncWebSocketImpl::ReceiveNextMessage(
35    std::string* message, const base::TimeDelta& timeout) {
36  return core_->ReceiveNextMessage(message, timeout);
37}
38
39bool SyncWebSocketImpl::HasNextMessage() {
40  return core_->HasNextMessage();
41}
42
43SyncWebSocketImpl::Core::Core(net::URLRequestContextGetter* context_getter)
44    : context_getter_(context_getter),
45      is_connected_(false),
46      on_update_event_(&lock_) {}
47
48bool SyncWebSocketImpl::Core::IsConnected() {
49  base::AutoLock lock(lock_);
50  return is_connected_;
51}
52
53bool SyncWebSocketImpl::Core::Connect(const GURL& url) {
54  bool success = false;
55  base::WaitableEvent event(false, false);
56  context_getter_->GetNetworkTaskRunner()->PostTask(
57      FROM_HERE,
58      base::Bind(&SyncWebSocketImpl::Core::ConnectOnIO,
59                 this, url, &success, &event));
60  event.Wait();
61  return success;
62}
63
64bool SyncWebSocketImpl::Core::Send(const std::string& message) {
65  bool success = false;
66  base::WaitableEvent event(false, false);
67  context_getter_->GetNetworkTaskRunner()->PostTask(
68      FROM_HERE,
69      base::Bind(&SyncWebSocketImpl::Core::SendOnIO,
70                 this, message, &success, &event));
71  event.Wait();
72  return success;
73}
74
75SyncWebSocket::StatusCode SyncWebSocketImpl::Core::ReceiveNextMessage(
76    std::string* message,
77    const base::TimeDelta& timeout) {
78  base::AutoLock lock(lock_);
79  base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
80  base::TimeDelta next_wait = timeout;
81  while (received_queue_.empty() && is_connected_) {
82    if (next_wait <= base::TimeDelta())
83      return SyncWebSocket::kTimeout;
84    on_update_event_.TimedWait(next_wait);
85    next_wait = deadline - base::TimeTicks::Now();
86  }
87  if (!is_connected_)
88    return SyncWebSocket::kDisconnected;
89  *message = received_queue_.front();
90  received_queue_.pop_front();
91  return SyncWebSocket::kOk;
92}
93
94bool SyncWebSocketImpl::Core::HasNextMessage() {
95  base::AutoLock lock(lock_);
96  return !received_queue_.empty();
97}
98
99void SyncWebSocketImpl::Core::OnMessageReceived(const std::string& message) {
100  base::AutoLock lock(lock_);
101  received_queue_.push_back(message);
102  on_update_event_.Signal();
103}
104
105void SyncWebSocketImpl::Core::OnClose() {
106  base::AutoLock lock(lock_);
107  is_connected_ = false;
108  on_update_event_.Signal();
109}
110
111SyncWebSocketImpl::Core::~Core() { }
112
113void SyncWebSocketImpl::Core::ConnectOnIO(
114    const GURL& url,
115    bool* success,
116    base::WaitableEvent* event) {
117  {
118    base::AutoLock lock(lock_);
119    received_queue_.clear();
120  }
121  socket_.reset(new WebSocket(url, this));
122  socket_->Connect(base::Bind(
123      &SyncWebSocketImpl::Core::OnConnectCompletedOnIO,
124      this, success, event));
125}
126
127void SyncWebSocketImpl::Core::OnConnectCompletedOnIO(
128    bool* success,
129    base::WaitableEvent* event,
130    int error) {
131  *success = (error == net::OK);
132  if (*success) {
133    base::AutoLock lock(lock_);
134    is_connected_ = true;
135  }
136  event->Signal();
137}
138
139void SyncWebSocketImpl::Core::SendOnIO(
140    const std::string& message,
141    bool* success,
142    base::WaitableEvent* event) {
143  *success = socket_->Send(message);
144  event->Signal();
145}
146
147void SyncWebSocketImpl::Core::OnDestruct() const {
148  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner =
149      context_getter_->GetNetworkTaskRunner();
150  if (network_task_runner->BelongsToCurrentThread())
151    delete this;
152  else
153    network_task_runner->DeleteSoon(FROM_HERE, this);
154}
155