OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 shelf.util; | 5 library shelf.util; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'package:stack_trace/stack_trace.dart'; | 9 import 'package:stack_trace/stack_trace.dart'; |
10 | 10 import 'package:string_scanner/string_scanner.dart'; |
11 import 'string_scanner.dart'; | |
12 | 11 |
13 /// Like [Future.sync], but wraps the Future in [Chain.track] as well. | 12 /// Like [Future.sync], but wraps the Future in [Chain.track] as well. |
14 Future syncFuture(callback()) => Chain.track(new Future.sync(callback)); | 13 Future syncFuture(callback()) => Chain.track(new Future.sync(callback)); |
15 | 14 |
16 const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; | 15 const _WEEKDAYS = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; |
17 const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", | 16 const _MONTHS = const ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", |
18 "Sep", "Oct", "Nov", "Dec"]; | 17 "Sep", "Oct", "Nov", "Dec"]; |
19 | 18 |
20 final _shortWeekdayRegExp = new RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); | 19 final _shortWeekdayRegExp = new RegExp(r"Mon|Tue|Wed|Thu|Fri|Sat|Sun"); |
21 final _longWeekdayRegExp = | 20 final _longWeekdayRegExp = |
(...skipping 27 matching lines...) Expand all Loading... |
49 return buffer.toString(); | 48 return buffer.toString(); |
50 } | 49 } |
51 | 50 |
52 // TODO(nweiz): Move this into an http_parser package. | 51 // TODO(nweiz): Move this into an http_parser package. |
53 /// Parses an HTTP-formatted date into a UTC [DateTime]. | 52 /// Parses an HTTP-formatted date into a UTC [DateTime]. |
54 /// | 53 /// |
55 /// This follows [RFC | 54 /// This follows [RFC |
56 /// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will | 55 /// 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3). It will |
57 /// throw a [FormatException] if [date] is invalid. | 56 /// throw a [FormatException] if [date] is invalid. |
58 DateTime parseHttpDate(String date) { | 57 DateTime parseHttpDate(String date) { |
59 var errorMessage = 'Invalid HTTP date "$date".'; | 58 try { |
60 var scanner = new StringScanner(date); | 59 var scanner = new StringScanner(date); |
61 | 60 |
62 if (scanner.scan(_longWeekdayRegExp)) { | 61 if (scanner.scan(_longWeekdayRegExp)) { |
63 // RFC 850 starts with a long weekday. | 62 // RFC 850 starts with a long weekday. |
64 scanner.expect(", ", errorMessage); | 63 scanner.expect(", "); |
65 var day = _parseInt(scanner, errorMessage, 2); | 64 var day = _parseInt(scanner, 2); |
66 scanner.expect("-", errorMessage); | 65 scanner.expect("-"); |
67 var month = _parseMonth(scanner, errorMessage); | 66 var month = _parseMonth(scanner); |
68 scanner.expect("-", errorMessage); | 67 scanner.expect("-"); |
69 var year = 1900 + _parseInt(scanner, errorMessage, 2); | 68 var year = 1900 + _parseInt(scanner, 2); |
70 scanner.expect(" ", errorMessage); | 69 scanner.expect(" "); |
71 var time = _parseTime(scanner, errorMessage); | 70 var time = _parseTime(scanner); |
72 scanner.expect(" GMT", errorMessage); | 71 scanner.expect(" GMT"); |
73 if (!scanner.isDone) throw new FormatException(errorMessage); | 72 scanner.expectDone(); |
74 | 73 |
75 return _makeDateTime(year, month, day, time, errorMessage); | 74 return _makeDateTime(year, month, day, time); |
| 75 } |
| 76 |
| 77 // RFC 1123 and asctime both start with a short weekday. |
| 78 scanner.expect(_shortWeekdayRegExp); |
| 79 if (scanner.scan(", ")) { |
| 80 // RFC 1123 follows the weekday with a comma. |
| 81 var day = _parseInt(scanner, 2); |
| 82 scanner.expect(" "); |
| 83 var month = _parseMonth(scanner); |
| 84 scanner.expect(" "); |
| 85 var year = _parseInt(scanner, 4); |
| 86 scanner.expect(" "); |
| 87 var time = _parseTime(scanner); |
| 88 scanner.expect(" GMT"); |
| 89 scanner.expectDone(); |
| 90 |
| 91 return _makeDateTime(year, month, day, time); |
| 92 } |
| 93 |
| 94 // asctime follows the weekday with a space. |
| 95 scanner.expect(" "); |
| 96 var month = _parseMonth(scanner); |
| 97 scanner.expect(" "); |
| 98 var day = scanner.scan(" ") ? |
| 99 _parseInt(scanner, 1) : |
| 100 _parseInt(scanner, 2); |
| 101 scanner.expect(" "); |
| 102 var time = _parseTime(scanner); |
| 103 scanner.expect(" "); |
| 104 var year = _parseInt(scanner, 4); |
| 105 scanner.expectDone(); |
| 106 |
| 107 return _makeDateTime(year, month, day, time); |
| 108 } on FormatException catch (error) { |
| 109 throw new FormatException('Invalid HTTP date "$date": ${error.message}'); |
76 } | 110 } |
77 | |
78 // RFC 1123 and asctime both start with a short weekday. | |
79 scanner.expect(_shortWeekdayRegExp, errorMessage); | |
80 if (scanner.scan(", ")) { | |
81 // RFC 1123 follows the weekday with a comma. | |
82 var day = _parseInt(scanner, errorMessage, 2); | |
83 scanner.expect(" ", errorMessage); | |
84 var month = _parseMonth(scanner, errorMessage); | |
85 scanner.expect(" ", errorMessage); | |
86 var year = _parseInt(scanner, errorMessage, 4); | |
87 scanner.expect(" ", errorMessage); | |
88 var time = _parseTime(scanner, errorMessage); | |
89 scanner.expect(" GMT", errorMessage); | |
90 if (!scanner.isDone) throw new FormatException(errorMessage); | |
91 | |
92 return _makeDateTime(year, month, day, time, errorMessage); | |
93 } | |
94 | |
95 // asctime follows the weekday with a space. | |
96 scanner.expect(" ", errorMessage); | |
97 var month = _parseMonth(scanner, errorMessage); | |
98 scanner.expect(" ", errorMessage); | |
99 var day = scanner.scan(" ") ? | |
100 _parseInt(scanner, errorMessage, 1) : | |
101 _parseInt(scanner, errorMessage, 2); | |
102 scanner.expect(" ", errorMessage); | |
103 var time = _parseTime(scanner, errorMessage); | |
104 scanner.expect(" ", errorMessage); | |
105 var year = _parseInt(scanner, errorMessage, 4); | |
106 if (!scanner.isDone) throw new FormatException(errorMessage); | |
107 | |
108 return _makeDateTime(year, month, day, time, errorMessage); | |
109 } | 111 } |
110 | 112 |
111 /// Parses a short-form month name to a form accepted by [DateTime]. | 113 /// Parses a short-form month name to a form accepted by [DateTime]. |
112 int _parseMonth(StringScanner scanner, String errorMessage) { | 114 int _parseMonth(StringScanner scanner) { |
113 scanner.expect(_monthRegExp, errorMessage); | 115 scanner.expect(_monthRegExp); |
114 // DateTime uses 1-indexed months. | 116 // DateTime uses 1-indexed months. |
115 return _MONTHS.indexOf(scanner.lastMatch[0]) + 1; | 117 return _MONTHS.indexOf(scanner.lastMatch[0]) + 1; |
116 } | 118 } |
117 | 119 |
118 /// Parses an int an enforces that it has exactly [digits] digits. | 120 /// Parses an int an enforces that it has exactly [digits] digits. |
119 int _parseInt(StringScanner scanner, String errorMessage, int digits) { | 121 int _parseInt(StringScanner scanner, int digits) { |
120 scanner.expect(_digitRegExp, errorMessage); | 122 scanner.expect(_digitRegExp); |
121 if (scanner.lastMatch[0].length != digits) { | 123 if (scanner.lastMatch[0].length != digits) { |
122 throw new FormatException(errorMessage); | 124 scanner.error("expected a $digits-digit number."); |
123 } else { | 125 } else { |
124 return int.parse(scanner.lastMatch[0]); | 126 return int.parse(scanner.lastMatch[0]); |
125 } | 127 } |
126 } | 128 } |
127 | 129 |
128 /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. | 130 /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock. |
129 DateTime _parseTime(StringScanner scanner, String errorMessage) { | 131 DateTime _parseTime(StringScanner scanner) { |
130 var hours = _parseInt(scanner, errorMessage, 2); | 132 var hours = _parseInt(scanner, 2); |
131 scanner.expect(':', errorMessage); | 133 if (hours >= 24) scanner.error("hours may not be greater than 24."); |
| 134 scanner.expect(':'); |
132 | 135 |
133 var minutes = _parseInt(scanner, errorMessage, 2); | 136 var minutes = _parseInt(scanner, 2); |
134 scanner.expect(':', errorMessage); | 137 if (minutes >= 60) scanner.error("minutes may not be greater than 60."); |
| 138 scanner.expect(':'); |
135 | 139 |
136 var seconds = _parseInt(scanner, errorMessage, 2); | 140 var seconds = _parseInt(scanner, 2); |
137 | 141 if (seconds >= 60) scanner.error("seconds may not be greater than 60."); |
138 if (hours >= 24 || minutes >= 60 || seconds >= 60) { | |
139 throw new FormatException(errorMessage); | |
140 } | |
141 | 142 |
142 return new DateTime(1, 1, 1, hours, minutes, seconds); | 143 return new DateTime(1, 1, 1, hours, minutes, seconds); |
143 } | 144 } |
144 | 145 |
145 /// Returns a UTC [DateTime] from the given components. | 146 /// Returns a UTC [DateTime] from the given components. |
146 /// | 147 /// |
147 /// Validates that [day] is a valid day for [month]. If it's not, throws a | 148 /// Validates that [day] is a valid day for [month]. If it's not, throws a |
148 /// [FormatException] with [errorMessage]. | 149 /// [FormatException]. |
149 DateTime _makeDateTime(int year, int month, int day, DateTime time, | 150 DateTime _makeDateTime(int year, int month, int day, DateTime time) { |
150 String errorMessage) { | |
151 if (day < 1) throw new FormatException(errorMessage); | |
152 var dateTime = new DateTime.utc( | 151 var dateTime = new DateTime.utc( |
153 year, month, day, time.hour, time.minute, time.second); | 152 year, month, day, time.hour, time.minute, time.second); |
154 | 153 |
155 // If [day] was too large, it will cause [month] to overflow. | 154 // If [day] was too large, it will cause [month] to overflow. |
156 if (dateTime.month != month) throw new FormatException(errorMessage); | 155 if (dateTime.month != month) { |
| 156 throw new FormatException("invalid day '$day' for month '$month'."); |
| 157 } |
157 return dateTime; | 158 return dateTime; |
158 } | 159 } |
OLD | NEW |