1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef NET_PROXY_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
6#define NET_PROXY_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
7
8#include <set>
9#include <string>
10
11#include "base/memory/ref_counted.h"
12#include "base/memory/scoped_vector.h"
13#include "base/message_loop/message_loop_proxy.h"
14#include "base/threading/non_thread_safe.h"
15#include "base/time/time.h"
16#include "base/timer/timer.h"
17#include "net/proxy/dhcp_proxy_script_fetcher.h"
18
19namespace base {
20class SequencedWorkerPool;
21}
22
23namespace net {
24
25class DhcpProxyScriptAdapterFetcher;
26class URLRequestContext;
27
28// Windows-specific implementation.
29class NET_EXPORT_PRIVATE DhcpProxyScriptFetcherWin
30    : public DhcpProxyScriptFetcher,
31      public base::SupportsWeakPtr<DhcpProxyScriptFetcherWin>,
32      NON_EXPORTED_BASE(public base::NonThreadSafe) {
33 public:
34  // Creates a DhcpProxyScriptFetcherWin that issues requests through
35  // |url_request_context|. |url_request_context| must remain valid for
36  // the lifetime of DhcpProxyScriptFetcherWin.
37  explicit DhcpProxyScriptFetcherWin(URLRequestContext* url_request_context);
38  virtual ~DhcpProxyScriptFetcherWin();
39
40  // DhcpProxyScriptFetcher implementation.
41  int Fetch(base::string16* utf16_text,
42            const net::CompletionCallback& callback) OVERRIDE;
43  void Cancel() OVERRIDE;
44  const GURL& GetPacURL() const OVERRIDE;
45  std::string GetFetcherName() const OVERRIDE;
46
47  // Sets |adapter_names| to contain the name of each network adapter on
48  // this machine that has DHCP enabled and is not a loop-back adapter. Returns
49  // false on error.
50  static bool GetCandidateAdapterNames(std::set<std::string>* adapter_names);
51
52 protected:
53  int num_pending_fetchers() const;
54
55  URLRequestContext* url_request_context() const;
56
57  scoped_refptr<base::TaskRunner> GetTaskRunner();
58
59  // This inner class encapsulate work done on a worker pool thread.
60  // The class calls GetCandidateAdapterNames, which can take a couple of
61  // hundred milliseconds.
62  class NET_EXPORT_PRIVATE AdapterQuery
63      : public base::RefCountedThreadSafe<AdapterQuery> {
64   public:
65    AdapterQuery();
66    virtual ~AdapterQuery();
67
68    // This is the method that runs on the worker pool thread.
69    void GetCandidateAdapterNames();
70
71    // This set is valid after GetCandidateAdapterNames has
72    // been run. Its lifetime is scoped by this object.
73    const std::set<std::string>& adapter_names() const;
74
75   protected:
76    // Virtual method introduced to allow unit testing.
77    virtual bool ImplGetCandidateAdapterNames(
78        std::set<std::string>* adapter_names);
79
80   private:
81    // This is constructed on the originating thread, then used on the
82    // worker thread, then used again on the originating thread only when
83    // the task has completed on the worker thread. No locking required.
84    std::set<std::string> adapter_names_;
85
86    DISALLOW_COPY_AND_ASSIGN(AdapterQuery);
87  };
88
89  // Virtual methods introduced to allow unit testing.
90  virtual DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher();
91  virtual AdapterQuery* ImplCreateAdapterQuery();
92  virtual base::TimeDelta ImplGetMaxWait();
93  virtual void ImplOnGetCandidateAdapterNamesDone() {}
94
95 private:
96  // Event/state transition handlers
97  void CancelImpl();
98  void OnGetCandidateAdapterNamesDone(scoped_refptr<AdapterQuery> query);
99  void OnFetcherDone(int result);
100  void OnWaitTimer();
101  void TransitionToDone();
102
103  // This is the outer state machine for fetching PAC configuration from
104  // DHCP.  It relies for sub-states on the state machine of the
105  // DhcpProxyScriptAdapterFetcher class.
106  //
107  // The goal of the implementation is to the following work in parallel
108  // for all network adapters that are using DHCP:
109  // a) Try to get the PAC URL configured in DHCP;
110  // b) If one is configured, try to fetch the PAC URL.
111  // c) Once this is done for all adapters, or a timeout has passed after
112  //    it has completed for the fastest adapter, return the PAC file
113  //    available for the most preferred network adapter, if any.
114  //
115  // The state machine goes from START->WAIT_ADAPTERS when it starts a
116  // worker thread to get the list of adapters with DHCP enabled.
117  // It then goes from WAIT_ADAPTERS->NO_RESULTS when it creates
118  // and starts an DhcpProxyScriptAdapterFetcher for each adapter.  It goes
119  // from NO_RESULTS->SOME_RESULTS when it gets the first result; at this
120  // point a wait timer is started.  It goes from SOME_RESULTS->DONE in
121  // two cases: All results are known, or the wait timer expired.  A call
122  // to Cancel() will also go straight to DONE from any state.  Any
123  // way the DONE state is entered, we will at that point cancel any
124  // outstanding work and return the best known PAC script or the empty
125  // string.
126  //
127  // The state machine is reset for each Fetch(), a call to which is
128  // only valid in states START and DONE, as only one Fetch() is
129  // allowed to be outstanding at any given time.
130  enum State {
131    STATE_START,
132    STATE_WAIT_ADAPTERS,
133    STATE_NO_RESULTS,
134    STATE_SOME_RESULTS,
135    STATE_DONE,
136  };
137
138  // Current state of this state machine.
139  State state_;
140
141  // Vector, in Windows' network adapter preference order, of
142  // DhcpProxyScriptAdapterFetcher objects that are or were attempting
143  // to fetch a PAC file based on DHCP configuration.
144  typedef ScopedVector<DhcpProxyScriptAdapterFetcher> FetcherVector;
145  FetcherVector fetchers_;
146
147  // Number of fetchers we are waiting for.
148  int num_pending_fetchers_;
149
150  // Lets our client know we're done. Not valid in states START or DONE.
151  net::CompletionCallback callback_;
152
153  // Pointer to string we will write results to. Not valid in states
154  // START and DONE.
155  base::string16* destination_string_;
156
157  // PAC URL retrieved from DHCP, if any. Valid only in state STATE_DONE.
158  GURL pac_url_;
159
160  base::OneShotTimer<DhcpProxyScriptFetcherWin> wait_timer_;
161
162  URLRequestContext* const url_request_context_;
163
164  // NULL or the AdapterQuery currently in flight.
165  scoped_refptr<AdapterQuery> last_query_;
166
167  // Time |Fetch()| was last called, 0 if never.
168  base::TimeTicks fetch_start_time_;
169
170  // Worker pool we use for all DHCP lookup tasks.
171  scoped_refptr<base::SequencedWorkerPool> worker_pool_;
172
173  DISALLOW_IMPLICIT_CONSTRUCTORS(DhcpProxyScriptFetcherWin);
174};
175
176}  // namespace net
177
178#endif  // NET_PROXY_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
179