Skip to content

Commit

Permalink
Issue #8: Check if value is single value
Browse files Browse the repository at this point in the history
  • Loading branch information
spaceone committed Oct 4, 2019
1 parent a36e519 commit d2b9fb2
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 3 deletions.
2 changes: 2 additions & 0 deletions httoop/header/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

class Authorization(AuthRequestElement):
is_request_header = True
is_single_value_header = True


class ProxyAuthenticate(_ListElement, _HopByHopElement, AuthResponseElement):
Expand All @@ -16,6 +17,7 @@ class ProxyAuthenticate(_ListElement, _HopByHopElement, AuthResponseElement):
class ProxyAuthorization(_HopByHopElement, AuthRequestElement):
__name__ = 'Proxy-Authorization'
is_request_header = True
is_single_value_header = True


class WWWAuthenticate(_ListElement, AuthResponseElement):
Expand Down
2 changes: 2 additions & 0 deletions httoop/header/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class Age(HeaderElement):

is_response_header = True
is_single_value_header = True


class CacheControl(HeaderElement):
Expand All @@ -18,6 +19,7 @@ class CacheControl(HeaderElement):
class Expires(HeaderElement):

is_response_header = True
is_single_value_header = True


class Pragma(HeaderElement):
Expand Down
6 changes: 6 additions & 0 deletions httoop/header/conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __eq__(self, other):
class ETag(HeaderElement):

is_response_header = True
is_single_value_header = True

def __eq__(self, other):
if not isinstance(other, ETag):
Expand All @@ -36,23 +37,28 @@ def __eq__(self, other):
class LastModified(_DateComparable, HeaderElement):
__name__ = 'Last-Modified'
is_response_header = True
is_single_value_header = True


class IfMatch(HeaderElement):
__name__ = 'If-Match'
is_request_header = True
is_single_value_header = True


class IfModifiedSince(_DateComparable, HeaderElement):
__name__ = 'If-Modified-Since'
is_request_header = True
is_single_value_header = True


class IfNoneMatch(HeaderElement):
__name__ = 'If-None-Match'
is_request_header = True
is_single_value_header = True


class IfUnmodifiedSince(_DateComparable, HeaderElement):
__name__ = 'If-Unmodified-Since'
is_request_header = True
is_single_value_header = True
5 changes: 4 additions & 1 deletion httoop/header/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class HeaderElement(with_metaclass(HeaderType)):
hop_by_hop = False
list_element = False
encode_latin1_quoted_printable = False
is_single_value_header = False
ignore_single_value_error = False

# Regular expression that matches `special' characters in parameters, the
# existance of which force quoting of the parameter value.
Expand Down Expand Up @@ -376,4 +378,5 @@ class _HopByHopElement(object):


class _ListElement(object):
list_element = True
list_element = True # TODO: rename list_element into something like allow_comma_separation = False, allow_split_foobar
is_single_value_header = False
9 changes: 7 additions & 2 deletions httoop/header/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,13 @@ def parse(self, data):
value = b''.join(value).rstrip()
Element = HEADER.get(name, HeaderElement)
value = Element.decode(value)
name = name.decode('ascii', 'ignore')

self.append(name.decode('ascii', 'ignore'), value)
if name in self and Element.is_single_value_header and not Element.ignore_single_value_error:
# TODO: this is only for non-splitted values
raise InvalidHeader(_(u"Single header value given more than once: %r") % (name,))

self.append(name, value)

def compose(self):
return b'%s\r\n' % b''.join(b'%s: %s\r\n' % (k, v) for k, v in self.__items())
Expand All @@ -145,7 +150,7 @@ def __encoded_items(self):
if Element is not HeaderElement:
key = Element.__name__
key = key.encode('ascii', 'ignore')
if Element.list_element:
if Element.list_element: # TODO: and not Element.is_single_value_header?
for value in Element.split(values):
yield key, Element.encode(value)
else:
Expand Down
17 changes: 17 additions & 0 deletions httoop/header/messaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class ContentDisposition(HeaderElement):

__name__ = 'Content-Disposition'
is_response_header = True
is_single_value_header = True

from httoop.date import Date

Expand Down Expand Up @@ -143,6 +144,7 @@ class ContentEncoding(CodecElement, HeaderElement):
__name__ = 'Content-Encoding'
is_request_header = True
is_response_header = True
#is_single_value_header = True

