예제 #1
0
    def test_parsing(self):
        self.assertEqual(parse_etags(r'"" ,  "etag", "e\\tag", W/"weak"'),
                         ['""', '"etag"', r'"e\\tag"', 'W/"weak"'])
        self.assertEqual(parse_etags('*'), ['*'])

        # Ignore RFC 2616 ETags that are invalid according to RFC 7232.
        self.assertEqual(parse_etags(r'"etag", "e\"t\"ag"'), ['"etag"'])
예제 #2
0
def validate_matching_preconditions(request, meta):
    """Check that the ETag conforms with the preconditions set."""

    etag = meta['hash'] if not UPDATE_MD5 else meta['checksum']
    if not etag:
        etag = None

    if_match = request.META.get('HTTP_IF_MATCH')
    if if_match is not None:
        if etag is None:
            raise faults.PreconditionFailed('Resource does not exist')
        if (if_match != '*'
                and etag not in [x.lower() for x in parse_etags(if_match)]):
            raise faults.PreconditionFailed('Resource ETag does not match')

    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
    if if_none_match is not None:
        # TODO: If this passes, must ignore If-Modified-Since header.
        if etag is not None:
            if (if_none_match == '*' or etag in [x.lower() for x in
                                                 parse_etags(if_none_match)]):
                # TODO: Continue if an If-Modified-Since header is present.
                if request.method in ('HEAD', 'GET'):
                    raise faults.NotModified('Resource ETag matches')
                raise faults.PreconditionFailed(
                    'Resource exists or ETag matches')
예제 #3
0
파일: util.py 프로젝트: cstavr/synnefo
def validate_matching_preconditions(request, meta):
    """Check that the ETag conforms with the preconditions set."""

    etag = meta['checksum']
    if not etag:
        etag = None

    if_match = request.META.get('HTTP_IF_MATCH')
    if if_match is not None:
        if etag is None:
            raise faults.PreconditionFailed('Resource does not exist')
        if (if_match != '*'
                and etag not in [x.lower() for x in parse_etags(if_match)]):
            raise faults.PreconditionFailed('Resource ETag does not match')

    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
    if if_none_match is not None:
        # TODO: If this passes, must ignore If-Modified-Since header.
        if etag is not None:
            if (if_none_match == '*'
                    or etag in [x.lower() for x in parse_etags(if_none_match)]):
                # TODO: Continue if an If-Modified-Since header is present.
                if request.method in ('HEAD', 'GET'):
                    raise faults.NotModified('Resource ETag matches')
                raise faults.PreconditionFailed(
                    'Resource exists or ETag matches')
예제 #4
0
    def test_parsing(self):
        self.assertEqual(
            http.parse_etags(r'"" ,  "etag", "e\\tag", W/"weak"'), ['""', '"etag"', r'"e\\tag"', 'W/"weak"']
        )
        self.assertEqual(http.parse_etags("*"), ["*"])

        # Ignore RFC 2616 ETags that are invalid according to RFC 7232.
        self.assertEqual(http.parse_etags(r'"etag", "e\"t\"ag"'), ['"etag"'])
예제 #5
0
def get_conditional_response(request, etag=None, last_modified=None, response=None):
    # Only return conditional responses on successful requests.
    if response and not (200 <= response.status_code < 300):
        return response

    # Get HTTP request headers.
    if_match_etags = parse_etags(request.META.get('HTTP_IF_MATCH', ''))
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
    if if_unmodified_since:
        if_unmodified_since = parse_http_date_safe(if_unmodified_since)
    if_none_match_etags = parse_etags(request.META.get('HTTP_IF_NONE_MATCH', ''))
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since:
        if_modified_since = parse_http_date_safe(if_modified_since)
예제 #6
0
        def get_not_modified_response(resource_etag):
            if not request.method in ('GET', 'HEAD'):
                return None

            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
            if if_none_match:
                # There can be more than one ETag in the request, so we
                # consider the list of values.
                try:
                    etags = parse_etags(if_none_match)
                except ValueError:
                    # In case of invalid etag ignore all ETag headers.
                    # Apparently Opera sends invalidly quoted headers at times
                    # (we should be returning a 400 response, but that's a
                    # little extreme) -- this is Django bug #10681.
                    if_none_match = None

            if not if_none_match:
                return None

            if not resource_etag:
                return None

            if resource_etag in etags:
                return Response(status=status.HTTP_304_NOT_MODIFIED)
            else:
                return None
예제 #7
0
def get_conditional_response(request,
                             etag=None,
                             last_modified=None,
                             response=None):
    # Only return conditional responses on successful requests.
    if response and not (200 <= response.status_code < 300):
        return response

    # Get HTTP request headers.
    if_match_etags = parse_etags(request.META.get('HTTP_IF_MATCH', ''))
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
    if_unmodified_since = if_unmodified_since and parse_http_date_safe(
        if_unmodified_since)
    if_none_match_etags = parse_etags(
        request.META.get('HTTP_IF_NONE_MATCH', ''))
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if_modified_since = if_modified_since and parse_http_date_safe(
        if_modified_since)

    # Step 1 of section 6 of RFC 7232: Test the If-Match precondition.
    if if_match_etags and not _if_match_passes(etag, if_match_etags):
        return _precondition_failed(request)

    # Step 2: Test the If-Unmodified-Since precondition.
    if (not if_match_etags
            and if_unmodified_since and not _if_unmodified_since_passes(
                last_modified, if_unmodified_since)):
        return _precondition_failed(request)

    # Step 3: Test the If-None-Match precondition.
    if if_none_match_etags and not _if_none_match_passes(
            etag, if_none_match_etags):
        if request.method in ('GET', 'HEAD'):
            return _not_modified(request, response)
        else:
            return _precondition_failed(request)

    # Step 4: Test the If-Modified-Since precondition.
    if (not if_none_match_etags and if_modified_since and
            not _if_modified_since_passes(last_modified, if_modified_since)):
        if request.method in ('GET', 'HEAD'):
            return _not_modified(request, response)

    # Step 5: Test the If-Range precondition (not supported).
    # Step 6: Return original response since there isn't a conditional response.
    return response
