Skip to content
This repository has been archived by the owner on Nov 23, 2020. It is now read-only.

Commit

Permalink
Merge pull request #244 from quantmind/dev
Browse files Browse the repository at this point in the history
Bug fixes & docs
  • Loading branch information
lsbardel authored Sep 9, 2016
2 parents 0839757 + ff809e7 commit ad1f343
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 23 deletions.
19 changes: 10 additions & 9 deletions pulsar/apps/http/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def encode(self, method, uri):
o = self.options
qop = o.get('qop')
realm = o.get('realm')
nonce = o['nonce']
nonce = o.get('nonce')
entdig = None
p_parsed = urlparse(uri)
path = p_parsed.path
Expand All @@ -113,8 +113,8 @@ def encode(self, method, uri):
else:
# XXX handle auth-int.
return
base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
'response="%s"' % (self.username, realm, nonce, path, respdig)
base = ('username="%s", realm="%s", nonce="%s", uri="%s", '
'response="%s"' % (self.username, realm, nonce, path, respdig))
opaque = o.get('opaque')
if opaque:
base += ', opaque="%s"' % opaque
Expand All @@ -123,21 +123,22 @@ def encode(self, method, uri):
base += ', algorithm="%s"' % self.algorithm
if qop:
base += ', qop=%s, nc=%s, cnonce="%s"' % (qop, ncvalue, cnonce)
return 'Digest %s' % (base)
return 'Digest %s' % base

@noerror
def handle_401(self, response, exc=None):
"""Takes the given response and tries digest-auth, if needed."""
if response.status_code == 401:
request = response.request
response._handle_401 = getattr(response, '_handle_401', 0) + 1
s_auth = response.headers.get('www-authenticate', '')
if 'digest' in s_auth.lower() and response._handle_401 < 2:
if 'digest' not in s_auth.lower():
return
request = response.request
if not request.headers.get('authorization'):
self.options = parse_dict_header(s_auth.replace('Digest ', ''))
authorization = self.encode(request.method, request.url)
params = request.inp_params.copy()
headers = params.pop('headers', [])
headers.append(('authorization', self.encode(
request.method, request.url)))
headers.append(('authorization', authorization))
params['headers'] = headers
response.request_again = request_again(request.method,
request.url,
Expand Down
21 changes: 19 additions & 2 deletions pulsar/apps/socket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,26 @@ class EchoServerProtocol(pulsar.ProtocolConsumer):
python script.py --bind 127.0.0.1:8070
This will listen for both ipv4 and ipv6 sockets on all hosts on port 8080::
This will accept connection from the 127.0.0.1 network interface and port 8070.
This means pulsar will be able to accept connections only from clients
running into the same computer it is running.
On the other hand, it is possible to listen for connections from all
the network interfaces available on the server by specifying ``:<port>``.
For example, this will listen for both ipv4 and ipv6 sockets **on all hosts**
on port 8080::
python script.py --bind :8080
**Use this notation when running pulsar inside Docker or any other container**.
You can bind to a random available port by specifying 0 as the port number::
python script.py --bind :0
useful during testing.
backlog
---------
To specify the maximum number of queued connections you can use the
Expand Down Expand Up @@ -128,7 +144,8 @@ class Bind(SocketSetting):
The socket to bind.
A string of the form: ``HOST``, ``HOST:PORT``, ``unix:PATH``.
An IP is a valid HOST.
An IP is a valid HOST. Specify ``:PORT`` to listen for connections
from all the network interfaces available on the server.
"""


Expand Down
36 changes: 25 additions & 11 deletions pulsar/apps/wsgi/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ def stream_mapping(value, request):
def attr_iter(attrs):
for k in sorted(attrs):
v = attrs[k]
if v is not None:
if v is True:
yield " %s" % k
elif v is not None:
yield " %s='%s'" % (k, escape(v, force=True))


Expand Down Expand Up @@ -245,6 +247,11 @@ def __repr__(self):
def __str__(self):
return self.__repr__()

def extend(self, iterable):
"""Extend this string with an iterable"""
for child in iterable:
self.append(child)

def append(self, child):
'''Append ``child`` to the list of :attr:`children`.
Expand Down Expand Up @@ -810,12 +817,6 @@ def absolute_path(self, path, minify=True):
else:
return path

def extend(self, iterable):
'''Add a list (iterable) of media to this container
'''
for media in iterable:
self.append(media)


class Links(Media):
'''A :class:`.Media` container for ``link`` tags.
Expand Down Expand Up @@ -882,15 +883,15 @@ def script(self, src, type=None, **kwargs):
path = self.absolute_path(src)
return Html('script', src=path, type=type, **kwargs).render()

def append(self, src=None, type=None, **kwargs):
def append(self, src=None, **kwargs):
'''add a new script to the container.
:param src: a ``string`` representing an absolute path to the script
or relative path (does not start with ``http`` or ``/``), in which
case the :attr:`Media.media_path` attribute is prepended.
'''
if src:
script = self.script(src, type=type, **kwargs)
script = self.script(src, **kwargs)
if script not in self.children:
self.children.append(script)

Expand Down Expand Up @@ -1091,6 +1092,18 @@ def __add__(self, other):
return self


class Body(Html):

def __init__(self, **kwargs):
super().__init__('body')
self.scripts = Scripts(**kwargs)
self.before_render(add_scripts)


def add_scripts(request, body):
body.append(body.scripts)


class HtmlDocument(Html):
'''An :class:`.Html` component rendered as an HTML5_ document.
Expand All @@ -1117,13 +1130,14 @@ def __init__(self, title=None, media_path='/media/', charset=None,
super().__init__(None, **params)
self.head = Head(title=title, media_path=media_path, minified=minified,
charset=charset, asset_protocol=asset_protocol)
self.body = Html('body')
self.body = Body(media_path=media_path, minified=minified,
asset_protocol=asset_protocol)

def do_stream(self, request):
# stream the body
body = self.body.render(request)
# the body has asynchronous components
# delay the header untl later
# delay the header until later
if isawaitable(body):
yield self._html(request, body)

Expand Down
10 changes: 10 additions & 0 deletions tests/http/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,16 @@ async def test_digest_authentication(self):
auth=HTTPDigestAuth('luca', 'bla'))
self.assertEqual(r.status_code, 200)

async def test_digest_authentication_failure(self):
sessions = self.client()
r = await sessions.get(self.httpbin(
'digest-auth/luca/bla/auth'))
self.assertEqual(r.status_code, 401)
r = await sessions.get(self.httpbin(
'digest-auth/luca/bla/auth'),
auth=HTTPDigestAuth('luca', 'foo'))
self.assertEqual(r.status_code, 401)

async def test_missing_host_400(self):
http = self._client

Expand Down
7 changes: 6 additions & 1 deletion tests/wsgi/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def testEmptyAttr(self):
self.assertTrue(" value='0'" in c.flatatt())
self.assertEqual(c.attr('value'), 0)

def test_true_attribute(self):
txt = wsgi.Html('div', bla=True).render()
self.assertEqual(txt, '<div bla></div>')

def test_option_empty_attribute(self):
opt = wsgi.Html('option', '--------', value='')
self.assertEqual(opt.attr('value'), '')
Expand Down Expand Up @@ -174,7 +178,8 @@ def test_document_empty_body(self):
'<title>test</title>',
"<meta charset='utf-8'>",
'</head>',
'<body></body>',
'<body>',
'</body>',
'</html>')))

def test_document(self):
Expand Down

0 comments on commit ad1f343

Please sign in to comment.