# IANA assigned HTTP Content-Encoding values
CODECS = {
Expand All @@ -161,30 +163,35 @@ class ContentLanguage(HeaderElement):
__name__ = 'Content-Language'
is_request_header = True
is_response_header = True
is_single_value_header = True


class ContentLength(HeaderElement):
__name__ = 'Content-Length'
is_request_header = True
is_response_header = True
is_single_value_header = True


class ContentLocation(HeaderElement):
__name__ = 'Content-Location'
is_response_header = True
is_single_value_header = True


class ContentMD5(HeaderElement):
__name__ = 'Content-MD5'
is_request_header = True
is_response_header = True
is_single_value_header = True


class ContentType(HeaderElement, MimeType, CodecElement):

__name__ = 'Content-Type'
is_request_header = True
is_response_header = True
is_single_value_header = True

raise_on_missing_codec = False

Expand Down Expand Up @@ -231,11 +238,13 @@ class Date(HeaderElement):

priority = '\x01'
is_response_header = True
is_single_value_header = True


class Expect(HeaderElement):

is_response_header = True
is_single_value_header = True

@property
def is_100_continue(self):
Expand All @@ -244,6 +253,7 @@ def is_100_continue(self):

class From(HeaderElement):
is_request_header = True
is_single_value_header = True


class Forwarded(HeaderElement):
Expand Down Expand Up @@ -275,6 +285,7 @@ def parse(cls, elementstr):
class Host(HeaderElement):

is_request_header = True
is_single_value_header = True

priority = '\x03'
RE_HOSTNAME = re.compile(r'^([^\x00-\x1F\x7F()^\'"<>@,;:/\[\]={} \t\\\\"]+)$')
Expand Down Expand Up @@ -339,28 +350,33 @@ class XForwardedHost(Host):

class Location(HeaderElement):
is_response_header = True
is_single_value_header = True


class MaxForwards(HeaderElement):

__name__ = 'Max-Forwards'
is_response_header = True
is_single_value_header = True


class Referer(HeaderElement):
is_request_header = True
is_single_value_header = True


class RetryAfter(HeaderElement):

__name__ = 'Retry-After'
is_response_header = True
is_single_value_header = True


class Server(HeaderElement):

priority = '\x02'
is_response_header = True
is_single_value_header = True


class SetCookie(_ListElement, _CookieElement):
Expand Down Expand Up @@ -458,6 +474,7 @@ class UserAgent(HeaderElement):
__name__ = 'User-Agent'
is_response_header = True
is_request_header = True
is_single_value_header = True


class Via(HeaderElement):
Expand Down
3 changes: 3 additions & 0 deletions httoop/header/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ContentRange(HeaderElement):

__name__ = 'Content-Range'
is_response_header = True
is_single_value_header = True

def __init__(self, value, range_, length):
self.range = range_
Expand Down Expand Up @@ -56,11 +57,13 @@ def parse(cls, elementstr):
class IfRange(HeaderElement):
__name__ = 'If-Range'
is_request_header = True
is_single_value_header = True


class Range(HeaderElement):

is_request_header = True
is_single_value_header = True

def __init__(self, value, ranges, params=None):
self.ranges = ranges
Expand Down
4 changes: 4 additions & 0 deletions httoop/header/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class StrictTransportSecurity(HeaderElement):

__name__ = 'Strict-Transport-Security'
is_response_header = True
is_single_value_header = True

@property
def include_sub_domains(self):
Expand All @@ -53,6 +54,7 @@ class ContentTypeOptions(HeaderElement):

__name__ = 'X-Content-Type-Options'
is_response_header = True
is_single_value_header = True

@property
def nosniff(self):
Expand All @@ -70,6 +72,7 @@ class FrameOptions(HeaderElement):

__name__ = 'X-Frame-Options'
is_response_header = True
is_single_value_header = True

RE_PARAMS = re.compile('\\s+')

Expand Down Expand Up @@ -106,6 +109,7 @@ class XSSProtection(HeaderElement):

__name__ = 'X-XSS-Protection'
is_response_header = True
is_single_value_header = True

@property
def enabled(self):
Expand Down

0 comments on commit d2b9fb2

Please sign in to comment.