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 |