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 <netdb.h>
21 #include <sys/wait.h>
22 
23 #include <thrift/c_glib/transport/thrift_transport.h>
24 #include <thrift/c_glib/transport/thrift_buffered_transport.h>
25 #include <thrift/c_glib/transport/thrift_server_transport.h>
26 #include <thrift/c_glib/transport/thrift_server_socket.h>
27 
28 #define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
29 
30 /* substituted functions to test failures of system and library calls */
31 static int socket_error = 0;
32 int
my_socket(int domain,int type,int protocol)33 my_socket(int domain, int type, int protocol)
34 {
35   if (socket_error == 0)
36     {
37       return socket (domain, type, protocol);
38     }
39   return -1;
40 }
41 
42 static int recv_error = 0;
43 ssize_t
my_recv(int socket,void * buffer,size_t length,int flags)44 my_recv(int socket, void *buffer, size_t length, int flags)
45 {
46   if (recv_error == 0)
47     {
48       return recv (socket, buffer, length, flags);
49     }
50   return -1;
51 }
52 
53 static int send_error = 0;
54 ssize_t
my_send(int socket,const void * buffer,size_t length,int flags)55 my_send(int socket, const void *buffer, size_t length, int flags)
56 {
57   if (send_error == 0)
58     {
59       return send (socket, buffer, length, flags);
60     }
61   return -1;
62 }
63 
64 #define socket my_socket
65 #define recv my_recv
66 #define send my_send
67 #include "../src/thrift/c_glib/transport/thrift_socket.c"
68 #undef socket
69 #undef recv
70 #undef send
71 
72 static void thrift_socket_server (const int port);
73 static void thrift_socket_server_open (const int port, int times);
74 /* test object creation and destruction */
75 static void
test_create_and_destroy(void)76 test_create_and_destroy(void)
77 {
78   gchar *hostname = NULL;
79   guint port = 0;
80 
81   GObject *object = NULL;
82   object = g_object_new (THRIFT_TYPE_SOCKET, NULL);
83   g_assert (object != NULL);
84   g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, NULL);
85   g_free (hostname);
86 
87   g_object_unref (object);
88 }
89 
90 static void
test_open_and_close(void)91 test_open_and_close(void)
92 {
93   ThriftSocket *tsocket = NULL;
94   ThriftTransport *transport = NULL;
95   GError *err = NULL;
96   int port = 51199;
97   pid_t pid;
98   int status;
99 
100   pid = fork ();
101   g_assert ( pid >= 0 );
102 
103   if ( pid == 0 )
104     {
105       /* child listens */
106       thrift_socket_server_open (port, 1);
107       exit (0);
108     } else {
109 	/* parent connects, wait a bit for the socket to be created */
110 	sleep (1);
111 
112 	/* open a connection and close it */
113 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
114 				"port", port, NULL);
115 	transport = THRIFT_TRANSPORT (tsocket);
116 	thrift_socket_open (transport, NULL);
117 	g_assert (thrift_socket_is_open (transport) == TRUE);
118 	thrift_socket_close (transport, NULL);
119 	g_assert (thrift_socket_is_open (transport) == FALSE);
120 
121 	/* test close failure */
122 	tsocket->sd = -1;
123 	thrift_socket_close (transport, NULL);
124 	g_object_unref (tsocket);
125 
126 	/* try a hostname lookup failure */
127 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
128 				NULL);
129 	transport = THRIFT_TRANSPORT (tsocket);
130 	g_assert (thrift_socket_open (transport, &err) == FALSE);
131 	g_object_unref (tsocket);
132 	g_error_free (err);
133 	err = NULL;
134 
135 	/* try an error call to socket() */
136 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", NULL);
137 	transport = THRIFT_TRANSPORT (tsocket);
138 	socket_error = 1;
139 	g_assert (thrift_socket_open (transport, &err) == FALSE);
140 	socket_error = 0;
141 	g_object_unref (tsocket);
142 	g_error_free (err);
143 	g_assert ( wait (&status) == pid );
144 	g_assert ( status == 0 );
145     }
146 }
147 
148 static void
test_read_and_write(void)149 test_read_and_write(void)
150 {
151   ThriftSocket *tsocket = NULL;
152   ThriftTransport *transport = NULL;
153   pid_t pid;
154   int port = 51199;
155   int status;
156   guchar buf[10] = TEST_DATA; /* a buffer */
157 
158   pid = fork ();
159   g_assert ( pid >= 0 );
160 
161   if ( pid == 0 )
162     {
163       /* child listens */
164       thrift_socket_server (port);
165       exit (0);
166     } else {
167 	/* parent connects, wait a bit for the socket to be created */
168 	sleep (1);
169 
170 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
171 				"port", port, NULL);
172 	transport = THRIFT_TRANSPORT (tsocket);
173 	g_assert (thrift_socket_open (transport, NULL) == TRUE);
174 	g_assert (thrift_socket_is_open (transport));
175 	thrift_socket_write (transport, buf, 10, NULL);
176 
177 	/* write fail */
178 	send_error = 1;
179 	thrift_socket_write (transport, buf, 1, NULL);
180 	send_error = 0;
181 
182 	thrift_socket_write_end (transport, NULL);
183 	thrift_socket_flush (transport, NULL);
184 	thrift_socket_close (transport, NULL);
185 	g_object_unref (tsocket);
186 
187 	g_assert ( wait (&status) == pid );
188 	g_assert ( status == 0 );
189     }
190 }
191 
192 /* test ThriftSocket's peek() implementation */
193 static void
test_peek(void)194 test_peek(void)
195 {
196   gint status;
197   pid_t pid;
198   guint port = 51199;
199   gchar data = 'A';
200   ThriftTransport *client_transport;
201   GError *error = NULL;
202 
203   client_transport = g_object_new (THRIFT_TYPE_SOCKET,
204 				   "hostname", "localhost",
205 				   "port",     port,
206 				   NULL);
207 
208   /* thrift_transport_peek returns FALSE when the socket is closed */
209   g_assert (thrift_transport_is_open (client_transport) == FALSE);
210   g_assert (thrift_transport_peek (client_transport, &error) == FALSE);
211   g_assert (error == NULL);
212 
213   pid = fork ();
214   g_assert (pid >= 0);
215 
216   if (pid == 0)
217     {
218       ThriftServerTransport *server_transport = NULL;
219 
220       g_object_unref (client_transport);
221 
222       /* child listens */
223       server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
224 				       "port", port,
225 				       NULL);
226       g_assert (server_transport != NULL);
227 
228       thrift_server_transport_listen (server_transport, &error);
229       g_assert (error == NULL);
230 
231       client_transport = g_object_new
232 	  (THRIFT_TYPE_BUFFERED_TRANSPORT,
233 	   "transport",  thrift_server_transport_accept (server_transport, &error),
234 	   "r_buf_size", 0,
235 	   "w_buf_size", sizeof data,
236 	   NULL);
237       g_assert (error == NULL);
238       g_assert (client_transport != NULL);
239 
240       /* write exactly one character to the client */
241       g_assert (thrift_transport_write (client_transport,
242 					&data,
243 					sizeof data,
244 					&error) == TRUE);
245 
246       thrift_transport_flush (client_transport, &error);
247       thrift_transport_write_end (client_transport, &error);
248       thrift_transport_close (client_transport, &error);
249 
250       g_object_unref (client_transport);
251       g_object_unref (server_transport);
252 
253       exit (0);
254     }
255   else {
256       /* parent connects, wait a bit for the socket to be created */
257       sleep (1);
258 
259       /* connect to the child */
260       thrift_transport_open (client_transport, &error);
261       g_assert (error == NULL);
262       g_assert (thrift_transport_is_open (client_transport) == TRUE);
263 
264       /* thrift_transport_peek returns TRUE when the socket is open and there is
265        data available to be read */
266       g_assert (thrift_transport_peek (client_transport, &error) == TRUE);
267       g_assert (error == NULL);
268 
269       /* read exactly one character from the server */
270       g_assert_cmpint (thrift_transport_read (client_transport,
271 					      &data,
272 					      sizeof data,
273 					      &error), ==, sizeof data);
274 
275       /* thrift_transport_peek returns FALSE when the socket is open but there is
276        no (more) data available to be read */
277       g_assert (thrift_transport_is_open (client_transport) == TRUE);
278       g_assert (thrift_transport_peek (client_transport, &error) == FALSE);
279       g_assert (error == NULL);
280 
281       thrift_transport_read_end (client_transport, &error);
282       thrift_transport_close (client_transport, &error);
283 
284       g_object_unref (client_transport);
285 
286       g_assert (wait (&status) == pid);
287       g_assert (status == 0);
288   }
289 }
290 
291 static void
thrift_socket_server_open(const int port,int times)292 thrift_socket_server_open (const int port, int times)
293 {
294   ThriftServerTransport *transport = NULL;
295   ThriftTransport *client = NULL;
296   int i;
297   ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
298 					      "port", port, NULL);
299 
300   transport = THRIFT_SERVER_TRANSPORT (tsocket);
301   thrift_server_transport_listen (transport, NULL);
302   for(i=0;i<times;i++){
303       client = thrift_server_transport_accept (transport, NULL);
304       g_assert (client != NULL);
305       thrift_socket_close (client, NULL);
306       g_object_unref (client);
307   }
308   g_object_unref (tsocket);
309 }
310 
311 
312 static void
thrift_socket_server(const int port)313 thrift_socket_server (const int port)
314 {
315   int bytes = 0;
316   ThriftServerTransport *transport = NULL;
317   ThriftTransport *client = NULL;
318   guchar buf[10]; /* a buffer */
319   guchar match[10] = TEST_DATA;
320 
321   ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
322 					      "port", port, NULL);
323 
324   transport = THRIFT_SERVER_TRANSPORT (tsocket);
325   thrift_server_transport_listen (transport, NULL);
326   client = thrift_server_transport_accept (transport, NULL);
327   g_assert (client != NULL);
328 
329   /* read 10 bytes */
330   bytes = thrift_socket_read (client, buf, 10, NULL);
331   g_assert (bytes == 10); /* make sure we've read 10 bytes */
332   g_assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */
333 
334   /* failed read */
335   recv_error = 1;
336   thrift_socket_read (client, buf, 1, NULL);
337   recv_error = 0;
338 
339   thrift_socket_read_end (client, NULL);
340   thrift_socket_close (client, NULL);
341   g_object_unref (tsocket);
342   g_object_unref (client);
343 }
344 
345 int
main(int argc,char * argv[])346 main(int argc, char *argv[])
347 {
348 #if (!GLIB_CHECK_VERSION (2, 36, 0))
349   g_type_init();
350 #endif
351 
352   g_test_init (&argc, &argv, NULL);
353 
354   g_test_add_func ("/testtransportsocket/CreateAndDestroy", test_create_and_destroy);
355   g_test_add_func ("/testtransportsocket/OpenAndClose", test_open_and_close);
356   g_test_add_func ("/testtransportsocket/ReadAndWrite", test_read_and_write);
357   g_test_add_func ("/testtransportsocket/Peek", test_peek);
358 
359   return g_test_run ();
360 }
361 
362