| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 | 
|  | 3 // BSD-style license that can be found in the LICENSE file. | 
|  | 4 | 
|  | 5 import "package:expect/expect.dart"; | 
|  | 6 import 'dart:async'; | 
|  | 7 import 'dart:crypto'; | 
|  | 8 import 'dart:io'; | 
|  | 9 import 'dart:isolate'; | 
|  | 10 import 'dart:uri'; | 
|  | 11 import 'dart:utf'; | 
|  | 12 | 
|  | 13 class Server { | 
|  | 14   HttpServer server; | 
|  | 15   bool passwordChanged = false; | 
|  | 16   var ha1; | 
|  | 17 | 
|  | 18   static Future<Server> start(String algorithm, String qop) { | 
|  | 19     return new Server()._start(algorithm, qop); | 
|  | 20   } | 
|  | 21 | 
|  | 22   Future<Server> _start(String serverAlgorithm, String serverQop) { | 
|  | 23     // Calculate ha1. | 
|  | 24     String realm = "test"; | 
|  | 25     String username = "dart"; | 
|  | 26     String password = "password"; | 
|  | 27     var hasher = new MD5(); | 
|  | 28     hasher.add("${username}:${realm}:${password}".codeUnits); | 
|  | 29     ha1 = CryptoUtils.bytesToHex(hasher.close()); | 
|  | 30 | 
|  | 31     var completer = new Completer(); | 
|  | 32     HttpServer.bind("127.0.0.1", 0).then((s) { | 
|  | 33       server = s; | 
|  | 34       server.listen((HttpRequest request) { | 
|  | 35           // Just use a fixed nonce. | 
|  | 36         var nonce = "12345678"; | 
|  | 37 | 
|  | 38         sendUnauthorizedResponse(HttpResponse response) { | 
|  | 39           response.statusCode = HttpStatus.UNAUTHORIZED; | 
|  | 40           StringBuffer authHeader = new StringBuffer(); | 
|  | 41           authHeader.write('Digest'); | 
|  | 42           authHeader.write(', realm="$realm"'); | 
|  | 43           authHeader.write(', nonce="$nonce"'); | 
|  | 44           if (serverAlgorithm != null) { | 
|  | 45             authHeader.write(', algorithm=$serverAlgorithm'); | 
|  | 46           } | 
|  | 47           authHeader.write(', domain="/digest/"'); | 
|  | 48           if (serverQop != null) authHeader.write(', qop="$serverQop"'); | 
|  | 49           response.headers.set(HttpHeaders.WWW_AUTHENTICATE, authHeader); | 
|  | 50         } | 
|  | 51 | 
|  | 52         var response = request.response; | 
|  | 53         if (request.headers[HttpHeaders.AUTHORIZATION] != null) { | 
|  | 54           Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length); | 
|  | 55           String authorization = | 
|  | 56             request.headers[HttpHeaders.AUTHORIZATION][0]; | 
|  | 57           HeaderValue header = | 
|  | 58               new HeaderValue.fromString( | 
|  | 59                   authorization, parameterSeparator: ","); | 
|  | 60           if (header.value == "basic") { | 
|  | 61             sendUnauthorizedResponse(response); | 
|  | 62           } else { | 
|  | 63             var uri = header.parameters["uri"]; | 
|  | 64             var qop = header.parameters["qop"]; | 
|  | 65             var cnonce = header.parameters["cnonce"]; | 
|  | 66             var nc = header.parameters["nc"]; | 
|  | 67             Expect.equals("digest", header.value); | 
|  | 68             Expect.equals("dart", header.parameters["username"]); | 
|  | 69             Expect.equals(realm, header.parameters["realm"]); | 
|  | 70             Expect.equals("MD5", header.parameters["algorithm"]); | 
|  | 71             Expect.equals(nonce, header.parameters["nonce"]); | 
|  | 72             Expect.equals(request.uri.path, uri); | 
|  | 73             if (qop != null) { | 
|  | 74               // A server qop of auth-int is downgraded to none by the client. | 
|  | 75               Expect.equals("auth", serverQop); | 
|  | 76               Expect.equals("auth", header.parameters["qop"]); | 
|  | 77               Expect.isNotNull(cnonce); | 
|  | 78               Expect.isNotNull(nc); | 
|  | 79             } else { | 
|  | 80               Expect.isNull(cnonce); | 
|  | 81               Expect.isNull(nc); | 
|  | 82             } | 
|  | 83             Expect.isNotNull(header.parameters["response"]); | 
|  | 84 | 
|  | 85             var hasher = new MD5(); | 
|  | 86             hasher.add("${request.method}:${uri}".codeUnits); | 
|  | 87             var ha2 = CryptoUtils.bytesToHex(hasher.close()); | 
|  | 88 | 
|  | 89             var x; | 
|  | 90             hasher = new MD5(); | 
|  | 91             if (qop == null || qop == "" || qop == "none") { | 
|  | 92               hasher.add("$ha1:${nonce}:$ha2".codeUnits); | 
|  | 93             } else { | 
|  | 94               hasher.add("$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits); | 
|  | 95             } | 
|  | 96             Expect.equals(CryptoUtils.bytesToHex(hasher.close()), | 
|  | 97                           header.parameters["response"]); | 
|  | 98 | 
|  | 99             // Add a bogus Authentication-Info for testing. | 
|  | 100             var info = 'rspauth="77180d1ab3d6c9de084766977790f482", ' | 
|  | 101                        'cnonce="8f971178", ' | 
|  | 102                        'nc=000002c74, ' | 
|  | 103                        'qop=auth'; | 
|  | 104             response.headers.set("Authentication-Info", info); | 
|  | 105           } | 
|  | 106         } else { | 
|  | 107           sendUnauthorizedResponse(response); | 
|  | 108         } | 
|  | 109         response.close(); | 
|  | 110       }); | 
|  | 111       completer.complete(this); | 
|  | 112     }); | 
|  | 113     return completer.future; | 
|  | 114   } | 
|  | 115 | 
|  | 116   void shutdown() { | 
|  | 117     server.close(); | 
|  | 118   } | 
|  | 119 | 
|  | 120   int get port => server.port; | 
|  | 121 } | 
|  | 122 | 
|  | 123 void testNoCredentials(String algorithm, String qop) { | 
|  | 124   Server.start(algorithm, qop).then((server) { | 
|  | 125     HttpClient client = new HttpClient(); | 
|  | 126 | 
|  | 127     // Add digest credentials which does not match the path requested. | 
|  | 128     client.addCredentials( | 
|  | 129         Uri.parse("http://127.0.0.1:${server.port}/xxx"), | 
|  | 130         "test", | 
|  | 131         new HttpClientDigestCredentials("dart", "password")); | 
|  | 132 | 
|  | 133     // Add basic credentials for the path requested. | 
|  | 134     client.addCredentials( | 
|  | 135         Uri.parse("http://127.0.0.1:${server.port}/digest"), | 
|  | 136         "test", | 
|  | 137         new HttpClientBasicCredentials("dart", "password")); | 
|  | 138 | 
|  | 139     Future makeRequest(Uri url) { | 
|  | 140       return client.getUrl(url) | 
|  | 141         .then((HttpClientRequest request) => request.close()) | 
|  | 142         .then((HttpClientResponse response) { | 
|  | 143           Expect.equals(HttpStatus.UNAUTHORIZED, response.statusCode); | 
|  | 144           return response.fold(null, (x, y) {}); | 
|  | 145         }); | 
|  | 146     } | 
|  | 147 | 
|  | 148     var futures = []; | 
|  | 149     for (int i = 0; i < 5; i++) { | 
|  | 150       futures.add( | 
|  | 151           makeRequest( | 
|  | 152               Uri.parse("http://127.0.0.1:${server.port}/digest"))); | 
|  | 153     } | 
|  | 154     Future.wait(futures).then((_) { | 
|  | 155       server.shutdown(); | 
|  | 156       client.close(); | 
|  | 157     }); | 
|  | 158   }); | 
|  | 159 } | 
|  | 160 | 
|  | 161 void testCredentials(String algorithm, String qop) { | 
|  | 162   Server.start(algorithm, qop).then((server) { | 
|  | 163     HttpClient client = new HttpClient(); | 
|  | 164 | 
|  | 165     Future makeRequest(Uri url) { | 
|  | 166       return client.getUrl(url) | 
|  | 167         .then((HttpClientRequest request) => request.close()) | 
|  | 168         .then((HttpClientResponse response) { | 
|  | 169             Expect.equals(HttpStatus.OK, response.statusCode); | 
|  | 170             Expect.equals(1, response.headers["Authentication-Info"].length); | 
|  | 171           return response.fold(null, (x, y) {}); | 
|  | 172         }); | 
|  | 173     } | 
|  | 174 | 
|  | 175     client.addCredentials( | 
|  | 176         Uri.parse("http://127.0.0.1:${server.port}/digest"), | 
|  | 177         "test", | 
|  | 178         new HttpClientDigestCredentials("dart", "password")); | 
|  | 179 | 
|  | 180     var futures = []; | 
|  | 181     for (int i = 0; i < 5; i++) { | 
|  | 182       futures.add( | 
|  | 183           makeRequest( | 
|  | 184               Uri.parse("http://127.0.0.1:${server.port}/digest"))); | 
|  | 185     } | 
|  | 186     Future.wait(futures).then((_) { | 
|  | 187       server.shutdown(); | 
|  | 188       client.close(); | 
|  | 189     }); | 
|  | 190   }); | 
|  | 191 } | 
|  | 192 | 
|  | 193 void testBasicAuthenticateCallback(String algorithm, String qop) { | 
|  | 194   Server.start(algorithm, qop).then((server) { | 
|  | 195     HttpClient client = new HttpClient(); | 
|  | 196 | 
|  | 197     client.authenticate = (Uri url, String scheme, String realm) { | 
|  | 198       Expect.equals("Digest", scheme); | 
|  | 199       Expect.equals("test", realm); | 
|  | 200       Completer completer = new Completer(); | 
|  | 201       new Timer(const Duration(milliseconds: 10), () { | 
|  | 202           client.addCredentials( | 
|  | 203               Uri.parse("http://127.0.0.1:${server.port}/digest"), | 
|  | 204               "test", | 
|  | 205               new HttpClientDigestCredentials("dart", "password")); | 
|  | 206         completer.complete(true); | 
|  | 207       }); | 
|  | 208       return completer.future; | 
|  | 209     }; | 
|  | 210 | 
|  | 211     Future makeRequest(Uri url) { | 
|  | 212       return client.getUrl(url) | 
|  | 213         .then((HttpClientRequest request) => request.close()) | 
|  | 214         .then((HttpClientResponse response) { | 
|  | 215             Expect.equals(HttpStatus.OK, response.statusCode); | 
|  | 216             Expect.equals(1, response.headers["Authentication-Info"].length); | 
|  | 217           return response.fold(null, (x, y) {}); | 
|  | 218         }); | 
|  | 219     } | 
|  | 220 | 
|  | 221     var futures = []; | 
|  | 222     for (int i = 0; i < 5; i++) { | 
|  | 223       futures.add( | 
|  | 224           makeRequest( | 
|  | 225               Uri.parse("http://127.0.0.1:${server.port}/digest"))); | 
|  | 226     } | 
|  | 227     Future.wait(futures).then((_) { | 
|  | 228       server.shutdown(); | 
|  | 229       client.close(); | 
|  | 230     }); | 
|  | 231   }); | 
|  | 232 } | 
|  | 233 | 
|  | 234 // An Apache virtual directory configuration like this can be used for | 
|  | 235 // running the local server tests. | 
|  | 236 // | 
|  | 237 //  <Directory "/usr/local/prj/website/digest/"> | 
|  | 238 //    AllowOverride None | 
|  | 239 //    Order deny,allow | 
|  | 240 //    Deny from all | 
|  | 241 //    Allow from 127.0.0.0/255.0.0.0 ::1/128 | 
|  | 242 //    AuthType Digest | 
|  | 243 //    AuthName "test" | 
|  | 244 //   AuthDigestDomain /digest/ | 
|  | 245 //    AuthDigestProvider file | 
|  | 246 //    AuthUserFile /usr/local/prj/apache/passwd/digest-passwd | 
|  | 247 //    Require valid-user | 
|  | 248 //  </Directory> | 
|  | 249 // | 
|  | 250 | 
|  | 251 void testLocalServerDigest() { | 
|  | 252   HttpClient client = new HttpClient(); | 
|  | 253 | 
|  | 254   Future makeRequest() { | 
|  | 255     return client.getUrl(Uri.parse("http://127.0.0.1/digest/test")) | 
|  | 256         .then((HttpClientRequest request) => request.close()) | 
|  | 257         .then((HttpClientResponse response) { | 
|  | 258             Expect.equals(HttpStatus.OK, response.statusCode); | 
|  | 259             return response.fold(null, (x, y) {}); | 
|  | 260         }); | 
|  | 261   } | 
|  | 262 | 
|  | 263   client.addCredentials( | 
|  | 264         Uri.parse("http://127.0.0.1/digest"), | 
|  | 265         "test", | 
|  | 266         new HttpClientDigestCredentials("dart", "password")); | 
|  | 267 | 
|  | 268   client.authenticate = (Uri url, String scheme, String realm) { | 
|  | 269     client.addCredentials( | 
|  | 270         Uri.parse("http://127.0.0.1/digest"), | 
|  | 271         "test", | 
|  | 272         new HttpClientDigestCredentials("dart", "password")); | 
|  | 273     return new Future.value(true); | 
|  | 274   }; | 
|  | 275 | 
|  | 276   next() { | 
|  | 277     makeRequest().then((_) => next()); | 
|  | 278   } | 
|  | 279   next(); | 
|  | 280 } | 
|  | 281 | 
|  | 282 main() { | 
|  | 283   testNoCredentials(null, null); | 
|  | 284   testNoCredentials("MD5", null); | 
|  | 285   testNoCredentials("MD5", "auth"); | 
|  | 286   testCredentials(null, null); | 
|  | 287   testCredentials("MD5", null); | 
|  | 288   testCredentials("MD5", "auth"); | 
|  | 289   testCredentials("MD5", "auth-int"); | 
|  | 290   testBasicAuthenticateCallback(null, null); | 
|  | 291   testBasicAuthenticateCallback("MD5", null); | 
|  | 292   testBasicAuthenticateCallback("MD5", "auth"); | 
|  | 293   testBasicAuthenticateCallback("MD5", "auth-int"); | 
|  | 294   // These teste are not normally run. They can be used for locally | 
|  | 295   // testing with another web server (e.g. Apache). | 
|  | 296   //testLocalServerDigest(); | 
|  | 297 } | 
| OLD | NEW | 
|---|