<commit_before># -*- coding: utf-8 -*-
"""
This module contains functions for formulating systems of Ordinary Differential
Equations (ODE-systems) which may be integrated numerically to model temporal
evolution of concentrations in reaction systems.
"""
from __future__ import (absolute_import, division, print_function)

from itertools import chain
import math

from ..units import to_unitless, get_derived_unit
from ..util.pyutil import deprecated
from ..util._expr import Expr
from .rates import RateExpr, MassAction, law_of_mass_action_rates as _lomar

law_of_mass_action_rates = deprecated(
    use_instead='.rates.law_of_mass_action_rates')(_lomar)

def dCdt(rsys, rates):
    """ Returns a list of the time derivatives of the concentrations

    Parameters
    ----------
    rsys: ReactionSystem instance
    rates: array_like
        rates (to be weighted by stoichiometries) of the reactions
        in ``rsys``

    Examples
    --------
    >>> from chempy import ReactionSystem, Reaction
    >>> line, keys = 'H2O -> H+ + OH- ; 1e-4', 'H2O H+ OH-'
    >>> rsys = ReactionSystem([Reaction.from_string(line, keys)], keys)
    >>> dCdt(rsys, [0.0054])
    [-0.0054, 0.0054, 0.0054]

    """
    f = [0]*rsys.ns
    net_stoichs = rsys.net_stoichs()
    for idx_s in range(rsys.ns):
        for idx_r in range(rsys.nr):
            f[idx_s] += net_stoichs[idx_r, idx_s]*rates[idx_r]
    return f

