OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import "package:expect/expect.dart"; | 5 import "package:expect/expect.dart"; |
6 import "dart:async"; | 6 import "dart:async"; |
| 7 import 'dart:crypto'; |
7 import "dart:io"; | 8 import "dart:io"; |
8 import "dart:uri"; | 9 import "dart:uri"; |
| 10 import 'dart:utf'; |
9 | 11 |
10 class Server { | 12 class Server { |
11 HttpServer server; | 13 HttpServer server; |
12 bool secure; | 14 bool secure; |
13 int proxyHops; | 15 int proxyHops; |
14 List<String> directRequestPaths; | 16 List<String> directRequestPaths; |
15 int requestCount = 0; | 17 int requestCount = 0; |
16 | 18 |
17 Server(this.proxyHops, this.directRequestPaths, this.secure); | 19 Server(this.proxyHops, this.directRequestPaths, this.secure); |
18 | 20 |
(...skipping 22 matching lines...) Expand all Loading... |
41 } else { | 43 } else { |
42 Expect.isNull(request.headers[HttpHeaders.VIA]); | 44 Expect.isNull(request.headers[HttpHeaders.VIA]); |
43 } | 45 } |
44 var body = new StringBuffer(); | 46 var body = new StringBuffer(); |
45 request.listen( | 47 request.listen( |
46 (data) { | 48 (data) { |
47 body.write(new String.fromCharCodes(data)); | 49 body.write(new String.fromCharCodes(data)); |
48 }, | 50 }, |
49 onDone: () { | 51 onDone: () { |
50 String path = request.uri.path.substring(1); | 52 String path = request.uri.path.substring(1); |
51 String content = "$path$path$path"; | 53 if (path != "A") { |
52 Expect.equals(content, body.toString()); | 54 String content = "$path$path$path"; |
| 55 Expect.equals(content, body.toString()); |
| 56 } |
53 response.write(request.uri.path); | 57 response.write(request.uri.path); |
54 response.close(); | 58 response.close(); |
55 }); | 59 }); |
56 }); | 60 }); |
57 return x.future; | 61 return x.future; |
58 }); | 62 }); |
59 } | 63 } |
60 | 64 |
61 void shutdown() { | 65 void shutdown() { |
62 server.close(); | 66 server.close(); |
63 } | 67 } |
64 | 68 |
65 int get port => server.port; | 69 int get port => server.port; |
66 } | 70 } |
67 | 71 |
68 Future<Server> setupServer(int proxyHops, | 72 Future<Server> setupServer(int proxyHops, |
69 {List<String> directRequestPaths: const <String>[], | 73 {List<String> directRequestPaths: const <String>[], |
70 secure: false}) { | 74 secure: false}) { |
71 Server server = new Server(proxyHops, directRequestPaths, secure); | 75 Server server = new Server(proxyHops, directRequestPaths, secure); |
72 return server.start(); | 76 return server.start(); |
73 } | 77 } |
74 | 78 |
75 class ProxyServer { | 79 class ProxyServer { |
76 HttpServer server; | 80 HttpServer server; |
77 HttpClient client; | 81 HttpClient client; |
78 int requestCount = 0; | 82 int requestCount = 0; |
| 83 String username; |
| 84 String password; |
79 | 85 |
80 ProxyServer() : client = new HttpClient(); | 86 ProxyServer() : client = new HttpClient(); |
81 | 87 |
| 88 authenticationRequired(request) { |
| 89 request.fold(null, (x, y) {}).then((_) { |
| 90 var response = request.response; |
| 91 response.headers.set(HttpHeaders.PROXY_AUTHENTICATE, |
| 92 "Basic, realm=realm"); |
| 93 response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED; |
| 94 response.close(); |
| 95 }); |
| 96 } |
| 97 |
82 Future<ProxyServer> start() { | 98 Future<ProxyServer> start() { |
83 var x = new Completer(); | 99 var x = new Completer(); |
84 HttpServer.bind().then((s) { | 100 HttpServer.bind().then((s) { |
85 server = s; | 101 server = s; |
86 x.complete(this); | 102 x.complete(this); |
87 server.listen((HttpRequest request) { | 103 server.listen((HttpRequest request) { |
88 requestCount++; | 104 requestCount++; |
| 105 if (username != null && password != null) { |
| 106 if (request.headers[HttpHeaders.PROXY_AUTHORIZATION] == null) { |
| 107 authenticationRequired(request); |
| 108 return; |
| 109 } else { |
| 110 Expect.equals( |
| 111 1, request.headers[HttpHeaders.PROXY_AUTHORIZATION].length); |
| 112 String authorization = |
| 113 request.headers[HttpHeaders.PROXY_AUTHORIZATION][0]; |
| 114 List<String> tokens = authorization.split(" "); |
| 115 Expect.equals("Basic", tokens[0]); |
| 116 String auth = |
| 117 CryptoUtils.bytesToBase64(encodeUtf8("$username:$password")); |
| 118 if (auth != tokens[1]) { |
| 119 authenticationRequired(request); |
| 120 return; |
| 121 } |
| 122 } |
| 123 } |
89 // Open the connection from the proxy. | 124 // Open the connection from the proxy. |
90 client.openUrl(request.method, request.uri) | 125 client.openUrl(request.method, request.uri) |
91 .then((HttpClientRequest clientRequest) { | 126 .then((HttpClientRequest clientRequest) { |
92 // Forward all headers. | 127 // Forward all headers. |
93 request.headers.forEach((String name, List<String> values) { | 128 request.headers.forEach((String name, List<String> values) { |
94 values.forEach((String value) { | 129 values.forEach((String value) { |
95 if (name != "content-length" && name != "via") { | 130 if (name != "content-length" && name != "via") { |
96 clientRequest.headers.add(name, value); | 131 clientRequest.headers.add(name, value); |
97 } | 132 } |
98 }); | 133 }); |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 } | 390 } |
356 | 391 |
357 test(false); | 392 test(false); |
358 test(true); | 393 test(true); |
359 } | 394 } |
360 }); | 395 }); |
361 }); | 396 }); |
362 }); | 397 }); |
363 } | 398 } |
364 | 399 |
| 400 |
| 401 int testProxyAuthenticateCount = 0; |
| 402 void testProxyAuthenticate() { |
| 403 setupProxyServer().then((proxyServer) { |
| 404 proxyServer.username = "test"; |
| 405 proxyServer.password = "test"; |
| 406 setupServer(1).then((server) { |
| 407 setupServer(1, secure: true).then((secureServer) { |
| 408 HttpClient client = new HttpClient(); |
| 409 |
| 410 Completer step1 = new Completer(); |
| 411 Completer step2 = new Completer(); |
| 412 |
| 413 // Test with no authentication. |
| 414 client.findProxy = (Uri uri) { |
| 415 return "PROXY localhost:${proxyServer.port}"; |
| 416 }; |
| 417 |
| 418 const int loopCount = 2; |
| 419 for (int i = 0; i < loopCount; i++) { |
| 420 test(bool secure) { |
| 421 String url = secure |
| 422 ? "https://localhost:${secureServer.port}/$i" |
| 423 : "http://127.0.0.1:${server.port}/$i"; |
| 424 |
| 425 client.postUrl(Uri.parse(url)) |
| 426 .then((HttpClientRequest clientRequest) { |
| 427 String content = "$i$i$i"; |
| 428 clientRequest.write(content); |
| 429 return clientRequest.close(); |
| 430 }) |
| 431 .then((HttpClientResponse response) { |
| 432 response.listen((_) {}, onDone: () { |
| 433 testProxyAuthenticateCount++; |
| 434 Expect.equals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED, |
| 435 response.statusCode); |
| 436 if (testProxyAuthenticateCount == loopCount * 2) { |
| 437 Expect.equals(0, server.requestCount); |
| 438 Expect.equals(0, secureServer.requestCount); |
| 439 step1.complete(null); |
| 440 } |
| 441 }); |
| 442 }); |
| 443 } |
| 444 |
| 445 test(false); |
| 446 test(true); |
| 447 } |
| 448 |
| 449 step1.future.then((_) { |
| 450 testProxyAuthenticateCount = 0; |
| 451 client.findProxy = (Uri uri) { |
| 452 return "PROXY test:test@localhost:${proxyServer.port}"; |
| 453 }; |
| 454 |
| 455 for (int i = 0; i < loopCount; i++) { |
| 456 test(bool secure) { |
| 457 String url = secure |
| 458 ? "https://localhost:${secureServer.port}/$i" |
| 459 : "http://127.0.0.1:${server.port}/$i"; |
| 460 |
| 461 client.postUrl(Uri.parse(url)) |
| 462 .then((HttpClientRequest clientRequest) { |
| 463 String content = "$i$i$i"; |
| 464 clientRequest.write(content); |
| 465 return clientRequest.close(); |
| 466 }) |
| 467 .then((HttpClientResponse response) { |
| 468 response.listen((_) {}, onDone: () { |
| 469 testProxyAuthenticateCount++; |
| 470 Expect.equals(HttpStatus.OK, response.statusCode); |
| 471 if (testProxyAuthenticateCount == loopCount * 2) { |
| 472 Expect.equals(loopCount, server.requestCount); |
| 473 Expect.equals(loopCount, secureServer.requestCount); |
| 474 step2.complete(null); |
| 475 } |
| 476 }); |
| 477 }); |
| 478 } |
| 479 |
| 480 test(false); |
| 481 test(true); |
| 482 } |
| 483 }); |
| 484 |
| 485 step2.future.then((_) { |
| 486 testProxyAuthenticateCount = 0; |
| 487 client.findProxy = (Uri uri) { |
| 488 return "PROXY localhost:${proxyServer.port}"; |
| 489 }; |
| 490 |
| 491 client.authenticateProxy = (host, port, scheme, realm) { |
| 492 client.addProxyCredentials( |
| 493 "localhost", |
| 494 proxyServer.port, |
| 495 "realm", |
| 496 new HttpClientBasicCredentials("test", "test")); |
| 497 return new Future.immediate(true); |
| 498 }; |
| 499 |
| 500 for (int i = 0; i < loopCount; i++) { |
| 501 test(bool secure) { |
| 502 String url = secure |
| 503 ? "https://localhost:${secureServer.port}/A" |
| 504 : "http://127.0.0.1:${server.port}/A"; |
| 505 |
| 506 client.postUrl(Uri.parse(url)) |
| 507 .then((HttpClientRequest clientRequest) { |
| 508 String content = "$i$i$i"; |
| 509 clientRequest.write(content); |
| 510 return clientRequest.close(); |
| 511 }) |
| 512 .then((HttpClientResponse response) { |
| 513 response.listen((_) {}, onDone: () { |
| 514 testProxyAuthenticateCount++; |
| 515 Expect.equals(HttpStatus.OK, response.statusCode); |
| 516 if (testProxyAuthenticateCount == loopCount * 2) { |
| 517 Expect.equals(loopCount * 2, server.requestCount); |
| 518 Expect.equals(loopCount * 2, secureServer.requestCount); |
| 519 proxyServer.shutdown(); |
| 520 server.shutdown(); |
| 521 secureServer.shutdown(); |
| 522 client.close(); |
| 523 } |
| 524 }); |
| 525 }); |
| 526 } |
| 527 test(false); |
| 528 test(true); |
| 529 } |
| 530 }); |
| 531 |
| 532 }); |
| 533 }); |
| 534 }); |
| 535 } |
| 536 |
365 int testRealProxyDoneCount = 0; | 537 int testRealProxyDoneCount = 0; |
366 void testRealProxy() { | 538 void testRealProxy() { |
367 setupServer(1).then((server) { | 539 setupServer(1).then((server) { |
368 HttpClient client = new HttpClient(); | 540 HttpClient client = new HttpClient(); |
| 541 client.addProxyCredentials("localhost", |
| 542 8080, |
| 543 "test", |
| 544 new HttpClientBasicCredentials("test", "test")); |
369 | 545 |
370 List<String> proxy = | 546 List<String> proxy = |
371 ["PROXY localhost:8080", | 547 ["PROXY localhost:8080", |
372 "PROXY localhost:8080; PROXY hede.hule.hest:8080", | 548 "PROXY localhost:8080; PROXY hede.hule.hest:8080", |
373 "PROXY hede.hule.hest:8080; PROXY localhost:8080", | 549 "PROXY hede.hule.hest:8080; PROXY localhost:8080", |
374 "PROXY localhost:8080; DIRECT"]; | 550 "PROXY localhost:8080; DIRECT"]; |
375 | 551 |
376 client.findProxy = (Uri uri) { | 552 client.findProxy = (Uri uri) { |
377 // Pick the proxy configuration based on the request path. | 553 // Pick the proxy configuration based on the request path. |
378 int index = int.parse(uri.path.substring(1)); | 554 int index = int.parse(uri.path.substring(1)); |
379 return proxy[index]; | 555 return proxy[index]; |
380 }; | 556 }; |
381 | 557 |
382 for (int i = 0; i < proxy.length; i++) { | 558 for (int i = 0; i < proxy.length; i++) { |
383 client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i")) | 559 client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i")) |
384 .then((HttpClientRequest clientRequest) { | 560 .then((HttpClientRequest clientRequest) { |
385 String content = "$i$i$i"; | 561 String content = "$i$i$i"; |
386 clientRequest.contentLength = content.length; | 562 clientRequest.contentLength = content.length; |
387 clientRequest.write(content); | 563 clientRequest.write(content); |
388 return clientRequest.close(); | 564 return clientRequest.close(); |
389 }) | 565 }) |
390 .then((HttpClientResponse response) { | 566 .then((HttpClientResponse response) { |
391 response.listen((_) {}, onDone: () { | 567 response.listen((_) {}, onDone: () { |
392 testRealProxyDoneCount++; | 568 if (++testRealProxyDoneCount == proxy.length) { |
393 if (testRealProxyDoneCount == proxy.length) { | |
394 Expect.equals(proxy.length, server.requestCount); | 569 Expect.equals(proxy.length, server.requestCount); |
395 server.shutdown(); | 570 server.shutdown(); |
396 client.close(); | 571 client.close(); |
| 572 } |
| 573 }); |
| 574 }); |
| 575 } |
| 576 }); |
| 577 } |
| 578 |
| 579 int testRealProxyAuthDoneCount = 0; |
| 580 void testRealProxyAuth() { |
| 581 setupServer(1).then((server) { |
| 582 HttpClient client = new HttpClient(); |
| 583 |
| 584 List<String> proxy = |
| 585 ["PROXY test:test@localhost:8080", |
| 586 "PROXY test:test@localhost:8080; PROXY hede.hule.hest:8080", |
| 587 "PROXY hede.hule.hest:8080; PROXY test:test@localhost:8080", |
| 588 "PROXY test:test@localhost:8080; DIRECT"]; |
| 589 |
| 590 client.findProxy = (Uri uri) { |
| 591 // Pick the proxy configuration based on the request path. |
| 592 int index = int.parse(uri.path.substring(1)); |
| 593 return proxy[index]; |
| 594 }; |
| 595 |
| 596 for (int i = 0; i < proxy.length; i++) { |
| 597 client.getUrl(Uri.parse("http://127.0.0.1:${server.port}/$i")) |
| 598 .then((HttpClientRequest clientRequest) { |
| 599 String content = "$i$i$i"; |
| 600 clientRequest.contentLength = content.length; |
| 601 clientRequest.write(content); |
| 602 return clientRequest.close(); |
| 603 }) |
| 604 .then((HttpClientResponse response) { |
| 605 response.listen((_) {}, onDone: () { |
| 606 if (++testRealProxyAuthDoneCount == proxy.length) { |
| 607 Expect.equals(proxy.length, server.requestCount); |
| 608 server.shutdown(); |
| 609 client.close(); |
397 } | 610 } |
398 }); | 611 }); |
399 }); | 612 }); |
400 } | 613 } |
401 }); | 614 }); |
402 } | 615 } |
403 | 616 |
404 void InitializeSSL() { | 617 void InitializeSSL() { |
405 var testPkcertDatabase = | 618 var testPkcertDatabase = |
406 new Path(new Options().script).directoryPath.append('pkcert/'); | 619 new Path(new Options().script).directoryPath.append('pkcert/'); |
407 SecureSocket.initialize(database: testPkcertDatabase.toNativePath(), | 620 SecureSocket.initialize(database: testPkcertDatabase.toNativePath(), |
408 password: 'dartdart'); | 621 password: 'dartdart'); |
409 } | 622 } |
410 | 623 |
411 main() { | 624 main() { |
412 InitializeSSL(); | 625 InitializeSSL(); |
413 testInvalidProxy(); | 626 testInvalidProxy(); |
414 testDirectProxy(); | 627 testDirectProxy(); |
415 testProxy(); | 628 testProxy(); |
416 testProxyChain(); | 629 testProxyChain(); |
417 testProxyFromEnviroment(); | 630 testProxyFromEnviroment(); |
| 631 testProxyAuthenticate(); |
418 // This test is not normally run. It can be used for locally testing | 632 // This test is not normally run. It can be used for locally testing |
419 // with a real proxy server (e.g. Apache). | 633 // with a real proxy server (e.g. Apache). |
420 //testRealProxy(); | 634 //testRealProxy(); |
| 635 //testRealProxyAuth(); |
421 } | 636 } |
OLD | NEW |