Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: sdk/lib/_internal/pub_generated/lib/src/barback/web_socket_api.dart

Issue 896623005: Use native async/await support in pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, 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 pub.barback.web_socket_api;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'package:http_parser/http_parser.dart';
11 import 'package:path/path.dart' as path;
12 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
13
14 import '../exit_codes.dart' as exit_codes;
15 import '../io.dart';
16 import '../log.dart' as log;
17 import '../utils.dart';
18 import 'asset_environment.dart';
19
20 /// Implements the [WebSocket] API for communicating with a running pub serve
21 /// process, mainly for use by the Editor.
22 ///
23 /// This is a [JSON-RPC 2.0](http://www.jsonrpc.org/specification) server. Its
24 /// methods are described in the method-level documentation below.
25 class WebSocketApi {
26 final AssetEnvironment _environment;
27 final json_rpc.Server _server;
28
29 /// Whether the application should exit when this connection closes.
30 bool _exitOnClose = false;
31
32 WebSocketApi(CompatibleWebSocket socket, this._environment)
33 : _server = new json_rpc.Server(socket) {
34 _server.registerMethod("urlToAssetId", _urlToAssetId);
35 _server.registerMethod("pathToUrls", _pathToUrls);
36 _server.registerMethod("serveDirectory", _serveDirectory);
37 _server.registerMethod("unserveDirectory", _unserveDirectory);
38
39 /// Tells the server to exit as soon as this WebSocket connection is closed.
40 ///
41 /// This takes no arguments and returns no results. It can safely be called
42 /// as a JSON-RPC notification.
43 _server.registerMethod("exitOnClose", () {
44 _exitOnClose = true;
45 });
46 }
47
48 /// Listens on the socket.
49 ///
50 /// Returns a future that completes when the socket has closed. It will
51 /// complete with an error if the socket had an error, otherwise it will
52 /// complete to `null`.
53 Future listen() {
54 return _server.listen().then((_) {
55 if (!_exitOnClose) return;
56 log.message("WebSocket connection closed, terminating.");
57 flushThenExit(exit_codes.SUCCESS);
58 });
59 }
60
61 /// Given a URL to an asset that is served by pub, returns the ID of the
62 /// asset that would be accessed by that URL.
63 ///
64 /// The method name is "urlToAssetId" and it takes a "url" parameter for the
65 /// URL being mapped:
66 ///
67 /// "params": {
68 /// "url": "http://localhost:8080/index.html"
69 /// }
70 ///
71 /// If successful, it returns a map containing the asset ID's package and
72 /// path:
73 ///
74 /// "result": {
75 /// "package": "myapp",
76 /// "path": "web/index.html"
77 /// }
78 ///
79 /// The "path" key in the result is a URL path that's relative to the root
80 /// directory of the package identified by "package". The location of this
81 /// package may vary depending on which source it was installed from.
82 ///
83 /// An optional "line" key may be provided whose value must be an integer. If
84 /// given, the result will also include a "line" key that maps the line in
85 /// the served final file back to the corresponding source line in the asset
86 /// that was used to generate that file.
87 ///
88 /// Examples (where "myapp" is the root package and pub serve is being run
89 /// normally with "web" bound to port 8080 and "test" to 8081):
90 ///
91 /// http://localhost:8080/index.html -> myapp|web/index.html
92 /// http://localhost:8081/sub/main.dart -> myapp|test/sub/main.dart
93 ///
94 /// If the URL is not a domain being served by pub, this returns an error:
95 ///
96 /// http://localhost:1234/index.html -> NOT_SERVED error
97 ///
98 /// This does *not* currently support the implicit index.html behavior that
99 /// pub serve provides for user-friendliness:
100 ///
101 /// http://localhost:1234 -> NOT_SERVED error
102 ///
103 /// This does *not* currently check to ensure the asset actually exists. It
104 /// only maps what the corresponding asset *should* be for that URL.
105 Future<Map> _urlToAssetId(json_rpc.Parameters params) {
106 var url = params["url"].asUri;
107
108 // If a line number was given, map it to the output line.
109 var line = params["line"].asIntOr(null);
110
111 return _environment.getAssetIdForUrl(url).then((id) {
112 if (id == null) {
113 throw new json_rpc.RpcException(
114 _Error.NOT_SERVED,
115 '"${url.host}:${url.port}" is not being served by pub.');
116 }
117
118 // TODO(rnystrom): When this is hooked up to actually talk to barback to
119 // see if assets exist, consider supporting implicit index.html at that
120 // point.
121
122 var result = {
123 "package": id.package,
124 "path": id.path
125 };
126
127 // Map the line.
128 // TODO(rnystrom): Right now, source maps are not supported and it just
129 // passes through the original line. This lets the editor start using
130 // this API before we've fully implemented it. See #12339 and #16061.
131 if (line != null) result["line"] = line;
132
133 return result;
134 });
135 }
136
137 /// Given a path on the filesystem, returns the URLs served by pub that can be
138 /// used to access asset found at that path.
139 ///
140 /// The method name is "pathToUrls" and it takes a "path" key (a native OS
141 /// path which may be absolute or relative to the root directory of the
142 /// entrypoint package) for the path being mapped:
143 ///
144 /// "params": {
145 /// "path": "web/index.html"
146 /// }
147 ///
148 /// If successful, it returns a map containing the list of URLs that can be
149 /// used to access that asset.
150 ///
151 /// "result": {
152 /// "urls": ["http://localhost:8080/index.html"]
153 /// }
154 ///
155 /// The "path" key may refer to a path in another package, either by referring
156 /// to its location within the top-level "packages" directory or by referring
157 /// to its location on disk. Only the "lib" directory is visible in other
158 /// packages:
159 ///
160 /// "params": {
161 /// "path": "packages/http/http.dart"
162 /// }
163 ///
164 /// Assets in the "lib" directory will usually have one URL for each server:
165 ///
166 /// "result": {
167 /// "urls": [
168 /// "http://localhost:8080/packages/http/http.dart",
169 /// "http://localhost:8081/packages/http/http.dart"
170 /// ]
171 /// }
172 ///
173 /// An optional "line" key may be provided whose value must be an integer. If
174 /// given, the result will also include a "line" key that maps the line in
175 /// the source file to the corresponding output line in the resulting asset
176 /// served at the URL.
177 ///
178 /// Examples (where "myapp" is the root package and pub serve is being run
179 /// normally with "web" bound to port 8080 and "test" to 8081):
180 ///
181 /// web/index.html -> http://localhost:8080/index.html
182 /// test/sub/main.dart -> http://localhost:8081/sub/main.dart
183 ///
184 /// If the asset is not in a directory being served by pub, returns an error:
185 ///
186 /// example/index.html -> NOT_SERVED error
187 Future<Map> _pathToUrls(json_rpc.Parameters params) {
188 var assetPath = params["path"].asString;
189 var line = params["line"].asIntOr(null);
190
191 return _environment.getUrlsForAssetPath(assetPath).then((urls) {
192 if (urls.isEmpty) {
193 throw new json_rpc.RpcException(
194 _Error.NOT_SERVED,
195 'Asset path "$assetPath" is not currently being served.');
196 }
197
198 var result = {
199 "urls": urls.map((url) => url.toString()).toList()
200 };
201
202 // Map the line.
203 // TODO(rnystrom): Right now, source maps are not supported and it just
204 // passes through the original line. This lets the editor start using
205 // this API before we've fully implemented it. See #12339 and #16061.
206 if (line != null) result["line"] = line;
207
208 return result;
209 });
210 }
211
212 /// Given a relative directory path within the entrypoint package, binds a
213 /// new port to serve from that path and returns its URL.
214 ///
215 /// The method name is "serveDirectory" and it takes a "path" key (a native
216 /// OS path relative to the root of the entrypoint package) for the directory
217 /// being served:
218 ///
219 /// "params": {
220 /// "path": "example/awesome"
221 /// }
222 ///
223 /// If successful, it returns a map containing the URL that can be used to
224 /// access the directory.
225 ///
226 /// "result": {
227 /// "url": "http://localhost:8083"
228 /// }
229 ///
230 /// If the directory is already being served, returns the previous URL.
231 Future<Map> _serveDirectory(json_rpc.Parameters params) {
232 var rootDirectory = _validateRelativePath(params, "path");
233 return _environment.serveDirectory(rootDirectory).then((server) {
234 return {
235 "url": server.url.toString()
236 };
237 }).catchError((error) {
238 if (error is! OverlappingSourceDirectoryException) throw error;
239
240 var dir = pluralize(
241 "directory",
242 error.overlappingDirectories.length,
243 plural: "directories");
244 var overlapping =
245 toSentence(error.overlappingDirectories.map((dir) => '"$dir"'));
246 print("data: ${error.overlappingDirectories}");
247 throw new json_rpc.RpcException(
248 _Error.OVERLAPPING,
249 'Path "$rootDirectory" overlaps already served $dir $overlapping.',
250 data: {
251 "directories": error.overlappingDirectories
252 });
253 });
254 }
255
256 /// Given a relative directory path within the entrypoint package, unbinds
257 /// the server previously bound to that directory and returns its (now
258 /// unreachable) URL.
259 ///
260 /// The method name is "unserveDirectory" and it takes a "path" key (a
261 /// native OS path relative to the root of the entrypoint package) for the
262 /// directory being unserved:
263 ///
264 /// "params": {
265 /// "path": "example/awesome"
266 /// }
267 ///
268 /// If successful, it returns a map containing the URL that used to be used
269 /// to access the directory.
270 ///
271 /// "result": {
272 /// "url": "http://localhost:8083"
273 /// }
274 ///
275 /// If no server is bound to that directory, it returns a `NOT_SERVED` error.
276 Future<Map> _unserveDirectory(json_rpc.Parameters params) {
277 var rootDirectory = _validateRelativePath(params, "path");
278 return _environment.unserveDirectory(rootDirectory).then((url) {
279 if (url == null) {
280 throw new json_rpc.RpcException(
281 _Error.NOT_SERVED,
282 'Directory "$rootDirectory" is not bound to a server.');
283 }
284
285 return {
286 "url": url.toString()
287 };
288 });
289 }
290
291 /// Validates that [command] has a field named [key] whose value is a string
292 /// containing a relative path that doesn't reach out of the entrypoint
293 /// package's root directory.
294 ///
295 /// Returns the path if found, or throws a [_WebSocketException] if
296 /// validation failed.
297 String _validateRelativePath(json_rpc.Parameters params, String key) {
298 var pathString = params[key].asString;
299
300 if (!path.isRelative(pathString)) {
301 throw new json_rpc.RpcException.invalidParams(
302 '"$key" must be a relative path. Got "$pathString".');
303 }
304
305 if (!path.isWithin(".", pathString)) {
306 throw new json_rpc.RpcException.invalidParams(
307 '"$key" cannot reach out of its containing directory. ' 'Got "$pathStr ing".');
308 }
309
310 return pathString;
311 }
312 }
313
314 /// The pub-specific JSON RPC error codes.
315 class _Error {
316 /// The specified directory is not being served.
317 static const NOT_SERVED = 1;
318
319 /// The specified directory overlaps one or more ones already being served.
320 static const OVERLAPPING = 2;
321 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698