Dart Documentationpub.httpPubHttpClient

PubHttpClient class

An HTTP client that transforms 40* errors and socket exceptions into more user-friendly error messages.

class PubHttpClient extends http.BaseClient {
 http.Client inner;
 Uri tokenEndpoint;

 PubHttpClient([http.Client inner])
   : this.inner = inner == null ? new http.Client() : inner;

 Future<http.StreamedResponse> send(http.BaseRequest request) {
   _logRequest(request);

   // TODO(nweiz): remove this when issue 4061 is fixed.
   var stackTrace;
   try {
     throw null;
   } catch (_, localStackTrace) {
     stackTrace = localStackTrace;
   }

   // TODO(nweiz): Ideally the timeout would extend to reading from the
   // response input stream, but until issue 3657 is fixed that's not feasible.
   return timeout(inner.send(request).then((streamedResponse) {
     _logResponse(streamedResponse);

     var status = streamedResponse.statusCode;
     // 401 responses should be handled by the OAuth2 client. It's very
     // unlikely that they'll be returned by non-OAuth2 requests. We also want
     // to pass along 400 responses from the token endpoint.
     var tokenRequest = urisEqual(
         streamedResponse.request.url, tokenEndpoint);
     if (status < 400 || status == 401 || (status == 400 && tokenRequest)) {
       return streamedResponse;
     }

     return http.Response.fromStream(streamedResponse).then((response) {
       throw new PubHttpException(response);
     });
   }).catchError((error) {
     if (error is SocketException &&
         error.osError != null) {
       if (error.osError.errorCode == 8 ||
           error.osError.errorCode == -2 ||
           error.osError.errorCode == -5 ||
           error.osError.errorCode == 11001 ||
           error.osError.errorCode == 11004) {
         throw 'Could not resolve URL "${request.url.origin}".';
       } else if (error.osError.errorCode == -12276) {
         throw 'Unable to validate SSL certificate for '
             '"${request.url.origin}".';
       }
     }
     throw error;
   }), HTTP_TIMEOUT, 'fetching URL "${request.url}"');
 }

 /// Logs the fact that [request] was sent, and information about it.
 void _logRequest(http.BaseRequest request) {
   var requestLog = new StringBuffer();
   requestLog.writeln("HTTP ${request.method} ${request.url}");
   request.headers.forEach((name, value) =>
       requestLog.writeln(_logField(name, value)));

   if (request.method == 'POST') {
     var contentTypeString = request.headers[HttpHeaders.CONTENT_TYPE];
     if (contentTypeString == null) contentTypeString = '';
     var contentType = ContentType.parse(contentTypeString);
     if (request is http.MultipartRequest) {
       requestLog.writeln();
       requestLog.writeln("Body fields:");
       request.fields.forEach((name, value) =>
           requestLog.writeln(_logField(name, value)));

       // TODO(nweiz): make MultipartRequest.files readable, and log them?
     } else if (request is http.Request) {
       if (contentType.value == 'application/x-www-form-urlencoded') {
         requestLog.writeln();
         requestLog.writeln("Body fields:");
         request.bodyFields.forEach((name, value) =>
             requestLog.writeln(_logField(name, value)));
       } else if (contentType.value == 'text/plain' ||
           contentType.value == 'application/json') {
         requestLog.write(request.body);
       }
     }
   }

   log.fine(requestLog.toString().trim());
 }

 /// Logs the fact that [response] was received, and information about it.
 void _logResponse(http.StreamedResponse response) {
   // TODO(nweiz): Fork the response stream and log the response body. Be
   // careful not to log OAuth2 private data, though.

   var responseLog = new StringBuffer();
   var request = response.request;
   responseLog.writeln("HTTP response ${response.statusCode} "
       "${response.reasonPhrase} for ${request.method} ${request.url}");
   response.headers.forEach((name, value) =>
       responseLog.writeln(_logField(name, value)));

   log.fine(responseLog.toString().trim());
 }

 /// Returns a log-formatted string for the HTTP field or header with the given
 /// [name] and [value].
 String _logField(String name, String value) {
   if (_CENSORED_FIELDS.contains(name.toLowerCase())) {
     return "$name: <censored>";
   } else {
     return "$name: $value";
   }
 }
}

Extends

BaseClient > PubHttpClient

Constructors

new PubHttpClient([Client inner]) #

Creates a new Client using the default implementation. This implementation uses an underlying dart:io HttpClient to make requests.

docs inherited from Client
PubHttpClient([http.Client inner])
 : this.inner = inner == null ? new http.Client() : inner;

Properties

Client inner #

http.Client inner

Uri tokenEndpoint #

Uri tokenEndpoint

Methods

void close() #

inherited from BaseClient

Closes the client and cleans up any resources associated with it. It's important to close each client when it's done being used; failing to do so can cause the Dart process to hang.

void close() {}

Future<Response> delete(url, {Map<String, String> headers}) #

inherited from BaseClient