def get_odesys(rsys, include_params=False, substitutions=None,
               SymbolicSys=None,
               unit_registry=None, output_conc_unit=None,
               output_time_unit=None, **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    Parameters
    ----------
    rsys : ReactionSystem
        note that if :attr:`param` if not RateExpr it will be inspected for
        :meth:`_as_RateExpr`, lacking such it will be used to construct a
        :class:`MassAction` instance.
    include_params : bool (default: False)
        whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be tuple like: (new_vars, callback)
    SymbolicSys : class (optional)
        default : :class:`pyneqsys.SymbolicSys`
    unit_registry: dict (optional)
        see :func:`chempy.units.get_derived_units`
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    \*\*kwargs :
        Keyword arguemnts pass on to `SymbolicSys`

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    param_keys
    arg_keys
    p_units

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys = get_odesys(rsys)[0]
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([ 0.7042,  0.0042,  0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    substance_keys = list(rsys.substances.keys())

    if 'names' not in kwargs:
        kwargs['names'] = list(rsys.substances.values())  # pyodesys>=0.5.3

    def _param(rxn):
        if isinstance(rxn.param, RateExpr):
            return rxn.param
        else:
            try:
                return rxn.param._as_RateExpr(rxn)
            except AttributeError:
                return MassAction([rxn.param], rxn=rxn)
    r_exprs = [_param(rxn) for rxn in rsys.rxns]

    _original_param_keys = set.union(*(set(ratex.parameter_keys) for ratex in r_exprs))
    _from_subst = set()
    _active_subst = {}
    _passive_subst = {}
    substitutions = substitutions or {}
    for key, v in substitutions.items():
        if key not in _original_param_keys:
            raise ValueError("Substitution: '%s' does not appear in any rate expressions.")
        if isinstance(v, Expr):
            _from_subst.update(v.parameter_keys)
            _active_subst[key] = v
        else:
            _passive_subst[key] = v
    param_keys = list(filter(lambda x: x not in substitutions, _original_param_keys.union(_from_subst)))

    arg_keys = []
    p_defaults = []
    if not include_params:
        for ratex in r_exprs:
            if ratex.arg_keys is not None:
                arg_keys.extend(ratex.arg_keys)
                p_defaults.extend(ratex.args)

    if unit_registry is None:
        def pre_processor(x, y, p):
            return (
                x,
                rsys.as_per_substance_array(y),
                [p[k] for k in param_keys] + [p[k] for k in arg_keys]
            )

        def post_processor(x, y, p):
            return (
                x,
                y,  # dict(zip(substance_keys, y)),
                dict(zip(param_keys+arg_keys, p))
            )
        p_units = [None]*(len(param_keys) + len(arg_keys))
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        p_units = [get_derived_unit(unit_registry, k) for k in param_keys]
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_rates = ratex._dedimensionalisation(unit_registry)
            p_units.extend(_pu)
            new_r_exprs.append(_new_rates)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def pre_processor(x, y, p):
            return (
                to_unitless(x, time_unit),
                rsys.as_per_substance_array(to_unitless(y, conc_unit)),
                [to_unitless(p[k], p_unit) for k, p_unit in zip(chain(param_keys, arg_keys), p_units)]
            )

        def post_processor(x, y, p):
            time = x*time_unit
            if output_time_unit is not None:
                time = time.rescale(output_time_unit)
            conc = y*conc_unit
            if output_conc_unit is not None:
                conc = conc.rescale(output_conc_unit)
            return time, conc, [elem*p_unit for elem, p_unit in zip(p, p_units)]

    kwargs['pre_processors'] = [pre_processor] + kwargs.get('pre_processors', [])
    kwargs['post_processors'] = kwargs.get('post_processors', []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(chain(
            zip(substance_keys, y),
            zip(param_keys, p[:len(param_keys)]),
            zip(arg_keys, p[len(param_keys):])
        ))
        for k, act in _active_subst.items():
            if unit_registry is not None:
                _, act = act._dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return dCdt(rsys, [rat(variables, backend=backend) for rat in r_exprs])

    return SymbolicSys.from_callback(
        dydt, len(substance_keys),
        len(param_keys) + (0 if include_params else len(arg_keys)),
        **kwargs), param_keys, arg_keys, p_units
<commit_msg>tweak<commit_after># -*- coding: utf-8 -*-
"""
This module contains functions for formulating systems of Ordinary Differential
Equations (ODE-systems) which may be integrated numerically to model temporal
evolution of concentrations in reaction systems.
"""
from __future__ import (absolute_import, division, print_function)

from itertools import chain
import math

from ..units import to_unitless, get_derived_unit
from ..util.pyutil import deprecated
from ..util._expr import Expr
from .rates import RateExpr, MassAction, law_of_mass_action_rates as _lomar

law_of_mass_action_rates = deprecated(
    use_instead='.rates.law_of_mass_action_rates')(_lomar)

def dCdt(rsys, rates):
    """ Returns a list of the time derivatives of the concentrations

    Parameters
    ----------
    rsys: ReactionSystem instance
    rates: array_like
        rates (to be weighted by stoichiometries) of the reactions
        in ``rsys``

    Examples
    --------
    >>> from chempy import ReactionSystem, Reaction
    >>> line, keys = 'H2O -> H+ + OH- ; 1e-4', 'H2O H+ OH-'
    >>> rsys = ReactionSystem([Reaction.from_string(line, keys)], keys)
    >>> dCdt(rsys, [0.0054])
    [-0.0054, 0.0054, 0.0054]

    """
    f = [0]*rsys.ns
    net_stoichs = rsys.net_stoichs()
    for idx_s in range(rsys.ns):
        for idx_r in range(rsys.nr):
            f[idx_s] += net_stoichs[idx_r, idx_s]*rates[idx_r]
    return f

def get_odesys(rsys, include_params=False, substitutions=None,
               SymbolicSys=None,
               unit_registry=None, output_conc_unit=None,
               output_time_unit=None, **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    Parameters
    ----------
    rsys : ReactionSystem
        note that if :attr:`param` if not RateExpr it will be inspected for
        :meth:`_as_RateExpr`, lacking such it will be used to construct a
        :class:`MassAction` instance.
    include_params : bool (default: False)
        whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be tuple like: (new_vars, callback)
    SymbolicSys : class (optional)
        default : :class:`pyneqsys.SymbolicSys`
    unit_registry: dict (optional)
        see :func:`chempy.units.get_derived_units`
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    \*\*kwargs :
        Keyword arguemnts pass on to `SymbolicSys`

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    param_keys
    arg_keys
    p_units

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys = get_odesys(rsys)[0]
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([ 0.7042,  0.0042,  0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    substance_keys = list(rsys.substances.keys())

    if 'names' not in kwargs:
        kwargs['names'] = list(rsys.substances.values())  # pyodesys>=0.5.3

    def _param(rxn):
        if isinstance(rxn.param, RateExpr):
            return rxn.param
        else:
            try:
                convertible = rxn.param._as_RateExpr
            except AttributeError:
                return MassAction([rxn.param], rxn=rxn)
            else:
                return convertible(rxn)
    r_exprs = [_param(rxn) for rxn in rsys.rxns]

    _original_param_keys = set.union(*(set(ratex.parameter_keys) for ratex in r_exprs))
    _from_subst = set()
    _active_subst = {}
    _passive_subst = {}
    substitutions = substitutions or {}
    for key, v in substitutions.items():
        if key not in _original_param_keys:
            raise ValueError("Substitution: '%s' does not appear in any rate expressions.")
        if isinstance(v, Expr):
            _from_subst.update(v.parameter_keys)
            _active_subst[key] = v
        else:
            _passive_subst[key] = v
    param_keys = list(filter(lambda x: x not in substitutions, _original_param_keys.union(_from_subst)))

    arg_keys = []
    p_defaults = []
    if not include_params:
        for ratex in r_exprs:
            if ratex.arg_keys is not None:
                arg_keys.extend(ratex.arg_keys)
                p_defaults.extend(ratex.args)

    if unit_registry is None:
        def pre_processor(x, y, p):
            return (
                x,
                rsys.as_per_substance_array(y),
                [p[k] for k in param_keys] + [p[k] for k in arg_keys]
            )

        def post_processor(x, y, p):
            return (
                x,
                y,  # dict(zip(substance_keys, y)),
                dict(zip(param_keys+arg_keys, p))
            )
        p_units = [None]*(len(param_keys) + len(arg_keys))
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        p_units = [get_derived_unit(unit_registry, k) for k in param_keys]
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_rates = ratex._dedimensionalisation(unit_registry)
            p_units.extend(_pu)
            new_r_exprs.append(_new_rates)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def pre_processor(x, y, p):
            return (
                to_unitless(x, time_unit),
                rsys.as_per_substance_array(to_unitless(y, conc_unit)),
                [to_unitless(p[k], p_unit) for k, p_unit in zip(chain(param_keys, arg_keys), p_units)]
            )

        def post_processor(x, y, p):
            time = x*time_unit
            if output_time_unit is not None:
                time = time.rescale(output_time_unit)
            conc = y*conc_unit
            if output_conc_unit is not None:
                conc = conc.rescale(output_conc_unit)
            return time, conc, [elem*p_unit for elem, p_unit in zip(p, p_units)]

    kwargs['pre_processors'] = [pre_processor] + kwargs.get('pre_processors', [])
    kwargs['post_processors'] = kwargs.get('post_processors', []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(chain(
            zip(substance_keys, y),
            zip(param_keys, p[:len(param_keys)]),
            zip(arg_keys, p[len(param_keys):])
        ))
        for k, act in _active_subst.items():
            if unit_registry is not None:
                _, act = act._dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return dCdt(rsys, [rat(variables, backend=backend) for rat in r_exprs])

    return SymbolicSys.from_callback(
        dydt, len(substance_keys),
        len(param_keys) + (0 if include_params else len(arg_keys)),
        **kwargs), param_keys, arg_keys, p_units
<|endoftext|>


<commit_before>"""Utilities to manage open connections."""

from __future__ import absolute_import, division, print_function
__metaclass__ = type

import io
import os
import select
import socket
import time

from . import errors
from .makefile import MakeFile

import six

try:
    import fcntl
except ImportError:
    try:
        from ctypes import windll, WinError
        import ctypes.wintypes
        _SetHandleInformation = windll.kernel32.SetHandleInformation
        _SetHandleInformation.argtypes = [
            ctypes.wintypes.HANDLE,
            ctypes.wintypes.DWORD,
            ctypes.wintypes.DWORD,
        ]
        _SetHandleInformation.restype = ctypes.wintypes.BOOL
    except ImportError:
        def prevent_socket_inheritance(sock):
            """Stub inheritance prevention.

            Dummy function, since neither fcntl nor ctypes are available.
            """
            pass
    else:
        def prevent_socket_inheritance(sock):
            """Mark the given socket fd as non-inheritable (Windows)."""
            if not _SetHandleInformation(sock.fileno(), 1, 0):
                raise WinError()
else:
    def prevent_socket_inheritance(sock):
        """Mark the given socket fd as non-inheritable (POSIX)."""
        fd = sock.fileno()
        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)

class ConnectionManager:
    """Class which manages HTTPConnection objects.

    This is for connections which are being kept-alive for follow-up requests.
    """

    def __init__(self, server):
        """Initialize ConnectionManager object.

        Args:
            server (cheroot.server.HTTPServer): web server object
                that uses this ConnectionManager instance.
        """
        self.server = server
        self.connections = []

    def put(self, conn):
        """Put idle connection into the ConnectionManager to be managed.

        Args:
            conn (cheroot.server.HTTPConnection): HTTP connection
                to be managed.
        """
        conn.last_used = time.time()
        conn.ready_with_data = conn.rfile.has_data()
        self.connections.append(conn)

    def expire(self):
        """Expire least recently used connections.

        This happens if there are either too many open connections, or if the
        connections have been timed out.

        This should be called periodically.
        """
        if not self.connections:
            return

        # Look at the first connection - if it can be closed, then do
        # that, and wait for get_conn to return it.
        conn = self.connections[0]
        if conn.closeable:
            return

        # Connection too old?
        if (conn.last_used + self.server.timeout) < time.time():
            conn.closeable = True
            return

    def get_conn(self, server_socket):
        """Return a HTTPConnection object which is ready to be handled.

        A connection returned by this method should be ready for a worker
        to handle it. If there are no connections ready, None will be
        returned.

        Any connection returned by this method will need to be `put`
        back if it should be examined again for another request.

        Args:
            server_socket (socket.socket): Socket to listen to for new
            connections.
        Returns:
            cheroot.server.HTTPConnection instance, or None.

        """
        # Grab file descriptors from sockets, but stop if we find a
        # connection which is already marked as ready.
        socket_dict = {}
        for conn in self.connections:
            if conn.closeable or conn.ready_with_data:
                break
            socket_dict[conn.socket.fileno()] = conn
        else:
            # No ready connection.
            conn = None

        # We have a connection ready for use.
        if conn:
            self.connections.remove(conn)
            return conn

        # Will require a select call.
        ss_fileno = server_socket.fileno()
        socket_dict[ss_fileno] = server_socket
        try:
            rlist, _, _ = select.select(list(socket_dict), [], [], 0.1)
            # No available socket.
            if not rlist:
                return None
        except OSError:
            # Mark any connection which no longer appears valid.
            for fno, conn in list(socket_dict.items()):
                # If the server socket is invalid, we'll just ignore it and
                # wait to be shutdown.
                if fno == ss_fileno:
                    continue
                try:
                    os.fstat(fno)
                except OSError:
                    # Socket is invalid, close the connection, insert at
                    # the front.
                    self.connections.remove(conn)
                    self.connections.insert(0, conn)
                    conn.closeable = True

            # Wait for the next tick to occur.
            return None

        try:
            # See if we have a new connection coming in.
            rlist.remove(ss_fileno)
        except ValueError:
            # No new connection, but reuse existing socket.
            conn = socket_dict[rlist.pop()]
        else:
            conn = server_socket

        # All remaining connections in rlist should be marked as ready.
        for fno in rlist:
            socket_dict[fno].ready_with_data = True

        # New connection.
        if conn is server_socket:
            return self._from_server_socket(server_socket)

        self.connections.remove(conn)
        return conn

    def _from_server_socket(self, server_socket):
        try:
            s, addr = server_socket.accept()
            if self.server.stats['Enabled']:
                self.server.stats['Accepts'] += 1
            prevent_socket_inheritance(s)
            if hasattr(s, 'settimeout'):
                s.settimeout(self.server.timeout)

            mf = MakeFile
            ssl_env = {}
            # if ssl cert and key are set, we try to be a secure HTTP server
            if self.server.ssl_adapter is not None:
                try:
                    s, ssl_env = self.server.ssl_adapter.wrap(s)
                except errors.NoSSLError:
                    msg = (
                        'The client sent a plain HTTP request, but '
                        'this server only speaks HTTPS on this port.'
                    )
                    buf = [
                        '%s 400 Bad Request\r\n' % self.server.protocol,
                        'Content-Length: %s\r\n' % len(msg),
                        'Content-Type: text/plain\r\n\r\n',
                        msg,
                    ]

                    sock_to_make = s if not six.PY2 else s._sock
                    wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE)
                    try:
                        wfile.write(''.join(buf).encode('ISO-8859-1'))
                    except socket.error as ex:
                        if ex.args[0] not in errors.socket_errors_to_ignore:
                            raise
                    return
                if not s:
                    return
                mf = self.server.ssl_adapter.makefile
                # Re-apply our timeout since we may have a new socket object
                if hasattr(s, 'settimeout'):
                    s.settimeout(self.server.timeout)

            conn = self.server.ConnectionClass(self.server, s, mf)

            if not isinstance(
                    self.server.bind_addr,
                    (six.text_type, six.binary_type),
            ):
                # optional values
                # Until we do DNS lookups, omit REMOTE_HOST
                if addr is None:  # sometimes this can happen
                    # figure out if AF_INET or AF_INET6.
                    if len(s.getsockname()) == 2:
                        # AF_INET
                        addr = ('0.0.0.0', 0)
                    else:
                        # AF_INET6
                        addr = ('::', 0)
                conn.remote_addr = addr[0]
                conn.remote_port = addr[1]

            conn.ssl_env = ssl_env
            return conn

        except socket.timeout:
            # The only reason for the timeout in start() is so we can
            # notice keyboard interrupts on Win32, which don't interrupt
            # accept() by default
            return
        except socket.error as ex:
            if self.server.stats['Enabled']:
                self.server.stats['Socket Errors'] += 1
            if ex.args[0] in errors.socket_error_eintr:
                # I *think* this is right. EINTR should occur when a signal
                # is received during the accept() call; all docs say retry
                # the call, and I *think* I'm reading it right that Python
                # will then go ahead and poll for and handle the signal
                # elsewhere. See
                # https://github.com/cherrypy/cherrypy/issues/707.
                return
            if ex.args[0] in errors.socket_errors_nonblocking:
                # Just try again. See
                # https://github.com/cherrypy/cherrypy/issues/479.
                return
            if ex.args[0] in errors.socket_errors_to_ignore:
                # Our socket was closed.
                # See https://github.com/cherrypy/cherrypy/issues/686.
                return
            raise

    def close(self):
        """Close all monitored connections."""
        for conn in self.connections[:]:
            conn.close()
        self.connections = []

    @property
    def can_add_keepalive_connection(self):
        ka_limit = self.server.keep_alive_conn_limit
        stats = [ka_limit, len(self.connections)]
        return ka_limit is None or len(self.connections) < ka_limit
<commit_msg>Drop an unused leftover arg from `cheroot.connections`<commit_after>"""Utilities to manage open connections."""

from __future__ import absolute_import, division, print_function
__metaclass__ = type

import io
import os
import select
import socket
import time

from . import errors
from .makefile import MakeFile

import six

try:
    import fcntl
except ImportError:
    try:
        from ctypes import windll, WinError
        import ctypes.wintypes
        _SetHandleInformation = windll.kernel32.SetHandleInformation
        _SetHandleInformation.argtypes = [
            ctypes.wintypes.HANDLE,
            ctypes.wintypes.DWORD,
            ctypes.wintypes.DWORD,
        ]
        _SetHandleInformation.restype = ctypes.wintypes.BOOL
    except ImportError:
        def prevent_socket_inheritance(sock):
            """Stub inheritance prevention.

            Dummy function, since neither fcntl nor ctypes are available.
            """
            pass
    else:
        def prevent_socket_inheritance(sock):
            """Mark the given socket fd as non-inheritable (Windows)."""
            if not _SetHandleInformation(sock.fileno(), 1, 0):
                raise WinError()
else:
    def prevent_socket_inheritance(sock):
        """Mark the given socket fd as non-inheritable (POSIX)."""
        fd = sock.fileno()
        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)

class ConnectionManager:
    """Class which manages HTTPConnection objects.

    This is for connections which are being kept-alive for follow-up requests.
    """

    def __init__(self, server):
        """Initialize ConnectionManager object.

        Args:
            server (cheroot.server.HTTPServer): web server object
                that uses this ConnectionManager instance.
        """
        self.server = server
        self.connections = []

    def put(self, conn):
        """Put idle connection into the ConnectionManager to be managed.

        Args:
            conn (cheroot.server.HTTPConnection): HTTP connection
                to be managed.
        """
        conn.last_used = time.time()
        conn.ready_with_data = conn.rfile.has_data()
        self.connections.append(conn)

    def expire(self):
        """Expire least recently used connections.

        This happens if there are either too many open connections, or if the
        connections have been timed out.

        This should be called periodically.
        """
        if not self.connections:
            return

        # Look at the first connection - if it can be closed, then do
        # that, and wait for get_conn to return it.
        conn = self.connections[0]
        if conn.closeable:
            return

        # Connection too old?
        if (conn.last_used + self.server.timeout) < time.time():
            conn.closeable = True
            return

    def get_conn(self, server_socket):
        """Return a HTTPConnection object which is ready to be handled.

        A connection returned by this method should be ready for a worker
        to handle it. If there are no connections ready, None will be
        returned.

        Any connection returned by this method will need to be `put`
        back if it should be examined again for another request.

        Args:
            server_socket (socket.socket): Socket to listen to for new
            connections.
        Returns:
            cheroot.server.HTTPConnection instance, or None.

        """
        # Grab file descriptors from sockets, but stop if we find a
        # connection which is already marked as ready.
        socket_dict = {}
        for conn in self.connections:
            if conn.closeable or conn.ready_with_data:
                break
            socket_dict[conn.socket.fileno()] = conn
        else:
            # No ready connection.
            conn = None

        # We have a connection ready for use.
        if conn:
            self.connections.remove(conn)
            return conn

        # Will require a select call.
        ss_fileno = server_socket.fileno()
        socket_dict[ss_fileno] = server_socket
        try:
            rlist, _, _ = select.select(list(socket_dict), [], [], 0.1)
            # No available socket.
            if not rlist:
                return None
        except OSError:
            # Mark any connection which no longer appears valid.
            for fno, conn in list(socket_dict.items()):
                # If the server socket is invalid, we'll just ignore it and
                # wait to be shutdown.
                if fno == ss_fileno:
                    continue
                try:
                    os.fstat(fno)
                except OSError:
                    # Socket is invalid, close the connection, insert at
                    # the front.
                    self.connections.remove(conn)
                    self.connections.insert(0, conn)
                    conn.closeable = True

            # Wait for the next tick to occur.
            return None

        try:
            # See if we have a new connection coming in.
            rlist.remove(ss_fileno)
        except ValueError:
            # No new connection, but reuse existing socket.
            conn = socket_dict[rlist.pop()]
        else:
            conn = server_socket

        # All remaining connections in rlist should be marked as ready.
        for fno in rlist:
            socket_dict[fno].ready_with_data = True

        # New connection.
        if conn is server_socket:
            return self._from_server_socket(server_socket)

        self.connections.remove(conn)
        return conn

    def _from_server_socket(self, server_socket):
        try:
            s, addr = server_socket.accept()
            if self.server.stats['Enabled']:
                self.server.stats['Accepts'] += 1
            prevent_socket_inheritance(s)
            if hasattr(s, 'settimeout'):
                s.settimeout(self.server.timeout)

            mf = MakeFile
            ssl_env = {}
            # if ssl cert and key are set, we try to be a secure HTTP server
            if self.server.ssl_adapter is not None:
                try:
                    s, ssl_env = self.server.ssl_adapter.wrap(s)
                except errors.NoSSLError:
                    msg = (
                        'The client sent a plain HTTP request, but '
                        'this server only speaks HTTPS on this port.'
                    )
                    buf = [
                        '%s 400 Bad Request\r\n' % self.server.protocol,
                        'Content-Length: %s\r\n' % len(msg),
                        'Content-Type: text/plain\r\n\r\n',
                        msg,
                    ]

                    sock_to_make = s if not six.PY2 else s._sock
                    wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE)
                    try:
                        wfile.write(''.join(buf).encode('ISO-8859-1'))
                    except socket.error as ex:
                        if ex.args[0] not in errors.socket_errors_to_ignore:
                            raise
                    return
                if not s:
                    return
                mf = self.server.ssl_adapter.makefile
                # Re-apply our timeout since we may have a new socket object
                if hasattr(s, 'settimeout'):
                    s.settimeout(self.server.timeout)

            conn = self.server.ConnectionClass(self.server, s, mf)

            if not isinstance(
                    self.server.bind_addr,
                    (six.text_type, six.binary_type),
            ):
                # optional values
                # Until we do DNS lookups, omit REMOTE_HOST
                if addr is None:  # sometimes this can happen
                    # figure out if AF_INET or AF_INET6.
                    if len(s.getsockname()) == 2:
                        # AF_INET
                        addr = ('0.0.0.0', 0)
                    else:
                        # AF_INET6
                        addr = ('::', 0)
                conn.remote_addr = addr[0]
                conn.remote_port = addr[1]

            conn.ssl_env = ssl_env
            return conn

        except socket.timeout:
            # The only reason for the timeout in start() is so we can
            # notice keyboard interrupts on Win32, which don't interrupt
            # accept() by default
            return
        except socket.error as ex:
            if self.server.stats['Enabled']:
                self.server.stats['Socket Errors'] += 1
            if ex.args[0] in errors.socket_error_eintr:
                # I *think* this is right. EINTR should occur when a signal
                # is received during the accept() call; all docs say retry
                # the call, and I *think* I'm reading it right that Python
                # will then go ahead and poll for and handle the signal
                # elsewhere. See
                # https://github.com/cherrypy/cherrypy/issues/707.
                return
            if ex.args[0] in errors.socket_errors_nonblocking:
                # Just try again. See
                # https://github.com/cherrypy/cherrypy/issues/479.
                return
            if ex.args[0] in errors.socket_errors_to_ignore:
                # Our socket was closed.
                # See https://github.com/cherrypy/cherrypy/issues/686.
                return
            raise

    def close(self):
        """Close all monitored connections."""
        for conn in self.connections[:]:
            conn.close()
        self.connections = []

    @property
    def can_add_keepalive_connection(self):
        ka_limit = self.server.keep_alive_conn_limit
        return ka_limit is None or len(self.connections) < ka_limit
<|endoftext|>


<commit_before>"""Utilities to manage open connections."""

from __future__ import absolute_import, division, print_function
__metaclass__ = type

import collections
import copy
import io
import os
import socket
import time

from . import errors
from ._compat import selectors
from ._compat import suppress
from .makefile import MakeFile

import six

try:
    import fcntl
except ImportError:
    try:
        from ctypes import windll, WinError
        import ctypes.wintypes
        _SetHandleInformation = windll.kernel32.SetHandleInformation
        _SetHandleInformation.argtypes = [
            ctypes.wintypes.HANDLE,
            ctypes.wintypes.DWORD,
            ctypes.wintypes.DWORD,
        ]
        _SetHandleInformation.restype = ctypes.wintypes.BOOL
    except ImportError:
        def prevent_socket_inheritance(sock):
            """Stub inheritance prevention.

            Dummy function, since neither fcntl nor ctypes are available.
            """
            pass
    else:
        def prevent_socket_inheritance(sock):
            """Mark the given socket fd as non-inheritable (Windows)."""
            if not _SetHandleInformation(sock.fileno(), 1, 0):
                raise WinError()
else:
    def prevent_socket_inheritance(sock):
        """Mark the given socket fd as non-inheritable (POSIX)."""
        fd = sock.fileno()
        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)

class ConnectionManager:
    """Class which manages HTTPConnection objects.

    This is for connections which are being kept-alive for follow-up requests.
    """

    def __init__(self, server):
        """Initialize ConnectionManager object.

        Args:
            server (cheroot.server.HTTPServer): web server object
                that uses this ConnectionManager instance.
        """
        self.server = server
        self._readable_conns = collections.deque()
        self._selector = selectors.DefaultSelector()

        self._selector.register(
            server.socket.fileno(),
            selectors.EVENT_READ, data=server,
        )

    def put(self, conn):
        """Put idle connection into the ConnectionManager to be managed.

        Args:
            conn (cheroot.server.HTTPConnection): HTTP connection
                to be managed.
        """
        conn.last_used = time.time()
        # if this conn doesn't have any more data waiting to be read,
        # register it with the selector.
        if conn.rfile.has_data():
            self._readable_conns.append(conn)
        else:
            self._selector.register(
                conn.socket.fileno(), selectors.EVENT_READ, data=conn,
            )

    def expire(self):
        """Expire least recently used connections.

        This happens if there are either too many open connections, or if the
        connections have been timed out.

        This should be called periodically.
        """
        # find any connections still registered with the selector
        # that have not been active recently enough.
        threshold = time.time() - self.server.timeout
        timed_out_connections = (
            (sock_fd, conn)
            for _, (_, sock_fd, _, conn)
            in copy.copy(self._selector.get_map()).items()
            if conn != self.server and conn.last_used < threshold
        )
        for sock_fd, conn in timed_out_connections:
            self._selector.unregister(sock_fd)
            conn.close()

    def get_conn(self):  # noqa: C901  # FIXME
        """Return a HTTPConnection object which is ready to be handled.

        A connection returned by this method should be ready for a worker
        to handle it. If there are no connections ready, None will be
        returned.

        Any connection returned by this method will need to be `put`
        back if it should be examined again for another request.

        Returns:
            cheroot.server.HTTPConnection instance, or None.

        """
        # return a readable connection if any exist
        with suppress(IndexError):
            return self._readable_conns.popleft()

        # Will require a select call.
        try:
            # The timeout value impacts performance and should be carefully
            # chosen. Ref:
            # github.com/cherrypy/cheroot/issues/305#issuecomment-663985165
            rlist = [
                key for key, _
                in self._selector.select(timeout=0.01)
            ]
        except OSError:
            # Mark any connection which no longer appears valid
            for _, key in copy.copy(self._selector.get_map()).items():
                # If the server socket is invalid, we'll just ignore it and
                # wait to be shutdown.
                if key.data == self.server:
                    continue

                try:
                    os.fstat(key.fd)
                except OSError:
                    # Socket is invalid, close the connection
                    self._selector.unregister(key.fd)
                    conn = key.data
                    conn.close()

            # Wait for the next tick to occur.
            return None

        for key in rlist:
            if key.data is self.server:
                # New connection
                return self._from_server_socket(self.server.socket)

            conn = key.data
            # unregister connection from the selector until the server
            # has read from it and returned it via put()
            self._selector.unregister(key.fd)
            self._readable_conns.append(conn)

        try:
            return self._readable_conns.popleft()
        except IndexError:
            return None

    def _from_server_socket(self, server_socket):  # noqa: C901  # FIXME
        try:
            s, addr = server_socket.accept()
            if self.server.stats['Enabled']:
                self.server.stats['Accepts'] += 1
            prevent_socket_inheritance(s)
            if hasattr(s, 'settimeout'):
                s.settimeout(self.server.timeout)

            mf = MakeFile
            ssl_env = {}
            # if ssl cert and key are set, we try to be a secure HTTP server
            if self.server.ssl_adapter is not None:
                try:
                    s, ssl_env = self.server.ssl_adapter.wrap(s)
                except errors.NoSSLError:
                    msg = (
                        'The client sent a plain HTTP request, but '
                        'this server only speaks HTTPS on this port.'
                    )
                    buf = [
                        '%s 400 Bad Request\r\n' % self.server.protocol,
                        'Content-Length: %s\r\n' % len(msg),
                        'Content-Type: text/plain\r\n\r\n',
                        msg,
                    ]

                    sock_to_make = s if not six.PY2 else s._sock
                    wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE)
                    try:
                        wfile.write(''.join(buf).encode('ISO-8859-1'))
                    except socket.error as ex:
                        if ex.args[0] not in errors.socket_errors_to_ignore:
                            raise
                    return
                if not s:
                    return
                mf = self.server.ssl_adapter.makefile
                # Re-apply our timeout since we may have a new socket object
                if hasattr(s, 'settimeout'):
                    s.settimeout(self.server.timeout)

            conn = self.server.ConnectionClass(self.server, s, mf)

            if not isinstance(
                    self.server.bind_addr,
                    (six.text_type, six.binary_type),
            ):
                # optional values
                # Until we do DNS lookups, omit REMOTE_HOST
                if addr is None:  # sometimes this can happen
                    # figure out if AF_INET or AF_INET6.
                    if len(s.getsockname()) == 2:
                        # AF_INET
                        addr = ('0.0.0.0', 0)
                    else:
                        # AF_INET6
                        addr = ('::', 0)
                conn.remote_addr = addr[0]
                conn.remote_port = addr[1]

            conn.ssl_env = ssl_env
            return conn

        except socket.timeout:
            # The only reason for the timeout in start() is so we can
            # notice keyboard interrupts on Win32, which don't interrupt
            # accept() by default
            return
        except socket.error as ex:
            if self.server.stats['Enabled']:
                self.server.stats['Socket Errors'] += 1
            if ex.args[0] in errors.socket_error_eintr:
                # I *think* this is right. EINTR should occur when a signal
                # is received during the accept() call; all docs say retry
                # the call, and I *think* I'm reading it right that Python
                # will then go ahead and poll for and handle the signal
                # elsewhere. See
                # https://github.com/cherrypy/cherrypy/issues/707.
                return
            if ex.args[0] in errors.socket_errors_nonblocking:
                # Just try again. See
                # https://github.com/cherrypy/cherrypy/issues/479.
                return
            if ex.args[0] in errors.socket_errors_to_ignore:
                # Our socket was closed.
                # See https://github.com/cherrypy/cherrypy/issues/686.
                return
            raise

    def close(self):
        """Close all monitored connections."""
        for conn in self._readable_conns:
            conn.close()
        self._readable_conns.clear()

        for _, key in copy.copy(self._selector.get_map()).items():
            if key.data != self.server:  # server closes its own socket
                key.data.socket.close()

        self._selector.close()

    @property
    def _num_connections(self):  # noqa: D401
        """The current number of connections.

        Includes any in the readable list or registered with the selector,
        minus one for the server socket, which is always registered
        with the selector.
        """
        return len(self._readable_conns) + len(self._selector.get_map()) - 1

    @property
    def can_add_keepalive_connection(self):
        """Flag whether it is allowed to add a new keep-alive connection."""
        ka_limit = self.server.keep_alive_conn_limit
        return ka_limit is None or self._num_connections < ka_limit
<commit_msg>connections: do not modify selector during iteration<commit_after>"""Utilities to manage open connections."""

from __future__ import absolute_import, division, print_function
__metaclass__ = type

import collections
import io
import os
import socket
import time

from . import errors
from ._compat import selectors
from ._compat import suppress
from .makefile import MakeFile

import six

try:
    import fcntl
except ImportError:
    try:
        from ctypes import windll, WinError
        import ctypes.wintypes
        _SetHandleInformation = windll.kernel32.SetHandleInformation
        _SetHandleInformation.argtypes = [
            ctypes.wintypes.HANDLE,
            ctypes.wintypes.DWORD,
            ctypes.wintypes.DWORD,
        ]
        _SetHandleInformation.restype = ctypes.wintypes.BOOL
    except ImportError:
        def prevent_socket_inheritance(sock):
            """Stub inheritance prevention.

            Dummy function, since neither fcntl nor ctypes are available.
            """
            pass
    else:
        def prevent_socket_inheritance(sock):
            """Mark the given socket fd as non-inheritable (Windows)."""
            if not _SetHandleInformation(sock.fileno(), 1, 0):
                raise WinError()
else:
    def prevent_socket_inheritance(sock):
        """Mark the given socket fd as non-inheritable (POSIX)."""
        fd = sock.fileno()
        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)

class ConnectionManager:
    """Class which manages HTTPConnection objects.

    This is for connections which are being kept-alive for follow-up requests.
    """

    def __init__(self, server):
        """Initialize ConnectionManager object.

        Args:
            server (cheroot.server.HTTPServer): web server object
                that uses this ConnectionManager instance.
        """
        self.server = server
        self._readable_conns = collections.deque()
        self._selector = selectors.DefaultSelector()

        self._selector.register(
            server.socket.fileno(),
            selectors.EVENT_READ, data=server,
        )

    def put(self, conn):
        """Put idle connection into the ConnectionManager to be managed.

        Args:
            conn (cheroot.server.HTTPConnection): HTTP connection
                to be managed.
        """
        conn.last_used = time.time()
        # if this conn doesn't have any more data waiting to be read,
        # register it with the selector.
        if conn.rfile.has_data():
            self._readable_conns.append(conn)
        else:
            self._selector.register(
                conn.socket.fileno(), selectors.EVENT_READ, data=conn,
            )

    def expire(self):
        """Expire least recently used connections.

        This happens if there are either too many open connections, or if the
        connections have been timed out.

        This should be called periodically.
        """
        # find any connections still registered with the selector
        # that have not been active recently enough.
        threshold = time.time() - self.server.timeout
        timed_out_connections = [
            (sock_fd, conn)
            for _, (_, sock_fd, _, conn)
            in self._selector.get_map().items()
            if conn != self.server and conn.last_used < threshold
        ]
        for sock_fd, conn in timed_out_connections:
            self._selector.unregister(sock_fd)
            conn.close()

    def get_conn(self):  # noqa: C901  # FIXME
        """Return a HTTPConnection object which is ready to be handled.

        A connection returned by this method should be ready for a worker
        to handle it. If there are no connections ready, None will be
        returned.

        Any connection returned by this method will need to be `put`
        back if it should be examined again for another request.

        Returns:
            cheroot.server.HTTPConnection instance, or None.

        """
        # return a readable connection if any exist
        with suppress(IndexError):
            return self._readable_conns.popleft()

        # Will require a select call.
        try:
            # The timeout value impacts performance and should be carefully
            # chosen. Ref:
            # github.com/cherrypy/cheroot/issues/305#issuecomment-663985165
            rlist = [
                key for key, _
                in self._selector.select(timeout=0.01)
            ]
        except OSError:
            # Mark any connection which no longer appears valid
            invalid_entries = []
            for _, key in self._selector.get_map().items():
                # If the server socket is invalid, we'll just ignore it and
                # wait to be shutdown.
                if key.data == self.server:
                    continue

                try:
                    os.fstat(key.fd)
                except OSError:
                    invalid_entries.append((key.fd, key.data))

            for sock_fd, conn in invalid_entries:
                self._selector.unregister(sock_fd)
                conn.close()

            # Wait for the next tick to occur.
            return None

        for key in rlist:
            if key.data is self.server:
                # New connection
                return self._from_server_socket(self.server.socket)

            conn = key.data
            # unregister connection from the selector until the server
            # has read from it and returned it via put()
            self._selector.unregister(key.fd)
            self._readable_conns.append(conn)

        try:
            return self._readable_conns.popleft()
        except IndexError:
            return None

    def _from_server_socket(self, server_socket):  # noqa: C901  # FIXME
        try:
            s, addr = server_socket.accept()
            if self.server.stats['Enabled']:
                self.server.stats['Accepts'] += 1
            prevent_socket_inheritance(s)
            if hasattr(s, 'settimeout'):
                s.settimeout(self.server.timeout)

            mf = MakeFile
            ssl_env = {}
            # if ssl cert and key are set, we try to be a secure HTTP server
            if self.server.ssl_adapter is not None:
                try:
                    s, ssl_env = self.server.ssl_adapter.wrap(s)
                except errors.NoSSLError:
                    msg = (
                        'The client sent a plain HTTP request, but '
                        'this server only speaks HTTPS on this port.'
                    )
                    buf = [
                        '%s 400 Bad Request\r\n' % self.server.protocol,
                        'Content-Length: %s\r\n' % len(msg),
                        'Content-Type: text/plain\r\n\r\n',
                        msg,
                    ]

                    sock_to_make = s if not six.PY2 else s._sock
                    wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE)
                    try:
                        wfile.write(''.join(buf).encode('ISO-8859-1'))
                    except socket.error as ex:
                        if ex.args[0] not in errors.socket_errors_to_ignore:
                            raise
                    return
                if not s:
                    return
                mf = self.server.ssl_adapter.makefile
                # Re-apply our timeout since we may have a new socket object
                if hasattr(s, 'settimeout'):
                    s.settimeout(self.server.timeout)

            conn = self.server.ConnectionClass(self.server, s, mf)

            if not isinstance(
                    self.server.bind_addr,
                    (six.text_type, six.binary_type),
            ):
                # optional values
                # Until we do DNS lookups, omit REMOTE_HOST
                if addr is None:  # sometimes this can happen
                    # figure out if AF_INET or AF_INET6.
                    if len(s.getsockname()) == 2:
                        # AF_INET
                        addr = ('0.0.0.0', 0)
                    else:
                        # AF_INET6
                        addr = ('::', 0)
                conn.remote_addr = addr[0]
                conn.remote_port = addr[1]

            conn.ssl_env = ssl_env
            return conn

        except socket.timeout:
            # The only reason for the timeout in start() is so we can
            # notice keyboard interrupts on Win32, which don't interrupt
            # accept() by default
            return
        except socket.error as ex:
            if self.server.stats['Enabled']:
                self.server.stats['Socket Errors'] += 1
            if ex.args[0] in errors.socket_error_eintr:
                # I *think* this is right. EINTR should occur when a signal
                # is received during the accept() call; all docs say retry
                # the call, and I *think* I'm reading it right that Python
                # will then go ahead and poll for and handle the signal
                # elsewhere. See
                # https://github.com/cherrypy/cherrypy/issues/707.
                return
            if ex.args[0] in errors.socket_errors_nonblocking:
                # Just try again. See
                # https://github.com/cherrypy/cherrypy/issues/479.
                return
            if ex.args[0] in errors.socket_errors_to_ignore:
                # Our socket was closed.
                # See https://github.com/cherrypy/cherrypy/issues/686.
                return
            raise

    def close(self):
        """Close all monitored connections."""
        for conn in self._readable_conns:
            conn.close()
        self._readable_conns.clear()

        for _, key in self._selector.get_map().items():
            if key.data != self.server:  # server closes its own socket
                key.data.socket.close()

        self._selector.close()

    @property
    def _num_connections(self):  # noqa: D401
        """The current number of connections.

        Includes any in the readable list or registered with the selector,
        minus one for the server socket, which is always registered
        with the selector.
        """
        return len(self._readable_conns) + len(self._selector.get_map()) - 1

    @property
    def can_add_keepalive_connection(self):
        """Flag whether it is allowed to add a new keep-alive connection."""
        ka_limit = self.server.keep_alive_conn_limit
        return ka_limit is None or self._num_connections < ka_limit
