Example #1
0
    def testIfModSince(self):
        now = time.time()
        e = {'SERVER_NAME': 'foo',
             'SERVER_PORT': '80',
             'REQUEST_METHOD': 'GET'}

        # not modified since
        t_notmod = rfc1123_date(now)
        e['HTTP_IF_MODIFIED_SINCE'] = t_notmod
        out = BytesIO()
        resp = HTTPResponse(stdout=out)
        req = HTTPRequest(sys.stdin, e, resp)
        data = self.file.index_html(req, resp)
        self.assertEqual(resp.getStatus(), 304)
        self.assertEqual(data, b'')

        # modified since
        t_mod = rfc1123_date(now - 100)
        e['HTTP_IF_MODIFIED_SINCE'] = t_mod
        out = BytesIO()
        resp = HTTPResponse(stdout=out)
        req = HTTPRequest(sys.stdin, e, resp)
        data = self.file.index_html(req, resp)
        self.assertEqual(resp.getStatus(), 200)
        self.assertEqual(data, bytes(self.file.data))
 def __set_http_headers(self):
     """Set HTTP headers on the reply.
     """
     response = self.request.response
     expires = time.time() + CACHE_TIME
     # set response headers
     response.setHeader('Content-Type', self.context.content_type)
     response.setHeader('Content-Length', self.context.content_length)
     response.setHeader('Cache-Control', 'public,max-age=%s' % CACHE_TIME)
     response.setHeader('Last-Modified', rfc1123_date(self.context.lmt))
     response.setHeader('Expires', rfc1123_date(expires))
Example #3
0
 def __call__(self):
     # Download the asset.
     response = self.request.response
     response.setHeader(
         'Content-Disposition',
         'inline;filename=%s' % urllib.quote(
             self.context.filename.encode('utf-8')))
     response.setHeader(
         'Content-Length',
         '%s' % self.context.length)
     response.setHeader(
         'Content-Type',
         self.context.mimetype)
     # For caching, acquired one level.
     response.setHeader(
         'Last-Modified',
         rfc1123_date(self.context.modified()))
     # We don't support advanced streaming (yet).
     response.setHeader(
         'Accept-Ranges', None)
     if self.context.data is not None:
         return self.context.data
     # We have a stream, nice !
     chunk = self.context.stream.read(CHUNK_SIZE)
     while chunk:
         response.write(chunk)
         chunk = self.context.stream.read(CHUNK_SIZE)
     return ''
Example #4
0
 def _if_modified_since_request_handler(self, REQUEST, RESPONSE):
     # HTTP If-Modified-Since header handling: return True if
     # we can handle this request by returning a 304 response
     header = REQUEST.get_header('If-Modified-Since', None)
     if header is not None:
         header = header.split(';')[0]
         # Some proxies seem to send invalid date strings for this
         # header. If the date string is not valid, we ignore it
         # rather than raise an error to be generally consistent
         # with common servers such as Apache (which can usually
         # understand the screwy date string as a lucky side effect
         # of the way they parse it).
         # This happens to be what RFC2616 tells us to do in the face of an
         # invalid date.
         try:
             mod_since = int(DateTime(header).timeTime())
         except Exception:
             mod_since = None
         if mod_since is not None:
             if self._p_mtime:
                 last_mod = int(self._p_mtime)
             else:
                 last_mod = 0
             if last_mod > 0 and last_mod <= mod_since:
                 RESPONSE.setHeader('Last-Modified',
                                    rfc1123_date(self._p_mtime))
                 RESPONSE.setHeader('Content-Type', self.content_type)
                 RESPONSE.setHeader('Accept-Ranges', 'bytes')
                 RESPONSE.setStatus(304)
                 return True
Example #5
0
    def HEAD(self, REQUEST, RESPONSE):
        """Retrieve resource information without a response body."""
        self.dav__init(REQUEST, RESPONSE)

        content_type = None
        if hasattr(self, 'content_type'):
            content_type = absattr(self.content_type)
        if content_type is None:
            url = urlfix(REQUEST['URL'], 'HEAD')
            name = unquote([_f for _f in url.split('/') if _f][-1])
            content_type, encoding = mimetypes.guess_type(name)
        if content_type is None:
            if hasattr(self, 'default_content_type'):
                content_type = absattr(self.default_content_type)
        if content_type is None:
            content_type = 'application/octet-stream'
        RESPONSE.setHeader('Content-Type', content_type.lower())

        if hasattr(aq_base(self), 'get_size'):
            RESPONSE.setHeader('Content-Length', absattr(self.get_size))
        if hasattr(self, '_p_mtime'):
            mtime = rfc1123_date(self._p_mtime)
            RESPONSE.setHeader('Last-Modified', mtime)
        if hasattr(aq_base(self), 'http__etag'):
            etag = self.http__etag(readonly=1)
            if etag:
                RESPONSE.setHeader('Etag', etag)
        RESPONSE.setStatus(200)
        return RESPONSE
