| 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((HttpRequest request) { |
| 131 DebugLogger.error('HttpServer: an error occured: $e'); | 132 if (request.uri.path == "/echo") { |
| 132 }; | 133 _handleEchoRequest(request, request.response); |
| 133 httpServer.defaultRequestHandler = (request, response) { | 134 } else { |
| 134 _handleFileOrDirectoryRequest(request, response, allowedPort); | 135 _handleFileOrDirectoryRequest( |
| 135 }; | 136 request, request.response, allowedPort); |
| 136 httpServer.addRequestHandler( | 137 } |
| 137 (req) => req.path == "/echo", _handleEchoRequest); | 138 }, |
| 138 | 139 onError: (e) { |
| 139 httpServer.listen(host, port); | 140 DebugLogger.error('HttpServer: an error occured: $e'); |
| 140 _serverList.add(httpServer); | 141 }); |
| 142 _serverList.add(httpServer); |
| 143 }); |
| 141 } | 144 } |
| 142 | 145 |
| 143 void _handleFileOrDirectoryRequest(HttpRequest request, | 146 void _handleFileOrDirectoryRequest(HttpRequest request, |
| 144 HttpResponse response, | 147 HttpResponse response, |
| 145 int allowedPort) { | 148 int allowedPort) { |
| 146 var path = _getFilePathFromRequestPath(request.path); | 149 var path = _getFilePathFromRequestPath(request.uri.path); |
| 147 if (path != null) { | 150 if (path != null) { |
| 148 var file = new File.fromPath(path); | 151 var file = new File.fromPath(path); |
| 149 file.exists().then((exists) { | 152 file.exists().then((exists) { |
| 150 if (exists) { | 153 if (exists) { |
| 151 _sendFileContent(request, response, allowedPort, path, file); | 154 _sendFileContent(request, response, allowedPort, path, file); |
| 152 } else { | 155 } else { |
| 153 var directory = new Directory.fromPath(path); | 156 var directory = new Directory.fromPath(path); |
| 154 directory.exists().then((exists) { | 157 directory.exists().then((exists) { |
| 155 if (exists) { | 158 if (exists) { |
| 156 _listDirectory(directory).then((entries) { | 159 _listDirectory(directory).then((entries) { |
| 157 _sendDirectoryListing(entries, request, response); | 160 _sendDirectoryListing(entries, request, response); |
| 158 }); | 161 }); |
| 159 } else { | 162 } else { |
| 160 _sendNotFound(request, response); | 163 _sendNotFound(request, response); |
| 161 } | 164 } |
| 162 }); | 165 }); |
| 163 } | 166 } |
| 164 }); | 167 }); |
| 165 } else { | 168 } else { |
| 166 if (request.path == '/') { | 169 if (request.uri.path == '/') { |
| 167 var entries = [new _Entry('root_dart', 'root_dart/'), | 170 var entries = [new _Entry('root_dart', 'root_dart/'), |
| 168 new _Entry('root_build', 'root_build/'), | 171 new _Entry('root_build', 'root_build/'), |
| 169 new _Entry('echo', 'echo')]; | 172 new _Entry('echo', 'echo')]; |
| 170 _sendDirectoryListing(entries, request, response); | 173 _sendDirectoryListing(entries, request, response); |
| 171 } else { | 174 } else { |
| 172 _sendNotFound(request, response); | 175 _sendNotFound(request, response); |
| 173 } | 176 } |
| 174 } | 177 } |
| 175 } | 178 } |
| 176 | 179 |
| 177 void _handleEchoRequest(HttpRequest request, HttpResponse response) { | 180 void _handleEchoRequest(HttpRequest request, HttpResponse response) { |
| 178 response.headers.set("Access-Control-Allow-Origin", "*"); | 181 response.headers.set("Access-Control-Allow-Origin", "*"); |
| 179 request.inputStream.pipe(response.outputStream); | 182 request.pipe(response); |
| 180 } | 183 } |
| 181 | 184 |
| 182 Path _getFilePathFromRequestPath(String urlRequestPath) { | 185 Path _getFilePathFromRequestPath(String urlRequestPath) { |
| 183 // Go to the top of the file to see an explanation of the URL path scheme. | 186 // 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(); | 187 var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); |
| 185 var pathSegments = requestPath.segments(); | 188 var pathSegments = requestPath.segments(); |
| 186 if (pathSegments.length > 0) { | 189 if (pathSegments.length > 0) { |
| 187 var basePath; | 190 var basePath; |
| 188 var relativePath; | 191 var relativePath; |
| 189 if (pathSegments[0] == PREFIX_BUILDDIR) { | 192 if (pathSegments[0] == PREFIX_BUILDDIR) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 208 return basePath.join(relativePath); | 211 return basePath.join(relativePath); |
| 209 } | 212 } |
| 210 } | 213 } |
| 211 return null; | 214 return null; |
| 212 } | 215 } |
| 213 | 216 |
| 214 Future<List<_Entry>> _listDirectory(Directory directory) { | 217 Future<List<_Entry>> _listDirectory(Directory directory) { |
| 215 var completer = new Completer(); | 218 var completer = new Completer(); |
| 216 var entries = []; | 219 var entries = []; |
| 217 | 220 |
| 218 directory.list() | 221 directory.list().listen( |
| 219 ..onFile = (filepath) { | 222 (FileSystemEntity fse) { |
| 220 var filename = new Path(filepath).filename; | 223 var filename = new Path(fse.path).filename; |
| 221 entries.add(new _Entry(filename, filename)); | 224 if (fse is File) { |
| 222 } | 225 entries.add(new _Entry(filename, filename)); |
| 223 ..onDir = (dirpath) { | 226 } else if (fse is Directory) { |
| 224 var filename = new Path(dirpath).filename; | 227 entries.add(new _Entry(filename, '$filename/')); |
| 225 entries.add(new _Entry(filename, '$filename/')); | 228 } |
| 226 } | 229 }, |
| 227 ..onDone = (_) { | 230 onDone: () { |
| 228 completer.complete(entries); | 231 completer.complete(entries); |
| 229 }; | 232 }); |
| 230 return completer.future; | 233 return completer.future; |
| 231 } | 234 } |
| 232 | 235 |
| 233 void _sendDirectoryListing(List<_Entry> entries, | 236 void _sendDirectoryListing(List<_Entry> entries, |
| 234 HttpRequest request, | 237 HttpRequest request, |
| 235 HttpResponse response) { | 238 HttpResponse response) { |
| 236 response.headers.set('Content-Type', 'text/html'); | 239 response.headers.set('Content-Type', 'text/html'); |
| 237 var header = '''<!DOCTYPE html> | 240 var header = '''<!DOCTYPE html> |
| 238 <html> | 241 <html> |
| 239 <head> | 242 <head> |
| 240 <title>${request.path}</title> | 243 <title>${request.uri.path}</title> |
| 241 </head> | 244 </head> |
| 242 <body> | 245 <body> |
| 243 <code> | 246 <code> |
| 244 <div>${request.path}</div> | 247 <div>${request.uri.path}</div> |
| 245 <hr/> | 248 <hr/> |
| 246 <ul>'''; | 249 <ul>'''; |
| 247 var footer = ''' | 250 var footer = ''' |
| 248 </ul> | 251 </ul> |
| 249 </code> | 252 </code> |
| 250 </body> | 253 </body> |
| 251 </html>'''; | 254 </html>'''; |
| 252 | 255 |
| 253 | 256 |
| 254 entries.sort(); | 257 entries.sort(); |
| 255 response.outputStream.writeString(header); | 258 response.write(header); |
| 256 for (var entry in entries) { | 259 for (var entry in entries) { |
| 257 response.outputStream.writeString( | 260 response.write( |
| 258 '<li><a href="${new Path(request.path).append(entry.name)}">' | 261 '<li><a href="${new Path(request.uri.path).append(entry.name)}">' |
| 259 '${entry.displayName}</a></li>'); | 262 '${entry.displayName}</a></li>'); |
| 260 } | 263 } |
| 261 response.outputStream.writeString(footer); | 264 response.write(footer); |
| 262 response.outputStream.close(); | 265 response.close(); |
| 263 } | 266 } |
| 264 | 267 |
| 265 void _sendFileContent(HttpRequest request, | 268 void _sendFileContent(HttpRequest request, |
| 266 HttpResponse response, | 269 HttpResponse response, |
| 267 int allowedPort, | 270 int allowedPort, |
| 268 Path path, | 271 Path path, |
| 269 File file) { | 272 File file) { |
| 270 if (allowedPort != -1) { | 273 if (allowedPort != -1) { |
| 271 var origin = new Uri(request.headers.value('Origin')); | 274 var origin = new Uri(request.headers.value('Origin')); |
| 272 // Allow loading from http://*:$allowedPort in browsers. | 275 // 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'"); | 292 response.headers.set(header, "script-src 'self'; object-src 'self'"); |
| 290 } | 293 } |
| 291 } | 294 } |
| 292 if (path.filename.endsWith('.html')) { | 295 if (path.filename.endsWith('.html')) { |
| 293 response.headers.set('Content-Type', 'text/html'); | 296 response.headers.set('Content-Type', 'text/html'); |
| 294 } else if (path.filename.endsWith('.js')) { | 297 } else if (path.filename.endsWith('.js')) { |
| 295 response.headers.set('Content-Type', 'application/javascript'); | 298 response.headers.set('Content-Type', 'application/javascript'); |
| 296 } else if (path.filename.endsWith('.dart')) { | 299 } else if (path.filename.endsWith('.dart')) { |
| 297 response.headers.set('Content-Type', 'application/dart'); | 300 response.headers.set('Content-Type', 'application/dart'); |
| 298 } | 301 } |
| 299 file.openInputStream().pipe(response.outputStream); | 302 file.openRead().pipe(response); |
| 300 } | 303 } |
| 301 | 304 |
| 302 void _sendNotFound(HttpRequest request, HttpResponse response) { | 305 void _sendNotFound(HttpRequest request, HttpResponse response) { |
| 303 // NOTE: Since some tests deliberately try to access non-existent files. | 306 // NOTE: Since some tests deliberately try to access non-existent files. |
| 304 // We might want to remove this warning (otherwise it will show | 307 // We might want to remove this warning (otherwise it will show |
| 305 // up in the debug.log every time). | 308 // up in the debug.log every time). |
| 306 DebugLogger.warning('HttpServer: could not find file for request path: ' | 309 if (request.uri.path != "/favicon.ico") { |
| 307 '"${request.path}"'); | 310 DebugLogger.warning('HttpServer: could not find file for request path: ' |
| 311 '"${request.uri.path}"'); |
| 312 } |
| 308 response.statusCode = HttpStatus.NOT_FOUND; | 313 response.statusCode = HttpStatus.NOT_FOUND; |
| 309 try { | 314 response.close(); |
| 310 response.outputStream.close(); | 315 response.done.catchError((e) { |
| 311 } catch (e) { | 316 DebugLogger.warning( |
| 312 if (e is StreamException) { | 317 'HttpServer: error while closing the response stream: $e'); |
| 313 DebugLogger.warning('HttpServer: error while closing the response ' | 318 }); |
| 314 'stream: $e'); | |
| 315 } else { | |
| 316 throw e; | |
| 317 } | |
| 318 } | |
| 319 } | 319 } |
| 320 } | 320 } |
| 321 | 321 |
| 322 // Helper class for displaying directory listings. | 322 // Helper class for displaying directory listings. |
| 323 class _Entry { | 323 class _Entry { |
| 324 final String name; | 324 final String name; |
| 325 final String displayName; | 325 final String displayName; |
| 326 | 326 |
| 327 _Entry(this.name, this.displayName); | 327 _Entry(this.name, this.displayName); |
| 328 | 328 |
| 329 int compareTo(_Entry other) { | 329 int compareTo(_Entry other) { |
| 330 return name.compareTo(other.name); | 330 return name.compareTo(other.name); |
| 331 } | 331 } |
| 332 } | 332 } |
| OLD | NEW |