1 // Copyright 2012 The Chromium Authors
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/spdy/spdy_proxy_client_socket.h"
6
7 #include <algorithm> // min
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/notreached.h"
15 #include "base/strings/string_util.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "base/values.h"
18 #include "net/base/auth.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/proxy_chain.h"
21 #include "net/base/proxy_delegate.h"
22 #include "net/http/http_auth_cache.h"
23 #include "net/http/http_auth_handler_factory.h"
24 #include "net/http/http_log_util.h"
25 #include "net/http/http_request_info.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/log/net_log_event_type.h"
28 #include "net/log/net_log_source_type.h"
29 #include "net/spdy/spdy_http_utils.h"
30 #include "net/traffic_annotation/network_traffic_annotation.h"
31 #include "url/gurl.h"
32
33 namespace net {
34
SpdyProxyClientSocket(const base::WeakPtr<SpdyStream> & spdy_stream,const ProxyChain & proxy_chain,size_t proxy_chain_index,const std::string & user_agent,const HostPortPair & endpoint,const NetLogWithSource & source_net_log,scoped_refptr<HttpAuthController> auth_controller,ProxyDelegate * proxy_delegate)35 SpdyProxyClientSocket::SpdyProxyClientSocket(
36 const base::WeakPtr<SpdyStream>& spdy_stream,
37 const ProxyChain& proxy_chain,
38 size_t proxy_chain_index,
39 const std::string& user_agent,
40 const HostPortPair& endpoint,
41 const NetLogWithSource& source_net_log,
42 scoped_refptr<HttpAuthController> auth_controller,
43 ProxyDelegate* proxy_delegate)
44 : spdy_stream_(spdy_stream),
45 endpoint_(endpoint),
46 auth_(std::move(auth_controller)),
47 proxy_chain_(proxy_chain),
48 proxy_chain_index_(proxy_chain_index),
49 proxy_delegate_(proxy_delegate),
50 user_agent_(user_agent),
51 net_log_(NetLogWithSource::Make(spdy_stream->net_log().net_log(),
52 NetLogSourceType::PROXY_CLIENT_SOCKET)),
53 source_dependency_(source_net_log.source()) {
54 request_.method = "CONNECT";
55 request_.url = GURL("https://" + endpoint.ToString());
56 net_log_.BeginEventReferencingSource(NetLogEventType::SOCKET_ALIVE,
57 source_net_log.source());
58 net_log_.AddEventReferencingSource(
59 NetLogEventType::HTTP2_PROXY_CLIENT_SESSION,
60 spdy_stream->net_log().source());
61
62 spdy_stream_->SetDelegate(this);
63 was_ever_used_ = spdy_stream_->WasEverUsed();
64 }
65
~SpdyProxyClientSocket()66 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
67 Disconnect();
68 net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
69 }
70
GetConnectResponseInfo() const71 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
72 return response_.headers.get() ? &response_ : nullptr;
73 }
74
75 const scoped_refptr<HttpAuthController>&
GetAuthController() const76 SpdyProxyClientSocket::GetAuthController() const {
77 return auth_;
78 }
79
RestartWithAuth(CompletionOnceCallback callback)80 int SpdyProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
81 // A SPDY Stream can only handle a single request, so the underlying
82 // stream may not be reused and a new SpdyProxyClientSocket must be
83 // created (possibly on top of the same SPDY Session).
84 next_state_ = STATE_DISCONNECTED;
85 return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
86 }
87
88 // Ignore priority changes, just use priority of initial request. Since multiple
89 // requests are pooled on the SpdyProxyClientSocket, reprioritization doesn't
90 // really work.
91 //
92 // TODO(mmenke): Use a single priority value for all SpdyProxyClientSockets,
93 // regardless of what priority they're created with.
SetStreamPriority(RequestPriority priority)94 void SpdyProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
95
96 // Sends a HEADERS frame to the proxy with a CONNECT request
97 // for the specified endpoint. Waits for the server to send back
98 // a HEADERS frame. OK will be returned if the status is 200.
99 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
100 // In any of these cases, Read() may be called to retrieve the HTTP
101 // response body. Any other return values should be considered fatal.
102 // TODO(rch): handle 407 proxy auth requested correctly, perhaps
103 // by creating a new stream for the subsequent request.
104 // TODO(rch): create a more appropriate error code to disambiguate
105 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
Connect(CompletionOnceCallback callback)106 int SpdyProxyClientSocket::Connect(CompletionOnceCallback callback) {
107 DCHECK(read_callback_.is_null());
108 if (next_state_ == STATE_OPEN)
109 return OK;
110
111 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
112 next_state_ = STATE_GENERATE_AUTH_TOKEN;
113
114 int rv = DoLoop(OK);
115 if (rv == ERR_IO_PENDING)
116 read_callback_ = std::move(callback);
117 return rv;
118 }
119
Disconnect()120 void SpdyProxyClientSocket::Disconnect() {
121 read_buffer_queue_.Clear();
122 user_buffer_ = nullptr;
123 user_buffer_len_ = 0;
124 read_callback_.Reset();
125
126 write_buffer_len_ = 0;
127 write_callback_.Reset();
128
129 next_state_ = STATE_DISCONNECTED;
130
131 if (spdy_stream_.get()) {
132 // This will cause OnClose to be invoked, which takes care of
133 // cleaning up all the internal state.
134 spdy_stream_->Cancel(ERR_ABORTED);
135 DCHECK(!spdy_stream_.get());
136 }
137 }
138
IsConnected() const139 bool SpdyProxyClientSocket::IsConnected() const {
140 return next_state_ == STATE_OPEN;
141 }
142
IsConnectedAndIdle() const143 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
144 return IsConnected() && read_buffer_queue_.IsEmpty() &&
145 spdy_stream_->IsOpen();
146 }
147
NetLog() const148 const NetLogWithSource& SpdyProxyClientSocket::NetLog() const {
149 return net_log_;
150 }
151
WasEverUsed() const152 bool SpdyProxyClientSocket::WasEverUsed() const {
153 return was_ever_used_ || (spdy_stream_.get() && spdy_stream_->WasEverUsed());
154 }
155
GetNegotiatedProtocol() const156 NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const {
157 // Do not delegate to `spdy_stream_`. While `spdy_stream_` negotiated ALPN
158 // with the proxy, this object represents the tunneled TCP connection to the
159 // origin.
160 return kProtoUnknown;
161 }
162
GetSSLInfo(SSLInfo * ssl_info)163 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
164 // Do not delegate to `spdy_stream_`. While `spdy_stream_` connected to the
165 // proxy with TLS, this object represents the tunneled TCP connection to the
166 // origin.
167 return false;
168 }
169
GetTotalReceivedBytes() const170 int64_t SpdyProxyClientSocket::GetTotalReceivedBytes() const {
171 NOTIMPLEMENTED();
172 return 0;
173 }
174
ApplySocketTag(const SocketTag & tag)175 void SpdyProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
176 // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the
177 // underlying socket may multiplex multiple streams, applying this request's
178 // socket tag to the multiplexed session would incorrectly apply the socket
179 // tag to all mutliplexed streams. Fortunately socket tagging is only
180 // supported on Android without the data reduction proxy, so only simple HTTP
181 // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce
182 // that a specific (non-default) tag isn't being applied.
183 CHECK(tag == SocketTag());
184 }
185
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)186 int SpdyProxyClientSocket::Read(IOBuffer* buf,
187 int buf_len,
188 CompletionOnceCallback callback) {
189 int rv = ReadIfReady(buf, buf_len, std::move(callback));
190 if (rv == ERR_IO_PENDING) {
191 user_buffer_ = buf;
192 user_buffer_len_ = static_cast<size_t>(buf_len);
193 }
194 return rv;
195 }
196
ReadIfReady(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)197 int SpdyProxyClientSocket::ReadIfReady(IOBuffer* buf,
198 int buf_len,
199 CompletionOnceCallback callback) {
200 DCHECK(!read_callback_);
201 DCHECK(!user_buffer_);
202
203 if (next_state_ == STATE_DISCONNECTED)
204 return ERR_SOCKET_NOT_CONNECTED;
205
206 if (next_state_ == STATE_CLOSED && read_buffer_queue_.IsEmpty()) {
207 return 0;
208 }
209
210 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
211 DCHECK(buf);
212 size_t result = PopulateUserReadBuffer(buf->data(), buf_len);
213 if (result == 0) {
214 read_callback_ = std::move(callback);
215 return ERR_IO_PENDING;
216 }
217 return result;
218 }
219
CancelReadIfReady()220 int SpdyProxyClientSocket::CancelReadIfReady() {
221 // Only a pending ReadIfReady() can be canceled.
222 DCHECK(!user_buffer_) << "Pending Read() cannot be canceled";
223 read_callback_.Reset();
224 return OK;
225 }
226
PopulateUserReadBuffer(char * data,size_t len)227 size_t SpdyProxyClientSocket::PopulateUserReadBuffer(char* data, size_t len) {
228 return read_buffer_queue_.Dequeue(data, len);
229 }
230
Write(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag & traffic_annotation)231 int SpdyProxyClientSocket::Write(
232 IOBuffer* buf,
233 int buf_len,
234 CompletionOnceCallback callback,
235 const NetworkTrafficAnnotationTag& traffic_annotation) {
236 DCHECK(write_callback_.is_null());
237 if (next_state_ != STATE_OPEN)
238 return ERR_SOCKET_NOT_CONNECTED;
239 if (end_stream_state_ == EndStreamState::kEndStreamSent)
240 return ERR_CONNECTION_CLOSED;
241
242 DCHECK(spdy_stream_.get());
243 spdy_stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND);
244 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
245 buf->data());
246 write_callback_ = std::move(callback);
247 write_buffer_len_ = buf_len;
248 return ERR_IO_PENDING;
249 }
250
SetReceiveBufferSize(int32_t size)251 int SpdyProxyClientSocket::SetReceiveBufferSize(int32_t size) {
252 // Since this StreamSocket sits on top of a shared SpdySession, it
253 // is not safe for callers to change this underlying socket.
254 return ERR_NOT_IMPLEMENTED;
255 }
256
SetSendBufferSize(int32_t size)257 int SpdyProxyClientSocket::SetSendBufferSize(int32_t size) {
258 // Since this StreamSocket sits on top of a shared SpdySession, it
259 // is not safe for callers to change this underlying socket.
260 return ERR_NOT_IMPLEMENTED;
261 }
262
GetPeerAddress(IPEndPoint * address) const263 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
264 if (!IsConnected())
265 return ERR_SOCKET_NOT_CONNECTED;
266 return spdy_stream_->GetPeerAddress(address);
267 }
268
GetLocalAddress(IPEndPoint * address) const269 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
270 if (!IsConnected())
271 return ERR_SOCKET_NOT_CONNECTED;
272 return spdy_stream_->GetLocalAddress(address);
273 }
274
RunWriteCallback(int result)275 void SpdyProxyClientSocket::RunWriteCallback(int result) {
276 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
277 // `write_callback_` might be consumed by OnClose().
278 if (write_callback_) {
279 std::move(write_callback_).Run(result);
280 }
281 if (!weak_ptr) {
282 // `this` was already destroyed while running `write_callback_`. Must
283 // return immediately without touching any field member.
284 return;
285 }
286
287 if (end_stream_state_ == EndStreamState::kEndStreamReceived) {
288 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
289 FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::MaybeSendEndStream,
290 weak_factory_.GetMutableWeakPtr()));
291 }
292 }
293
OnIOComplete(int result)294 void SpdyProxyClientSocket::OnIOComplete(int result) {
295 DCHECK_NE(STATE_DISCONNECTED, next_state_);
296 int rv = DoLoop(result);
297 if (rv != ERR_IO_PENDING) {
298 std::move(read_callback_).Run(rv);
299 }
300 }
301
DoLoop(int last_io_result)302 int SpdyProxyClientSocket::DoLoop(int last_io_result) {
303 DCHECK_NE(next_state_, STATE_DISCONNECTED);
304 int rv = last_io_result;
305 do {
306 State state = next_state_;
307 next_state_ = STATE_DISCONNECTED;
308 switch (state) {
309 case STATE_GENERATE_AUTH_TOKEN:
310 DCHECK_EQ(OK, rv);
311 rv = DoGenerateAuthToken();
312 break;
313 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
314 rv = DoGenerateAuthTokenComplete(rv);
315 break;
316 case STATE_SEND_REQUEST:
317 DCHECK_EQ(OK, rv);
318 net_log_.BeginEvent(
319 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
320 rv = DoSendRequest();
321 break;
322 case STATE_SEND_REQUEST_COMPLETE:
323 net_log_.EndEventWithNetErrorCode(
324 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
325 rv = DoSendRequestComplete(rv);
326 if (rv >= 0 || rv == ERR_IO_PENDING) {
327 // Emit extra event so can use the same events as
328 // HttpProxyClientSocket.
329 net_log_.BeginEvent(
330 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
331 }
332 break;
333 case STATE_READ_REPLY_COMPLETE:
334 rv = DoReadReplyComplete(rv);
335 net_log_.EndEventWithNetErrorCode(
336 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
337 break;
338 default:
339 NOTREACHED() << "bad state";
340 rv = ERR_UNEXPECTED;
341 break;
342 }
343 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
344 next_state_ != STATE_OPEN);
345 return rv;
346 }
347
DoGenerateAuthToken()348 int SpdyProxyClientSocket::DoGenerateAuthToken() {
349 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
350 return auth_->MaybeGenerateAuthToken(
351 &request_,
352 base::BindOnce(&SpdyProxyClientSocket::OnIOComplete,
353 weak_factory_.GetWeakPtr()),
354 net_log_);
355 }
356
DoGenerateAuthTokenComplete(int result)357 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
358 DCHECK_NE(ERR_IO_PENDING, result);
359 if (result == OK)
360 next_state_ = STATE_SEND_REQUEST;
361 return result;
362 }
363
DoSendRequest()364 int SpdyProxyClientSocket::DoSendRequest() {
365 next_state_ = STATE_SEND_REQUEST_COMPLETE;
366
367 // Add Proxy-Authentication header if necessary.
368 HttpRequestHeaders authorization_headers;
369 if (auth_->HaveAuth()) {
370 auth_->AddAuthorizationHeader(&authorization_headers);
371 }
372
373 if (proxy_delegate_) {
374 HttpRequestHeaders proxy_delegate_headers;
375 proxy_delegate_->OnBeforeTunnelRequest(proxy_chain_, proxy_chain_index_,
376 &proxy_delegate_headers);
377 request_.extra_headers.MergeFrom(proxy_delegate_headers);
378 }
379
380 std::string request_line;
381 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
382 &request_line, &request_.extra_headers);
383
384 NetLogRequestHeaders(net_log_,
385 NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
386 request_line, &request_.extra_headers);
387
388 spdy::Http2HeaderBlock headers;
389 CreateSpdyHeadersFromHttpRequest(request_, std::nullopt,
390 request_.extra_headers, &headers);
391
392 return spdy_stream_->SendRequestHeaders(std::move(headers),
393 MORE_DATA_TO_SEND);
394 }
395
DoSendRequestComplete(int result)396 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
397 if (result < 0)
398 return result;
399
400 // Wait for HEADERS frame from the server
401 next_state_ = STATE_READ_REPLY_COMPLETE;
402 return ERR_IO_PENDING;
403 }
404
DoReadReplyComplete(int result)405 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
406 // We enter this method directly from DoSendRequestComplete, since
407 // we are notified by a callback when the HEADERS frame arrives.
408
409 if (result < 0)
410 return result;
411
412 // Require the "HTTP/1.x" status line for SSL CONNECT.
413 if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
414 return ERR_TUNNEL_CONNECTION_FAILED;
415
416 NetLogResponseHeaders(
417 net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
418 response_.headers.get());
419
420 if (proxy_delegate_) {
421 int rv = proxy_delegate_->OnTunnelHeadersReceived(
422 proxy_chain_, proxy_chain_index_, *response_.headers);
423 if (rv != OK) {
424 DCHECK_NE(ERR_IO_PENDING, rv);
425 return rv;
426 }
427 }
428
429 switch (response_.headers->response_code()) {
430 case 200: // OK
431 next_state_ = STATE_OPEN;
432 return OK;
433
434 case 407: // Proxy Authentication Required
435 next_state_ = STATE_OPEN;
436 SanitizeProxyAuth(response_);
437 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
438
439 default:
440 // Ignore response to avoid letting the proxy impersonate the target
441 // server. (See http://crbug.com/137891.)
442 return ERR_TUNNEL_CONNECTION_FAILED;
443 }
444 }
445
446 // SpdyStream::Delegate methods:
447 // Called when SYN frame has been sent.
448 // Returns true if no more data to be sent after SYN frame.
OnHeadersSent()449 void SpdyProxyClientSocket::OnHeadersSent() {
450 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
451
452 OnIOComplete(OK);
453 }
454
OnEarlyHintsReceived(const spdy::Http2HeaderBlock & headers)455 void SpdyProxyClientSocket::OnEarlyHintsReceived(
456 const spdy::Http2HeaderBlock& headers) {}
457
OnHeadersReceived(const spdy::Http2HeaderBlock & response_headers)458 void SpdyProxyClientSocket::OnHeadersReceived(
459 const spdy::Http2HeaderBlock& response_headers) {
460 // If we've already received the reply, existing headers are too late.
461 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
462 // initial response.
463 if (next_state_ != STATE_READ_REPLY_COMPLETE)
464 return;
465
466 // Save the response
467 const int rv = SpdyHeadersToHttpResponse(response_headers, &response_);
468 DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
469
470 OnIOComplete(OK);
471 }
472
473 // Called when data is received or on EOF (if `buffer is nullptr).
OnDataReceived(std::unique_ptr<SpdyBuffer> buffer)474 void SpdyProxyClientSocket::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
475 if (buffer) {
476 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED,
477 buffer->GetRemainingSize(),
478 buffer->GetRemainingData());
479 read_buffer_queue_.Enqueue(std::move(buffer));
480 } else {
481 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
482 nullptr);
483
484 if (end_stream_state_ == EndStreamState::kNone) {
485 // The peer sent END_STREAM. Schedule a DATA frame with END_STREAM.
486 end_stream_state_ = EndStreamState::kEndStreamReceived;
487 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
488 FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::MaybeSendEndStream,
489 weak_factory_.GetWeakPtr()));
490 }
491 }
492
493 if (read_callback_) {
494 if (user_buffer_) {
495 int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_);
496 user_buffer_ = nullptr;
497 user_buffer_len_ = 0;
498 std::move(read_callback_).Run(rv);
499 } else {
500 // If ReadIfReady() is used instead of Read(), tell the caller that data
501 // is available for reading.
502 std::move(read_callback_).Run(OK);
503 }
504 }
505 }
506
OnDataSent()507 void SpdyProxyClientSocket::OnDataSent() {
508 if (end_stream_state_ == EndStreamState::kEndStreamSent) {
509 CHECK(write_callback_.is_null());
510 return;
511 }
512
513 DCHECK(!write_callback_.is_null());
514
515 int rv = write_buffer_len_;
516 write_buffer_len_ = 0;
517
518 // Proxy write callbacks result in deep callback chains. Post to allow the
519 // stream's write callback chain to unwind (see crbug.com/355511).
520 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
521 FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::RunWriteCallback,
522 weak_factory_.GetWeakPtr(), rv));
523 }
524
OnTrailers(const spdy::Http2HeaderBlock & trailers)525 void SpdyProxyClientSocket::OnTrailers(const spdy::Http2HeaderBlock& trailers) {
526 // |spdy_stream_| is of type SPDY_BIDIRECTIONAL_STREAM, so trailers are
527 // combined with response headers and this method will not be calld.
528 NOTREACHED();
529 }
530
OnClose(int status)531 void SpdyProxyClientSocket::OnClose(int status) {
532 was_ever_used_ = spdy_stream_->WasEverUsed();
533 spdy_stream_.reset();
534
535 bool connecting = next_state_ != STATE_DISCONNECTED &&
536 next_state_ < STATE_OPEN;
537 if (next_state_ == STATE_OPEN)
538 next_state_ = STATE_CLOSED;
539 else
540 next_state_ = STATE_DISCONNECTED;
541
542 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
543 CompletionOnceCallback write_callback = std::move(write_callback_);
544 write_buffer_len_ = 0;
545
546 // If we're in the middle of connecting, we need to make sure
547 // we invoke the connect callback.
548 if (connecting) {
549 DCHECK(!read_callback_.is_null());
550 std::move(read_callback_).Run(status);
551 } else if (!read_callback_.is_null()) {
552 // If we have a read_callback_, the we need to make sure we call it back.
553 OnDataReceived(std::unique_ptr<SpdyBuffer>());
554 }
555 // This may have been deleted by read_callback_, so check first.
556 if (weak_ptr.get() && !write_callback.is_null())
557 std::move(write_callback).Run(ERR_CONNECTION_CLOSED);
558 }
559
CanGreaseFrameType() const560 bool SpdyProxyClientSocket::CanGreaseFrameType() const {
561 return false;
562 }
563
source_dependency() const564 NetLogSource SpdyProxyClientSocket::source_dependency() const {
565 return source_dependency_;
566 }
567
MaybeSendEndStream()568 void SpdyProxyClientSocket::MaybeSendEndStream() {
569 DCHECK_NE(end_stream_state_, EndStreamState::kNone);
570 if (end_stream_state_ == EndStreamState::kEndStreamSent)
571 return;
572
573 if (!spdy_stream_)
574 return;
575
576 // When there is a pending write, wait until the write completes.
577 if (write_callback_)
578 return;
579
580 auto buffer = base::MakeRefCounted<IOBufferWithSize>(/*buffer_size=*/0);
581 spdy_stream_->SendData(buffer.get(), /*length=*/0, NO_MORE_DATA_TO_SEND);
582 end_stream_state_ = EndStreamState::kEndStreamSent;
583 }
584
585 } // namespace net
586