OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library base_request; | 5 library base_request; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
10 import 'dart:uri'; | 10 import 'dart:uri'; |
11 | 11 |
| 12 import 'byte_stream.dart'; |
12 import 'client.dart'; | 13 import 'client.dart'; |
13 import 'streamed_response.dart'; | 14 import 'streamed_response.dart'; |
| 15 import 'utils.dart'; |
14 | 16 |
15 /// The base class for HTTP requests. | 17 /// The base class for HTTP requests. |
16 /// | 18 /// |
17 /// Subclasses of [BaseRequest] can be constructed manually and passed to | 19 /// Subclasses of [BaseRequest] can be constructed manually and passed to |
18 /// [BaseClient.send], which allows the user to provide fine-grained control | 20 /// [BaseClient.send], which allows the user to provide fine-grained control |
19 /// over the request properties. However, usually it's easier to use convenience | 21 /// over the request properties. However, usually it's easier to use convenience |
20 /// methods like [get] or [BaseClient.get]. | 22 /// methods like [get] or [BaseClient.get]. |
21 abstract class BaseRequest { | 23 abstract class BaseRequest { |
22 /// The HTTP method of the request. Most commonly "GET" or "POST", less | 24 /// The HTTP method of the request. Most commonly "GET" or "POST", less |
23 /// commonly "HEAD", "PUT", or "DELETE". Non-standard method names are also | 25 /// commonly "HEAD", "PUT", or "DELETE". Non-standard method names are also |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 | 78 |
77 /// Whether the request has been finalized. | 79 /// Whether the request has been finalized. |
78 bool get finalized => _finalized; | 80 bool get finalized => _finalized; |
79 bool _finalized = false; | 81 bool _finalized = false; |
80 | 82 |
81 /// Creates a new HTTP request. | 83 /// Creates a new HTTP request. |
82 BaseRequest(this.method, this.url) | 84 BaseRequest(this.method, this.url) |
83 : headers = <String>{}; | 85 : headers = <String>{}; |
84 | 86 |
85 /// Finalizes the HTTP request in preparation for it being sent. This freezes | 87 /// Finalizes the HTTP request in preparation for it being sent. This freezes |
86 /// all mutable fields and returns an [InputStream] that should emit the body | 88 /// all mutable fields and returns a single-subscription [ByteStream] that |
87 /// of the request. The stream may be closed to indicate a request with no | 89 /// emits the body of the request. |
88 /// body. | |
89 /// | 90 /// |
90 /// The base implementation of this returns null rather than an [InputStream]; | 91 /// The base implementation of this returns null rather than a [ByteStream]; |
91 /// subclasses are responsible for creating the return value. They should also | 92 /// subclasses are responsible for creating the return value, which should be |
| 93 /// single-subscription to ensure that no data is dropped. They should also |
92 /// freeze any additional mutable fields they add that don't make sense to | 94 /// freeze any additional mutable fields they add that don't make sense to |
93 /// change after the request headers are sent. | 95 /// change after the request headers are sent. |
94 InputStream finalize() { | 96 ByteStream finalize() { |
95 // TODO(nweiz): freeze headers | 97 // TODO(nweiz): freeze headers |
96 if (finalized) throw new StateError("Can't finalize a finalized Request."); | 98 if (finalized) throw new StateError("Can't finalize a finalized Request."); |
97 _finalized = true; | 99 _finalized = true; |
98 return null; | 100 return null; |
99 } | 101 } |
100 | 102 |
101 /// Sends this request. | 103 /// Sends this request. |
102 /// | 104 /// |
103 /// This automatically initializes a new [Client] and closes that client once | 105 /// This automatically initializes a new [Client] and closes that client once |
104 /// the request is complete. If you're planning on making multiple requests to | 106 /// the request is complete. If you're planning on making multiple requests to |
105 /// the same server, you should use a single [Client] for all of those | 107 /// the same server, you should use a single [Client] for all of those |
106 /// requests. | 108 /// requests. |
107 Future<StreamedResponse> send() { | 109 Future<StreamedResponse> send() { |
108 var client = new Client(); | 110 var client = new Client(); |
109 return client.send(this).then((response) { | 111 return client.send(this).then((response) { |
110 // TODO(nweiz): This makes me sick to my stomach, but it's currently the | 112 var stream = onDone(response.stream, client.close); |
111 // best way to listen for the response stream being closed. Kill it with | 113 return new StreamedResponse( |
112 // fire once issue 4202 is fixed. | 114 new ByteStream(stream), |
113 new Timer.repeating(100, (timer) { | 115 response.statusCode, |
114 if (response.stream.closed) { | 116 response.contentLength, |
115 client.close(); | 117 request: response.request, |
116 timer.cancel(); | 118 headers: response.headers, |
117 } | 119 isRedirect: response.isRedirect, |
118 }); | 120 persistentConnection: response.persistentConnection, |
119 | 121 reasonPhrase: response.reasonPhrase); |
120 return response; | 122 }).catchError((e) { |
121 }).catchError((_) { client.close(); }); | 123 client.close(); |
| 124 throw e; |
| 125 }); |
122 } | 126 } |
123 | 127 |
124 /// Throws an error if this request has been finalized. | 128 /// Throws an error if this request has been finalized. |
125 void _checkFinalized() { | 129 void _checkFinalized() { |
126 if (!finalized) return; | 130 if (!finalized) return; |
127 throw new StateError("Can't modify a finalized Request."); | 131 throw new StateError("Can't modify a finalized Request."); |
128 } | 132 } |
129 | 133 |
130 String toString() => "$method $url"; | 134 String toString() => "$method $url"; |
131 } | 135 } |
OLD | NEW |