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

Side by Side Diff: utils/pub/http.dart

Issue 14297021: Move pub into sdk/lib/_internal. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Disallow package: imports of pub. Created 7 years, 8 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
« no previous file with comments | « utils/pub/hosted_source.dart ('k') | utils/pub/io.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Helpers for dealing with HTTP.
6 library pub.http;
7
8 import 'dart:async';
9 import 'dart:io';
10 import 'dart:json' as json;
11
12 import 'package:http/http.dart' as http;
13
14 import 'io.dart';
15 import 'log.dart' as log;
16 import 'oauth2.dart' as oauth2;
17 import 'utils.dart';
18
19 // TODO(nweiz): make this configurable
20 /// The amount of time in milliseconds to allow HTTP requests before assuming
21 /// they've failed.
22 final HTTP_TIMEOUT = 30 * 1000;
23
24 /// Headers and field names that should be censored in the log output.
25 final _CENSORED_FIELDS = const ['refresh_token', 'authorization'];
26
27 /// An HTTP client that transforms 40* errors and socket exceptions into more
28 /// user-friendly error messages.
29 class PubHttpClient extends http.BaseClient {
30 http.Client inner;
31
32 PubHttpClient([http.Client inner])
33 : this.inner = inner == null ? new http.Client() : inner;
34
35 Future<http.StreamedResponse> send(http.BaseRequest request) {
36 _logRequest(request);
37
38 // TODO(nweiz): remove this when issue 4061 is fixed.
39 var stackTrace;
40 try {
41 throw null;
42 } catch (_, localStackTrace) {
43 stackTrace = localStackTrace;
44 }
45
46 // TODO(nweiz): Ideally the timeout would extend to reading from the
47 // response input stream, but until issue 3657 is fixed that's not feasible.
48 return timeout(inner.send(request).then((streamedResponse) {
49 _logResponse(streamedResponse);
50
51 var status = streamedResponse.statusCode;
52 // 401 responses should be handled by the OAuth2 client. It's very
53 // unlikely that they'll be returned by non-OAuth2 requests. We also want
54 // to pass along 400 responses from the token endpoint.
55 var tokenRequest = urisEqual(
56 streamedResponse.request.url, oauth2.tokenEndpoint);
57 if (status < 400 || status == 401 || (status == 400 && tokenRequest)) {
58 return streamedResponse;
59 }
60
61 return http.Response.fromStream(streamedResponse).then((response) {
62 throw new PubHttpException(response);
63 });
64 }).catchError((error) {
65 if (error is SocketIOException &&
66 error.osError != null) {
67 if (error.osError.errorCode == 8 ||
68 error.osError.errorCode == -2 ||
69 error.osError.errorCode == -5 ||
70 error.osError.errorCode == 11001 ||
71 error.osError.errorCode == 11004) {
72 fail('Could not resolve URL "${request.url.origin}".');
73 } else if (error.osError.errorCode == -12276) {
74 fail('Unable to validate SSL certificate for '
75 '"${request.url.origin}".');
76 }
77 }
78 throw error;
79 }), HTTP_TIMEOUT, 'fetching URL "${request.url}"');
80 }
81
82 /// Logs the fact that [request] was sent, and information about it.
83 void _logRequest(http.BaseRequest request) {
84 var requestLog = new StringBuffer();
85 requestLog.writeln("HTTP ${request.method} ${request.url}");
86 request.headers.forEach((name, value) =>
87 requestLog.writeln(_logField(name, value)));
88
89 if (request.method == 'POST') {
90 var contentTypeString = request.headers[HttpHeaders.CONTENT_TYPE];
91 if (contentTypeString == null) contentTypeString = '';
92 var contentType = new ContentType.fromString(contentTypeString);
93 if (request is http.MultipartRequest) {
94 requestLog.writeln();
95 requestLog.writeln("Body fields:");
96 request.fields.forEach((name, value) =>
97 requestLog.writeln(_logField(name, value)));
98
99 // TODO(nweiz): make MultipartRequest.files readable, and log them?
100 } else if (request is http.Request) {
101 if (contentType.value == 'application/x-www-form-urlencoded') {
102 requestLog.writeln();
103 requestLog.writeln("Body fields:");
104 request.bodyFields.forEach((name, value) =>
105 requestLog.writeln(_logField(name, value)));
106 } else if (contentType.value == 'text/plain' ||
107 contentType.value == 'application/json') {
108 requestLog.write(request.body);
109 }
110 }
111 }
112
113 log.fine(requestLog.toString().trim());
114 }
115
116 /// Logs the fact that [response] was received, and information about it.
117 void _logResponse(http.StreamedResponse response) {
118 // TODO(nweiz): Fork the response stream and log the response body. Be
119 // careful not to log OAuth2 private data, though.
120
121 var responseLog = new StringBuffer();
122 var request = response.request;
123 responseLog.writeln("HTTP response ${response.statusCode} "
124 "${response.reasonPhrase} for ${request.method} ${request.url}");
125 response.headers.forEach((name, value) =>
126 responseLog.writeln(_logField(name, value)));
127
128 log.fine(responseLog.toString().trim());
129 }
130
131 /// Returns a log-formatted string for the HTTP field or header with the given
132 /// [name] and [value].
133 String _logField(String name, String value) {
134 if (_CENSORED_FIELDS.contains(name.toLowerCase())) {
135 return "$name: <censored>";
136 } else {
137 return "$name: $value";
138 }
139 }
140 }
141
142 /// The HTTP client to use for all HTTP requests.
143 final httpClient = new PubHttpClient();
144
145 /// Handles a successful JSON-formatted response from pub.dartlang.org.
146 ///
147 /// These responses are expected to be of the form `{"success": {"message":
148 /// "some message"}}`. If the format is correct, the message will be printed;
149 /// otherwise an error will be raised.
150 void handleJsonSuccess(http.Response response) {
151 var parsed = parseJsonResponse(response);
152 if (parsed['success'] is! Map ||
153 !parsed['success'].containsKey('message') ||
154 parsed['success']['message'] is! String) {
155 invalidServerResponse(response);
156 }
157 log.message(parsed['success']['message']);
158 }
159
160 /// Handles an unsuccessful JSON-formatted response from pub.dartlang.org.
161 ///
162 /// These responses are expected to be of the form `{"error": {"message": "some
163 /// message"}}`. If the format is correct, the message will be raised as an
164 /// error; otherwise an [invalidServerResponse] error will be raised.
165 void handleJsonError(http.Response response) {
166 var errorMap = parseJsonResponse(response);
167 if (errorMap['error'] is! Map ||
168 !errorMap['error'].containsKey('message') ||
169 errorMap['error']['message'] is! String) {
170 invalidServerResponse(response);
171 }
172 throw errorMap['error']['message'];
173 }
174
175 /// Parses a response body, assuming it's JSON-formatted. Throws a user-friendly
176 /// error if the response body is invalid JSON, or if it's not a map.
177 Map parseJsonResponse(http.Response response) {
178 var value;
179 try {
180 value = json.parse(response.body);
181 } catch (e) {
182 // TODO(nweiz): narrow this catch clause once issue 6775 is fixed.
183 invalidServerResponse(response);
184 }
185 if (value is! Map) invalidServerResponse(response);
186 return value;
187 }
188
189 /// Throws an error describing an invalid response from the server.
190 void invalidServerResponse(http.Response response) =>
191 fail('Invalid server response:\n${response.body}');
192
193 /// Exception thrown when an HTTP operation fails.
194 class PubHttpException implements Exception {
195 final http.Response response;
196
197 const PubHttpException(this.response);
198
199 String toString() => 'HTTP error ${response.statusCode}: '
200 '${response.reasonPhrase}';
201 }
OLDNEW
« no previous file with comments | « utils/pub/hosted_source.dart ('k') | utils/pub/io.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698