<|endoftext|>


<commit_before>"""
A library for integrating Python's builtin ``ssl`` library with Cheroot.

The ssl module must be importable for SSL functionality.

To use this module, set ``HTTPServer.ssl_adapter`` to an instance of
``BuiltinSSLAdapter``.
"""

try:
    import ssl
except ImportError:
    ssl = None

try:
    from _pyio import DEFAULT_BUFFER_SIZE
except ImportError:
    try:
        from io import DEFAULT_BUFFER_SIZE
    except ImportError:
        DEFAULT_BUFFER_SIZE = -1

import sys

from . import Adapter
from .. import errors
from ..makefile import MakeFile

class BuiltinSSLAdapter(Adapter):

    """A wrapper for integrating Python's builtin ssl module with CherryPy."""

    certificate = None
    """The filename of the server SSL certificate."""

    private_key = None
    """The filename of the server's private key file."""

    certificate_chain = None
    """The filename of the certificate chain file."""

    """The ssl.SSLContext that will be used to wrap sockets where available
    (on Python > 2.7.9 / 3.3)
    """
    ciphers = None
    """The ciphers list of SSL."""
    context = None

    def __init__(self, certificate, private_key, certificate_chain=None, ciphers=None):
        if ssl is None:
            raise ImportError('You must install the ssl module to use HTTPS.')

        super(BuiltinSSLAdapter, self).__init__(certificate, private_key, certificate_chain, ciphers)

        if hasattr(ssl, 'create_default_context'):
            self.context = ssl.create_default_context(
                purpose=ssl.Purpose.CLIENT_AUTH,
                cafile=certificate_chain
            )
            self.context.load_cert_chain(certificate, private_key)
            if(self.ciphers != None):
                self.context.set_ciphers(ciphers)

    def bind(self, sock):
        """Wrap and return the given socket."""
        return sock

    def wrap(self, sock):
        """Wrap and return the given socket, plus WSGI environ entries."""
        try:
            if self.context is not None:
                s = self.context.wrap_socket(sock, do_handshake_on_connect=True,
                                             server_side=True)
            else:
                s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
                                    server_side=True, certfile=self.certificate,
                                    keyfile=self.private_key,
                                    ssl_version=ssl.PROTOCOL_SSLv23,
                                    ca_certs=self.certificate_chain)
        except ssl.SSLError:
            e = sys.exc_info()[1]
            if e.errno == ssl.SSL_ERROR_EOF:
                # This is almost certainly due to the cherrypy engine
                # 'pinging' the socket to assert it's connectable;
                # the 'ping' isn't SSL.
                return None, {}
            elif e.errno == ssl.SSL_ERROR_SSL:
                if 'http request' in e.args[1]:
                    # The client is speaking HTTP to an HTTPS server.
                    raise errors.NoSSLError

                # Check if it's one of the known errors
                # Errors that are caught by PyOpenSSL, but thrown by built-in ssl
                _block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca',
                                 'inappropriate fallback', 'wrong version number',
                                 'no shared cipher', 'certificate unknown', 'ccs received early')
                for error_text in _block_errors:
                    if error_text in e.args[1].lower():
                        # Accepted error, let's pass
                        return None, {}
            elif 'handshake operation timed out' in e.args[0]:
                # This error is thrown by builtin SSL after a timeout
                # when client is speaking HTTP to an HTTPS server.
                # The connection can safely be dropped.
                return None, {}
            raise
        return s, self.get_environ(s)

    # TODO: fill this out more with mod ssl env
    def get_environ(self, sock):
        """Create WSGI environ entries to be merged into each request."""
        cipher = sock.cipher()
        ssl_environ = {
            'wsgi.url_scheme': 'https',
            'HTTPS': 'on',
            'SSL_PROTOCOL': cipher[1],
            'SSL_CIPHER': cipher[0]
            # SSL_VERSION_INTERFACE     string  The mod_ssl program version
            # SSL_VERSION_LIBRARY   string  The OpenSSL program version
        }
        return ssl_environ

    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
        return MakeFile(sock, mode, bufsize)
