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