7 from dnsdisttests
import DNSDistTest
9 class TestAPIBasics(DNSDistTest
):
13 _webServerBasicAuthPassword
= 'secret'
14 _webServerAPIKey
= 'apisecret'
15 # paths accessible using the API key only
16 _apiOnlyPaths
= ['/api/v1/servers/localhost/config', '/api/v1/servers/localhost/config/allow-from', '/api/v1/servers/localhost/statistics']
17 # paths accessible using an API key or basic auth
18 _statsPaths
= [ '/jsonstat?command=stats', '/jsonstat?command=dynblocklist', '/api/v1/servers/localhost']
19 # paths accessible using basic auth only (list not exhaustive)
20 _basicOnlyPaths
= ['/', '/index.html']
21 _config_params
= ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
22 _config_template
= """
23 setACL({"127.0.0.1/32", "::1/128"})
24 newServer{address="127.0.0.1:%s"}
25 webserver("127.0.0.1:%s", "%s", "%s")
28 def testBasicAuth(self
):
30 API: Basic Authentication
32 for path
in self
._basicOnlyPaths
+ self
._statsPaths
:
33 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + path
34 r
= requests
.get(url
, auth
=('whatever', "evilsecret"), timeout
=self
._webTimeout
)
35 self
.assertEquals(r
.status_code
, 401)
36 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
38 self
.assertEquals(r
.status_code
, 200)
40 def testXAPIKey(self
):
44 headers
= {'x-api-key': self
._webServerAPIKey
}
45 for path
in self
._apiOnlyPaths
+ self
._statsPaths
:
46 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + path
47 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
49 self
.assertEquals(r
.status_code
, 200)
51 def testWrongXAPIKey(self
):
55 headers
= {'x-api-key': "evilapikey"}
56 for path
in self
._apiOnlyPaths
+ self
._statsPaths
:
57 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + path
58 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
59 self
.assertEquals(r
.status_code
, 401)
61 def testBasicAuthOnly(self
):
63 API: Basic Authentication Only
65 headers
= {'x-api-key': self
._webServerAPIKey
}
66 for path
in self
._basicOnlyPaths
:
67 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + path
68 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
69 self
.assertEquals(r
.status_code
, 401)
71 def testAPIKeyOnly(self
):
75 for path
in self
._apiOnlyPaths
:
76 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + path
77 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
78 self
.assertEquals(r
.status_code
, 401)
80 def testServersLocalhost(self
):
82 API: /api/v1/servers/localhost
84 headers
= {'x-api-key': self
._webServerAPIKey
}
85 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost'
86 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
88 self
.assertEquals(r
.status_code
, 200)
89 self
.assertTrue(r
.json())
92 self
.assertEquals(content
['daemon_type'], 'dnsdist')
94 rule_groups
= ['response-rules', 'cache-hit-response-rules', 'self-answered-response-rules', 'rules']
95 for key
in ['version', 'acl', 'local', 'servers', 'frontends', 'pools'] + rule_groups
:
96 self
.assertIn(key
, content
)
98 for rule_group
in rule_groups
:
99 for rule
in content
[rule_group
]:
100 for key
in ['id', 'creationOrder', 'matches', 'rule', 'action', 'uuid']:
101 self
.assertIn(key
, rule
)
102 for key
in ['id', 'creationOrder', 'matches']:
103 self
.assertTrue(rule
[key
] >= 0)
105 for server
in content
['servers']:
106 for key
in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
107 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order', 'sendErrors',
109 self
.assertIn(key
, server
)
111 for key
in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
112 'qps', 'queries', 'order']:
113 self
.assertTrue(server
[key
] >= 0)
115 self
.assertTrue(server
['state'] in ['up', 'down', 'UP', 'DOWN'])
117 for frontend
in content
['frontends']:
118 for key
in ['id', 'address', 'udp', 'tcp', 'type', 'queries']:
119 self
.assertIn(key
, frontend
)
121 for key
in ['id', 'queries']:
122 self
.assertTrue(frontend
[key
] >= 0)
124 for pool
in content
['pools']:
125 for key
in ['id', 'name', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
126 self
.assertIn(key
, pool
)
128 for key
in ['id', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']:
129 self
.assertTrue(pool
[key
] >= 0)
131 def testServersIDontExist(self
):
133 API: /api/v1/servers/idonotexist (should be 404)
135 headers
= {'x-api-key': self
._webServerAPIKey
}
136 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/idonotexist'
137 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
138 self
.assertEquals(r
.status_code
, 404)
140 def testServersLocalhostConfig(self
):
142 API: /api/v1/servers/localhost/config
144 headers
= {'x-api-key': self
._webServerAPIKey
}
145 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost/config'
146 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
148 self
.assertEquals(r
.status_code
, 200)
149 self
.assertTrue(r
.json())
152 for entry
in content
:
153 for key
in ['type', 'name', 'value']:
154 self
.assertIn(key
, entry
)
156 self
.assertEquals(entry
['type'], 'ConfigSetting')
157 values
[entry
['name']] = entry
['value']
159 for key
in ['acl', 'control-socket', 'ecs-override', 'ecs-source-prefix-v4',
160 'ecs-source-prefix-v6', 'fixup-case', 'max-outstanding', 'server-policy',
161 'stale-cache-entries-ttl', 'tcp-recv-timeout', 'tcp-send-timeout',
162 'truncate-tc', 'verbose', 'verbose-health-checks']:
163 self
.assertIn(key
, values
)
165 for key
in ['max-outstanding', 'stale-cache-entries-ttl', 'tcp-recv-timeout',
167 self
.assertTrue(values
[key
] >= 0)
169 self
.assertTrue(values
['ecs-source-prefix-v4'] >= 0 and values
['ecs-source-prefix-v4'] <= 32)
170 self
.assertTrue(values
['ecs-source-prefix-v6'] >= 0 and values
['ecs-source-prefix-v6'] <= 128)
172 def testServersLocalhostConfigAllowFrom(self
):
174 API: /api/v1/servers/localhost/config/allow-from
176 headers
= {'x-api-key': self
._webServerAPIKey
}
177 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost/config/allow-from'
178 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
180 self
.assertEquals(r
.status_code
, 200)
181 self
.assertTrue(r
.json())
183 for key
in ['type', 'name', 'value']:
184 self
.assertIn(key
, content
)
186 self
.assertEquals(content
['name'], 'allow-from')
187 self
.assertEquals(content
['type'], 'ConfigSetting')
188 acl
= content
['value']
189 expectedACL
= ["127.0.0.1/32", "::1/128"]
192 self
.assertEquals(acl
, expectedACL
)
194 def testServersLocalhostConfigAllowFromPut(self
):
196 API: PUT /api/v1/servers/localhost/config/allow-from (should be refused)
198 The API is read-only by default, so this should be refused
200 newACL
= ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
201 payload
= json
.dumps({"name": "allow-from",
202 "type": "ConfigSetting",
204 headers
= {'x-api-key': self
._webServerAPIKey
}
205 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost/config/allow-from'
206 r
= requests
.put(url
, headers
=headers
, timeout
=self
._webTimeout
, data
=payload
)
208 self
.assertEquals(r
.status_code
, 405)
210 def testServersLocalhostStatistics(self
):
212 API: /api/v1/servers/localhost/statistics
214 headers
= {'x-api-key': self
._webServerAPIKey
}
215 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost/statistics'
216 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
218 self
.assertEquals(r
.status_code
, 200)
219 self
.assertTrue(r
.json())
222 for entry
in content
:
223 self
.assertIn('type', entry
)
224 self
.assertIn('name', entry
)
225 self
.assertIn('value', entry
)
226 self
.assertEquals(entry
['type'], 'StatisticItem')
227 values
[entry
['name']] = entry
['value']
229 expected
= ['responses', 'servfail-responses', 'queries', 'acl-drops',
230 'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
231 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
232 'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
233 'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
234 'latency-slow', 'latency-sum', 'latency-count', 'latency-avg100', 'latency-avg1000',
235 'latency-avg10000', 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
236 'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
237 'cache-misses', 'cpu-iowait', 'cpu-steal', 'cpu-sys-msec', 'cpu-user-msec', 'fd-usage', 'dyn-blocked',
238 'dyn-block-nmg-size', 'rule-servfail', 'security-status',
239 'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors',
240 'doh-query-pipe-full', 'doh-response-pipe-full']
243 self
.assertIn(key
, values
)
244 self
.assertTrue(values
[key
] >= 0)
247 self
.assertIn(key
, expected
)
249 def testJsonstatStats(self
):
251 API: /jsonstat?command=stats
253 headers
= {'x-api-key': self
._webServerAPIKey
}
254 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/jsonstat?command=stats'
255 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
257 self
.assertEquals(r
.status_code
, 200)
258 self
.assertTrue(r
.json())
261 expected
= ['responses', 'servfail-responses', 'queries', 'acl-drops',
262 'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
263 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
264 'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
265 'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
266 'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
267 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
268 'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
269 'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
270 'dyn-block-nmg-size', 'packetcache-hits', 'packetcache-misses', 'over-capacity-drops',
274 self
.assertIn(key
, content
)
275 self
.assertTrue(content
[key
] >= 0)
277 def testJsonstatDynblocklist(self
):
279 API: /jsonstat?command=dynblocklist
281 headers
= {'x-api-key': self
._webServerAPIKey
}
282 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/jsonstat?command=dynblocklist'
283 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
285 self
.assertEquals(r
.status_code
, 200)
290 for key
in ['reason', 'seconds', 'blocks', 'action']:
291 self
.assertIn(key
, content
)
293 for key
in ['blocks']:
294 self
.assertTrue(content
[key
] >= 0)
296 class TestAPIServerDown(DNSDistTest
):
299 _webServerPort
= 8083
300 _webServerBasicAuthPassword
= 'secret'
301 _webServerAPIKey
= 'apisecret'
302 # paths accessible using the API key
303 _config_params
= ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
304 _config_template
= """
305 setACL({"127.0.0.1/32", "::1/128"})
306 newServer{address="127.0.0.1:%s"}
307 getServer(0):setDown()
308 webserver("127.0.0.1:%s", "%s", "%s")
311 def testServerDownNoLatencyLocalhost(self
):
313 API: /api/v1/servers/localhost, no latency for a down server
315 headers
= {'x-api-key': self
._webServerAPIKey
}
316 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost'
317 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
319 self
.assertEquals(r
.status_code
, 200)
320 self
.assertTrue(r
.json())
323 self
.assertEquals(content
['servers'][0]['latency'], None)
325 class TestAPIWritable(DNSDistTest
):
328 _webServerPort
= 8083
329 _webServerBasicAuthPassword
= 'secret'
330 _webServerAPIKey
= 'apisecret'
331 _APIWriteDir
= '/tmp'
332 _config_params
= ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey', '_APIWriteDir']
333 _config_template
= """
334 setACL({"127.0.0.1/32", "::1/128"})
335 newServer{address="127.0.0.1:%s"}
336 webserver("127.0.0.1:%s", "%s", "%s")
337 setAPIWritable(true, "%s")
340 def testSetACL(self
):
344 headers
= {'x-api-key': self
._webServerAPIKey
}
345 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost/config/allow-from'
346 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
348 self
.assertEquals(r
.status_code
, 200)
349 self
.assertTrue(r
.json())
351 acl
= content
['value']
352 expectedACL
= ["127.0.0.1/32", "::1/128"]
355 self
.assertEquals(acl
, expectedACL
)
357 newACL
= ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
358 payload
= json
.dumps({"name": "allow-from",
359 "type": "ConfigSetting",
361 r
= requests
.put(url
, headers
=headers
, timeout
=self
._webTimeout
, data
=payload
)
363 self
.assertEquals(r
.status_code
, 200)
364 self
.assertTrue(r
.json())
366 acl
= content
['value']
368 self
.assertEquals(acl
, newACL
)
370 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
372 self
.assertEquals(r
.status_code
, 200)
373 self
.assertTrue(r
.json())
375 acl
= content
['value']
377 self
.assertEquals(acl
, newACL
)
379 configFile
= self
._APIWriteDir
+ '/' + 'acl.conf'
380 self
.assertTrue(os
.path
.isfile(configFile
))
382 with
open(configFile
, 'rt') as f
:
383 header
= f
.readline()
386 self
.assertEquals(header
, """-- Generated by the REST API, DO NOT EDIT\n""")
388 self
.assertIn(body
, {
389 """setACL({"192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"})\n""",
390 """setACL({"192.0.2.0/24", "203.0.113.0/24", "198.51.100.0/24"})\n""",
391 """setACL({"198.51.100.0/24", "192.0.2.0/24", "203.0.113.0/24"})\n""",
392 """setACL({"198.51.100.0/24", "203.0.113.0/24", "192.0.2.0/24"})\n""",
393 """setACL({"203.0.113.0/24", "192.0.2.0/24", "198.51.100.0/24"})\n""",
394 """setACL({"203.0.113.0/24", "198.51.100.0/24", "192.0.2.0/24"})\n"""
397 class TestAPICustomHeaders(DNSDistTest
):
400 _webServerPort
= 8083
401 _webServerBasicAuthPassword
= 'secret'
402 _webServerAPIKey
= 'apisecret'
403 # paths accessible using the API key only
404 _apiOnlyPath
= '/api/v1/servers/localhost/config'
405 # paths accessible using basic auth only (list not exhaustive)
407 _consoleKey
= DNSDistTest
.generateConsoleKey()
408 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
409 _config_params
= ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
410 _config_template
= """
412 controlSocket("127.0.0.1:%s")
413 setACL({"127.0.0.1/32", "::1/128"})
414 newServer({address="127.0.0.1:%s"})
415 webserver("127.0.0.1:%s", "%s", "%s", {["X-Frame-Options"]="", ["X-Custom"]="custom"})
418 def testBasicHeaders(self
):
420 API: Basic custom headers
423 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + self
._basicOnlyPath
425 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
427 self
.assertEquals(r
.status_code
, 200)
428 self
.assertEquals(r
.headers
.get('x-custom'), "custom")
429 self
.assertFalse("x-frame-options" in r
.headers
)
431 def testBasicHeadersUpdate(self
):
433 API: Basic update of custom headers
436 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + self
._basicOnlyPath
437 self
.sendConsoleCommand('setWebserverConfig({customHeaders={["x-powered-by"]="dnsdist"}})')
438 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
440 self
.assertEquals(r
.status_code
, 200)
441 self
.assertEquals(r
.headers
.get('x-powered-by'), "dnsdist")
442 self
.assertTrue("x-frame-options" in r
.headers
)
445 class TestAPIAuth(DNSDistTest
):
448 _webServerPort
= 8083
449 _webServerBasicAuthPassword
= 'secret'
450 _webServerBasicAuthPasswordNew
= 'password'
451 _webServerAPIKey
= 'apisecret'
452 _webServerAPIKeyNew
= 'apipassword'
453 # paths accessible using the API key only
454 _apiOnlyPath
= '/api/v1/servers/localhost/config'
455 # paths accessible using basic auth only (list not exhaustive)
457 _consoleKey
= DNSDistTest
.generateConsoleKey()
458 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
459 _config_params
= ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
460 _config_template
= """
462 controlSocket("127.0.0.1:%s")
463 setACL({"127.0.0.1/32", "::1/128"})
464 newServer{address="127.0.0.1:%s"}
465 webserver("127.0.0.1:%s", "%s", "%s")
468 def testBasicAuthChange(self
):
470 API: Basic Authentication updating credentials
473 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + self
._basicOnlyPath
474 self
.sendConsoleCommand('setWebserverConfig({{password="{}"}})'.format(self
._webServerBasicAuthPasswordNew
))
476 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPasswordNew
), timeout
=self
._webTimeout
)
478 self
.assertEquals(r
.status_code
, 200)
480 # Make sure the old password is not usable any more
481 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
482 self
.assertEquals(r
.status_code
, 401)
484 def testXAPIKeyChange(self
):
486 API: X-Api-Key updating credentials
489 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + self
._apiOnlyPath
490 self
.sendConsoleCommand('setWebserverConfig({{apiKey="{}"}})'.format(self
._webServerAPIKeyNew
))
492 headers
= {'x-api-key': self
._webServerAPIKeyNew
}
493 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
495 self
.assertEquals(r
.status_code
, 200)
497 # Make sure the old password is not usable any more
498 headers
= {'x-api-key': self
._webServerAPIKey
}
499 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
500 self
.assertEquals(r
.status_code
, 401)
502 def testBasicAuthOnlyChange(self
):
504 API: X-Api-Key updated to none (disabled)
507 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + self
._apiOnlyPath
508 self
.sendConsoleCommand('setWebserverConfig({{apiKey="{}"}})'.format(self
._webServerAPIKeyNew
))
510 headers
= {'x-api-key': self
._webServerAPIKeyNew
}
511 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
513 self
.assertEquals(r
.status_code
, 200)
516 self
.sendConsoleCommand('setWebserverConfig({apiKey=""})')
518 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
519 self
.assertEquals(r
.status_code
, 401)
521 class TestAPIACL(DNSDistTest
):
524 _webServerPort
= 8083
525 _webServerBasicAuthPassword
= 'secret'
526 _webServerAPIKey
= 'apisecret'
527 _consoleKey
= DNSDistTest
.generateConsoleKey()
528 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
529 _config_params
= ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
530 _config_template
= """
532 controlSocket("127.0.0.1:%s")
533 setACL({"127.0.0.1/32", "::1/128"})
534 newServer{address="127.0.0.1:%s"}
535 webserver("127.0.0.1:%s", "%s", "%s", {}, "192.0.2.1")
538 def testACLChange(self
):
540 API: Should be denied by ACL then allowed
543 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + "/"
545 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
546 self
.assertTrue(False)
547 except requests
.exceptions
.ConnectionError
as exp
:
551 self
.sendConsoleCommand('setWebserverConfig({acl="127.0.0.1"})')
553 r
= requests
.get(url
, auth
=('whatever', self
._webServerBasicAuthPassword
), timeout
=self
._webTimeout
)
555 self
.assertEquals(r
.status_code
, 200)