예제 #8
0
파일: views.py 프로젝트: TZanke/djangodav
 def evaluate_conditions(self, res):
     if not res.exists:
         return
     etag = res.get_etag()
     mtime = res.get_mtime_stamp()
     cond_if_match = self.request.META.get('HTTP_IF_MATCH', None)
     if cond_if_match:
         etags = parse_etags(cond_if_match)
         if '*' in etags or etag in etags:
             raise ResponseException(HttpResponsePreconditionFailed())
     cond_if_modified_since = self.request.META.get(
         'HTTP_IF_MODIFIED_SINCE', False)
     if cond_if_modified_since:
         # Parse and evaluate, but don't raise anything just yet...
         # This might be ignored based on If-None-Match evaluation.
         cond_if_modified_since = parse_time(cond_if_modified_since)
         if cond_if_modified_since and cond_if_modified_since > mtime:
             cond_if_modified_since = True
         else:
             cond_if_modified_since = False
     cond_if_none_match = self.request.META.get('HTTP_IF_NONE_MATCH', None)
     if cond_if_none_match:
         etags = parse_etags(cond_if_none_match)
         if '*' in etags or etag in etags:
             if self.request.method in ('GET', 'HEAD'):
                 raise ResponseException(HttpResponseNotModified())
             raise ResponseException(HttpResponsePreconditionFailed())
         # Ignore If-Modified-Since header...
         cond_if_modified_since = False
     cond_if_unmodified_since = self.request.META.get(
         'HTTP_IF_UNMODIFIED_SINCE', None)
     if cond_if_unmodified_since:
         cond_if_unmodified_since = parse_time(cond_if_unmodified_since)
         if cond_if_unmodified_since and cond_if_unmodified_since <= mtime:
             raise ResponseException(HttpResponsePreconditionFailed())
     if cond_if_modified_since:
         # This previously evaluated True and is not being ignored...
         raise ResponseException(HttpResponseNotModified())
     # TODO: complete If header handling...
     cond_if = self.request.META.get('HTTP_IF', None)
     if cond_if:
         if not cond_if.startswith('<'):
             cond_if = '<*>' + cond_if
예제 #9
0
        def inner(request, *args, **kwargs):
            # Get HTTP request headers
            if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
            if_match = request.META.get("HTTP_IF_MATCH")
            if if_none_match or if_match:
                # There can be more than one ETag in the request, so we
                # consider the list of values.
                etags = parse_etags(if_none_match or if_match)

            # Compute values (if any) for the requested resource.
            if etag_func:
                res_etag = etag_func(request, *args, **kwargs)
            else:
                res_etag = None
            if last_modified_func:
                dt = last_modified_func(request, *args, **kwargs)
                if dt:
                    res_last_modified = formatdate(timegm(dt.utctimetuple()))[:26] + 'GMT'
                else:
                    res_last_modified = None
            else:
                res_last_modified = None

            response = None
            if not ((if_match and (if_modified_since or if_none_match)) or
                    (if_match and if_none_match)):
                # We only get here if no undefined combinations of headers are
                # specified.
                if ((if_none_match and (res_etag in etags or
                        "*" in etags and res_etag)) and
                        (not if_modified_since or
                            res_last_modified == if_modified_since)):
                    if request.method in ("GET", "HEAD"):
                        response = HttpResponseNotModified()
                    else:
                        response = HttpResponse(status=412)
                elif if_match and ((not res_etag and "*" in etags) or
                        (res_etag and res_etag not in etags)):
                    response = HttpResponse(status=412)
                elif (not if_none_match and if_modified_since and
                        request.method == "GET" and
                        res_last_modified == if_modified_since):
                    response = HttpResponseNotModified()

            if response is None:
                response = func(request, *args, **kwargs)

            # Set relevant headers on the response if they don't already exist.
            if res_last_modified and not response.has_header('Last-Modified'):
                response['Last-Modified'] = res_last_modified
            if res_etag and not response.has_header('ETag'):
                response['ETag'] = quote_etag(res_etag)

            return response
예제 #10
0
    def set_etag(self, request, response):
        if 'ETag' in response:
            etag = parse_etags(response['ETag'])[0]
        else:
            etag = hashlib.md5(response.content).hexdigest()
            response['ETag'] = quote_etag(etag)

        # Cache the etag for subsequent look ups. This can be cached
        # indefinitely since these are unique values
        cache = self.get_cache(request, response)
        cache.set(etag, 1, MAX_CACHE_AGE)
