blob: 5abd41c707d274cef758b6d898f1dd9f6a659efd [file] [log] [blame]
Mark Slee89e2bb82007-03-01 00:20:36 +00001#
David Reissea2cba82009-03-30 21:35:00 +00002# Licensed to the Apache Software Foundation (ASF) under one
3# or more contributor license agreements. See the NOTICE file
4# distributed with this work for additional information
5# regarding copyright ownership. The ASF licenses this file
6# to you under the Apache License, Version 2.0 (the
7# "License"); you may not use this file except in compliance
8# with the License. You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing,
13# software distributed under the License is distributed on an
14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15# KIND, either express or implied. See the License for the
16# specific language governing permissions and limitations
17# under the License.
18#
Mark Slee89e2bb82007-03-01 00:20:36 +000019
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090020from io import BytesIO
Roger Meier3f5a2642012-04-13 14:20:08 +000021import os
Bryan Duxbury69720412012-01-03 17:32:30 +000022import socket
Roger Meier3f5a2642012-04-13 14:20:08 +000023import sys
Bryan Duxbury69720412012-01-03 17:32:30 +000024import warnings
25
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090026from six.moves import urllib
27from six.moves import http_client
Mark Sleebd8b9912007-02-27 20:17:00 +000028
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090029from .TTransport import *
30import six
Bryan Duxbury69720412012-01-03 17:32:30 +000031
Mark Sleebd8b9912007-02-27 20:17:00 +000032
33class THttpClient(TTransportBase):
Mark Sleebd8b9912007-02-27 20:17:00 +000034 """Http implementation of TTransport base."""
35
David Reiss2aa28902009-03-26 06:22:18 +000036 def __init__(self, uri_or_host, port=None, path=None):
37 """THttpClient supports two different types constructor parameters.
38
39 THttpClient(host, port, path) - deprecated
40 THttpClient(uri)
41
Bryan Duxbury69720412012-01-03 17:32:30 +000042 Only the second supports https.
43 """
David Reiss2aa28902009-03-26 06:22:18 +000044 if port is not None:
Bryan Duxbury69720412012-01-03 17:32:30 +000045 warnings.warn(
46 "Please use the THttpClient('http://host:port/path') syntax",
47 DeprecationWarning,
48 stacklevel=2)
David Reiss2aa28902009-03-26 06:22:18 +000049 self.host = uri_or_host
50 self.port = port
51 assert path
52 self.path = path
53 self.scheme = 'http'
54 else:
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090055 parsed = urllib.parse.urlparse(uri_or_host)
David Reiss2aa28902009-03-26 06:22:18 +000056 self.scheme = parsed.scheme
57 assert self.scheme in ('http', 'https')
58 if self.scheme == 'http':
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090059 self.port = parsed.port or http_client.HTTP_PORT
David Reiss2aa28902009-03-26 06:22:18 +000060 elif self.scheme == 'https':
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090061 self.port = parsed.port or http_client.HTTPS_PORT
David Reiss2aa28902009-03-26 06:22:18 +000062 self.host = parsed.hostname
63 self.path = parsed.path
Bryan Duxbury727d67d2010-09-02 01:00:19 +000064 if parsed.query:
65 self.path += '?%s' % parsed.query
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090066 self.__wbuf = BytesIO()
Mark Sleebd8b9912007-02-27 20:17:00 +000067 self.__http = None
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090068 self.__http_response = None
David Reissff3d2492010-03-09 05:19:16 +000069 self.__timeout = None
Roger Meierfa392e92012-04-11 22:15:15 +000070 self.__custom_headers = None
Mark Sleebd8b9912007-02-27 20:17:00 +000071
72 def open(self):
David Reiss2aa28902009-03-26 06:22:18 +000073 if self.scheme == 'http':
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090074 self.__http = http_client.HTTPConnection(self.host, self.port)
David Reiss2aa28902009-03-26 06:22:18 +000075 else:
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090076 self.__http = http_client.HTTPSConnection(self.host, self.port)
Mark Sleebd8b9912007-02-27 20:17:00 +000077
78 def close(self):
79 self.__http.close()
80 self.__http = None
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090081 self.__http_response = None
David Reiss0c90f6f2008-02-06 22:18:40 +000082
Mark Sleebd8b9912007-02-27 20:17:00 +000083 def isOpen(self):
Bryan Duxbury69720412012-01-03 17:32:30 +000084 return self.__http is not None
Mark Sleebd8b9912007-02-27 20:17:00 +000085
David Reissff3d2492010-03-09 05:19:16 +000086 def setTimeout(self, ms):
87 if not hasattr(socket, 'getdefaulttimeout'):
88 raise NotImplementedError
89
90 if ms is None:
91 self.__timeout = None
92 else:
Bryan Duxbury69720412012-01-03 17:32:30 +000093 self.__timeout = ms / 1000.0
David Reissff3d2492010-03-09 05:19:16 +000094
Roger Meierfa392e92012-04-11 22:15:15 +000095 def setCustomHeaders(self, headers):
96 self.__custom_headers = headers
97
Mark Sleebd8b9912007-02-27 20:17:00 +000098 def read(self, sz):
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090099 return self.__http_response.read(sz)
Mark Sleebd8b9912007-02-27 20:17:00 +0000100
101 def write(self, buf):
102 self.__wbuf.write(buf)
103
David Reissff3d2492010-03-09 05:19:16 +0000104 def __withTimeout(f):
105 def _f(*args, **kwargs):
106 orig_timeout = socket.getdefaulttimeout()
107 socket.setdefaulttimeout(args[0].__timeout)
Jens Geyer4f2c0a02015-01-05 21:17:06 +0100108 try:
109 result = f(*args, **kwargs)
110 finally:
111 socket.setdefaulttimeout(orig_timeout)
David Reissff3d2492010-03-09 05:19:16 +0000112 return result
113 return _f
114
Mark Sleebd8b9912007-02-27 20:17:00 +0000115 def flush(self):
David Reiss7c1f6f82009-03-24 20:10:24 +0000116 if self.isOpen():
117 self.close()
Bryan Duxbury69720412012-01-03 17:32:30 +0000118 self.open()
David Reiss7c1f6f82009-03-24 20:10:24 +0000119
Mark Sleebd8b9912007-02-27 20:17:00 +0000120 # Pull data out of buffer
121 data = self.__wbuf.getvalue()
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +0900122 self.__wbuf = BytesIO()
Mark Sleebd8b9912007-02-27 20:17:00 +0000123
124 # HTTP request
David Reiss2aa28902009-03-26 06:22:18 +0000125 self.__http.putrequest('POST', self.path)
Mark Sleebd8b9912007-02-27 20:17:00 +0000126
127 # Write headers
Mark Sleebd8b9912007-02-27 20:17:00 +0000128 self.__http.putheader('Content-Type', 'application/x-thrift')
129 self.__http.putheader('Content-Length', str(len(data)))
Roger Meierfa392e92012-04-11 22:15:15 +0000130
Roger Meier3f5a2642012-04-13 14:20:08 +0000131 if not self.__custom_headers or 'User-Agent' not in self.__custom_headers:
132 user_agent = 'Python/THttpClient'
133 script = os.path.basename(sys.argv[0])
134 if script:
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +0900135 user_agent = '%s (%s)' % (user_agent, urllib.parse.quote(script))
Roger Meier3f5a2642012-04-13 14:20:08 +0000136 self.__http.putheader('User-Agent', user_agent)
137
Roger Meierfa392e92012-04-11 22:15:15 +0000138 if self.__custom_headers:
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +0900139 for key, val in six.iteritems(self.__custom_headers):
Roger Meierfa392e92012-04-11 22:15:15 +0000140 self.__http.putheader(key, val)
141
Mark Sleebd8b9912007-02-27 20:17:00 +0000142 self.__http.endheaders()
143
144 # Write payload
145 self.__http.send(data)
146
147 # Get reply to flush the request
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +0900148 self.__http_response = self.__http.getresponse()
149 self.code = self.__http_response.status
150 self.message = self.__http_response.reason
151 self.headers = self.__http_response.msg
David Reissff3d2492010-03-09 05:19:16 +0000152
153 # Decorate if we know how to timeout
154 if hasattr(socket, 'getdefaulttimeout'):
155 flush = __withTimeout(flush)