Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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:io'; | 7 import 'dart:io'; |
| 8 import 'dart:isolate'; | 8 import 'dart:isolate'; |
| 9 import 'test_suite.dart'; // For TestUtils. | 9 import 'test_suite.dart'; // For TestUtils. |
| 10 // TODO(efortuna): Rewrite to not use the args library and simply take an | 10 // TODO(efortuna): Rewrite to not use the args library and simply take an |
| 11 // expected number of arguments, so test.dart doesn't rely on the args library? | 11 // expected number of arguments, so test.dart doesn't rely on the args library? |
| 12 // See discussion on https://codereview.chromium.org/11931025/. | 12 // See discussion on https://codereview.chromium.org/11931025/. |
| 13 import 'vendored_pkg/args/args.dart'; | 13 import 'vendored_pkg/args/args.dart'; |
| 14 import 'utils.dart'; | |
| 15 | |
| 16 const PREFIX_BUILDDIR = 'root_build'; | |
|
ricow1
2013/02/12 07:14:04
how about putting these in the utils class, we use
Emily Fortuna
2013/02/12 18:04:09
I know you have an explanation for how these are u
kustermann
2013/02/15 08:00:30
I'm not sure what we should do. Here's my reasonin
kustermann
2013/02/15 08:00:30
Good idea. Done.
| |
| 17 const PREFIX_DARTDIR = 'root_dart'; | |
| 14 | 18 |
| 15 main() { | 19 main() { |
| 16 /** Convenience method for local testing. */ | 20 /** Convenience method for local testing. */ |
| 17 var parser = new ArgParser(); | 21 var parser = new ArgParser(); |
| 18 parser.addOption('port', abbr: 'p', | 22 parser.addOption('port', abbr: 'p', |
| 19 help: 'The main server port we wish to respond to requests.', | 23 help: 'The main server port we wish to respond to requests.', |
| 20 defaultsTo: '0'); | 24 defaultsTo: '0'); |
| 21 parser.addOption('crossOriginPort', abbr: 'c', | 25 parser.addOption('crossOriginPort', abbr: 'c', |
| 22 help: 'A different port that accepts request from the main server port.', | 26 help: 'A different port that accepts request from the main server port.', |
| 23 defaultsTo: '0'); | 27 defaultsTo: '0'); |
| 24 parser.addOption('mode', abbr: 'm', help: 'Testing mode.', | 28 parser.addOption('mode', abbr: 'm', help: 'Testing mode.', |
| 25 defaultsTo: 'release'); | 29 defaultsTo: 'release'); |
| 26 parser.addOption('arch', abbr: 'a', help: 'Testing architecture.', | 30 parser.addOption('arch', abbr: 'a', help: 'Testing architecture.', |
| 27 defaultsTo: 'ia32'); | 31 defaultsTo: 'ia32'); |
| 28 parser.addFlag('help', abbr: 'h', negatable: false, | 32 parser.addFlag('help', abbr: 'h', negatable: false, |
| 29 help: 'Print this usage information.'); | 33 help: 'Print this usage information.'); |
| 30 parser.addOption('package-root', help: 'The package root to use.'); | 34 parser.addOption('package-root', help: 'The package root to use.'); |
| 31 var args = parser.parse(new Options().arguments); | 35 var args = parser.parse(new Options().arguments); |
| 32 if (args['help']) { | 36 if (args['help']) { |
| 33 print(parser.getUsage()); | 37 print(parser.getUsage()); |
| 34 } else { | 38 } else { |
| 35 // Pretend we're running test.dart so that TestUtils doesn't get confused | 39 // Pretend we're running test.dart so that TestUtils doesn't get confused |
| 36 // about the "current directory." This is only used if we're trying to run | 40 // about the "current directory." This is only used if we're trying to run |
| 37 // this file independently for local testing. | 41 // this file independently for local testing. |
| 38 TestUtils.testScriptPath = new Path(new Options().script) | 42 TestUtils.testScriptPath = new Path(new Options().script) |
| 39 .directoryPath | 43 .directoryPath |
| 40 .join(new Path('../../test.dart')) | 44 .join(new Path('../../test.dart')) |
| 41 .canonicalize() | 45 .canonicalize() |
| 42 .toNativePath(); | 46 .toNativePath(); |
| 47 // Note: args['package-root'] is always the build directory. We have the | |
| 48 // implicit assumption that it contains the 'packages' subdirectory. | |
| 49 // TODO: We should probably rename 'package-root' to 'build-directory'. | |
| 43 TestingServerRunner._packageRootDir = new Path(args['package-root']); | 50 TestingServerRunner._packageRootDir = new Path(args['package-root']); |
| 51 TestingServerRunner._buildDirectory = new Path(args['package-root']); | |
| 44 TestingServerRunner.startHttpServer('127.0.0.1', | 52 TestingServerRunner.startHttpServer('127.0.0.1', |
| 45 port: int.parse(args['port'])); | 53 port: int.parse(args['port'])); |
| 46 print('Server listening on port ' | 54 print('Server listening on port ' |
| 47 '${TestingServerRunner.serverList[0].port}.'); | 55 '${TestingServerRunner.serverList[0].port}.'); |
| 48 TestingServerRunner.startHttpServer('127.0.0.1', | 56 TestingServerRunner.startHttpServer('127.0.0.1', |
| 49 allowedPort: TestingServerRunner.serverList[0].port, port: | 57 allowedPort: TestingServerRunner.serverList[0].port, port: |
| 50 int.parse(args['crossOriginPort'])); | 58 int.parse(args['crossOriginPort'])); |
| 51 print( | 59 print( |
| 52 'Server listening on port ${TestingServerRunner.serverList[1].port}.'); | 60 'Server listening on port ${TestingServerRunner.serverList[1].port}.'); |
| 53 } | 61 } |
| 54 } | 62 } |
| 55 /** | 63 /** |
| 56 * Runs a set of servers that are initialized specifically for the needs of our | 64 * Runs a set of servers that are initialized specifically for the needs of our |
| 57 * test framework, such as dealing with package-root. | 65 * test framework, such as dealing with package-root. |
| 58 */ | 66 */ |
| 59 class TestingServerRunner { | 67 class TestingServerRunner { |
| 60 static List serverList = []; | 68 static List serverList = []; |
| 61 static Path _packageRootDir = null; | 69 static Path _packageRootDir = null; |
| 70 static Path _buildDirectory = null; | |
| 62 | 71 |
| 63 // Added as a getter so that the function will be called again each time the | 72 // Added as a getter so that the function will be called again each time the |
| 64 // default request handler closure is executed. | 73 // default request handler closure is executed. |
| 65 static Path get packageRootDir => _packageRootDir; | 74 static Path get packageRootDir => _packageRootDir; |
| 75 static Path get buildDirectory => _buildDirectory; | |
| 66 | 76 |
| 67 static setPackageRootDir(Map configuration) { | 77 static setPackageRootDir(Map configuration) { |
| 68 _packageRootDir = TestUtils.currentWorkingDirectory.join( | 78 _packageRootDir = TestUtils.absolutePath( |
| 79 new Path(TestUtils.buildDir(configuration))); | |
| 80 } | |
| 81 | |
| 82 static setBuildDir(Map configuration) { | |
| 83 _buildDirectory = TestUtils.absolutePath( | |
| 69 new Path(TestUtils.buildDir(configuration))); | 84 new Path(TestUtils.buildDir(configuration))); |
| 70 } | 85 } |
| 71 | 86 |
| 72 static startHttpServer(String host, {int allowedPort:-1, int port: 0}) { | 87 static startHttpServer(String host, {int allowedPort:-1, int port: 0}) { |
| 73 var basePath = TestUtils.dartDir(); | |
| 74 var httpServer = new HttpServer(); | 88 var httpServer = new HttpServer(); |
| 75 var packagesDirName = 'packages'; | |
| 76 httpServer.onError = (e) { | 89 httpServer.onError = (e) { |
| 77 // TODO(ricow): Once we have a debug log we should write this out there. | 90 DebugLogger.error('HttpServer: an error occured: $e'); |
| 78 print('Test http server error: $e'); | |
| 79 }; | 91 }; |
| 80 httpServer.defaultRequestHandler = (request, resp) { | 92 httpServer.defaultRequestHandler = (request, resp) { |
| 81 var requestPath = new Path(request.path.substring(1)).canonicalize(); | 93 void respondWithNotFound() { |
| 82 var path = basePath.join(requestPath); | 94 // NOTE: Since some tests deliberately try to access non-existent files. |
| 83 var file = new File(path.toNativePath()); | 95 // We might want to remove this warning (otherwise it will show |
| 84 | 96 // up in the debug.log every time). |
| 85 if (requestPath.segments().contains(packagesDirName)) { | 97 DebugLogger.warning('HttpServer: could not find file for request path: ' |
| 86 // Essentially implement the packages path rewriting, so we don't have | 98 '"${request.path}"'); |
| 87 // to pass environment variables to the browsers. | 99 resp.statusCode = HttpStatus.NOT_FOUND; |
| 88 var requestPathStr = requestPath.toNativePath().substring( | 100 try { |
| 89 requestPath.toNativePath().indexOf(packagesDirName)); | 101 resp.outputStream.close(); |
| 90 path = packageRootDir.append(requestPathStr); | 102 } catch (e) { |
| 91 file = new File(path.toNativePath()); | 103 if (e is StreamException) { |
| 92 } | 104 DebugLogger.warning('HttpServer: error while closing the response ' |
| 93 file.exists().then((exists) { | 105 'stream: $e'); |
| 94 if (exists) { | |
| 95 if (allowedPort != -1) { | |
| 96 // Allow loading from localhost:$allowedPort in browsers. | |
| 97 resp.headers.set("Access-Control-Allow-Origin", | |
| 98 "http://127.0.0.1:$allowedPort"); | |
| 99 resp.headers.set('Access-Control-Allow-Credentials', 'true'); | |
| 100 } else { | 106 } else { |
| 101 // No allowedPort specified. Allow from anywhere (but cross-origin | 107 throw e; |
| 102 // requests *with credentials* will fail because you can't use "*"). | |
| 103 resp.headers.set("Access-Control-Allow-Origin", "*"); | |
| 104 } | |
| 105 if (path.toNativePath().endsWith('.html')) { | |
| 106 resp.headers.set('Content-Type', 'text/html'); | |
| 107 } else if (path.toNativePath().endsWith('.js')) { | |
| 108 resp.headers.set('Content-Type', 'application/javascript'); | |
| 109 } else if (path.toNativePath().endsWith('.dart')) { | |
| 110 resp.headers.set('Content-Type', 'application/dart'); | |
| 111 } | |
| 112 file.openInputStream().pipe(resp.outputStream); | |
| 113 } else { | |
| 114 resp.statusCode = HttpStatus.NOT_FOUND; | |
| 115 try { | |
| 116 resp.outputStream.close(); | |
| 117 } catch (e) { | |
| 118 if (e is StreamException) { | |
| 119 print('Test http_server error closing the response stream: $e'); | |
| 120 } else { | |
| 121 throw e; | |
| 122 } | |
| 123 } | 108 } |
| 124 } | 109 } |
| 125 }); | 110 } |
| 111 | |
| 112 void respondWithFileContent(Path path, File file) { | |
| 113 if (allowedPort != -1) { | |
| 114 // Allow loading from localhost:$allowedPort in browsers. | |
| 115 resp.headers.set("Access-Control-Allow-Origin", | |
| 116 "http://127.0.0.1:$allowedPort"); | |
| 117 resp.headers.set('Access-Control-Allow-Credentials', 'true'); | |
| 118 } else { | |
| 119 // No allowedPort specified. Allow from anywhere (but cross-origin | |
| 120 // requests *with credentials* will fail because you can't use "*"). | |
| 121 resp.headers.set("Access-Control-Allow-Origin", "*"); | |
| 122 } | |
| 123 if (path.filename.endsWith('.html')) { | |
| 124 resp.headers.set('Content-Type', 'text/html'); | |
| 125 } else if (path.filename.endsWith('.js')) { | |
| 126 resp.headers.set('Content-Type', 'application/javascript'); | |
| 127 } else if (path.filename.endsWith('.dart')) { | |
| 128 resp.headers.set('Content-Type', 'application/dart'); | |
| 129 } | |
| 130 file.openInputStream().pipe(resp.outputStream); | |
| 131 } | |
| 132 | |
| 133 Path getFilePathFromRequestPath(String urlRequestPath) { | |
|
ricow1
2013/02/12 07:14:04
could we just move this and the other functions ou
kustermann
2013/02/15 08:00:30
Done.
| |
| 134 // TODO(kustermann,ricow): We could change this to the following scheme: | |
| 135 // http://host:port/root_packages/X -> $BuildDir/packages/X | |
| 136 // Issue: 8368 | |
| 137 | |
| 138 // NOTE: files from the dart and from the build directory are served as | |
|
ricow1
2013/02/12 07:14:04
from the dart -> from the dart repository
kustermann
2013/02/15 08:00:30
Done.
| |
| 139 // follows: | |
| 140 // http://host:port/$PREFIX_DARTDIR/X -> $DartDir/X | |
| 141 // http://host:port/$PREFIX_BUILDDIR/X -> $BuildDir/X | |
| 142 | |
| 143 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); | |
| 144 var pathSegments = requestPath.segments(); | |
| 145 if (pathSegments.length > 1) { | |
|
ricow1
2013/02/12 07:14:04
using segments here seems like a lot of work, why
Emily Fortuna
2013/02/12 18:04:09
Also, consider using the relativeTo function from
kustermann
2013/02/15 08:00:30
a) segments was used in the old code as well ('req
kustermann
2013/02/15 08:00:30
a) I don't like using vendored_pkg: the less thing
| |
| 146 var basePath; | |
| 147 var relativePath; | |
| 148 if (pathSegments[0] == PREFIX_BUILDDIR) { | |
| 149 basePath = _buildDirectory; | |
| 150 relativePath = new Path( | |
| 151 pathSegments.getRange(1, pathSegments.length - 1).join('/')); | |
| 152 } else if (pathSegments[0] == PREFIX_DARTDIR) { | |
| 153 basePath = TestUtils.dartDir(); | |
| 154 relativePath = new Path( | |
| 155 pathSegments.getRange(1, pathSegments.length - 1).join('/')); | |
| 156 } | |
| 157 var packagesDirName = 'packages'; | |
| 158 var packagesIndex = pathSegments.indexOf(packagesDirName); | |
| 159 if (packagesIndex != -1 && packagesIndex < pathSegments.length - 1) { | |
| 160 var start = packagesIndex + 1; | |
| 161 var length = pathSegments.length - start; | |
| 162 basePath = _packageRootDir.append(packagesDirName); | |
| 163 relativePath = new Path( | |
| 164 pathSegments.getRange(start, length).join('/')); | |
| 165 } | |
| 166 if (basePath != null && relativePath != null) { | |
| 167 return basePath.join(relativePath); | |
| 168 } | |
| 169 } | |
| 170 return null; | |
| 171 } | |
| 172 | |
| 173 var path = getFilePathFromRequestPath(request.path); | |
| 174 if (path != null) { | |
| 175 var file = new File.fromPath(path); | |
| 176 file.exists().then((exists) { | |
| 177 if (exists) { | |
| 178 respondWithFileContent(path, file); | |
| 179 } else { | |
| 180 respondWithNotFound(); | |
| 181 } | |
| 182 }); | |
| 183 } else { | |
| 184 respondWithNotFound(); | |
| 185 } | |
| 126 }; | 186 }; |
| 127 | 187 |
| 128 // Echos back the contents of the request as the response data. | 188 // Echos back the contents of the request as the response data. |
| 129 httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) { | 189 httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) { |
| 130 resp.headers.set("Access-Control-Allow-Origin", "*"); | 190 resp.headers.set("Access-Control-Allow-Origin", "*"); |
| 131 | 191 |
| 132 request.inputStream.pipe(resp.outputStream); | 192 request.inputStream.pipe(resp.outputStream); |
| 133 }); | 193 }); |
| 134 | 194 |
| 135 httpServer.listen(host, port); | 195 httpServer.listen(host, port); |
| 136 serverList.add(httpServer); | 196 serverList.add(httpServer); |
| 137 } | 197 } |
| 138 | 198 |
| 139 static terminateHttpServers() { | 199 static terminateHttpServers() { |
| 140 for (var server in serverList) server.close(); | 200 for (var server in serverList) server.close(); |
| 141 } | 201 } |
| 142 } | 202 } |
| OLD | NEW |