Example #6
0
    def index_html(self, REQUEST, RESPONSE):
        """
        The default view of the contents of a File or Image.

        Returns the contents of the file or image.  Also, sets the
        Content-Type HTTP header to the objects content type.
        """

        if self._if_modified_since_request_handler(REQUEST, RESPONSE):
            # we were able to handle this by returning a 304
            # unfortunately, because the HTTP cache manager uses the cache
            # API, and because 304 responses are required to carry the Expires
            # header for HTTP/1.1, we need to call ZCacheable_set here.
            # This is nonsensical for caches other than the HTTP cache manager
            # unfortunately.
            self.ZCacheable_set(None)
            return b''

        if self.precondition and hasattr(self, str(self.precondition)):
            # Grab whatever precondition was defined and then
            # execute it.  The precondition will raise an exception
            # if something violates its terms.
            c = getattr(self, str(self.precondition))
            if hasattr(c, 'isDocTemp') and c.isDocTemp:
                c(REQUEST['PARENTS'][1], REQUEST)
            else:
                c()

        if self._range_request_handler(REQUEST, RESPONSE):
            # we served a chunk of content in response to a range request.
            return b''

        RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime))
        RESPONSE.setHeader('Content-Type', self.content_type)
        RESPONSE.setHeader('Content-Length', self.size)
        RESPONSE.setHeader('Accept-Ranges', 'bytes')

        if self.ZCacheable_isCachingEnabled():
            result = self.ZCacheable_get(default=None)
            if result is not None:
                # We will always get None from RAMCacheManager and HTTP
                # Accelerated Cache Manager but we will get
                # something implementing the IStreamIterator interface
                # from a "FileCacheManager"
                return result

        self.ZCacheable_set(None)

        data = self.data
        if isinstance(data, bytes):
            RESPONSE.setBase(None)
            return data

        while data is not None:
            RESPONSE.write(data.data)
            data = data.next

        return b''
Example #7
0
    def __init__(self, path, name):
        self.path = path

        f = open(path, 'rb')
        data = f.read()
        f.close()
        self.content_type, enc = guess_content_type(path, data)
        self.__name__ = name
        self.lmt = float(os.path.getmtime(path)) or time()
        self.lmh = rfc1123_date(self.lmt)
Example #8
0
    def __init__(self, path, name):
        self.path = path

        f = open(path, 'rb')
        self.data = f.read()
        f.close()
        self.content_type, enc = guess_content_type(path, self.data)
        self.__name__ = name
        self.lmt = float(os.path.getmtime(path)) or time.time()
        self.lmh = rfc1123_date(self.lmt)
Example #9
0
 def _fetchlm(self):
     now = time()
     if now - self.lmt_last_checked > self.lmt_check_period:
         self.lmt_last_checked = now
         lmt = float(self.context.getLastMod()) or now
         lmh = rfc1123_date(lmt)
         d = self._last_mod = dict(lmt = lmt, lmh = lmh)
         ##print "***** LMT reread", d
     else:
         d = self._last_mod
     return d
Example #10
0
    def render(self):
        """See `IFeed`."""
        expires = rfc1123_date(time.time() + self.max_age)
        if self.date_updated is not None:
            last_modified = rfc1123_date(
                time.mktime(self.date_updated.timetuple()))
        else:
            last_modified = rfc1123_date(time.time())
        response = self.request.response
        response.setHeader('Expires', expires)
        response.setHeader('Cache-Control', 'max-age=%d' % self.max_age)
        response.setHeader('X-Cache-Control', 'max-age=%d' % self.max_age)
        response.setHeader('Last-Modified', last_modified)

        if self.format == 'atom':
            return self.renderAtom()
        elif self.format == 'html':
            return self.renderHTML()
        else:
            raise UnsupportedFeedFormat("Format %s is not supported" %
                                        self.format)
Example #11
0
    def render(self):
        """See `IFeed`."""
        expires = rfc1123_date(time.time() + self.max_age)
        if self.date_updated is not None:
            last_modified = rfc1123_date(
                time.mktime(self.date_updated.timetuple()))
        else:
            last_modified = rfc1123_date(time.time())
        response = self.request.response
        response.setHeader('Expires', expires)
        response.setHeader('Cache-Control', 'max-age=%d' % self.max_age)
        response.setHeader('X-Cache-Control', 'max-age=%d' % self.max_age)
        response.setHeader('Last-Modified', last_modified)

        if self.format == 'atom':
            return self.renderAtom()
        elif self.format == 'html':
            return self.renderHTML()
        else:
            raise UnsupportedFeedFormat("Format %s is not supported" %
                                        self.format)
