| 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 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 class _HttpIncoming extends Stream<List<int>> { | 7 class _HttpIncoming extends Stream<List<int>> { |
| 8 final int _transferLength; | 8 final int _transferLength; |
| 9 final Completer _dataCompleter = new Completer(); | 9 final Completer _dataCompleter = new Completer(); |
| 10 Stream<List<int>> _stream; | 10 Stream<List<int>> _stream; |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 _HeaderValue.parse(challenge[0], parameterSeparator: ","); | 374 _HeaderValue.parse(challenge[0], parameterSeparator: ","); |
| 375 _AuthenticationScheme scheme = | 375 _AuthenticationScheme scheme = |
| 376 new _AuthenticationScheme.fromString(header.value); | 376 new _AuthenticationScheme.fromString(header.value); |
| 377 String realm = header.parameters["realm"]; | 377 String realm = header.parameters["realm"]; |
| 378 | 378 |
| 379 // See if any credentials are available. | 379 // See if any credentials are available. |
| 380 var proxy = _httpRequest._proxy; | 380 var proxy = _httpRequest._proxy; |
| 381 | 381 |
| 382 var cr = _httpClient._findProxyCredentials(proxy); | 382 var cr = _httpClient._findProxyCredentials(proxy); |
| 383 if (cr != null) { | 383 if (cr != null) { |
| 384 return retryWithProxyCredentials(cr); | 384 if (cr.scheme == _AuthenticationScheme.BASIC) { |
| 385 return retryWithProxyCredentials(cr); |
| 386 } |
| 387 |
| 388 // Digest authentication only supports the MD5 algorithm. |
| 389 if (cr.scheme == _AuthenticationScheme.DIGEST && |
| 390 (header.parameters["algorithm"] == null || |
| 391 header.parameters["algorithm"].toLowerCase() == "md5")) { |
| 392 if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) { |
| 393 // If the nonce is not set then this is the first authenticate |
| 394 // response for these credentials. Set up authentication state. |
| 395 if (cr.nonce == null) { |
| 396 cr.nonce = header.parameters["nonce"]; |
| 397 cr.algorithm = "MD5"; |
| 398 cr.qop = header.parameters["qop"]; |
| 399 cr.nonceCount = 0; |
| 400 } |
| 401 // Credentials where found, prepare for retrying the request. |
| 402 return retryWithProxyCredentials(cr); |
| 403 } else if (header.parameters["stale"] != null && |
| 404 header.parameters["stale"].toLowerCase() == "true") { |
| 405 // If stale is true retry with new nonce. |
| 406 cr.nonce = header.parameters["nonce"]; |
| 407 // Credentials where found, prepare for retrying the request. |
| 408 return retryWithProxyCredentials(cr); |
| 409 } |
| 410 } |
| 385 } | 411 } |
| 386 | 412 |
| 387 // Ask for more credentials if none found. | 413 // Ask for more credentials if none found. |
| 388 if (_httpClient._authenticateProxy != null) { | 414 if (_httpClient._authenticateProxy != null) { |
| 389 Future authComplete = _httpClient._authenticateProxy(proxy.host, | 415 Future authComplete = _httpClient._authenticateProxy(proxy.host, |
| 390 proxy.port, | 416 proxy.port, |
| 391 "basic", | 417 "basic", |
| 392 realm); | 418 realm); |
| 393 return authComplete.then((credsAvailable) { | 419 return authComplete.then((credsAvailable) { |
| 394 if (credsAvailable) { | 420 if (credsAvailable) { |
| (...skipping 723 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1118 } | 1144 } |
| 1119 }, | 1145 }, |
| 1120 onDone: () { | 1146 onDone: () { |
| 1121 close(); | 1147 close(); |
| 1122 }); | 1148 }); |
| 1123 } | 1149 } |
| 1124 | 1150 |
| 1125 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { | 1151 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { |
| 1126 // Start with pausing the parser. | 1152 // Start with pausing the parser. |
| 1127 _subscription.pause(); | 1153 _subscription.pause(); |
| 1128 _Credentials cr; // Credentials used to authorize this request. | 1154 _ProxyCredentials proxyCreds; // Credentials used to authorize proxy. |
| 1155 _SiteCredentials creds; // Credentials used to authorize this request. |
| 1129 var outgoing = new _HttpOutgoing(_socket); | 1156 var outgoing = new _HttpOutgoing(_socket); |
| 1130 // Create new request object, wrapping the outgoing connection. | 1157 // Create new request object, wrapping the outgoing connection. |
| 1131 var request = new _HttpClientRequest(outgoing, | 1158 var request = new _HttpClientRequest(outgoing, |
| 1132 uri, | 1159 uri, |
| 1133 method, | 1160 method, |
| 1134 proxy, | 1161 proxy, |
| 1135 _httpClient, | 1162 _httpClient, |
| 1136 this); | 1163 this); |
| 1137 request.headers.host = uri.domain; | 1164 request.headers.host = uri.domain; |
| 1138 request.headers.port = port; | 1165 request.headers.port = port; |
| 1139 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); | 1166 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); |
| 1140 if (proxy.isAuthenticated) { | 1167 if (proxy.isAuthenticated) { |
| 1141 // If the proxy configuration contains user information use that | 1168 // If the proxy configuration contains user information use that |
| 1142 // for proxy basic authorization. | 1169 // for proxy basic authorization. |
| 1143 String auth = CryptoUtils.bytesToBase64( | 1170 String auth = CryptoUtils.bytesToBase64( |
| 1144 _encodeString("${proxy.username}:${proxy.password}")); | 1171 _encodeString("${proxy.username}:${proxy.password}")); |
| 1145 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); | 1172 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); |
| 1146 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { | 1173 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { |
| 1147 var cr = _httpClient._findProxyCredentials(proxy); | 1174 proxyCreds = _httpClient._findProxyCredentials(proxy); |
| 1148 if (cr != null) { | 1175 if (proxyCreds != null) { |
| 1149 cr.authorize(request); | 1176 proxyCreds.authorize(request); |
| 1150 } | 1177 } |
| 1151 } | 1178 } |
| 1152 if (uri.userInfo != null && !uri.userInfo.isEmpty) { | 1179 if (uri.userInfo != null && !uri.userInfo.isEmpty) { |
| 1153 // If the URL contains user information use that for basic | 1180 // If the URL contains user information use that for basic |
| 1154 // authorization. | 1181 // authorization. |
| 1155 String auth = | 1182 String auth = |
| 1156 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo)); | 1183 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo)); |
| 1157 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | 1184 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 1158 } else { | 1185 } else { |
| 1159 // Look for credentials. | 1186 // Look for credentials. |
| 1160 cr = _httpClient._findCredentials(uri); | 1187 creds = _httpClient._findCredentials(uri); |
| 1161 if (cr != null) { | 1188 if (creds != null) { |
| 1162 cr.authorize(request); | 1189 creds.authorize(request); |
| 1163 } | 1190 } |
| 1164 } | 1191 } |
| 1165 // Start sending the request (lazy, delayed until the user provides | 1192 // Start sending the request (lazy, delayed until the user provides |
| 1166 // data). | 1193 // data). |
| 1167 _httpParser.responseToMethod = method; | 1194 _httpParser.responseToMethod = method; |
| 1168 _streamFuture = outgoing.done | 1195 _streamFuture = outgoing.done |
| 1169 .then((s) { | 1196 .then((s) { |
| 1170 // Request sent, set up response completer. | 1197 // Request sent, set up response completer. |
| 1171 _nextResponseCompleter = new Completer(); | 1198 _nextResponseCompleter = new Completer(); |
| 1172 | 1199 |
| 1173 // Listen for response. | 1200 // Listen for response. |
| 1174 _nextResponseCompleter.future | 1201 _nextResponseCompleter.future |
| 1175 .then((incoming) { | 1202 .then((incoming) { |
| 1176 incoming.dataDone.then((_) { | 1203 incoming.dataDone.then((_) { |
| 1177 if (!_dispose && | 1204 if (!_dispose && |
| 1178 incoming.headers.persistentConnection && | 1205 incoming.headers.persistentConnection && |
| 1179 request.persistentConnection) { | 1206 request.persistentConnection) { |
| 1180 // Return connection, now we are done. | 1207 // Return connection, now we are done. |
| 1181 _httpClient._returnConnection(this); | 1208 _httpClient._returnConnection(this); |
| 1182 _subscription.resume(); | 1209 _subscription.resume(); |
| 1183 } else { | 1210 } else { |
| 1184 destroy(); | 1211 destroy(); |
| 1185 } | 1212 } |
| 1186 }); | 1213 }); |
| 1187 // For digest authentication check if the server | 1214 // For digest authentication if proxy check if the proxy |
| 1188 // requests the client to start using a new nonce. | 1215 // requests the client to start using a new nonce for proxy |
| 1189 if (cr != null && cr.scheme == _AuthenticationScheme.DIGEST) { | 1216 // authentication. |
| 1217 if (proxyCreds != null && |
| 1218 proxyCreds.scheme == _AuthenticationScheme.DIGEST) { |
| 1219 var authInfo = incoming.headers["proxy-authentication-info"]; |
| 1220 if (authInfo != null && authInfo.length == 1) { |
| 1221 var header = |
| 1222 _HeaderValue.parse( |
| 1223 authInfo[0], parameterSeparator: ','); |
| 1224 var nextnonce = header.parameters["nextnonce"]; |
| 1225 if (nextnonce != null) proxyCreds.nonce = nextnonce; |
| 1226 } |
| 1227 } |
| 1228 // For digest authentication check if the server requests the |
| 1229 // client to start using a new nonce. |
| 1230 if (creds != null && |
| 1231 creds.scheme == _AuthenticationScheme.DIGEST) { |
| 1190 var authInfo = incoming.headers["authentication-info"]; | 1232 var authInfo = incoming.headers["authentication-info"]; |
| 1191 if (authInfo != null && authInfo.length == 1) { | 1233 if (authInfo != null && authInfo.length == 1) { |
| 1192 var header = | 1234 var header = |
| 1193 _HeaderValue.parse( | 1235 _HeaderValue.parse( |
| 1194 authInfo[0], parameterSeparator: ','); | 1236 authInfo[0], parameterSeparator: ','); |
| 1195 var nextnonce = header.parameters["nextnonce"]; | 1237 var nextnonce = header.parameters["nextnonce"]; |
| 1196 if (nextnonce != null) cr.nonce = nextnonce; | 1238 if (nextnonce != null) creds.nonce = nextnonce; |
| 1197 } | 1239 } |
| 1198 } | 1240 } |
| 1199 request._onIncoming(incoming); | 1241 request._onIncoming(incoming); |
| 1200 }) | 1242 }) |
| 1201 // If we see a state error, we failed to get the 'first' | 1243 // If we see a state error, we failed to get the 'first' |
| 1202 // element. | 1244 // element. |
| 1203 // Transform the error to a HttpParserException, for | 1245 // Transform the error to a HttpParserException, for |
| 1204 // consistency. | 1246 // consistency. |
| 1205 .catchError((error) { | 1247 .catchError((error) { |
| 1206 throw new HttpParserException( | 1248 throw new HttpParserException( |
| (...skipping 811 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2018 String realm; | 2060 String realm; |
| 2019 bool used = false; | 2061 bool used = false; |
| 2020 | 2062 |
| 2021 // Digest specific fields. | 2063 // Digest specific fields. |
| 2022 String ha1; | 2064 String ha1; |
| 2023 String nonce; | 2065 String nonce; |
| 2024 String algorithm; | 2066 String algorithm; |
| 2025 String qop; | 2067 String qop; |
| 2026 int nonceCount; | 2068 int nonceCount; |
| 2027 | 2069 |
| 2028 _Credentials(this.realm, this.credentials) { | 2070 _Credentials(this.credentials, this.realm) { |
| 2029 if (credentials.scheme == _AuthenticationScheme.DIGEST) { | 2071 if (credentials.scheme == _AuthenticationScheme.DIGEST) { |
| 2030 // Calculate the H(A1) value once. There is no mentioning of | 2072 // Calculate the H(A1) value once. There is no mentioning of |
| 2031 // username/password encoding in RFC 2617. However there is an | 2073 // username/password encoding in RFC 2617. However there is an |
| 2032 // open draft for adding an additional accept-charset parameter to | 2074 // open draft for adding an additional accept-charset parameter to |
| 2033 // the WWW-Authenticate and Proxy-Authenticate headers, see | 2075 // the WWW-Authenticate and Proxy-Authenticate headers, see |
| 2034 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For | 2076 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
| 2035 // now always use UTF-8 encoding. | 2077 // now always use UTF-8 encoding. |
| 2036 _HttpClientDigestCredentials creds = credentials; | 2078 _HttpClientDigestCredentials creds = credentials; |
| 2037 var hasher = new MD5(); | 2079 var hasher = new MD5(); |
| 2038 hasher.add(_encodeString(creds.username)); | 2080 hasher.add(_encodeString(creds.username)); |
| 2039 hasher.add([_CharCode.COLON]); | 2081 hasher.add([_CharCode.COLON]); |
| 2040 hasher.add(realm.codeUnits); | 2082 hasher.add(realm.codeUnits); |
| 2041 hasher.add([_CharCode.COLON]); | 2083 hasher.add([_CharCode.COLON]); |
| 2042 hasher.add(_encodeString(creds.password)); | 2084 hasher.add(_encodeString(creds.password)); |
| 2043 ha1 = CryptoUtils.bytesToHex(hasher.close()); | 2085 ha1 = CryptoUtils.bytesToHex(hasher.close()); |
| 2044 } | 2086 } |
| 2045 } | 2087 } |
| 2046 | 2088 |
| 2047 _AuthenticationScheme get scheme => credentials.scheme; | 2089 _AuthenticationScheme get scheme => credentials.scheme; |
| 2048 | 2090 |
| 2049 void authorize(HttpClientRequest request); | 2091 void authorize(HttpClientRequest request); |
| 2050 } | 2092 } |
| 2051 | 2093 |
| 2052 class _SiteCredentials extends _Credentials { | 2094 class _SiteCredentials extends _Credentials { |
| 2053 Uri uri; | 2095 Uri uri; |
| 2054 | 2096 |
| 2055 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds) | 2097 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds) |
| 2056 : super(realm, creds); | 2098 : super(creds, realm); |
| 2057 | 2099 |
| 2058 bool applies(Uri uri, _AuthenticationScheme scheme) { | 2100 bool applies(Uri uri, _AuthenticationScheme scheme) { |
| 2059 if (scheme != null && credentials.scheme != scheme) return false; | 2101 if (scheme != null && credentials.scheme != scheme) return false; |
| 2060 if (uri.domain != this.uri.domain) return false; | 2102 if (uri.domain != this.uri.domain) return false; |
| 2061 int thisPort = | 2103 int thisPort = |
| 2062 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; | 2104 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; |
| 2063 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; | 2105 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; |
| 2064 if (otherPort != thisPort) return false; | 2106 if (otherPort != thisPort) return false; |
| 2065 return uri.path.startsWith(this.uri.path); | 2107 return uri.path.startsWith(this.uri.path); |
| 2066 } | 2108 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 2079 | 2121 |
| 2080 | 2122 |
| 2081 class _ProxyCredentials extends _Credentials { | 2123 class _ProxyCredentials extends _Credentials { |
| 2082 String host; | 2124 String host; |
| 2083 int port; | 2125 int port; |
| 2084 | 2126 |
| 2085 _ProxyCredentials(this.host, | 2127 _ProxyCredentials(this.host, |
| 2086 this.port, | 2128 this.port, |
| 2087 realm, | 2129 realm, |
| 2088 _HttpClientCredentials creds) | 2130 _HttpClientCredentials creds) |
| 2089 : super(realm, creds); | 2131 : super(creds, realm); |
| 2090 | |
| 2091 | 2132 |
| 2092 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { | 2133 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { |
| 2093 return proxy.host == host && proxy.port == port; | 2134 return proxy.host == host && proxy.port == port; |
| 2094 } | 2135 } |
| 2095 | 2136 |
| 2096 void authorize(HttpClientRequest request) { | 2137 void authorize(HttpClientRequest request) { |
| 2097 // Digest credentials cannot be used without a nonce from the | 2138 // Digest credentials cannot be used without a nonce from the |
| 2098 // server. | 2139 // server. |
| 2099 if (credentials.scheme == _AuthenticationScheme.DIGEST && | 2140 if (credentials.scheme == _AuthenticationScheme.DIGEST && |
| 2100 nonce == null) { | 2141 nonce == null) { |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2206 return buffer.toString(); | 2247 return buffer.toString(); |
| 2207 } | 2248 } |
| 2208 | 2249 |
| 2209 void authorize(_Credentials credentials, HttpClientRequest request) { | 2250 void authorize(_Credentials credentials, HttpClientRequest request) { |
| 2210 request.headers.set(HttpHeaders.AUTHORIZATION, | 2251 request.headers.set(HttpHeaders.AUTHORIZATION, |
| 2211 authorization(credentials, request)); | 2252 authorization(credentials, request)); |
| 2212 } | 2253 } |
| 2213 | 2254 |
| 2214 void authorizeProxy(_ProxyCredentials credentials, | 2255 void authorizeProxy(_ProxyCredentials credentials, |
| 2215 HttpClientRequest request) { | 2256 HttpClientRequest request) { |
| 2216 // TODO(sgjesse): Implement!!! | 2257 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, |
| 2217 throw new UnsupportedError("Digest authentication not yet supported"); | 2258 authorization(credentials, request)); |
| 2218 } | 2259 } |
| 2219 | 2260 |
| 2220 String username; | 2261 String username; |
| 2221 String password; | 2262 String password; |
| 2222 } | 2263 } |
| 2223 | 2264 |
| 2224 | 2265 |
| 2225 class _RedirectInfo implements RedirectInfo { | 2266 class _RedirectInfo implements RedirectInfo { |
| 2226 const _RedirectInfo(int this.statusCode, | 2267 const _RedirectInfo(int this.statusCode, |
| 2227 String this.method, | 2268 String this.method, |
| 2228 Uri this.location); | 2269 Uri this.location); |
| 2229 final int statusCode; | 2270 final int statusCode; |
| 2230 final String method; | 2271 final String method; |
| 2231 final Uri location; | 2272 final Uri location; |
| 2232 } | 2273 } |
| OLD | NEW |