]>
Commit | Line | Data |
---|---|---|
c62d93f1 MT |
1 | #!/usr/bin/python |
2 | ############################################################################### | |
3 | # # | |
4 | # Pakfire - The IPFire package management system # | |
5 | # Copyright (C) 2011 Pakfire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
ae0bc1af | 22 | import httplib |
c62d93f1 | 23 | import socket |
d5166686 | 24 | import ssl |
c62d93f1 MT |
25 | import time |
26 | import xmlrpclib | |
27 | ||
28 | import logging | |
29 | log = logging.getLogger("pakfire.client") | |
30 | ||
31 | from pakfire.constants import * | |
32 | from pakfire.i18n import _ | |
33 | ||
2dbaea05 MT |
34 | # Set the default socket timeout to 30 seconds. |
35 | socket.setdefaulttimeout(30) | |
36 | ||
c611f46b MT |
37 | |
38 | ||
39 | ||
c62d93f1 MT |
40 | class XMLRPCMixin: |
41 | user_agent = "pakfire/%s" % PAKFIRE_VERSION | |
42 | ||
43 | def single_request(self, *args, **kwargs): | |
44 | ret = None | |
45 | ||
46 | # Tries can be passed to this method. | |
47 | tries = kwargs.pop("tries", 100) | |
48 | timeout = 1 | |
49 | ||
50 | while tries: | |
51 | try: | |
52 | ret = xmlrpclib.Transport.single_request(self, *args, **kwargs) | |
53 | ||
37ff3f8f | 54 | # Catch errors related to the connection. Just try again. |
2dbaea05 MT |
55 | except (socket.error, ssl.SSLError), e: |
56 | log.warning("Exception: %s: %s" % (e.__class__.__name__, e)) | |
c62d93f1 | 57 | |
37ff3f8f MT |
58 | # Presumably, the server closed the connection before sending anything. |
59 | except httplib.BadStatusLine: | |
2dbaea05 MT |
60 | # Try again immediately. |
61 | continue | |
37ff3f8f MT |
62 | |
63 | # The XML reponse could not be parsed. | |
2dbaea05 MT |
64 | except xmlrpclib.ResponseError, e: |
65 | log.warning("Exception: %s: %s" % (e.__class__.__name__, e)) | |
37ff3f8f MT |
66 | |
67 | except xmlrpclib.ProtocolError, e: | |
68 | if e.errcode == 403: | |
69 | # Possibly, the user credentials are invalid. | |
70 | # Cannot go on. | |
71 | raise XMLRPCForbiddenError(e) | |
72 | ||
73 | elif e.errcode == 404: | |
74 | # Some invalid URL was called. | |
75 | # Cannot go on. | |
76 | raise XMLRPCNotFoundError(e) | |
77 | ||
e3eb9333 MT |
78 | elif e.errcode == 500: |
79 | # This could have various reasons, so we can not | |
80 | # be sure to kill connections here. | |
81 | # But to visualize the issue, we will raise an | |
82 | # exception on the last try. | |
83 | if tries == 1: | |
84 | raise XMLRPCInternalServerError(e) | |
85 | ||
37ff3f8f MT |
86 | elif e.errcode == 503: |
87 | # Possibly the hub is not running but the SSL proxy | |
88 | # is. Just try again in a short time. | |
89 | pass | |
90 | ||
91 | else: | |
92 | # Log all XMLRPC protocol errors. | |
93 | log.error(_("XMLRPC protocol error:")) | |
94 | log.error(" %s" % _("URL: %s") % e.url) | |
95 | log.error(" %s" % _(" HTTP headers:")) | |
96 | for header in e.headers.items(): | |
97 | log.error(" %s: %s" % header) | |
98 | log.error(" %s" % _("Error code: %s") % e.errcode) | |
99 | log.error(" %s" % _("Error message: %s") % e.errmsg) | |
100 | ||
101 | # If an unhandled error code appeared, we raise an | |
102 | # error. | |
103 | raise | |
104 | ||
105 | except xmlrpclib.Fault: | |
106 | raise | |
107 | ||
c62d93f1 MT |
108 | else: |
109 | # If request was successful, we can break the loop. | |
110 | break | |
111 | ||
112 | # If the request was not successful, we wait a little time to try | |
113 | # it again. | |
114 | tries -= 1 | |
115 | timeout *= 2 | |
116 | if timeout > 60: | |
117 | timeout = 60 | |
118 | ||
4cdde79f MT |
119 | log.warning(_("Trying again in %(timeout)s second(s). %(tries)s tries left.") \ |
120 | % { "timeout" : timeout, "tries" : tries }) | |
c62d93f1 MT |
121 | time.sleep(timeout) |
122 | ||
123 | else: | |
37ff3f8f | 124 | raise XMLRPCTransportError, _("Maximum number of tries was reached. Giving up.") |
c62d93f1 MT |
125 | |
126 | return ret | |
127 | ||
128 | ||
37ff3f8f | 129 | |
c62d93f1 MT |
130 | class XMLRPCTransport(XMLRPCMixin, xmlrpclib.Transport): |
131 | """ | |
132 | Handles the XMLRPC connection over HTTP. | |
133 | """ | |
134 | pass | |
135 | ||
136 | ||
137 | class SafeXMLRPCTransport(XMLRPCMixin, xmlrpclib.SafeTransport): | |
138 | """ | |
139 | Handles the XMLRPC connection over HTTPS. | |
140 | """ | |
141 | pass | |
142 | ||
143 | ||
144 | class Connection(xmlrpclib.ServerProxy): | |
145 | """ | |
146 | Class wrapper that automatically chooses the right transport | |
147 | method depending on the given URL. | |
148 | """ | |
149 | ||
150 | def __init__(self, url): | |
151 | # Create transport channel to the server. | |
152 | if url.startswith("https://"): | |
153 | transport = SafeXMLRPCTransport() | |
154 | elif url.startswith("http://"): | |
155 | transport = XMLRPCTransport() | |
156 | ||
157 | xmlrpclib.ServerProxy.__init__(self, url, transport=transport, | |
158 | allow_none=True) |