~mandel/ubuntu-sso-client/libsoup-ssl-dialog

« back to all changes in this revision

Viewing changes to ubuntu_sso/utils/webclient/txweb.py

  • Committer: Manuel de la Pena
  • Date: 2012-03-15 09:50:34 UTC
  • mfrom: (846.22.37 ubuntu-sso-client)
  • Revision ID: manuel.delapena@canonical.com-20120315095034-62t8g37w7b9itg0v
Merged with trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
"""A webclient backend that uses twisted.web.client."""
17
17
 
18
18
import base64
19
 
 
20
 
from StringIO import StringIO
21
 
 
22
 
from twisted.internet import defer, protocol
23
 
from zope.interface import implements
 
19
import urlparse
 
20
 
 
21
from twisted.internet import defer
24
22
 
25
23
from ubuntu_sso.utils.webclient.common import (
26
24
    BaseWebClient,
31
29
)
32
30
 
33
31
 
34
 
class StringProtocol(protocol.Protocol):
35
 
    """Hold the stuff received in a StringIO."""
36
 
 
37
 
    # pylint: disable=C0103
38
 
    def __init__(self):
39
 
        """Initialize this instance."""
40
 
        self.deferred = defer.Deferred()
41
 
        self.content = StringIO()
42
 
 
43
 
    def dataReceived(self, data):
44
 
        """Some more blocks received."""
45
 
        self.content.write(data)
46
 
 
47
 
    def connectionLost(self, reason=protocol.connectionDone):
48
 
        """No more bytes available."""
49
 
        self.deferred.callback(self.content.getvalue())
50
 
 
51
 
 
52
 
class StringProducer(object):
53
 
    """Simple implementation of IBodyProducer."""
54
 
 
55
 
    # delay import, otherwise a default reactor gets installed
56
 
    from twisted.web import iweb
57
 
 
58
 
    implements(iweb.IBodyProducer)
59
 
 
60
 
    def __init__(self, body):
61
 
        """Initialize this instance with some bytes."""
62
 
        self.body = body
63
 
        self.length = len(body)
64
 
 
65
 
    # pylint: disable=C0103
66
 
    def startProducing(self, consumer):
67
 
        """Start producing to the given IConsumer provider."""
68
 
        consumer.write(self.body)
69
 
        return defer.succeed(None)
70
 
 
71
 
    def pauseProducing(self):
72
 
        """In our case, do nothing."""
73
 
 
74
 
    def stopProducing(self):
75
 
        """In our case, do nothing."""
 
32
class RawResponse(object):
 
33
    """A raw response from the webcall."""
 
34
 
 
35
    def __init__(self, headers, content, code=200, phrase="OK"):
 
36
        """Initialize this response."""
 
37
        self.headers = headers
 
38
        self.content = content
 
39
        self.code = code
 
40
        self.phrase = phrase
76
41
 
77
42
 
78
43
class WebClient(BaseWebClient):
79
44
    """A simple web client that does not support proxies, yet."""
80
45
 
81
 
    # delay import, otherwise a default reactor gets installed
82
 
    from twisted.internet import reactor
83
 
    from twisted.web import client, http, http_headers
84
 
 
85
 
    # Undefined variable 'http_headers', 'client', 'reactor', 'http'
86
 
    # pylint: disable=E0602
 
46
    def __init__(self, connector=None, context_factory=None, **kwargs):
 
47
        """Initialize this webclient."""
 
48
        super(WebClient, self).__init__(**kwargs)
 
49
 
 
50
        if connector is None:
 
51
            from twisted.internet import reactor
 
52
            self.connector = reactor
 
53
        else:
 
54
            self.connector = connector
 
55
 
 
56
        if context_factory is None:
 
57
            from twisted.internet import ssl
 
58
            self.context_factory = ssl.ClientContextFactory()
 
59
        else:
 
60
            self.context_factory = context_factory
 
61
 
 
62
    @defer.inlineCallbacks
 
63
    def raw_request(self, method, uri, headers, postdata):
 
64
        """Make a raw http request."""
 
65
        # delay import, otherwise a default reactor gets installed
 
