Chromium Code Reviews| 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 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:io'; | 6 import 'dart:io'; |
| 7 | 7 |
| 8 import 'package:barback/barback.dart'; | 8 import 'package:barback/barback.dart'; |
| 9 import "package:crypto/crypto.dart"; | |
| 9 import 'package:mime/mime.dart'; | 10 import 'package:mime/mime.dart'; |
| 10 import 'package:path/path.dart' as path; | 11 import 'package:path/path.dart' as path; |
| 11 import 'package:shelf/shelf.dart' as shelf; | 12 import 'package:shelf/shelf.dart' as shelf; |
| 12 import 'package:stack_trace/stack_trace.dart'; | 13 import 'package:stack_trace/stack_trace.dart'; |
| 13 | 14 |
| 14 import '../barback.dart'; | 15 import '../barback.dart'; |
| 15 import '../io.dart'; | 16 import '../io.dart'; |
| 16 import '../log.dart' as log; | 17 import '../log.dart' as log; |
| 17 import '../utils.dart'; | 18 import '../utils.dart'; |
| 18 import 'base_server.dart'; | 19 import 'base_server.dart'; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 82 var parts = path.url.split(url.path); | 83 var parts = path.url.split(url.path); |
| 83 | 84 |
| 84 // Strip the leading "/" from the URL. | 85 // Strip the leading "/" from the URL. |
| 85 if (parts.isNotEmpty && parts.first == "/") parts = parts.skip(1); | 86 if (parts.isNotEmpty && parts.first == "/") parts = parts.skip(1); |
| 86 | 87 |
| 87 var relativePath = path.url.join(rootDirectory, path.url.joinAll(parts)); | 88 var relativePath = path.url.join(rootDirectory, path.url.joinAll(parts)); |
| 88 return new AssetId(package, relativePath); | 89 return new AssetId(package, relativePath); |
| 89 } | 90 } |
| 90 | 91 |
| 91 /// Handles an HTTP request. | 92 /// Handles an HTTP request. |
| 92 handleRequest(shelf.Request request) { | 93 Future<shelf.Response> handleRequest(shelf.Request request) async { |
| 93 if (request.method != "GET" && request.method != "HEAD") { | 94 if (request.method != "GET" && request.method != "HEAD") { |
| 94 return methodNotAllowed(request); | 95 return methodNotAllowed(request); |
| 95 } | 96 } |
| 96 | 97 |
| 97 var id; | 98 var id; |
| 98 try { | 99 try { |
| 99 id = urlToId(request.url); | 100 id = urlToId(request.url); |
| 100 } on FormatException catch (ex) { | 101 } on FormatException catch (ex) { |
| 101 // If we got here, we had a path like "/packages" which is a special | 102 // If we got here, we had a path like "/packages" which is a special |
| 102 // directory, but not a valid path since it lacks a following package | 103 // directory, but not a valid path since it lacks a following package |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 // Allow requests of any origin to access "pub serve". This is useful for | 148 // Allow requests of any origin to access "pub serve". This is useful for |
| 148 // running "pub serve" in parallel with another development server. Since | 149 // running "pub serve" in parallel with another development server. Since |
| 149 // "pub serve" is only used as a development server and doesn't require | 150 // "pub serve" is only used as a development server and doesn't require |
| 150 // any sort of credentials anyway, this is secure. | 151 // any sort of credentials anyway, this is secure. |
| 151 return response.change( | 152 return response.change( |
| 152 headers: const {"Access-Control-Allow-Origin": "*"}); | 153 headers: const {"Access-Control-Allow-Origin": "*"}); |
| 153 }); | 154 }); |
| 154 } | 155 } |
| 155 | 156 |
| 156 /// Returns the body of [asset] as a response to [request]. | 157 /// Returns the body of [asset] as a response to [request]. |
| 157 Future<shelf.Response> _serveAsset(shelf.Request request, Asset asset) { | 158 Future<shelf.Response> _serveAsset(shelf.Request request, Asset asset) async { |
| 158 return validateStream(asset.read()).then((stream) { | 159 try { |
| 159 addResult(new BarbackServerResult._success(request.url, asset.id)); | 160 var pair = tee(await validateStream(asset.read())); |
| 160 var headers = {}; | 161 var responseStream = pair.first; |
| 161 var mimeType = lookupMimeType(asset.id.path); | 162 var hashStream = pair.last; |
| 162 if (mimeType != null) headers['Content-Type'] = mimeType; | 163 |
| 163 return new shelf.Response.ok(stream, headers: headers); | 164 // Allow the asset to be cached based on its content hash. |
| 164 }).catchError((error, trace) { | 165 var sha = new SHA1(); |
| 166 await hashStream.forEach((chunk) { | |
| 167 sha.add(chunk); | |
| 168 }); | |
| 169 | |
| 170 var assetSha = CryptoUtils.bytesToHex(sha.close()); | |
|
nweiz
2016/03/12 01:30:51
CryptoUtils is going to get removed once I get aro
Bob Nystrom
2016/03/14 19:15:10
I didn't see anything related to hex in there, so
nweiz
2016/03/14 19:29:13
https://www.dartdocs.org/documentation/convert/1.0
| |
| 171 var previousSha = request.headers["if-none-match"]; | |
| 172 | |
| 173 var headers = { | |
| 174 "Cache-Control": "max-age=3600", | |
|
nweiz
2016/03/12 01:30:51
It's not clear to me why this is being set. Consid
Bob Nystrom
2016/03/14 19:15:10
I'm an HTTP noob, but from what I read, you have t
| |
| 175 "ETag": assetSha | |
| 176 }; | |
| 177 | |
| 178 if (assetSha == previousSha) { | |
| 179 // We're requesting an unchanged asset so don't push it down the wire | |
| 180 // again. | |
| 181 addResult(new BarbackServerResult._cached(request.url, asset.id)); | |
| 182 return new shelf.Response.notModified(headers: headers); | |
| 183 } else { | |
| 184 addResult(new BarbackServerResult._success(request.url, asset.id)); | |
| 185 var mimeType = lookupMimeType(asset.id.path); | |
| 186 if (mimeType != null) headers['Content-Type'] = mimeType; | |
| 187 return new shelf.Response.ok(responseStream, headers: headers); | |
| 188 } | |
| 189 } catch (error, trace) { | |
| 165 addResult(new BarbackServerResult._failure(request.url, asset.id, error)); | 190 addResult(new BarbackServerResult._failure(request.url, asset.id, error)); |
| 166 | 191 |
| 167 // If we couldn't read the asset, handle the error gracefully. | 192 // If we couldn't read the asset, handle the error gracefully. |
| 168 if (error is FileSystemException) { | 193 if (error is FileSystemException) { |
| 169 // Assume this means the asset was a file-backed source asset | 194 // Assume this means the asset was a file-backed source asset |
| 170 // and we couldn't read it, so treat it like a missing asset. | 195 // and we couldn't read it, so treat it like a missing asset. |
| 171 return notFound(request, error: error.toString(), asset: asset.id); | 196 return notFound(request, error: error.toString(), asset: asset.id); |
| 172 } | 197 } |
| 173 | 198 |
| 174 trace = new Chain.forTrace(trace); | 199 trace = new Chain.forTrace(trace); |
| 175 logRequest(request, "$error\n$trace"); | 200 logRequest(request, "$error\n$trace"); |
| 176 | 201 |
| 177 // Otherwise, it's some internal error. | 202 // Otherwise, it's some internal error. |
| 178 return new shelf.Response.internalServerError(body: error.toString()); | 203 return new shelf.Response.internalServerError(body: error.toString()); |
| 179 }); | 204 } |
| 180 } | 205 } |
| 181 } | 206 } |
| 182 | 207 |
| 183 /// The result of the server handling a URL. | 208 /// The result of the server handling a URL. |
| 184 /// | 209 /// |
| 185 /// Only requests for which an asset was requested from barback will emit a | 210 /// Only requests for which an asset was requested from barback will emit a |
| 186 /// result. Malformed requests will be handled internally. | 211 /// result. Malformed requests will be handled internally. |
| 187 class BarbackServerResult { | 212 class BarbackServerResult { |
| 188 /// The requested url. | 213 /// The requested url. |
| 189 final Uri url; | 214 final Uri url; |
| 190 | 215 |
| 191 /// The id that [url] identifies. | 216 /// The id that [url] identifies. |
| 192 final AssetId id; | 217 final AssetId id; |
| 193 | 218 |
| 194 /// The error thrown by barback. | 219 /// The error thrown by barback. |
| 195 /// | 220 /// |
| 196 /// If the request was served successfully, this will be null. | 221 /// If the request was served successfully, this will be null. |
| 197 final error; | 222 final error; |
| 198 | 223 |
| 199 /// Whether the request was served successfully. | 224 /// Whether the request was served successfully. |
| 200 bool get isSuccess => error == null; | 225 bool get isSuccess => error == null; |
| 201 | 226 |
| 227 /// Whether the request was for a previously cached asset. | |
| 228 final bool isCached; | |
| 229 | |
| 202 /// Whether the request was served unsuccessfully. | 230 /// Whether the request was served unsuccessfully. |
| 203 bool get isFailure => !isSuccess; | 231 bool get isFailure => !isSuccess; |
| 204 | 232 |
| 205 BarbackServerResult._success(this.url, this.id) | 233 BarbackServerResult._success(this.url, this.id) |
| 206 : error = null; | 234 : error = null, |
| 235 isCached = false; | |
| 207 | 236 |
| 208 BarbackServerResult._failure(this.url, this.id, this.error); | 237 BarbackServerResult._cached(this.url, this.id) |
| 238 : error = null, | |
| 239 isCached = true; | |
| 240 | |
| 241 BarbackServerResult._failure(this.url, this.id, this.error) | |
| 242 : isCached = false; | |
| 209 } | 243 } |
| OLD | NEW |