예제 #11
0
파일: views.py 프로젝트: greybyte/djangodav
 def evaluate_conditions(self, res):
     if not res.exists:
         return
     etag = res.get_etag()
     mtime = res.get_mtime_stamp()
     cond_if_match = self.request.META.get('HTTP_IF_MATCH', None)
     if cond_if_match:
         etags = parse_etags(cond_if_match)
         if '*' in etags or etag in etags:
             raise ResponseException(HttpResponsePreconditionFailed())
     cond_if_modified_since = self.request.META.get('HTTP_IF_MODIFIED_SINCE', False)
     if cond_if_modified_since:
         # Parse and evaluate, but don't raise anything just yet...
         # This might be ignored based on If-None-Match evaluation.
         cond_if_modified_since = parse_time(cond_if_modified_since)
         if cond_if_modified_since and cond_if_modified_since > mtime:
             cond_if_modified_since = True
         else:
             cond_if_modified_since = False
     cond_if_none_match = self.request.META.get('HTTP_IF_NONE_MATCH', None)
     if cond_if_none_match:
         etags = parse_etags(cond_if_none_match)
         if '*' in etags or etag in etags:
             if self.request.method in ('GET', 'HEAD'):
                 raise ResponseException(HttpResponseNotModified())
             raise ResponseException(HttpResponsePreconditionFailed())
         # Ignore If-Modified-Since header...
         cond_if_modified_since = False
     cond_if_unmodified_since = self.request.META.get('HTTP_IF_UNMODIFIED_SINCE', None)
     if cond_if_unmodified_since:
         cond_if_unmodified_since = parse_time(cond_if_unmodified_since)
         if cond_if_unmodified_since and cond_if_unmodified_since <= mtime:
             raise ResponseException(HttpResponsePreconditionFailed())
     if cond_if_modified_since:
         # This previously evaluated True and is not being ignored...
         raise ResponseException(HttpResponseNotModified())
     # TODO: complete If header handling...
     cond_if = self.request.META.get('HTTP_IF', None)
     if cond_if:
         if not cond_if.startswith('<'):
             cond_if = '<*>' + cond_if
예제 #12
0
def get_conditional_response(request, etag=None, last_modified=None, response=None):
    # Only return conditional responses on successful requests.
    if response and not (200 <= response.status_code < 300):
        return response

    # Get HTTP request headers.
    if_match_etags = parse_etags(request.META.get('HTTP_IF_MATCH', ''))
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
    if if_unmodified_since:
        if_unmodified_since = parse_http_date_safe(if_unmodified_since)
    if_none_match_etags = parse_etags(request.META.get('HTTP_IF_NONE_MATCH', ''))
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since:
        if_modified_since = parse_http_date_safe(if_modified_since)

    # Step 1 of section 6 of RFC 7232: Test the If-Match precondition.
    if if_match_etags and not _if_match_passes(etag, if_match_etags):
        return _precondition_failed(request)

    # Step 2: Test the If-Unmodified-Since precondition.
    if (not if_match_etags and if_unmodified_since and
            not _if_unmodified_since_passes(last_modified, if_unmodified_since)):
        return _precondition_failed(request)

    # Step 3: Test the If-None-Match precondition.
    if if_none_match_etags and not _if_none_match_passes(etag, if_none_match_etags):
        if request.method in ('GET', 'HEAD'):
            return _not_modified(request, response)
        else:
            return _precondition_failed(request)

    # Step 4: Test the If-Modified-Since precondition.
    if (not if_none_match_etags and if_modified_since and
            not _if_modified_since_passes(last_modified, if_modified_since)):
        if request.method in ('GET', 'HEAD'):
            return _not_modified(request, response)

    # Step 5: Test the If-Range precondition (not supported).
    # Step 6: Return original response since there isn't a conditional response.
    return response
예제 #13
0
파일: util.py 프로젝트: parisk/synnefo
def validate_matching_preconditions(request, meta):
    """Check that the ETag conforms with the preconditions set."""

    etag = meta["hash"] if not UPDATE_MD5 else meta["checksum"]
    if not etag:
        etag = None

    if_match = request.META.get("HTTP_IF_MATCH")
    if if_match is not None:
        if etag is None:
            raise faults.PreconditionFailed("Resource does not exist")
        if if_match != "*" and etag not in [x.lower() for x in parse_etags(if_match)]:
            raise faults.PreconditionFailed("Resource ETag does not match")

    if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
    if if_none_match is not None:
        # TODO: If this passes, must ignore If-Modified-Since header.
        if etag is not None:
            if if_none_match == "*" or etag in [x.lower() for x in parse_etags(if_none_match)]:
                # TODO: Continue if an If-Modified-Since header is present.
                if request.method in ("HEAD", "GET"):
                    raise faults.NotModified("Resource ETag matches")
                raise faults.PreconditionFailed("Resource exists or ETag matches")
예제 #14
0
 def has_etag_changed(self):
     if not self.etag_attribute:
         return True
     etag = self.generate_etag()
     if not etag:
         return True
     match = self.request.META.get('HTTP_IF_NONE_MATCH')
     if match:
         values = parse_etags(match)
         for value in values:
             # Django appends ";gzip" when gzip is enabled
             clean_value = value.split(';')[0]
             if clean_value == '*' or clean_value == etag:
                 return False
     return True
예제 #15
0
 def get_etags_and_matchers(self, request):
     etags = None
     if_none_match = request.META.get(prepare_header_name("if-none-match"))
     if_match = request.META.get(prepare_header_name("if-match"))
     if if_none_match or if_match:
         # There can be more than one ETag in the request, so we
         # consider the list of values.
         try:
             etags = parse_etags(if_none_match or if_match)
         except ValueError:
             # In case of invalid etag ignore all ETag headers.
             # Apparently Opera sends invalidly quoted headers at times
             # (we should be returning a 400 response, but that's a
             # little extreme) -- this is Django bug #10681.
             if_none_match = None
             if_match = None
     return etags, if_none_match, if_match
예제 #16
0
 def get_etags_and_matchers(self, request):
     etags = None
     if_none_match = request.META.get(prepare_header_name("if-none-match"))
     if_match = request.META.get(prepare_header_name("if-match"))
     if if_none_match or if_match:
         # There can be more than one ETag in the request, so we
         # consider the list of values.
         try:
             etags = parse_etags(if_none_match or if_match)
         except ValueError:
             # In case of invalid etag ignore all ETag headers.
             # Apparently Opera sends invalidly quoted headers at times
             # (we should be returning a 400 response, but that's a
             # little extreme) -- this is Django bug #10681.
             if_none_match = None
             if_match = None
     return etags, if_none_match, if_match