66
        from twisted.web import client, error
 
67
 
 
68
        parsed_url = urlparse.urlparse(uri)
 
69
 
 
70
        # pylint: disable=E1101
 
71
        https = parsed_url.scheme == "https"
 
72
        host = parsed_url.netloc.split(":")[0]
 
73
        # pylint: enable=E1101
 
74
        if parsed_url.port is None:
 
75
            port = 443 if https else 80
 
76
        else:
 
77
            port = parsed_url.port
 
78
 
 
79
        factory = client.HTTPClientFactory(uri, method=method,
 
80
                                           postdata=postdata,
 
81
                                           headers=headers,
 
82
                                           followRedirect=False)
 
83
        # pylint: disable=E1103
 
84
        if https:
 
85
            self.connector.connectSSL(host, port, factory,
 
86
                                      self.context_factory)
 
87
        else:
 
88
            self.connector.connectTCP(host, port, factory)
 
89
        # pylint: enable=E1103
 
90
 
 
91
        try:
 
92
            content = yield factory.deferred
 
93
            response = RawResponse(factory.response_headers, content)
 
94
        except error.Error as e:
 
95
            response = RawResponse(factory.response_headers, e.response,
 
96
                                   int(e.status), e.message)
 
97
        defer.returnValue(response)
87
98
 
88
99
    @defer.inlineCallbacks
89
100
    def request(self, iri, method="GET", extra_headers=None,
90
101
                oauth_credentials=None, post_content=None):
91
102
        """Get the page, or fail trying."""
 
103
        # delay import, otherwise a default reactor gets installed
 
104
        from twisted.web import http
 
105
 
92
106
        uri = self.iri_to_uri(iri)
93
107
 
94
108
        if extra_headers:
107
121
            headers["Authorization"] = "Basic " + auth
108
122
 
109
123
        try:
110
 
            request_headers = http_headers.Headers()
111
 
            for key, value in headers.items():
112
 
                request_headers.addRawHeader(key, value)
113
 
            agent = client.Agent(reactor)
114
 
            if post_content:
115
 
                body_producer = StringProducer(post_content)
116
 
            else:
117
 
                body_producer = None
118
 
            agent_response = yield agent.request(method, uri,
119
 
                                                 headers=request_headers,
120
 
                                                 bodyProducer=body_producer)
121
 
            raw_headers = agent_response.headers.getAllRawHeaders()
122
 
            response_headers = HeaderDict(raw_headers)
 
124
            raw_response = yield self.raw_request(method, uri,
 
125
                                                  headers=headers,
 
126
                                                  postdata=post_content)
 
127
            response_headers = HeaderDict(raw_response.headers)
123
128
            if method.lower() != "head":
124
 
                response_content = yield self.get_agent_content(agent_response)
 
129
                response_content = raw_response.content
125
130
            else:
126
131
                response_content = ""
127
 
            if agent_response.code == http.OK:
 
132
            if raw_response.code == http.OK:
128
133
                defer.returnValue(Response(response_content, response_headers))
129
 
            if agent_response.code == http.UNAUTHORIZED:
130
 
                raise UnauthorizedError(agent_response.phrase,
 
134
            if raw_response.code == http.UNAUTHORIZED:
 
135
                raise UnauthorizedError(raw_response.phrase,
131
136
                                        response_content)
132
 
            raise WebClientError(agent_response.phrase, response_content)
 
137
            raise WebClientError(raw_response.phrase, response_content)
133
138
        except WebClientError:
134
139
            raise
135
140
        except Exception as e:
136
141
            raise WebClientError(e.message, e)
137
142
 
138
 
    def get_agent_content(self, agent_response):
139
 
        """Get the contents of an agent response."""
140
 
        string_protocol = StringProtocol()
141
 
        agent_response.deliverBody(string_protocol)
142
 
        return string_protocol.deferred
143
 
 
144
143
    def force_use_proxy(self, settings):
145
144
        """Setup this webclient to use the given proxy settings."""
146
 
        # No proxy support in twisted.web.client
 
145
        # No direct proxy support in twisted.web.client