<commit_msg>Fix None comparison in built in SSL adapter<commit_after>"""
A library for integrating Python's builtin ``ssl`` library with Cheroot.

The ssl module must be importable for SSL functionality.

To use this module, set ``HTTPServer.ssl_adapter`` to an instance of
``BuiltinSSLAdapter``.
"""

try:
    import ssl
except ImportError:
    ssl = None

try:
    from _pyio import DEFAULT_BUFFER_SIZE
except ImportError:
    try:
        from io import DEFAULT_BUFFER_SIZE
    except ImportError:
        DEFAULT_BUFFER_SIZE = -1

import sys

from . import Adapter
from .. import errors
from ..makefile import MakeFile

class BuiltinSSLAdapter(Adapter):

    """A wrapper for integrating Python's builtin ssl module with CherryPy."""

    certificate = None
    """The filename of the server SSL certificate."""

    private_key = None
    """The filename of the server's private key file."""

    certificate_chain = None
    """The filename of the certificate chain file."""

    """The ssl.SSLContext that will be used to wrap sockets where available
    (on Python > 2.7.9 / 3.3)
    """
    ciphers = None
    """The ciphers list of SSL."""
    context = None

    def __init__(self, certificate, private_key, certificate_chain=None, ciphers=None):
        if ssl is None:
            raise ImportError('You must install the ssl module to use HTTPS.')

        super(BuiltinSSLAdapter, self).__init__(certificate, private_key, certificate_chain, ciphers)

        if hasattr(ssl, 'create_default_context'):
            self.context = ssl.create_default_context(
                purpose=ssl.Purpose.CLIENT_AUTH,
                cafile=certificate_chain
            )
            self.context.load_cert_chain(certificate, private_key)
            if self.ciphers is not None:
                self.context.set_ciphers(ciphers)

    def bind(self, sock):
        """Wrap and return the given socket."""
        return sock

    def wrap(self, sock):
        """Wrap and return the given socket, plus WSGI environ entries."""
        try:
            if self.context is not None:
                s = self.context.wrap_socket(sock, do_handshake_on_connect=True,
                                             server_side=True)
            else:
                s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
                                    server_side=True, certfile=self.certificate,
                                    keyfile=self.private_key,
                                    ssl_version=ssl.PROTOCOL_SSLv23,
                                    ca_certs=self.certificate_chain)
        except ssl.SSLError:
            e = sys.exc_info()[1]
            if e.errno == ssl.SSL_ERROR_EOF:
                # This is almost certainly due to the cherrypy engine
                # 'pinging' the socket to assert it's connectable;
                # the 'ping' isn't SSL.
                return None, {}
            elif e.errno == ssl.SSL_ERROR_SSL:
                if 'http request' in e.args[1]:
                    # The client is speaking HTTP to an HTTPS server.
                    raise errors.NoSSLError

                # Check if it's one of the known errors
                # Errors that are caught by PyOpenSSL, but thrown by built-in ssl
                _block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca',
                                 'inappropriate fallback', 'wrong version number',
                                 'no shared cipher', 'certificate unknown', 'ccs received early')
                for error_text in _block_errors:
                    if error_text in e.args[1].lower():
                        # Accepted error, let's pass
                        return None, {}
            elif 'handshake operation timed out' in e.args[0]:
                # This error is thrown by builtin SSL after a timeout
                # when client is speaking HTTP to an HTTPS server.
                # The connection can safely be dropped.
                return None, {}
            raise
        return s, self.get_environ(s)

    # TODO: fill this out more with mod ssl env
    def get_environ(self, sock):
        """Create WSGI environ entries to be merged into each request."""
        cipher = sock.cipher()
        ssl_environ = {
            'wsgi.url_scheme': 'https',
            'HTTPS': 'on',
            'SSL_PROTOCOL': cipher[1],
            'SSL_CIPHER': cipher[0]
            # SSL_VERSION_INTERFACE     string  The mod_ssl program version
            # SSL_VERSION_LIBRARY   string  The OpenSSL program version
        }
        return ssl_environ

    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
        return MakeFile(sock, mode, bufsize)
