OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 request; | |
6 | |
7 import 'dart:convert'; | |
8 import 'dart:typed_data'; | |
9 | |
10 import 'package:http_parser/http_parser.dart'; | |
11 | |
12 import 'base_request.dart'; | |
13 import 'byte_stream.dart'; | |
14 import 'utils.dart'; | |
15 | |
16 /// An HTTP request where the entire request body is known in advance. | |
17 class Request extends BaseRequest { | |
18 /// The size of the request body, in bytes. This is calculated from | |
19 /// [bodyBytes]. | |
20 /// | |
21 /// The content length cannot be set for [Request], since it's automatically | |
22 /// calculated from [bodyBytes]. | |
23 int get contentLength => bodyBytes.length; | |
24 | |
25 set contentLength(int value) { | |
26 throw new UnsupportedError("Cannot set the contentLength property of " | |
27 "non-streaming Request objects."); | |
28 } | |
29 | |
30 /// The default encoding to use when converting between [bodyBytes] and | |
31 /// [body]. This is only used if [encoding] hasn't been manually set and if | |
32 /// the content-type header has no encoding information. | |
33 Encoding _defaultEncoding; | |
34 | |
35 /// The encoding used for the request. This encoding is used when converting | |
36 /// between [bodyBytes] and [body]. | |
37 /// | |
38 /// If the request has a `Content-Type` header and that header has a `charset` | |
39 /// parameter, that parameter's value is used as the encoding. Otherwise, if | |
40 /// [encoding] has been set manually, that encoding is used. If that hasn't | |
41 /// been set either, this defaults to [UTF8]. | |
42 /// | |
43 /// If the `charset` parameter's value is not a known [Encoding], reading this | |
44 /// will throw a [FormatException]. | |
45 /// | |
46 /// If the request has a `Content-Type` header, setting this will set the | |
47 /// charset parameter on that header. | |
48 Encoding get encoding { | |
49 if (_contentType == null || | |
50 !_contentType.parameters.containsKey('charset')) { | |
51 return _defaultEncoding; | |
52 } | |
53 return requiredEncodingForCharset(_contentType.parameters['charset']); | |
54 } | |
55 | |
56 set encoding(Encoding value) { | |
57 _checkFinalized(); | |
58 _defaultEncoding = value; | |
59 var contentType = _contentType; | |
60 if (contentType == null) return; | |
61 _contentType = contentType.change(parameters: {'charset': value.name}); | |
62 } | |
63 | |
64 // TODO(nweiz): make this return a read-only view | |
65 /// The bytes comprising the body of the request. This is converted to and | |
66 /// from [body] using [encoding]. | |
67 /// | |
68 /// This list should only be set, not be modified in place. | |
69 Uint8List get bodyBytes => _bodyBytes; | |
70 Uint8List _bodyBytes; | |
71 | |
72 set bodyBytes(List<int> value) { | |
73 _checkFinalized(); | |
74 _bodyBytes = toUint8List(value); | |
75 } | |
76 | |
77 /// The body of the request as a string. This is converted to and from | |
78 /// [bodyBytes] using [encoding]. | |
79 /// | |
80 /// When this is set, if the request does not yet have a `Content-Type` | |
81 /// header, one will be added with the type `text/plain`. Then the `charset` | |
82 /// parameter of the `Content-Type` header (whether new or pre-existing) will | |
83 /// be set to [encoding] if it wasn't already set. | |
84 String get body => encoding.decode(bodyBytes); | |
85 | |
86 set body(String value) { | |
87 bodyBytes = encoding.encode(value); | |
88 var contentType = _contentType; | |
89 if (contentType == null) { | |
90 _contentType = new MediaType("text", "plain", {'charset': encoding.name}); | |
91 } else if (!contentType.parameters.containsKey('charset')) { | |
92 _contentType = contentType.change(parameters: {'charset': encoding.name}); | |
93 } | |
94 } | |
95 | |
96 /// The form-encoded fields in the body of the request as a map from field | |
97 /// names to values. The form-encoded body is converted to and from | |
98 /// [bodyBytes] using [encoding] (in the same way as [body]). | |
99 /// | |
100 /// If the request doesn't have a `Content-Type` header of | |
101 /// `application/x-www-form-urlencoded`, reading this will throw a | |
102 /// [StateError]. | |
103 /// | |
104 /// If the request has a `Content-Type` header with a type other than | |
105 /// `application/x-www-form-urlencoded`, setting this will throw a | |
106 /// [StateError]. Otherwise, the content type will be set to | |
107 /// `application/x-www-form-urlencoded`. | |
108 /// | |
109 /// This map should only be set, not modified in place. | |
110 Map<String, String> get bodyFields { | |
111 var contentType = _contentType; | |
112 if (contentType == null || | |
113 contentType.mimeType != "application/x-www-form-urlencoded") { | |
114 throw new StateError('Cannot access the body fields of a Request without ' | |
115 'content-type "application/x-www-form-urlencoded".'); | |
116 } | |
117 | |
118 return queryToMap(body, encoding: encoding); | |
119 } | |
120 | |
121 set bodyFields(Map<String, String> fields) { | |
122 var contentType = _contentType; | |
123 if (contentType == null) { | |
124 _contentType = new MediaType("application", "x-www-form-urlencoded"); | |
125 } else if (contentType.mimeType != "application/x-www-form-urlencoded") { | |
126 throw new StateError('Cannot set the body fields of a Request with ' | |
127 'content-type "${contentType.mimeType}".'); | |
128 } | |
129 | |
130 this.body = mapToQuery(fields, encoding: encoding); | |
131 } | |
132 | |
133 /// Creates a new HTTP request. | |
134 Request(String method, Uri url) | |
135 : super(method, url), | |
136 _defaultEncoding = UTF8, | |
137 _bodyBytes = new Uint8List(0); | |
138 | |
139 /// Freezes all mutable fields and returns a single-subscription [ByteStream] | |
140 /// containing the request body. | |
141 ByteStream finalize() { | |
142 super.finalize(); | |
143 return new ByteStream.fromBytes(bodyBytes); | |
144 } | |
145 | |
146 /// The `Content-Type` header of the request (if it exists) as a | |
147 /// [MediaType]. | |
148 MediaType get _contentType { | |
149 var contentType = headers['content-type']; | |
150 if (contentType == null) return null; | |
151 return new MediaType.parse(contentType); | |
152 } | |
153 | |
154 set _contentType(MediaType value) { | |
155 headers['content-type'] = value.toString(); | |
156 } | |
157 | |
158 /// Throw an error if this request has been finalized. | |
159 void _checkFinalized() { | |
160 if (!finalized) return; | |
161 throw new StateError("Can't modify a finalized Request."); | |
162 } | |
163 } | |
OLD | NEW |