Sends an HTTP DELETE request with the given headers to the given URL, which can be a Uri or a String.

For more fine-grained control over the request, use send instead.

Future<Response> delete(url, {Map<String, String> headers}) =>
 _sendUnstreamed("DELETE", url, headers);

Future<Response> get(url, {Map<String, String> headers}) #

inherited from BaseClient

Sends an HTTP GET request with the given headers to the given URL, which can be a Uri or a String.

For more fine-grained control over the request, use send instead.

Future<Response> get(url, {Map<String, String> headers}) =>
 _sendUnstreamed("GET", url, headers);
inherited from BaseClient

Sends an HTTP HEAD request with the given headers to the given URL, which can be a Uri or a String.

For more fine-grained control over the request, use send instead.

Future<Response> head(url, {Map<String, String> headers}) =>
 _sendUnstreamed("HEAD", url, headers);

Future<Response> post(url, {Map<String, String> headers, Map<String, String> fields}) #

inherited from BaseClient

Sends an HTTP POST request with the given headers and fields to the given URL, which can be a Uri or a String. If any fields are specified, the content-type is automatically set to "application/x-www-form-urlencoded".

For more fine-grained control over the request, use send instead.

Future<Response> post(url,
   {Map<String, String> headers,
    Map<String, String> fields}) =>
 _sendUnstreamed("POST", url, headers, fields);

Future<Response> put(url, {Map<String, String> headers, Map<String, String> fields}) #

inherited from BaseClient

Sends an HTTP PUT request with the given headers and fields to the given URL, which can be a Uri or a String. If any fields are specified, the content-type is automatically set to "application/x-www-form-urlencoded".

For more fine-grained control over the request, use send instead.

Future<Response> put(url,
   {Map<String, String> headers,
    Map<String, String> fields}) =>
 _sendUnstreamed("PUT", url, headers, fields);

Future<String> read(url, {Map<String, String> headers}) #

inherited from BaseClient

Sends an HTTP GET request with the given headers to the given URL, which can be a Uri or a String, and returns a Future that completes to the body of the response as a String.

The Future will emit an HttpException if the response doesn't have a success status code.

For more fine-grained control over the request and response, use send or get instead.

Future<String> read(url, {Map<String, String> headers}) {
 return get(url, headers: headers).then((response) {
   _checkResponseSuccess(url, response);
   return response.body;
 });
}

Future<Uint8List> readBytes(url, {Map<String, String> headers}) #

inherited from BaseClient

Sends an HTTP GET request with the given headers to the given URL, which can be a Uri or a String, and returns a Future that completes to the body of the response as a list of bytes.

The Future will emit an HttpException if the response doesn't have a success status code.

For more fine-grained control over the request and response, use send or get instead.

Future<Uint8List> readBytes(url, {Map<String, String> headers}) {
 return get(url, headers: headers).then((response) {
   _checkResponseSuccess(url, response);
   return response.bodyBytes;
 });
}

Future<StreamedResponse> send(BaseRequest request) #

Sends an HTTP request and asynchronously returns the response.

Implementers should call BaseRequest.finalize to get the body of the request as a ByteStream. They shouldn't make any assumptions about the state of the stream; it could have data written to it asynchronously at a later point, or it could already be closed when it's returned.

docs inherited from BaseClient
Future<http.StreamedResponse> send(http.BaseRequest request) {
 _logRequest(request);

 // TODO(nweiz): remove this when issue 4061 is fixed.
 var stackTrace;
 try {
   throw null;
 } catch (_, localStackTrace) {
   stackTrace = localStackTrace;
 }

 // TODO(nweiz): Ideally the timeout would extend to reading from the
 // response input stream, but until issue 3657 is fixed that's not feasible.
 return timeout(inner.send(request).then((streamedResponse) {
   _logResponse(streamedResponse);

   var status = streamedResponse.statusCode;
   // 401 responses should be handled by the OAuth2 client. It's very
   // unlikely that they'll be returned by non-OAuth2 requests. We also want
   // to pass along 400 responses from the token endpoint.
   var tokenRequest = urisEqual(
       streamedResponse.request.url, tokenEndpoint);
   if (status < 400 || status == 401 || (status == 400 && tokenRequest)) {
     return streamedResponse;
   }

   return http.Response.fromStream(streamedResponse).then((response) {
     throw new PubHttpException(response);
   });
 }).catchError((error) {
   if (error is SocketException &&
       error.osError != null) {
     if (error.osError.errorCode == 8 ||
         error.osError.errorCode == -2 ||
         error.osError.errorCode == -5 ||
         error.osError.errorCode == 11001 ||
         error.osError.errorCode == 11004) {
       throw 'Could not resolve URL "${request.url.origin}".';
     } else if (error.osError.errorCode == -12276) {
       throw 'Unable to validate SSL certificate for '
           '"${request.url.origin}".';
     }
   }
   throw error;
 }), HTTP_TIMEOUT, 'fetching URL "${request.url}"');
}