# Twisted, the Framework of Your Internet
# Copyright (C) 2001-2003 Matthew W. Lefkowitz
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Port description language
This module implements a description mini-language for ports, and provides
functions to parse it and to use it to directly construct appropriate
network server services or to directly listen on them.
Here are some examples::
>>> s=service("80", server.Site())
>>> s=service("tcp:80", server.Site())
>>> s=service("tcp:80:interface=127.0.0.1", server.Site())
>>> s=service("ssl:443", server.Site())
>>> s=service("ssl:443:privateKey=mykey.pem", server.Site())
>>> s=service("ssl:443:privateKey=mykey.pem:certKey=cert.pem", server.Site())
>>> s=service("unix:/var/run/finger", FingerFactory())
>>> s=service("unix:/var/run/finger:mode=660", FingerFactory())
>>> p=listen("80", server.Site())
>>> p=listen("tcp:80", server.Site())
>>> p=listen("tcp:80:interface=127.0.0.1", server.Site())
>>> p=listen("ssl:443", server.Site())
>>> p=listen("ssl:443:privateKey=mykey.pem", server.Site())
>>> p=listen("ssl:443:privateKey=mykey.pem:certKey=cert.pem", server.Site())
>>> p=listen("unix:/var/run/finger", FingerFactory())
>>> p=listen("unix:/var/run/finger:mode=660", FingerFactory())
See specific function documentation for more information.
API Stability: unstable
Maintainer: U{Moshe Zadka<mailto:moshez@twistedmatrix.com>}
"""
from __future__ import generators
def _parseTCP(factory, port, interface="", backlog=5):
return (int(port), factory), {'interface': interface,
'backlog': int(backlog)}
def _parseUNIX(factory, address, mode='666', backlog=5):
return (address, factory), {'mode': int(mode, 8), 'backlog': int(backlog)}
def _parseSSL(factory, port, privateKey="server.pem", certKey=None,
sslmethod=None, interface='', backlog=5):
from twisted.internet import ssl
if certKey is None:
certKey = privateKey
kw = {}
if sslmethod is not None:
kw['sslmethod'] = getattr(ssl.SSL, sslmethod)
cf = ssl.DefaultOpenSSLContextFactory(privateKey, certKey, **kw)
return ((int(port), factory, cf),
{'interface': interface, 'backlog': int(backlog)})
_funcs = {"tcp": _parseTCP,
"unix": _parseUNIX,
"ssl": _parseSSL}
_OP, _STRING = range(2)
def _tokenize(description):
current = ''
ops = ':='
nextOps = {':': ':=', '=': ':'}
description = iter(description)
for n in description:
if n in ops:
yield _STRING, current
yield _OP, n
current = ''
ops = nextOps[n]
elif n=='\\':
current += description.next()
else:
current += n
yield _STRING, current
def _parse(description):
args, kw = [], {}
def add(sofar):
if len(sofar)==1:
args.append(sofar[0])
else:
kw[sofar[0]] = sofar[1]
sofar = ()
for (type, value) in _tokenize(description):
if type is _STRING:
sofar += (value,)
elif value==':':
add(sofar)
sofar = ()
add(sofar)
return args, kw
def parse(description, factory, default=None):
"""Parse a description of a reliable virtual circuit server
@type description: C{str}
@type factory: C{twisted.internet.interfaces.IProtocolFactory}
@type default: C{str} or C{None}
@rtype: C{tuple}
@return: a tuple of string, tuple and dictionary. The string
is the name of the method (sans C{'listen'}) to call, and
the tuple and dictionary are the arguments and keyword arguments
to the method.
@raises: C{ValueError} if the string is formatted incorrectly,
C{KeyError} if the type is other than unix, ssl or tcp.
Parse the description of a reliable virtual circuit server (that is,
a TCP port, a UNIX domain socket or an SSL port) and return the
data necessary to call the reactor methods to listen on the given
socket with the given factory.
An argument with no colons means a default port. Usually the default
port is C{tcp}, but passing a non-C{None} value as C{default} will
set that as the default. Otherwise, it is a colon-separated string.
The first part means the type -- currently,
it can only be ssl, unix or tcp. After that, comes a list of
arguments. Arguments can be positional or keyword, and can be mixed.
Keyword arguments are indicated by C{'name=value'}. If a value is supposed
to contain a C{':'}, a C{'='} or a C{'\\'}, escape it with a C{'\\'}.
For TCP, the arguments are the port (port number) and, optionally the
interface (interface on which to listen) and backlog (how many clients to
keep in the backlog).
For UNIX domain sockets, the arguments are address (the file name of the
socket) and optionally the mode (the mode bits of the file, as an octal
number) and the backlog (how many clients to keep in the backlog).
For SSL sockets, the arguments are the port (port number) and, optionally,
the privateKey (file in which the private key is in), certKey (file in
which the certification is in), sslmethod (the name of the SSL method
to allow), the interface (interface on which to listen) and the
backlog (how many clients to keep in the backlog).
"""
args, kw = _parse(description)
if not args or (len(args)==1 and not kw):
args[0:0] = [default or 'tcp']
return (args[0].upper(),)+_funcs[args[0]](factory, *args[1:], **kw)
def service(description, factory, default=None):
"""Return the service corresponding to a description
@type description: C{str}
@type factory: C{twisted.internet.interfaces.IProtocolFactory}
@type default: C{str} or C{None}
@rtype: C{twisted.application.service.IService}
@return: the service corresponding to a description of a reliable
virtual circuit server.
See the documentation of the C{parse} function for description
of the semantics of the arguments.
"""
from twisted.application import internet
name, args, kw = parse(description, factory, default)
return getattr(internet, name+'Server')(*args, **kw)
def listen(description, factory, default=None):
"""Listen on a port corresponding to a description
@type description: C{str}
@type factory: C{twisted.internet.interfaces.IProtocolFactory}
@type default: C{str} or C{None}
@rtype: C{twisted.internet.interfaces.IListeningPort}
@return: the port corresponding to a description of a reliable
virtual circuit server.
See the documentation of the C{parse} function for description
of the semantics of the arguments.
"""
from twisted.internet import reactor
name, args, kw = parse(description, factory, default)
return getattr(reactor, 'listen'+name)(*args, **kw)
__all__ = ['parse', 'service', 'listen']
syntax highlighted by Code2HTML, v. 0.9.1