| 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 |