<|endoftext|>


<commit_before>"""
stringjumble.py
Author: Roger Danilek
Credit: Adam Pikielny

Assignment:

The purpose of this challenge is to gain proficiency with
manipulating lists.

Write and submit a Python program that accepts a string from
the user and prints it back in three different ways:

* With all letters in reverse.
* With words in reverse order, but letters within each word in
  the correct order.
* With all words in correct order, but letters reversed within
  the words.

Output of your program should look like this:

Please enter a string of text (the bigger the better): There are a few techniques or tricks that you may find handy
You entered "There are a few techniques or tricks that you may find handy". Now jumble it:
ydnah dnif yam uoy taht skcirt ro seuqinhcet wef a era erehT
handy find may you that tricks or techniques few a are There
erehT era a wef seuqinhcet ro skcirt taht uoy yam dnif ydnah
"""
import sys
a= input("Please enter a string of text (the bigger the better): ")
b= list(a)
b.reverse()
for x in b:
    print(x, end="")
print()
words = a.split()
words.reverse()
for word in words:
    print(word, end=" ")
print()
bam = a.split()
for w in bam:
    print(list(w).reverse())

<commit_msg>Updated from Brython Server: October 5, 2015 2:34:31 PM GMT-4<commit_after>"""
stringjumble.py
Author: Roger Danilek
Credit: Adam Pikielny

Assignment:

The purpose of this challenge is to gain proficiency with
manipulating lists.

Write and submit a Python program that accepts a string from
the user and prints it back in three different ways:

* With all letters in reverse.
* With words in reverse order, but letters within each word in
  the correct order.
* With all words in correct order, but letters reversed within
  the words.

Output of your program should look like this:

Please enter a string of text (the bigger the better): There are a few techniques or tricks that you may find handy
You entered "There are a few techniques or tricks that you may find handy". Now jumble it:
ydnah dnif yam uoy taht skcirt ro seuqinhcet wef a era erehT
handy find may you that tricks or techniques few a are There
erehT era a wef seuqinhcet ro skcirt taht uoy yam dnif ydnah
"""
import sys
a= input("Please enter a string of text (the bigger the better): ")
b= list(a)
b.reverse()
for x in b:
    print(x, end="")
