OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/server/http_connection.h" | 5 #include "net/server/http_connection.h" |
6 | 6 |
7 #include <queue> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/location.h" | |
11 #include "base/message_loop/message_loop_proxy.h" | |
12 #include "net/base/net_errors.h" | |
7 #include "net/server/http_server.h" | 13 #include "net/server/http_server.h" |
8 #include "net/server/http_server_response_info.h" | 14 #include "net/server/http_server_response_info.h" |
9 #include "net/server/web_socket.h" | 15 #include "net/server/web_socket.h" |
10 #include "net/socket/stream_listen_socket.h" | 16 #include "net/socket/stream_socket.h" |
11 | 17 |
12 namespace net { | 18 namespace net { |
13 | 19 |
14 int HttpConnection::last_id_ = 0; | 20 namespace { |
21 | |
22 const size_t kInitialReadBufSize = 1024; | |
23 const size_t kMinimumReadBufSize = 128; | |
24 const int kCapacityIncreaseFactor = 2; | |
25 | |
26 const size_t kPendingDataLimit = 1 * 1024 * 1024; // 1 Mbytes. | |
27 | |
28 } // namespace | |
29 | |
30 HttpConnection::ReadIOBuffer::ReadIOBuffer() | |
31 : base_(new GrowableIOBuffer()) { | |
32 SetCapacity(kInitialReadBufSize); | |
33 } | |
34 | |
35 HttpConnection::ReadIOBuffer::~ReadIOBuffer() { | |
36 data_ = NULL; // base_ owns data_. | |
37 } | |
38 | |
39 IOBuffer* HttpConnection::ReadIOBuffer::GetUnusedIOBuffer() const { | |
40 return base_.get(); | |
41 } | |
42 | |
43 size_t HttpConnection::ReadIOBuffer::GetUnusedCapacity() const { | |
44 return static_cast<size_t>(base_->RemainingCapacity()); | |
45 } | |
46 | |
47 size_t HttpConnection::ReadIOBuffer::GetCapacity() const { | |
48 return static_cast<size_t>(base_->capacity()); | |
49 } | |
50 | |
51 void HttpConnection::ReadIOBuffer::SetCapacity(size_t capacity) { | |
52 base_->SetCapacity(static_cast<int>(capacity)); | |
53 data_ = base_->StartOfBuffer(); | |
54 } | |
55 | |
56 size_t HttpConnection::ReadIOBuffer::GetUnconsumedSize() const { | |
57 DCHECK_GE(base_->data(), data_); | |
58 return static_cast<size_t>(base_->data() - data_); | |
59 } | |
60 | |
61 void HttpConnection::ReadIOBuffer::DidRead(size_t bytes) { | |
62 DCHECK_LE(bytes, GetUnusedCapacity()); | |
63 base_->set_offset(base_->offset() + bytes); | |
64 } | |
65 | |
66 void HttpConnection::ReadIOBuffer::DidConsume(size_t bytes) { | |
67 DCHECK_LE(bytes, GetUnconsumedSize()); | |
68 if (bytes < GetUnconsumedSize()) { | |
69 data_ += bytes; | |
70 return; | |
71 } | |
72 // No need to keep consumed data because no data will be moved. | |
73 // If capacity is too big, reduce it. | |
74 if (base_->capacity() > static_cast<int>(kMinimumReadBufSize) | |
75 && base_->capacity() > base_->offset() * kCapacityIncreaseFactor) { | |
76 SetCapacity(base_->capacity() / kCapacityIncreaseFactor); | |
77 } | |
78 base_->set_offset(0); | |
79 } | |
80 | |
81 // IOBuffer of pending data which has a queue of pending data. Each pending data | |
82 // is stored in std::string. data() is the data of first std::string stored. | |
83 class HttpConnection::PendingWriteIOBuffer : public IOBuffer { | |
84 public: | |
85 PendingWriteIOBuffer(); | |
86 | |
87 // Whether or not pending data exists. | |
88 bool IsEmpty() const; | |
89 | |
90 // Appends new pending data. Changes data() if this is the first pending data. | |
91 void Append(const std::string& data); | |
92 // Consumes data and changes data() accordingly. | |
93 void DidConsume(size_t size); | |
94 | |
95 // Gets size of data to write this time. It is NOT total data size. | |
96 size_t GetSizeToWrite() const; | |
97 | |
98 size_t total_size() const { return total_size_; } | |
99 | |
100 private: | |
101 virtual ~PendingWriteIOBuffer(); | |
102 | |
103 std::queue<std::string> pending_data_; | |
104 size_t total_size_; | |
105 | |
106 DISALLOW_COPY_AND_ASSIGN(PendingWriteIOBuffer); | |
107 }; | |
108 | |
109 HttpConnection::PendingWriteIOBuffer::PendingWriteIOBuffer() | |
110 : total_size_(0) { | |
111 } | |
112 | |
113 HttpConnection::PendingWriteIOBuffer::~PendingWriteIOBuffer() { | |
114 } | |
115 | |
116 bool HttpConnection::PendingWriteIOBuffer::IsEmpty() const { | |
117 return pending_data_.empty(); | |
118 } | |
119 | |
120 void HttpConnection::PendingWriteIOBuffer::Append(const std::string& data) { | |
121 if (data.empty()) { | |
122 return; | |
123 } | |
124 | |
125 pending_data_.push(data); | |
mmenke
2014/05/23 19:20:58
Can't we just take IOBuffers instead, to avoid thi
byungchul
2014/05/28 01:19:35
To construct IOBuffer from std::string in any poin
Ryan Sleevi
2014/05/28 01:36:26
Copy-on-write is forbidden behaviour in C++11, and
byungchul
2014/05/30 00:19:02
Now, HttpServer uses this buffer directly. Buildin
| |
126 total_size_ += data.size(); | |
127 | |
128 // If new data is the first pending data, updates data_. | |
129 if (pending_data_.size() == 1) { | |
130 data_ = const_cast<char*>(pending_data_.front().data()); | |
131 } | |
132 } | |
133 | |
134 void HttpConnection::PendingWriteIOBuffer::DidConsume(size_t size) { | |
135 DCHECK_LE(size, total_size_); | |
136 DCHECK_LE(size, GetSizeToWrite()); | |
137 if (size == 0) { | |
138 return; | |
139 } | |
140 | |
141 if (size < GetSizeToWrite()) { | |
142 data_ += size; | |
143 } else { // size == GetSizeToWrite(). Updates data_ to next pending data. | |
144 pending_data_.pop(); | |
145 data_ = IsEmpty() ? NULL : const_cast<char*>(pending_data_.front().data()); | |
146 } | |
147 total_size_ -= size; | |
148 } | |
149 | |
150 size_t HttpConnection::PendingWriteIOBuffer::GetSizeToWrite() const { | |
151 if (IsEmpty()) { | |
152 DCHECK_EQ(total_size_, 0U); | |
153 return 0; | |
154 } | |
155 DCHECK_GE(data_, pending_data_.front().data()); | |
156 size_t consumed = static_cast<size_t>(data_ - pending_data_.front().data()); | |
157 DCHECK_GT(pending_data_.front().size(), consumed); | |
mmenke
2014/05/23 19:20:58
This class may be may effort than it's worth, as-i
mmenke
2014/05/23 19:20:58
Actually...Why don't we do something simpler, like
byungchul
2014/05/28 01:19:35
Any one needs to store date to write anyway. My im
Ryan Sleevi
2014/05/28 01:36:26
Normally, in net/ code, we leave buffering up to t
byungchul
2014/05/28 05:01:33
While keeping current HttpServer apis, I will try
byungchul
2014/05/30 00:19:02
Done.
| |
158 return pending_data_.front().size() - consumed; | |
159 } | |
160 | |
161 HttpConnection::HttpConnection(int id, | |
162 scoped_ptr<StreamSocket> socket, | |
163 Delegate* delegate) | |
164 : id_(id), | |
165 socket_(socket.Pass()), | |
166 delegate_(delegate), | |
167 read_buf_(new ReadIOBuffer()), | |
168 pending_write_buf_(new PendingWriteIOBuffer()) { | |
169 DoReadLoop(OK); | |
170 } | |
171 | |
172 HttpConnection::~HttpConnection() { | |
173 } | |
174 | |
175 void HttpConnection::DoReadLoop(int rv) { | |
mmenke
2014/05/23 19:20:58
Taking in an "rv" that's always OK seems a little
byungchul
2014/05/30 00:19:02
Done and moved to HttpServer.
| |
176 while (rv == OK) { | |
177 if (read_buf_->GetUnusedCapacity() == 0) { | |
178 if (read_buf_->GetCapacity() > kPendingDataLimit) { | |
179 LOG(ERROR) << "Too large read data is pending: capacity=" | |
180 << read_buf_->GetCapacity() | |
181 << ", consumed=" | |
182 << read_buf_->GetCapacity() - read_buf_->GetUnconsumedSize(); | |
183 Close(); | |
184 return; | |
185 } | |
186 read_buf_->SetCapacity(read_buf_->GetCapacity() * kMinimumReadBufSize); | |
187 } | |
188 rv = socket_->Read(read_buf_->GetUnusedIOBuffer(), | |
189 read_buf_->GetUnusedCapacity(), | |
190 base::Bind(&HttpConnection::OnReadCompleted, | |
191 AsWeakPtr())); | |
mmenke
2014/05/23 19:20:58
We don't need weak pointers here - we own the sock
byungchul
2014/05/30 00:19:02
Replaced with connection id in http server.
| |
192 if (rv == ERR_IO_PENDING) { | |
193 break; | |
194 } | |
195 rv = DidRead(rv); | |
196 } | |
197 } | |
198 | |
199 void HttpConnection::OnReadCompleted(int rv) { | |
200 if (DidRead(rv) == OK) { | |
201 DoReadLoop(OK); | |
202 } | |
203 } | |
204 | |
205 int HttpConnection::DidRead(int rv) { | |
206 if (rv <= 0) { | |
207 Close(); | |
208 return rv == 0 ? ERR_CONNECTION_CLOSED : rv; | |
209 } | |
210 | |
211 read_buf_->DidRead(static_cast<size_t>(rv)); | |
212 delegate_->DidRead(this); | |
213 return OK; | |
214 } | |
15 | 215 |
16 void HttpConnection::Send(const std::string& data) { | 216 void HttpConnection::Send(const std::string& data) { |
17 if (!socket_.get()) | 217 if (pending_write_buf_->total_size() + data.size() > kPendingDataLimit) { |
pfeldman
2014/05/26 09:21:31
Before we go further, could you elaborate on this
mmenke
2014/05/27 14:32:08
It's buffering 100 MB payloads in RAM? That just
byungchul
2014/05/30 00:19:02
Increased websocket buffer to 100Mb in cast of dev
| |
18 return; | 218 LOG(ERROR) << "Too large write data is pending: size=" |
19 socket_->Send(data); | 219 << pending_write_buf_->total_size() + data.size(); |
220 Close(); | |
221 return; | |
222 } | |
223 bool writing_in_progress = !pending_write_buf_->IsEmpty(); | |
224 pending_write_buf_->Append(data); | |
225 if (!writing_in_progress) { | |
226 DoWriteLoop(OK); | |
227 } | |
20 } | 228 } |
21 | 229 |
22 void HttpConnection::Send(const char* bytes, int len) { | 230 void HttpConnection::Send(const char* bytes, int len) { |
23 if (!socket_.get()) | 231 Send(std::string(bytes, len)); |
mmenke
2014/05/23 19:20:58
This copy really shouldn't be needed.
byungchul
2014/05/30 00:19:02
This is used only by net/server/web_socket.cc. One
| |
24 return; | |
25 socket_->Send(bytes, len); | |
26 } | 232 } |
27 | 233 |
28 void HttpConnection::Send(const HttpServerResponseInfo& response) { | 234 void HttpConnection::Send(const HttpServerResponseInfo& response) { |
29 Send(response.Serialize()); | 235 Send(response.Serialize()); |
30 } | 236 } |
31 | 237 |
32 HttpConnection::HttpConnection(HttpServer* server, | 238 void HttpConnection::DoWriteLoop(int rv) { |
mmenke
2014/05/23 19:20:58
Again, rv is always "OK", so doesn't seem like it
byungchul
2014/05/30 00:19:02
Done.
| |
33 scoped_ptr<StreamListenSocket> sock) | 239 while (rv == OK && pending_write_buf_->GetSizeToWrite() > 0) { |
34 : server_(server), | 240 rv = socket_->Write(pending_write_buf_.get(), |
35 socket_(sock.Pass()) { | 241 pending_write_buf_->GetSizeToWrite(), |
36 id_ = last_id_++; | 242 base::Bind(&HttpConnection::OnWriteCompleted, |
37 } | 243 AsWeakPtr())); |
mmenke
2014/05/23 19:20:58
WeakPtr not needed.
byungchul
2014/05/30 00:19:02
Done.
| |
38 | 244 if (rv == ERR_IO_PENDING || rv == OK) { |
39 HttpConnection::~HttpConnection() { | 245 break; |
40 server_->delegate_->OnClose(id_); | 246 } |
41 } | 247 rv = DidWrite(rv); |
42 | 248 } |
43 void HttpConnection::Shift(int num_bytes) { | 249 } |
44 recv_data_ = recv_data_.substr(num_bytes); | 250 |
251 void HttpConnection::OnWriteCompleted(int rv) { | |
252 if (DidWrite(rv) == OK) { | |
253 DoWriteLoop(OK); | |
254 } | |
255 } | |
256 | |
257 int HttpConnection::DidWrite(int rv) { | |
258 if (rv < 0) { | |
259 Close(); | |
260 return rv; | |
261 } | |
262 | |
263 pending_write_buf_->DidConsume(rv); | |
264 return OK; | |
265 } | |
266 | |
267 void HttpConnection::Close() { | |
mmenke
2014/05/23 19:20:58
This function can end up being called twice: Once
byungchul
2014/05/28 01:19:35
That's why I use a weak ptr.
Ryan Sleevi
2014/05/28 01:36:26
I don't really think that's an ideal solution.
No
byungchul
2014/05/30 00:19:02
HttpServer handles it and/so never calls Close() t
| |
268 // DidClose() may delete this object. Call it in next run loop to make sure | |
269 // any DidRead()/DidWrite() callbacks in the stack return. | |
270 base::MessageLoopProxy::current()->PostTask( | |
271 FROM_HERE, | |
272 base::Bind(&HttpConnection::CloseInNextRunLoop, AsWeakPtr())); | |
mmenke
2014/05/23 19:20:58
So we always call back Delegate::DidClose asynchro
byungchul
2014/05/28 01:19:35
They are different. DidClose() could be called for
| |
273 } | |
274 | |
275 void HttpConnection::CloseInNextRunLoop() { | |
mmenke
2014/05/23 19:20:58
I think this and Close() are misnamed. Close() sh
byungchul
2014/05/30 00:19:02
Not necessary. Removed.
| |
276 socket_->Disconnect(); | |
277 delegate_->DidClose(this); | |
278 } | |
279 | |
280 void HttpConnection::UpgradeToWebSocket(const HttpServerRequestInfo& request, | |
281 size_t* pos) { | |
282 DCHECK(!web_socket_); | |
283 web_socket_.reset(WebSocket::CreateWebSocket(this, request, pos)); | |
45 } | 284 } |
46 | 285 |
47 } // namespace net | 286 } // namespace net |
OLD | NEW |