1from collections import OrderedDict 2from datetime import datetime, timedelta 3from io import BytesIO 4import json 5import uuid 6 7from hpack.struct import HeaderTuple 8from http.cookies import BaseCookie, Morsel 9from hyperframe.frame import HeadersFrame, DataFrame, ContinuationFrame 10 11from .constants import response_codes, h2_headers 12from .logger import get_logger 13from .utils import isomorphic_decode, isomorphic_encode 14 15missing = object() 16 17 18class Response(object): 19 """Object representing the response to a HTTP request 20 21 :param handler: RequestHandler being used for this response 22 :param request: Request that this is the response for 23 24 .. attribute:: request 25 26 Request associated with this Response. 27 28 .. attribute:: encoding 29 30 The encoding to use when converting unicode to strings for output. 31 32 .. attribute:: add_required_headers 33 34 Boolean indicating whether mandatory headers should be added to the 35 response. 36 37 .. attribute:: send_body_for_head_request 38 39 Boolean, default False, indicating whether the body content should be 40 sent when the request method is HEAD. 41 42 .. attribute:: writer 43 44 The ResponseWriter for this response 45 46 .. attribute:: status 47 48 Status tuple (code, message). Can be set to an integer in which case the 49 message part is filled in automatically, or a tuple (code, message) in 50 which case code is an int and message is a text or binary string. 51 52 .. attribute:: headers 53 54 List of HTTP headers to send with the response. Each item in the list is a 55 tuple of (name, value). 56 57 .. attribute:: content 58 59 The body of the response. This can either be a string or a iterable of response 60 parts. If it is an iterable, any item may be a string or a function of zero 61 parameters which, when called, returns a string.""" 62 63 def __init__(self, handler, request, response_writer_cls=None): 64 self.request = request 65 self.encoding = "utf8" 66 67 self.add_required_headers = True 68 self.send_body_for_head_request = False 69 self.close_connection = False 70 71 self.logger = get_logger() 72 self.writer = response_writer_cls(handler, self) if response_writer_cls else ResponseWriter(handler, self) 73 74 self._status = (200, None) 75 self.headers = ResponseHeaders() 76 self.content = [] 77 78 @property 79 def status(self): 80 return self._status 81 82 @status.setter 83 def status(self, value): 84 if hasattr(value, "__len__"): 85 if len(value) != 2: 86 raise ValueError 87 else: 88 code = int(value[0]) 89 message = value[1] 90 # Only call str() if message is not a string type, so that we 91 # don't get `str(b"foo") == "b'foo'"` in Python 3. 92 if not isinstance(message, (bytes, str)): 93 message = str(message) 94 self._status = (code, message) 95 else: 96 self._status = (int(value), None) 97 98 def set_cookie(self, name, value, path="/", domain=None, max_age=None, 99 expires=None, secure=False, httponly=False, comment=None): 100 """Set a cookie to be sent with a Set-Cookie header in the 101 response 102 103 :param name: name of the cookie (a binary string) 104 :param value: value of the cookie (a binary string, or None) 105 :param max_age: datetime.timedelta int representing the time (in seconds) 106 until the cookie expires 107 :param path: String path to which the cookie applies 108 :param domain: String domain to which the cookie applies 109 :param secure: Boolean indicating whether the cookie is marked as secure 110 :param httponly: Boolean indicating whether the cookie is marked as 111 HTTP Only 112 :param comment: String comment 113 :param expires: datetime.datetime or datetime.timedelta indicating a 114 time or interval from now when the cookie expires 115 116 """ 117 # TODO(Python 3): Convert other parameters (e.g. path) to bytes, too. 118 if value is None: 119 value = b'' 120 max_age = 0 121 expires = timedelta(days=-1) 122 123 name = isomorphic_decode(name) 124 value = isomorphic_decode(value) 125 126 days = {i+1: name for i, name in enumerate(["jan", "feb", "mar", 127 "apr", "may", "jun", 128 "jul", "aug", "sep", 129 "oct", "nov", "dec"])} 130 131 if isinstance(expires, timedelta): 132 expires = datetime.utcnow() + expires 133 134 if expires is not None: 135 expires_str = expires.strftime("%d %%s %Y %H:%M:%S GMT") 136 expires_str = expires_str % days[expires.month] 137 expires = expires_str 138 139 if max_age is not None: 140 if hasattr(max_age, "total_seconds"): 141 max_age = int(max_age.total_seconds()) 142 max_age = "%.0d" % max_age 143 144 m = Morsel() 145 146 def maybe_set(key, value): 147 if value is not None and value is not False: 148 m[key] = value 149 150 m.set(name, value, value) 151 maybe_set("path", path) 152 maybe_set("domain", domain) 153 maybe_set("comment", comment) 154 maybe_set("expires", expires) 155 maybe_set("max-age", max_age) 156 maybe_set("secure", secure) 157 maybe_set("httponly", httponly) 158 159 self.headers.append("Set-Cookie", m.OutputString()) 160 161 def unset_cookie(self, name): 162 """Remove a cookie from those that are being sent with the response""" 163 name = isomorphic_decode(name) 164 cookies = self.headers.get("Set-Cookie") 165 parser = BaseCookie() 166 for cookie in cookies: 167 parser.load(isomorphic_decode(cookie)) 168 169 if name in parser.keys(): 170 del self.headers["Set-Cookie"] 171 for m in parser.values(): 172 if m.key != name: 173 self.headers.append(("Set-Cookie", m.OutputString())) 174 175 def delete_cookie(self, name, path="/", domain=None): 176 """Delete a cookie on the client by setting it to the empty string 177 and to expire in the past""" 178 self.set_cookie(name, None, path=path, domain=domain, max_age=0, 179 expires=timedelta(days=-1)) 180 181 def iter_content(self, read_file=False): 182 """Iterator returning chunks of response body content. 183 184 If any part of the content is a function, this will be called 185 and the resulting value (if any) returned. 186 187 :param read_file: boolean controlling the behaviour when content is a 188 file handle. When set to False the handle will be 189 returned directly allowing the file to be passed to 190 the output in small chunks. When set to True, the 191 entire content of the file will be returned as a 192 string facilitating non-streaming operations like 193 template substitution. 194 """ 195 if isinstance(self.content, bytes): 196 yield self.content 197 elif isinstance(self.content, str): 198 yield self.content.encode(self.encoding) 199 elif hasattr(self.content, "read"): 200 if read_file: 201 yield self.content.read() 202 else: 203 yield self.content 204 else: 205 for item in self.content: 206 if hasattr(item, "__call__"): 207 value = item() 208 else: 209 value = item 210 if value: 211 yield value 212 213 def write_status_headers(self): 214 """Write out the status line and headers for the response""" 215 self.writer.write_status(*self.status) 216 for item in self.headers: 217 self.writer.write_header(*item) 218 self.writer.end_headers() 219 220 def write_content(self): 221 """Write out the response content""" 222 if self.request.method != "HEAD" or self.send_body_for_head_request: 223 for item in self.iter_content(): 224 self.writer.write_content(item) 225 226 def write(self): 227 """Write the whole response""" 228 self.write_status_headers() 229 self.write_content() 230 231 def set_error(self, code, message=u""): 232 """Set the response status headers and return a JSON error object: 233 234 {"error": {"code": code, "message": message}} 235 code is an int (HTTP status code), and message is a text string. 236 """ 237 err = {"code": code, 238 "message": message} 239 data = json.dumps({"error": err}) 240 self.status = code 241 self.headers = [("Content-Type", "application/json"), 242 ("Content-Length", len(data))] 243 self.content = data 244 if code == 500: 245 if isinstance(message, str) and message: 246 first_line = message.splitlines()[0] 247 else: 248 first_line = "<no message given>" 249 self.logger.error("Exception loading %s: %s" % (self.request.url, 250 first_line)) 251 self.logger.info(message) 252 253 254class MultipartContent(object): 255 def __init__(self, boundary=None, default_content_type=None): 256 self.items = [] 257 if boundary is None: 258 boundary = str(uuid.uuid4()) 259 self.boundary = boundary 260 self.default_content_type = default_content_type 261 262 def __call__(self): 263 boundary = b"--" + self.boundary.encode("ascii") 264 rv = [b"", boundary] 265 for item in self.items: 266 rv.append(item.to_bytes()) 267 rv.append(boundary) 268 rv[-1] += b"--" 269 return b"\r\n".join(rv) 270 271 def append_part(self, data, content_type=None, headers=None): 272 if content_type is None: 273 content_type = self.default_content_type 274 self.items.append(MultipartPart(data, content_type, headers)) 275 276 def __iter__(self): 277 #This is hackish; when writing the response we need an iterable 278 #or a string. For a multipart/byterange response we want an 279 #iterable that contains a single callable; the MultipartContent 280 #object itself 281 yield self 282 283 284class MultipartPart(object): 285 def __init__(self, data, content_type=None, headers=None): 286 assert isinstance(data, bytes), data 287 self.headers = ResponseHeaders() 288 289 if content_type is not None: 290 self.headers.set("Content-Type", content_type) 291 292 if headers is not None: 293 for name, value in headers: 294 if name.lower() == b"content-type": 295 func = self.headers.set 296 else: 297 func = self.headers.append 298 func(name, value) 299 300 self.data = data 301 302 def to_bytes(self): 303 rv = [] 304 for key, value in self.headers: 305 assert isinstance(key, bytes) 306 assert isinstance(value, bytes) 307 rv.append(b"%s: %s" % (key, value)) 308 rv.append(b"") 309 rv.append(self.data) 310 return b"\r\n".join(rv) 311 312 313def _maybe_encode(s): 314 """Encode a string or an int into binary data using isomorphic_encode().""" 315 if isinstance(s, int): 316 return b"%i" % (s,) 317 return isomorphic_encode(s) 318 319 320class ResponseHeaders(object): 321 """Dictionary-like object holding the headers for the response""" 322 def __init__(self): 323 self.data = OrderedDict() 324 325 def set(self, key, value): 326 """Set a header to a specific value, overwriting any previous header 327 with the same name 328 329 :param key: Name of the header to set 330 :param value: Value to set the header to 331 """ 332 key = _maybe_encode(key) 333 value = _maybe_encode(value) 334 self.data[key.lower()] = (key, [value]) 335 336 def append(self, key, value): 337 """Add a new header with a given name, not overwriting any existing 338 headers with the same name 339 340 :param key: Name of the header to add 341 :param value: Value to set for the header 342 """ 343 key = _maybe_encode(key) 344 value = _maybe_encode(value) 345 if key.lower() in self.data: 346 self.data[key.lower()][1].append(value) 347 else: 348 self.set(key, value) 349 350 def get(self, key, default=missing): 351 """Get the set values for a particular header.""" 352 key = _maybe_encode(key) 353 try: 354 return self[key] 355 except KeyError: 356 if default is missing: 357 return [] 358 return default 359 360 def __getitem__(self, key): 361 """Get a list of values for a particular header 362 363 """ 364 key = _maybe_encode(key) 365 return self.data[key.lower()][1] 366 367 def __delitem__(self, key): 368 key = _maybe_encode(key) 369 del self.data[key.lower()] 370 371 def __contains__(self, key): 372 key = _maybe_encode(key) 373 return key.lower() in self.data 374 375 def __setitem__(self, key, value): 376 self.set(key, value) 377 378 def __iter__(self): 379 for key, values in self.data.values(): 380 for value in values: 381 yield key, value 382 383 def items(self): 384 return list(self) 385 386 def update(self, items_iter): 387 for name, value in items_iter: 388 self.append(name, value) 389 390 def __repr__(self): 391 return repr(self.data) 392 393 394class H2Response(Response): 395 396 def __init__(self, handler, request): 397 super(H2Response, self).__init__(handler, request, response_writer_cls=H2ResponseWriter) 398 399 def write_status_headers(self): 400 self.writer.write_headers(self.headers, *self.status) 401 402 # Hacky way of detecting last item in generator 403 def write_content(self): 404 """Write out the response content""" 405 if self.request.method != "HEAD" or self.send_body_for_head_request: 406 item = None 407 item_iter = self.iter_content() 408 try: 409 item = next(item_iter) 410 while True: 411 check_last = next(item_iter) 412 self.writer.write_data(item, last=False) 413 item = check_last 414 except StopIteration: 415 if item: 416 self.writer.write_data(item, last=True) 417 418 419class H2ResponseWriter(object): 420 421 def __init__(self, handler, response): 422 self.socket = handler.request 423 self.h2conn = handler.conn 424 self._response = response 425 self._handler = handler 426 self.stream_ended = False 427 self.content_written = False 428 self.request = response.request 429 self.logger = response.logger 430 431 def write_headers(self, headers, status_code, status_message=None, stream_id=None, last=False): 432 """ 433 Send a HEADER frame that is tracked by the local state machine. 434 435 Write a HEADER frame using the H2 Connection object, will only work if the stream is in a state to send 436 HEADER frames. 437 438 :param headers: List of (header, value) tuples 439 :param status_code: The HTTP status code of the response 440 :param stream_id: Id of stream to send frame on. Will use the request stream ID if None 441 :param last: Flag to signal if this is the last frame in stream. 442 """ 443 formatted_headers = [] 444 secondary_headers = [] # Non ':' prefixed headers are to be added afterwards 445 446 for header, value in headers: 447 # h2_headers are native strings 448 # header field names are strings of ASCII 449 if isinstance(header, bytes): 450 header = header.decode('ascii') 451 # value in headers can be either string or integer 452 if isinstance(value, bytes): 453 value = self.decode(value) 454 if header in h2_headers: 455 header = ':' + header 456 formatted_headers.append((header, str(value))) 457 else: 458 secondary_headers.append((header, str(value))) 459 460 formatted_headers.append((':status', str(status_code))) 461 formatted_headers.extend(secondary_headers) 462 463 with self.h2conn as connection: 464 connection.send_headers( 465 stream_id=self.request.h2_stream_id if stream_id is None else stream_id, 466 headers=formatted_headers, 467 end_stream=last or self.request.method == "HEAD" 468 ) 469 470 self.write(connection) 471 472 def write_data(self, item, last=False, stream_id=None): 473 """ 474 Send a DATA frame that is tracked by the local state machine. 475 476 Write a DATA frame using the H2 Connection object, will only work if the stream is in a state to send 477 DATA frames. Uses flow control to split data into multiple data frames if it exceeds the size that can 478 be in a single frame. 479 480 :param item: The content of the DATA frame 481 :param last: Flag to signal if this is the last frame in stream. 482 :param stream_id: Id of stream to send frame on. Will use the request stream ID if None 483 """ 484 if isinstance(item, (str, bytes)): 485 data = BytesIO(self.encode(item)) 486 else: 487 data = item 488 489 # Find the length of the data 490 data.seek(0, 2) 491 data_len = data.tell() 492 data.seek(0) 493 494 # If the data is longer than max payload size, need to write it in chunks 495 payload_size = self.get_max_payload_size() 496 while data_len > payload_size: 497 self.write_data_frame(data.read(payload_size), False, stream_id) 498 data_len -= payload_size 499 payload_size = self.get_max_payload_size() 500 501 self.write_data_frame(data.read(), last, stream_id) 502 503 def write_data_frame(self, data, last, stream_id=None): 504 with self.h2conn as connection: 505 connection.send_data( 506 stream_id=self.request.h2_stream_id if stream_id is None else stream_id, 507 data=data, 508 end_stream=last, 509 ) 510 self.write(connection) 511 self.stream_ended = last 512 513 def write_push(self, promise_headers, push_stream_id=None, status=None, response_headers=None, response_data=None): 514 """Write a push promise, and optionally write the push content. 515 516 This will write a push promise to the request stream. If you do not provide headers and data for the response, 517 then no response will be pushed, and you should push them yourself using the ID returned from this function 518 519 :param promise_headers: A list of header tuples that matches what the client would use to 520 request the pushed response 521 :param push_stream_id: The ID of the stream the response should be pushed to. If none given, will 522 use the next available id. 523 :param status: The status code of the response, REQUIRED if response_headers given 524 :param response_headers: The headers of the response 525 :param response_data: The response data. 526 :return: The ID of the push stream 527 """ 528 with self.h2conn as connection: 529 push_stream_id = push_stream_id if push_stream_id is not None else connection.get_next_available_stream_id() 530 connection.push_stream(self.request.h2_stream_id, push_stream_id, promise_headers) 531 self.write(connection) 532 533 has_data = response_data is not None 534 if response_headers is not None: 535 assert status is not None 536 self.write_headers(response_headers, status, stream_id=push_stream_id, last=not has_data) 537 538 if has_data: 539 self.write_data(response_data, last=True, stream_id=push_stream_id) 540 541 return push_stream_id 542 543 def end_stream(self, stream_id=None): 544 """Ends the stream with the given ID, or the one that request was made on if no ID given.""" 545 with self.h2conn as connection: 546 connection.end_stream(stream_id if stream_id is not None else self.request.h2_stream_id) 547 self.write(connection) 548 self.stream_ended = True 549 550 def write_raw_header_frame(self, headers, stream_id=None, end_stream=False, end_headers=False, frame_cls=HeadersFrame): 551 """ 552 Ignores the statemachine of the stream and sends a HEADER frame regardless. 553 554 Unlike `write_headers`, this does not check to see if a stream is in the correct state to have HEADER frames 555 sent through to it. It will build a HEADER frame and send it without using the H2 Connection object other than 556 to HPACK encode the headers. 557 558 :param headers: List of (header, value) tuples 559 :param stream_id: Id of stream to send frame on. Will use the request stream ID if None 560 :param end_stream: Set to True to add END_STREAM flag to frame 561 :param end_headers: Set to True to add END_HEADERS flag to frame 562 """ 563 if not stream_id: 564 stream_id = self.request.h2_stream_id 565 566 header_t = [] 567 for header, value in headers: 568 header_t.append(HeaderTuple(header, value)) 569 570 with self.h2conn as connection: 571 frame = frame_cls(stream_id, data=connection.encoder.encode(header_t)) 572 573 if end_stream: 574 self.stream_ended = True 575 frame.flags.add('END_STREAM') 576 if end_headers: 577 frame.flags.add('END_HEADERS') 578 579 data = frame.serialize() 580 self.write_raw(data) 581 582 def write_raw_data_frame(self, data, stream_id=None, end_stream=False): 583 """ 584 Ignores the statemachine of the stream and sends a DATA frame regardless. 585 586 Unlike `write_data`, this does not check to see if a stream is in the correct state to have DATA frames 587 sent through to it. It will build a DATA frame and send it without using the H2 Connection object. It will 588 not perform any flow control checks. 589 590 :param data: The data to be sent in the frame 591 :param stream_id: Id of stream to send frame on. Will use the request stream ID if None 592 :param end_stream: Set to True to add END_STREAM flag to frame 593 """ 594 if not stream_id: 595 stream_id = self.request.h2_stream_id 596 597 frame = DataFrame(stream_id, data=data) 598 599 if end_stream: 600 self.stream_ended = True 601 frame.flags.add('END_STREAM') 602 603 data = frame.serialize() 604 self.write_raw(data) 605 606 def write_raw_continuation_frame(self, headers, stream_id=None, end_headers=False): 607 """ 608 Ignores the statemachine of the stream and sends a CONTINUATION frame regardless. 609 610 This provides the ability to create and write a CONTINUATION frame to the stream, which is not exposed by 611 `write_headers` as the h2 library handles the split between HEADER and CONTINUATION internally. Will perform 612 HPACK encoding on the headers. 613 614 :param headers: List of (header, value) tuples 615 :param stream_id: Id of stream to send frame on. Will use the request stream ID if None 616 :param end_headers: Set to True to add END_HEADERS flag to frame 617 """ 618 self.write_raw_header_frame(headers, stream_id=stream_id, end_headers=end_headers, frame_cls=ContinuationFrame) 619 620 621 def get_max_payload_size(self, stream_id=None): 622 """Returns the maximum size of a payload for the given stream.""" 623 stream_id = stream_id if stream_id is not None else self.request.h2_stream_id 624 with self.h2conn as connection: 625 return min(connection.remote_settings.max_frame_size, connection.local_flow_control_window(stream_id)) - 9 626 627 def write(self, connection): 628 self.content_written = True 629 data = connection.data_to_send() 630 self.socket.sendall(data) 631 632 def write_raw(self, raw_data): 633 """Used for sending raw bytes/data through the socket""" 634 635 self.content_written = True 636 self.socket.sendall(raw_data) 637 638 def decode(self, data): 639 """Convert bytes to unicode according to response.encoding.""" 640 if isinstance(data, bytes): 641 return data.decode(self._response.encoding) 642 elif isinstance(data, str): 643 return data 644 else: 645 raise ValueError(type(data)) 646 647 def encode(self, data): 648 """Convert unicode to bytes according to response.encoding.""" 649 if isinstance(data, bytes): 650 return data 651 elif isinstance(data, str): 652 return data.encode(self._response.encoding) 653 else: 654 raise ValueError 655 656 657class ResponseWriter(object): 658 """Object providing an API to write out a HTTP response. 659 660 :param handler: The RequestHandler being used. 661 :param response: The Response associated with this writer.""" 662 def __init__(self, handler, response): 663 self._wfile = handler.wfile 664 self._response = response 665 self._handler = handler 666 self._status_written = False 667 self._headers_seen = set() 668 self._headers_complete = False 669 self.content_written = False 670 self.request = response.request 671 self.file_chunk_size = 32 * 1024 672 self.default_status = 200 673 674 def _seen_header(self, name): 675 return self.encode(name.lower()) in self._headers_seen 676 677 def write_status(self, code, message=None): 678 """Write out the status line of a response. 679 680 :param code: The integer status code of the response. 681 :param message: The message of the response. Defaults to the message commonly used 682 with the status code.""" 683 if message is None: 684 if code in response_codes: 685 message = response_codes[code][0] 686 else: 687 message = '' 688 self.write(b"%s %d %s\r\n" % 689 (isomorphic_encode(self._response.request.protocol_version), code, isomorphic_encode(message))) 690 self._status_written = True 691 692 def write_header(self, name, value): 693 """Write out a single header for the response. 694 695 If a status has not been written, a default status will be written (currently 200) 696 697 :param name: Name of the header field 698 :param value: Value of the header field 699 :return: A boolean indicating whether the write succeeds 700 """ 701 if not self._status_written: 702 self.write_status(self.default_status) 703 self._headers_seen.add(self.encode(name.lower())) 704 if not self.write(name): 705 return False 706 if not self.write(b": "): 707 return False 708 if isinstance(value, int): 709 if not self.write(str(value)): 710 return False 711 elif not self.write(value): 712 return False 713 return self.write(b"\r\n") 714 715 def write_default_headers(self): 716 for name, f in [("Server", self._handler.version_string), 717 ("Date", self._handler.date_time_string)]: 718 if not self._seen_header(name): 719 if not self.write_header(name, f()): 720 return False 721 722 if (isinstance(self._response.content, (bytes, str)) and 723 not self._seen_header("content-length")): 724 #Would be nice to avoid double-encoding here 725 if not self.write_header("Content-Length", len(self.encode(self._response.content))): 726 return False 727 728 return True 729 730 def end_headers(self): 731 """Finish writing headers and write the separator. 732 733 Unless add_required_headers on the response is False, 734 this will also add HTTP-mandated headers that have not yet been supplied 735 to the response headers. 736 :return: A boolean indicating whether the write succeeds 737 """ 738 739 if self._response.add_required_headers: 740 if not self.write_default_headers(): 741 return False 742 743 if not self.write("\r\n"): 744 return False 745 if not self._seen_header("content-length"): 746 self._response.close_connection = True 747 self._headers_complete = True 748 749 return True 750 751 def write_content(self, data): 752 """Write the body of the response. 753 754 HTTP-mandated headers will be automatically added with status default to 200 if they have 755 not been explicitly set. 756 :return: A boolean indicating whether the write succeeds 757 """ 758 if not self._status_written: 759 self.write_status(self.default_status) 760 if not self._headers_complete: 761 self._response.content = data 762 self.end_headers() 763 return self.write_raw_content(data) 764 765 def write_raw_content(self, data): 766 """Writes the data 'as is'""" 767 if data is None: 768 raise ValueError('data cannot be None') 769 if isinstance(data, (str, bytes)): 770 # Deliberately allows both text and binary types. See `self.encode`. 771 return self.write(data) 772 else: 773 return self.write_content_file(data) 774 775 def write(self, data): 776 """Write directly to the response, converting unicode to bytes 777 according to response.encoding. 778 :return: A boolean indicating whether the write succeeds 779 """ 780 self.content_written = True 781 try: 782 self._wfile.write(self.encode(data)) 783 return True 784 except OSError: 785 # This can happen if the socket got closed by the remote end 786 return False 787 788 def write_content_file(self, data): 789 """Write a file-like object directly to the response in chunks.""" 790 self.content_written = True 791 success = True 792 while True: 793 buf = data.read(self.file_chunk_size) 794 if not buf: 795 success = False 796 break 797 try: 798 self._wfile.write(buf) 799 except OSError: 800 success = False 801 break 802 data.close() 803 return success 804 805 def encode(self, data): 806 """Convert unicode to bytes according to response.encoding.""" 807 if isinstance(data, bytes): 808 return data 809 elif isinstance(data, str): 810 return data.encode(self._response.encoding) 811 else: 812 raise ValueError("data %r should be text or binary, but is %s" % (data, type(data))) 813