Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: pkg/dev_compiler/tool/input_sdk/lib/io/http_headers.dart

Issue 2698353003: unfork DDC's copy of most SDK libraries (Closed)
Patch Set: revert core_patch Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 part of dart.io;
6
7 class _HttpHeaders implements HttpHeaders {
8 final Map<String, List<String>> _headers;
9 final String protocolVersion;
10
11 bool _mutable = true; // Are the headers currently mutable?
12 List<String> _noFoldingHeaders;
13
14 int _contentLength = -1;
15 bool _persistentConnection = true;
16 bool _chunkedTransferEncoding = false;
17 String _host;
18 int _port;
19
20 final int _defaultPortForScheme;
21
22 _HttpHeaders(this.protocolVersion,
23 {int defaultPortForScheme: HttpClient.DEFAULT_HTTP_PORT,
24 _HttpHeaders initialHeaders})
25 : _headers = new HashMap<String, List<String>>(),
26 _defaultPortForScheme = defaultPortForScheme {
27 if (initialHeaders != null) {
28 initialHeaders._headers.forEach((name, value) => _headers[name] = value);
29 _contentLength = initialHeaders._contentLength;
30 _persistentConnection = initialHeaders._persistentConnection;
31 _chunkedTransferEncoding = initialHeaders._chunkedTransferEncoding;
32 _host = initialHeaders._host;
33 _port = initialHeaders._port;
34 }
35 if (protocolVersion == "1.0") {
36 _persistentConnection = false;
37 _chunkedTransferEncoding = false;
38 }
39 }
40
41 List<String> operator[](String name) => _headers[name.toLowerCase()];
42
43 String value(String name) {
44 name = name.toLowerCase();
45 List<String> values = _headers[name];
46 if (values == null) return null;
47 if (values.length > 1) {
48 throw new HttpException("More than one value for header $name");
49 }
50 return values[0];
51 }
52
53 void add(String name, value) {
54 _checkMutable();
55 _addAll(_validateField(name), value);
56 }
57
58 void _addAll(String name, value) {
59 assert(name == _validateField(name));
60 if (value is Iterable) {
61 for (var v in value) {
62 _add(name, _validateValue(v));
63 }
64 } else {
65 _add(name, _validateValue(value));
66 }
67 }
68
69 void set(String name, Object value) {
70 _checkMutable();
71 name = _validateField(name);
72 _headers.remove(name);
73 if (name == HttpHeaders.TRANSFER_ENCODING) {
74 _chunkedTransferEncoding = false;
75 }
76 _addAll(name, value);
77 }
78
79 void remove(String name, Object value) {
80 _checkMutable();
81 name = _validateField(name);
82 value = _validateValue(value);
83 List<String> values = _headers[name];
84 if (values != null) {
85 int index = values.indexOf(value);
86 if (index != -1) {
87 values.removeRange(index, index + 1);
88 }
89 if (values.length == 0) _headers.remove(name);
90 }
91 if (name == HttpHeaders.TRANSFER_ENCODING && value == "chunked") {
92 _chunkedTransferEncoding = false;
93 }
94 }
95
96 void removeAll(String name) {
97 _checkMutable();
98 name = _validateField(name);
99 _headers.remove(name);
100 }
101
102 void forEach(void f(String name, List<String> values)) {
103 _headers.forEach(f);
104 }
105
106 void noFolding(String name) {
107 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>();
108 _noFoldingHeaders.add(name);
109 }
110
111 bool get persistentConnection => _persistentConnection;
112
113 void set persistentConnection(bool persistentConnection) {
114 _checkMutable();
115 if (persistentConnection == _persistentConnection) return;
116 if (persistentConnection) {
117 if (protocolVersion == "1.1") {
118 remove(HttpHeaders.CONNECTION, "close");
119 } else {
120 if (_contentLength == -1) {
121 throw new HttpException(
122 "Trying to set 'Connection: Keep-Alive' on HTTP 1.0 headers with "
123 "no ContentLength");
124 }
125 add(HttpHeaders.CONNECTION, "keep-alive");
126 }
127 } else {
128 if (protocolVersion == "1.1") {
129 add(HttpHeaders.CONNECTION, "close");
130 } else {
131 remove(HttpHeaders.CONNECTION, "keep-alive");
132 }
133 }
134 _persistentConnection = persistentConnection;
135 }
136
137 int get contentLength => _contentLength;
138
139 void set contentLength(int contentLength) {
140 _checkMutable();
141 if (protocolVersion == "1.0" &&
142 persistentConnection &&
143 contentLength == -1) {
144 throw new HttpException(
145 "Trying to clear ContentLength on HTTP 1.0 headers with "
146 "'Connection: Keep-Alive' set");
147 }
148 if (_contentLength == contentLength) return;
149 _contentLength = contentLength;
150 if (_contentLength >= 0) {
151 if (chunkedTransferEncoding) chunkedTransferEncoding = false;
152 _set(HttpHeaders.CONTENT_LENGTH, contentLength.toString());
153 } else {
154 removeAll(HttpHeaders.CONTENT_LENGTH);
155 if (protocolVersion == "1.1") {
156 chunkedTransferEncoding = true;
157 }
158 }
159 }
160
161 bool get chunkedTransferEncoding => _chunkedTransferEncoding;
162
163 void set chunkedTransferEncoding(bool chunkedTransferEncoding) {
164 _checkMutable();
165 if (chunkedTransferEncoding && protocolVersion == "1.0") {
166 throw new HttpException(
167 "Trying to set 'Transfer-Encoding: Chunked' on HTTP 1.0 headers");
168 }
169 if (chunkedTransferEncoding == _chunkedTransferEncoding) return;
170 if (chunkedTransferEncoding) {
171 List<String> values = _headers[HttpHeaders.TRANSFER_ENCODING];
172 if ((values == null || values.last != "chunked")) {
173 // Headers does not specify chunked encoding - add it if set.
174 _addValue(HttpHeaders.TRANSFER_ENCODING, "chunked");
175 }
176 contentLength = -1;
177 } else {
178 // Headers does specify chunked encoding - remove it if not set.
179 remove(HttpHeaders.TRANSFER_ENCODING, "chunked");
180 }
181 _chunkedTransferEncoding = chunkedTransferEncoding;
182 }
183
184 String get host => _host;
185
186 void set host(String host) {
187 _checkMutable();
188 _host = host;
189 _updateHostHeader();
190 }
191
192 int get port => _port;
193
194 void set port(int port) {
195 _checkMutable();
196 _port = port;
197 _updateHostHeader();
198 }
199
200 DateTime get ifModifiedSince {
201 List<String> values = _headers[HttpHeaders.IF_MODIFIED_SINCE];
202 if (values != null) {
203 try {
204 return HttpDate.parse(values[0]);
205 } on Exception catch (e) {
206 return null;
207 }
208 }
209 return null;
210 }
211
212 void set ifModifiedSince(DateTime ifModifiedSince) {
213 _checkMutable();
214 // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT).
215 String formatted = HttpDate.format(ifModifiedSince.toUtc());
216 _set(HttpHeaders.IF_MODIFIED_SINCE, formatted);
217 }
218
219 DateTime get date {
220 List<String> values = _headers[HttpHeaders.DATE];
221 if (values != null) {
222 try {
223 return HttpDate.parse(values[0]);
224 } on Exception catch (e) {
225 return null;
226 }
227 }
228 return null;
229 }
230
231 void set date(DateTime date) {
232 _checkMutable();
233 // Format "DateTime" header with date in Greenwich Mean Time (GMT).
234 String formatted = HttpDate.format(date.toUtc());
235 _set("date", formatted);
236 }
237
238 DateTime get expires {
239 List<String> values = _headers[HttpHeaders.EXPIRES];
240 if (values != null) {
241 try {
242 return HttpDate.parse(values[0]);
243 } on Exception catch (e) {
244 return null;
245 }
246 }
247 return null;
248 }
249
250 void set expires(DateTime expires) {
251 _checkMutable();
252 // Format "Expires" header with date in Greenwich Mean Time (GMT).
253 String formatted = HttpDate.format(expires.toUtc());
254 _set(HttpHeaders.EXPIRES, formatted);
255 }
256
257 ContentType get contentType {
258 var values = _headers["content-type"];
259 if (values != null) {
260 return ContentType.parse(values[0]);
261 } else {
262 return null;
263 }
264 }
265
266 void set contentType(ContentType contentType) {
267 _checkMutable();
268 _set(HttpHeaders.CONTENT_TYPE, contentType.toString());
269 }
270
271 void clear() {
272 _checkMutable();
273 _headers.clear();
274 _contentLength = -1;
275 _persistentConnection = true;
276 _chunkedTransferEncoding = false;
277 _host = null;
278 _port = null;
279 }
280
281 // [name] must be a lower-case version of the name.
282 void _add(String name, value) {
283 assert(name == _validateField(name));
284 // Use the length as index on what method to call. This is notable
285 // faster than computing hash and looking up in a hash-map.
286 switch (name.length) {
287 case 4:
288 if (HttpHeaders.DATE == name) {
289 _addDate(name, value);
290 return;
291 }
292 if (HttpHeaders.HOST == name) {
293 _addHost(name, value);
294 return;
295 }
296 break;
297 case 7:
298 if (HttpHeaders.EXPIRES == name) {
299 _addExpires(name, value);
300 return;
301 }
302 break;
303 case 10:
304 if (HttpHeaders.CONNECTION == name) {
305 _addConnection(name, value);
306 return;
307 }
308 break;
309 case 12:
310 if (HttpHeaders.CONTENT_TYPE == name) {
311 _addContentType(name, value);
312 return;
313 }
314 break;
315 case 14:
316 if (HttpHeaders.CONTENT_LENGTH == name) {
317 _addContentLength(name, value);
318 return;
319 }
320 break;
321 case 17:
322 if (HttpHeaders.TRANSFER_ENCODING == name) {
323 _addTransferEncoding(name, value);
324 return;
325 }
326 if (HttpHeaders.IF_MODIFIED_SINCE == name) {
327 _addIfModifiedSince(name, value);
328 return;
329 }
330 }
331 _addValue(name, value);
332 }
333
334 void _addContentLength(String name, value) {
335 if (value is int) {
336 contentLength = value;
337 } else if (value is String) {
338 contentLength = int.parse(value);
339 } else {
340 throw new HttpException("Unexpected type for header named $name");
341 }
342 }
343
344 void _addTransferEncoding(String name, value) {
345 if (value == "chunked") {
346 chunkedTransferEncoding = true;
347 } else {
348 _addValue(HttpHeaders.TRANSFER_ENCODING, value);
349 }
350 }
351
352 void _addDate(String name, value) {
353 if (value is DateTime) {
354 date = value;
355 } else if (value is String) {
356 _set(HttpHeaders.DATE, value);
357 } else {
358 throw new HttpException("Unexpected type for header named $name");
359 }
360 }
361
362 void _addExpires(String name, value) {
363 if (value is DateTime) {
364 expires = value;
365 } else if (value is String) {
366 _set(HttpHeaders.EXPIRES, value);
367 } else {
368 throw new HttpException("Unexpected type for header named $name");
369 }
370 }
371
372 void _addIfModifiedSince(String name, value) {
373 if (value is DateTime) {
374 ifModifiedSince = value;
375 } else if (value is String) {
376 _set(HttpHeaders.IF_MODIFIED_SINCE, value);
377 } else {
378 throw new HttpException("Unexpected type for header named $name");
379 }
380 }
381
382 void _addHost(String name, value) {
383 if (value is String) {
384 int pos = value.indexOf(":");
385 if (pos == -1) {
386 _host = value;
387 _port = HttpClient.DEFAULT_HTTP_PORT;
388 } else {
389 if (pos > 0) {
390 _host = value.substring(0, pos);
391 } else {
392 _host = null;
393 }
394 if (pos + 1 == value.length) {
395 _port = HttpClient.DEFAULT_HTTP_PORT;
396 } else {
397 try {
398 _port = int.parse(value.substring(pos + 1));
399 } on FormatException catch (e) {
400 _port = null;
401 }
402 }
403 }
404 _set(HttpHeaders.HOST, value);
405 } else {
406 throw new HttpException("Unexpected type for header named $name");
407 }
408 }
409
410 void _addConnection(String name, value) {
411 var lowerCaseValue = value.toLowerCase();
412 if (lowerCaseValue == 'close') {
413 _persistentConnection = false;
414 } else if (lowerCaseValue == 'keep-alive') {
415 _persistentConnection = true;
416 }
417 _addValue(name, value);
418 }
419
420 void _addContentType(String name, value) {
421 _set(HttpHeaders.CONTENT_TYPE, value);
422 }
423
424 void _addValue(String name, Object value) {
425 List<String> values = _headers[name];
426 if (values == null) {
427 values = new List<String>();
428 _headers[name] = values;
429 }
430 if (value is DateTime) {
431 values.add(HttpDate.format(value));
432 } else if (value is String) {
433 values.add(value);
434 } else {
435 values.add(_validateValue(value.toString()));
436 }
437 }
438
439 void _set(String name, String value) {
440 assert(name == _validateField(name));
441 List<String> values = new List<String>();
442 _headers[name] = values;
443 values.add(value);
444 }
445
446 _checkMutable() {
447 if (!_mutable) throw new HttpException("HTTP headers are not mutable");
448 }
449
450 _updateHostHeader() {
451 bool defaultPort = _port == null || _port == _defaultPortForScheme;
452 _set("host", defaultPort ? host : "$host:$_port");
453 }
454
455 _foldHeader(String name) {
456 if (name == HttpHeaders.SET_COOKIE ||
457 (_noFoldingHeaders != null &&
458 _noFoldingHeaders.indexOf(name) != -1)) {
459 return false;
460 }
461 return true;
462 }
463
464 void _finalize() {
465 _mutable = false;
466 }
467
468 int _write(Uint8List buffer, int offset) {
469 void write(List<int> bytes) {
470 int len = bytes.length;
471 for (int i = 0; i < len; i++) {
472 buffer[offset + i] = bytes[i];
473 }
474 offset += len;
475 }
476
477 // Format headers.
478 for (String name in _headers.keys) {
479 List<String> values = _headers[name];
480 bool fold = _foldHeader(name);
481 var nameData = name.codeUnits;
482 write(nameData);
483 buffer[offset++] = _CharCode.COLON;
484 buffer[offset++] = _CharCode.SP;
485 for (int i = 0; i < values.length; i++) {
486 if (i > 0) {
487 if (fold) {
488 buffer[offset++] = _CharCode.COMMA;
489 buffer[offset++] = _CharCode.SP;
490 } else {
491 buffer[offset++] = _CharCode.CR;
492 buffer[offset++] = _CharCode.LF;
493 write(nameData);
494 buffer[offset++] = _CharCode.COLON;
495 buffer[offset++] = _CharCode.SP;
496 }
497 }
498 write(values[i].codeUnits);
499 }
500 buffer[offset++] = _CharCode.CR;
501 buffer[offset++] = _CharCode.LF;
502 }
503 return offset;
504 }
505
506 String toString() {
507 StringBuffer sb = new StringBuffer();
508 _headers.forEach((String name, List<String> values) {
509 sb..write(name)..write(": ");
510 bool fold = _foldHeader(name);
511 for (int i = 0; i < values.length; i++) {
512 if (i > 0) {
513 if (fold) {
514 sb.write(", ");
515 } else {
516 sb..write("\n")..write(name)..write(": ");
517 }
518 }
519 sb.write(values[i]);
520 }
521 sb.write("\n");
522 });
523 return sb.toString();
524 }
525
526 List<Cookie> _parseCookies() {
527 // Parse a Cookie header value according to the rules in RFC 6265.
528 var cookies = new List<Cookie>();
529 void parseCookieString(String s) {
530 int index = 0;
531
532 bool done() => index == -1 || index == s.length;
533
534 void skipWS() {
535 while (!done()) {
536 if (s[index] != " " && s[index] != "\t") return;
537 index++;
538 }
539 }
540
541 String parseName() {
542 int start = index;
543 while (!done()) {
544 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break;
545 index++;
546 }
547 return s.substring(start, index);
548 }
549
550 String parseValue() {
551 int start = index;
552 while (!done()) {
553 if (s[index] == " " || s[index] == "\t" || s[index] == ";") break;
554 index++;
555 }
556 return s.substring(start, index);
557 }
558
559 bool expect(String expected) {
560 if (done()) return false;
561 if (s[index] != expected) return false;
562 index++;
563 return true;
564 }
565
566 while (!done()) {
567 skipWS();
568 if (done()) return;
569 String name = parseName();
570 skipWS();
571 if (!expect("=")) {
572 index = s.indexOf(';', index);
573 continue;
574 }
575 skipWS();
576 String value = parseValue();
577 try {
578 cookies.add(new _Cookie(name, value));
579 } catch (_) {
580 // Skip it, invalid cookie data.
581 }
582 skipWS();
583 if (done()) return;
584 if (!expect(";")) {
585 index = s.indexOf(';', index);
586 continue;
587 }
588 }
589 }
590 List<String> values = _headers[HttpHeaders.COOKIE];
591 if (values != null) {
592 values.forEach((headerValue) => parseCookieString(headerValue));
593 }
594 return cookies;
595 }
596
597 static String _validateField(String field) {
598 for (var i = 0; i < field.length; i++) {
599 if (!_HttpParser._isTokenChar(field.codeUnitAt(i))) {
600 throw new FormatException(
601 "Invalid HTTP header field name: ${JSON.encode(field)}");
602 }
603 }
604 return field.toLowerCase();
605 }
606
607 static _validateValue(value) {
608 if (value is! String) return value;
609 for (var i = 0; i < value.length; i++) {
610 if (!_HttpParser._isValueChar(value.codeUnitAt(i))) {
611 throw new FormatException(
612 "Invalid HTTP header field value: ${JSON.encode(value)}");
613 }
614 }
615 return value;
616 }
617 }
618
619
620 class _HeaderValue implements HeaderValue {
621 String _value;
622 Map<String, String> _parameters;
623 Map<String, String> _unmodifiableParameters;
624
625 _HeaderValue([String this._value = "", Map<String, String> parameters]) {
626 if (parameters != null) {
627 _parameters = new HashMap<String, String>.from(parameters);
628 }
629 }
630
631 static _HeaderValue parse(String value,
632 {parameterSeparator: ";",
633 valueSeparator: null,
634 preserveBackslash: false}) {
635 // Parse the string.
636 var result = new _HeaderValue();
637 result._parse(value, parameterSeparator, valueSeparator, preserveBackslash);
638 return result;
639 }
640
641 String get value => _value;
642
643 void _ensureParameters() {
644 if (_parameters == null) {
645 _parameters = new HashMap<String, String>();
646 }
647 }
648
649 Map<String, String> get parameters {
650 _ensureParameters();
651 if (_unmodifiableParameters == null) {
652 _unmodifiableParameters = new UnmodifiableMapView(_parameters);
653 }
654 return _unmodifiableParameters;
655 }
656
657 String toString() {
658 StringBuffer sb = new StringBuffer();
659 sb.write(_value);
660 if (parameters != null && parameters.length > 0) {
661 _parameters.forEach((String name, String value) {
662 sb..write("; ")..write(name)..write("=")..write(value);
663 });
664 }
665 return sb.toString();
666 }
667
668 void _parse(String s,
669 String parameterSeparator,
670 String valueSeparator,
671 bool preserveBackslash) {
672 int index = 0;
673
674 bool done() => index == s.length;
675
676 void skipWS() {
677 while (!done()) {
678 if (s[index] != " " && s[index] != "\t") return;
679 index++;
680 }
681 }
682
683 String parseValue() {
684 int start = index;
685 while (!done()) {
686 if (s[index] == " " ||
687 s[index] == "\t" ||
688 s[index] == valueSeparator ||
689 s[index] == parameterSeparator) break;
690 index++;
691 }
692 return s.substring(start, index);
693 }
694
695 void expect(String expected) {
696 if (done() || s[index] != expected) {
697 throw new HttpException("Failed to parse header value");
698 }
699 index++;
700 }
701
702 void maybeExpect(String expected) {
703 if (s[index] == expected) index++;
704 }
705
706 void parseParameters() {
707 var parameters = new HashMap<String, String>();
708 _parameters = new UnmodifiableMapView(parameters);
709
710 String parseParameterName() {
711 int start = index;
712 while (!done()) {
713 if (s[index] == " " ||
714 s[index] == "\t" ||
715 s[index] == "=" ||
716 s[index] == parameterSeparator ||
717 s[index] == valueSeparator) break;
718 index++;
719 }
720 return s.substring(start, index).toLowerCase();
721 }
722
723 String parseParameterValue() {
724 if (!done() && s[index] == "\"") {
725 // Parse quoted value.
726 StringBuffer sb = new StringBuffer();
727 index++;
728 while (!done()) {
729 if (s[index] == "\\") {
730 if (index + 1 == s.length) {
731 throw new HttpException("Failed to parse header value");
732 }
733 if (preserveBackslash && s[index + 1] != "\"") {
734 sb.write(s[index]);
735 }
736 index++;
737 } else if (s[index] == "\"") {
738 index++;
739 break;
740 }
741 sb.write(s[index]);
742 index++;
743 }
744 return sb.toString();
745 } else {
746 // Parse non-quoted value.
747 var val = parseValue();
748 return val == "" ? null : val;
749 }
750 }
751
752 while (!done()) {
753 skipWS();
754 if (done()) return;
755 String name = parseParameterName();
756 skipWS();
757 if (done()) {
758 parameters[name] = null;
759 return;
760 }
761 maybeExpect("=");
762 skipWS();
763 if(done()) {
764 parameters[name] = null;
765 return;
766 }
767 String value = parseParameterValue();
768 if (name == 'charset' && this is _ContentType) {
769 // Charset parameter of ContentTypes are always lower-case.
770 value = value.toLowerCase();
771 }
772 parameters[name] = value;
773 skipWS();
774 if (done()) return;
775 // TODO: Implement support for multi-valued parameters.
776 if(s[index] == valueSeparator) return;
777 expect(parameterSeparator);
778 }
779 }
780
781 skipWS();
782 _value = parseValue();
783 skipWS();
784 if (done()) return;
785 maybeExpect(parameterSeparator);
786 parseParameters();
787 }
788 }
789
790
791 class _ContentType extends _HeaderValue implements ContentType {
792 String _primaryType = "";
793 String _subType = "";
794
795 _ContentType(String primaryType,
796 String subType,
797 String charset,
798 Map<String, String> parameters)
799 : _primaryType = primaryType, _subType = subType, super("") {
800 if (_primaryType == null) _primaryType = "";
801 if (_subType == null) _subType = "";
802 _value = "$_primaryType/$_subType";
803 if (parameters != null) {
804 _ensureParameters();
805 parameters.forEach((String key, String value) {
806 String lowerCaseKey = key.toLowerCase();
807 if (lowerCaseKey == "charset") {
808 value = value.toLowerCase();
809 }
810 this._parameters[lowerCaseKey] = value;
811 });
812 }
813 if (charset != null) {
814 _ensureParameters();
815 this._parameters["charset"] = charset.toLowerCase();
816 }
817 }
818
819 _ContentType._();
820
821 static _ContentType parse(String value) {
822 var result = new _ContentType._();
823 result._parse(value, ";", null, false);
824 int index = result._value.indexOf("/");
825 if (index == -1 || index == (result._value.length - 1)) {
826 result._primaryType = result._value.trim().toLowerCase();
827 result._subType = "";
828 } else {
829 result._primaryType =
830 result._value.substring(0, index).trim().toLowerCase();
831 result._subType = result._value.substring(index + 1).trim().toLowerCase();
832 }
833 return result;
834 }
835
836 String get mimeType => '$primaryType/$subType';
837
838 String get primaryType => _primaryType;
839
840 String get subType => _subType;
841
842 String get charset => parameters["charset"];
843 }
844
845
846 class _Cookie implements Cookie {
847 String name;
848 String value;
849 DateTime expires;
850 int maxAge;
851 String domain;
852 String path;
853 bool httpOnly = false;
854 bool secure = false;
855
856 _Cookie([this.name, this.value]) {
857 // Default value of httponly is true.
858 httpOnly = true;
859 _validate();
860 }
861
862 _Cookie.fromSetCookieValue(String value) {
863 // Parse the 'set-cookie' header value.
864 _parseSetCookieValue(value);
865 }
866
867 // Parse a 'set-cookie' header value according to the rules in RFC 6265.
868 void _parseSetCookieValue(String s) {
869 int index = 0;
870
871 bool done() => index == s.length;
872
873 String parseName() {
874 int start = index;
875 while (!done()) {
876 if (s[index] == "=") break;
877 index++;
878 }
879 return s.substring(start, index).trim();
880 }
881
882 String parseValue() {
883 int start = index;
884 while (!done()) {
885 if (s[index] == ";") break;
886 index++;
887 }
888 return s.substring(start, index).trim();
889 }
890
891 void expect(String expected) {
892 if (done()) throw new HttpException("Failed to parse header value [$s]");
893 if (s[index] != expected) {
894 throw new HttpException("Failed to parse header value [$s]");
895 }
896 index++;
897 }
898
899 void parseAttributes() {
900 String parseAttributeName() {
901 int start = index;
902 while (!done()) {
903 if (s[index] == "=" || s[index] == ";") break;
904 index++;
905 }
906 return s.substring(start, index).trim().toLowerCase();
907 }
908
909 String parseAttributeValue() {
910 int start = index;
911 while (!done()) {
912 if (s[index] == ";") break;
913 index++;
914 }
915 return s.substring(start, index).trim().toLowerCase();
916 }
917
918 while (!done()) {
919 String name = parseAttributeName();
920 String value = "";
921 if (!done() && s[index] == "=") {
922 index++; // Skip the = character.
923 value = parseAttributeValue();
924 }
925 if (name == "expires") {
926 expires = HttpDate._parseCookieDate(value);
927 } else if (name == "max-age") {
928 maxAge = int.parse(value);
929 } else if (name == "domain") {
930 domain = value;
931 } else if (name == "path") {
932 path = value;
933 } else if (name == "httponly") {
934 httpOnly = true;
935 } else if (name == "secure") {
936 secure = true;
937 }
938 if (!done()) index++; // Skip the ; character
939 }
940 }
941
942 name = parseName();
943 if (done() || name.length == 0) {
944 throw new HttpException("Failed to parse header value [$s]");
945 }
946 index++; // Skip the = character.
947 value = parseValue();
948 _validate();
949 if (done()) return;
950 index++; // Skip the ; character.
951 parseAttributes();
952 }
953
954 String toString() {
955 StringBuffer sb = new StringBuffer();
956 sb..write(name)..write("=")..write(value);
957 if (expires != null) {
958 sb..write("; Expires=")..write(HttpDate.format(expires));
959 }
960 if (maxAge != null) {
961 sb..write("; Max-Age=")..write(maxAge);
962 }
963 if (domain != null) {
964 sb..write("; Domain=")..write(domain);
965 }
966 if (path != null) {
967 sb..write("; Path=")..write(path);
968 }
969 if (secure) sb.write("; Secure");
970 if (httpOnly) sb.write("; HttpOnly");
971 return sb.toString();
972 }
973
974 void _validate() {
975 const SEPERATORS = const [
976 "(", ")", "<", ">", "@", ",", ";", ":", "\\",
977 '"', "/", "[", "]", "?", "=", "{", "}"];
978 for (int i = 0; i < name.length; i++) {
979 int codeUnit = name.codeUnits[i];
980 if (codeUnit <= 32 ||
981 codeUnit >= 127 ||
982 SEPERATORS.indexOf(name[i]) >= 0) {
983 throw new FormatException(
984 "Invalid character in cookie name, code unit: '$codeUnit'");
985 }
986 }
987 for (int i = 0; i < value.length; i++) {
988 int codeUnit = value.codeUnits[i];
989 if (!(codeUnit == 0x21 ||
990 (codeUnit >= 0x23 && codeUnit <= 0x2B) ||
991 (codeUnit >= 0x2D && codeUnit <= 0x3A) ||
992 (codeUnit >= 0x3C && codeUnit <= 0x5B) ||
993 (codeUnit >= 0x5D && codeUnit <= 0x7E))) {
994 throw new FormatException(
995 "Invalid character in cookie value, code unit: '$codeUnit'");
996 }
997 }
998 }
999 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/tool/input_sdk/lib/io/http_date.dart ('k') | pkg/dev_compiler/tool/input_sdk/lib/io/http_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698