예제 #17
0
 def get_conditional_response(self, request):
     # etag
     etag = self.get_etag()
     if_none_match_etags = set(etag.strip("W/") for etag in parse_etags(request.META.get('HTTP_IF_NONE_MATCH', '')))
     if etag and etag.strip("W/") in if_none_match_etags:
         return self.make_not_modified_response()
     # last modified
     last_modified_dt = self.get_last_modified_dt()
     if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
     if_modified_since = if_modified_since and parse_http_date_safe(if_modified_since)
     if (
         not if_none_match_etags and
         isinstance(last_modified_dt, datetime) and
         if_modified_since and
         int(last_modified_dt.timestamp()) <= if_modified_since
     ):
         return self.make_not_modified_response()
     return self.build_and_make_response()
예제 #18
0
    def is_precondition_failed(self, request, response, *args, **kwargs):
        # ETags are enabled. Check for conditional request headers. The current
        # ETag value is used for the conditional requests. After the request
        # method handler has been processed, the new ETag will be calculated.
        if self.use_etags and 'HTTP_IF_MATCH' in request.META:
            request_etag = parse_etags(request.META['HTTP_IF_MATCH'])[0]
            etag = self.get_etag(request, request_etag)
            if request_etag != etag:
                return True

        # Last-Modified date enabled. check for conditional request headers. The
        # current modification datetime value is used for the conditional
        # requests. After the request method handler has been processed, the new
        # Last-Modified datetime will be returned.
        if self.use_last_modified and 'HTTP_IF_UNMODIFIED_SINCE' in request.META:
            last_modified = self.get_last_modified(request, *args, **kwargs)
            known_last_modified = EPOCH_DATE + timedelta(seconds=parse_http_date(request.META['HTTP_IF_UNMODIFIED_SINCE']))
            if  known_last_modified != last_modified:
                return True

        return False
예제 #19
0
 def testParsing(self):
     etags = http.parse_etags(r'"", "etag", "e\"t\"ag", "e\\tag", W/"weak"')
     self.assertEqual(etags, ['', 'etag', 'e"t"ag', r'e\tag', 'weak'])
예제 #20
0
 def test_parsing(self):
     etags = http.parse_etags(r'"", "etag", "e\"t\"ag", "e\\tag", W/"weak"')
     self.assertEqual(etags, ['', 'etag', 'e"t"ag', r'e\tag', 'weak'])
예제 #21
0
 def test_parsing(self):
     etags = http.parse_etags(r'"", "etag", "e\"t\"ag", "e\\tag", W/"weak"')
     self.assertEqual(etags, ["", "etag", 'e"t"ag', r"e\tag", "weak"])
