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

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: 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("$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits );
Anders Johnsen 2013/05/07 13:23:58 Nit: long line.
209 }
210 Expect.equals(CryptoUtils.bytesToHex(hasher.close()),
211 header.parameters["response"]);
212
213 // Add a bogus Proxy-Authentication-Info for testing.
214 var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
215 'cnonce="8f971178", '
216 'nc=000002c74, '
217 'qop=auth';
218 request.response.headers.set("Proxy-Authentication-Info", info);
121 } 219 }
122 } 220 }
123 } 221 }
124 // Open the connection from the proxy. 222 // Open the connection from the proxy.
125 if (request.method == "CONNECT") { 223 if (request.method == "CONNECT") {
126 var tmp = request.uri.toString().split(":"); 224 var tmp = request.uri.toString().split(":");
127 Socket.connect(tmp[0], int.parse(tmp[1])) 225 Socket.connect(tmp[0], int.parse(tmp[1]))
128 .then((socket) { 226 .then((socket) {
129 request.response.reasonPhrase = "Connection established"; 227 request.response.reasonPhrase = "Connection established";
130 request.response.detachSocket() 228 request.response.detachSocket()
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 test(false); 503 test(false);
406 test(true); 504 test(true);
407 } 505 }
408 }); 506 });
409 }); 507 });
410 }); 508 });
411 } 509 }
412 510
413 511
414 int testProxyAuthenticateCount = 0; 512 int testProxyAuthenticateCount = 0;
415 void testProxyAuthenticate() { 513 Future testProxyAuthenticate(bool useDigestAuthentication) {
514 testProxyAuthenticateCount = 0;
515 var completer = new Completer();
516
416 setupProxyServer().then((proxyServer) { 517 setupProxyServer().then((proxyServer) {
417 proxyServer.username = "test";
418 proxyServer.password = "test";
419 setupServer(1).then((server) { 518 setupServer(1).then((server) {
420 setupServer(1, secure: true).then((secureServer) { 519 setupServer(1, secure: true).then((secureServer) {
421 HttpClient client = new HttpClient(); 520 HttpClient client = new HttpClient();
422 521
423 Completer step1 = new Completer(); 522 Completer step1 = new Completer();
424 Completer step2 = new Completer(); 523 Completer step2 = new Completer();
425 524
525 if (useDigestAuthentication) {
526 proxyServer.useDigestAuthentication("test", "test");
527 } else {
528 proxyServer.useBasicAuthentication("test", "test");
529 }
530
426 // Test with no authentication. 531 // Test with no authentication.
427 client.findProxy = (Uri uri) { 532 client.findProxy = (Uri uri) {
428 return "PROXY localhost:${proxyServer.port}"; 533 return "PROXY localhost:${proxyServer.port}";
429 }; 534 };
430 535
431 const int loopCount = 2; 536 const int loopCount = 2;
432 for (int i = 0; i < loopCount; i++) { 537 for (int i = 0; i < loopCount; i++) {
433 test(bool secure) { 538 test(bool secure) {
434 String url = secure 539 String url = secure
435 ? "https://localhost:${secureServer.port}/$i" 540 ? "https://localhost:${secureServer.port}/$i"
(...skipping 16 matching lines...) Expand all
452 step1.complete(null); 557 step1.complete(null);
453 } 558 }
454 }); 559 });
455 } 560 }
456 561
457 test(false); 562 test(false);
458 test(true); 563 test(true);
459 } 564 }
460 step1.future.then((_) { 565 step1.future.then((_) {
461 testProxyAuthenticateCount = 0; 566 testProxyAuthenticateCount = 0;
462 client.findProxy = (Uri uri) { 567 if (useDigestAuthentication) {
463 return "PROXY test:test@localhost:${proxyServer.port}"; 568 client.findProxy = (Uri uri) => "PROXY localhost:${proxyServer.port}";
464 }; 569 client.addProxyCredentials(
570 "localhost",
571 proxyServer.port,
572 "test",
573 new HttpClientDigestCredentials("test", "test"));
Anders Johnsen 2013/05/07 13:23:58 make user and pass not equal (here and other place
574 } else {
575 client.findProxy = (Uri uri) {
576 return "PROXY test:test@localhost:${proxyServer.port}";
577 };
578 }
465 579
466 for (int i = 0; i < loopCount; i++) { 580 for (int i = 0; i < loopCount; i++) {
467 test(bool secure) { 581 test(bool secure) {
582 var path = useDigestAuthentication ? "A" : "$i";
468 String url = secure 583 String url = secure
469 ? "https://localhost:${secureServer.port}/$i" 584 ? "https://localhost:${secureServer.port}/$path"
470 : "http://localhost:${server.port}/$i"; 585 : "http://localhost:${server.port}/$path";
471 586
472 client.postUrl(Uri.parse(url)) 587 client.postUrl(Uri.parse(url))
473 .then((HttpClientRequest clientRequest) { 588 .then((HttpClientRequest clientRequest) {
474 String content = "$i$i$i"; 589 String content = "$i$i$i";
475 clientRequest.write(content); 590 clientRequest.write(content);
476 return clientRequest.close(); 591 return clientRequest.close();
477 }) 592 })
478 .then((HttpClientResponse response) { 593 .then((HttpClientResponse response) {
479 response.listen((_) {}, onDone: () { 594 response.listen((_) {}, onDone: () {
480 testProxyAuthenticateCount++; 595 testProxyAuthenticateCount++;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 response.listen((_) {}, onDone: () { 639 response.listen((_) {}, onDone: () {
525 testProxyAuthenticateCount++; 640 testProxyAuthenticateCount++;
526 Expect.equals(HttpStatus.OK, response.statusCode); 641 Expect.equals(HttpStatus.OK, response.statusCode);
527 if (testProxyAuthenticateCount == loopCount * 2) { 642 if (testProxyAuthenticateCount == loopCount * 2) {
528 Expect.equals(loopCount * 2, server.requestCount); 643 Expect.equals(loopCount * 2, server.requestCount);
529 Expect.equals(loopCount * 2, secureServer.requestCount); 644 Expect.equals(loopCount * 2, secureServer.requestCount);
530 proxyServer.shutdown(); 645 proxyServer.shutdown();
531 server.shutdown(); 646 server.shutdown();
532 secureServer.shutdown(); 647 secureServer.shutdown();
533 client.close(); 648 client.close();
649 completer.complete(null);
534 } 650 }
535 }); 651 });
536 }); 652 });
537 } 653 }
538 test(false); 654 test(false);
539 test(true); 655 test(true);
540 } 656 }
541 }); 657 });
542 658
543 }); 659 });
544 }); 660 });
545 }); 661 });
662
663 return completer.future;
546 } 664 }
547 665
548 int testRealProxyDoneCount = 0; 666 int testRealProxyDoneCount = 0;
549 void testRealProxy() { 667 void testRealProxy() {
550 setupServer(1).then((server) { 668 setupServer(1).then((server) {
551 HttpClient client = new HttpClient(); 669 HttpClient client = new HttpClient();
552 client.addProxyCredentials("localhost", 670 client.addProxyCredentials("localhost",
553 8080, 671 8080,
554 "test", 672 "test",
555 new HttpClientBasicCredentials("test", "test")); 673 new HttpClientBasicCredentials("test", "test"));
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
632 password: 'dartdart'); 750 password: 'dartdart');
633 } 751 }
634 752
635 main() { 753 main() {
636 InitializeSSL(); 754 InitializeSSL();
637 testInvalidProxy(); 755 testInvalidProxy();
638 testDirectProxy(); 756 testDirectProxy();
639 testProxy(); 757 testProxy();
640 testProxyChain(); 758 testProxyChain();
641 testProxyFromEnviroment(); 759 testProxyFromEnviroment();
642 testProxyAuthenticate(); 760 // The two invocations of uses the same global variable for state -
761 // run one after the other.
762 testProxyAuthenticate(false)
763 .then((_) => testProxyAuthenticate(true));
643 // This test is not normally run. It can be used for locally testing 764 // This test is not normally run. It can be used for locally testing
644 // with a real proxy server (e.g. Apache). 765 // with a real proxy server (e.g. Apache).
645 //testRealProxy(); 766 //testRealProxy();
646 //testRealProxyAuth(); 767 //testRealProxyAuth();
647 } 768 }
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