| OLD | NEW |
| (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 library base_request; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:collection'; | |
| 9 | |
| 10 import 'byte_stream.dart'; | |
| 11 import 'client.dart'; | |
| 12 import 'streamed_response.dart'; | |
| 13 import 'utils.dart'; | |
| 14 | |
| 15 /// The base class for HTTP requests. | |
| 16 /// | |
| 17 /// Subclasses of [BaseRequest] can be constructed manually and passed to | |
| 18 /// [BaseClient.send], which allows the user to provide fine-grained control | |
| 19 /// over the request properties. However, usually it's easier to use convenience | |
| 20 /// methods like [get] or [BaseClient.get]. | |
| 21 abstract class BaseRequest { | |
| 22 /// The HTTP method of the request. Most commonly "GET" or "POST", less | |
| 23 /// commonly "HEAD", "PUT", or "DELETE". Non-standard method names are also | |
| 24 /// supported. | |
| 25 final String method; | |
| 26 | |
| 27 /// The URL to which the request will be sent. | |
| 28 final Uri url; | |
| 29 | |
| 30 /// The size of the request body, in bytes. | |
| 31 /// | |
| 32 /// This defaults to `null`, which indicates that the size of the request is | |
| 33 /// not known in advance. | |
| 34 int get contentLength => _contentLength; | |
| 35 int _contentLength; | |
| 36 | |
| 37 set contentLength(int value) { | |
| 38 if (value != null && value < 0) { | |
| 39 throw new ArgumentError("Invalid content length $value."); | |
| 40 } | |
| 41 _checkFinalized(); | |
| 42 _contentLength = value; | |
| 43 } | |
| 44 | |
| 45 /// Whether a persistent connection should be maintained with the server. | |
| 46 /// Defaults to true. | |
| 47 bool get persistentConnection => _persistentConnection; | |
| 48 bool _persistentConnection = true; | |
| 49 | |
| 50 set persistentConnection(bool value) { | |
| 51 _checkFinalized(); | |
| 52 _persistentConnection = value; | |
| 53 } | |
| 54 | |
| 55 /// Whether the client should follow redirects while resolving this request. | |
| 56 /// Defaults to true. | |
| 57 bool get followRedirects => _followRedirects; | |
| 58 bool _followRedirects = true; | |
| 59 | |
| 60 set followRedirects(bool value) { | |
| 61 _checkFinalized(); | |
| 62 _followRedirects = value; | |
| 63 } | |
| 64 | |
| 65 /// The maximum number of redirects to follow when [followRedirects] is true. | |
| 66 /// If this number is exceeded the [BaseResponse] future will signal a | |
| 67 /// [RedirectException]. Defaults to 5. | |
| 68 int get maxRedirects => _maxRedirects; | |
| 69 int _maxRedirects = 5; | |
| 70 | |
| 71 set maxRedirects(int value) { | |
| 72 _checkFinalized(); | |
| 73 _maxRedirects = value; | |
| 74 } | |
| 75 | |
| 76 // TODO(nweiz): automatically parse cookies from headers | |
| 77 | |
| 78 // TODO(nweiz): make this a HttpHeaders object | |
| 79 /// The headers for this request. | |
| 80 final Map<String, String> headers; | |
| 81 | |
| 82 /// Whether the request has been finalized. | |
| 83 bool get finalized => _finalized; | |
| 84 bool _finalized = false; | |
| 85 | |
| 86 /// Creates a new HTTP request. | |
| 87 BaseRequest(this.method, this.url) | |
| 88 : headers = new LinkedHashMap( | |
| 89 equals: (key1, key2) => key1.toLowerCase() == key2.toLowerCase(), | |
| 90 hashCode: (key) => key.toLowerCase().hashCode); | |
| 91 | |
| 92 /// Finalizes the HTTP request in preparation for it being sent. This freezes | |
| 93 /// all mutable fields and returns a single-subscription [ByteStream] that | |
| 94 /// emits the body of the request. | |
| 95 /// | |
| 96 /// The base implementation of this returns null rather than a [ByteStream]; | |
| 97 /// subclasses are responsible for creating the return value, which should be | |
| 98 /// single-subscription to ensure that no data is dropped. They should also | |
| 99 /// freeze any additional mutable fields they add that don't make sense to | |
| 100 /// change after the request headers are sent. | |
| 101 ByteStream finalize() { | |
| 102 // TODO(nweiz): freeze headers | |
| 103 if (finalized) throw new StateError("Can't finalize a finalized Request."); | |
| 104 _finalized = true; | |
| 105 return null; | |
| 106 } | |
| 107 | |
| 108 /// Sends this request. | |
| 109 /// | |
| 110 /// This automatically initializes a new [Client] and closes that client once | |
| 111 /// the request is complete. If you're planning on making multiple requests to | |
| 112 /// the same server, you should use a single [Client] for all of those | |
| 113 /// requests. | |
| 114 Future<StreamedResponse> send() { | |
| 115 var client = new Client(); | |
| 116 return client.send(this).then((response) { | |
| 117 var stream = onDone(response.stream, client.close); | |
| 118 return new StreamedResponse( | |
| 119 new ByteStream(stream), | |
| 120 response.statusCode, | |
| 121 contentLength: response.contentLength, | |
| 122 request: response.request, | |
| 123 headers: response.headers, | |
| 124 isRedirect: response.isRedirect, | |
| 125 persistentConnection: response.persistentConnection, | |
| 126 reasonPhrase: response.reasonPhrase); | |
| 127 }).catchError((e) { | |
| 128 client.close(); | |
| 129 throw e; | |
| 130 }); | |
| 131 } | |
| 132 | |
| 133 // Throws an error if this request has been finalized. | |
| 134 void _checkFinalized() { | |
| 135 if (!finalized) return; | |
| 136 throw new StateError("Can't modify a finalized Request."); | |
| 137 } | |
| 138 | |
| 139 String toString() => "$method $url"; | |
| 140 } | |
| OLD | NEW |