Example #12
0
    def dav__init(self, request, response):
        # We are allowed to accept a url w/o a trailing slash
        # for a collection, but are supposed to provide a
        # hint to the client that it should be using one.
        # [WebDAV, 5.2]
        pathinfo = request.get('PATH_INFO', '')
        if pathinfo and pathinfo[-1] != '/':
            location = '%s/' % request['URL1']
            response.setHeader('Content-Location', location)
        response.setHeader('Date', rfc1123_date(), 1)

        # Initialize ETag header
        self.http__etag()
Example #13
0
    def dav__init(self, request, response):
        # Init expected HTTP 1.1 / WebDAV headers which are not
        # currently set by the base response object automagically.
        #
        # We sniff for a ZServer response object, because we don't
        # want to write duplicate headers (since ZS writes Date
        # and Connection itself).
        if not hasattr(response, '_server_version'):
            response.setHeader('Date', rfc1123_date(), 1)

        # Initialize ETag header
        self.http__etag()

        # HTTP Range support
        if HTTPRangeInterface.providedBy(self):
            response.setHeader('Accept-Ranges', 'bytes')
        else:
            response.setHeader('Accept-Ranges', 'none')
Example #14
0
    def updateCookieCredentials(self, request, response, user, login):
        """ Set a cookie to authenticate again people after a change
        of credentials.
        """
        now = int(time.time())
        timestamp = now % self.lifetime

        session = self._get_session(request)
        session.set('login', login)
        session.set('user', user)
        session.set('timestamp', timestamp)

        service = getUtility(ISecretService)
        cookie = service.digest(
            str(IClientId(request)),
            login,
            (now - timestamp) / self.lifetime)
        response.setCookie(
            self.cookie_name, cookie,
            path=self._get_cookie_path(request),
            expires=rfc1123_date(now + self.lifetime),
            http_only=True,
            secure=self.cookie_secure)
Example #15
0
    def GET(self):
        request = self.request
        response = request.response

        resource = self.resource
        try:
            modified = ICMFDublinCore(resource).modified
        except TypeError:
            modified = datetime.datetime.now()
        lmt = long(time.mktime(modified.timetuple()))

        header = request.getHeader('If-Modified-Since', None)
        if header is not None:
            header = header.split(';')[0]
            try:    mod_since=long(timeFromDateTimeString(header))
            except: mod_since=None
            if mod_since is not None:
                if lmt > 0 and lmt <= mod_since:
                    response.setStatus(304)
                    return ''

        response.setHeader('Last-Modified', rfc1123_date(lmt))
        return resource.render(request, attr=self.attr)
Example #16
0
 def dav__getlastmodified(self):
     return rfc1123_date(self.v_self()._p_mtime)
Example #17
0
 def updated_rfc(self):
     lmt = zope_datetime.time(self.updated.isoformat())
     return zope_datetime.rfc1123_date(lmt)
