Package: bottle-fdsend
=============bottle-fdsend=============This package implements functions for constructing bottle's ``HTTPResponse``objects from file handles. Unlike the ``bottle.static_file()`` function,functions in this package allow for serving files constructed in memory, orunpacked from compressed archives into memory.Installation============To install bottle-fdsend, you can use pip or easy_install commands::pip install bottle-fdsendeasy_install bottle-fdsendBasic usage===========The function we will be using most of the time is the ``fdsend.send_file()``function.  At it's simplest, we simply pass a file-like object to thisfunction and it will return a ``bottle.HTTPResponse`` object.For example::from StringIO import StringIOfrom fdsend import send_filedef my_request_handler():s = StringIO('foo')return send_file(s)Because we are working with in-memory files and not physical files, however,none of the common response headers are set if we don't supply additionalmetadata about the file. In order to set Content-Type, Content-Length andsimilar headers, we need to pass a few optional arguments.Note that ``send_file()`` merely constructs a response object. We have toreturn it in order to have bottle serve it. We also need to keep in mind thatthe returned response object is a brand new one and anything we do to``bottle.response`` is not reflected on it. If we need to set additionalheaders and such, we must do so on the returned response object, not theglobal ``bottle.response`` object.Content type============There are two ways to set the Content-Type header. One is to pass the ctypeargument::def my_request_handler():....return send_file(s, ctype='text/html')Another method is to specify a filename with extension:def my_request_handler():...return send_file(s, filename='foo.html')If you pass both arguments, the ``ctype`` argument takes precedence when itcomes to Content-Type header.Character set and encoding==========================When we set the Content-Type header by means of passing a filename, the contenttype is automatically calculated based on file extension. In case of fileswhose MIME type (content type) starts with 'test/' (e.g., 'text/html','text/plain', and so on), character set is appended to the Content-Type header.For example, ``send_file(s, filename='foo.html')`` results in a Content-Typeheader that looks like this::Content-Type: text/html; charset=UTF-8The 'UTF-8' character set is the default. If our data is using a differentcharacter set, we need to explicitly specify it. ::def my_request_handler():...return send_file(s, filename='foo.html', charset='ascii')With above snippet, we get a different Content-Type header::Content-Type: text/html; charset=asciiAnother header gets automatically added depending on file extension, and thatis the Content-Encoding header. This applies to compressed files. For example::def my_request_handler():...return send_file(s, filename='foo.html.gz')The above snippet generates the following headers:Content-Type: text/html; charset=UTF-8Content-Encoding: gzipThe Content-Encoding header cannot be set manually, but it can be omitted bymanually passing the ``ctype`` argument.File size and byte serving==========================Because it would be wasteful to read from the file handle just to obtain thefile size, it is our responsibility to know in advance the size of our file andtell ``send_file()`` what size it should use. This is done by using the aptlynamed ``size`` parameter.def my_request_handler():...return send_file(s, size=2000)The size is in bytes, and when it is passed, two headers are added::Content-Length: 20000Accept-Range: bytesThe first header tells the client the size of the payload, and the secondheader announces we are able to do byte serving_. Byte serving is especiallyuseful when browsers want to retrieve portions of the files (e.g., resume adownload, load files in stages, like in video players, PDF extensions, etc).As programmers, we don't really need to do anything to take advantage of byteserving techniques: ``send_file()`` takes care of it. However, we do need toknow the total size of the file and pass it.Note that response when doing byte serving is 206, not 200.File timestamp and 304 Not Modified responses=============================================If you want the Last-Modified header to be set, you must pass the timestampargument. The timestamp must be in seconds since Unix epoch. ::def my_request_handler():...return send_file(s, timestamp=1429458831)The above timestamp will generate the following Last-Modified header::Last-Modified: Sun, 19 Apr 2015 15:53:51 GMTPassing the timestamp also causes ``send_file()`` to automatically return aHTTP 304 Not Modified response when client includes a valid``If-Modified-Since`` request header.Content-Disposition===================When Content-Disposition header is set to a value of 'attachment', most modernbrowsers will offer the user to download the file (by opening a downloaddialog, for instance) instead of trying to display the contents in the browserwindow. To set this header, we need to pass both the filename and the``attachment`` argument::def my_request_handler():...return send_file(s, filename='foo.html', attachment=True)Byte serving wrappers=====================Lastly, we can control how the ranges are returned from the file-like object inbyte serving.The simplest wrapper we can use is the bottle's own ``bottle._file_iter_range``generator function. This wrapper allows us to iterate over the desired rangeand return file data in chunks (1MB by default).While this works in most cases, it does not work for some types of file-likeobjects, such as file handles for ZIP file contents using DEFLATE compressionwhich do not allow ``seek()`` to be called on them. (Not the mention the factthat ``bottle._file_iter_range`` is not a public API and therefore subject tochange).This package provides two alternatives. One is``fdsend.rangewrapper.range_iter`` generator function and another is``fdsend.rangewrapper.RangeWrapper`` class.The generator function is similar to bottle's generator function, butspecifically designed to work around file-like objects that do not support``seek()``.The ``RangeWrapper`` is a bit different and it returns a file-like object thathas its own ``read()`` method which is restricted to the requested range.The primary difference between the two is whether ``wsgi.file_wrapper`` featureis used on not. This feature requires a file-like object to be passed in orderto be used.The default wrapper is ``fdsend.rangewrapper.range_iter``.It is also possible to write your own wrapper. The wrapper must be a callable(function, class, etc) and must accept the following positional arguments:- file handle- offset (in bytes from the start of the file)- length (size of the range in bytes)The return value must be a valid WSGI response body (string, iterable,file-like object).Feature requests and bug reports================================Please report all feature requests and bugs to our `issue tracker`_... _byte serving: https://en.wikipedia.org/wiki/Byte_serving.. _issue tracker: https://github.com/Outernet-Project/bottle-fdsend/issues