3 # Copyright (c) 2006- Facebook
4 # Distributed under the Thrift Software License
6 # See accompanying file LICENSE or visit the Thrift site at:
7 # http://developers.facebook.com/thrift/
9 from cStringIO
import StringIO
10 from struct
import pack
,unpack
11 from thrift
.Thrift
import TException
13 class TTransportException(TException
):
15 """Custom Transport Exception class"""
23 def __init__(self
, type=UNKNOWN
, message
=None):
24 TException
.__init
__(self
, message
)
29 """Base class for Thrift transport layer."""
43 def readAll(self
, sz
):
47 chunk
= self
.read(sz
-have
)
62 # This class should be thought of as an interface.
63 class CReadableTransport
:
64 """base class for transports that are readable from C"""
66 # TODO(dreiss): Think about changing this interface to allow us to use
67 # a (Python, not c) StringIO instead, because it allows
68 # you to write after reading.
70 # NOTE: This is a classic class, so properties will NOT work
71 # correctly for setting.
73 def cstringio_buf(self
):
74 """A cStringIO buffer that contains the current chunk we are reading."""
77 def cstringio_refill(self
, partialread
, reqlen
):
78 """Refills cstringio_buf.
80 Returns the currently used buffer (which can but need not be the same as
81 the old cstringio_buf). partialread is what the C code has read from the
82 buffer, and should be inserted into the buffer before any more reads. The
83 return value must be a new, not borrowed reference. Something along the
84 lines of self._buf should be fine.
86 If reqlen bytes can't be read, throw EOFError.
90 class TServerTransportBase
:
92 """Base class for Thrift server transports."""
103 class TTransportFactoryBase
:
105 """Base class for a Transport Factory"""
107 def getTransport(self
, trans
):
110 class TBufferedTransportFactory
:
112 """Factory transport that builds buffered transports"""
114 def getTransport(self
, trans
):
115 buffered
= TBufferedTransport(trans
)
119 class TBufferedTransport(TTransportBase
,CReadableTransport
):
121 """Class that wraps another transport and buffers its I/O."""
123 DEFAULT_BUFFER
= 4096
125 def __init__(self
, trans
):
127 self
.__wbuf
= StringIO()
128 self
.__rbuf
= StringIO("")
131 return self
.__trans
.isOpen()
134 return self
.__trans
.open()
137 return self
.__trans
.close()
140 ret
= self
.__rbuf
.read(sz
)
144 self
.__rbuf
= StringIO(self
.__trans
.read(max(sz
, self
.DEFAULT_BUFFER
)))
145 return self
.__rbuf
.read(sz
)
147 def write(self
, buf
):
148 self
.__wbuf
.write(buf
)
151 self
.__trans
.write(self
.__wbuf
.getvalue())
153 self
.__wbuf
= StringIO()
155 # Implement the CReadableTransport interface.
157 def cstringio_buf(self
):
160 def cstringio_refill(self
, partialread
, reqlen
):
161 retstring
= partialread
162 if reqlen
< self
.DEFAULT_BUFFER
:
163 # try to make a read of as much as we can.
164 retstring
+= self
.__trans
.read(self
.DEFAULT_BUFFER
)
166 # but make sure we do read reqlen bytes.
167 if len(retstring
) < reqlen
:
168 retstring
+= self
.__trans
.readAll(reqlen
- len(retstring
))
170 self
.__rbuf
= StringIO(retstring
)
173 class TMemoryBuffer(TTransportBase
, CReadableTransport
):
174 """Wraps a cStringIO object as a TTransport.
176 NOTE: Unlike the C++ version of this class, you cannot write to it
177 then immediately read from it. If you want to read from a
178 TMemoryBuffer, you must either pass a string to the constructor.
179 TODO(dreiss): Make this work like the C++ version.
182 def __init__(self
, value
=None):
183 """value -- a value to read from for stringio
185 If value is set, this will be a transport for reading,
186 otherwise, it is for writing"""
187 if value
is not None:
188 self
._buffer
= StringIO(value
)
190 self
._buffer
= StringIO()
193 return not self
._buffer
.closed
202 return self
._buffer
.read(sz
)
204 def write(self
, buf
):
205 self
._buffer
.write(buf
)
211 return self
._buffer
.getvalue()
213 # Implement the CReadableTransport interface.
215 def cstringio_buf(self
):
218 def cstringio_refill(self
, partialread
, reqlen
):
219 # only one shot at reading...
222 class TFramedTransportFactory
:
224 """Factory transport that builds framed transports"""
226 def getTransport(self
, trans
):
227 framed
= TFramedTransport(trans
)
231 class TFramedTransport(TTransportBase
):
233 """Class that wraps another transport and frames its I/O when writing."""
235 def __init__(self
, trans
, read
=True, write
=True):
242 self
.__wbuf
= StringIO()
247 return self
.__trans
.isOpen()
250 return self
.__trans
.open()
253 return self
.__trans
.close()
256 if self
.__rbuf
== None:
257 return self
.__trans
.read(sz
)
258 if len(self
.__rbuf
) == 0:
260 give
= min(len(self
.__rbuf
), sz
)
261 buff
= self
.__rbuf
[0:give
]
262 self
.__rbuf
= self
.__rbuf
[give
:]
266 buff
= self
.__trans
.readAll(4)
267 sz
, = unpack('!i', buff
)
268 self
.__rbuf
= self
.__trans
.readAll(sz
)
270 def write(self
, buf
):
271 if self
.__wbuf
== None:
272 return self
.__trans
.write(buf
)
273 self
.__wbuf
.write(buf
)
276 if self
.__wbuf
== None:
277 return self
.__trans
.flush()
278 wout
= self
.__wbuf
.getvalue()
280 # N.B.: Doing this string concatenation is WAY cheaper than making
281 # two separate calls to the underlying socket object. Socket writes in
282 # Python turn out to be REALLY expensive, but it seems to do a pretty
283 # good job of managing string buffer operations without excessive copies
284 buf
= pack("!i", wsz
) + wout
285 self
.__trans
.write(buf
)
287 self
.__wbuf
= StringIO()