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 'dart:uri'; |
9 import 'test_suite.dart'; // For TestUtils. | 10 import 'test_suite.dart'; // For TestUtils. |
10 // TODO(efortuna): Rewrite to not use the args library and simply take an | 11 // 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? | 12 // expected number of arguments, so test.dart doesn't rely on the args library? |
12 // See discussion on https://codereview.chromium.org/11931025/. | 13 // See discussion on https://codereview.chromium.org/11931025/. |
13 import 'vendored_pkg/args/args.dart'; | 14 import 'vendored_pkg/args/args.dart'; |
14 | 15 |
15 main() { | 16 main() { |
16 /** Convenience method for local testing. */ | 17 /** Convenience method for local testing. */ |
17 var parser = new ArgParser(); | 18 var parser = new ArgParser(); |
18 parser.addOption('port', abbr: 'p', | 19 parser.addOption('port', abbr: 'p', |
19 help: 'The main server port we wish to respond to requests.', | 20 help: 'The main server port we wish to respond to requests.', |
20 defaultsTo: '0'); | 21 defaultsTo: '0'); |
21 parser.addOption('crossOriginPort', abbr: 'c', | 22 parser.addOption('crossOriginPort', abbr: 'c', |
22 help: 'A different port that accepts request from the main server port.', | 23 help: 'A different port that accepts request from the main server port.', |
23 defaultsTo: '0'); | 24 defaultsTo: '0'); |
24 parser.addOption('mode', abbr: 'm', help: 'Testing mode.', | 25 parser.addOption('mode', abbr: 'm', help: 'Testing mode.', |
25 defaultsTo: 'release'); | 26 defaultsTo: 'release'); |
26 parser.addOption('arch', abbr: 'a', help: 'Testing architecture.', | 27 parser.addOption('arch', abbr: 'a', help: 'Testing architecture.', |
27 defaultsTo: 'ia32'); | 28 defaultsTo: 'ia32'); |
28 parser.addFlag('help', abbr: 'h', negatable: false, | 29 parser.addFlag('help', abbr: 'h', negatable: false, |
29 help: 'Print this usage information.'); | 30 help: 'Print this usage information.'); |
30 parser.addOption('package-root', help: 'The package root to use.'); | 31 parser.addOption('package-root', help: 'The package root to use.'); |
| 32 parser.addOption('network', help: 'The network interface to use.', |
| 33 defaultsTo: '127.0.0.1'); |
31 var args = parser.parse(new Options().arguments); | 34 var args = parser.parse(new Options().arguments); |
32 if (args['help']) { | 35 if (args['help']) { |
33 print(parser.getUsage()); | 36 print(parser.getUsage()); |
34 } else { | 37 } else { |
35 // Pretend we're running test.dart so that TestUtils doesn't get confused | 38 // 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 | 39 // about the "current directory." This is only used if we're trying to run |
37 // this file independently for local testing. | 40 // this file independently for local testing. |
38 TestUtils.testScriptPath = new Path(new Options().script) | 41 TestUtils.testScriptPath = new Path(new Options().script) |
39 .directoryPath | 42 .directoryPath |
40 .join(new Path('../../test.dart')) | 43 .join(new Path('../../test.dart')) |
41 .canonicalize() | 44 .canonicalize() |
42 .toNativePath(); | 45 .toNativePath(); |
43 TestingServerRunner._packageRootDir = new Path(args['package-root']); | 46 TestingServerRunner._packageRootDir = new Path(args['package-root']); |
44 TestingServerRunner.startHttpServer('127.0.0.1', | 47 var network = args['network']; |
| 48 TestingServerRunner.startHttpServer(network, |
45 port: int.parse(args['port'])); | 49 port: int.parse(args['port'])); |
46 print('Server listening on port ' | 50 print('Server listening on port ' |
47 '${TestingServerRunner.serverList[0].port}.'); | 51 '${TestingServerRunner.serverList[0].port}.'); |
48 TestingServerRunner.startHttpServer('127.0.0.1', | 52 TestingServerRunner.startHttpServer(network, |
49 allowedPort: TestingServerRunner.serverList[0].port, port: | 53 allowedPort: TestingServerRunner.serverList[0].port, port: |
50 int.parse(args['crossOriginPort'])); | 54 int.parse(args['crossOriginPort'])); |
51 print( | 55 print( |
52 'Server listening on port ${TestingServerRunner.serverList[1].port}.'); | 56 'Server listening on port ${TestingServerRunner.serverList[1].port}.'); |
53 } | 57 } |
54 } | 58 } |
55 /** | 59 /** |
56 * Runs a set of servers that are initialized specifically for the needs of our | 60 * Runs a set of servers that are initialized specifically for the needs of our |
57 * test framework, such as dealing with package-root. | 61 * test framework, such as dealing with package-root. |
58 */ | 62 */ |
(...skipping 27 matching lines...) Expand all Loading... |
86 // Essentially implement the packages path rewriting, so we don't have | 90 // Essentially implement the packages path rewriting, so we don't have |
87 // to pass environment variables to the browsers. | 91 // to pass environment variables to the browsers. |
88 var requestPathStr = requestPath.toNativePath().substring( | 92 var requestPathStr = requestPath.toNativePath().substring( |
89 requestPath.toNativePath().indexOf(packagesDirName)); | 93 requestPath.toNativePath().indexOf(packagesDirName)); |
90 path = packageRootDir.append(requestPathStr); | 94 path = packageRootDir.append(requestPathStr); |
91 file = new File(path.toNativePath()); | 95 file = new File(path.toNativePath()); |
92 } | 96 } |
93 file.exists().then((exists) { | 97 file.exists().then((exists) { |
94 if (exists) { | 98 if (exists) { |
95 if (allowedPort != -1) { | 99 if (allowedPort != -1) { |
96 // Allow loading from localhost:$allowedPort in browsers. | 100 if (request.headers.value('Origin') != null) { |
97 resp.headers.set("Access-Control-Allow-Origin", | 101 var origin = new Uri(request.headers.value('Origin')); |
98 "http://127.0.0.1:$allowedPort"); | 102 // Allow loading from http://*:$allowedPort in browsers. |
99 resp.headers.set('Access-Control-Allow-Credentials', 'true'); | 103 var allowedOrigin = |
| 104 '${origin.scheme}://${origin.domain}:${allowedPort}'; |
| 105 resp.headers.set("Access-Control-Allow-Origin", allowedOrigin); |
| 106 resp.headers.set('Access-Control-Allow-Credentials', 'true'); |
| 107 } |
100 } else { | 108 } else { |
101 // No allowedPort specified. Allow from anywhere (but cross-origin | 109 // No allowedPort specified. Allow from anywhere (but cross-origin |
102 // requests *with credentials* will fail because you can't use "*"). | 110 // requests *with credentials* will fail because you can't use "*"). |
103 resp.headers.set("Access-Control-Allow-Origin", "*"); | 111 resp.headers.set("Access-Control-Allow-Origin", "*"); |
104 } | 112 } |
105 if (path.toNativePath().endsWith('.html')) { | 113 if (path.toNativePath().endsWith('.html')) { |
106 resp.headers.set('Content-Type', 'text/html'); | 114 resp.headers.set('Content-Type', 'text/html'); |
107 } else if (path.toNativePath().endsWith('.js')) { | 115 } else if (path.toNativePath().endsWith('.js')) { |
108 resp.headers.set('Content-Type', 'application/javascript'); | 116 resp.headers.set('Content-Type', 'application/javascript'); |
109 } else if (path.toNativePath().endsWith('.dart')) { | 117 } else if (path.toNativePath().endsWith('.dart')) { |
110 resp.headers.set('Content-Type', 'application/dart'); | 118 resp.headers.set('Content-Type', 'application/dart'); |
111 } | 119 } |
112 file.openInputStream().pipe(resp.outputStream); | 120 file.openInputStream().pipe(resp.outputStream); |
113 } else { | 121 } else { |
114 resp.statusCode = HttpStatus.NOT_FOUND; | 122 var directory = new Directory.fromPath(path); |
115 try { | 123 directory.exists().then((exists) { |
116 resp.outputStream.close(); | 124 if (!exists) { |
117 } catch (e) { | 125 sendNotFound(resp); |
118 if (e is StreamException) { | |
119 print('Test http_server error closing the response stream: $e'); | |
120 } else { | 126 } else { |
121 throw e; | 127 sendDirectoryListing(directory, request, resp); |
122 } | 128 } |
123 } | 129 }); |
124 } | 130 } |
125 }); | 131 }); |
126 }; | 132 }; |
127 | 133 |
128 // Echos back the contents of the request as the response data. | 134 // Echos back the contents of the request as the response data. |
129 httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) { | 135 httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) { |
130 resp.headers.set("Access-Control-Allow-Origin", "*"); | 136 resp.headers.set("Access-Control-Allow-Origin", "*"); |
131 | 137 |
132 request.inputStream.pipe(resp.outputStream); | 138 request.inputStream.pipe(resp.outputStream); |
133 }); | 139 }); |
134 | 140 |
135 httpServer.listen(host, port); | 141 httpServer.listen(host, port); |
136 serverList.add(httpServer); | 142 serverList.add(httpServer); |
137 } | 143 } |
138 | 144 |
| 145 static void sendNotFound(HttpResponse response) { |
| 146 response.statusCode = HttpStatus.NOT_FOUND; |
| 147 try { |
| 148 response.outputStream.close(); |
| 149 } catch (e) { |
| 150 if (e is StreamException) { |
| 151 print('Test http_server error closing the response stream: $e'); |
| 152 } else { |
| 153 throw e; |
| 154 } |
| 155 } |
| 156 } |
| 157 |
| 158 /** |
| 159 * Sends a simple listing of all the files and sub-directories within |
| 160 * directory. |
| 161 * |
| 162 * This is intended to make it easier to browse tests when manually running |
| 163 * tests against this test server. |
| 164 */ |
| 165 static void sendDirectoryListing(Directory directory, HttpRequest request, |
| 166 HttpResponse response) { |
| 167 response.headers.set('Content-Type', 'text/html'); |
| 168 var header = '''<!DOCTYPE html> |
| 169 <html> |
| 170 <head> |
| 171 <title>${request.path}</title> |
| 172 </head> |
| 173 <body> |
| 174 <code> |
| 175 <div>${request.path}</div> |
| 176 <hr/> |
| 177 <ul>'''; |
| 178 var footer = ''' |
| 179 </ul> |
| 180 </code> |
| 181 </body> |
| 182 </html>'''; |
| 183 |
| 184 var entries = []; |
| 185 |
| 186 directory.list() |
| 187 ..onFile = (filepath) { |
| 188 var filename = new Path(filepath).filename; |
| 189 entries.add(new _Entry(filename, filename)); |
| 190 } |
| 191 ..onDir = (dirpath) { |
| 192 var filename = new Path(dirpath).filename; |
| 193 entries.add(new _Entry(filename, '$filename/')); |
| 194 } |
| 195 ..onDone = (_) { |
| 196 var requestPath = new Path.raw(request.path); |
| 197 entries.sort(); |
| 198 |
| 199 response.outputStream.writeString(header); |
| 200 for (var entry in entries) { |
| 201 response.outputStream.writeString( |
| 202 '<li><a href="${requestPath.append(entry.name)}">' |
| 203 '${entry.displayName}</a></li>'); |
| 204 } |
| 205 response.outputStream.writeString(footer); |
| 206 response.outputStream.close(); |
| 207 }; |
| 208 } |
| 209 |
139 static terminateHttpServers() { | 210 static terminateHttpServers() { |
140 for (var server in serverList) server.close(); | 211 for (var server in serverList) server.close(); |
141 } | 212 } |
142 } | 213 } |
| 214 |
| 215 // Helper class for displaying directory listings. |
| 216 class _Entry { |
| 217 final String name; |
| 218 final String displayName; |
| 219 |
| 220 _Entry(this.name, this.displayName); |
| 221 |
| 222 int compareTo(_Entry other) { |
| 223 return name.compareTo(other.name); |
| 224 } |
| 225 } |
OLD | NEW |