Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 # | 1 # |
2 # XML-RPC CLIENT LIBRARY | 2 # XML-RPC CLIENT LIBRARY |
3 # $Id$ | 3 # $Id$ |
4 # | 4 # |
5 # an XML-RPC client interface for Python. | 5 # an XML-RPC client interface for Python. |
6 # | 6 # |
7 # the marshalling and response parser code can also be used to | 7 # the marshalling and response parser code can also be used to |
8 # implement XML-RPC servers. | 8 # implement XML-RPC servers. |
9 # | 9 # |
10 # Notes: | 10 # Notes: |
(...skipping 1128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1139 # in the HTTP header, as described in RFC 1952 | 1139 # in the HTTP header, as described in RFC 1952 |
1140 # | 1140 # |
1141 # @param data the unencoded data | 1141 # @param data the unencoded data |
1142 # @return the encoded data | 1142 # @return the encoded data |
1143 | 1143 |
1144 def gzip_encode(data): | 1144 def gzip_encode(data): |
1145 """data -> gzip encoded data | 1145 """data -> gzip encoded data |
1146 | 1146 |
1147 Encode data using the gzip content encoding as described in RFC 1952 | 1147 Encode data using the gzip content encoding as described in RFC 1952 |
1148 """ | 1148 """ |
1149 f = StringIO.StringIO() | 1149 f = StringIO.StringIO() |
Benjamin
2009/06/11 16:24:58
You used try, finally for closing the files in gzi
krisvale
2009/06/11 21:02:02
I'm rather inclined to remove it from gzip_decode,
| |
1150 gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) | 1150 gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) |
1151 gzf.write(data) | 1151 gzf.write(data) |
1152 gzf.close() | 1152 gzf.close() |
1153 encoded = f.getvalue() | 1153 encoded = f.getvalue() |
1154 f.close() | 1154 f.close() |
1155 return encoded | 1155 return encoded |
1156 | 1156 |
1157 ## | 1157 ## |
1158 # Decode a string using the gzip content encoding such as specified by the | 1158 # Decode a string using the gzip content encoding such as specified by the |
1159 # Content-Encoding: gzip | 1159 # Content-Encoding: gzip |
1160 # in the HTTP header, as described in RFC 1952 | 1160 # in the HTTP header, as described in RFC 1952 |
1161 # | 1161 # |
1162 # @param data The encoded data | 1162 # @param data The encoded data |
1163 # @return the unencoded data | 1163 # @return the unencoded data |
1164 # @raises ValueError if data is not correctly coded. | |
1164 | 1165 |
1165 def gzip_decode(data): | 1166 def gzip_decode(data): |
1166 """gzip encoded data -> unencoded data | 1167 """gzip encoded data -> unencoded data |
1167 | 1168 |
1168 Decode data using the gzip content encoding as described in RFC 1952 | 1169 Decode data using the gzip content encoding as described in RFC 1952 |
1169 """ | 1170 """ |
1170 f = StringIO.StringIO(data) | 1171 f = StringIO.StringIO(data) |
1171 gzf = gzip.GzipFile(mode="rb", fileobj=f) | 1172 gzf = gzip.GzipFile(mode="rb", fileobj=f) |
1172 try: | 1173 try: |
1173 return gzf.read() | 1174 decoded = gzf.read() |
1174 finally: | 1175 except IOError: |
1175 gzf.close() | 1176 raise ValueError("invalid data") |
1176 f.close() | 1177 f.close() |
1178 gzf.close() | |
1179 return decoded | |
1177 | 1180 |
1178 ## | 1181 ## |
1179 # Return a decoded file-like object for the gzip encoding | 1182 # Return a decoded file-like object for the gzip encoding |
1180 # as described in RFC 1952. | 1183 # as described in RFC 1952. |
1181 # | 1184 # |
1182 # @param response A stream supporting a read() method | 1185 # @param response A stream supporting a read() method |
1183 # @return a file-like object that the decoded data can be read() from | 1186 # @return a file-like object that the decoded data can be read() from |
1184 | 1187 |
1185 class GzipDecodedResponse(gzip.GzipFile): | 1188 class GzipDecodedResponse(gzip.GzipFile): |
1186 """a file-like object to decode a response encoded with the gzip | 1189 """a file-like object to decode a response encoded with the gzip |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1226 #if true, we'll request gzip encoding | 1229 #if true, we'll request gzip encoding |
1227 accept_gzip_encoding = True | 1230 accept_gzip_encoding = True |
1228 | 1231 |
1229 # if positive, encode request using gzip if it exceeds this threshold | 1232 # if positive, encode request using gzip if it exceeds this threshold |
1230 # note that many server will get confused, so only use it if you know | 1233 # note that many server will get confused, so only use it if you know |
1231 # that they can decode such a request | 1234 # that they can decode such a request |
1232 encode_threshold = None #None = don't encode | 1235 encode_threshold = None #None = don't encode |
1233 | 1236 |
1234 def __init__(self, use_datetime=0): | 1237 def __init__(self, use_datetime=0): |
1235 self._use_datetime = use_datetime | 1238 self._use_datetime = use_datetime |
1236 self._connection = None | 1239 self._connection = (None, None) |
1237 self._extra_headers = [] | 1240 self._extra_headers = [] |
1238 ## | 1241 ## |
1239 # Send a complete request, and parse the response. | 1242 # Send a complete request, and parse the response. |
1240 # Retry request if a cached connection has disconnected. | 1243 # Retry request if a cached connection has disconnected. |
1241 # | 1244 # |
1242 # @param host Target host. | 1245 # @param host Target host. |
1243 # @param handler Target PRC handler. | 1246 # @param handler Target PRC handler. |
1244 # @param request_body XML-RPC request body. | 1247 # @param request_body XML-RPC request body. |
1245 # @param verbose Debugging flag. | 1248 # @param verbose Debugging flag. |
1246 # @return Parsed response. | 1249 # @return Parsed response. |
(...skipping 21 matching lines...) Expand all Loading... | |
1268 | 1271 |
1269 def single_request(self, host, handler, request_body, verbose=0): | 1272 def single_request(self, host, handler, request_body, verbose=0): |
1270 # issue XML-RPC request | 1273 # issue XML-RPC request |
1271 | 1274 |
1272 h = self.make_connection(host) | 1275 h = self.make_connection(host) |
1273 if verbose: | 1276 if verbose: |
1274 h.set_debuglevel(1) | 1277 h.set_debuglevel(1) |
1275 | 1278 |
1276 try: | 1279 try: |
1277 self.send_request(h, handler, request_body) | 1280 self.send_request(h, handler, request_body) |
1278 self.send_extra_headers(h) | 1281 self.send_host(h, host) |
1279 self.send_user_agent(h) | 1282 self.send_user_agent(h) |
1280 self.send_content(h, request_body) | 1283 self.send_content(h, request_body) |
1281 | 1284 |
1282 response = h.getresponse(buffering=True) | 1285 response = h.getresponse(buffering=True) |
1283 if response.status == 200: | 1286 if response.status == 200: |
1284 self.verbose = verbose | 1287 self.verbose = verbose |
1285 return self.parse_response(response) | 1288 return self.parse_response(response) |
1286 except Fault: | 1289 except Fault: |
1287 raise | 1290 raise |
1288 except Exception: | 1291 except Exception: |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1340 | 1343 |
1341 return host, extra_headers, x509 | 1344 return host, extra_headers, x509 |
1342 | 1345 |
1343 ## | 1346 ## |
1344 # Connect to server. | 1347 # Connect to server. |
1345 # | 1348 # |
1346 # @param host Target host. | 1349 # @param host Target host. |
1347 # @return A connection handle. | 1350 # @return A connection handle. |
1348 | 1351 |
1349 def make_connection(self, host): | 1352 def make_connection(self, host): |
1350 if self._connection: | 1353 #return an existing connection if possible. This allows |
1351 return self._connection | 1354 #HTTP/1.1 keep-alive. |
1355 if self._connection and host == self._connection[0]: | |
1356 return self._connection[1] | |
1357 | |
1352 # create a HTTP connection object from a host descriptor | 1358 # create a HTTP connection object from a host descriptor |
1353 host, self._extra_headers, x509 = self.get_host_info(host) | 1359 chost, self._extra_headers, x509 = self.get_host_info(host) |
1354 self._connection = httplib.HTTPConnection(host) | 1360 #store the host argument along with the connection object |
1355 return self._connection | 1361 self._connection = host, httplib.HTTPConnection(chost) |
1362 return self._connection[1] | |
1356 | 1363 |
1357 ## | 1364 ## |
1358 # Clear any cached connection object. | 1365 # Clear any cached connection object. |
1359 # Used in the event of socket errors. | 1366 # Used in the event of socket errors. |
1360 # | 1367 # |
1361 def close(self): | 1368 def close(self): |
1362 if self._connection: | 1369 if self._connection[1]: |
1363 self._connection.close() | 1370 self._connection[1].close() |
1364 self._connection = None | 1371 self._connection = (None, None) |
1365 | 1372 |
1366 ## | 1373 ## |
1367 # Send request header. | 1374 # Send request header. |
1368 # | 1375 # |
1369 # @param connection Connection handle. | 1376 # @param connection Connection handle. |
1370 # @param handler Target RPC handler. | 1377 # @param handler Target RPC handler. |
1371 # @param request_body XML-RPC body. | 1378 # @param request_body XML-RPC body. |
1372 | 1379 |
1373 def send_request(self, connection, handler, request_body): | 1380 def send_request(self, connection, handler, request_body): |
1374 if (self.accept_gzip_encoding): | 1381 if (self.accept_gzip_encoding): |
1375 connection.putrequest("POST", handler, skip_accept_encoding=True) | 1382 connection.putrequest("POST", handler, skip_accept_encoding=True) |
1376 connection.putheader("Accept-Encoding", "gzip") | 1383 connection.putheader("Accept-Encoding", "gzip") |
1377 else: | 1384 else: |
1378 connection.putrequest("POST", handler) | 1385 connection.putrequest("POST", handler) |
1379 | 1386 |
1380 ## | 1387 ## |
1381 # Send extra headers. | 1388 # Send host name. |
1382 # | 1389 # |
1383 # @param connection Connection handle. | 1390 # @param connection Connection handle. |
1384 | 1391 # @param host Host name. |
1385 def send_extra_headers(self, connection): | 1392 #· |
1393 # Note: This function doesn't actually add the "Host" | |
1394 # header anymore, it is done as part of the connection.putrequest() in | |
1395 # send_request() above. | |
1396 | |
1397 def send_host(self, connection, host): | |
1386 extra_headers = self._extra_headers | 1398 extra_headers = self._extra_headers |
1387 if extra_headers: | 1399 if extra_headers: |
1388 if isinstance(extra_headers, DictType): | 1400 if isinstance(extra_headers, DictType): |
1389 extra_headers = extra_headers.items() | 1401 extra_headers = extra_headers.items() |
1390 for key, value in extra_headers: | 1402 for key, value in extra_headers: |
1391 connection.putheader(key, value) | 1403 connection.putheader(key, value) |
1392 | 1404 |
1393 ## | 1405 ## |
1394 # Send user-agent identifier. | 1406 # Send user-agent identifier. |
1395 # | 1407 # |
1396 # @param connection Connection handle. | 1408 # @param connection Connection handle. |
1397 | 1409 |
1398 def send_user_agent(self, connection): | 1410 def send_user_agent(self, connection): |
1399 connection.putheader("User-Agent", self.user_agent) | 1411 connection.putheader("User-Agent", self.user_agent) |
1400 | 1412 |
1401 ## | 1413 ## |
1402 # Send request body. | 1414 # Send request body. |
1403 # | 1415 # |
1404 # @param connection Connection handle. | 1416 # @param connection Connection handle. |
1405 # @param request_body XML-RPC request body. | 1417 # @param request_body XML-RPC request body. |
1406 | 1418 |
1407 def send_content(self, connection, request_body): | 1419 def send_content(self, connection, request_body): |
1408 connection.putheader("Content-Type", "text/xml") | 1420 connection.putheader("Content-Type", "text/xml") |
1409 | 1421 |
1410 #optionally encode the request | 1422 #optionally encode the request |
1411 if (self.encode_threshold is not None and | 1423 if (self.encode_threshold is not None and |
1412 self.encode_threshold < len(request_body)): | 1424 self.encode_threshold < len(request_body)): |
1413 connection.putheader("Content-Encoding", "gzipp") | 1425 connection.putheader("Content-Encoding", "gzip") |
krisvale
2009/06/11 15:25:51
typo, should be "gzip"
| |
1414 request_body = gzip_encode(request_body) | 1426 request_body = gzip_encode(request_body) |
1415 | 1427 |
1416 connection.putheader("Content-Length", str(len(request_body))) | 1428 connection.putheader("Content-Length", str(len(request_body))) |
1417 connection.endheaders(request_body) | 1429 connection.endheaders(request_body) |
1418 | 1430 |
1419 ## | 1431 ## |
1420 # Parse response. | 1432 # Parse response. |
1421 # | 1433 # |
1422 # @param file Stream. | 1434 # @param file Stream. |
1423 # @return Response tuple and target method. | 1435 # @return Response tuple and target method. |
1424 | 1436 |
1425 def parse_response(self, response): | 1437 def parse_response(self, response): |
1426 # read response data from httpresponse, and parse it | 1438 # read response data from httpresponse, and parse it |
1427 if response.getheader("Content-Encoding", "") == "gzip": | 1439 if response.getheader("Content-Encoding", "") == "gzip": |
1428 stream = GzipDecodedResponse(response) | 1440 stream = GzipDecodedResponse(response) |
1429 print "gudn" | |
krisvale
2009/06/11 15:25:51
This will go away
| |
1430 else: | 1441 else: |
1431 stream = response | 1442 stream = response |
1432 | 1443 |
1433 p, u = self.getparser() | 1444 p, u = self.getparser() |
1434 | 1445 |
1435 while 1: | 1446 while 1: |
1436 data = stream.read(1024) | 1447 data = stream.read(1024) |
1437 if not data: | 1448 if not data: |
1438 break | 1449 break |
1439 if self.verbose: | 1450 if self.verbose: |
1440 print "body:", repr(data) | 1451 print "body:", repr(data) |
1441 p.feed(data) | 1452 p.feed(data) |
1442 | 1453 |
1443 if stream is not response: | 1454 if stream is not response: |
1444 stream.close() | 1455 stream.close() |
1445 p.close() | 1456 p.close() |
1446 | 1457 |
1447 return u.close() | 1458 return u.close() |
1448 | 1459 |
1449 ## | 1460 ## |
1450 # Standard transport class for XML-RPC over HTTPS. | 1461 # Standard transport class for XML-RPC over HTTPS. |
1451 | 1462 |
1452 class SafeTransport(Transport): | 1463 class SafeTransport(Transport): |
1453 """Handles an HTTPS transaction to an XML-RPC server.""" | 1464 """Handles an HTTPS transaction to an XML-RPC server.""" |
1454 | 1465 |
1455 # FIXME: mostly untested | 1466 # FIXME: mostly untested |
1456 | 1467 |
1457 def make_connection(self, host): | 1468 def make_connection(self, host): |
1458 if self._connection: | 1469 if self._connection and host == self._connection[0]: |
1459 return self._connection | 1470 return self._connection[1] |
1460 # create a HTTPS connection object from a host descriptor | 1471 # create a HTTPS connection object from a host descriptor |
1461 # host may be a string, or a (host, x509-dict) tuple | 1472 # host may be a string, or a (host, x509-dict) tuple |
1462 host, self._extra_headers, x509 = self.get_host_info(host) | 1473 try: |
1463 self._connection = httplib.HTTPSConnection(host, None, **(x509 or {})) | 1474 HTTPS = httplib.HTTPSConnection |
1464 return self._connection | 1475 except AttributeError: |
1476 raise NotImplementedError( | |
1477 "your version of httplib doesn't support HTTPS" | |
1478 ) | |
1479 else: | |
1480 chost, self._extra_headers, x509 = self.get_host_info(host) | |
1481 self._connection = host, HTTPSConnection(chost, None, **(x509 or {}) ) | |
1482 return self._connection[1] | |
1465 | 1483 |
1466 ## | 1484 ## |
1467 # Standard server proxy. This class establishes a virtual connection | 1485 # Standard server proxy. This class establishes a virtual connection |
1468 # to an XML-RPC server. | 1486 # to an XML-RPC server. |
1469 # <p> | 1487 # <p> |
1470 # This class is available as ServerProxy and Server. New code should | 1488 # This class is available as ServerProxy and Server. New code should |
1471 # use ServerProxy, to avoid confusion. | 1489 # use ServerProxy, to avoid confusion. |
1472 # | 1490 # |
1473 # @def ServerProxy(uri, **options) | 1491 # @def ServerProxy(uri, **options) |
1474 # @param uri The connection point on the server. | 1492 # @param uri The connection point on the server. |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1555 | 1573 |
1556 __str__ = __repr__ | 1574 __str__ = __repr__ |
1557 | 1575 |
1558 def __getattr__(self, name): | 1576 def __getattr__(self, name): |
1559 # magic method dispatcher | 1577 # magic method dispatcher |
1560 return _Method(self.__request, name) | 1578 return _Method(self.__request, name) |
1561 | 1579 |
1562 # note: to call a remote object with an non-standard name, use | 1580 # note: to call a remote object with an non-standard name, use |
1563 # result getattr(server, "strange-python-name")(args) | 1581 # result getattr(server, "strange-python-name")(args) |
1564 | 1582 |
1565 def __call__(self, method, *args, **kwds): | 1583 def __call__(self, attr): |
1566 """A workaround to call special methods on the ServerProxy""" | 1584 """A workaround to get special attributes on the ServerProxy |
1567 if method == "close": | 1585 without interfering with the magic __getattr__ |
1568 return self.__close(*args, **kwds) | 1586 """ |
1569 raise AttributeError("method %r not found"%method) | 1587 if attr == "close": |
Benjamin
2009/06/11 16:24:58
There should be space between the string and % and
krisvale
2009/06/11 21:02:02
Done.
| |
1588 return self.__close | |
1589 elif attr == "transport": | |
1590 return self.__transport | |
1591 raise AttributeError("Attribute %r not found" % (attr,)) | |
1570 | 1592 |
1571 # compatibility | 1593 # compatibility |
1572 | 1594 |
1573 Server = ServerProxy | 1595 Server = ServerProxy |
1574 | 1596 |
1575 # -------------------------------------------------------------------- | 1597 # -------------------------------------------------------------------- |
1576 # test code | 1598 # test code |
1577 | 1599 |
1578 if __name__ == "__main__": | 1600 if __name__ == "__main__": |
1579 | 1601 |
(...skipping 10 matching lines...) Expand all Loading... | |
1590 print "ERROR", v | 1612 print "ERROR", v |
1591 | 1613 |
1592 multi = MultiCall(server) | 1614 multi = MultiCall(server) |
1593 multi.currentTime.getCurrentTime() | 1615 multi.currentTime.getCurrentTime() |
1594 multi.currentTime.getCurrentTime() | 1616 multi.currentTime.getCurrentTime() |
1595 try: | 1617 try: |
1596 for response in multi(): | 1618 for response in multi(): |
1597 print response | 1619 print response |
1598 except Error, v: | 1620 except Error, v: |
1599 print "ERROR", v | 1621 print "ERROR", v |
LEFT | RIGHT |