View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.test.transport;
17  
18  import org.fourthline.cling.UpnpServiceConfiguration;
19  import org.fourthline.cling.mock.MockProtocolFactory;
20  import org.fourthline.cling.mock.MockRouter;
21  import org.fourthline.cling.mock.MockUpnpServiceConfiguration;
22  import org.fourthline.cling.model.message.StreamRequestMessage;
23  import org.fourthline.cling.model.message.StreamResponseMessage;
24  import org.fourthline.cling.model.message.UpnpRequest;
25  import org.fourthline.cling.model.message.UpnpResponse;
26  import org.fourthline.cling.protocol.ProtocolCreationException;
27  import org.fourthline.cling.protocol.ReceivingSync;
28  import org.fourthline.cling.transport.spi.StreamClient;
29  import org.fourthline.cling.transport.spi.StreamServer;
30  import org.fourthline.cling.transport.spi.UpnpStream;
31  import org.testng.annotations.AfterClass;
32  import org.testng.annotations.BeforeClass;
33  import org.testng.annotations.BeforeMethod;
34  import org.testng.annotations.Test;
35  
36  import java.net.InetAddress;
37  import java.net.URI;
38  import java.util.logging.Logger;
39  
40  import static org.testng.Assert.*;
41  
42  abstract public class StreamServerClientTest {
43  
44      final private static Logger log = Logger.getLogger(StreamServerClientTest.class.getName());
45  
46      public static final String TEST_HOST = "localhost";
47      public static final int TEST_PORT = 8081;
48  
49      public UpnpServiceConfiguration configuration = new MockUpnpServiceConfiguration(false, true);
50  
51      public MockProtocolFactory protocolFactory = new MockProtocolFactory() {
52  
53          @Override
54          public ReceivingSync createReceivingSync(StreamRequestMessage requestMessage) throws ProtocolCreationException {
55              String path = requestMessage.getUri().getPath();
56              if (path.endsWith(OKEmptyResponse.PATH)) {
57                  lastExecutedServerProtocol = new OKEmptyResponse(requestMessage);
58              } else if (path.endsWith(OKBodyResponse.PATH)) {
59                  lastExecutedServerProtocol = new OKBodyResponse(requestMessage);
60              } else if (path.endsWith(NoResponse.PATH)) {
61                  lastExecutedServerProtocol = new NoResponse(requestMessage);
62              } else if (path.endsWith(DelayedResponse.PATH)) {
63                  lastExecutedServerProtocol = new DelayedResponse(requestMessage);
64              } else if (path.endsWith(TooLongResponse.PATH)) {
65                  lastExecutedServerProtocol = new TooLongResponse(requestMessage);
66              } else if (path.endsWith(CheckAliveResponse.PATH)) {
67                  lastExecutedServerProtocol = new CheckAliveResponse(requestMessage);
68              } else if (path.endsWith(CheckAliveLongResponse.PATH)) {
69                  lastExecutedServerProtocol = new CheckAliveLongResponse(requestMessage);
70              } else {
71                  throw new ProtocolCreationException("Invalid test path: " + path);
72              }
73              return lastExecutedServerProtocol;
74          }
75      };
76  
77      public MockRouter router = new MockRouter(configuration, protocolFactory) {
78          @Override
79          public void received(UpnpStream stream) {
80              stream.run();
81          }
82      };
83  
84      public StreamServer server;
85      public StreamClient client;
86      public TestProtocol lastExecutedServerProtocol;
87  
88  
89      @BeforeClass
90      public void start() throws Exception {
91          server = createStreamServer(TEST_PORT);
92          server.init(InetAddress.getByName(TEST_HOST), router);
93          configuration.getStreamServerExecutorService().execute(server);
94  
95          client = createStreamClient(configuration);
96          Thread.sleep(1000);
97      }
98  
99      @AfterClass
100     public void stop() throws Exception {
101         server.stop();
102         client.stop();
103         Thread.sleep(1000);
104     }
105 
106     @BeforeMethod
107     public void clearLastProtocol() {
108         lastExecutedServerProtocol = null;
109     }
110 
111     @Test
112     public void basic() throws Exception {
113         StreamResponseMessage responseMessage;
114 
115         responseMessage = client.sendRequest(createRequestMessage(OKEmptyResponse.PATH));
116         assertEquals(responseMessage.getOperation().getStatusCode(), 200);
117         assertFalse(responseMessage.hasBody());
118         assertTrue(lastExecutedServerProtocol.isComplete);
119 
120         lastExecutedServerProtocol = null;
121         responseMessage = client.sendRequest(createRequestMessage(OKBodyResponse.PATH));
122         assertEquals(responseMessage.getOperation().getStatusCode(), 200);
123         assertTrue(responseMessage.hasBody());
124         assertEquals(responseMessage.getBodyString(), "foo");
125         assertTrue(lastExecutedServerProtocol.isComplete);
126 
127         lastExecutedServerProtocol = null;
128         responseMessage = client.sendRequest(createRequestMessage(NoResponse.PATH));
129         assertEquals(responseMessage.getOperation().getStatusCode(), 404);
130         assertFalse(responseMessage.hasBody());
131         assertFalse(lastExecutedServerProtocol.isComplete);
132     }
133 
134     @Test
135     public void cancelled() throws Exception {
136         final boolean[] tests = new boolean[1];
137 
138         final Thread requestThread = new Thread(new Runnable() {
139             @Override
140             public void run() {
141                 try {
142                     client.sendRequest(createRequestMessage(DelayedResponse.PATH));
143                 } catch (InterruptedException ex) {
144                     // We expect this thread to be interrupted
145                     tests[0] = true;
146                 }
147             }
148         });
149 
150         requestThread.start();
151 
152         // Cancel the request after 250ms
153         new Thread(new Runnable() {
154             @Override
155             public void run() {
156                 try {
157                     Thread.sleep(250);
158                 } catch (InterruptedException ex) {
159                     // Ignore
160                 }
161                 requestThread.interrupt();
162             }
163         }).start();
164 
165         Thread.sleep(3000);
166         for (boolean test : tests) {
167             assertTrue(test);
168         }
169         // The server doesn't check if the connection is still alive, so it will complete
170         assertTrue(lastExecutedServerProtocol.isComplete);
171     }
172 
173     @Test
174     public void expired() throws Exception {
175         StreamResponseMessage responseMessage = client.sendRequest(createRequestMessage(TooLongResponse.PATH));
176         assertNull(responseMessage);
177         assertFalse(lastExecutedServerProtocol.isComplete);
178         // The client expires the HTTP connection but the server doesn't check if
179         // it's alive, so the server will complete the request after a while
180         Thread.sleep(3000);
181         assertTrue(lastExecutedServerProtocol.isComplete);
182     }
183 
184     @Test
185     public void checkAlive() throws Exception {
186         StreamResponseMessage responseMessage = client.sendRequest(createRequestMessage(CheckAliveResponse.PATH));
187         assertEquals(responseMessage.getOperation().getStatusCode(), 200);
188         assertFalse(responseMessage.hasBody());
189         assertTrue(lastExecutedServerProtocol.isComplete);
190     }
191 
192     @Test
193     public void checkAliveExpired() throws Exception {
194         StreamResponseMessage responseMessage = client.sendRequest(createRequestMessage(CheckAliveLongResponse.PATH));
195         assertNull(responseMessage);
196         // The client expires the HTTP connection and the server checks if the
197         // connection is still alive, it will abort the request
198         Thread.sleep(3000);
199         assertFalse(lastExecutedServerProtocol.isComplete);
200     }
201 
202     @Test
203     public void checkAliveCancelled() throws Exception {
204         final boolean[] tests = new boolean[1];
205 
206         final Thread requestThread = new Thread(new Runnable() {
207             @Override
208             public void run() {
209                 try {
210                     client.sendRequest(createRequestMessage(CheckAliveResponse.PATH));
211                 } catch (InterruptedException ex) {
212                     // We expect this thread to be interrupted
213                     tests[0] = true;
214                 }
215             }
216         });
217 
218         requestThread.start();
219 
220         // Cancel the request after 1 second
221         new Thread(new Runnable() {
222             @Override
223             public void run() {
224                 try {
225                     Thread.sleep(1000);
226                 } catch (InterruptedException ex) {
227                     // Ignore
228                 }
229                 requestThread.interrupt();
230             }
231         }).start();
232 
233         Thread.sleep(3000);
234         for (boolean test : tests) {
235             assertTrue(test);
236         }
237         assertFalse(lastExecutedServerProtocol.isComplete);
238     }
239 
240     protected StreamRequestMessage createRequestMessage(String path) {
241         return new StreamRequestMessage(
242             UpnpRequest.Method.GET,
243             URI.create("http://" + TEST_HOST + ":" + TEST_PORT + path)
244         );
245     }
246 
247     abstract public StreamServer createStreamServer(int port);
248 
249     abstract public StreamClient createStreamClient(UpnpServiceConfiguration configuration);
250 
251     public abstract class TestProtocol extends ReceivingSync<StreamRequestMessage, StreamResponseMessage> {
252         volatile public boolean isComplete;
253 
254         public TestProtocol(StreamRequestMessage inputMessage) {
255             super(null, inputMessage);
256         }
257     }
258 
259     public class OKEmptyResponse extends TestProtocol {
260 
261         public static final String PATH = "/ok";
262 
263         public OKEmptyResponse(StreamRequestMessage inputMessage) {
264             super(inputMessage);
265         }
266 
267         @Override
268         protected StreamResponseMessage executeSync() {
269             isComplete = true;
270             return new StreamResponseMessage(UpnpResponse.Status.OK);
271         }
272     }
273 
274     public class OKBodyResponse extends TestProtocol{
275 
276         public static final String PATH = "/okbody";
277 
278         public OKBodyResponse(StreamRequestMessage inputMessage) {
279             super(inputMessage);
280         }
281 
282         @Override
283         protected StreamResponseMessage executeSync() {
284             isComplete = true;
285             return new StreamResponseMessage("foo");
286         }
287     }
288 
289     public class NoResponse extends TestProtocol {
290 
291         public static final String PATH = "/noresponse";
292 
293         public NoResponse(StreamRequestMessage inputMessage) {
294             super(inputMessage);
295         }
296 
297         @Override
298         protected StreamResponseMessage executeSync() {
299             return null;
300         }
301     }
302 
303     public class DelayedResponse extends TestProtocol {
304 
305         public static final String PATH = "/delayed";
306 
307         public DelayedResponse(StreamRequestMessage inputMessage) {
308             super(inputMessage);
309         }
310 
311         @Override
312         protected StreamResponseMessage executeSync() {
313             try {
314                 log.info("Sleeping for 2 seconds before completion...");
315                 Thread.sleep(2000);
316             } catch (InterruptedException ex) {
317                 throw new RuntimeException(ex);
318             }
319             isComplete = true;
320             return new StreamResponseMessage(UpnpResponse.Status.OK);
321         }
322     }
323 
324     public class TooLongResponse extends TestProtocol {
325 
326         public static final String PATH = "/toolong";
327 
328         public TooLongResponse(StreamRequestMessage inputMessage) {
329             super(inputMessage);
330         }
331 
332         @Override
333         protected StreamResponseMessage executeSync() {
334             try {
335                 log.info("Sleeping for 4 seconds before completion...");
336                 Thread.sleep(4000);
337             } catch (InterruptedException ex) {
338                 throw new RuntimeException(ex);
339             }
340             isComplete = true;
341             return new StreamResponseMessage(UpnpResponse.Status.OK);
342         }
343     }
344 
345     public class CheckAliveResponse extends TestProtocol {
346 
347         public static final String PATH = "/checkalive";
348 
349         public CheckAliveResponse(StreamRequestMessage inputMessage) {
350             super(inputMessage);
351         }
352 
353         @Override
354         protected StreamResponseMessage executeSync() {
355             // Return OK response after 2 seconds, check if client connection every 500ms
356             int i = 0;
357             while (i < 4) {
358                 try {
359                     log.info("Sleeping for 500ms before checking connection...");
360                     Thread.sleep(500);
361                 } catch (InterruptedException ex) {
362                     return null;
363                 }
364                 if (getRemoteClientInfo().isRequestCancelled()) {
365                     return null;
366                 }
367                 i++;
368             }
369             isComplete = true;
370             return new StreamResponseMessage(UpnpResponse.Status.OK);
371         }
372     }
373 
374     public class CheckAliveLongResponse extends TestProtocol {
375 
376         public static final String PATH = "/checkalivelong";
377 
378         public CheckAliveLongResponse(StreamRequestMessage inputMessage) {
379             super(inputMessage);
380         }
381 
382         @Override
383         protected StreamResponseMessage executeSync() {
384             // Return OK response after 5 seconds, check if client connection every 500ms
385             int i = 0;
386             while (i < 10) {
387                 try {
388                     log.info("Sleeping for 500ms before checking connection...");
389                     Thread.sleep(500);
390                 } catch (InterruptedException ex) {
391                     return null;
392                 }
393                 if (getRemoteClientInfo().isRequestCancelled()) {
394                     return null;
395                 }
396                 i++;
397             }
398             isComplete = true;
399             return new StreamResponseMessage(UpnpResponse.Status.OK);
400         }
401     }
402 
403 }