Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: tests/standalone/io/http_proxy_test.dart

Issue 14660011: Add support for digest authentication of HTTP proxies (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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:crypto';
8 import "dart:io"; 8 import "dart:io";
9 import "dart:uri"; 9 import "dart:uri";
10 import 'dart:utf'; 10 import 'dart:utf';
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 {List<String> directRequestPaths: const <String>[], 73 {List<String> directRequestPaths: const <String>[],
74 secure: false}) { 74 secure: false}) {
75 Server server = new Server(proxyHops, directRequestPaths, secure); 75 Server server = new Server(proxyHops, directRequestPaths, secure);
76 return server.start(); 76 return server.start();
77 } 77 }
78 78
79 class ProxyServer { 79 class ProxyServer {
80 HttpServer server; 80 HttpServer server;
81 HttpClient client; 81 HttpClient client;
82 int requestCount = 0; 82 int requestCount = 0;
83 String authScheme;
84 String realm = "test";
83 String username; 85 String username;
84 String password; 86 String password;
85 87
88 var ha1;
89 String serverAlgorithm = "MD5";
90 String serverQop = "auth";
91 Set ncs = new Set();
92
93 var nonce = "12345678"; // No need for random nonce in test.
94
86 ProxyServer() : client = new HttpClient(); 95 ProxyServer() : client = new HttpClient();
87 96
88 authenticationRequired(request) { 97 void useBasicAuthentication(String username, String password) {
98 this.username = username;
99 this.password = password;
100 authScheme = "Basic";
101 }
102
103 void useDigestAuthentication(String username, String password) {
104 this.username = username;
105 this.password = password;
106 authScheme = "Digest";
107
108 // Calculate ha1.
109 var hasher = new MD5();
110 hasher.add("${username}:${realm}:${password}".codeUnits);
111 ha1 = CryptoUtils.bytesToHex(hasher.close());
112 }
113
114 basicAuthenticationRequired(request) {
89 request.fold(null, (x, y) {}).then((_) { 115 request.fold(null, (x, y) {}).then((_) {
90 var response = request.response; 116 var response = request.response;
91 response.headers.set(HttpHeaders.PROXY_AUTHENTICATE, 117 response.headers.set(HttpHeaders.PROXY_AUTHENTICATE,
92 "Basic, realm=realm"); 118 "Basic, realm=$realm");
93 response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED; 119 response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED;
94 response.close(); 120 response.close();
95 }); 121 });
96 } 122 }
97 123
124 digestAuthenticationRequired(request, {stale: false}) {
125 request.fold(null, (x, y) {}).then((_) {
126 var response = request.response;
127 response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED;
128 StringBuffer authHeader = new StringBuffer();
129 authHeader.write('Digest');
130 authHeader.write(', realm="$realm"');
131 authHeader.write(', nonce="$nonce"');
132 if (stale) authHeader.write(', stale="true"');
133 if (serverAlgorithm != null) {
134 authHeader.write(', algorithm=$serverAlgorithm');
135 }
136 if (serverQop != null) authHeader.write(', qop="$serverQop"');
137 response.headers.set(HttpHeaders.PROXY_AUTHENTICATE, authHeader);
138 response.close();
139 });
140 }
141
98 Future<ProxyServer> start() { 142 Future<ProxyServer> start() {
99 var x = new Completer(); 143 var x = new Completer();
100 HttpServer.bind("localhost", 0).then((s) { 144 HttpServer.bind("localhost", 0).then((s) {
101 server = s; 145 server = s;
102 x.complete(this); 146 x.complete(this);
103 server.listen((HttpRequest request) { 147 server.listen((HttpRequest request) {
104 requestCount++; 148 requestCount++;
105 if (username != null && password != null) { 149 if (username != null && password != null) {
106 if (request.headers[HttpHeaders.PROXY_AUTHORIZATION] == null) { 150 if (request.headers[HttpHeaders.PROXY_AUTHORIZATION] == null) {
107 authenticationRequired(request); 151 if (authScheme == "Digest") {
152 digestAuthenticationRequired(request);
153 } else {
154 basicAuthenticationRequired(request);
155 }
108 return; 156 return;
109 } else { 157 } else {
110 Expect.equals( 158 Expect.equals(
111 1, request.headers[HttpHeaders.PROXY_AUTHORIZATION].length); 159 1, request.headers[HttpHeaders.PROXY_AUTHORIZATION].length);
112 String authorization = 160 String authorization =
113 request.headers[HttpHeaders.PROXY_AUTHORIZATION][0]; 161 request.headers[HttpHeaders.PROXY_AUTHORIZATION][0];
114 List<String> tokens = authorization.split(" "); 162 if (authScheme == "Basic") {
115 Expect.equals("Basic", tokens[0]); 163 List<String> tokens = authorization.split(" ");
116 String auth = 164 Expect.equals("Basic", tokens[0]);
117 CryptoUtils.bytesToBase64(encodeUtf8("$username:$password")); 165 String auth =
118 if (auth != tokens[1]) { 166 CryptoUtils.bytesToBase64(encodeUtf8("$username:$password"));
119 authenticationRequired(request); 167 if (auth != tokens[1]) {
120 return; 168 authenticationRequired(request);
169 return;
170 }
171 } else {
172 HeaderValue header =
173 HeaderValue.parse(
174 authorization, parameterSeparator: ",");
175 Expect.equals("Digest", header.value);
176 var uri = header.parameters["uri"];
177 var qop = header.parameters["qop"];
178 var cnonce = header.parameters["cnonce"];
179 var nc = header.parameters["nc"];
180 Expect.equals(username, header.parameters["username"]);
181 Expect.equals(realm, header.parameters["realm"]);
182 Expect.equals("MD5", header.parameters["algorithm"]);
183 Expect.equals(nonce, header.parameters["nonce"]);
184 Expect.equals(request.uri.toString(), uri);
185 if (qop != null) {
186 // A server qop of auth-int is downgraded to none by the client.
187 Expect.equals("auth", serverQop);
188 Expect.equals("auth", header.parameters["qop"]);
189 Expect.isNotNull(cnonce);
190 Expect.isNotNull(nc);
191 Expect.isFalse(ncs.contains(nc));
192 ncs.add(nc);
193 } else {
194 Expect.isNull(cnonce);
195 Expect.isNull(nc);
196 }
197 Expect.isNotNull(header.parameters["response"]);
198
199 var hasher = new MD5();
200 hasher.add("${request.method}:${uri}".codeUnits);
201 var ha2 = CryptoUtils.bytesToHex(hasher.close());
202
203 var x;
204 hasher = new MD5();
205 if (qop == null || qop == "" || qop == "none") {
206 hasher.add("$ha1:${nonce}:$ha2".codeUnits);
207 } else {
208 hasher.add(
209 "$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits);
210 }
211 Expect.equals(CryptoUtils.bytesToHex(hasher.close()),
212 header.parameters["response"]);
213
214 // Add a bogus Proxy-Authentication-Info for testing.
215 var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
216 'cnonce="8f971178", '
217 'nc=000002c74, '
218 'qop=auth';
219 request.response.headers.set("Proxy-Authentication-Info", info);
121 } 220 }
122 } 221 }
123 } 222 }
124 // Open the connection from the proxy. 223 // Open the connection from the proxy.
125 if (request.method == "CONNECT") { 224 if (request.method == "CONNECT") {
126 var tmp = request.uri.toString().split(":"); 225 var tmp = request.uri.toString().split(":");
127 Socket.connect(tmp[0], int.parse(tmp[1])) 226 Socket.connect(tmp[0], int.parse(tmp[1]))
128 .then((socket) { 227 .then((socket) {
129 request.response.reasonPhrase = "Connection established"; 228 request.response.reasonPhrase = "Connection established";
130 request.response.detachSocket() 229 request.response.detachSocket()
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 test(false); 504 test(false);
406 test(true); 505 test(true);
407 } 506 }
408 }); 507 });
409 }); 508 });
410 }); 509 });
411 } 510 }
412 511
413 512
414 int testProxyAuthenticateCount = 0; 513 int testProxyAuthenticateCount = 0;
415 void testProxyAuthenticate() { 514 Future testProxyAuthenticate(bool useDigestAuthentication) {
515 testProxyAuthenticateCount = 0;
516 var completer = new Completer();
517
416 setupProxyServer().then((proxyServer) { 518 setupProxyServer().then((proxyServer) {
417 proxyServer.username = "test";
418 proxyServer.password = "test";
419 setupServer(1).then((server) { 519 setupServer(1).then((server) {
420 setupServer(1, secure: true).then((secureServer) { 520 setupServer(1, secure: true).then((secureServer) {
421 HttpClient client = new HttpClient(); 521 HttpClient client = new HttpClient();
422 522
423 Completer step1 = new Completer(); 523 Completer step1 = new Completer();
424 Completer step2 = new Completer(); 524 Completer step2 = new Completer();
425 525
526 if (useDigestAuthentication) {
527 proxyServer.useDigestAuthentication("dart", "password");
528 } else {
529 proxyServer.useBasicAuthentication("dart", "password");
530 }
531
426 // Test with no authentication. 532 // Test with no authentication.
427 client.findProxy = (Uri uri) { 533 client.findProxy = (Uri uri) {
428 return "PROXY localhost:${proxyServer.port}"; 534 return "PROXY localhost:${proxyServer.port}";
429 }; 535 };
430 536
431 const int loopCount = 2; 537 const int loopCount = 2;
432 for (int i = 0; i < loopCount; i++) { 538 for (int i = 0; i < loopCount; i++) {
433 test(bool secure) { 539 test(bool secure) {
434 String url = secure 540 String url = secure
435 ? "https://localhost:${secureServer.port}/$i" 541 ? "https://localhost:${secureServer.port}/$i"
(...skipping 16 matching lines...) Expand all
452 step1.complete(null); 558 step1.complete(null);
453 } 559 }
454 }); 560 });
455 } 561 }
456 562
457 test(false); 563 test(false);
458 test(true); 564 test(true);
459 } 565 }
460 step1.future.then((_) { 566 step1.future.then((_) {
461 testProxyAuthenticateCount = 0; 567 testProxyAuthenticateCount = 0;
462 client.findProxy = (Uri uri) { 568 if (useDigestAuthentication) {
463 return "PROXY test:test@localhost:${proxyServer.port}"; 569 client.findProxy = (Uri uri) => "PROXY localhost:${proxyServer.port}";
464 }; 570 client.addProxyCredentials(
571 "localhost",
572 proxyServer.port,
573 "test",
574 new HttpClientDigestCredentials("dart", "password"));
575 } else {
576 client.findProxy = (Uri uri) {
577 return "PROXY dart:password@localhost:${proxyServer.port}";
578 };
579 }
465 580
466 for (int i = 0; i < loopCount; i++) { 581 for (int i = 0; i < loopCount; i++) {
467 test(bool secure) { 582 test(bool secure) {
583 var path = useDigestAuthentication ? "A" : "$i";
468 String url = secure 584 String url = secure
469 ? "https://localhost:${secureServer.port}/$i" 585 ? "https://localhost:${secureServer.port}/$path"
470 : "http://localhost:${server.port}/$i"; 586 : "http://localhost:${server.port}/$path";
471 587
472 client.postUrl(Uri.parse(url)) 588 client.postUrl(Uri.parse(url))
473 .then((HttpClientRequest clientRequest) { 589 .then((HttpClientRequest clientRequest) {
474 String content = "$i$i$i"; 590 String content = "$i$i$i";
475 clientRequest.write(content); 591 clientRequest.write(content);
476 return clientRequest.close(); 592 return clientRequest.close();
477 }) 593 })
478 .then((HttpClientResponse response) { 594 .then((HttpClientResponse response) {
479 response.listen((_) {}, onDone: () { 595 response.listen((_) {}, onDone: () {
480 testProxyAuthenticateCount++; 596 testProxyAuthenticateCount++;
(...skipping 16 matching lines...) Expand all
497 testProxyAuthenticateCount = 0; 613 testProxyAuthenticateCount = 0;
498 client.findProxy = (Uri uri) { 614 client.findProxy = (Uri uri) {
499 return "PROXY localhost:${proxyServer.port}"; 615 return "PROXY localhost:${proxyServer.port}";
500 }; 616 };
501 617
502 client.authenticateProxy = (host, port, scheme, realm) { 618 client.authenticateProxy = (host, port, scheme, realm) {
503 client.addProxyCredentials( 619 client.addProxyCredentials(
504 "localhost", 620 "localhost",
505 proxyServer.port, 621 proxyServer.port,
506 "realm", 622 "realm",
507 new HttpClientBasicCredentials("test", "test")); 623 new HttpClientBasicCredentials("dart", "password"));
508 return new Future.value(true); 624 return new Future.value(true);
509 }; 625 };
510 626
511 for (int i = 0; i < loopCount; i++) { 627 for (int i = 0; i < loopCount; i++) {
512 test(bool secure) { 628 test(bool secure) {
513 String url = secure 629 String url = secure
514 ? "https://localhost:${secureServer.port}/A" 630 ? "https://localhost:${secureServer.port}/A"
515 : "http://localhost:${server.port}/A"; 631 : "http://localhost:${server.port}/A";
516 632
517 client.postUrl(Uri.parse(url)) 633 client.postUrl(Uri.parse(url))
518 .then((HttpClientRequest clientRequest) { 634 .then((HttpClientRequest clientRequest) {
519 String content = "$i$i$i"; 635 String content = "$i$i$i";
520 clientRequest.write(content); 636 clientRequest.write(content);
521 return clientRequest.close(); 637 return clientRequest.close();
522 }) 638 })
523 .then((HttpClientResponse response) { 639 .then((HttpClientResponse response) {
524 response.listen((_) {}, onDone: () { 640 response.listen((_) {}, onDone: () {
525 testProxyAuthenticateCount++; 641 testProxyAuthenticateCount++;
526 Expect.equals(HttpStatus.OK, response.statusCode); 642 Expect.equals(HttpStatus.OK, response.statusCode);
527 if (testProxyAuthenticateCount == loopCount * 2) { 643 if (testProxyAuthenticateCount == loopCount * 2) {
528 Expect.equals(loopCount * 2, server.requestCount); 644 Expect.equals(loopCount * 2, server.requestCount);
529 Expect.equals(loopCount * 2, secureServer.requestCount); 645 Expect.equals(loopCount * 2, secureServer.requestCount);
530 proxyServer.shutdown(); 646 proxyServer.shutdown();
531 server.shutdown(); 647 server.shutdown();
532 secureServer.shutdown(); 648 secureServer.shutdown();
533 client.close(); 649 client.close();
650 completer.complete(null);
534 } 651 }
535 }); 652 });
536 }); 653 });
537 } 654 }
538 test(false); 655 test(false);
539 test(true); 656 test(true);
540 } 657 }
541 }); 658 });
542 659
543 }); 660 });
544 }); 661 });
545 }); 662 });
663
664 return completer.future;
546 } 665 }
547 666
548 int testRealProxyDoneCount = 0; 667 int testRealProxyDoneCount = 0;
549 void testRealProxy() { 668 void testRealProxy() {
550 setupServer(1).then((server) { 669 setupServer(1).then((server) {
551 HttpClient client = new HttpClient(); 670 HttpClient client = new HttpClient();
552 client.addProxyCredentials("localhost", 671 client.addProxyCredentials(
553 8080, 672 "localhost",
554 "test", 673 8080,
555 new HttpClientBasicCredentials("test", "test")); 674 "test",
675 new HttpClientBasicCredentials("dart", "password"));
556 676
557 List<String> proxy = 677 List<String> proxy =
558 ["PROXY localhost:8080", 678 ["PROXY localhost:8080",
559 "PROXY localhost:8080; PROXY hede.hule.hest:8080", 679 "PROXY localhost:8080; PROXY hede.hule.hest:8080",
560 "PROXY hede.hule.hest:8080; PROXY localhost:8080", 680 "PROXY hede.hule.hest:8080; PROXY localhost:8080",
561 "PROXY localhost:8080; DIRECT"]; 681 "PROXY localhost:8080; DIRECT"];
562 682
563 client.findProxy = (Uri uri) { 683 client.findProxy = (Uri uri) {
564 // Pick the proxy configuration based on the request path. 684 // Pick the proxy configuration based on the request path.
565 int index = int.parse(uri.path.substring(1)); 685 int index = int.parse(uri.path.substring(1));
(...skipping 20 matching lines...) Expand all
586 } 706 }
587 }); 707 });
588 } 708 }
589 709
590 int testRealProxyAuthDoneCount = 0; 710 int testRealProxyAuthDoneCount = 0;
591 void testRealProxyAuth() { 711 void testRealProxyAuth() {
592 setupServer(1).then((server) { 712 setupServer(1).then((server) {
593 HttpClient client = new HttpClient(); 713 HttpClient client = new HttpClient();
594 714
595 List<String> proxy = 715 List<String> proxy =
596 ["PROXY test:test@localhost:8080", 716 ["PROXY dart:password@localhost:8080",
597 "PROXY test:test@localhost:8080; PROXY hede.hule.hest:8080", 717 "PROXY dart:password@localhost:8080; PROXY hede.hule.hest:8080",
598 "PROXY hede.hule.hest:8080; PROXY test:test@localhost:8080", 718 "PROXY hede.hule.hest:8080; PROXY dart:password@localhost:8080",
599 "PROXY test:test@localhost:8080; DIRECT"]; 719 "PROXY dart:password@localhost:8080; DIRECT"];
600 720
601 client.findProxy = (Uri uri) { 721 client.findProxy = (Uri uri) {
602 // Pick the proxy configuration based on the request path. 722 // Pick the proxy configuration based on the request path.
603 int index = int.parse(uri.path.substring(1)); 723 int index = int.parse(uri.path.substring(1));
604 return proxy[index]; 724 return proxy[index];
605 }; 725 };
606 726
607 for (int i = 0; i < proxy.length; i++) { 727 for (int i = 0; i < proxy.length; i++) {
608 client.getUrl(Uri.parse("http://localhost:${server.port}/$i")) 728 client.getUrl(Uri.parse("http://localhost:${server.port}/$i"))
609 .then((HttpClientRequest clientRequest) { 729 .then((HttpClientRequest clientRequest) {
(...skipping 22 matching lines...) Expand all
632 password: 'dartdart'); 752 password: 'dartdart');
633 } 753 }
634 754
635 main() { 755 main() {
636 InitializeSSL(); 756 InitializeSSL();
637 testInvalidProxy(); 757 testInvalidProxy();
638 testDirectProxy(); 758 testDirectProxy();
639 testProxy(); 759 testProxy();
640 testProxyChain(); 760 testProxyChain();
641 testProxyFromEnviroment(); 761 testProxyFromEnviroment();
642 testProxyAuthenticate(); 762 // The two invocations of uses the same global variable for state -
763 // run one after the other.
764 testProxyAuthenticate(false)
765 .then((_) => testProxyAuthenticate(true));
643 // This test is not normally run. It can be used for locally testing 766 // This test is not normally run. It can be used for locally testing
644 // with a real proxy server (e.g. Apache). 767 // with a real proxy server (e.g. Apache).
645 //testRealProxy(); 768 //testRealProxy();
646 //testRealProxyAuth(); 769 //testRealProxyAuth();
647 } 770 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698