def file_ranges(): # Apache compatibility: yield ntob("\r\n") for start, stop in r: if debug: cherrypy.log( 'Multipart; start: %r, stop: %r' % ( start, stop), 'TOOLS.STATIC') yield ntob("--" + boundary, 'ascii') yield ntob("\r\nContent-type: %s" % content_type, 'ascii') yield ntob( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % ( start, stop - 1, content_length), 'ascii') fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield ntob("\r\n") # Final boundary yield ntob("--" + boundary + "--", 'ascii') # Apache compatibility: yield ntob("\r\n")
def file_ranges(): # Apache compatibility: yield ntob('\r\n') for start, stop in r: if debug: cherrypy.log( 'Multipart; start: %r, stop: %r' % ( start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob( '\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % ( start, stop - 1, content_length), 'ascii') fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield ntob('\r\n') # Final boundary yield ntob('--' + boundary + '--', 'ascii') # Apache compatibility: yield ntob('\r\n')
def _serve_fileobj(fileobj, content_type, content_length, debug = False): response = cherrypy.serving.response request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers['Accept-Ranges'] = 'bytes' r = httputil.get_ranges(request.headers.get('Range'), content_length) if r == []: response.headers['Content-Range'] = 'bytes */%s' % content_length message = 'Invalid Range (first-byte-pos greater than Content-Length)' if debug: cherrypy.log(message, 'TOOLS.STATIC') raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log('Single part; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') response.status = '206 Partial Content' response.headers['Content-Range'] = 'bytes %s-%s/%s' % (start, stop - 1, content_length) response.headers['Content-Length'] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: response.status = '206 Partial Content' from mimetools import choose_boundary boundary = choose_boundary() ct = 'multipart/byteranges; boundary=%s' % boundary response.headers['Content-Type'] = ct if 'Content-Length' in response.headers: del response.headers['Content-Length'] def file_ranges(): yield ntob('\r\n') for start, stop in r: if debug: cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob('\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % (start, stop - 1, content_length), 'ascii') fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob('\r\n') yield ntob('--' + boundary + '--', 'ascii') yield ntob('\r\n') response.body = file_ranges() return response.body if debug: cherrypy.log('No byteranges requested', 'TOOLS.STATIC') response.headers['Content-Length'] = content_length response.body = fileobj return response.body
def file_ranges(): for start, stop in r: yield to_unicode("--" + boundary) yield to_unicode("\r\nContent-type: {0}".format(content_type)) yield to_unicode( "\r\nContent-range: bytes {0}-{1}/{2}\r\n\r\n".format( start, stop - 1, content_length)) fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield to_unicode("\r\n") yield to_unicode("--" + boundary + "--")
def file_ranges(): yield ntob('\r\n') for start, stop in r: if debug: cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob('\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % (start, stop - 1, content_length), 'ascii') fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob('\r\n') yield ntob('--' + boundary + '--', 'ascii') yield ntob('\r\n')
def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, c_len)) bodyfile.seek(start) for chunk in file_generator_limited(bodyfile, stop - start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n"
def file_ranges(): for start, stop in r: yield to_unicode("--" + boundary) yield to_unicode("\r\nContent-type: {0}".format(content_type)) yield to_unicode( "\r\nContent-range: bytes {0}-{1}/{2}\r\n\r\n".format( start, stop - 1, content_length ) ) fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield to_unicode("\r\n") yield to_unicode("--" + boundary + "--")
def file_ranges(): yield ntob("\r\n") for start, stop in r: if debug: cherrypy.log("Multipart; start: %r, stop: %r" % (start, stop), "TOOLS.STATIC") yield ntob("--" + boundary, "ascii") yield ntob("\r\nContent-type: %s" % content_type, "ascii") yield ntob( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, content_length), "ascii" ) fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob("\r\n") yield ntob("--" + boundary + "--", "ascii") yield ntob("\r\n")
def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, c_len)) bodyfile.seek(start) for chunk in file_generator_limited( bodyfile, stop - start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n"
def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: if debug: cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, content_length)) fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop-start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n"
def _serve_fileobj(fileobj, content_type, content_length, debug=False): """Internal. Set response.body to the given file object, perhaps ranged.""" response = cherrypy.serving.response # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(request.headers.get('Range'), content_length) if r == []: response.headers['Content-Range'] = "bytes */%s" % content_length message = ("Invalid Range (first-byte-pos greater than " "Content-Length)") if debug: cherrypy.log(message, 'TOOLS.STATIC') raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log( 'Single part; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') response.status = "206 Partial Content" response.headers['Content-Range'] = ( "bytes %s-%s/%s" % (start, stop - 1, content_length)) response.headers['Content-Length'] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" try: # Python 3 from email.generator import _make_boundary as make_boundary except ImportError: # Python 2 from mimetools import choose_boundary as make_boundary boundary = make_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers['Content-Type'] = ct if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] def file_ranges(): # Apache compatibility: yield ntob("\r\n") for start, stop in r: if debug: cherrypy.log( 'Multipart; start: %r, stop: %r' % ( start, stop), 'TOOLS.STATIC') yield ntob("--" + boundary, 'ascii') yield ntob("\r\nContent-type: %s" % content_type, 'ascii') yield ntob( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % ( start, stop - 1, content_length), 'ascii') fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield ntob("\r\n") # Final boundary yield ntob("--" + boundary + "--", 'ascii') # Apache compatibility: yield ntob("\r\n") response.body = file_ranges() return response.body else: if debug: cherrypy.log('No byteranges requested', 'TOOLS.STATIC') # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory response.headers['Content-Length'] = content_length response.body = fileobj return response.body
def serve_file(path, content_type=None, disposition=None, name=None): """Set status, headers, and body in order to serve the given file. The Content-Type header will be set to the content_type arg, if provided. If not provided, the Content-Type will be guessed by the file extension of the 'path' argument. If disposition is not None, the Content-Disposition header will be set to "<disposition>; filename=<name>". If name is None, it will be set to the basename of path. If disposition is None, no Content-Disposition header will be written. """ response = cherrypy.response # If path is relative, users should fix it by making path absolute. # That is, CherryPy should not guess where the application root is. # It certainly should *not* use cwd (since CP may be invoked from a # variety of paths). If using tools.static, you can make your relative # paths become absolute by supplying a value for "tools.static.root". if not os.path.isabs(path): raise ValueError("'%s' is not an absolute path." % path) try: st = os.stat(path) except OSError: raise cherrypy.NotFound() # Check if path is a directory. if stat.S_ISDIR(st.st_mode): # Let the caller deal with it as they like. raise cherrypy.NotFound() # Set the Last-Modified response header, so that # modified-since validation code can work. response.headers["Last-Modified"] = http.HTTPDate(st.st_mtime) cptools.validate_since() if content_type is None: # Set content-type based on filename extension ext = "" i = path.rfind(".") if i != -1: ext = path[i:].lower() content_type = mimetypes.types_map.get(ext, "text/plain") response.headers["Content-Type"] = content_type if disposition is not None: if name is None: name = os.path.basename(path) cd = '%s; filename="%s"' % (disposition, name) response.headers["Content-Disposition"] = cd # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory c_len = st.st_size bodyfile = open(path, "rb") # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code if cherrypy.request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = http.get_ranges(cherrypy.request.headers.get("Range"), c_len) if r == []: response.headers["Content-Range"] = "bytes */%s" % c_len message = "Invalid Range (first-byte-pos greater than Content-Length)" raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > c_len: stop = c_len r_len = stop - start response.status = "206 Partial Content" response.headers["Content-Range"] = "bytes %s-%s/%s" % (start, stop - 1, c_len) response.headers["Content-Length"] = r_len bodyfile.seek(start) response.body = file_generator_limited(bodyfile, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" import mimetools boundary = mimetools.choose_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers["Content-Type"] = ct if response.headers.has_key("Content-Length"): # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, c_len)) bodyfile.seek(start) for chunk in file_generator_limited(bodyfile, stop - start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n" response.body = file_ranges() else: response.headers["Content-Length"] = c_len response.body = bodyfile else: response.headers["Content-Length"] = c_len response.body = bodyfile return response.body
def serve_fileobj(fileobj, headers, content_length): status_code = 200 headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(headers.get('Range'), content_length) if r == []: headers['Content-Range'] = "bytes */{0}".format(content_length) message = "Invalid Range (first-byte-pos greater than Content-Length)" raise RequestedRangeNotSatisfiable(message) if not r: headers['Content-Length'] = content_length return fileobj, headers, status_code # Return a multipart/byteranges response. status_code = 206 if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start headers['Content-Range'] = "bytes {0}-{1}/{2}".format( start, stop - 1, content_length ) headers['Content-Length'] = r_len fileobj.seek(start) body = file_generator_limited(fileobj, r_len) return body, headers, status_code try: # Python 3 from email.generator import _make_boundary as make_boundary except ImportError: # Python 2 from mimetools import choose_boundary as make_boundary boundary = make_boundary() content_type = "multipart/byteranges; boundary={0}".format(boundary) headers['Content-Type'] = content_type if "Content-Length" in headers: del headers["Content-Length"] def file_ranges(): for start, stop in r: yield to_unicode("--" + boundary) yield to_unicode("\r\nContent-type: {0}".format(content_type)) yield to_unicode( "\r\nContent-range: bytes {0}-{1}/{2}\r\n\r\n".format( start, stop - 1, content_length ) ) fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield to_unicode("\r\n") yield to_unicode("--" + boundary + "--") body = file_ranges() return body, headers, status_code
def _serve_fileobj(fileobj, content_type, content_length, debug=False): """Internal. Set response.body to the given file object, perhaps ranged.""" response = cherrypy.serving.response # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(request.headers.get("Range"), content_length) if r == []: response.headers["Content-Range"] = "bytes */%s" % content_length message = "Invalid Range (first-byte-pos greater than Content-Length)" if debug: cherrypy.log(message, "TOOLS.STATIC") raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log("Single part; start: %r, stop: %r" % (start, stop), "TOOLS.STATIC") response.status = "206 Partial Content" response.headers["Content-Range"] = "bytes %s-%s/%s" % (start, stop - 1, content_length) response.headers["Content-Length"] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" try: # Python 3 from email.generator import _make_boundary as choose_boundary except ImportError: # Python 2 from mimetools import choose_boundary boundary = choose_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers["Content-Type"] = ct if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] def file_ranges(): # Apache compatibility: yield ntob("\r\n") for start, stop in r: if debug: cherrypy.log("Multipart; start: %r, stop: %r" % (start, stop), "TOOLS.STATIC") yield ntob("--" + boundary, "ascii") yield ntob("\r\nContent-type: %s" % content_type, "ascii") yield ntob( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, content_length), "ascii" ) fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob("\r\n") # Final boundary yield ntob("--" + boundary + "--", "ascii") # Apache compatibility: yield ntob("\r\n") response.body = file_ranges() return response.body else: if debug: cherrypy.log("No byteranges requested", "TOOLS.STATIC") # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory response.headers["Content-Length"] = content_length response.body = fileobj return response.body
def _serve_fileobj(fileobj, content_type, content_length, debug=False): """Internal. Set response.body to the given file object, perhaps ranged.""" response = cherrypy.serving.response # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers['Accept-Ranges'] = 'bytes' r = httputil.get_ranges(request.headers.get('Range'), content_length) if r == []: response.headers['Content-Range'] = 'bytes */%s' % content_length message = ('Invalid Range (first-byte-pos greater than ' 'Content-Length)') if debug: cherrypy.log(message, 'TOOLS.STATIC') raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log( 'Single part; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') response.status = '206 Partial Content' response.headers['Content-Range'] = ( 'bytes %s-%s/%s' % (start, stop - 1, content_length)) response.headers['Content-Length'] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: # Return a multipart/byteranges response. response.status = '206 Partial Content' boundary = make_boundary() ct = 'multipart/byteranges; boundary=%s' % boundary response.headers['Content-Type'] = ct if 'Content-Length' in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers['Content-Length'] def file_ranges(): # Apache compatibility: yield ntob('\r\n') for start, stop in r: if debug: cherrypy.log( 'Multipart; start: %r, stop: %r' % ( start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob( '\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % ( start, stop - 1, content_length), 'ascii') fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield ntob('\r\n') # Final boundary yield ntob('--' + boundary + '--', 'ascii') # Apache compatibility: yield ntob('\r\n') response.body = file_ranges() return response.body else: if debug: cherrypy.log('No byteranges requested', 'TOOLS.STATIC') # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory response.headers['Content-Length'] = content_length response.body = fileobj return response.body
def serve_file(path, content_type=None, disposition=None, name=None): """Set status, headers, and body in order to serve the given file. The Content-Type header will be set to the content_type arg, if provided. If not provided, the Content-Type will be guessed by the file extension of the 'path' argument. If disposition is not None, the Content-Disposition header will be set to "<disposition>; filename=<name>". If name is None, it will be set to the basename of path. If disposition is None, no Content-Disposition header will be written. """ response = cherrypy.response # If path is relative, users should fix it by making path absolute. # That is, CherryPy should not guess where the application root is. # It certainly should *not* use cwd (since CP may be invoked from a # variety of paths). If using tools.staticdir, you can make your relative # paths become absolute by supplying a value for "tools.staticdir.root". if not os.path.isabs(path): raise ValueError("'%s' is not an absolute path." % path) try: st = os.stat(path) except OSError: raise cherrypy.NotFound() # Check if path is a directory. if stat.S_ISDIR(st.st_mode): # Let the caller deal with it as they like. raise cherrypy.NotFound() # Set the Last-Modified response header, so that # modified-since validation code can work. response.headers['Last-Modified'] = http.HTTPDate(st.st_mtime) cptools.validate_since() if content_type is None: # Set content-type based on filename extension ext = "" i = path.rfind('.') if i != -1: ext = path[i:].lower() content_type = mimetypes.types_map.get(ext, "text/plain") response.headers['Content-Type'] = content_type if disposition is not None: if name is None: name = os.path.basename(path) cd = '%s; filename="%s"' % (disposition, name) response.headers["Content-Disposition"] = cd # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory c_len = st.st_size bodyfile = open(path, 'rb') # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code if cherrypy.request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = http.get_ranges(cherrypy.request.headers.get('Range'), c_len) if r == []: response.headers['Content-Range'] = "bytes */%s" % c_len message = "Invalid Range (first-byte-pos greater than Content-Length)" raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > c_len: stop = c_len r_len = stop - start response.status = "206 Partial Content" response.headers['Content-Range'] = ("bytes %s-%s/%s" % (start, stop - 1, c_len)) response.headers['Content-Length'] = r_len bodyfile.seek(start) response.body = file_generator_limited(bodyfile, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" import mimetools boundary = mimetools.choose_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers['Content-Type'] = ct if response.headers.has_key("Content-Length"): # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, c_len)) bodyfile.seek(start) for chunk in file_generator_limited( bodyfile, stop - start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n" response.body = file_ranges() return response.body response.headers['Content-Length'] = c_len response.body = bodyfile return response.body
def _serve_fileobj(fileobj, content_type, content_length, debug=False): response = cherrypy.serving.response request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(request.headers.get("Range"), content_length) if r == []: response.headers["Content-Range"] = "bytes */%s" % content_length message = "Invalid Range (first-byte-pos greater than Content-Length)" if debug: cherrypy.log(message, "TOOLS.STATIC") raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log("Single part; start: %r, stop: %r" % (start, stop), "TOOLS.STATIC") response.status = "206 Partial Content" response.headers["Content-Range"] = "bytes %s-%s/%s" % (start, stop - 1, content_length) response.headers["Content-Length"] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: response.status = "206 Partial Content" from mimetools import choose_boundary boundary = choose_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers["Content-Type"] = ct if "Content-Length" in response.headers: del response.headers["Content-Length"] def file_ranges(): yield ntob("\r\n") for start, stop in r: if debug: cherrypy.log("Multipart; start: %r, stop: %r" % (start, stop), "TOOLS.STATIC") yield ntob("--" + boundary, "ascii") yield ntob("\r\nContent-type: %s" % content_type, "ascii") yield ntob( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, content_length), "ascii" ) fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob("\r\n") yield ntob("--" + boundary + "--", "ascii") yield ntob("\r\n") response.body = file_ranges() return response.body if debug: cherrypy.log("No byteranges requested", "TOOLS.STATIC") response.headers["Content-Length"] = content_length response.body = fileobj return response.body
def serve_fileobj(fileobj, headers, content_length): status_code = 200 headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(headers.get('Range'), content_length) if r == []: headers['Content-Range'] = "bytes */{0}".format(content_length) message = "Invalid Range (first-byte-pos greater than Content-Length)" raise RequestedRangeNotSatisfiable(message) if not r: headers['Content-Length'] = content_length return fileobj, headers, status_code # Return a multipart/byteranges response. status_code = 206 if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start headers['Content-Range'] = "bytes {0}-{1}/{2}".format( start, stop - 1, content_length) headers['Content-Length'] = r_len fileobj.seek(start) body = file_generator_limited(fileobj, r_len) return body, headers, status_code try: # Python 3 from email.generator import _make_boundary as make_boundary except ImportError: # Python 2 from mimetools import choose_boundary as make_boundary boundary = make_boundary() content_type = "multipart/byteranges; boundary={0}".format(boundary) headers['Content-Type'] = content_type if "Content-Length" in headers: del headers["Content-Length"] def file_ranges(): for start, stop in r: yield to_unicode("--" + boundary) yield to_unicode("\r\nContent-type: {0}".format(content_type)) yield to_unicode( "\r\nContent-range: bytes {0}-{1}/{2}\r\n\r\n".format( start, stop - 1, content_length)) fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield to_unicode("\r\n") yield to_unicode("--" + boundary + "--") body = file_ranges() return body, headers, status_code
def serve_raw_file(self, file, content_type=None, disposition=None, name=None): # Adapted from CherryPy's serve_file(), modified to work with file-like # objects response = cherrypy.response st = None if isinstance(file, str): path = file if not os.path.isabs(path): raise ValueError("'%s' is not an absolute path." % path) try: st = os.stat(path) except OSError: raise cherrypy.NotFound() if stat.S_ISDIR(st.st_mode): raise cherrypy.NotFound() response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime) cptools.validate_since() file = open(path, "rb") else: path = getattr(file, "name", None) if path: try: st = os.stat(path) except OSError: pass else: if not hasattr(file, "read"): raise ValueError( "Expected a file-like object, got %r instead " "(object has no read() method)" % file) if not hasattr(file, "seek"): raise ValueError("Can't serve file-like object %r " "(object has no seek() method)" % file) if not hasattr(file, "tell"): raise ValueError("Can't serve file-like object %r " "(object has no tell() method)" % file) # Set the content type if content_type is None: if path: content_type = mimetypes.guess_type(path)[0] if not content_type: content_type = "text/plain" response.headers["Content-Type"] = content_type # Set the content disposition if disposition is not None: cd = disposition if not name and path: name = os.path.basename(path) if name: cd = rfc6266.build_header(name, cd) response.headers["Content-Disposition"] = cd if self.use_xsendfile and path: response.headers["X-Sendfile"] = path return "" # Find the size of the file if st is None: start = file.tell() file.seek(0, 2) # Move to the end of the file c_len = file.tell() - start file.seek(start) else: c_len = st.st_size # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code if cherrypy.request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(cherrypy.request.headers.get('Range'), c_len) if r == []: response.headers['Content-Range'] = "bytes */%s" % c_len message = "Invalid Range (first-byte-pos greater than Content-Length)" raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > c_len: stop = c_len r_len = stop - start response.status = "206 Partial Content" response.headers['Content-Range'] = ( "bytes %s-%s/%s" % (start, stop - 1, c_len)) response.headers['Content-Length'] = r_len file.seek(start) response.body = file_generator_limited(file, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" import mimetools boundary = mimetools.choose_boundary() ct = "multipart/byteranges; boundary=%s" % boundary response.headers['Content-Type'] = ct if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] def file_ranges(): # Apache compatibility: yield "\r\n" for start, stop in r: yield "--" + boundary yield "\r\nContent-type: %s" % content_type yield ( "\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, c_len)) file.seek(start) for chunk in file_generator_limited( file, stop - start): yield chunk yield "\r\n" # Final boundary yield "--" + boundary + "--" # Apache compatibility: yield "\r\n" response.body = file_ranges() else: response.headers['Content-Length'] = c_len response.body = file else: response.headers['Content-Length'] = c_len response.body = file return response.body