for line in lines:
if b'\n' in line:
raise ValueError('Newline in header: ' + repr(line))
+ future = None
if self.stream.closed():
- self._write_future = Future()
- self._write_future.set_exception(iostream.StreamClosedError())
+ future = self._write_future = Future()
+ future.set_exception(iostream.StreamClosedError())
else:
if callback is not None:
self._write_callback = stack_context.wrap(callback)
else:
- self._write_future = Future()
+ future = self._write_future = Future()
data = b"\r\n".join(lines) + b"\r\n\r\n"
if chunk:
data += self._format_chunk(chunk)
self._pending_write = self.stream.write(data)
self._pending_write.add_done_callback(self._on_write_complete)
- return self._write_future
+ return future
def _format_chunk(self, chunk):
if self._expected_content_remaining is not None:
cls._static_hashes = {}
def head(self, path):
- self.get(path, include_body=False)
+ return self.get(path, include_body=False)
+ @gen.coroutine
def get(self, path, include_body=True):
# Set up our path instance variables.
self.path = self.parse_url_path(path)
# the request will be treated as if the header didn't exist.
request_range = httputil._parse_request_range(range_header)
+ size = self.get_content_size()
if request_range:
start, end = request_range
- size = self.get_content_size()
if (start is not None and start >= size) or end == 0:
# As per RFC 2616 14.35.1, a range is not satisfiable only: if
# the first requested byte is equal to or greater than the
self.set_header("Content-Range",
httputil._get_content_range(start, end, size))
else:
- start = end = size = None
+ start = end = None
+
+ if start is not None and end is not None:
+ content_length = end - start
+ elif end is not None:
+ content_length = end
+ elif start is not None:
+ content_length = size - start
+ else:
+ content_length = size
+ self.set_header("Content-Length", content_length)
if include_body:
content = self.get_content(self.absolute_path, start, end)
content = [content]
for chunk in content:
self.write(chunk)
+ yield self.flush()
else:
assert self.request.method == "HEAD"
- if start is not None and end is not None:
- content_length = end - start
- elif end is not None:
- content_length = end
- else:
- if size is None:
- size = self.get_content_size()
- if start is not None:
- content_length = size - start
- else:
- content_length = size
- self.set_header("Content-Length", content_length)
def compute_etag(self):
"""Sets the ``Etag`` header based on static url version.
def get_content_size(self):
"""Retrieve the total size of the resource at the given path.
- This method may be overridden by subclasses. It will only
- be called if a partial result is requested from `get_content`,
- or on ``HEAD`` requests.
+ This method may be overridden by subclasses.
.. versionadded:: 3.1
+
+ .. versionchanged:: 3.3
+ This method is now always called, instead of only when
+ partial results are requested.
"""
stat_result = self._stat()
return stat_result[stat.ST_SIZE]
import sys
import tornado
+from tornado.concurrent import Future
from tornado import escape
from tornado import httputil
from tornado.log import access_log
return WSGIAdapter(self)(environ, start_response)
+# WSGI has no facilities for flow control, so just return an already-done
+# Future when the interface requires it.
+_dummy_future = Future()
+_dummy_future.set_result(None)
+
+
class _WSGIConnection(httputil.HTTPConnection):
def __init__(self, method, start_response, context):
self.method = method
self.write(chunk, callback)
elif callback is not None:
callback()
+ return _dummy_future
def write(self, chunk, callback=None):
if self._expected_content_remaining is not None:
self._write_buffer.append(chunk)
if callback is not None:
callback()
+ return _dummy_future
def finish(self):
if (self._expected_content_remaining is not None and