xref: /aosp_15_r20/external/cronet/net/spdy/spdy_proxy_client_socket.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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