print()
words = a.split()
words.reverse()
for word in words:
    print(word, end=" ")
print()
bam = a.split()
for w in bam:
    f = list(w).reverse()
    print(f, end=""

<|endoftext|>


<commit_before>__author__ = 'Daniel Greenfeld'
__version__ = "0.1.0"

import math

class String(str):
    """ Adding critically unimportant functionality to Python's str type """

    def len(self):
        return self.__len__()

    @property
    def length(self):
        return self.len()

    @property
    def size(self):
        return self.length

    @property
    def width(self):
        return self.length

    @property
    def height(self):
        return self.length

    @property
    def area(self):
        return self.height * self.width

class ConqueringString(String):
    """ Adding stupidly dangerous functionality to Python's str type """

    def __init__(self, text):
        super(ConqueringString, self).__init__(text)
        self._length = self.__len__()

    def __len__(self):
        try:
            return self._length
        except AttributeError:
            return super(ConqueringString, self).__len__()

    def len(self, value=None):
        if value is None:
            return self._length
        self._length = value

    @property
    def length(self):
        return self.len()

    @length.setter
    def length(self, value):
        self._length = value

    @property
    def size(self):
        return self.length

    @size.setter
    def size(self, value):
        self.length = value

    @property
    def area(self):
        return self.height * self.width

    @area.setter
    def area(self, value):
        self.length = math.sqrt(value)

if __name__ == "__main__":
    s = ConqueringString("Hello, World!")
    print(s)
    print(s.length)
    s.length = 5
    print(s.length)
    print(s.area)
    s.area = 50
    print(s.area)
    print(len(s))
    print(s[5:10])  # slicing still works!
    print(s.upper())  # other methods still work!
<commit_msg>Fix height, width, and area calculation<commit_after>__author__ = 'Daniel Greenfeld'
__version__ = "0.1.0"

import math

class String(str):
    """ Adding critically unimportant functionality to Python's str type """

    def len(self):
        return self.__len__()

    @property
    def length(self):
        return self.len()

    @property
    def size(self):
        return self.length

    @property
    def width(self):
        return max(len(l) for l in self.splitlines())

    @property
    def height(self):
        return len(self.splitlines())

    @property
    def area(self):
        return self.height * self.width

class ConqueringString(String):
    """ Adding stupidly dangerous functionality to Python's str type """

    def __init__(self, text):
        super(ConqueringString, self).__init__(text)
        self._length = self.__len__()

    def __len__(self):
        try:
            return self._length
        except AttributeError:
            return super(ConqueringString, self).__len__()

    def len(self, value=None):
        if value is None:
            return self._length
        self._length = value

    @property
    def length(self):
        return self.len()

    @length.setter
    def length(self, value):
        self._length = value

    @property
    def size(self):
        return self.length

    @size.setter
    def size(self, value):
        self.length = value

    @property
    def area(self):
        return self.height * self.width

    @area.setter
    def area(self, value):
        self.length = math.sqrt(value)

if __name__ == "__main__":
    s = ConqueringString("Hello, World!")
    print(s)
    print(s.length)
    s.length = 5
    print(s.length)
    print(s.area)
    s.area = 50
    print(s.area)
    print(len(s))
    print(s[5:10])  # slicing still works!
    print(s.upper())  # other methods still work!
<|endoftext|>