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:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
| 10 import 'dart:uri'; | 10 import 'dart:uri'; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 94 int get crossOriginPort => _serverList[1].port; | 94 int get crossOriginPort => _serverList[1].port; |
| 95 | 95 |
| 96 /** | 96 /** |
| 97 * [startServers] will start two Http servers. | 97 * [startServers] will start two Http servers. |
| 98 * The first server listens on [port] and sets | 98 * The first server listens on [port] and sets |
| 99 * "Access-Control-Allow-Origin: *" | 99 * "Access-Control-Allow-Origin: *" |
| 100 * The second server listens on [crossOriginPort] and sets | 100 * The second server listens on [crossOriginPort] and sets |
| 101 * "Access-Control-Allow-Origin: client:port1 | 101 * "Access-Control-Allow-Origin: client:port1 |
| 102 * "Access-Control-Allow-Credentials: true" | 102 * "Access-Control-Allow-Credentials: true" |
| 103 */ | 103 */ |
| 104 void startServers(String host, {int port: 0, int crossOriginPort: 0}) { | 104 Future startServers(String host, {int port: 0, int crossOriginPort: 0}) { |
| 105 _startHttpServer(host, port: port); | 105 return _startHttpServer(host, port: port).then((_) { |
| 106 _startHttpServer(host, | 106 _startHttpServer(host, |
| 107 port: crossOriginPort, | 107 port: crossOriginPort, |
| 108 allowedPort:_serverList[0].port); | 108 allowedPort:_serverList[0].port); |
| 109 }); | |
| 109 } | 110 } |
| 110 | 111 |
| 111 String httpServerCommandline() { | 112 String httpServerCommandline() { |
| 112 var dart = TestUtils.dartTestExecutable.toNativePath(); | 113 var dart = TestUtils.dartTestExecutable.toNativePath(); |
| 113 var dartDir = TestUtils.dartDir(); | 114 var dartDir = TestUtils.dartDir(); |
| 114 var script = dartDir.join(new Path("tools/testing/dart/http_server.dart")); | 115 var script = dartDir.join(new Path("tools/testing/dart/http_server.dart")); |
| 115 var buildDirectory = _buildDirectory.toNativePath(); | 116 var buildDirectory = _buildDirectory.toNativePath(); |
| 116 var csp = useContentSecurityPolicy ? '--csp ' : ''; | 117 var csp = useContentSecurityPolicy ? '--csp ' : ''; |
| 117 | 118 |
| 118 return '$dart $script -p $port -c $crossOriginPort $csp' | 119 return '$dart $script -p $port -c $crossOriginPort $csp' |
| 119 '--build-directory=$buildDirectory'; | 120 '--build-directory=$buildDirectory'; |
| 120 } | 121 } |
| 121 | 122 |
| 122 void stopServers() { | 123 void stopServers() { |
| 123 for (var server in _serverList) { | 124 for (var server in _serverList) { |
| 124 server.close(); | 125 server.close(); |
| 125 } | 126 } |
| 126 } | 127 } |
| 127 | 128 |
| 128 void _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { | 129 Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { |
| 129 var httpServer = new HttpServer(); | 130 return HttpServer.bind(host, port).then((HttpServer httpServer) { |
| 130 httpServer.onError = (e) { | 131 httpServer.listen( |
| 131 DebugLogger.error('HttpServer: an error occured: $e'); | 132 (HttpRequest request) { |
|
Bill Hesse
2013/03/13 12:56:41
Doesn't indentation look nicer (smaller) if we put
Søren Gjesse
2013/03/13 15:17:18
Done.
| |
| 132 }; | 133 if (request.uri.path == "/echo") { |
| 133 httpServer.defaultRequestHandler = (request, response) { | 134 _handleEchoRequest(request, request.response); |
| 134 _handleFileOrDirectoryRequest(request, response, allowedPort); | 135 } else { |
| 135 }; | 136 _handleFileOrDirectoryRequest( |
| 136 httpServer.addRequestHandler( | 137 request, request.response, allowedPort); |
| 137 (req) => req.path == "/echo", _handleEchoRequest); | 138 } |
| 138 | 139 }, |
| 139 httpServer.listen(host, port); | 140 onError: (e) { |
| 140 _serverList.add(httpServer); | 141 DebugLogger.error('HttpServer: an error occured: $e'); |
| 142 }); | |
| 143 _serverList.add(httpServer); | |
| 144 }); | |
| 141 } | 145 } |
| 142 | 146 |
| 143 void _handleFileOrDirectoryRequest(HttpRequest request, | 147 void _handleFileOrDirectoryRequest(HttpRequest request, |
| 144 HttpResponse response, | 148 HttpResponse response, |
| 145 int allowedPort) { | 149 int allowedPort) { |
| 146 var path = _getFilePathFromRequestPath(request.path); | 150 var path = _getFilePathFromRequestPath(request.uri.path); |
| 147 if (path != null) { | 151 if (path != null) { |
| 148 var file = new File.fromPath(path); | 152 var file = new File.fromPath(path); |
| 149 file.exists().then((exists) { | 153 file.exists().then((exists) { |
| 150 if (exists) { | 154 if (exists) { |
| 151 _sendFileContent(request, response, allowedPort, path, file); | 155 _sendFileContent(request, response, allowedPort, path, file); |
| 152 } else { | 156 } else { |
| 153 var directory = new Directory.fromPath(path); | 157 var directory = new Directory.fromPath(path); |
| 154 directory.exists().then((exists) { | 158 directory.exists().then((exists) { |
| 155 if (exists) { | 159 if (exists) { |
| 156 _listDirectory(directory).then((entries) { | 160 _listDirectory(directory).then((entries) { |
| 157 _sendDirectoryListing(entries, request, response); | 161 _sendDirectoryListing(entries, request, response); |
| 158 }); | 162 }); |
| 159 } else { | 163 } else { |
| 160 _sendNotFound(request, response); | 164 _sendNotFound(request, response); |
| 161 } | 165 } |
| 162 }); | 166 }); |
| 163 } | 167 } |
| 164 }); | 168 }); |
| 165 } else { | 169 } else { |
| 166 if (request.path == '/') { | 170 if (request.uri.path == '/') { |
| 167 var entries = [new _Entry('root_dart', 'root_dart/'), | 171 var entries = [new _Entry('root_dart', 'root_dart/'), |
| 168 new _Entry('root_build', 'root_build/'), | 172 new _Entry('root_build', 'root_build/'), |
| 169 new _Entry('echo', 'echo')]; | 173 new _Entry('echo', 'echo')]; |
| 170 _sendDirectoryListing(entries, request, response); | 174 _sendDirectoryListing(entries, request, response); |
| 171 } else { | 175 } else { |
| 172 _sendNotFound(request, response); | 176 _sendNotFound(request, response); |
| 173 } | 177 } |
| 174 } | 178 } |
| 175 } | 179 } |
| 176 | 180 |
| 177 void _handleEchoRequest(HttpRequest request, HttpResponse response) { | 181 void _handleEchoRequest(HttpRequest request, HttpResponse response) { |
| 178 response.headers.set("Access-Control-Allow-Origin", "*"); | 182 response.headers.set("Access-Control-Allow-Origin", "*"); |
| 179 request.inputStream.pipe(response.outputStream); | 183 request.pipe(response); |
| 180 } | 184 } |
| 181 | 185 |
| 182 Path _getFilePathFromRequestPath(String urlRequestPath) { | 186 Path _getFilePathFromRequestPath(String urlRequestPath) { |
| 183 // Go to the top of the file to see an explanation of the URL path scheme. | 187 // Go to the top of the file to see an explanation of the URL path scheme. |
| 184 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); | 188 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); |
| 185 var pathSegments = requestPath.segments(); | 189 var pathSegments = requestPath.segments(); |
| 186 if (pathSegments.length > 0) { | 190 if (pathSegments.length > 0) { |
| 187 var basePath; | 191 var basePath; |
| 188 var relativePath; | 192 var relativePath; |
| 189 if (pathSegments[0] == PREFIX_BUILDDIR) { | 193 if (pathSegments[0] == PREFIX_BUILDDIR) { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 208 return basePath.join(relativePath); | 212 return basePath.join(relativePath); |
| 209 } | 213 } |
| 210 } | 214 } |
| 211 return null; | 215 return null; |
| 212 } | 216 } |
| 213 | 217 |
| 214 Future<List<_Entry>> _listDirectory(Directory directory) { | 218 Future<List<_Entry>> _listDirectory(Directory directory) { |
| 215 var completer = new Completer(); | 219 var completer = new Completer(); |
| 216 var entries = []; | 220 var entries = []; |
| 217 | 221 |
| 218 directory.list() | 222 directory.list().listen( |
| 219 ..onFile = (filepath) { | 223 (FileSystemEntity fse) { |
| 220 var filename = new Path(filepath).filename; | 224 var filename = new Path(fse.path).filename; |
| 221 entries.add(new _Entry(filename, filename)); | 225 if (fse is File) { |
| 222 } | 226 entries.add(new _Entry(filename, filename)); |
| 223 ..onDir = (dirpath) { | 227 } else if (fse is Directory) { |
| 224 var filename = new Path(dirpath).filename; | 228 entries.add(new _Entry(filename, '$filename/')); |
| 225 entries.add(new _Entry(filename, '$filename/')); | 229 } |
| 226 } | 230 }, |
| 227 ..onDone = (_) { | 231 onDone: () { |
| 228 completer.complete(entries); | 232 completer.complete(entries); |
| 229 }; | 233 }); |
| 230 return completer.future; | 234 return completer.future; |
| 231 } | 235 } |
| 232 | 236 |
| 233 void _sendDirectoryListing(List<_Entry> entries, | 237 void _sendDirectoryListing(List<_Entry> entries, |
| 234 HttpRequest request, | 238 HttpRequest request, |
| 235 HttpResponse response) { | 239 HttpResponse response) { |
| 236 response.headers.set('Content-Type', 'text/html'); | 240 response.headers.set('Content-Type', 'text/html'); |
| 237 var header = '''<!DOCTYPE html> | 241 var header = '''<!DOCTYPE html> |
| 238 <html> | 242 <html> |
| 239 <head> | 243 <head> |
| 240 <title>${request.path}</title> | 244 <title>${request.uri.path}</title> |
| 241 </head> | 245 </head> |
| 242 <body> | 246 <body> |
| 243 <code> | 247 <code> |
| 244 <div>${request.path}</div> | 248 <div>${request.uri.path}</div> |
| 245 <hr/> | 249 <hr/> |
| 246 <ul>'''; | 250 <ul>'''; |
| 247 var footer = ''' | 251 var footer = ''' |
| 248 </ul> | 252 </ul> |
| 249 </code> | 253 </code> |
| 250 </body> | 254 </body> |
| 251 </html>'''; | 255 </html>'''; |
| 252 | 256 |
| 253 | 257 |
| 254 entries.sort(); | 258 entries.sort(); |
| 255 response.outputStream.writeString(header); | 259 response.write(header); |
| 256 for (var entry in entries) { | 260 for (var entry in entries) { |
| 257 response.outputStream.writeString( | 261 response.write( |
| 258 '<li><a href="${new Path(request.path).append(entry.name)}">' | 262 '<li><a href="${new Path(request.uri.path).append(entry.name)}">' |
| 259 '${entry.displayName}</a></li>'); | 263 '${entry.displayName}</a></li>'); |
| 260 } | 264 } |
| 261 response.outputStream.writeString(footer); | 265 response.write(footer); |
| 262 response.outputStream.close(); | 266 response.close(); |
| 263 } | 267 } |
| 264 | 268 |
| 265 void _sendFileContent(HttpRequest request, | 269 void _sendFileContent(HttpRequest request, |
| 266 HttpResponse response, | 270 HttpResponse response, |
| 267 int allowedPort, | 271 int allowedPort, |
| 268 Path path, | 272 Path path, |
| 269 File file) { | 273 File file) { |
| 270 if (allowedPort != -1) { | 274 if (allowedPort != -1) { |
| 271 var origin = new Uri(request.headers.value('Origin')); | 275 var origin = new Uri(request.headers.value('Origin')); |
| 272 // Allow loading from http://*:$allowedPort in browsers. | 276 // Allow loading from http://*:$allowedPort in browsers. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 289 response.headers.set(header, "script-src 'self'; object-src 'self'"); | 293 response.headers.set(header, "script-src 'self'; object-src 'self'"); |
| 290 } | 294 } |
| 291 } | 295 } |
| 292 if (path.filename.endsWith('.html')) { | 296 if (path.filename.endsWith('.html')) { |
| 293 response.headers.set('Content-Type', 'text/html'); | 297 response.headers.set('Content-Type', 'text/html'); |
| 294 } else if (path.filename.endsWith('.js')) { | 298 } else if (path.filename.endsWith('.js')) { |
| 295 response.headers.set('Content-Type', 'application/javascript'); | 299 response.headers.set('Content-Type', 'application/javascript'); |
| 296 } else if (path.filename.endsWith('.dart')) { | 300 } else if (path.filename.endsWith('.dart')) { |
| 297 response.headers.set('Content-Type', 'application/dart'); | 301 response.headers.set('Content-Type', 'application/dart'); |
| 298 } | 302 } |
| 299 file.openInputStream().pipe(response.outputStream); | 303 file.openRead().pipe(response); |
| 300 } | 304 } |
| 301 | 305 |
| 302 void _sendNotFound(HttpRequest request, HttpResponse response) { | 306 void _sendNotFound(HttpRequest request, HttpResponse response) { |
| 303 // NOTE: Since some tests deliberately try to access non-existent files. | 307 // NOTE: Since some tests deliberately try to access non-existent files. |
| 304 // We might want to remove this warning (otherwise it will show | 308 // We might want to remove this warning (otherwise it will show |
| 305 // up in the debug.log every time). | 309 // up in the debug.log every time). |
| 306 DebugLogger.warning('HttpServer: could not find file for request path: ' | 310 if (request.uri.path != "/favicon.ico") { |
| 307 '"${request.path}"'); | 311 DebugLogger.warning('HttpServer: could not find file for request path: ' |
| 312 '"${request.uri.path}"'); | |
| 313 } | |
| 308 response.statusCode = HttpStatus.NOT_FOUND; | 314 response.statusCode = HttpStatus.NOT_FOUND; |
| 309 try { | 315 response.close(); |
| 310 response.outputStream.close(); | 316 response.done.catchError((e) { |
| 311 } catch (e) { | 317 DebugLogger.warning('HttpServer: error while closing the response ' |
|
Bill Hesse
2013/03/13 12:56:41
Put the entire string on the next line?
Søren Gjesse
2013/03/13 15:17:18
Done.
| |
| 312 if (e is StreamException) { | 318 'stream: $e'); |
| 313 DebugLogger.warning('HttpServer: error while closing the response ' | 319 }); |
| 314 'stream: $e'); | |
| 315 } else { | |
| 316 throw e; | |
| 317 } | |
| 318 } | |
| 319 } | 320 } |
| 320 } | 321 } |
| 321 | 322 |
| 322 // Helper class for displaying directory listings. | 323 // Helper class for displaying directory listings. |
| 323 class _Entry { | 324 class _Entry { |
| 324 final String name; | 325 final String name; |
| 325 final String displayName; | 326 final String displayName; |
| 326 | 327 |
| 327 _Entry(this.name, this.displayName); | 328 _Entry(this.name, this.displayName); |
| 328 | 329 |
| 329 int compareTo(_Entry other) { | 330 int compareTo(_Entry other) { |
| 330 return name.compareTo(other.name); | 331 return name.compareTo(other.name); |
| 331 } | 332 } |
| 332 } | 333 } |
| OLD | NEW |