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 class _HttpHeaders implements HttpHeaders { | |
6 _HttpHeaders() : _headers = new Map<String, List<String>>(); | |
7 | |
8 List<String> operator[](String name) { | |
9 name = name.toLowerCase(); | |
10 return _headers[name]; | |
11 } | |
12 | |
13 String value(String name) { | |
14 name = name.toLowerCase(); | |
15 List<String> values = _headers[name]; | |
16 if (values == null) return null; | |
17 if (values.length > 1) { | |
18 throw new HttpException("More than one value for header $name"); | |
19 } | |
20 return values[0]; | |
21 } | |
22 | |
23 void add(String name, Object value) { | |
24 _checkMutable(); | |
25 if (value is List) { | |
26 for (int i = 0; i < value.length; i++) { | |
27 _add(name, value[i]); | |
28 } | |
29 } else { | |
30 _add(name, value); | |
31 } | |
32 } | |
33 | |
34 void set(String name, Object value) { | |
35 name = name.toLowerCase(); | |
36 _checkMutable(); | |
37 removeAll(name); | |
38 add(name, value); | |
39 } | |
40 | |
41 void remove(String name, Object value) { | |
42 _checkMutable(); | |
43 name = name.toLowerCase(); | |
44 List<String> values = _headers[name]; | |
45 if (values != null) { | |
46 int index = values.indexOf(value); | |
47 if (index != -1) { | |
48 values.removeRange(index, 1); | |
49 } | |
50 } | |
51 } | |
52 | |
53 void removeAll(String name) { | |
54 _checkMutable(); | |
55 name = name.toLowerCase(); | |
56 _headers.remove(name); | |
57 } | |
58 | |
59 void forEach(void f(String name, List<String> values)) { | |
60 _headers.forEach(f); | |
61 } | |
62 | |
63 void noFolding(String name) { | |
64 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); | |
65 _noFoldingHeaders.add(name); | |
66 } | |
67 | |
68 String get host => _host; | |
69 | |
70 void set host(String host) { | |
71 _checkMutable(); | |
72 _host = host; | |
73 _updateHostHeader(); | |
74 } | |
75 | |
76 int get port => _port; | |
77 | |
78 void set port(int port) { | |
79 _checkMutable(); | |
80 _port = port; | |
81 _updateHostHeader(); | |
82 } | |
83 | |
84 Date get ifModifiedSince { | |
85 List<String> values = _headers["if-modified-since"]; | |
86 if (values != null) { | |
87 try { | |
88 return _HttpUtils.parseDate(values[0]); | |
89 } on Exception catch (e) { | |
90 return null; | |
91 } | |
92 } | |
93 return null; | |
94 } | |
95 | |
96 void set ifModifiedSince(Date ifModifiedSince) { | |
97 _checkMutable(); | |
98 // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). | |
99 String formatted = _HttpUtils.formatDate(ifModifiedSince.toUtc()); | |
100 _set("if-modified-since", formatted); | |
101 } | |
102 | |
103 Date get date { | |
104 List<String> values = _headers["date"]; | |
105 if (values != null) { | |
106 try { | |
107 return _HttpUtils.parseDate(values[0]); | |
108 } on Exception catch (e) { | |
109 return null; | |
110 } | |
111 } | |
112 return null; | |
113 } | |
114 | |
115 void set date(Date date) { | |
116 _checkMutable(); | |
117 // Format "Date" header with date in Greenwich Mean Time (GMT). | |
118 String formatted = _HttpUtils.formatDate(date.toUtc()); | |
119 _set("date", formatted); | |
120 } | |
121 | |
122 Date get expires { | |
123 List<String> values = _headers["expires"]; | |
124 if (values != null) { | |
125 try { | |
126 return _HttpUtils.parseDate(values[0]); | |
127 } on Exception catch (e) { | |
128 return null; | |
129 } | |
130 } | |
131 return null; | |
132 } | |
133 | |
134 void set expires(Date expires) { | |
135 _checkMutable(); | |
136 // Format "Expires" header with date in Greenwich Mean Time (GMT). | |
137 String formatted = _HttpUtils.formatDate(expires.toUtc()); | |
138 _set("expires", formatted); | |
139 } | |
140 | |
141 ContentType get contentType { | |
142 var values = _headers["content-type"]; | |
143 if (values != null) { | |
144 return new ContentType.fromString(values[0]); | |
145 } else { | |
146 return new ContentType(); | |
147 } | |
148 } | |
149 | |
150 void set contentType(ContentType contentType) { | |
151 _checkMutable(); | |
152 _set("content-type", contentType.toString()); | |
153 } | |
154 | |
155 void _add(String name, Object value) { | |
156 var lowerCaseName = name.toLowerCase(); | |
157 // TODO(sgjesse): Add immutable state throw HttpException is immutable. | |
158 if (lowerCaseName == "date") { | |
159 if (value is Date) { | |
160 date = value; | |
161 } else if (value is String) { | |
162 _set("date", value); | |
163 } else { | |
164 throw new HttpException("Unexpected type for header named $name"); | |
165 } | |
166 } else if (lowerCaseName == "expires") { | |
167 if (value is Date) { | |
168 expires = value; | |
169 } else if (value is String) { | |
170 _set("expires", value); | |
171 } else { | |
172 throw new HttpException("Unexpected type for header named $name"); | |
173 } | |
174 } else if (lowerCaseName == "if-modified-since") { | |
175 if (value is Date) { | |
176 ifModifiedSince = value; | |
177 } else if (value is String) { | |
178 _set("if-modified-since", value); | |
179 } else { | |
180 throw new HttpException("Unexpected type for header named $name"); | |
181 } | |
182 } else if (lowerCaseName == "host") { | |
183 int pos = value.indexOf(":"); | |
184 if (pos == -1) { | |
185 _host = value; | |
186 _port = HttpClient.DEFAULT_HTTP_PORT; | |
187 } else { | |
188 if (pos > 0) { | |
189 _host = value.substring(0, pos); | |
190 } else { | |
191 _host = null; | |
192 } | |
193 if (pos + 1 == value.length) { | |
194 _port = HttpClient.DEFAULT_HTTP_PORT; | |
195 } else { | |
196 try { | |
197 _port = parseInt(value.substring(pos + 1)); | |
198 } on FormatException catch (e) { | |
199 _port = null; | |
200 } | |
201 } | |
202 _set("host", value); | |
203 } | |
204 } else if (lowerCaseName == "content-type") { | |
205 _set("content-type", value); | |
206 } else { | |
207 name = lowerCaseName; | |
208 List<String> values = _headers[name]; | |
209 if (values == null) { | |
210 values = new List<String>(); | |
211 _headers[name] = values; | |
212 } | |
213 if (value is Date) { | |
214 values.add(_HttpUtils.formatDate(value)); | |
215 } else { | |
216 values.add(value.toString()); | |
217 } | |
218 } | |
219 } | |
220 | |
221 void _set(String name, String value) { | |
222 name = name.toLowerCase(); | |
223 List<String> values = new List<String>(); | |
224 _headers[name] = values; | |
225 values.add(value); | |
226 } | |
227 | |
228 _checkMutable() { | |
229 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); | |
230 } | |
231 | |
232 _updateHostHeader() { | |
233 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; | |
234 String portPart = defaultPort ? "" : ":$_port"; | |
235 _set("host", "$host$portPart"); | |
236 } | |
237 | |
238 _foldHeader(String name) { | |
239 if (name == "set-cookie" || | |
240 (_noFoldingHeaders != null && | |
241 _noFoldingHeaders.indexOf(name) != -1)) { | |
242 return false; | |
243 } | |
244 return true; | |
245 } | |
246 | |
247 _write(_HttpConnectionBase connection) { | |
248 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; | |
249 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; | |
250 final CRLF = const [_CharCode.CR, _CharCode.LF]; | |
251 | |
252 var bufferSize = 16 * 1024; | |
253 var buffer = new Uint8List(bufferSize); | |
254 var bufferPos = 0; | |
255 | |
256 void writeBuffer() { | |
257 connection._writeFrom(buffer, 0, bufferPos); | |
258 bufferPos = 0; | |
259 } | |
260 | |
261 // Format headers. | |
262 _headers.forEach((String name, List<String> values) { | |
263 bool fold = _foldHeader(name); | |
264 List<int> nameData; | |
265 nameData = name.charCodes; | |
266 int nameDataLen = nameData.length; | |
267 if (nameDataLen + 2 > bufferSize - bufferPos) writeBuffer(); | |
268 buffer.setRange(bufferPos, nameDataLen, nameData); | |
269 bufferPos += nameDataLen; | |
270 buffer[bufferPos++] = _CharCode.COLON; | |
271 buffer[bufferPos++] = _CharCode.SP; | |
272 for (int i = 0; i < values.length; i++) { | |
273 List<int> data = values[i].charCodes; | |
274 int dataLen = data.length; | |
275 // Worst case here is writing the name, value and 6 additional bytes. | |
276 if (nameDataLen + dataLen + 6 > bufferSize - bufferPos) writeBuffer(); | |
277 if (i > 0) { | |
278 if (fold) { | |
279 buffer[bufferPos++] = _CharCode.COMMA; | |
280 buffer[bufferPos++] = _CharCode.SP; | |
281 } else { | |
282 buffer[bufferPos++] = _CharCode.CR; | |
283 buffer[bufferPos++] = _CharCode.LF; | |
284 buffer.setRange(bufferPos, nameDataLen, nameData); | |
285 bufferPos += nameDataLen; | |
286 buffer[bufferPos++] = _CharCode.COLON; | |
287 buffer[bufferPos++] = _CharCode.SP; | |
288 } | |
289 } | |
290 buffer.setRange(bufferPos, dataLen, data); | |
291 bufferPos += dataLen; | |
292 } | |
293 buffer[bufferPos++] = _CharCode.CR; | |
294 buffer[bufferPos++] = _CharCode.LF; | |
295 }); | |
296 writeBuffer(); | |
297 } | |
298 | |
299 String toString() { | |
300 StringBuffer sb = new StringBuffer(); | |
301 _headers.forEach((String name, List<String> values) { | |
302 sb.add(name); | |
303 sb.add(": "); | |
304 bool fold = _foldHeader(name); | |
305 for (int i = 0; i < values.length; i++) { | |
306 if (i > 0) { | |
307 if (fold) { | |
308 sb.add(", "); | |
309 } else { | |
310 sb.add("\n"); | |
311 sb.add(name); | |
312 sb.add(": "); | |
313 } | |
314 } | |
315 sb.add(values[i]); | |
316 } | |
317 sb.add("\n"); | |
318 }); | |
319 return sb.toString(); | |
320 } | |
321 | |
322 bool _mutable = true; // Are the headers currently mutable? | |
323 Map<String, List<String>> _headers; | |
324 List<String> _noFoldingHeaders; | |
325 | |
326 String _host; | |
327 int _port; | |
328 } | |
329 | |
330 | |
331 class _HeaderValue implements HeaderValue { | |
332 _HeaderValue([String this.value = ""]); | |
333 | |
334 _HeaderValue.fromString(String value, {this.parameterSeparator: ";"}) { | |
335 // Parse the string. | |
336 _parse(value); | |
337 } | |
338 | |
339 Map<String, String> get parameters { | |
340 if (_parameters == null) _parameters = new Map<String, String>(); | |
341 return _parameters; | |
342 } | |
343 | |
344 String toString() { | |
345 StringBuffer sb = new StringBuffer(); | |
346 sb.add(value); | |
347 if (parameters != null && parameters.length > 0) { | |
348 _parameters.forEach((String name, String value) { | |
349 sb.add("; "); | |
350 sb.add(name); | |
351 sb.add("="); | |
352 sb.add(value); | |
353 }); | |
354 } | |
355 return sb.toString(); | |
356 } | |
357 | |
358 void _parse(String s) { | |
359 int index = 0; | |
360 | |
361 bool done() => index == s.length; | |
362 | |
363 void skipWS() { | |
364 while (!done()) { | |
365 if (s[index] != " " && s[index] != "\t") return; | |
366 index++; | |
367 } | |
368 } | |
369 | |
370 String parseValue() { | |
371 int start = index; | |
372 while (!done()) { | |
373 if (s[index] == " " || | |
374 s[index] == "\t" || | |
375 s[index] == parameterSeparator) break; | |
376 index++; | |
377 } | |
378 return s.substring(start, index).toLowerCase(); | |
379 } | |
380 | |
381 void expect(String expected) { | |
382 if (done() || s[index] != expected) { | |
383 throw new HttpException("Failed to parse header value"); | |
384 } | |
385 index++; | |
386 } | |
387 | |
388 void maybeExpect(String expected) { | |
389 if (s[index] == expected) index++; | |
390 } | |
391 | |
392 void parseParameters() { | |
393 _parameters = new Map<String, String>(); | |
394 | |
395 String parseParameterName() { | |
396 int start = index; | |
397 while (!done()) { | |
398 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; | |
399 index++; | |
400 } | |
401 return s.substring(start, index).toLowerCase(); | |
402 } | |
403 | |
404 String parseParameterValue() { | |
405 if (s[index] == "\"") { | |
406 // Parse quoted value. | |
407 StringBuffer sb = new StringBuffer(); | |
408 index++; | |
409 while (!done()) { | |
410 if (s[index] == "\\") { | |
411 if (index + 1 == s.length) { | |
412 throw new HttpException("Failed to parse header value"); | |
413 } | |
414 index++; | |
415 } else if (s[index] == "\"") { | |
416 index++; | |
417 break; | |
418 } | |
419 sb.add(s[index]); | |
420 index++; | |
421 } | |
422 return sb.toString(); | |
423 } else { | |
424 // Parse non-quoted value. | |
425 return parseValue(); | |
426 } | |
427 } | |
428 | |
429 while (!done()) { | |
430 skipWS(); | |
431 if (done()) return; | |
432 String name = parseParameterName(); | |
433 skipWS(); | |
434 expect("="); | |
435 skipWS(); | |
436 String value = parseParameterValue(); | |
437 _parameters[name] = value; | |
438 skipWS(); | |
439 if (done()) return; | |
440 expect(parameterSeparator); | |
441 } | |
442 } | |
443 | |
444 skipWS(); | |
445 value = parseValue(); | |
446 skipWS(); | |
447 if (done()) return; | |
448 maybeExpect(parameterSeparator); | |
449 parseParameters(); | |
450 } | |
451 | |
452 String value; | |
453 String parameterSeparator; | |
454 Map<String, String> _parameters; | |
455 } | |
456 | |
457 | |
458 class _ContentType extends _HeaderValue implements ContentType { | |
459 _ContentType(String primaryType, String subType) | |
460 : _primaryType = primaryType, _subType = subType, super(""); | |
461 | |
462 _ContentType.fromString(String value) : super.fromString(value); | |
463 | |
464 String get value => "$_primaryType/$_subType"; | |
465 | |
466 void set value(String s) { | |
467 int index = s.indexOf("/"); | |
468 if (index == -1 || index == (s.length - 1)) { | |
469 primaryType = s.trim().toLowerCase(); | |
470 subType = ""; | |
471 } else { | |
472 primaryType = s.substring(0, index).trim().toLowerCase(); | |
473 subType = s.substring(index + 1).trim().toLowerCase(); | |
474 } | |
475 } | |
476 | |
477 String get primaryType => _primaryType; | |
478 | |
479 void set primaryType(String s) { | |
480 _primaryType = s; | |
481 } | |
482 | |
483 String get subType => _subType; | |
484 | |
485 void set subType(String s) { | |
486 _subType = s; | |
487 } | |
488 | |
489 String get charset => parameters["charset"]; | |
490 | |
491 void set charset(String s) { | |
492 parameters["charset"] = s; | |
493 } | |
494 | |
495 String _primaryType = ""; | |
496 String _subType = ""; | |
497 } | |
498 | |
499 | |
500 class _Cookie implements Cookie { | |
501 _Cookie([String this.name, String this.value]); | |
502 | |
503 _Cookie.fromSetCookieValue(String value) { | |
504 // Parse the Set-Cookie header value. | |
505 _parseSetCookieValue(value); | |
506 } | |
507 | |
508 // Parse a Set-Cookie header value according to the rules in RFC 6265. | |
509 void _parseSetCookieValue(String s) { | |
510 int index = 0; | |
511 | |
512 bool done() => index == s.length; | |
513 | |
514 String parseName() { | |
515 int start = index; | |
516 while (!done()) { | |
517 if (s[index] == "=") break; | |
518 index++; | |
519 } | |
520 return s.substring(start, index).trim().toLowerCase(); | |
521 } | |
522 | |
523 String parseValue() { | |
524 int start = index; | |
525 while (!done()) { | |
526 if (s[index] == ";") break; | |
527 index++; | |
528 } | |
529 return s.substring(start, index).trim().toLowerCase(); | |
530 } | |
531 | |
532 void expect(String expected) { | |
533 if (done()) throw new HttpException("Failed to parse header value [$s]"); | |
534 if (s[index] != expected) { | |
535 throw new HttpException("Failed to parse header value [$s]"); | |
536 } | |
537 index++; | |
538 } | |
539 | |
540 void parseAttributes() { | |
541 String parseAttributeName() { | |
542 int start = index; | |
543 while (!done()) { | |
544 if (s[index] == "=" || s[index] == ";") break; | |
545 index++; | |
546 } | |
547 return s.substring(start, index).trim().toLowerCase(); | |
548 } | |
549 | |
550 String parseAttributeValue() { | |
551 int start = index; | |
552 while (!done()) { | |
553 if (s[index] == ";") break; | |
554 index++; | |
555 } | |
556 return s.substring(start, index).trim().toLowerCase(); | |
557 } | |
558 | |
559 while (!done()) { | |
560 String name = parseAttributeName(); | |
561 String value = ""; | |
562 if (!done() && s[index] == "=") { | |
563 index++; // Skip the = character. | |
564 value = parseAttributeValue(); | |
565 } | |
566 if (name == "expires") { | |
567 expires = _HttpUtils.parseCookieDate(value); | |
568 } else if (name == "max-age") { | |
569 maxAge = parseInt(value); | |
570 } else if (name == "domain") { | |
571 domain = value; | |
572 } else if (name == "path") { | |
573 path = value; | |
574 } else if (name == "httponly") { | |
575 httpOnly = true; | |
576 } else if (name == "secure") { | |
577 secure = true; | |
578 } | |
579 if (!done()) index++; // Skip the ; character | |
580 } | |
581 } | |
582 | |
583 name = parseName(); | |
584 if (done() || name.length == 0) { | |
585 throw new HttpException("Failed to parse header value [$s]"); | |
586 } | |
587 index++; // Skip the = character. | |
588 value = parseValue(); | |
589 if (done()) return; | |
590 index++; // Skip the ; character. | |
591 parseAttributes(); | |
592 } | |
593 | |
594 String toString() { | |
595 StringBuffer sb = new StringBuffer(); | |
596 sb.add(name); | |
597 sb.add("="); | |
598 sb.add(value); | |
599 if (expires != null) { | |
600 sb.add("; Expires="); | |
601 sb.add(_HttpUtils.formatDate(expires)); | |
602 } | |
603 if (maxAge != null) { | |
604 sb.add("; Max-Age="); | |
605 sb.add(maxAge); | |
606 } | |
607 if (domain != null) { | |
608 sb.add("; Domain="); | |
609 sb.add(domain); | |
610 } | |
611 if (path != null) { | |
612 sb.add("; Path="); | |
613 sb.add(path); | |
614 } | |
615 if (secure) sb.add("; Secure"); | |
616 if (httpOnly) sb.add("; HttpOnly"); | |
617 return sb.toString(); | |
618 } | |
619 | |
620 String name; | |
621 String value; | |
622 Date expires; | |
623 int maxAge; | |
624 String domain; | |
625 String path; | |
626 bool httpOnly = false; | |
627 bool secure = false; | |
628 } | |
629 | |
630 | |
631 // The close queue handles graceful closing of HTTP connections. When | 5 // The close queue handles graceful closing of HTTP connections. When |
632 // a connection is added to the queue it will enter a wait state | 6 // a connection is added to the queue it will enter a wait state |
633 // waiting for all data written and possibly socket shutdown from | 7 // waiting for all data written and possibly socket shutdown from |
634 // peer. | 8 // peer. |
635 class _CloseQueue { | 9 class _CloseQueue { |
636 _CloseQueue() : _q = new Set<_HttpConnectionBase>(); | 10 _CloseQueue() : _q = new Set<_HttpConnectionBase>(); |
637 | 11 |
638 void add(_HttpConnectionBase connection) { | 12 void add(_HttpConnectionBase connection) { |
639 void closeIfDone() { | 13 void closeIfDone() { |
640 // We only check for write closed here. This means that we are | 14 // We only check for write closed here. This means that we are |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
694 _q.forEach((_HttpConnectionBase connection) { | 68 _q.forEach((_HttpConnectionBase connection) { |
695 connection._socket.close(); | 69 connection._socket.close(); |
696 }); | 70 }); |
697 } | 71 } |
698 | 72 |
699 final Set<_HttpConnectionBase> _q; | 73 final Set<_HttpConnectionBase> _q; |
700 } | 74 } |
701 | 75 |
702 | 76 |
703 class _HttpRequestResponseBase { | 77 class _HttpRequestResponseBase { |
704 final int START = 0; | 78 static const int START = 0; |
705 final int HEADER_SENT = 1; | 79 static const int HEADER_SENT = 1; |
706 final int DONE = 2; | 80 static const int DONE = 2; |
707 final int UPGRADED = 3; | 81 static const int UPGRADED = 3; |
708 | 82 |
709 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) | 83 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) |
710 : _headers = new _HttpHeaders() { | 84 : _state = START, _headResponse = false; |
711 _state = START; | |
712 _headResponse = false; | |
713 } | |
714 | 85 |
715 int get contentLength => _contentLength; | 86 int get contentLength => _contentLength; |
716 HttpHeaders get headers => _headers; | 87 HttpHeaders get headers => _headers; |
717 | 88 |
718 bool get persistentConnection { | 89 bool get persistentConnection { |
719 List<String> connection = headers[HttpHeaders.CONNECTION]; | 90 List<String> connection = headers[HttpHeaders.CONNECTION]; |
720 if (_protocolVersion == "1.1") { | 91 if (_protocolVersion == "1.1") { |
721 if (connection == null) return true; | 92 if (connection == null) return true; |
722 return !headers[HttpHeaders.CONNECTION].some( | 93 return !headers[HttpHeaders.CONNECTION].some( |
723 (value) => value.toLowerCase() == "close"); | 94 (value) => value.toLowerCase() == "close"); |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
951 HttpSession session([init(HttpSession session)]) { | 322 HttpSession session([init(HttpSession session)]) { |
952 if (_session != null) { | 323 if (_session != null) { |
953 // It's already mapped, use it. | 324 // It's already mapped, use it. |
954 return _session; | 325 return _session; |
955 } | 326 } |
956 // Create session, store it in connection, and return. | 327 // Create session, store it in connection, and return. |
957 var sessionManager = _httpConnection._server._sessionManager; | 328 var sessionManager = _httpConnection._server._sessionManager; |
958 return _session = sessionManager.createSession(init); | 329 return _session = sessionManager.createSession(init); |
959 } | 330 } |
960 | 331 |
961 void _onRequestStart(String method, String uri, String version) { | 332 void _onRequestReceived(String method, |
| 333 String uri, |
| 334 String version, |
| 335 _HttpHeaders headers) { |
962 _method = method; | 336 _method = method; |
963 _uri = uri; | 337 _uri = uri; |
964 _parseRequestUri(uri); | 338 _parseRequestUri(uri); |
965 } | 339 _headers = headers; |
966 | |
967 void _onHeaderReceived(String name, String value) { | |
968 _headers.add(name, value); | |
969 } | |
970 | |
971 void _onHeadersComplete() { | |
972 if (_httpConnection._server._sessionManagerInstance != null) { | 340 if (_httpConnection._server._sessionManagerInstance != null) { |
973 // Map to session if exists. | 341 // Map to session if exists. |
974 var sessionId = cookies.reduce(null, (last, cookie) { | 342 var sessionId = cookies.reduce(null, (last, cookie) { |
975 if (last != null) return last; | 343 if (last != null) return last; |
976 return cookie.name.toUpperCase() == _DART_SESSION_ID ? | 344 return cookie.name.toUpperCase() == _DART_SESSION_ID ? |
977 cookie.value : null; | 345 cookie.value : null; |
978 }); | 346 }); |
979 if (sessionId != null) { | 347 if (sessionId != null) { |
980 var sessionManager = _httpConnection._server._sessionManager; | 348 var sessionManager = _httpConnection._server._sessionManager; |
981 _session = sessionManager.getSession(sessionId); | 349 _session = sessionManager.getSession(sessionId); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1048 _BufferList _buffer; | 416 _BufferList _buffer; |
1049 Function _streamErrorHandler; | 417 Function _streamErrorHandler; |
1050 _HttpSession _session; | 418 _HttpSession _session; |
1051 } | 419 } |
1052 | 420 |
1053 | 421 |
1054 // HTTP response object for sending a HTTP response. | 422 // HTTP response object for sending a HTTP response. |
1055 class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { | 423 class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
1056 _HttpResponse(_HttpConnection httpConnection) | 424 _HttpResponse(_HttpConnection httpConnection) |
1057 : super(httpConnection), | 425 : super(httpConnection), |
1058 _statusCode = HttpStatus.OK; | 426 _statusCode = HttpStatus.OK { |
| 427 _headers = new _HttpHeaders(); |
| 428 } |
1059 | 429 |
1060 void set contentLength(int contentLength) { | 430 void set contentLength(int contentLength) { |
1061 if (_state >= HEADER_SENT) throw new HttpException("Header already sent"); | 431 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
| 432 throw new HttpException("Header already sent"); |
| 433 } |
1062 _contentLength = contentLength; | 434 _contentLength = contentLength; |
1063 } | 435 } |
1064 | 436 |
1065 int get statusCode => _statusCode; | 437 int get statusCode => _statusCode; |
1066 void set statusCode(int statusCode) { | 438 void set statusCode(int statusCode) { |
1067 if (_outputStream != null) throw new HttpException("Header already sent"); | 439 if (_outputStream != null) throw new HttpException("Header already sent"); |
1068 _statusCode = statusCode; | 440 _statusCode = statusCode; |
1069 } | 441 } |
1070 | 442 |
1071 String get reasonPhrase => _findReasonPhrase(_statusCode); | 443 String get reasonPhrase => _findReasonPhrase(_statusCode); |
1072 void set reasonPhrase(String reasonPhrase) { | 444 void set reasonPhrase(String reasonPhrase) { |
1073 if (_outputStream != null) throw new HttpException("Header already sent"); | 445 if (_outputStream != null) throw new HttpException("Header already sent"); |
1074 _reasonPhrase = reasonPhrase; | 446 _reasonPhrase = reasonPhrase; |
1075 } | 447 } |
1076 | 448 |
1077 List<Cookie> get cookies { | 449 List<Cookie> get cookies { |
1078 if (_cookies == null) _cookies = new List<Cookie>(); | 450 if (_cookies == null) _cookies = new List<Cookie>(); |
1079 return _cookies; | 451 return _cookies; |
1080 } | 452 } |
1081 | 453 |
1082 OutputStream get outputStream { | 454 OutputStream get outputStream { |
1083 if (_state >= DONE) throw new HttpException("Response closed"); | 455 if (_state >= _HttpRequestResponseBase.DONE) { |
| 456 throw new HttpException("Response closed"); |
| 457 } |
1084 if (_outputStream == null) { | 458 if (_outputStream == null) { |
1085 _outputStream = new _HttpOutputStream(this); | 459 _outputStream = new _HttpOutputStream(this); |
1086 } | 460 } |
1087 return _outputStream; | 461 return _outputStream; |
1088 } | 462 } |
1089 | 463 |
1090 DetachedSocket detachSocket() { | 464 DetachedSocket detachSocket() { |
1091 if (_state >= DONE) throw new HttpException("Response closed"); | 465 if (_state >= _HttpRequestResponseBase.DONE) { |
| 466 throw new HttpException("Response closed"); |
| 467 } |
1092 // Ensure that headers are written. | 468 // Ensure that headers are written. |
1093 if (_state == START) { | 469 if (_state == _HttpRequestResponseBase.START) { |
1094 _writeHeader(); | 470 _writeHeader(); |
1095 } | 471 } |
1096 _state = UPGRADED; | 472 _state = _HttpRequestResponseBase.UPGRADED; |
1097 // Ensure that any trailing data is written. | 473 // Ensure that any trailing data is written. |
1098 _writeDone(); | 474 _writeDone(); |
1099 // Indicate to the connection that the response handling is done. | 475 // Indicate to the connection that the response handling is done. |
1100 return _httpConnection._detachSocket(); | 476 return _httpConnection._detachSocket(); |
1101 } | 477 } |
1102 | 478 |
1103 // Delegate functions for the HttpOutputStream implementation. | 479 // Delegate functions for the HttpOutputStream implementation. |
1104 bool _streamWrite(List<int> buffer, bool copyBuffer) { | 480 bool _streamWrite(List<int> buffer, bool copyBuffer) { |
1105 if (_done) throw new HttpException("Response closed"); | 481 if (_done) throw new HttpException("Response closed"); |
1106 return _write(buffer, copyBuffer); | 482 return _write(buffer, copyBuffer); |
1107 } | 483 } |
1108 | 484 |
1109 bool _streamWriteFrom(List<int> buffer, int offset, int len) { | 485 bool _streamWriteFrom(List<int> buffer, int offset, int len) { |
1110 if (_done) throw new HttpException("Response closed"); | 486 if (_done) throw new HttpException("Response closed"); |
1111 return _writeList(buffer, offset, len); | 487 return _writeList(buffer, offset, len); |
1112 } | 488 } |
1113 | 489 |
1114 void _streamFlush() { | 490 void _streamFlush() { |
1115 _httpConnection._flush(); | 491 _httpConnection._flush(); |
1116 } | 492 } |
1117 | 493 |
1118 void _streamClose() { | 494 void _streamClose() { |
1119 _ensureHeadersSent(); | 495 _ensureHeadersSent(); |
1120 _state = DONE; | 496 _state = _HttpRequestResponseBase.DONE; |
1121 // Stop tracking no pending write events. | 497 // Stop tracking no pending write events. |
1122 _httpConnection._onNoPendingWrites = null; | 498 _httpConnection._onNoPendingWrites = null; |
1123 // Ensure that any trailing data is written. | 499 // Ensure that any trailing data is written. |
1124 _writeDone(); | 500 _writeDone(); |
1125 // Indicate to the connection that the response handling is done. | 501 // Indicate to the connection that the response handling is done. |
1126 _httpConnection._responseClosed(); | 502 _httpConnection._responseClosed(); |
1127 } | 503 } |
1128 | 504 |
1129 void _streamSetNoPendingWriteHandler(callback()) { | 505 void _streamSetNoPendingWriteHandler(callback()) { |
1130 if (_state != DONE) { | 506 if (_state != _HttpRequestResponseBase.DONE) { |
1131 _httpConnection._onNoPendingWrites = callback; | 507 _httpConnection._onNoPendingWrites = callback; |
1132 } | 508 } |
1133 } | 509 } |
1134 | 510 |
1135 void _streamSetCloseHandler(callback()) { | 511 void _streamSetCloseHandler(callback()) { |
1136 // TODO(sgjesse): Handle this. | 512 // TODO(sgjesse): Handle this. |
1137 } | 513 } |
1138 | 514 |
1139 void _streamSetErrorHandler(callback(e)) { | 515 void _streamSetErrorHandler(callback(e)) { |
1140 _streamErrorHandler = callback; | 516 _streamErrorHandler = callback; |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1239 } | 615 } |
1240 // Add all the cookies set to the headers. | 616 // Add all the cookies set to the headers. |
1241 if (_cookies != null) { | 617 if (_cookies != null) { |
1242 _cookies.forEach((cookie) { | 618 _cookies.forEach((cookie) { |
1243 _headers.add("set-cookie", cookie); | 619 _headers.add("set-cookie", cookie); |
1244 }); | 620 }); |
1245 } | 621 } |
1246 | 622 |
1247 // Write headers. | 623 // Write headers. |
1248 bool allWritten = _writeHeaders(); | 624 bool allWritten = _writeHeaders(); |
1249 _state = HEADER_SENT; | 625 _state = _HttpRequestResponseBase.HEADER_SENT; |
1250 return allWritten; | 626 return allWritten; |
1251 } | 627 } |
1252 | 628 |
1253 int _statusCode; // Response status code. | 629 int _statusCode; // Response status code. |
1254 String _reasonPhrase; // Response reason phrase. | 630 String _reasonPhrase; // Response reason phrase. |
1255 _HttpOutputStream _outputStream; | 631 _HttpOutputStream _outputStream; |
1256 Function _streamErrorHandler; | 632 Function _streamErrorHandler; |
1257 } | 633 } |
1258 | 634 |
1259 | 635 |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1437 final int hashCode; | 813 final int hashCode; |
1438 static int _nextHashCode = 0; | 814 static int _nextHashCode = 0; |
1439 } | 815 } |
1440 | 816 |
1441 | 817 |
1442 // HTTP server connection over a socket. | 818 // HTTP server connection over a socket. |
1443 class _HttpConnection extends _HttpConnectionBase { | 819 class _HttpConnection extends _HttpConnectionBase { |
1444 _HttpConnection(HttpServer this._server) { | 820 _HttpConnection(HttpServer this._server) { |
1445 _httpParser = new _HttpParser.requestParser(); | 821 _httpParser = new _HttpParser.requestParser(); |
1446 // Register HTTP parser callbacks. | 822 // Register HTTP parser callbacks. |
1447 _httpParser.requestStart = _onRequestStart; | 823 _httpParser.requestStart = _onRequestReceived; |
1448 _httpParser.headerReceived = _onHeaderReceived; | |
1449 _httpParser.headersComplete = _onHeadersComplete; | |
1450 _httpParser.dataReceived = _onDataReceived; | 824 _httpParser.dataReceived = _onDataReceived; |
1451 _httpParser.dataEnd = _onDataEnd; | 825 _httpParser.dataEnd = _onDataEnd; |
1452 _httpParser.error = _onError; | 826 _httpParser.error = _onError; |
1453 _httpParser.closed = _onClosed; | 827 _httpParser.closed = _onClosed; |
1454 _httpParser.responseStart = (statusCode, reasonPhrase, version) { | 828 _httpParser.responseStart = (statusCode, reasonPhrase, version) { |
1455 assert(false); | 829 assert(false); |
1456 }; | 830 }; |
1457 } | 831 } |
1458 | 832 |
1459 void _onClosed() { | 833 void _onClosed() { |
1460 _state |= _HttpConnectionBase.READ_CLOSED; | 834 _state |= _HttpConnectionBase.READ_CLOSED; |
1461 } | 835 } |
1462 | 836 |
1463 void _onError(e) { | 837 void _onError(e) { |
1464 onError(e); | 838 onError(e); |
1465 // Propagate the error to the streams. | 839 // Propagate the error to the streams. |
1466 if (_request != null && _request._streamErrorHandler != null) { | 840 if (_request != null && _request._streamErrorHandler != null) { |
1467 _request._streamErrorHandler(e); | 841 _request._streamErrorHandler(e); |
1468 } | 842 } |
1469 if (_response != null && _response._streamErrorHandler != null) { | 843 if (_response != null && _response._streamErrorHandler != null) { |
1470 _response._streamErrorHandler(e); | 844 _response._streamErrorHandler(e); |
1471 } | 845 } |
1472 if (_socket != null) _socket.close(); | 846 if (_socket != null) _socket.close(); |
1473 } | 847 } |
1474 | 848 |
1475 void _onRequestStart(String method, String uri, String version) { | 849 void _onRequestReceived(String method, |
| 850 String uri, |
| 851 String version, |
| 852 _HttpHeaders headers) { |
1476 _state = _HttpConnectionBase.ACTIVE; | 853 _state = _HttpConnectionBase.ACTIVE; |
1477 // Create new request and response objects for this request. | 854 // Create new request and response objects for this request. |
1478 _request = new _HttpRequest(this); | 855 _request = new _HttpRequest(this); |
1479 _response = new _HttpResponse(this); | 856 _response = new _HttpResponse(this); |
1480 _request._onRequestStart(method, uri, version); | 857 _request._onRequestReceived(method, uri, version, headers); |
1481 _request._protocolVersion = version; | 858 _request._protocolVersion = version; |
1482 _response._protocolVersion = version; | 859 _response._protocolVersion = version; |
1483 _response._headResponse = method == "HEAD"; | 860 _response._headResponse = method == "HEAD"; |
1484 } | |
1485 | |
1486 void _onHeaderReceived(String name, String value) { | |
1487 _request._onHeaderReceived(name, value); | |
1488 } | |
1489 | |
1490 void _onHeadersComplete() { | |
1491 _request._onHeadersComplete(); | |
1492 _response.persistentConnection = _httpParser.persistentConnection; | 861 _response.persistentConnection = _httpParser.persistentConnection; |
1493 if (onRequestReceived != null) { | 862 if (onRequestReceived != null) { |
1494 onRequestReceived(_request, _response); | 863 onRequestReceived(_request, _response); |
1495 } | 864 } |
1496 } | 865 } |
1497 | 866 |
1498 void _onDataReceived(List<int> data) { | 867 void _onDataReceived(List<int> data) { |
1499 _request._onDataReceived(data); | 868 _request._onDataReceived(data); |
1500 } | 869 } |
1501 | 870 |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 _HttpSessionManager _sessionManagerInstance; | 1035 _HttpSessionManager _sessionManagerInstance; |
1667 } | 1036 } |
1668 | 1037 |
1669 | 1038 |
1670 class _HttpClientRequest | 1039 class _HttpClientRequest |
1671 extends _HttpRequestResponseBase implements HttpClientRequest { | 1040 extends _HttpRequestResponseBase implements HttpClientRequest { |
1672 _HttpClientRequest(String this._method, | 1041 _HttpClientRequest(String this._method, |
1673 Uri this._uri, | 1042 Uri this._uri, |
1674 _HttpClientConnection connection) | 1043 _HttpClientConnection connection) |
1675 : super(connection) { | 1044 : super(connection) { |
| 1045 _headers = new _HttpHeaders(); |
1676 _connection = connection; | 1046 _connection = connection; |
1677 // Default GET and HEAD requests to have no content. | 1047 // Default GET and HEAD requests to have no content. |
1678 if (_method == "GET" || _method == "HEAD") { | 1048 if (_method == "GET" || _method == "HEAD") { |
1679 _contentLength = 0; | 1049 _contentLength = 0; |
1680 } | 1050 } |
1681 } | 1051 } |
1682 | 1052 |
1683 void set contentLength(int contentLength) { | 1053 void set contentLength(int contentLength) { |
1684 if (_state >= HEADER_SENT) throw new HttpException("Header already sent"); | 1054 if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
| 1055 throw new HttpException("Header already sent"); |
| 1056 } |
1685 _contentLength = contentLength; | 1057 _contentLength = contentLength; |
1686 } | 1058 } |
1687 | 1059 |
1688 List<Cookie> get cookies { | 1060 List<Cookie> get cookies { |
1689 if (_cookies == null) _cookies = new List<Cookie>(); | 1061 if (_cookies == null) _cookies = new List<Cookie>(); |
1690 return _cookies; | 1062 return _cookies; |
1691 } | 1063 } |
1692 | 1064 |
1693 OutputStream get outputStream { | 1065 OutputStream get outputStream { |
1694 if (_done) throw new HttpException("Request closed"); | 1066 if (_done) throw new HttpException("Request closed"); |
(...skipping 13 matching lines...) Expand all Loading... |
1708 if (_done) throw new HttpException("Request closed"); | 1080 if (_done) throw new HttpException("Request closed"); |
1709 return _writeList(buffer, offset, len); | 1081 return _writeList(buffer, offset, len); |
1710 } | 1082 } |
1711 | 1083 |
1712 void _streamFlush() { | 1084 void _streamFlush() { |
1713 _httpConnection._flush(); | 1085 _httpConnection._flush(); |
1714 } | 1086 } |
1715 | 1087 |
1716 void _streamClose() { | 1088 void _streamClose() { |
1717 _ensureHeadersSent(); | 1089 _ensureHeadersSent(); |
1718 _state = DONE; | 1090 _state = _HttpRequestResponseBase.DONE; |
1719 // Stop tracking no pending write events. | 1091 // Stop tracking no pending write events. |
1720 _httpConnection._onNoPendingWrites = null; | 1092 _httpConnection._onNoPendingWrites = null; |
1721 // Ensure that any trailing data is written. | 1093 // Ensure that any trailing data is written. |
1722 _writeDone(); | 1094 _writeDone(); |
1723 _connection._requestClosed(); | 1095 _connection._requestClosed(); |
1724 } | 1096 } |
1725 | 1097 |
1726 void _streamSetNoPendingWriteHandler(callback()) { | 1098 void _streamSetNoPendingWriteHandler(callback()) { |
1727 if (_state != DONE) { | 1099 if (_state != _HttpRequestResponseBase.DONE) { |
1728 _httpConnection._onNoPendingWrites = callback; | 1100 _httpConnection._onNoPendingWrites = callback; |
1729 } | 1101 } |
1730 } | 1102 } |
1731 | 1103 |
1732 void _streamSetCloseHandler(callback()) { | 1104 void _streamSetCloseHandler(callback()) { |
1733 // TODO(sgjesse): Handle this. | 1105 // TODO(sgjesse): Handle this. |
1734 } | 1106 } |
1735 | 1107 |
1736 void _streamSetErrorHandler(callback(e)) { | 1108 void _streamSetErrorHandler(callback(e)) { |
1737 _streamErrorHandler = callback; | 1109 _streamErrorHandler = callback; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1781 if (i > 0) sb.add("; "); | 1153 if (i > 0) sb.add("; "); |
1782 sb.add(_cookies[i].name); | 1154 sb.add(_cookies[i].name); |
1783 sb.add("="); | 1155 sb.add("="); |
1784 sb.add(_cookies[i].value); | 1156 sb.add(_cookies[i].value); |
1785 } | 1157 } |
1786 _headers.add("cookie", sb.toString()); | 1158 _headers.add("cookie", sb.toString()); |
1787 } | 1159 } |
1788 | 1160 |
1789 // Write headers. | 1161 // Write headers. |
1790 _writeHeaders(); | 1162 _writeHeaders(); |
1791 _state = HEADER_SENT; | 1163 _state = _HttpRequestResponseBase.HEADER_SENT; |
1792 } | 1164 } |
1793 | 1165 |
1794 String _method; | 1166 String _method; |
1795 Uri _uri; | 1167 Uri _uri; |
1796 _HttpClientConnection _connection; | 1168 _HttpClientConnection _connection; |
1797 _HttpOutputStream _outputStream; | 1169 _HttpOutputStream _outputStream; |
1798 Function _streamErrorHandler; | 1170 Function _streamErrorHandler; |
1799 } | 1171 } |
1800 | 1172 |
1801 | 1173 |
(...skipping 26 matching lines...) Expand all Loading... |
1828 return _cookies; | 1200 return _cookies; |
1829 } | 1201 } |
1830 | 1202 |
1831 InputStream get inputStream { | 1203 InputStream get inputStream { |
1832 if (_inputStream == null) { | 1204 if (_inputStream == null) { |
1833 _inputStream = new _HttpInputStream(this); | 1205 _inputStream = new _HttpInputStream(this); |
1834 } | 1206 } |
1835 return _inputStream; | 1207 return _inputStream; |
1836 } | 1208 } |
1837 | 1209 |
1838 void _onResponseStart(int statusCode, String reasonPhrase, String version) { | 1210 void _onResponseReceived(int statusCode, |
| 1211 String reasonPhrase, |
| 1212 String version, |
| 1213 _HttpHeaders headers) { |
1839 _statusCode = statusCode; | 1214 _statusCode = statusCode; |
1840 _reasonPhrase = reasonPhrase; | 1215 _reasonPhrase = reasonPhrase; |
1841 } | 1216 _headers = headers; |
| 1217 // Get parsed content length. |
| 1218 _contentLength = _httpConnection._httpParser.contentLength; |
1842 | 1219 |
1843 void _onHeaderReceived(String name, String value) { | 1220 // Prepare for receiving data. |
1844 _headers.add(name, value); | 1221 _headers._mutable = false; |
| 1222 _buffer = new _BufferList(); |
| 1223 |
| 1224 if (isRedirect && _connection.followRedirects) { |
| 1225 if (_connection._redirects == null || |
| 1226 _connection._redirects.length < _connection.maxRedirects) { |
| 1227 // Check the location header. |
| 1228 List<String> location = headers[HttpHeaders.LOCATION]; |
| 1229 if (location == null || location.length > 1) { |
| 1230 throw new RedirectException("Invalid redirect", |
| 1231 _connection._redirects); |
| 1232 } |
| 1233 // Check for redirect loop |
| 1234 if (_connection._redirects != null) { |
| 1235 Uri redirectUrl = new Uri.fromString(location[0]); |
| 1236 for (int i = 0; i < _connection._redirects.length; i++) { |
| 1237 if (_connection._redirects[i].location.toString() == |
| 1238 redirectUrl.toString()) { |
| 1239 throw new RedirectLoopException(_connection._redirects); |
| 1240 } |
| 1241 } |
| 1242 } |
| 1243 // Drain body and redirect. |
| 1244 inputStream.onData = inputStream.read; |
| 1245 _connection.redirect(); |
| 1246 } else { |
| 1247 throw new RedirectLimitExceededException(_connection._redirects); |
| 1248 } |
| 1249 } else if (statusCode == HttpStatus.UNAUTHORIZED) { |
| 1250 _handleUnauthorized(); |
| 1251 } else if (_connection._onResponse != null) { |
| 1252 _connection._onResponse(this); |
| 1253 } |
1845 } | 1254 } |
1846 | 1255 |
1847 void _handleUnauthorized() { | 1256 void _handleUnauthorized() { |
1848 | 1257 |
1849 void retryRequest(_Credentials cr) { | 1258 void retryRequest(_Credentials cr) { |
1850 if (cr != null) { | 1259 if (cr != null) { |
1851 // Drain body and retry. | 1260 // Drain body and retry. |
1852 // TODO(sgjesse): Support digest. | 1261 // TODO(sgjesse): Support digest. |
1853 if (cr.scheme == _AuthenticationScheme.BASIC) { | 1262 if (cr.scheme == _AuthenticationScheme.BASIC) { |
1854 inputStream.onData = inputStream.read; | 1263 inputStream.onData = inputStream.read; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1910 } | 1319 } |
1911 } | 1320 } |
1912 | 1321 |
1913 // Fall through to here to perform normal response handling if | 1322 // Fall through to here to perform normal response handling if |
1914 // there is no sensible authorization handling. | 1323 // there is no sensible authorization handling. |
1915 if (_connection._onResponse != null) { | 1324 if (_connection._onResponse != null) { |
1916 _connection._onResponse(this); | 1325 _connection._onResponse(this); |
1917 } | 1326 } |
1918 } | 1327 } |
1919 | 1328 |
1920 void _onHeadersComplete() { | |
1921 // Get parsed content length. | |
1922 _contentLength = _httpConnection._httpParser.contentLength; | |
1923 | |
1924 // Prepare for receiving data. | |
1925 _headers._mutable = false; | |
1926 _buffer = new _BufferList(); | |
1927 | |
1928 if (isRedirect && _connection.followRedirects) { | |
1929 if (_connection._redirects == null || | |
1930 _connection._redirects.length < _connection.maxRedirects) { | |
1931 // Check the location header. | |
1932 List<String> location = headers[HttpHeaders.LOCATION]; | |
1933 if (location == null || location.length > 1) { | |
1934 throw new RedirectException("Invalid redirect", | |
1935 _connection._redirects); | |
1936 } | |
1937 // Check for redirect loop | |
1938 if (_connection._redirects != null) { | |
1939 Uri redirectUrl = new Uri.fromString(location[0]); | |
1940 for (int i = 0; i < _connection._redirects.length; i++) { | |
1941 if (_connection._redirects[i].location.toString() == | |
1942 redirectUrl.toString()) { | |
1943 throw new RedirectLoopException(_connection._redirects); | |
1944 } | |
1945 } | |
1946 } | |
1947 // Drain body and redirect. | |
1948 inputStream.onData = inputStream.read; | |
1949 _connection.redirect(); | |
1950 } else { | |
1951 throw new RedirectLimitExceededException(_connection._redirects); | |
1952 } | |
1953 } else if (statusCode == HttpStatus.UNAUTHORIZED) { | |
1954 _handleUnauthorized(); | |
1955 } else if (_connection._onResponse != null) { | |
1956 _connection._onResponse(this); | |
1957 } | |
1958 } | |
1959 | |
1960 void _onDataReceived(List<int> data) { | 1329 void _onDataReceived(List<int> data) { |
1961 _buffer.add(data); | 1330 _buffer.add(data); |
1962 if (_inputStream != null) _inputStream._dataReceived(); | 1331 if (_inputStream != null) _inputStream._dataReceived(); |
1963 } | 1332 } |
1964 | 1333 |
1965 void _onDataEnd() { | 1334 void _onDataEnd() { |
1966 if (_inputStream != null) { | 1335 if (_inputStream != null) { |
1967 _inputStream._closeReceived(); | 1336 _inputStream._closeReceived(); |
1968 } else { | 1337 } else { |
1969 inputStream._streamMarkedClosed = true; | 1338 inputStream._streamMarkedClosed = true; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2004 extends _HttpConnectionBase implements HttpClientConnection { | 1373 extends _HttpConnectionBase implements HttpClientConnection { |
2005 | 1374 |
2006 _HttpClientConnection(_HttpClient this._client) { | 1375 _HttpClientConnection(_HttpClient this._client) { |
2007 _httpParser = new _HttpParser.responseParser(); | 1376 _httpParser = new _HttpParser.responseParser(); |
2008 } | 1377 } |
2009 | 1378 |
2010 void _connectionEstablished(_SocketConnection socketConn) { | 1379 void _connectionEstablished(_SocketConnection socketConn) { |
2011 super._connectionEstablished(socketConn._socket); | 1380 super._connectionEstablished(socketConn._socket); |
2012 _socketConn = socketConn; | 1381 _socketConn = socketConn; |
2013 // Register HTTP parser callbacks. | 1382 // Register HTTP parser callbacks. |
2014 _httpParser.responseStart = _onResponseStart; | 1383 _httpParser.responseStart = _onResponseReceived; |
2015 _httpParser.headerReceived = _onHeaderReceived; | |
2016 _httpParser.headersComplete = _onHeadersComplete; | |
2017 _httpParser.dataReceived = _onDataReceived; | 1384 _httpParser.dataReceived = _onDataReceived; |
2018 _httpParser.dataEnd = _onDataEnd; | 1385 _httpParser.dataEnd = _onDataEnd; |
2019 _httpParser.error = _onError; | 1386 _httpParser.error = _onError; |
2020 _httpParser.closed = _onClosed; | 1387 _httpParser.closed = _onClosed; |
2021 _httpParser.requestStart = (method, uri, version) { assert(false); }; | 1388 _httpParser.requestStart = (method, uri, version) { assert(false); }; |
2022 _state = _HttpConnectionBase.ACTIVE; | 1389 _state = _HttpConnectionBase.ACTIVE; |
2023 } | 1390 } |
2024 | 1391 |
2025 void _checkSocketDone() { | 1392 void _checkSocketDone() { |
2026 if (_isAllDone) { | 1393 if (_isAllDone) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2079 } | 1446 } |
2080 // Propagate the error to the streams. | 1447 // Propagate the error to the streams. |
2081 if (_response != null && _response._streamErrorHandler != null) { | 1448 if (_response != null && _response._streamErrorHandler != null) { |
2082 _response._streamErrorHandler(e); | 1449 _response._streamErrorHandler(e); |
2083 } | 1450 } |
2084 if (_socketConn != null) { | 1451 if (_socketConn != null) { |
2085 _client._closeSocketConnection(_socketConn); | 1452 _client._closeSocketConnection(_socketConn); |
2086 } | 1453 } |
2087 } | 1454 } |
2088 | 1455 |
2089 void _onResponseStart(int statusCode, String reasonPhrase, String version) { | 1456 void _onResponseReceived(int statusCode, |
2090 _response._onResponseStart(statusCode, reasonPhrase, version); | 1457 String reasonPhrase, |
2091 } | 1458 String version, |
2092 | 1459 _HttpHeaders headers) { |
2093 void _onHeaderReceived(String name, String value) { | 1460 _response._onResponseReceived(statusCode, reasonPhrase, version, headers); |
2094 _response._onHeaderReceived(name, value); | |
2095 } | |
2096 | |
2097 void _onHeadersComplete() { | |
2098 _response._onHeadersComplete(); | |
2099 } | 1461 } |
2100 | 1462 |
2101 void _onDataReceived(List<int> data) { | 1463 void _onDataReceived(List<int> data) { |
2102 _response._onDataReceived(data); | 1464 _response._onDataReceived(data); |
2103 } | 1465 } |
2104 | 1466 |
2105 void _onDataEnd(bool close) { | 1467 void _onDataEnd(bool close) { |
2106 _response._onDataEnd(); | 1468 _response._onDataEnd(); |
2107 _state |= _HttpConnectionBase.RESPONSE_DONE; | 1469 _state |= _HttpConnectionBase.RESPONSE_DONE; |
2108 _checkSocketDone(); | 1470 _checkSocketDone(); |
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2711 | 2073 |
2712 | 2074 |
2713 class _RedirectInfo implements RedirectInfo { | 2075 class _RedirectInfo implements RedirectInfo { |
2714 const _RedirectInfo(int this.statusCode, | 2076 const _RedirectInfo(int this.statusCode, |
2715 String this.method, | 2077 String this.method, |
2716 Uri this.location); | 2078 Uri this.location); |
2717 final int statusCode; | 2079 final int statusCode; |
2718 final String method; | 2080 final String method; |
2719 final Uri location; | 2081 final Uri location; |
2720 } | 2082 } |
OLD | NEW |