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 <signal.h>
22 #include <sys/wait.h>
23 
24 #include <thrift/c_glib/transport/thrift_transport.h>
25 #include <thrift/c_glib/transport/thrift_socket.h>
26 #include <thrift/c_glib/transport/thrift_server_transport.h>
27 #include <thrift/c_glib/transport/thrift_server_socket.h>
28 
29 #define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
30 
31 #include "../src/thrift/c_glib/transport/thrift_buffered_transport.c"
32 
33 static void thrift_server (const int port);
34 static void thrift_socket_server_open (const int port, int times);
35 
36 /* test object creation and destruction */
37 static void
test_create_and_destroy(void)38 test_create_and_destroy(void)
39 {
40   ThriftTransport *transport = NULL;
41   guint r_buf_size = 0;
42   guint w_buf_size = 0;
43 
44   GObject *object = NULL;
45   object = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, NULL);
46   g_assert (object != NULL);
47   g_object_get (G_OBJECT (object), "transport", &transport,
48 		"r_buf_size", &r_buf_size,
49 		"w_buf_size", &w_buf_size, NULL);
50   g_object_unref (object);
51 }
52 
53 static void
test_open_and_close(void)54 test_open_and_close(void)
55 {
56   ThriftSocket *tsocket = NULL;
57   ThriftTransport *transport = NULL;
58   GError *err = NULL;
59   pid_t pid;
60   int port = 51199;
61   int status;
62 
63   pid = fork ();
64   g_assert ( pid >= 0 );
65 
66   if ( pid == 0 )
67     {
68       /* child listens */
69       thrift_socket_server_open (port,1);
70       exit (0);
71     } else {
72 	/* parent connects, wait a bit for the socket to be created */
73 	sleep (1);
74 	/* create a ThriftSocket */
75 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
76 				"port", port, NULL);
77 
78 	/* create a BufferedTransport wrapper of the Socket */
79 	transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
80 				  "transport", THRIFT_TRANSPORT (tsocket), NULL);
81 
82 	/* this shouldn't work */
83 	g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
84 	g_assert (thrift_buffered_transport_is_open (transport) == TRUE);
85 	g_assert (thrift_buffered_transport_close (transport, NULL) == TRUE);
86 	g_object_unref (transport);
87 	g_object_unref (tsocket);
88 
89 	/* try and underlying socket failure */
90 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
91 				NULL);
92 
93 	/* create a BufferedTransport wrapper of the Socket */
94 	transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
95 				  "transport", THRIFT_TRANSPORT (tsocket), NULL);
96 
97 	g_assert (thrift_buffered_transport_open (transport, &err) == FALSE);
98 	g_object_unref (transport);
99 	g_object_unref (tsocket);
100 	g_error_free (err);
101 	err = NULL;
102 	g_assert ( wait (&status) == pid );
103 	g_assert ( status == 0 );
104     }
105 }
106 
107 static void
test_read_and_write(void)108 test_read_and_write(void)
109 {
110   int status;
111   pid_t pid;
112   ThriftSocket *tsocket = NULL;
113   ThriftTransport *transport = NULL;
114   int port = 51199;
115   guchar buf[10] = TEST_DATA; /* a buffer */
116 
117   pid = fork ();
118   g_assert ( pid >= 0 );
119 
120   if ( pid == 0 )
121     {
122       /* child listens */
123       thrift_server (port);
124       exit (0);
125     } else {
126 	/* parent connects, wait a bit for the socket to be created */
127 	sleep (1);
128 
129 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
130 				"port", port, NULL);
131 	transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
132 				  "transport", THRIFT_TRANSPORT (tsocket),
133 				  "w_buf_size", 4, NULL);
134 
135 	g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
136 	g_assert (thrift_buffered_transport_is_open (transport));
137 
138 	/* write 10 bytes */
139 	thrift_buffered_transport_write (transport, buf, 10, NULL);
140 
141 	/* write 1 byte at a time */
142 	thrift_buffered_transport_write (transport, buf, 1, NULL);
143 	thrift_buffered_transport_write (transport, buf, 1, NULL);
144 	thrift_buffered_transport_write (transport, buf, 1, NULL);
145 
146 	/* overflow the buffer */
147 	thrift_buffered_transport_write (transport, buf, 2, NULL);
148 	thrift_buffered_transport_write (transport, buf, 1, NULL);
149 	thrift_buffered_transport_flush (transport, NULL);
150 
151 	/* write 1 byte and flush */
152 	thrift_buffered_transport_write (transport, buf, 1, NULL);
153 	thrift_buffered_transport_flush (transport, NULL);
154 
155 	/* write and overflow buffer with 2 system calls */
156 	thrift_buffered_transport_write (transport, buf, 1, NULL);
157 	thrift_buffered_transport_write (transport, buf, 3, NULL);
158 
159 	/* write 10 bytes */
160 	thrift_buffered_transport_write (transport, buf, 10, NULL);
161 
162 	thrift_buffered_transport_write_end (transport, NULL);
163 	thrift_buffered_transport_flush (transport, NULL);
164 	thrift_buffered_transport_close (transport, NULL);
165 
166 	g_object_unref (transport);
167 	g_object_unref (tsocket);
168 
169 	g_assert ( wait (&status) == pid );
170 	g_assert ( status == 0 );
171     }
172 }
173 
174 
175 static void
thrift_socket_server_open(const int port,int times)176 thrift_socket_server_open (const int port, int times)
177 {
178   ThriftServerTransport *transport = NULL;
179   ThriftTransport *client = NULL;
180   int i;
181   ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
182 					      "port", port, NULL);
183 
184   transport = THRIFT_SERVER_TRANSPORT (tsocket);
185   thrift_server_transport_listen (transport, NULL);
186   for(i=0;i<times;i++){
187       client = thrift_server_transport_accept (transport, NULL);
188       g_assert (client != NULL);
189       thrift_socket_close (client, NULL);
190       g_object_unref (client);
191   }
192   g_object_unref (tsocket);
193 }
194 
195 static void
thrift_server(const int port)196 thrift_server (const int port)
197 {
198   int bytes = 0;
199   ThriftServerTransport *transport = NULL;
200   ThriftTransport *client = NULL;
201   guchar buf[10]; /* a buffer */
202   guchar match[10] = TEST_DATA;
203 
204   ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
205 					      "port", port, NULL);
206 
207   transport = THRIFT_SERVER_TRANSPORT (tsocket);
208   thrift_server_transport_listen (transport, NULL);
209 
210   /* wrap the client in a BufferedTransport */
211   client = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, "transport",
212 			 thrift_server_transport_accept (transport, NULL),
213 			 "r_buf_size", 5, NULL);
214   g_assert (client != NULL);
215 
216   /* read 10 bytes */
217   bytes = thrift_buffered_transport_read (client, buf, 10, NULL);
218   g_assert (bytes == 10); /* make sure we've read 10 bytes */
219   g_assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */
220 
221   /* read 1 byte */
222   bytes = thrift_buffered_transport_read (client, buf, 1, NULL);
223 
224   bytes = thrift_buffered_transport_read (client, buf, 6, NULL);
225   bytes = thrift_buffered_transport_read (client, buf, 2, NULL);
226   bytes = thrift_buffered_transport_read (client, buf, 1, NULL);
227 
228   thrift_buffered_transport_read_end (client, NULL);
229   thrift_buffered_transport_close (client, NULL);
230   g_object_unref (client);
231   g_object_unref (tsocket);
232 }
233 
234 static void
test_write_fail(void)235 test_write_fail(void)
236 {
237   int status;
238   pid_t pid;
239   ThriftSocket *tsocket = NULL;
240   ThriftTransport *transport = NULL;
241   int port = 51198;
242   guchar buf[10] = TEST_DATA; /* a buffer */
243 
244   /* SIGPIPE when send to disconnected socket */
245   signal(SIGPIPE, SIG_IGN);
246 
247   pid = fork ();
248   g_assert ( pid >= 0 );
249 
250   if ( pid == 0 )
251     {
252       /* child listens */
253       ThriftServerTransport *transport = NULL;
254       ThriftTransport *client = NULL;
255 
256       ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
257 						  "port", port, NULL);
258 
259       transport = THRIFT_SERVER_TRANSPORT (tsocket);
260       thrift_server_transport_listen (transport, NULL);
261 
262       /* wrap the client in a BufferedTransport */
263       client = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, "transport",
264 			     thrift_server_transport_accept (transport, NULL),
265 			     "r_buf_size", 5, NULL);
266       g_assert (client != NULL);
267 
268       /* just close socket */
269       thrift_buffered_transport_close (client, NULL);
270       g_object_unref (client);
271       g_object_unref (tsocket);
272       exit (0);
273     } else {
274 	/* parent connects, wait a bit for the socket to be created */
275 	sleep (1);
276 
277 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
278 				"port", port, NULL);
279 	transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
280 				  "transport", THRIFT_TRANSPORT (tsocket),
281 				  "w_buf_size", 4, NULL);
282 
283 
284 	g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE);
285 	g_assert (thrift_buffered_transport_is_open (transport));
286 
287 	/* recognize disconnection */
288 	sleep(1);
289 	g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == TRUE);
290 	g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == FALSE);
291 
292 	/* write and overflow buffer */
293 	g_assert (thrift_buffered_transport_write (transport, buf, 10, NULL) == FALSE);
294 
295 	/* write 1 and flush */
296 	g_assert (thrift_buffered_transport_write (transport, buf, 1, NULL) == TRUE);
297 	g_assert (thrift_buffered_transport_flush (transport, NULL) == FALSE);
298 
299 	thrift_buffered_transport_close (transport, NULL);
300 
301 	g_object_unref (transport);
302 	g_object_unref (tsocket);
303 
304 	g_assert ( wait (&status) == pid );
305 	g_assert ( status == 0 );
306     }
307 }
308 
309 int
main(int argc,char * argv[])310 main(int argc, char *argv[])
311 {
312 #if (!GLIB_CHECK_VERSION (2, 36, 0))
313   g_type_init();
314 #endif
315 
316   g_test_init (&argc, &argv, NULL);
317 
318   g_test_add_func ("/testbufferedtransport/CreateAndDestroy", test_create_and_destroy);
319   g_test_add_func ("/testbufferedtransport/OpenAndClose", test_open_and_close);
320   g_test_add_func ("/testbufferedtransport/ReadAndWrite", test_read_and_write);
321   g_test_add_func ("/testbufferedtransport/WriteFail", test_write_fail);
322 
323   return g_test_run ();
324 }
325 
326