| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 library dartiverse_search; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:convert'; | |
| 9 import 'dart:io'; | |
| 10 | |
| 11 import 'package:http_server/http_server.dart' as http_server; | |
| 12 import 'package:route/server.dart' show Router; | |
| 13 import 'package:logging/logging.dart' show Logger, Level, LogRecord; | |
| 14 import 'package:dartiverse_search/search_engine.dart'; | |
| 15 | |
| 16 | |
| 17 final Logger log = new Logger('DartiverseSearch'); | |
| 18 | |
| 19 | |
| 20 // List of search-engines used. | |
| 21 final List<SearchEngine> searchEngines = [ | |
| 22 new StackOverflowSearchEngine(), | |
| 23 new GithubSearchEngine() | |
| 24 ]; | |
| 25 | |
| 26 | |
| 27 /** | |
| 28 * Handle an established [WebSocket] connection. | |
| 29 * | |
| 30 * The WebSocket can send search requests as JSON-formatted messages, | |
| 31 * which will be responded to with a series of results and finally a done | |
| 32 * message. | |
| 33 */ | |
| 34 void handleWebSocket(WebSocket webSocket) { | |
| 35 log.info('New WebSocket connection'); | |
| 36 | |
| 37 // Listen for incoming data. We expect the data to be a JSON-encoded String. | |
| 38 webSocket | |
| 39 .map((string) => JSON.decode(string)) | |
| 40 .listen((json) { | |
| 41 // The JSON object should contains a 'request' entry. | |
| 42 var request = json['request']; | |
| 43 switch (request) { | |
| 44 case 'search': | |
| 45 // Initiate a new search. | |
| 46 var input = json['input']; | |
| 47 log.info("Searching for '$input'"); | |
| 48 int done = 0; | |
| 49 for (var engine in searchEngines) { | |
| 50 engine.search(input) | |
| 51 .listen((result) { | |
| 52 // The search-engine found a result. Send it to the client. | |
| 53 log.info("Got result from ${engine.name} for '$input': " | |
| 54 "${result.title}"); | |
| 55 var response = { | |
| 56 'response': 'searchResult', | |
| 57 'source': engine.name, | |
| 58 'title': result.title, | |
| 59 'link': result.link | |
| 60 }; | |
| 61 webSocket.add(JSON.encode(response)); | |
| 62 }, onError: (error) { | |
| 63 log.warning("Error while searching on ${engine.name}: $error"); | |
| 64 }, onDone: () { | |
| 65 done++; | |
| 66 if (done == searchEngines.length) { | |
| 67 // All search-engines are done. Send done message to the | |
| 68 // client. | |
| 69 webSocket.add(JSON.encode({ 'response': 'searchDone' })); | |
| 70 } | |
| 71 }); | |
| 72 } | |
| 73 break; | |
| 74 | |
| 75 default: | |
| 76 log.warning("Invalid request: '$request'"); | |
| 77 } | |
| 78 }, onError: (error) { | |
| 79 log.warning('Bad WebSocket request'); | |
| 80 }); | |
| 81 } | |
| 82 | |
| 83 void main() { | |
| 84 // Set up logger. | |
| 85 Logger.root.level = Level.ALL; | |
| 86 Logger.root.onRecord.listen((LogRecord rec) { | |
| 87 print('${rec.level.name}: ${rec.time}: ${rec.message}'); | |
| 88 }); | |
| 89 | |
| 90 var buildPath = Platform.script.resolve('../build/web').toFilePath(); | |
| 91 if (!new Directory(buildPath).existsSync()) { | |
| 92 log.severe("The 'build/' directory was not found. Please run 'pub build'."); | |
| 93 return; | |
| 94 } | |
| 95 | |
| 96 int port = 9223; // TODO use args from command line to set this | |
| 97 | |
| 98 HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((server) { | |
| 99 log.info("Search server is running on " | |
| 100 "'http://${server.address.address}:$port/'"); | |
| 101 var router = new Router(server); | |
| 102 | |
| 103 // The client will connect using a WebSocket. Upgrade requests to '/ws' and | |
| 104 // forward them to 'handleWebSocket'. | |
| 105 router.serve('/ws') | |
| 106 .transform(new WebSocketTransformer()) | |
| 107 .listen(handleWebSocket); | |
| 108 | |
| 109 // Set up default handler. This will serve files from our 'build' directory. | |
| 110 var virDir = new http_server.VirtualDirectory(buildPath); | |
| 111 // Disable jail root, as packages are local symlinks. | |
| 112 virDir.jailRoot = false; | |
| 113 virDir.allowDirectoryListing = true; | |
| 114 virDir.directoryHandler = (dir, request) { | |
| 115 // Redirect directory requests to index.html files. | |
| 116 var indexUri = new Uri.file(dir.path).resolve('index.html'); | |
| 117 virDir.serveFile(new File(indexUri.toFilePath()), request); | |
| 118 }; | |
| 119 | |
| 120 // Add an error page handler. | |
| 121 virDir.errorPageHandler = (HttpRequest request) { | |
| 122 log.warning("Resource not found: ${request.uri.path}"); | |
| 123 request.response.statusCode = HttpStatus.NOT_FOUND; | |
| 124 request.response.close(); | |
| 125 }; | |
| 126 | |
| 127 // Serve everything not routed elsewhere through the virtual directory. | |
| 128 virDir.serve(router.defaultStream); | |
| 129 | |
| 130 // Special handling of client.dart. Running 'pub build' generates | |
| 131 // JavaScript files but does not copy the Dart files, which are | |
| 132 // needed for the Dartium browser. | |
| 133 router.serve("/client.dart").listen((request) { | |
| 134 Uri clientScript = Platform.script.resolve("../web/client.dart"); | |
| 135 virDir.serveFile(new File(clientScript.toFilePath()), request); | |
| 136 }); | |
| 137 }); | |
| 138 } | |
| OLD | NEW |