Example #18
0
    def _range_request_handler(self, REQUEST, RESPONSE):
        # HTTP Range header handling: return True if we've served a range
        # chunk out of our data.
        range = REQUEST.get_header('Range', None)
        request_range = REQUEST.get_header('Request-Range', None)
        if request_range is not None:
            # Netscape 2 through 4 and MSIE 3 implement a draft version
            # Later on, we need to serve a different mime-type as well.
            range = request_range
        if_range = REQUEST.get_header('If-Range', None)
        if range is not None:
            ranges = HTTPRangeSupport.parseRange(range)

            if if_range is not None:
                # Only send ranges if the data isn't modified, otherwise send
                # the whole object. Support both ETags and Last-Modified dates!
                if len(if_range) > 1 and if_range[:2] == 'ts':
                    # ETag:
                    if if_range != self.http__etag():
                        # Modified, so send a normal response. We delete
                        # the ranges, which causes us to skip to the 200
                        # response.
                        ranges = None
                else:
                    # Date
                    date = if_range.split(';')[0]
                    try:
                        mod_since = int(DateTime(date).timeTime())
                    except Exception:
                        mod_since = None
                    if mod_since is not None:
                        if self._p_mtime:
                            last_mod = int(self._p_mtime)
                        else:
                            last_mod = 0
                        if last_mod > mod_since:
                            # Modified, so send a normal response. We delete
                            # the ranges, which causes us to skip to the 200
                            # response.
                            ranges = None

            if ranges:
                # Search for satisfiable ranges.
                satisfiable = 0
                for start, end in ranges:
                    if start < self.size:
                        satisfiable = 1
                        break

                if not satisfiable:
                    RESPONSE.setHeader('Content-Range',
                                       'bytes */%d' % self.size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', self.size)
                    RESPONSE.setStatus(416)
                    return True

                ranges = HTTPRangeSupport.expandRanges(ranges, self.size)

                if len(ranges) == 1:
                    # Easy case, set extra header and return partial set.
                    start, end = ranges[0]
                    size = end - start

                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader(
                        'Content-Range',
                        'bytes %d-%d/%d' % (start, end - 1, self.size))
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    if isinstance(data, bytes):
                        RESPONSE.write(data[start:end])
                        return True

                    # Linked Pdata objects. Urgh.
                    pos = 0
                    while data is not None:
                        length = len(data.data)
                        pos = pos + length
                        if pos > start:
                            # We are within the range
                            lstart = length - (pos - start)

                            if lstart < 0:
                                lstart = 0

                            # find the endpoint
                            if end <= pos:
                                lend = length - (pos - end)

                                # Send and end transmission
                                RESPONSE.write(data[lstart:lend])
                                break

                            # Not yet at the end, transmit what we have.
                            RESPONSE.write(data[lstart:])

                        data = data.next

                    return True

                else:
                    boundary = _make_boundary()

                    # Calculate the content length
                    size = (
                        8 + len(boundary)  # End marker length
                        + len(ranges) * (  # Constant lenght per set
                            49 + len(boundary) + len(self.content_type) +
                            len('%d' % self.size)))
                    for start, end in ranges:
                        # Variable length per set
                        size = (size + len('%d%d' % (start, end - 1)) + end -
                                start)

                    # Some clients implement an earlier draft of the spec, they
                    # will only accept x-byteranges.
                    draftprefix = (request_range is not None) and 'x-' or ''

                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader(
                        'Content-Type', f'multipart/{draftprefix}byteranges;'
                        f' boundary={boundary}')
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    # The Pdata map allows us to jump into the Pdata chain
                    # arbitrarily during out-of-order range searching.
                    pdata_map = {}
                    pdata_map[0] = data

                    for start, end in ranges:
                        RESPONSE.write(b'\r\n--' + boundary.encode('ascii') +
                                       b'\r\n')
                        RESPONSE.write(b'Content-Type: ' +
                                       self.content_type.encode('ascii') +
                                       b'\r\n')
                        RESPONSE.write(b'Content-Range: bytes ' +
                                       str(start).encode('ascii') + b'-' +
                                       str(end - 1).encode('ascii') + b'/' +
                                       str(self.size).encode('ascii') +
                                       b'\r\n\r\n')

                        if isinstance(data, bytes):
                            RESPONSE.write(data[start:end])

                        else:
                            # Yippee. Linked Pdata objects. The following
                            # calculations allow us to fast-forward through the
                            # Pdata chain without a lot of dereferencing if we
                            # did the work already.
                            first_size = len(pdata_map[0].data)
                            if start < first_size:
                                closest_pos = 0
                            else:
                                closest_pos = ((
                                    (start - first_size) >> 16 << 16) +
                                               first_size)
                            pos = min(closest_pos, max(pdata_map.keys()))
                            data = pdata_map[pos]

                            while data is not None:
                                length = len(data.data)
                                pos = pos + length
                                if pos > start:
                                    # We are within the range
                                    lstart = length - (pos - start)

                                    if lstart < 0:
                                        lstart = 0

                                    # find the endpoint
                                    if end <= pos:
                                        lend = length - (pos - end)

                                        # Send and loop to next range
                                        RESPONSE.write(data[lstart:lend])
                                        break

                                    # Not yet at the end,
                                    # transmit what we have.
                                    RESPONSE.write(data[lstart:])

                                data = data.next
                                # Store a reference to a Pdata chain link
                                # so we don't have to deref during
                                # this request again.
                                pdata_map[pos] = data

                    # Do not keep the link references around.
                    del pdata_map

                    RESPONSE.write(b'\r\n--' + boundary.encode('ascii') +
                                   b'--\r\n')
                    return True
Example #19
0
 def expire(self):
     return rfc1123_date(time.time() + 12 * 3600)
Example #20
0
 def createLastModifiedDate(self, offset=0):
     from zope.datetime import rfc1123_date
     return rfc1123_date(self.file._p_mtime + offset)
Example #21
0
 def testrfc1123_date(self):
     from zope.datetime import rfc1123_date
     self.assertEqual(rfc1123_date(time("2002-01-12T01:01:01.234Z")),
                      "Sat, 12 Jan 2002 01:01:01 GMT")