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

Side by Side Diff: lib/src/barback/barback_server.dart

Issue 1790503005: Provide and respect HTTP cache headers in pub serve. (Closed) Base URL: https://github.com/dart-lang/pub.git@master
Patch Set: Created 4 years, 9 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
« no previous file with comments | « no previous file | lib/src/command/serve.dart » ('j') | 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 '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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/command/serve.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698