Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / pool / http.py @ 74d988b0

History | View | Annotate | Download (5 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from synnefo.lib.pool import ObjectPool
35
from select import select
36

    
37
from httplib import (
38
    HTTPConnection as http_class,
39
    HTTPSConnection as https_class,
40
    ResponseNotReady
41
)
42

    
43
from new import instancemethod
44

    
45
import logging
46

    
47
log = logging.getLogger(__name__)
48

    
49
_pools = {}
50
pool_size = 8
51

    
52

    
53
USAGE_LIMIT = 1000
54

    
55

    
56
def init_http_pooling(size):
57
    global pool_size
58
    pool_size = size
59

    
60

    
61
def put_http_connection(conn):
62
    pool = conn._pool
63
    log.debug("HTTP-PUT-BEFORE: putting connection %r back to pool %r",
64
              conn, pool)
65
    if pool is None:
66
        log.debug("HTTP-PUT: connection %r does not have a pool", conn)
67
        return
68
    conn._pool = None
69
    pool.pool_put(conn)
70

    
71

    
72
class HTTPConnectionPool(ObjectPool):
73

    
74
    _scheme_to_class = {
75
        'http': http_class,
76
        'https': https_class,
77
    }
78

    
79
    def __init__(self, scheme, netloc, size=None):
80
        log.debug("INIT-POOL: Initializing pool of size %d, scheme: %s, "
81
                  "netloc: %s", size, scheme, netloc)
82
        ObjectPool.__init__(self, size=size)
83

    
84
        connection_class = self._scheme_to_class.get(scheme, None)
85
        if connection_class is None:
86
            m = 'Unsupported scheme: %s' % (scheme,)
87
            raise ValueError(m)
88

    
89
        self.connection_class = connection_class
90
        self.scheme = scheme
91
        self.netloc = netloc
92

    
93
    def _pool_create(self):
94
        log.debug("CREATE-HTTP-BEFORE from pool %r", self)
95
        conn = self.connection_class(self.netloc)
96
        conn._use_counter = USAGE_LIMIT
97
        conn._pool = self
98
        conn._real_close = conn.close
99
        conn.close = instancemethod(put_http_connection, conn, type(conn))
100
        return conn
101

    
102
    def _pool_verify(self, conn):
103
        log.debug("VERIFY-HTTP")
104
        # _pool verify is called at every pool_get().
105
        # Make sure this connection obj is associated with the proper pool.
106
        # The association is broken by put_http_connection(), to prevent
107
        # a connection object from being returned to the pool twice,
108
        # on duplicate invocations of conn.close().
109
        if not conn._pool:
110
            conn._pool = self
111
        if conn is None:
112
            return False
113
        sock = conn.sock
114
        if sock is None:
115
            return True
116
        if select((conn.sock,), (), (), 0)[0]:
117
            return False
118
        return True
119

    
120
    def _pool_cleanup(self, conn):
121
        log.debug("CLEANUP-HTTP")
122
        # every connection can be used a finite number of times
123
        conn._use_counter -= 1
124

    
125
        # see httplib source for connection states documentation
126
        if conn._use_counter > 0 and conn._HTTPConnection__state == 'Idle':
127
            try:
128
                conn.getresponse()
129
            except ResponseNotReady:
130
                log.debug("CLEANUP-HTTP: Not closing connection. Will reuse.")
131
                return False
132

    
133
        log.debug("CLEANUP-HTTP: Closing connection. Will not reuse.")
134
        conn._real_close()
135
        return True
136

    
137

    
138
def get_http_connection(netloc=None, scheme='http', pool_size=pool_size):
139
    log.debug("HTTP-GET: Getting HTTP connection")
140
    if netloc is None:
141
        m = "netloc cannot be None"
142
        raise ValueError(m)
143
    # does the pool need to be created?
144
    # ensure distinct pools are created for every (scheme, netloc) combination
145
    key = (scheme, netloc)
146
    if key not in _pools:
147
        log.debug("HTTP-GET: Creating pool for key %s", key)
148
        pool = HTTPConnectionPool(scheme, netloc, size=pool_size)
149
        _pools[key] = pool
150

    
151
    obj = _pools[key].pool_get()
152
    log.debug("HTTP-GET: Returning object %r", obj)
153
    return obj