Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2011 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 #include <vector>
      6 
      7 #include "base/string_util.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "net/base/net_errors.h"
     10 #include "net/base/net_log.h"
     11 #include "net/base/net_log_unittest.h"
     12 #include "net/base/test_completion_callback.h"
     13 #include "net/proxy/init_proxy_resolver.h"
     14 #include "net/proxy/proxy_config.h"
     15 #include "net/proxy/proxy_resolver.h"
     16 #include "net/proxy/proxy_script_fetcher.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace net {
     20 namespace {
     21 
     22 enum Error {
     23   kFailedDownloading = -100,
     24   kFailedParsing = -200,
     25 };
     26 
     27 class Rules {
     28  public:
     29   struct Rule {
     30     Rule(const GURL& url,
     31          int fetch_error,
     32          int set_pac_error)
     33         : url(url),
     34           fetch_error(fetch_error),
     35           set_pac_error(set_pac_error) {
     36     }
     37 
     38     string16 text() const {
     39       if (set_pac_error == OK)
     40         return UTF8ToUTF16(url.spec() + "!valid-script");
     41       if (fetch_error == OK)
     42         return UTF8ToUTF16(url.spec() + "!invalid-script");
     43       return string16();
     44     }
     45 
     46     GURL url;
     47     int fetch_error;
     48     int set_pac_error;
     49   };
     50 
     51   Rule AddSuccessRule(const char* url) {
     52     Rule rule(GURL(url), OK /*fetch_error*/, OK /*set_pac_error*/);
     53     rules_.push_back(rule);
     54     return rule;
     55   }
     56 
     57   void AddFailDownloadRule(const char* url) {
     58     rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
     59         ERR_UNEXPECTED /*set_pac_error*/));
     60   }
     61 
     62   void AddFailParsingRule(const char* url) {
     63     rules_.push_back(Rule(GURL(url), OK /*fetch_error*/,
     64         kFailedParsing /*set_pac_error*/));
     65   }
     66 
     67   const Rule& GetRuleByUrl(const GURL& url) const {
     68     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     69          ++it) {
     70       if (it->url == url)
     71         return *it;
     72     }
     73     LOG(FATAL) << "Rule not found for " << url;
     74     return rules_[0];
     75   }
     76 
     77   const Rule& GetRuleByText(const string16& text) const {
     78     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     79          ++it) {
     80       if (it->text() == text)
     81         return *it;
     82     }
     83     LOG(FATAL) << "Rule not found for " << text;
     84     return rules_[0];
     85   }
     86 
     87  private:
     88   typedef std::vector<Rule> RuleList;
     89   RuleList rules_;
     90 };
     91 
     92 class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
     93  public:
     94   explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
     95 
     96   // ProxyScriptFetcher implementation.
     97   virtual int Fetch(const GURL& url,
     98                     string16* text,
     99                     CompletionCallback* callback) {
    100     const Rules::Rule& rule = rules_->GetRuleByUrl(url);
    101     int rv = rule.fetch_error;
    102     EXPECT_NE(ERR_UNEXPECTED, rv);
    103     if (rv == OK)
    104       *text = rule.text();
    105     return rv;
    106   }
    107 
    108   virtual void Cancel() {}
    109 
    110   virtual URLRequestContext* GetRequestContext() { return NULL; }
    111 
    112  private:
    113   const Rules* rules_;
    114 };
    115 
    116 class RuleBasedProxyResolver : public ProxyResolver {
    117  public:
    118   RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes)
    119       : ProxyResolver(expects_pac_bytes), rules_(rules) {}
    120 
    121   // ProxyResolver implementation:
    122   virtual int GetProxyForURL(const GURL& /*url*/,
    123                              ProxyInfo* /*results*/,
    124                              CompletionCallback* /*callback*/,
    125                              RequestHandle* /*request_handle*/,
    126                              const BoundNetLog& /*net_log*/) {
    127     NOTREACHED();
    128     return ERR_UNEXPECTED;
    129   }
    130 
    131   virtual void CancelRequest(RequestHandle request_handle) {
    132     NOTREACHED();
    133   }
    134 
    135   virtual void CancelSetPacScript() {
    136     NOTREACHED();
    137   }
    138 
    139   virtual int SetPacScript(
    140       const scoped_refptr<ProxyResolverScriptData>& script_data,
    141       CompletionCallback* callback) {
    142 
    143    const GURL url =
    144       script_data->type() == ProxyResolverScriptData::TYPE_SCRIPT_URL ?
    145           script_data->url() : GURL();
    146 
    147     const Rules::Rule& rule = expects_pac_bytes() ?
    148         rules_->GetRuleByText(script_data->utf16()) :
    149         rules_->GetRuleByUrl(url);
    150 
    151     int rv = rule.set_pac_error;
    152     EXPECT_NE(ERR_UNEXPECTED, rv);
    153 
    154     if (expects_pac_bytes()) {
    155       EXPECT_EQ(rule.text(), script_data->utf16());
    156     } else {
    157       EXPECT_EQ(rule.url, url);
    158     }
    159 
    160     if (rv == OK)
    161       script_data_ = script_data;
    162     return rv;
    163   }
    164 
    165   const ProxyResolverScriptData* script_data() const { return script_data_; }
    166 
    167  private:
    168   const Rules* rules_;
    169   scoped_refptr<ProxyResolverScriptData> script_data_;
    170 };
    171 
    172 // Succeed using custom PAC script.
    173 TEST(InitProxyResolverTest, CustomPacSucceeds) {
    174   Rules rules;
    175   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    176   RuleBasedProxyScriptFetcher fetcher(&rules);
    177 
    178   ProxyConfig config;
    179   config.set_pac_url(GURL("http://custom/proxy.pac"));
    180 
    181   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    182 
    183   TestCompletionCallback callback;
    184   CapturingNetLog log(CapturingNetLog::kUnbounded);
    185   InitProxyResolver init(&resolver, &fetcher, &log);
    186   EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
    187   EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
    188 
    189   // Check the NetLog was filled correctly.
    190   CapturingNetLog::EntryList entries;
    191   log.GetEntries(&entries);
    192 
    193   EXPECT_EQ(6u, entries.size());
    194   EXPECT_TRUE(LogContainsBeginEvent(
    195       entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
    196   EXPECT_TRUE(LogContainsBeginEvent(
    197       entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    198   EXPECT_TRUE(LogContainsEndEvent(
    199       entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    200   EXPECT_TRUE(LogContainsBeginEvent(
    201       entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    202   EXPECT_TRUE(LogContainsEndEvent(
    203       entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    204   EXPECT_TRUE(LogContainsEndEvent(
    205       entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
    206 }
    207 
    208 // Fail downloading the custom PAC script.
    209 TEST(InitProxyResolverTest, CustomPacFails1) {
    210   Rules rules;
    211   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    212   RuleBasedProxyScriptFetcher fetcher(&rules);
    213 
    214   ProxyConfig config;
    215   config.set_pac_url(GURL("http://custom/proxy.pac"));
    216 
    217   rules.AddFailDownloadRule("http://custom/proxy.pac");
    218 
    219   TestCompletionCallback callback;
    220   CapturingNetLog log(CapturingNetLog::kUnbounded);
    221   InitProxyResolver init(&resolver, &fetcher, &log);
    222   EXPECT_EQ(kFailedDownloading,
    223             init.Init(config, base::TimeDelta(), NULL, &callback));
    224   EXPECT_EQ(NULL, resolver.script_data());
    225 
    226   // Check the NetLog was filled correctly.
    227   CapturingNetLog::EntryList entries;
    228   log.GetEntries(&entries);
    229 
    230   EXPECT_EQ(4u, entries.size());
    231   EXPECT_TRUE(LogContainsBeginEvent(
    232       entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
    233   EXPECT_TRUE(LogContainsBeginEvent(
    234       entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    235   EXPECT_TRUE(LogContainsEndEvent(
    236       entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    237   EXPECT_TRUE(LogContainsEndEvent(
    238       entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
    239 }
    240 
    241 // Fail parsing the custom PAC script.
    242 TEST(InitProxyResolverTest, CustomPacFails2) {
    243   Rules rules;
    244   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    245   RuleBasedProxyScriptFetcher fetcher(&rules);
    246 
    247   ProxyConfig config;
    248   config.set_pac_url(GURL("http://custom/proxy.pac"));
    249 
    250   rules.AddFailParsingRule("http://custom/proxy.pac");
    251 
    252   TestCompletionCallback callback;
    253   InitProxyResolver init(&resolver, &fetcher, NULL);
    254   EXPECT_EQ(kFailedParsing,
    255             init.Init(config, base::TimeDelta(), NULL, &callback));
    256   EXPECT_EQ(NULL, resolver.script_data());
    257 }
    258 
    259 // Fail downloading the custom PAC script, because the fetcher was NULL.
    260 TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) {
    261   Rules rules;
    262   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    263 
    264   ProxyConfig config;
    265   config.set_pac_url(GURL("http://custom/proxy.pac"));
    266 
    267   TestCompletionCallback callback;
    268   InitProxyResolver init(&resolver, NULL, NULL);
    269   EXPECT_EQ(ERR_UNEXPECTED,
    270             init.Init(config, base::TimeDelta(), NULL, &callback));
    271   EXPECT_EQ(NULL, resolver.script_data());
    272 }
    273 
    274 // Succeeds in choosing autodetect (wpad).
    275 TEST(InitProxyResolverTest, AutodetectSuccess) {
    276   Rules rules;
    277   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    278   RuleBasedProxyScriptFetcher fetcher(&rules);
    279 
    280   ProxyConfig config;
    281   config.set_auto_detect(true);
    282 
    283   Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
    284 
    285   TestCompletionCallback callback;
    286   InitProxyResolver init(&resolver, &fetcher, NULL);
    287   EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
    288   EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
    289 }
    290 
    291 // Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
    292 TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
    293   Rules rules;
    294   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    295   RuleBasedProxyScriptFetcher fetcher(&rules);
    296 
    297   ProxyConfig config;
    298   config.set_auto_detect(true);
    299   config.set_pac_url(GURL("http://custom/proxy.pac"));
    300 
    301   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    302   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    303 
    304   TestCompletionCallback callback;
    305   InitProxyResolver init(&resolver, &fetcher, NULL);
    306   EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
    307   EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
    308 }
    309 
    310 // Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
    311 TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
    312   Rules rules;
    313   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    314   RuleBasedProxyScriptFetcher fetcher(&rules);
    315 
    316   ProxyConfig config;
    317   config.set_auto_detect(true);
    318   config.set_pac_url(GURL("http://custom/proxy.pac"));
    319   config.proxy_rules().ParseFromString("unused-manual-proxy:99");
    320 
    321   rules.AddFailParsingRule("http://wpad/wpad.dat");
    322   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    323 
    324   TestCompletionCallback callback;
    325   CapturingNetLog log(CapturingNetLog::kUnbounded);
    326 
    327   ProxyConfig effective_config;
    328   InitProxyResolver init(&resolver, &fetcher, &log);
    329   EXPECT_EQ(OK, init.Init(config, base::TimeDelta(),
    330                           &effective_config, &callback));
    331   EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
    332 
    333   // Verify that the effective configuration no longer contains auto detect or
    334   // any of the manual settings.
    335   EXPECT_TRUE(effective_config.Equals(
    336       ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
    337 
    338   // Check the NetLog was filled correctly.
    339   // (Note that the Fetch and Set states are repeated since both WPAD and custom
    340   // PAC scripts are tried).
    341   CapturingNetLog::EntryList entries;
    342   log.GetEntries(&entries);
    343 
    344   EXPECT_EQ(11u, entries.size());
    345   EXPECT_TRUE(LogContainsBeginEvent(
    346       entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
    347   EXPECT_TRUE(LogContainsBeginEvent(
    348       entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    349   EXPECT_TRUE(LogContainsEndEvent(
    350       entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    351   EXPECT_TRUE(LogContainsBeginEvent(
    352       entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    353   EXPECT_TRUE(LogContainsEndEvent(
    354       entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    355   EXPECT_TRUE(LogContainsEvent(
    356       entries, 5,
    357       NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL,
    358       NetLog::PHASE_NONE));
    359   EXPECT_TRUE(LogContainsBeginEvent(
    360       entries, 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    361   EXPECT_TRUE(LogContainsEndEvent(
    362       entries, 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    363   EXPECT_TRUE(LogContainsBeginEvent(
    364       entries, 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    365   EXPECT_TRUE(LogContainsEndEvent(
    366       entries, 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
    367   EXPECT_TRUE(LogContainsEndEvent(
    368       entries, 10, NetLog::TYPE_INIT_PROXY_RESOLVER));
    369 }
    370 
    371 // Fails at WPAD (downloading), and fails at custom PAC (downloading).
    372 TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
    373   Rules rules;
    374   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    375   RuleBasedProxyScriptFetcher fetcher(&rules);
    376 
    377   ProxyConfig config;
    378   config.set_auto_detect(true);
    379   config.set_pac_url(GURL("http://custom/proxy.pac"));
    380 
    381   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    382   rules.AddFailDownloadRule("http://custom/proxy.pac");
    383 
    384   TestCompletionCallback callback;
    385   InitProxyResolver init(&resolver, &fetcher, NULL);
    386   EXPECT_EQ(kFailedDownloading,
    387             init.Init(config, base::TimeDelta(), NULL, &callback));
    388   EXPECT_EQ(NULL, resolver.script_data());
    389 }
    390 
    391 // Fails at WPAD (downloading), and fails at custom PAC (parsing).
    392 TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
    393   Rules rules;
    394   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    395   RuleBasedProxyScriptFetcher fetcher(&rules);
    396 
    397   ProxyConfig config;
    398   config.set_auto_detect(true);
    399   config.set_pac_url(GURL("http://custom/proxy.pac"));
    400 
    401   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    402   rules.AddFailParsingRule("http://custom/proxy.pac");
    403 
    404   TestCompletionCallback callback;
    405   InitProxyResolver init(&resolver, &fetcher, NULL);
    406   EXPECT_EQ(kFailedParsing,
    407             init.Init(config, base::TimeDelta(), NULL, &callback));
    408   EXPECT_EQ(NULL, resolver.script_data());
    409 }
    410 
    411 // Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
    412 // This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver
    413 // that doesn't |expects_pac_bytes|.
    414 TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
    415   Rules rules;
    416   RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
    417   RuleBasedProxyScriptFetcher fetcher(&rules);
    418 
    419   ProxyConfig config;
    420   config.set_auto_detect(true);
    421   config.set_pac_url(GURL("http://custom/proxy.pac"));
    422 
    423   rules.AddFailParsingRule("");  // Autodetect.
    424   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    425 
    426   TestCompletionCallback callback;
    427   InitProxyResolver init(&resolver, &fetcher, NULL);
    428   EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
    429   EXPECT_EQ(rule.url, resolver.script_data()->url());
    430 }
    431 
    432 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    433 // a 1 millisecond delay. This means it will now complete asynchronously.
    434 // Moreover, we test the NetLog to make sure it logged the pause.
    435 TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) {
    436   Rules rules;
    437   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    438   RuleBasedProxyScriptFetcher fetcher(&rules);
    439 
    440   ProxyConfig config;
    441   config.set_pac_url(GURL("http://custom/proxy.pac"));
    442 
    443   rules.AddFailDownloadRule("http://custom/proxy.pac");
    444 
    445   TestCompletionCallback callback;
    446   CapturingNetLog log(CapturingNetLog::kUnbounded);
    447   InitProxyResolver init(&resolver, &fetcher, &log);
    448   EXPECT_EQ(ERR_IO_PENDING,
    449             init.Init(config, base::TimeDelta::FromMilliseconds(1),
    450                       NULL, &callback));
    451 
    452   EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
    453   EXPECT_EQ(NULL, resolver.script_data());
    454 
    455   // Check the NetLog was filled correctly.
    456   CapturingNetLog::EntryList entries;
    457   log.GetEntries(&entries);
    458 
    459   EXPECT_EQ(6u, entries.size());
    460   EXPECT_TRUE(LogContainsBeginEvent(
    461       entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
    462   EXPECT_TRUE(LogContainsBeginEvent(
    463       entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
    464   EXPECT_TRUE(LogContainsEndEvent(
    465       entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
    466   EXPECT_TRUE(LogContainsBeginEvent(
    467       entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    468   EXPECT_TRUE(LogContainsEndEvent(
    469       entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    470   EXPECT_TRUE(LogContainsEndEvent(
    471       entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
    472 }
    473 
    474 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    475 // a -5 second delay instead of a 0 ms delay. This change should have no effect
    476 // so the rest of the test is unchanged.
    477 TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
    478   Rules rules;
    479   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
    480   RuleBasedProxyScriptFetcher fetcher(&rules);
    481 
    482   ProxyConfig config;
    483   config.set_pac_url(GURL("http://custom/proxy.pac"));
    484 
    485   rules.AddFailDownloadRule("http://custom/proxy.pac");
    486 
    487   TestCompletionCallback callback;
    488   CapturingNetLog log(CapturingNetLog::kUnbounded);
    489   InitProxyResolver init(&resolver, &fetcher, &log);
    490   EXPECT_EQ(kFailedDownloading,
    491             init.Init(config, base::TimeDelta::FromSeconds(-5),
    492                       NULL, &callback));
    493   EXPECT_EQ(NULL, resolver.script_data());
    494 
    495   // Check the NetLog was filled correctly.
    496   CapturingNetLog::EntryList entries;
    497   log.GetEntries(&entries);
    498 
    499   EXPECT_EQ(4u, entries.size());
    500   EXPECT_TRUE(LogContainsBeginEvent(
    501       entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
    502   EXPECT_TRUE(LogContainsBeginEvent(
    503       entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    504   EXPECT_TRUE(LogContainsEndEvent(
    505       entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
    506   EXPECT_TRUE(LogContainsEndEvent(
    507       entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
    508 }
    509 
    510 }  // namespace
    511 }  // namespace net
    512