1 /*
2  * 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  */
19 
20 #include <errno.h>
21 #include <glib.h>
22 #include <thrift/c_glib/thrift.h>
23 #include <thrift/c_glib/transport/thrift_transport.h>
24 #include <thrift/c_glib/thrift_configuration.h>
25 
26 /* object properties */
27 enum _ThriftTransportProperties
28 {
29   PROP_0,
30   PROP_THRIFT_TRANSPORT_CONFIGURATION,
31   PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE,
32   PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE
33 };
34 
35 /* define the GError domain string */
36 #define THRIFT_TRANSPORT_ERROR_DOMAIN "thrift-transport-error-quark"
37 
G_DEFINE_ABSTRACT_TYPE(ThriftTransport,thrift_transport,G_TYPE_OBJECT)38 G_DEFINE_ABSTRACT_TYPE(ThriftTransport, thrift_transport, G_TYPE_OBJECT)
39 
40 gboolean
41 thrift_transport_is_open (ThriftTransport *transport)
42 {
43   return THRIFT_TRANSPORT_GET_CLASS (transport)->is_open (transport);
44 }
45 
46 gboolean
thrift_transport_peek(ThriftTransport * transport,GError ** error)47 thrift_transport_peek (ThriftTransport *transport, GError **error)
48 {
49   return THRIFT_TRANSPORT_GET_CLASS (transport)->peek (transport, error);
50 }
51 
52 gboolean
thrift_transport_open(ThriftTransport * transport,GError ** error)53 thrift_transport_open (ThriftTransport *transport, GError **error)
54 {
55   return THRIFT_TRANSPORT_GET_CLASS (transport)->open (transport, error);
56 }
57 
58 gboolean
thrift_transport_close(ThriftTransport * transport,GError ** error)59 thrift_transport_close (ThriftTransport *transport, GError **error)
60 {
61   return THRIFT_TRANSPORT_GET_CLASS (transport)->close (transport, error);
62 }
63 
64 gint32
thrift_transport_read(ThriftTransport * transport,gpointer buf,guint32 len,GError ** error)65 thrift_transport_read (ThriftTransport *transport, gpointer buf,
66                        guint32 len, GError **error)
67 {
68   return THRIFT_TRANSPORT_GET_CLASS (transport)->read (transport, buf,
69                                                        len, error);
70 }
71 
72 gboolean
thrift_transport_read_end(ThriftTransport * transport,GError ** error)73 thrift_transport_read_end (ThriftTransport *transport, GError **error)
74 {
75   return THRIFT_TRANSPORT_GET_CLASS (transport)->read_end (transport,
76                                                            error);
77 }
78 
79 gboolean
thrift_transport_write(ThriftTransport * transport,const gpointer buf,const guint32 len,GError ** error)80 thrift_transport_write (ThriftTransport *transport, const gpointer buf,
81                         const guint32 len, GError **error)
82 {
83   return THRIFT_TRANSPORT_GET_CLASS (transport)->write (transport, buf,
84                                                         len, error);
85 }
86 
87 gboolean
thrift_transport_write_end(ThriftTransport * transport,GError ** error)88 thrift_transport_write_end (ThriftTransport *transport, GError **error)
89 {
90   return THRIFT_TRANSPORT_GET_CLASS (transport)->write_end (transport,
91                                                             error);
92 }
93 
94 gboolean
thrift_transport_flush(ThriftTransport * transport,GError ** error)95 thrift_transport_flush (ThriftTransport *transport, GError **error)
96 {
97   return THRIFT_TRANSPORT_GET_CLASS (transport)->flush (transport, error);
98 }
99 
100 gint32
thrift_transport_read_all(ThriftTransport * transport,gpointer buf,guint32 len,GError ** error)101 thrift_transport_read_all (ThriftTransport *transport, gpointer buf,
102                            guint32 len, GError **error)
103 {
104   return THRIFT_TRANSPORT_GET_CLASS (transport)->read_all (transport, buf,
105                                                            len, error);
106 }
107 
108 /* by default, peek returns true if and only if the transport is open */
109 static gboolean
thrift_transport_real_peek(ThriftTransport * transport,GError ** error)110 thrift_transport_real_peek (ThriftTransport *transport, GError **error)
111 {
112   THRIFT_UNUSED_VAR (error);
113 
114   return THRIFT_TRANSPORT_GET_CLASS (transport)->is_open (transport);
115 }
116 
117 static gint32
thrift_transport_real_read_all(ThriftTransport * transport,gpointer buf,guint32 len,GError ** error)118 thrift_transport_real_read_all (ThriftTransport *transport, gpointer buf,
119                                 guint32 len, GError **error)
120 {
121   ThriftTransportClass *ttc;
122   guint32 have;
123   gint32 ret;
124   gint8 *bytes;
125 
126   THRIFT_UNUSED_VAR (error);
127 
128   ttc = THRIFT_TRANSPORT_GET_CLASS (transport);
129   have = 0;
130   ret = 0;
131   bytes = (gint8*) buf;
132 
133   while (have < len) {
134     if ((ret = ttc->read (transport, (gpointer) (bytes + have), len - have,
135                           error)) < 0) {
136       return ret;
137     }
138     have += ret;
139   }
140 
141   return have;
142 }
143 
144 gboolean
thrift_transport_updateKnownMessageSize(ThriftTransport * transport,glong size,GError ** error)145 thrift_transport_updateKnownMessageSize(ThriftTransport *transport, glong size, GError **error)
146 {
147   gboolean boolean = TRUE;
148   ThriftTransport *tt = THRIFT_TRANSPORT (transport);
149   ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport);
150   glong consumed = tt->knowMessageSize_ - tt->remainingMessageSize_;
151   if(!ttc->resetConsumedMessageSize (transport, size, error))
152   {
153     boolean = FALSE;
154   }
155   if(!ttc->countConsumedMessageBytes (transport, consumed, error))
156   {
157     boolean = FALSE;
158   }
159   return boolean;
160 }
161 
162 gboolean
thrift_transport_checkReadBytesAvailable(ThriftTransport * transport,glong numBytes,GError ** error)163 thrift_transport_checkReadBytesAvailable(ThriftTransport *transport, glong numBytes, GError **error)
164 {
165   gboolean boolean = TRUE;
166   ThriftTransport *tt = THRIFT_TRANSPORT (transport);
167   if(tt->remainingMessageSize_ < numBytes)
168   {
169     g_set_error(error,
170                 THRIFT_TRANSPORT_ERROR,
171  		THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED,
172 		"MaxMessageSize reached");
173     boolean = FALSE;
174   }
175 
176   return boolean;
177 }
178 
179 gboolean
thrift_transport_resetConsumedMessageSize(ThriftTransport * transport,glong newSize,GError ** error)180 thrift_transport_resetConsumedMessageSize(ThriftTransport *transport, glong newSize, GError **error)
181 {
182   ThriftTransport *tt = THRIFT_TRANSPORT (transport);
183   if(newSize < 0)
184   {
185     if(tt->configuration != NULL)
186     {
187       tt->knowMessageSize_ = tt->configuration->maxMessageSize_;
188       tt->remainingMessageSize_ = tt->configuration->maxMessageSize_;
189     }
190     else
191     {
192       tt->knowMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE;
193       tt->remainingMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE;
194     }
195     return TRUE;
196   }
197   /* update only: message size can shrink, but not grow */
198   if(newSize > tt->knowMessageSize_)
199   {
200     g_set_error(error,
201                 THRIFT_TRANSPORT_ERROR,
202                 THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED,
203                 "MaxMessageSize reached");
204     return FALSE;
205   }
206 
207   tt->knowMessageSize_ = newSize;
208   tt->remainingMessageSize_ = newSize;
209 
210   return TRUE;
211 }
212 
213 gboolean
thrift_transport_countConsumedMessageBytes(ThriftTransport * transport,glong numBytes,GError ** error)214 thrift_transport_countConsumedMessageBytes(ThriftTransport *transport, glong numBytes, GError **error)
215 {
216   ThriftTransport *tt = THRIFT_TRANSPORT (transport);
217   if(tt->remainingMessageSize_ > numBytes)
218   {
219     tt->remainingMessageSize_ -= numBytes;
220   }
221   else
222   {
223     tt->remainingMessageSize_ = 0;
224     g_set_error(error,
225                 THRIFT_TRANSPORT_ERROR,
226                 THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED,
227                 "MaxMessageSize reached");
228     return FALSE;
229   }
230 
231   return TRUE;
232 }
233 
234 /* property accesor */
235 void
thrift_transport_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)236 thrift_transport_get_property(GObject *object, guint property_id,
237 		              GValue *value, GParamSpec *pspec)
238 {
239   ThriftTransport *transport = THRIFT_TRANSPORT (object);
240 
241   THRIFT_UNUSED_VAR (pspec);
242 
243   switch (property_id)
244   {
245     case PROP_THRIFT_TRANSPORT_CONFIGURATION:
246          g_value_set_object (value, transport->configuration);
247          break;
248     case PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE:
249          g_value_set_long (value, transport->remainingMessageSize_);
250          break;
251     case PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE:
252          g_value_set_long (value, transport->knowMessageSize_);
253          break;
254   }
255 }
256 
257 /* property mutator */
258 void
thrift_transport_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)259 thrift_transport_set_property (GObject *object, guint property_id,
260 		               const GValue *value, GParamSpec *pspec)
261 {
262   ThriftTransport *transport = THRIFT_TRANSPORT (object);
263 
264   THRIFT_UNUSED_VAR (pspec);
265 
266   switch (property_id)
267   {
268     case PROP_THRIFT_TRANSPORT_CONFIGURATION:
269          transport->configuration = g_value_get_object (value);
270          break;
271     case PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE:
272          transport->remainingMessageSize_ = g_value_get_long (value);
273          break;
274     case PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE:
275          transport->knowMessageSize_ = g_value_get_long (value);
276          break;
277   }
278 }
279 
280 /* define the GError domain for Thrift transports */
281 GQuark
thrift_transport_error_quark(void)282 thrift_transport_error_quark (void)
283 {
284   return g_quark_from_static_string (THRIFT_TRANSPORT_ERROR_DOMAIN);
285 }
286 
287 static void
thrift_transport_dispose(GObject * gobject)288 thrift_transport_dispose (GObject *gobject)
289 {
290   ThriftTransport *self = THRIFT_TRANSPORT (gobject);
291 
292   if(self->configuration != NULL)
293     g_clear_object (&self->configuration);
294 
295   /* Always chain up to the parent class; there is no need to check if
296    * the parent class implements the dispose() virtual function: it is
297    * always guaranteed to do so
298    */
299   G_OBJECT_CLASS (thrift_transport_parent_class)->dispose (gobject);
300 }
301 
302 /* class initializer for ThriftTransport */
303 static void
thrift_transport_class_init(ThriftTransportClass * cls)304 thrift_transport_class_init (ThriftTransportClass *cls)
305 {
306   GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
307   GParamSpec *param_spec = NULL;
308 
309   /* setup accessors and mutators */
310   gobject_class->get_property = thrift_transport_get_property;
311   gobject_class->set_property = thrift_transport_set_property;
312   gobject_class->dispose = thrift_transport_dispose;
313 
314   param_spec = g_param_spec_object ("configuration",
315                                     "configuration (construct)",
316                                     "Thrift Configuration",
317                                     THRIFT_TYPE_CONFIGURATION,
318                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
319   g_object_class_install_property (gobject_class,
320                                    PROP_THRIFT_TRANSPORT_CONFIGURATION,
321                                    param_spec);
322 
323   param_spec = g_param_spec_long ("remainingmessagesize",
324                                   "remainingmessagesize (construct)",
325                                   "Set the remaining message size",
326                                   0, /* min */
327                                   G_MAXINT32, /* max */
328                                   DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */
329                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
330   g_object_class_install_property (gobject_class,
331                                    PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE,
332                                    param_spec);
333 
334   param_spec = g_param_spec_long ("knowmessagesize",
335                                   "knowmessagesize (construct)",
336                                   "Set the known size of the message",
337                                   0, /* min */
338                                   G_MAXINT32, /* max */
339                                   DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */
340                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
341   g_object_class_install_property (gobject_class,
342                                    PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE,
343                                    param_spec);
344 
345 
346   /* set these as virtual methods to be implemented by a subclass */
347   cls->is_open = thrift_transport_is_open;
348   cls->open = thrift_transport_open;
349   cls->close = thrift_transport_close;
350   cls->read = thrift_transport_read;
351   cls->read_end = thrift_transport_read_end;
352   cls->write = thrift_transport_write;
353   cls->write_end = thrift_transport_write_end;
354   cls->flush = thrift_transport_flush;
355 
356   /* provide a default implementation for the peek and read_all methods */
357   cls->peek = thrift_transport_real_peek;
358   cls->read_all = thrift_transport_real_read_all;
359 
360   cls->updateKnownMessageSize = thrift_transport_updateKnownMessageSize;
361   cls->checkReadBytesAvailable = thrift_transport_checkReadBytesAvailable;
362   cls->resetConsumedMessageSize = thrift_transport_resetConsumedMessageSize;
363   cls->countConsumedMessageBytes = thrift_transport_countConsumedMessageBytes;
364 }
365 
366 static void
thrift_transport_init(ThriftTransport * transport)367 thrift_transport_init (ThriftTransport *transport)
368 {
369   THRIFT_UNUSED_VAR (transport);
370 }
371