• 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_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     }
341   } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
342            next_state_ != STATE_OPEN);
343   return rv;
344 }
345 
DoGenerateAuthToken()346 int SpdyProxyClientSocket::DoGenerateAuthToken() {
347   next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
348   return auth_->MaybeGenerateAuthToken(
349       &request_,
350       base::BindOnce(&SpdyProxyClientSocket::OnIOComplete,
351                      weak_factory_.GetWeakPtr()),
352       net_log_);
353 }
354 
DoGenerateAuthTokenComplete(int result)355 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
356   DCHECK_NE(ERR_IO_PENDING, result);
357   if (result == OK)
358     next_state_ = STATE_SEND_REQUEST;
359   return result;
360 }
361 
DoSendRequest()362 int SpdyProxyClientSocket::DoSendRequest() {
363   next_state_ = STATE_SEND_REQUEST_COMPLETE;
364 
365   // Add Proxy-Authentication header if necessary.
366   HttpRequestHeaders authorization_headers;
367   if (auth_->HaveAuth()) {
368     auth_->AddAuthorizationHeader(&authorization_headers);
369   }
370 
371   if (proxy_delegate_) {
372     HttpRequestHeaders proxy_delegate_headers;
373     int result = proxy_delegate_->OnBeforeTunnelRequest(
374         proxy_chain_, proxy_chain_index_, &proxy_delegate_headers);
375     if (result < 0) {
376       return result;
377     }
378     request_.extra_headers.MergeFrom(proxy_delegate_headers);
379   }
380 
381   std::string request_line;
382   BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
383                      &request_line, &request_.extra_headers);
384 
385   NetLogRequestHeaders(net_log_,
386                        NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
387                        request_line, &request_.extra_headers);
388 
389   quiche::HttpHeaderBlock headers;
390   CreateSpdyHeadersFromHttpRequest(request_, std::nullopt,
391                                    request_.extra_headers, &headers);
392 
393   return spdy_stream_->SendRequestHeaders(std::move(headers),
394                                           MORE_DATA_TO_SEND);
395 }
396 
DoSendRequestComplete(int result)397 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
398   if (result < 0)
399     return result;
400 
401   // Wait for HEADERS frame from the server
402   next_state_ = STATE_READ_REPLY_COMPLETE;
403   return ERR_IO_PENDING;
404 }
405 
DoReadReplyComplete(int result)406 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
407   // We enter this method directly from DoSendRequestComplete, since
408   // we are notified by a callback when the HEADERS frame arrives.
409 
410   if (result < 0)
411     return result;
412 
413   // Require the "HTTP/1.x" status line for SSL CONNECT.
414   if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
415     return ERR_TUNNEL_CONNECTION_FAILED;
416 
417   NetLogResponseHeaders(
418       net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
419       response_.headers.get());
420 
421   if (proxy_delegate_) {
422     int rv = proxy_delegate_->OnTunnelHeadersReceived(
423         proxy_chain_, proxy_chain_index_, *response_.headers);
424     if (rv != OK) {
425       DCHECK_NE(ERR_IO_PENDING, rv);
426       return rv;
427     }
428   }
429 
430   switch (response_.headers->response_code()) {
431     case 200:  // OK
432       next_state_ = STATE_OPEN;
433       return OK;
434 
435     case 407:  // Proxy Authentication Required
436       next_state_ = STATE_OPEN;
437       SanitizeProxyAuth(response_);
438       return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
439 
440     default:
441       // Ignore response to avoid letting the proxy impersonate the target
442       // server.  (See http://crbug.com/137891.)
443       return ERR_TUNNEL_CONNECTION_FAILED;
444   }
445 }
446 
447 // SpdyStream::Delegate methods:
448 // Called when SYN frame has been sent.
449 // Returns true if no more data to be sent after SYN frame.
OnHeadersSent()450 void SpdyProxyClientSocket::OnHeadersSent() {
451   DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
452 
453   OnIOComplete(OK);
454 }
455 
OnEarlyHintsReceived(const quiche::HttpHeaderBlock & headers)456 void SpdyProxyClientSocket::OnEarlyHintsReceived(
457     const quiche::HttpHeaderBlock& headers) {}
458 
OnHeadersReceived(const quiche::HttpHeaderBlock & response_headers)459 void SpdyProxyClientSocket::OnHeadersReceived(
460     const quiche::HttpHeaderBlock& response_headers) {
461   // If we've already received the reply, existing headers are too late.
462   // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
463   //                initial response.
464   if (next_state_ != STATE_READ_REPLY_COMPLETE)
465     return;
466 
467   // Save the response
468   const int rv = SpdyHeadersToHttpResponse(response_headers, &response_);
469   DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
470 
471   OnIOComplete(OK);
472 }
473 
474 // Called when data is received or on EOF (if `buffer is nullptr).
OnDataReceived(std::unique_ptr<SpdyBuffer> buffer)475 void SpdyProxyClientSocket::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
476   if (buffer) {
477     net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED,
478                                   buffer->GetRemainingSize(),
479                                   buffer->GetRemainingData());
480     read_buffer_queue_.Enqueue(std::move(buffer));
481   } else {
482     net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
483                                   nullptr);
484 
485     if (end_stream_state_ == EndStreamState::kNone) {
486       // The peer sent END_STREAM. Schedule a DATA frame with END_STREAM.
487       end_stream_state_ = EndStreamState::kEndStreamReceived;
488       base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
489           FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::MaybeSendEndStream,
490                                     weak_factory_.GetWeakPtr()));
491     }
492   }
493 
494   if (read_callback_) {
495     if (user_buffer_) {
496       int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_);
497       user_buffer_ = nullptr;
498       user_buffer_len_ = 0;
499       std::move(read_callback_).Run(rv);
500     } else {
501       // If ReadIfReady() is used instead of Read(), tell the caller that data
502       // is available for reading.
503       std::move(read_callback_).Run(OK);
504     }
505   }
506 }
507 
OnDataSent()508 void SpdyProxyClientSocket::OnDataSent() {
509   if (end_stream_state_ == EndStreamState::kEndStreamSent) {
510     CHECK(write_callback_.is_null());
511     return;
512   }
513 
514   DCHECK(!write_callback_.is_null());
515 
516   int rv = write_buffer_len_;
517   write_buffer_len_ = 0;
518 
519   // Proxy write callbacks result in deep callback chains. Post to allow the
520   // stream's write callback chain to unwind (see crbug.com/355511).
521   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
522       FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::RunWriteCallback,
523                                 weak_factory_.GetWeakPtr(), rv));
524 }
525 
OnTrailers(const quiche::HttpHeaderBlock & trailers)526 void SpdyProxyClientSocket::OnTrailers(
527     const quiche::HttpHeaderBlock& trailers) {
528   // |spdy_stream_| is of type SPDY_BIDIRECTIONAL_STREAM, so trailers are
529   // combined with response headers and this method will not be calld.
530   DUMP_WILL_BE_NOTREACHED();
531 }
532 
OnClose(int status)533 void SpdyProxyClientSocket::OnClose(int status)  {
534   was_ever_used_ = spdy_stream_->WasEverUsed();
535   spdy_stream_.reset();
536 
537   bool connecting = next_state_ != STATE_DISCONNECTED &&
538       next_state_ < STATE_OPEN;
539   if (next_state_ == STATE_OPEN)
540     next_state_ = STATE_CLOSED;
541   else
542     next_state_ = STATE_DISCONNECTED;
543 
544   base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
545   CompletionOnceCallback write_callback = std::move(write_callback_);
546   write_buffer_len_ = 0;
547 
548   // If we're in the middle of connecting, we need to make sure
549   // we invoke the connect callback.
550   if (connecting) {
551     DCHECK(!read_callback_.is_null());
552     std::move(read_callback_).Run(status);
553   } else if (!read_callback_.is_null()) {
554     // If we have a read_callback_, the we need to make sure we call it back.
555     OnDataReceived(std::unique_ptr<SpdyBuffer>());
556   }
557   // This may have been deleted by read_callback_, so check first.
558   if (weak_ptr.get() && !write_callback.is_null())
559     std::move(write_callback).Run(ERR_CONNECTION_CLOSED);
560 }
561 
CanGreaseFrameType() const562 bool SpdyProxyClientSocket::CanGreaseFrameType() const {
563   return false;
564 }
565 
source_dependency() const566 NetLogSource SpdyProxyClientSocket::source_dependency() const {
567   return source_dependency_;
568 }
569 
MaybeSendEndStream()570 void SpdyProxyClientSocket::MaybeSendEndStream() {
571   DCHECK_NE(end_stream_state_, EndStreamState::kNone);
572   if (end_stream_state_ == EndStreamState::kEndStreamSent)
573     return;
574 
575   if (!spdy_stream_)
576     return;
577 
578   // When there is a pending write, wait until the write completes.
579   if (write_callback_)
580     return;
581 
582   auto buffer = base::MakeRefCounted<IOBufferWithSize>(/*buffer_size=*/0);
583   spdy_stream_->SendData(buffer.get(), /*length=*/0, NO_MORE_DATA_TO_SEND);
584   end_stream_state_ = EndStreamState::kEndStreamSent;
585 }
586 
587 }  // namespace net
588