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 library http_server; | 5 library http_server; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'dart:convert' show | 10 import 'dart:convert' show HtmlEscape; |
11 HtmlEscape; | |
12 | 11 |
13 import 'path.dart'; | 12 import 'path.dart'; |
14 import 'test_suite.dart'; // For TestUtils. | 13 import 'test_suite.dart'; // For TestUtils. |
15 // TODO(efortuna): Rewrite to not use the args library and simply take an | 14 // TODO(efortuna): Rewrite to not use the args library and simply take an |
16 // expected number of arguments, so test.dart doesn't rely on the args library? | 15 // expected number of arguments, so test.dart doesn't rely on the args library? |
17 // See discussion on https://codereview.chromium.org/11931025/. | 16 // See discussion on https://codereview.chromium.org/11931025/. |
18 import 'vendored_pkg/args/args.dart'; | 17 import 'vendored_pkg/args/args.dart'; |
19 import 'utils.dart'; | 18 import 'utils.dart'; |
20 | 19 |
21 class DispatchingServer { | 20 class DispatchingServer { |
22 HttpServer server; | 21 HttpServer server; |
23 Map<String, Function> _handlers = new Map<String, Function>(); | 22 Map<String, Function> _handlers = new Map<String, Function>(); |
24 Function _notFound; | 23 Function _notFound; |
25 | 24 |
26 DispatchingServer(this.server, | 25 DispatchingServer( |
27 void onError(e), | 26 this.server, void onError(e), void this._notFound(HttpRequest request)) { |
28 void this._notFound(HttpRequest request)) { | |
29 server.listen(_dispatchRequest, onError: onError); | 27 server.listen(_dispatchRequest, onError: onError); |
30 } | 28 } |
31 | 29 |
32 void addHandler(String prefix, void handler(HttpRequest request)) { | 30 void addHandler(String prefix, void handler(HttpRequest request)) { |
33 _handlers[prefix] = handler; | 31 _handlers[prefix] = handler; |
34 } | 32 } |
35 | 33 |
36 void _dispatchRequest(HttpRequest request) { | 34 void _dispatchRequest(HttpRequest request) { |
37 // If the request path matches a prefix in _handlers, send it to that | 35 // If the request path matches a prefix in _handlers, send it to that |
38 // handler. Otherwise, run the notFound handler. | 36 // handler. Otherwise, run the notFound handler. |
39 for (String prefix in _handlers.keys) { | 37 for (String prefix in _handlers.keys) { |
40 if (request.uri.path.startsWith(prefix)) { | 38 if (request.uri.path.startsWith(prefix)) { |
41 _handlers[prefix](request); | 39 _handlers[prefix](request); |
42 return; | 40 return; |
43 } | 41 } |
44 } | 42 } |
45 _notFound(request); | 43 _notFound(request); |
46 } | 44 } |
47 } | 45 } |
48 | 46 |
49 | |
50 | |
51 /// Interface of the HTTP server: | 47 /// Interface of the HTTP server: |
52 /// | 48 /// |
53 /// /echo: This will stream the data received in the request stream back | 49 /// /echo: This will stream the data received in the request stream back |
54 /// to the client. | 50 /// to the client. |
55 /// /root_dart/X: This will serve the corresponding file from the dart | 51 /// /root_dart/X: This will serve the corresponding file from the dart |
56 /// directory (i.e. '$DartDirectory/X'). | 52 /// directory (i.e. '$DartDirectory/X'). |
57 /// /root_build/X: This will serve the corresponding file from the build | 53 /// /root_build/X: This will serve the corresponding file from the build |
58 /// directory (i.e. '$BuildDirectory/X'). | 54 /// directory (i.e. '$BuildDirectory/X'). |
59 /// /FOO/packages/BAR: This will serve the corresponding file from the packages | 55 /// /FOO/packages/BAR: This will serve the corresponding file from the packages |
60 /// directory (i.e. '$BuildDirectory/packages/BAR') or the | 56 /// directory (i.e. '$BuildDirectory/packages/BAR') or the |
61 /// passed-in package root | 57 /// passed-in package root |
62 /// /ws: This will upgrade the connection to a WebSocket connection and echo | 58 /// /ws: This will upgrade the connection to a WebSocket connection and echo |
63 /// all data back to the client. | 59 /// all data back to the client. |
64 /// | 60 /// |
65 /// In case a path does not refer to a file but rather to a directory, a | 61 /// In case a path does not refer to a file but rather to a directory, a |
66 /// directory listing will be displayed. | 62 /// directory listing will be displayed. |
67 | 63 |
68 const PREFIX_BUILDDIR = 'root_build'; | 64 const PREFIX_BUILDDIR = 'root_build'; |
69 const PREFIX_DARTDIR = 'root_dart'; | 65 const PREFIX_DARTDIR = 'root_dart'; |
70 | 66 |
71 // TODO(kustermann,ricow): We could change this to the following scheme: | 67 // TODO(kustermann,ricow): We could change this to the following scheme: |
72 // http://host:port/root_packages/X -> $BuildDir/packages/X | 68 // http://host:port/root_packages/X -> $BuildDir/packages/X |
73 // Issue: 8368 | 69 // Issue: 8368 |
74 | 70 |
75 main(List<String> arguments) { | 71 main(List<String> arguments) { |
76 // This script is in [dart]/tools/testing/dart. | 72 // This script is in [dart]/tools/testing/dart. |
77 TestUtils.setDartDirUri(Platform.script.resolve('../../..')); | 73 TestUtils.setDartDirUri(Platform.script.resolve('../../..')); |
78 /** Convenience method for local testing. */ | 74 /** Convenience method for local testing. */ |
79 var parser = new ArgParser(); | 75 var parser = new ArgParser(); |
80 parser.addOption('port', abbr: 'p', | 76 parser.addOption('port', |
| 77 abbr: 'p', |
81 help: 'The main server port we wish to respond to requests.', | 78 help: 'The main server port we wish to respond to requests.', |
82 defaultsTo: '0'); | 79 defaultsTo: '0'); |
83 parser.addOption('crossOriginPort', abbr: 'c', | 80 parser.addOption('crossOriginPort', |
| 81 abbr: 'c', |
84 help: 'A different port that accepts request from the main server port.', | 82 help: 'A different port that accepts request from the main server port.', |
85 defaultsTo: '0'); | 83 defaultsTo: '0'); |
86 parser.addFlag('help', abbr: 'h', negatable: false, | 84 parser.addFlag('help', |
87 help: 'Print this usage information.'); | 85 abbr: 'h', negatable: false, help: 'Print this usage information.'); |
88 parser.addOption('build-directory', help: 'The build directory to use.'); | 86 parser.addOption('build-directory', help: 'The build directory to use.'); |
89 parser.addOption('package-root', help: 'The package root to use.'); | 87 parser.addOption('package-root', help: 'The package root to use.'); |
90 parser.addOption('network', help: 'The network interface to use.', | 88 parser.addOption('network', |
91 defaultsTo: '0.0.0.0'); | 89 help: 'The network interface to use.', defaultsTo: '0.0.0.0'); |
92 parser.addFlag('csp', help: 'Use Content Security Policy restrictions.', | 90 parser.addFlag('csp', |
93 defaultsTo: false); | 91 help: 'Use Content Security Policy restrictions.', defaultsTo: false); |
94 parser.addOption('runtime', help: 'The runtime we are using (for csp flags).', | 92 parser.addOption('runtime', |
95 defaultsTo: 'none'); | 93 help: 'The runtime we are using (for csp flags).', defaultsTo: 'none'); |
96 | 94 |
97 var args = parser.parse(arguments); | 95 var args = parser.parse(arguments); |
98 if (args['help']) { | 96 if (args['help']) { |
99 print(parser.getUsage()); | 97 print(parser.getUsage()); |
100 } else { | 98 } else { |
101 var servers = new TestingServers(new Path(args['build-directory']), | 99 var servers = new TestingServers(new Path(args['build-directory']), |
102 args['csp'], | 100 args['csp'], args['runtime'], null, args['package-root']); |
103 args['runtime'], | |
104 null, | |
105 args['package-root']); | |
106 var port = int.parse(args['port']); | 101 var port = int.parse(args['port']); |
107 var crossOriginPort = int.parse(args['crossOriginPort']); | 102 var crossOriginPort = int.parse(args['crossOriginPort']); |
108 servers.startServers(args['network'], | 103 servers |
109 port: port, | 104 .startServers(args['network'], |
110 crossOriginPort: crossOriginPort).then((_) { | 105 port: port, crossOriginPort: crossOriginPort) |
| 106 .then((_) { |
111 DebugLogger.info('Server listening on port ${servers.port}'); | 107 DebugLogger.info('Server listening on port ${servers.port}'); |
112 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); | 108 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); |
113 }); | 109 }); |
114 } | 110 } |
115 } | 111 } |
116 | 112 |
117 /** | 113 /** |
118 * Runs a set of servers that are initialized specifically for the needs of our | 114 * Runs a set of servers that are initialized specifically for the needs of our |
119 * test framework, such as dealing with package-root. | 115 * test framework, such as dealing with package-root. |
120 */ | 116 */ |
(...skipping 10 matching lines...) Expand all Loading... |
131 ]; | 127 ]; |
132 | 128 |
133 List _serverList = []; | 129 List _serverList = []; |
134 Path _buildDirectory = null; | 130 Path _buildDirectory = null; |
135 Path _dartDirectory = null; | 131 Path _dartDirectory = null; |
136 Path _packageRoot; | 132 Path _packageRoot; |
137 final bool useContentSecurityPolicy; | 133 final bool useContentSecurityPolicy; |
138 final String runtime; | 134 final String runtime; |
139 DispatchingServer _server; | 135 DispatchingServer _server; |
140 | 136 |
141 TestingServers(Path buildDirectory, | 137 TestingServers(Path buildDirectory, this.useContentSecurityPolicy, |
142 this.useContentSecurityPolicy, | 138 [String this.runtime = 'none', |
143 [String this.runtime = 'none', String dartDirectory, | 139 String dartDirectory, |
144 String packageRoot]) { | 140 String packageRoot]) { |
145 _buildDirectory = TestUtils.absolutePath(buildDirectory); | 141 _buildDirectory = TestUtils.absolutePath(buildDirectory); |
146 _dartDirectory = dartDirectory == null ? TestUtils.dartDir | 142 _dartDirectory = |
147 : new Path(dartDirectory); | 143 dartDirectory == null ? TestUtils.dartDir : new Path(dartDirectory); |
148 _packageRoot = packageRoot == null ? | 144 _packageRoot = packageRoot == null |
149 _buildDirectory.append('packages') : | 145 ? _buildDirectory.append('packages') |
150 new Path(packageRoot); | 146 : new Path(packageRoot); |
151 } | 147 } |
152 | 148 |
153 int get port => _serverList[0].port; | 149 int get port => _serverList[0].port; |
154 int get crossOriginPort => _serverList[1].port; | 150 int get crossOriginPort => _serverList[1].port; |
155 DispatchingServer get server => _server; | 151 DispatchingServer get server => _server; |
156 | 152 |
157 /** | 153 /** |
158 * [startServers] will start two Http servers. | 154 * [startServers] will start two Http servers. |
159 * The first server listens on [port] and sets | 155 * The first server listens on [port] and sets |
160 * "Access-Control-Allow-Origin: *" | 156 * "Access-Control-Allow-Origin: *" |
161 * The second server listens on [crossOriginPort] and sets | 157 * The second server listens on [crossOriginPort] and sets |
162 * "Access-Control-Allow-Origin: client:port1 | 158 * "Access-Control-Allow-Origin: client:port1 |
163 * "Access-Control-Allow-Credentials: true" | 159 * "Access-Control-Allow-Credentials: true" |
164 */ | 160 */ |
165 Future startServers(String host, {int port: 0, int crossOriginPort: 0}) { | 161 Future startServers(String host, {int port: 0, int crossOriginPort: 0}) { |
166 return _startHttpServer(host, port: port).then((server) { | 162 return _startHttpServer(host, port: port).then((server) { |
167 _server = server; | 163 _server = server; |
168 return _startHttpServer(host, | 164 return _startHttpServer(host, |
169 port: crossOriginPort, | 165 port: crossOriginPort, allowedPort: _serverList[0].port); |
170 allowedPort:_serverList[0].port); | |
171 }); | 166 }); |
172 } | 167 } |
173 | 168 |
174 String httpServerCommandline() { | 169 String httpServerCommandline() { |
175 var dart = Platform.resolvedExecutable; | 170 var dart = Platform.resolvedExecutable; |
176 var dartDir = TestUtils.dartDir; | 171 var dartDir = TestUtils.dartDir; |
177 var script = dartDir.join(new Path("tools/testing/dart/http_server.dart")); | 172 var script = dartDir.join(new Path("tools/testing/dart/http_server.dart")); |
178 var buildDirectory = _buildDirectory.toNativePath(); | 173 var buildDirectory = _buildDirectory.toNativePath(); |
179 var csp = useContentSecurityPolicy ? '--csp ' : ''; | 174 var csp = useContentSecurityPolicy ? '--csp ' : ''; |
180 return '$dart $script -p $port -c $crossOriginPort $csp' | 175 return '$dart $script -p $port -c $crossOriginPort $csp' |
181 '--build-directory=$buildDirectory --runtime=$runtime ' | 176 '--build-directory=$buildDirectory --runtime=$runtime ' |
182 '--package-root=$_packageRoot'; | 177 '--package-root=$_packageRoot'; |
183 } | 178 } |
184 | 179 |
185 void stopServers() { | 180 void stopServers() { |
186 for (var server in _serverList) { | 181 for (var server in _serverList) { |
187 server.close(); | 182 server.close(); |
188 } | 183 } |
189 } | 184 } |
190 | 185 |
191 void _onError(e) { | 186 void _onError(e) { |
192 DebugLogger.error('HttpServer: an error occured', e); | 187 DebugLogger.error('HttpServer: an error occured', e); |
193 } | 188 } |
194 | 189 |
195 Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { | 190 Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { |
196 return HttpServer.bind(host, port).then((HttpServer httpServer) { | 191 return HttpServer.bind(host, port).then((HttpServer httpServer) { |
197 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); | 192 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); |
198 server.addHandler('/echo', _handleEchoRequest); | 193 server.addHandler('/echo', _handleEchoRequest); |
199 server.addHandler('/ws', _handleWebSocketRequest); | 194 server.addHandler('/ws', _handleWebSocketRequest); |
200 fileHandler(request) { | 195 fileHandler(request) { |
201 _handleFileOrDirectoryRequest(request, allowedPort); | 196 _handleFileOrDirectoryRequest(request, allowedPort); |
202 } | 197 } |
203 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); | 198 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); |
204 server.addHandler('/$PREFIX_DARTDIR', fileHandler); | 199 server.addHandler('/$PREFIX_DARTDIR', fileHandler); |
205 server.addHandler('/packages', fileHandler); | 200 server.addHandler('/packages', fileHandler); |
206 _serverList.add(httpServer); | 201 _serverList.add(httpServer); |
207 return server; | 202 return server; |
208 }); | 203 }); |
209 } | 204 } |
210 | 205 |
211 void _handleFileOrDirectoryRequest(HttpRequest request, | 206 void _handleFileOrDirectoryRequest(HttpRequest request, int allowedPort) { |
212 int allowedPort) { | |
213 // Enable browsers to cache file/directory responses. | 207 // Enable browsers to cache file/directory responses. |
214 var response = request.response; | 208 var response = request.response; |
215 response.headers.set("Cache-Control", | 209 response.headers |
216 "max-age=$_CACHE_EXPIRATION_IN_SECONDS"); | 210 .set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS"); |
217 var path = _getFilePathFromRequestPath(request.uri.path); | 211 var path = _getFilePathFromRequestPath(request.uri.path); |
218 if (path != null) { | 212 if (path != null) { |
219 var file = new File(path.toNativePath()); | 213 var file = new File(path.toNativePath()); |
220 file.exists().then((exists) { | 214 file.exists().then((exists) { |
221 if (exists) { | 215 if (exists) { |
222 _sendFileContent(request, response, allowedPort, path, file); | 216 _sendFileContent(request, response, allowedPort, path, file); |
223 } else { | 217 } else { |
224 var directory = new Directory(path.toNativePath()); | 218 var directory = new Directory(path.toNativePath()); |
225 directory.exists().then((exists) { | 219 directory.exists().then((exists) { |
226 if (exists) { | 220 if (exists) { |
227 _listDirectory(directory).then((entries) { | 221 _listDirectory(directory).then((entries) { |
228 _sendDirectoryListing(entries, request, response); | 222 _sendDirectoryListing(entries, request, response); |
229 }); | 223 }); |
230 } else { | 224 } else { |
231 _sendNotFound(request); | 225 _sendNotFound(request); |
232 } | 226 } |
233 }); | 227 }); |
234 } | 228 } |
235 }); | 229 }); |
236 } else { | 230 } else { |
237 if (request.uri.path == '/') { | 231 if (request.uri.path == '/') { |
238 var entries = [new _Entry('root_dart', 'root_dart/'), | 232 var entries = [ |
239 new _Entry('root_build', 'root_build/'), | 233 new _Entry('root_dart', 'root_dart/'), |
240 new _Entry('echo', 'echo')]; | 234 new _Entry('root_build', 'root_build/'), |
| 235 new _Entry('echo', 'echo') |
| 236 ]; |
241 _sendDirectoryListing(entries, request, response); | 237 _sendDirectoryListing(entries, request, response); |
242 } else { | 238 } else { |
243 _sendNotFound(request); | 239 _sendNotFound(request); |
244 } | 240 } |
245 } | 241 } |
246 } | 242 } |
247 | 243 |
248 void _handleEchoRequest(HttpRequest request) { | 244 void _handleEchoRequest(HttpRequest request) { |
249 request.response.headers.set("Access-Control-Allow-Origin", "*"); | 245 request.response.headers.set("Access-Control-Allow-Origin", "*"); |
250 request.pipe(request.response).catchError((e) { | 246 request.pipe(request.response).catchError((e) { |
(...skipping 27 matching lines...) Expand all Loading... |
278 | 274 |
279 Path _getFilePathFromRequestPath(String urlRequestPath) { | 275 Path _getFilePathFromRequestPath(String urlRequestPath) { |
280 // Go to the top of the file to see an explanation of the URL path scheme. | 276 // Go to the top of the file to see an explanation of the URL path scheme. |
281 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); | 277 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); |
282 var pathSegments = requestPath.segments(); | 278 var pathSegments = requestPath.segments(); |
283 if (pathSegments.length > 0) { | 279 if (pathSegments.length > 0) { |
284 var basePath; | 280 var basePath; |
285 var relativePath; | 281 var relativePath; |
286 if (pathSegments[0] == PREFIX_BUILDDIR) { | 282 if (pathSegments[0] == PREFIX_BUILDDIR) { |
287 basePath = _buildDirectory; | 283 basePath = _buildDirectory; |
288 relativePath = new Path( | 284 relativePath = new Path(pathSegments.skip(1).join('/')); |
289 pathSegments.skip(1).join('/')); | |
290 } else if (pathSegments[0] == PREFIX_DARTDIR) { | 285 } else if (pathSegments[0] == PREFIX_DARTDIR) { |
291 basePath = _dartDirectory; | 286 basePath = _dartDirectory; |
292 relativePath = new Path( | 287 relativePath = new Path(pathSegments.skip(1).join('/')); |
293 pathSegments.skip(1).join('/')); | |
294 } | 288 } |
295 var packagesIndex = pathSegments.indexOf('packages'); | 289 var packagesIndex = pathSegments.indexOf('packages'); |
296 if (packagesIndex != -1) { | 290 if (packagesIndex != -1) { |
297 var start = packagesIndex + 1; | 291 var start = packagesIndex + 1; |
298 basePath = _packageRoot; | 292 basePath = _packageRoot; |
299 relativePath = new Path(pathSegments.skip(start).join('/')); | 293 relativePath = new Path(pathSegments.skip(start).join('/')); |
300 } | 294 } |
301 if (basePath != null && relativePath != null) { | 295 if (basePath != null && relativePath != null) { |
302 return basePath.join(relativePath); | 296 return basePath.join(relativePath); |
303 } | 297 } |
304 } | 298 } |
305 return null; | 299 return null; |
306 } | 300 } |
307 | 301 |
308 Future<List<_Entry>> _listDirectory(Directory directory) { | 302 Future<List<_Entry>> _listDirectory(Directory directory) { |
309 var completer = new Completer(); | 303 var completer = new Completer(); |
310 var entries = []; | 304 var entries = []; |
311 | 305 |
312 directory.list().listen( | 306 directory.list().listen((FileSystemEntity fse) { |
313 (FileSystemEntity fse) { | 307 var filename = new Path(fse.path).filename; |
314 var filename = new Path(fse.path).filename; | 308 if (fse is File) { |
315 if (fse is File) { | 309 entries.add(new _Entry(filename, filename)); |
316 entries.add(new _Entry(filename, filename)); | 310 } else if (fse is Directory) { |
317 } else if (fse is Directory) { | 311 entries.add(new _Entry(filename, '$filename/')); |
318 entries.add(new _Entry(filename, '$filename/')); | 312 } |
319 } | 313 }, onDone: () { |
320 }, | 314 completer.complete(entries); |
321 onDone: () { | 315 }); |
322 completer.complete(entries); | |
323 }); | |
324 return completer.future; | 316 return completer.future; |
325 } | 317 } |
326 | 318 |
327 void _sendDirectoryListing(List<_Entry> entries, | 319 void _sendDirectoryListing( |
328 HttpRequest request, | 320 List<_Entry> entries, HttpRequest request, HttpResponse response) { |
329 HttpResponse response) { | |
330 response.headers.set('Content-Type', 'text/html'); | 321 response.headers.set('Content-Type', 'text/html'); |
331 var header = '''<!DOCTYPE html> | 322 var header = '''<!DOCTYPE html> |
332 <html> | 323 <html> |
333 <head> | 324 <head> |
334 <title>${request.uri.path}</title> | 325 <title>${request.uri.path}</title> |
335 </head> | 326 </head> |
336 <body> | 327 <body> |
337 <code> | 328 <code> |
338 <div>${request.uri.path}</div> | 329 <div>${request.uri.path}</div> |
339 <hr/> | 330 <hr/> |
340 <ul>'''; | 331 <ul>'''; |
341 var footer = ''' | 332 var footer = ''' |
342 </ul> | 333 </ul> |
343 </code> | 334 </code> |
344 </body> | 335 </body> |
345 </html>'''; | 336 </html>'''; |
346 | 337 |
347 | |
348 entries.sort(); | 338 entries.sort(); |
349 response.write(header); | 339 response.write(header); |
350 for (var entry in entries) { | 340 for (var entry in entries) { |
351 response.write( | 341 response.write( |
352 '<li><a href="${new Path(request.uri.path).append(entry.name)}">' | 342 '<li><a href="${new Path(request.uri.path).append(entry.name)}">' |
353 '${entry.displayName}</a></li>'); | 343 '${entry.displayName}</a></li>'); |
354 } | 344 } |
355 response.write(footer); | 345 response.write(footer); |
356 response.close(); | 346 response.close(); |
357 response.done.catchError((e) { | 347 response.done.catchError((e) { |
358 DebugLogger.warning( | 348 DebugLogger.warning( |
359 'HttpServer: error while closing the response stream', e); | 349 'HttpServer: error while closing the response stream', e); |
360 }); | 350 }); |
361 } | 351 } |
362 | 352 |
363 void _sendFileContent(HttpRequest request, | 353 void _sendFileContent(HttpRequest request, HttpResponse response, |
364 HttpResponse response, | 354 int allowedPort, Path path, File file) { |
365 int allowedPort, | |
366 Path path, | |
367 File file) { | |
368 if (allowedPort != -1) { | 355 if (allowedPort != -1) { |
369 var headerOrigin = request.headers.value('Origin'); | 356 var headerOrigin = request.headers.value('Origin'); |
370 var allowedOrigin; | 357 var allowedOrigin; |
371 if (headerOrigin != null) { | 358 if (headerOrigin != null) { |
372 var origin = Uri.parse(headerOrigin); | 359 var origin = Uri.parse(headerOrigin); |
373 // Allow loading from http://*:$allowedPort in browsers. | 360 // Allow loading from http://*:$allowedPort in browsers. |
374 allowedOrigin = | 361 allowedOrigin = '${origin.scheme}://${origin.host}:${allowedPort}'; |
375 '${origin.scheme}://${origin.host}:${allowedPort}'; | |
376 } else { | 362 } else { |
377 // IE10 appears to be bugged and is not sending the Origin header | 363 // IE10 appears to be bugged and is not sending the Origin header |
378 // when making CORS requests to the same domain but different port. | 364 // when making CORS requests to the same domain but different port. |
379 allowedOrigin = '*'; | 365 allowedOrigin = '*'; |
380 } | 366 } |
381 | 367 |
382 | |
383 response.headers.set("Access-Control-Allow-Origin", allowedOrigin); | 368 response.headers.set("Access-Control-Allow-Origin", allowedOrigin); |
384 response.headers.set('Access-Control-Allow-Credentials', 'true'); | 369 response.headers.set('Access-Control-Allow-Credentials', 'true'); |
385 } else { | 370 } else { |
386 // No allowedPort specified. Allow from anywhere (but cross-origin | 371 // No allowedPort specified. Allow from anywhere (but cross-origin |
387 // requests *with credentials* will fail because you can't use "*"). | 372 // requests *with credentials* will fail because you can't use "*"). |
388 response.headers.set("Access-Control-Allow-Origin", "*"); | 373 response.headers.set("Access-Control-Allow-Origin", "*"); |
389 } | 374 } |
390 if (useContentSecurityPolicy) { | 375 if (useContentSecurityPolicy) { |
391 // Chrome respects the standardized Content-Security-Policy header, | 376 // Chrome respects the standardized Content-Security-Policy header, |
392 // whereas Firefox and IE10 use X-Content-Security-Policy. Safari | 377 // whereas Firefox and IE10 use X-Content-Security-Policy. Safari |
393 // still uses the WebKit- prefixed version. | 378 // still uses the WebKit- prefixed version. |
394 var content_header_value = "script-src 'self'; object-src 'self'"; | 379 var content_header_value = "script-src 'self'; object-src 'self'"; |
395 for (var header in ["Content-Security-Policy", | 380 for (var header in [ |
396 "X-Content-Security-Policy"]) { | 381 "Content-Security-Policy", |
| 382 "X-Content-Security-Policy" |
| 383 ]) { |
397 response.headers.set(header, content_header_value); | 384 response.headers.set(header, content_header_value); |
398 } | 385 } |
399 if (const ["safari"].contains(runtime)) { | 386 if (const ["safari"].contains(runtime)) { |
400 response.headers.set("X-WebKit-CSP", content_header_value); | 387 response.headers.set("X-WebKit-CSP", content_header_value); |
401 } | 388 } |
402 } | 389 } |
403 if (path.filename.endsWith('.html')) { | 390 if (path.filename.endsWith('.html')) { |
404 response.headers.set('Content-Type', 'text/html'); | 391 response.headers.set('Content-Type', 'text/html'); |
405 } else if (path.filename.endsWith('.js')) { | 392 } else if (path.filename.endsWith('.js')) { |
406 response.headers.set('Content-Type', 'application/javascript'); | 393 response.headers.set('Content-Type', 'application/javascript'); |
(...skipping 12 matching lines...) Expand all Loading... |
419 } | 406 } |
420 | 407 |
421 void _sendNotFound(HttpRequest request) { | 408 void _sendNotFound(HttpRequest request) { |
422 bool isHarmlessPath(String path) { | 409 bool isHarmlessPath(String path) { |
423 return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) { | 410 return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) { |
424 return path.contains(pattern); | 411 return path.contains(pattern); |
425 }); | 412 }); |
426 } | 413 } |
427 if (!isHarmlessPath(request.uri.path)) { | 414 if (!isHarmlessPath(request.uri.path)) { |
428 DebugLogger.warning('HttpServer: could not find file for request path: ' | 415 DebugLogger.warning('HttpServer: could not find file for request path: ' |
429 '"${request.uri.path}"'); | 416 '"${request.uri.path}"'); |
430 } | 417 } |
431 var response = request.response; | 418 var response = request.response; |
432 response.statusCode = HttpStatus.NOT_FOUND; | 419 response.statusCode = HttpStatus.NOT_FOUND; |
433 | 420 |
434 // Send a nice HTML page detailing the error message. Most browsers expect | 421 // Send a nice HTML page detailing the error message. Most browsers expect |
435 // this, for example, Chrome will simply display a blank page if you don't | 422 // this, for example, Chrome will simply display a blank page if you don't |
436 // provide any information. A nice side effect of this is to work around | 423 // provide any information. A nice side effect of this is to work around |
437 // Firefox bug 1016313 | 424 // Firefox bug 1016313 |
438 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1016313). | 425 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1016313). |
439 response.headers.set(HttpHeaders.CONTENT_TYPE, 'text/html'); | 426 response.headers.set(HttpHeaders.CONTENT_TYPE, 'text/html'); |
(...skipping 22 matching lines...) Expand all Loading... |
462 class _Entry implements Comparable { | 449 class _Entry implements Comparable { |
463 final String name; | 450 final String name; |
464 final String displayName; | 451 final String displayName; |
465 | 452 |
466 _Entry(this.name, this.displayName); | 453 _Entry(this.name, this.displayName); |
467 | 454 |
468 int compareTo(_Entry other) { | 455 int compareTo(_Entry other) { |
469 return name.compareTo(other.name); | 456 return name.compareTo(other.name); |
470 } | 457 } |
471 } | 458 } |
OLD | NEW |