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_socket.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 #include "../src/thrift/c_glib/transport/thrift_framed_transport.c"
31 
32 static void thrift_server (const int port);
33 static void thrift_socket_server_open (const int port, int times);
34 
35 /* test object creation and destruction */
36 static void
test_create_and_destroy(void)37 test_create_and_destroy(void)
38 {
39   ThriftTransport *transport = NULL;
40   guint r_buf_size = 0;
41   guint w_buf_size = 0;
42 
43   GObject *object = NULL;
44   object = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, NULL);
45   g_assert (object != NULL);
46   g_object_get (G_OBJECT (object), "transport", &transport,
47 		"r_buf_size", &r_buf_size,
48 		"w_buf_size", &w_buf_size, NULL);
49   g_object_unref (object);
50 }
51 
52 static void
test_open_and_close(void)53 test_open_and_close(void)
54 {
55   ThriftSocket *tsocket = NULL;
56   ThriftTransport *transport = NULL;
57   GError *err = NULL;
58   pid_t pid;
59   int port = 51199;
60   int status;
61 
62   pid = fork ();
63   g_assert ( pid >= 0 );
64 
65   if ( pid == 0 )
66     {
67       /* child listens */
68       thrift_socket_server_open (port,1);
69       exit (0);
70     } else {
71 	/* parent connects, wait a bit for the socket to be created */
72 	sleep (1);
73 	/* create a ThriftSocket */
74 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost",
75 				"port", port, NULL);
76 
77 	/* create a BufferedTransport wrapper of the Socket */
78 	transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
79 				  "transport", THRIFT_TRANSPORT (tsocket), NULL);
80 
81 	/* this shouldn't work */
82 	g_assert (thrift_framed_transport_open (transport, NULL) == TRUE);
83 	g_assert (thrift_framed_transport_is_open (transport) == TRUE);
84 	g_assert (thrift_framed_transport_close (transport, NULL) == TRUE);
85 	g_object_unref (transport);
86 	g_object_unref (tsocket);
87 
88 	/* try and underlying socket failure */
89 	tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken",
90 				NULL);
91 
92 	/* create a BufferedTransport wrapper of the Socket */
93 	transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
94 				  "transport", THRIFT_TRANSPORT (tsocket), NULL);
95 
96 	g_assert (thrift_framed_transport_open (transport, &err) == FALSE);
97 	g_object_unref (transport);
98 	g_object_unref (tsocket);
99 	g_error_free (err);
100 	err = NULL;
101 
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_FRAMED_TRANSPORT,
132 				  "transport", THRIFT_TRANSPORT (tsocket),
133 				  "w_buf_size", 4, NULL);
134 
135 	g_assert (thrift_framed_transport_open (transport, NULL) == TRUE);
136 	g_assert (thrift_framed_transport_is_open (transport));
137 
138 	/* write 10 bytes */
139 	thrift_framed_transport_write (transport, buf, 10, NULL);
140 	thrift_framed_transport_flush (transport, NULL);
141 
142 	thrift_framed_transport_write (transport, buf, 1, NULL);
143 	thrift_framed_transport_flush (transport, NULL);
144 
145 	thrift_framed_transport_write (transport, buf, 10, NULL);
146 	thrift_framed_transport_flush (transport, NULL);
147 
148 	thrift_framed_transport_write (transport, buf, 10, NULL);
149 	thrift_framed_transport_flush (transport, NULL);
150 
151 	thrift_framed_transport_write_end (transport, NULL);
152 	thrift_framed_transport_flush (transport, NULL);
153 	thrift_framed_transport_close (transport, NULL);
154 
155 	g_object_unref (transport);
156 	g_object_unref (tsocket);
157 
158 	g_assert ( wait (&status) == pid );
159 	g_assert ( status == 0 );
160     }
161 }
162 
163 /* test reading from the transport after the peer has unexpectedly
164    closed the connection */
165 static void
test_read_after_peer_close(void)166 test_read_after_peer_close(void)
167 {
168   int status;
169   pid_t pid;
170   int port = 51199;
171   GError *err = NULL;
172 
173   pid = fork ();
174   g_assert (pid >= 0);
175 
176   if (pid == 0)
177     {
178       ThriftServerTransport *server_transport = NULL;
179       ThriftTransport *client_transport = NULL;
180 
181       /* child listens */
182       server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
183 				       "port", port,
184 				       NULL);
185       g_assert (server_transport != NULL);
186 
187       thrift_server_transport_listen (server_transport, &err);
188       g_assert (err == NULL);
189 
190       /* wrap the client transport in a ThriftFramedTransport */
191       client_transport = g_object_new
192 	  (THRIFT_TYPE_FRAMED_TRANSPORT,
193 	   "transport",  thrift_server_transport_accept (server_transport, &err),
194 	   "r_buf_size", 0,
195 	   NULL);
196       g_assert (err == NULL);
197       g_assert (client_transport != NULL);
198 
199       /* close the connection immediately after the client connects */
200       thrift_transport_close (client_transport, NULL);
201 
202       g_object_unref (client_transport);
203       g_object_unref (server_transport);
204 
205       exit (0);
206     } else {
207 	ThriftSocket *tsocket = NULL;
208 	ThriftTransport *transport = NULL;
209 	guchar buf[10]; /* a buffer */
210 
211 	/* parent connects, wait a bit for the socket to be created */
212 	sleep (1);
213 
214 	tsocket = g_object_new (THRIFT_TYPE_SOCKET,
215 				"hostname", "localhost",
216 				"port",     port,
217 				NULL);
218 	transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
219 				  "transport",  THRIFT_TRANSPORT (tsocket),
220 				  "w_buf_size", 0,
221 				  NULL);
222 
223 	g_assert (thrift_transport_open (transport, NULL) == TRUE);
224 	g_assert (thrift_transport_is_open (transport));
225 
226 	/* attempting to read from the transport after the peer has closed
227        the connection fails gracefully without generating a critical
228        warning or segmentation fault */
229 	thrift_transport_read (transport, buf, 10, &err);
230 	g_assert (err != NULL);
231 
232 	g_error_free (err);
233 	err = NULL;
234 
235 	thrift_transport_read_end (transport, &err);
236 	g_assert (err == NULL);
237 
238 	thrift_transport_close (transport, &err);
239 	g_assert (err == NULL);
240 
241 	g_object_unref (transport);
242 	g_object_unref (tsocket);
243 
244 	g_assert (wait (&status) == pid);
245 	g_assert (status == 0);
246     }
247 }
248 
249 static void
thrift_socket_server_open(const int port,int times)250 thrift_socket_server_open (const int port, int times)
251 {
252   ThriftServerTransport *transport = NULL;
253   ThriftTransport *client = NULL;
254   int i;
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   for(i=0;i<times;i++){
262       client = thrift_server_transport_accept (transport, NULL);
263       g_assert (client != NULL);
264       thrift_socket_close (client, NULL);
265       g_object_unref (client);
266   }
267   g_object_unref (tsocket);
268 }
269 
270 static void
thrift_server(const int port)271 thrift_server (const int port)
272 {
273   int bytes = 0;
274   ThriftServerTransport *transport = NULL;
275   ThriftTransport *client = NULL;
276   guchar buf[12]; /* a buffer */
277   guchar match[10] = TEST_DATA;
278 
279   ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
280 					      "port", port, NULL);
281 
282   transport = THRIFT_SERVER_TRANSPORT (tsocket);
283   thrift_server_transport_listen (transport, NULL);
284 
285   /* wrap the client in a BufferedTransport */
286   client = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport",
287 			 thrift_server_transport_accept (transport, NULL),
288 			 "r_buf_size", 5, NULL);
289   g_assert (client != NULL);
290 
291   /* read 10 bytes */
292   bytes = thrift_framed_transport_read (client, buf, 10, NULL);
293   g_assert (bytes == 10); /* make sure we've read 10 bytes */
294   g_assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */
295 
296   bytes = thrift_framed_transport_read (client, buf, 6, NULL);
297   bytes = thrift_framed_transport_read (client, buf, 5, NULL);
298   bytes = thrift_framed_transport_read (client, buf, 1, NULL);
299 
300   bytes = thrift_framed_transport_read (client, buf, 12, NULL);
301 
302   thrift_framed_transport_read_end (client, NULL);
303   thrift_framed_transport_close (client, NULL);
304   g_object_unref (client);
305   g_object_unref (tsocket);
306 }
307 
308 int
main(int argc,char * argv[])309 main(int argc, char *argv[])
310 {
311 #if (!GLIB_CHECK_VERSION (2, 36, 0))
312   g_type_init();
313 #endif
314 
315   g_test_init (&argc, &argv, NULL);
316 
317   g_test_add_func ("/testframedtransport/CreateAndDestroy", test_create_and_destroy);
318   g_test_add_func ("/testframedtransport/OpenAndClose", test_open_and_close);
319   g_test_add_func ("/testframedtransport/ReadAndWrite", test_read_and_write);
320   g_test_add_func ("/testframedtransport/ReadAfterPeerClose", test_read_after_peer_close);
321 
322   return g_test_run ();
323 }
324