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