예제 #22
0
"""
예제 #23
0
def _document_api_PUT(request, document_slug, document_locale):
    """
    Handle PUT requests for the document_api view.
    """

    # Try parsing one of the supported content types from the request
    try:
        content_type = request.META.get('CONTENT_TYPE', '')

        if content_type.startswith('application/json'):
            data = json.loads(request.body)

        elif content_type.startswith('multipart/form-data'):
            parser = MultiPartParser(request.META,
                                     StringIO(request.body),
                                     request.upload_handlers,
                                     request.encoding)
            data, files = parser.parse()

        elif content_type.startswith('text/html'):
            # TODO: Refactor this into wiki.content ?
            # First pass: Just assume the request body is an HTML fragment.
            html = request.body
            data = dict(content=html)

            # Second pass: Try parsing the body as a fuller HTML document,
            # and scrape out some of the interesting parts.
            try:
                doc = pq(html)
                head_title = doc.find('head title')
                if head_title.length > 0:
                    data['title'] = head_title.text()
                body_content = doc.find('body')
                if body_content.length > 0:
                    data['content'] = body_content.html()
            except Exception:
                pass

        else:
            resp = HttpResponse()
            resp.status_code = 400
            resp.content = ugettext(
                "Unsupported content-type: %s") % content_type
            return resp

    except Exception as e:
        resp = HttpResponse()
        resp.status_code = 400
        resp.content = ugettext("Request parsing error: %s") % e
        return resp

    try:
        # Look for existing document to edit:
        doc = Document.objects.get(locale=document_locale, slug=document_slug)
        section_id = request.GET.get('section', None)
        is_new = False

        # Use ETags to detect mid-air edit collision
        # see: http://www.w3.org/1999/04/Editing/
        if_match = request.META.get('HTTP_IF_MATCH')
        if if_match:
            try:
                expected_etags = parse_etags(if_match)
            except ValueError:
                expected_etags = []
            current_etag = calculate_etag(doc.get_html(section_id))
            if current_etag not in expected_etags:
                resp = HttpResponse()
                resp.status_code = 412
                resp.content = ugettext('ETag precondition failed')
                return resp

    except Document.DoesNotExist:
        # TODO: There should be a model utility for creating a doc...

        # Let's see if this slug path implies a parent...
        slug_parts = split_slug(document_slug)
        if not slug_parts['parent']:
            # Apparently, this is a root page!
            parent_doc = None
        else:
            # There's a parent implied, so make sure we can find it.
            parent_doc = get_object_or_404(Document, locale=document_locale,
                                           slug=slug_parts['parent'])

        # Create and save the new document; we'll revise it immediately.
        doc = Document(slug=document_slug, locale=document_locale,
                       title=data.get('title', document_slug),
                       parent_topic=parent_doc)
        doc.save()
        section_id = None  # No section editing for new document!
        is_new = True

    new_rev = doc.revise(request.user, data, section_id)
    doc.schedule_rendering('max-age=0')

    request.authkey.log('created' if is_new else 'updated',
                        new_rev, data.get('summary', None))

    resp = HttpResponse()
    if is_new:
        resp['Location'] = request.build_absolute_uri(doc.get_absolute_url())
        resp.status_code = 201
    else:
        resp.status_code = 205

    return resp
예제 #24
0
        def inner(request, *args, **kwargs):
            # Get HTTP request headers
            if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
            if_match = request.META.get("HTTP_IF_MATCH")
            if if_none_match or if_match:
                # There can be more than one ETag in the request, so we
                # consider the list of values.
                try:
                    etags = parse_etags(if_none_match or if_match)
                except ValueError:
                    # In case of invalid etag ignore all ETag headers.
                    # Apparently Opera sends invalidly quoted headers at times
                    # (we should be returning a 400 response, but that's a
                    # little extreme) -- this is Django bug #10681.
                    if_none_match = None
                    if_match = None

            # Compute values (if any) for the requested resource.
            if etag_func:
                res_etag = etag_func(request, *args, **kwargs)
            else:
                res_etag = None
            if last_modified_func:
                dt = last_modified_func(request, *args, **kwargs)
                if dt:
                    res_last_modified = formatdate(timegm(
                        dt.utctimetuple()))[:26] + 'GMT'
                else:
                    res_last_modified = None
            else:
                res_last_modified = None

            response = None
            if not ((if_match and (if_modified_since or if_none_match)) or
                    (if_match and if_none_match)):
                # We only get here if no undefined combinations of headers are
                # specified.
                if ((if_none_match and
                     (res_etag in etags or "*" in etags and res_etag))
                        and (not if_modified_since
                             or res_last_modified == if_modified_since)):
                    if request.method in ("GET", "HEAD"):
                        response = HttpResponseNotModified()
                    else:
                        logger.warning('Precondition Failed: %s' %
                                       request.path,
                                       extra={
                                           'status_code': 412,
                                           'request': request
                                       })
                        response = HttpResponse(status=412)
                elif if_match and ((not res_etag and "*" in etags) or
                                   (res_etag and res_etag not in etags)):
                    logger.warning('Precondition Failed: %s' % request.path,
                                   extra={
                                       'status_code': 412,
                                       'request': request
                                   })
                    response = HttpResponse(status=412)
                elif (not if_none_match and if_modified_since
                      and request.method == "GET"
                      and res_last_modified == if_modified_since):
                    response = HttpResponseNotModified()

            if response is None:
                response = func(request, *args, **kwargs)

            # Set relevant headers on the response if they don't already exist.
            if res_last_modified and not response.has_header('Last-Modified'):
                response['Last-Modified'] = res_last_modified
            if res_etag and not response.has_header('ETag'):
                response['ETag'] = quote_etag(res_etag)

            return response
예제 #25
0
파일: http.py 프로젝트: 101studio/django
        def inner(request, *args, **kwargs):
            # Get HTTP request headers
            if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
            if if_modified_since:
                if_modified_since = parse_http_date_safe(if_modified_since)
            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
            if_match = request.META.get("HTTP_IF_MATCH")
            if if_none_match or if_match:
                # There can be more than one ETag in the request, so we
                # consider the list of values.
                try:
                    etags = parse_etags(if_none_match or if_match)
                except ValueError:
                    # In case of invalid etag ignore all ETag headers.
                    # Apparently Opera sends invalidly quoted headers at times
                    # (we should be returning a 400 response, but that's a
                    # little extreme) -- this is Django bug #10681.
                    if_none_match = None
                    if_match = None

            # Compute values (if any) for the requested resource.
            if etag_func:
                res_etag = etag_func(request, *args, **kwargs)
            else:
                res_etag = None
            if last_modified_func:
                dt = last_modified_func(request, *args, **kwargs)
                if dt:
                    res_last_modified = timegm(dt.utctimetuple())
                else:
                    res_last_modified = None
            else:
                res_last_modified = None

            response = None
            if not ((if_match and (if_modified_since or if_none_match)) or
                    (if_match and if_none_match)):
                # We only get here if no undefined combinations of headers are
                # specified.
                if ((if_none_match and (res_etag in etags or
                        "*" in etags and res_etag)) and
                        (not if_modified_since or
                            (res_last_modified and if_modified_since and
                            res_last_modified <= if_modified_since))):
                    if request.method in ("GET", "HEAD"):
                        response = HttpResponseNotModified()
                    else:
                        logger.warning('Precondition Failed: %s' % request.path,
                            extra={
                                'status_code': 412,
                                'request': request
                            }
                        )
                        response = HttpResponse(status=412)
                elif if_match and ((not res_etag and "*" in etags) or
                        (res_etag and res_etag not in etags)):
                    logger.warning('Precondition Failed: %s' % request.path,
                        extra={
                            'status_code': 412,
                            'request': request
                        }
                    )
                    response = HttpResponse(status=412)
                elif (not if_none_match and request.method == "GET" and
                        res_last_modified and if_modified_since and
                        res_last_modified <= if_modified_since):
                    response = HttpResponseNotModified()

            if response is None:
                response = func(request, *args, **kwargs)

            # Set relevant headers on the response if they don't already exist.
            if res_last_modified and not response.has_header('Last-Modified'):
                response['Last-Modified'] = http_date(res_last_modified)
            if res_etag and not response.has_header('ETag'):
                response['ETag'] = quote_etag(res_etag)

            return response
예제 #26
0
파일: cache.py 프로젝트: nbsky/django
def get_conditional_response(request, etag=None, last_modified=None, response=None):
    # Get HTTP request headers
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since:
        if_modified_since = parse_http_date_safe(if_modified_since)
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
    if if_unmodified_since:
        if_unmodified_since = parse_http_date_safe(if_unmodified_since)
    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
    if_match = request.META.get('HTTP_IF_MATCH')
    etags = []
    if if_none_match or if_match:
        # There can be more than one ETag in the request, so we
        # consider the list of values.
        try:
            etags = parse_etags(if_none_match or if_match)
        except ValueError:
            # In case of an invalid ETag, ignore all ETag headers.
            # Apparently Opera sends invalidly quoted headers at times
            # (we should be returning a 400 response, but that's a
            # little extreme) -- this is bug #10681.
            if_none_match = None
            if_match = None

    # If-None-Match must be ignored if original result would be anything
    # other than a 2XX or 304 status. 304 status would result in no change.
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
    if response and not (200 <= response.status_code < 300):
        if_none_match = None
        if_match = None

    # If-Modified-Since must be ignored if the original result was not a 200.
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
    if response and response.status_code != 200:
        if_modified_since = None
        if_unmodified_since = None

    if not ((if_match and if_modified_since) or
            (if_none_match and if_unmodified_since) or
            (if_modified_since and if_unmodified_since) or
            (if_match and if_none_match)):
        # We only get here if no undefined combinations of headers are
        # specified.
        if ((if_none_match and (etag in etags or
                '*' in etags and etag)) and
                (not if_modified_since or
                    (last_modified and if_modified_since and
                    last_modified <= if_modified_since))):
            if request.method in ('GET', 'HEAD'):
                return _not_modified(request, response)
            else:
                return _precondition_failed(request)
        elif (if_match and ((not etag and '*' in etags) or
                (etag and etag not in etags) or
                (last_modified and if_unmodified_since and
                last_modified > if_unmodified_since))):
            return _precondition_failed(request)
        elif (not if_none_match and request.method in ('GET', 'HEAD') and
                last_modified and if_modified_since and
                last_modified <= if_modified_since):
            return _not_modified(request, response)
        elif (not if_match and
                last_modified and if_unmodified_since and
                last_modified > if_unmodified_since):
            return _precondition_failed(request)

    return response
예제 #27
0
        def inner(request, *args, **kwargs):
            # Get HTTP request headers
            if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
            if if_modified_since:
                if_modified_since = parse_http_date_safe(if_modified_since)
            if_unmodified_since = request.META.get("HTTP_IF_UNMODIFIED_SINCE")
            if if_unmodified_since:
                if_unmodified_since = parse_http_date_safe(if_unmodified_since)
            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
            if_match = request.META.get("HTTP_IF_MATCH")
            etags = []
            if if_none_match or if_match:
                # There can be more than one ETag in the request, so we
                # consider the list of values.
                try:
                    etags = parse_etags(if_none_match or if_match)
                except ValueError:
                    # In case of invalid etag ignore all ETag headers.
                    # Apparently Opera sends invalidly quoted headers at times
                    # (we should be returning a 400 response, but that's a
                    # little extreme) -- this is Django bug #10681.
                    if_none_match = None
                    if_match = None

            # Compute values (if any) for the requested resource.
            def get_last_modified():
                if last_modified_func:
                    dt = last_modified_func(request, *args, **kwargs)
                    if dt:
                        return timegm(dt.utctimetuple())

            res_etag = etag_func(request, *args, **
                                 kwargs) if etag_func else None
            res_last_modified = get_last_modified()

            response = None
            if not ((if_match and if_modified_since) or
                    (if_none_match and if_unmodified_since) or
                    (if_modified_since and if_unmodified_since) or
                    (if_match and if_none_match)):
                # We only get here if no undefined combinations of headers are
                # specified.
                if ((if_none_match and
                     (res_etag in etags or "*" in etags and res_etag))
                        and (not if_modified_since or
                             (res_last_modified and if_modified_since
                              and res_last_modified <= if_modified_since))):
                    if request.method in ("GET", "HEAD"):
                        response = HttpResponseNotModified()
                    else:
                        response = _precondition_failed(request)
                elif (if_match
                      and ((not res_etag and "*" in etags) or
                           (res_etag and res_etag not in etags) or
                           (res_last_modified and if_unmodified_since
                            and res_last_modified > if_unmodified_since))):
                    response = _precondition_failed(request)
                elif (not if_none_match and request.method in ("GET", "HEAD")
                      and res_last_modified and if_modified_since
                      and res_last_modified <= if_modified_since):
                    response = HttpResponseNotModified()
                elif (not if_match and res_last_modified
                      and if_unmodified_since
                      and res_last_modified > if_unmodified_since):
                    response = _precondition_failed(request)

            if response is None:
                response = func(request, *args, **kwargs)

            # Set relevant headers on the response if they don't already exist.
            if res_last_modified and not response.has_header('Last-Modified'):
                response['Last-Modified'] = http_date(res_last_modified)
            if res_etag and not response.has_header('ETag'):
                response['ETag'] = quote_etag(res_etag)

            return response
예제 #28
0
def get_conditional_response(request,
                             etag=None,
                             last_modified=None,
                             response=None):
    # Get HTTP request headers
    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since:
        if_modified_since = parse_http_date_safe(if_modified_since)
    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')
    if if_unmodified_since:
        if_unmodified_since = parse_http_date_safe(if_unmodified_since)
    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
    if_match = request.META.get('HTTP_IF_MATCH')
    etags = []
    if if_none_match or if_match:
        # There can be more than one ETag in the request, so we
        # consider the list of values.
        try:
            etags = parse_etags(if_none_match or if_match)
        except ValueError:
            # In case of an invalid ETag, ignore all ETag headers.
            # Apparently Opera sends invalidly quoted headers at times
            # (we should be returning a 400 response, but that's a
            # little extreme) -- this is bug #10681.
            if_none_match = None
            if_match = None

    # If-None-Match must be ignored if original result would be anything
    # other than a 2XX or 304 status. 304 status would result in no change.
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
    if response and not (200 <= response.status_code < 300):
        if_none_match = None
        if_match = None

    # If-Modified-Since must be ignored if the original result was not a 200.
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
    if response and response.status_code != 200:
        if_modified_since = None
        if_unmodified_since = None

    if not ((if_match and if_modified_since) or
            (if_none_match and if_unmodified_since) or
            (if_modified_since and if_unmodified_since) or
            (if_match and if_none_match)):
        # We only get here if no undefined combinations of headers are
        # specified.
        if ((if_none_match and (etag in etags or '*' in etags and etag))
                and (not if_modified_since or
                     (last_modified and if_modified_since
                      and last_modified <= if_modified_since))):
            if request.method in ('GET', 'HEAD'):
                return _not_modified(request, response)
            else:
                return _precondition_failed(request)
        elif (if_match and
              ((not etag and '*' in etags) or (etag and etag not in etags) or
               (last_modified and if_unmodified_since
                and last_modified > if_unmodified_since))):
            return _precondition_failed(request)
        elif (not if_none_match and request.method in ('GET', 'HEAD')
              and last_modified and if_modified_since
              and last_modified <= if_modified_since):
            return _not_modified(request, response)
        elif (not if_match and last_modified and if_unmodified_since
              and last_modified > if_unmodified_since):
            return _precondition_failed(request)

    return response
예제 #29
0
def _document_api_PUT(request, document_slug, document_locale):
    """
    Handle PUT requests for the document_api view.
    """

    # Try parsing one of the supported content types from the request
    try:
        content_type = request.META.get('CONTENT_TYPE', '')

        if content_type.startswith('application/json'):
            data = json.loads(request.body)

        elif content_type.startswith('multipart/form-data'):
            parser = MultiPartParser(request.META, StringIO(request.body),
                                     request.upload_handlers, request.encoding)
            data, files = parser.parse()

        elif content_type.startswith('text/html'):
            # TODO: Refactor this into wiki.content ?
            # First pass: Just assume the request body is an HTML fragment.
            html = request.body
            data = dict(content=html)

            # Second pass: Try parsing the body as a fuller HTML document,
            # and scrape out some of the interesting parts.
            try:
                doc = pq(html)
                head_title = doc.find('head title')
                if head_title.length > 0:
                    data['title'] = head_title.text()
                body_content = doc.find('body')
                if body_content.length > 0:
                    data['content'] = to_html(body_content)
            except Exception:
                pass

        else:
            resp = HttpResponse()
            resp.status_code = 400
            resp.content = ugettext(
                "Unsupported content-type: %s") % content_type
            return resp

    except Exception as e:
        resp = HttpResponse()
        resp.status_code = 400
        resp.content = ugettext("Request parsing error: %s") % e
        return resp

    try:
        # Look for existing document to edit:
        doc = Document.objects.get(locale=document_locale, slug=document_slug)
        section_id = request.GET.get('section', None)
        is_new = False

        # Use ETags to detect mid-air edit collision
        # see: http://www.w3.org/1999/04/Editing/
        if_match = request.META.get('HTTP_IF_MATCH')
        if if_match:
            try:
                expected_etags = parse_etags(if_match)
            except ValueError:
                expected_etags = []
            # Django's parse_etags returns a list of quoted rather than
            # un-quoted ETags starting with version 1.11.
            current_etag = quote_etag(calculate_etag(doc.get_html(section_id)))
            if current_etag not in expected_etags:
                resp = HttpResponse()
                resp.status_code = 412
                resp.content = ugettext('ETag precondition failed')
                return resp

    except Document.DoesNotExist:
        # TODO: There should be a model utility for creating a doc...

        # Let's see if this slug path implies a parent...
        slug_parts = split_slug(document_slug)
        if not slug_parts['parent']:
            # Apparently, this is a root page!
            parent_doc = None
        else:
            # There's a parent implied, so make sure we can find it.
            parent_doc = get_object_or_404(Document,
                                           locale=document_locale,
                                           slug=slug_parts['parent'])

        # Create and save the new document; we'll revise it immediately.
        doc = Document(slug=document_slug,
                       locale=document_locale,
                       title=data.get('title', document_slug),
                       parent_topic=parent_doc)
        doc.save()
        section_id = None  # No section editing for new document!
        is_new = True

    new_rev = doc.revise(request.user, data, section_id)
    doc.schedule_rendering('max-age=0')

    request.authkey.log('created' if is_new else 'updated', new_rev,
                        data.get('summary', None))

    resp = HttpResponse()
    if is_new:
        resp['Location'] = request.build_absolute_uri(doc.get_absolute_url())
        resp.status_code = 201
    else:
        resp.status_code = 205

    return resp
예제 #30
0
def test_parse_etags(inp):
    parse_etags(inp)
예제 #31
0
    def process_request(self, request, *args, **kwargs):
        # Initilize a new response for this request. Passing the response along
        # the request cycle allows for gradual modification of the headers.
        response = HttpResponse()

        # TODO keep track of a list of request headers used to
        # determine the resource representation for the 'Vary'
        # header.

        # ### 503 Service Unavailable
        # The server does not need to be unavailable for a resource to be
        # unavailable...
        if self.is_service_unavailable(request, response, *args, **kwargs):
            response.status_code = codes.service_unavailable
            return response

        # ### 414 Request URI Too Long _(not implemented)_
        # This should be be handled upstream by the Web server

        # ### 400 Bad Request _(not implemented)_
        # Note that many services respond with this code when entities are
        # unprocessable. This should really be a 422 Unprocessable Entity
        # Most actualy bad requests are handled upstream by the Web server
        # when parsing the HTTP message

        # ### 401 Unauthorized
        # Check if the request is authorized to access this resource.
        if self.is_unauthorized(request, response, *args, **kwargs):
            response.status_code = codes.unauthorized
            return response

        # ### 403 Forbidden
        # Check if this resource is forbidden for the request.
        if self.is_forbidden(request, response, *args, **kwargs):
            response.status_code = codes.forbidden
            return response

        # ### 501 Not Implemented _(not implemented)_
        # This technically refers to a service-wide response for an
        # unimplemented request method, again this is upstream.

        # ### 429 Too Many Requests
        # Both `rate_limit_count` and `rate_limit_seconds` must be none
        # falsy values to be checked.
        if self.rate_limit_count and self.rate_limit_seconds:
            if self.is_too_many_requests(request, response, *args, **kwargs):
                response.status_code = codes.too_many_requests
                return response

        # ### 405 Method Not Allowed
        if self.is_method_not_allowed(request, response, *args, **kwargs):
            response.status_code = codes.method_not_allowed
            return response

        # ### 406 Not Acceptable
        # Checks Accept and Accept-* headers
        if self.is_not_acceptable(request, response, *args, **kwargs):
            response.status_code = codes.not_acceptable
            return response

        # ### Process an _OPTIONS_ request
        # Enough processing has been performed to allow an OPTIONS request.
        if request.method == methods.OPTIONS and 'OPTIONS' in self.allowed_methods:
            return self.options(request, response)

        # ## Request Entity Checks
        # Only perform these checks if the request has supplied a body.
        if 'CONTENT_LENGTH' in request.META and request.META['CONTENT_LENGTH']:

            # ### 415 Unsupported Media Type
            # Check if the entity `Content-Type` supported for decoding.
            if self.is_unsupported_media_type(request, response, *args, **kwargs):
                response.status_code = codes.unsupported_media_type
                return response

            # ### 413 Request Entity Too Large
            # Check if the entity is too large for processing
            if self.is_request_entity_too_large(request, response, *args, **kwargs):
                response.status_code = codes.request_entity_too_large
                return response

        # ### 404 Not Found
        # Check if this resource exists. Note, if this requires a database
        # lookup or some other expensive lookup, the relevant object may
        # be _attached_ to the request or response object to be used
        # dowstream in the handler. This prevents multiple database
        # hits or filesystem lookups.
        if self.is_not_found(request, response, *args, **kwargs):
            response.status_code = codes.not_found
            return response

        # ### 410 Gone
        # Check if this resource used to exist, but does not anymore. A common
        # strategy for this when dealing with this in a database context is to
        # have an `archived` or `deleted` flag that can be used associated with
        # the given lookup key while the rest of the content in the row may be
        # deleted.
        if self.is_gone(request, response, *args, **kwargs):
            response.status_code = codes.gone
            return response

        # ### 428 Precondition Required
        # Prevents the "lost udpate" problem and requires client to confirm
        # the state of the resource has not changed since the last `GET`
        # request. This applies to `PUT` and `PATCH` requests.
        if request.method == methods.PUT or request.method == methods.PATCH:
            if self.is_precondition_required(request, response, *args, **kwargs):
                return UncacheableResponse(status=codes.precondition_required)

        # ### 412 Precondition Failed
        # Conditional requests applies to GET, HEAD, PUT, and PATCH.
        # For GET and HEAD, the request checks the either the entity changed
        # since the last time it requested it, `If-Modified-Since`, or if the
        # entity tag (ETag) has changed, `If-None-Match`.
        if request.method == methods.PUT or request.method == methods.PATCH:
            if self.is_precondition_failed(request, response, *args, **kwargs):
                return UncacheableResponse(status=codes.precondition_failed)

        # Check for conditional GET or HEAD request
        if request.method == methods.GET or request.method == methods.HEAD:

            # Check Etags before Last-Modified...
            if self.use_etags and 'HTTP_IF_NONE_MATCH' in request.META:
                # Parse request Etags (only one is currently supported)
                request_etag = parse_etags(request.META['HTTP_IF_NONE_MATCH'])[0]

                # Check if the request Etag is valid. The current Etag is
                # supplied to enable strategies where the etag does not need
                # to be used to regenerate the Etag. This may include
                # generating an MD5 of the resource and storing it as a key
                # in memcache.
                etag = self.get_etag(request, response, request_etag, *args, **kwargs)

                # Nothing has changed, simply return
                if request_etag == etag:
                    response.status_code = codes.not_modified
                    return response

            if self.use_last_modified and 'HTTP_IF_MODIFIED_SINCE' in request.META:
                # Get the last known modified date from the client, compare it
                # to the last modified date of the resource
                last_modified = self.get_last_modified(request, *args, **kwargs)
                known_last_modified = EPOCH_DATE + timedelta(seconds=parse_http_date(request.META['HTTP_IF_MODIFIED_SINCE']))

                if known_last_modified >= last_modified:
                    response.status_code = codes.not_modified
                    return response

        # Check if there is a request body
        if 'CONTENT_LENGTH' in request.META and request.META['CONTENT_LENGTH']:
            content_type = request._content_type
            if content_type in serializers:
                request.data = serializers.decode(content_type, request.body)