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.control;
17  
18  import org.fourthline.cling.UpnpService;
19  import org.fourthline.cling.mock.MockUpnpService;
20  import org.fourthline.cling.model.action.ActionException;
21  import org.fourthline.cling.model.action.ActionInvocation;
22  import org.fourthline.cling.model.message.Connection;
23  import org.fourthline.cling.model.message.StreamRequestMessage;
24  import org.fourthline.cling.model.message.StreamResponseMessage;
25  import org.fourthline.cling.model.message.UpnpMessage;
26  import org.fourthline.cling.model.message.UpnpRequest;
27  import org.fourthline.cling.model.message.UpnpResponse;
28  import org.fourthline.cling.model.message.control.IncomingActionResponseMessage;
29  import org.fourthline.cling.model.message.header.ContentTypeHeader;
30  import org.fourthline.cling.model.message.header.EXTHeader;
31  import org.fourthline.cling.model.message.header.ServerHeader;
32  import org.fourthline.cling.model.message.header.SoapActionHeader;
33  import org.fourthline.cling.model.message.header.UpnpHeader;
34  import org.fourthline.cling.model.message.header.UserAgentHeader;
35  import org.fourthline.cling.model.meta.Action;
36  import org.fourthline.cling.model.meta.LocalDevice;
37  import org.fourthline.cling.model.meta.LocalService;
38  import org.fourthline.cling.model.meta.QueryStateVariableAction;
39  import org.fourthline.cling.model.meta.Service;
40  import org.fourthline.cling.model.types.ErrorCode;
41  import org.fourthline.cling.model.types.SoapActionType;
42  import org.fourthline.cling.protocol.sync.ReceivingAction;
43  import org.seamless.util.MimeType;
44  import org.testng.annotations.Test;
45  
46  import java.net.InetAddress;
47  import java.net.URI;
48  import java.net.UnknownHostException;
49  
50  import static org.testng.Assert.*;
51  
52  /**
53   * @author Christian Bauer
54   */
55  public class ActionInvokeIncomingTest {
56  
57      public static final String SET_REQUEST = "<?xml version=\"1.0\"?>\n" +
58              " <s:Envelope\n" +
59              "     xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" +
60              "     s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
61              "   <s:Body>\n" +
62              "     <u:SetTarget xmlns:u=\"urn:schemas-upnp-org:service:SwitchPower:1\">\n" +
63              "       <NewTargetValue>1</NewTargetValue>\n" +
64              "     </u:SetTarget>\n" +
65              "   </s:Body>\n" +
66              " </s:Envelope>";
67  
68      public static final String GET_REQUEST = "<?xml version=\"1.0\"?>\n" +
69              " <s:Envelope\n" +
70              "     xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" +
71              "     s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
72              "   <s:Body>\n" +
73              "     <u:GetTarget xmlns:u=\"urn:schemas-upnp-org:service:SwitchPower:1\"/>\n" +
74              "   </s:Body>\n" +
75              " </s:Envelope>";
76  
77      public static final String QUERY_STATE_VARIABLE_REQUEST = "<?xml version=\"1.0\"?>\n" +
78              " <s:Envelope\n" +
79              "     xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" +
80              "     s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
81              "   <s:Body>\n" +
82              "     <u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\n" +
83              "       <varName>Status</varName>\n" +
84              "     </u:QueryStateVariable>\n" +
85              "   </s:Body>\n" +
86              " </s:Envelope>";
87  
88      @Test
89      public void incomingRemoteCallGet() throws Exception {
90          incomingRemoteCallGet(ActionSampleData.createTestDevice());
91      }
92  
93      @Test
94      public void incomingRemoteCallClientInfo() throws Exception {
95          UpnpMessage response =
96                  incomingRemoteCallGet(ActionSampleData.createTestDevice(ActionSampleData.LocalTestServiceWithClientInfo.class));
97  
98          assertEquals(response.getHeaders().size(), 4);
99          assertEquals(response.getHeaders().getFirstHeader("X-MY-HEADER"), "foobar");
100     }
101 
102     public IncomingActionResponseMessage incomingRemoteCallGet(LocalDevice ld) throws Exception {
103 
104         MockUpnpService upnpService = new MockUpnpService();
105         LocalService service = ld.getServices()[0];
106         upnpService.getRegistry().addDevice(ld);
107 
108         Action action = service.getAction("GetTarget");
109 
110         URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
111         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
112         request.setConnection(new Connection() {
113             @Override
114             public boolean isOpen() {
115                 return true;
116             }
117 
118             @Override
119             public InetAddress getRemoteAddress() {
120                 try {
121                     return InetAddress.getByName("10.0.0.1");
122                 } catch (UnknownHostException ex) {
123                     throw new RuntimeException(ex);
124                 }
125             }
126 
127             @Override
128             public InetAddress getLocalAddress() {
129                 try {
130                     return InetAddress.getByName("10.0.0.2");
131                 } catch (UnknownHostException ex) {
132                     throw new RuntimeException(ex);
133                 }
134             }
135         });
136         addMandatoryRequestHeaders(service, action, request);
137         request.setBody(UpnpMessage.BodyType.STRING, GET_REQUEST);
138 
139         ReceivingAction prot = new ReceivingAction(upnpService, request);
140 
141         prot.run();
142 
143         StreamResponseMessage response = prot.getOutputMessage();
144 
145         assertNotNull(response);
146         assertFalse(response.getOperation().isFailed());
147         assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
148         assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
149         assertEquals(
150             response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
151             new ServerHeader().getValue()
152         );
153 
154         IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
155         ActionInvocation responseInvocation = new ActionInvocation(action);
156         upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
157 
158         assertNotNull(responseInvocation.getOutput("RetTargetValue"));
159         return responseMessage;
160     }
161 
162     @Test
163     public void incomingRemoteCallGetConcurrent() throws Exception {
164 
165         // Register local device and its service
166         MockUpnpService upnpService = new MockUpnpService(false, false, true);
167         LocalDevice ld = ActionSampleData.createTestDevice(ActionSampleData.LocalTestServiceThrowsException.class);
168         LocalService service = ld.getServices()[0];
169         upnpService.getRegistry().addDevice(ld);
170 
171         // TODO: Use a latch instead of waiting
172         int i = 0;
173         while (i < 10) {
174             new Thread(new ConcurrentGetTest(upnpService, service)).start();
175             i++;
176         }
177 
178         // Wait for the threads to finish
179         Thread.sleep(2000);
180     }
181 
182     static class ConcurrentGetTest implements Runnable {
183         private UpnpService upnpService;
184         private LocalService service;
185 
186         ConcurrentGetTest(UpnpService upnpService, LocalService service) {
187             this.upnpService = upnpService;
188             this.service = service;
189         }
190 
191         public void run() {
192             Action action = service.getAction("GetTarget");
193 
194             URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
195             StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
196             request.getHeaders().add(
197                     UpnpHeader.Type.CONTENT_TYPE,
198                     new ContentTypeHeader(ContentTypeHeader.DEFAULT_CONTENT_TYPE_UTF8)
199             );
200 
201             SoapActionType actionType = new SoapActionType(service.getServiceType(), action.getName());
202             request.getHeaders().add(UpnpHeader.Type.SOAPACTION, new SoapActionHeader(actionType));
203             request.setBody(UpnpMessage.BodyType.STRING, GET_REQUEST);
204 
205             ReceivingAction prot = new ReceivingAction(upnpService, request);
206 
207             prot.run();
208 
209             StreamResponseMessage response = prot.getOutputMessage();
210 
211             assertNotNull(response);
212             assertFalse(response.getOperation().isFailed());
213             assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
214             assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
215             assertEquals(
216                 response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
217                 new ServerHeader().getValue()
218             );
219 
220             IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
221             ActionInvocation responseInvocation = new ActionInvocation(action);
222             upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
223 
224             assertNotNull(responseInvocation.getOutput("RetTargetValue"));
225         }
226     }
227 
228 
229     @Test
230     public void incomingRemoteCallSet() throws Exception {
231 
232         // Register local device and its service
233         MockUpnpService upnpService = new MockUpnpService();
234         LocalDevice ld = ActionSampleData.createTestDevice();
235         LocalService service = ld.getServices()[0];
236         upnpService.getRegistry().addDevice(ld);
237 
238         Action action = service.getAction("SetTarget");
239 
240         URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
241         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
242         addMandatoryRequestHeaders(service, action, request);
243         request.setBody(UpnpMessage.BodyType.STRING, SET_REQUEST);
244 
245         ReceivingAction prot = new ReceivingAction(upnpService, request);
246 
247         prot.run();
248 
249         StreamResponseMessage response = prot.getOutputMessage();
250 
251         assertNotNull(response);
252         assertFalse(response.getOperation().isFailed());
253         assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
254         assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
255         assertEquals(
256             response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
257             new ServerHeader().getValue()
258         );
259 
260         IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
261         ActionInvocation responseInvocation = new ActionInvocation(action);
262         upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
263 
264         assertEquals(responseInvocation.getOutput().length, 0);
265 
266     }
267 
268     @Test
269     public void incomingRemoteCallControlURINotFound() throws Exception {
270 
271         // Register local device and its service
272         MockUpnpService upnpService = new MockUpnpService();
273         LocalDevice ld = ActionSampleData.createTestDevice();
274         LocalService service = ld.getServices()[0];
275         upnpService.getRegistry().addDevice(ld);
276 
277         Action action = service.getAction("SetTarget");
278 
279         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, URI.create("/some/random/123/uri"));
280         addMandatoryRequestHeaders(service, action, request);
281         request.setBody(UpnpMessage.BodyType.STRING, SET_REQUEST);
282 
283         ReceivingAction prot = new ReceivingAction(upnpService, request);
284 
285         prot.run();
286 
287         UpnpMessage response = prot.getOutputMessage();
288 
289         assertNull(response);
290         // The StreamServer will send a 404 response
291     }
292 
293     @Test
294     public void incomingRemoteCallMethodException() throws Exception {
295 
296         // Register local device and its service
297         MockUpnpService upnpService = new MockUpnpService();
298         LocalDevice ld = ActionSampleData.createTestDevice(ActionSampleData.LocalTestServiceThrowsException.class);
299         LocalService service = ld.getServices()[0];
300         upnpService.getRegistry().addDevice(ld);
301 
302         Action action = service.getAction("SetTarget");
303 
304         URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
305         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
306         addMandatoryRequestHeaders(service, action, request);
307 
308         request.setBody(UpnpMessage.BodyType.STRING, SET_REQUEST);
309 
310         ReceivingAction prot = new ReceivingAction(upnpService, request);
311 
312         prot.run();
313 
314         StreamResponseMessage response = prot.getOutputMessage();
315 
316         assertNotNull(response);
317         assertTrue(response.getOperation().isFailed());
318         assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
319         assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
320         assertEquals(
321             response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
322             new ServerHeader().getValue()
323         );
324 
325         IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
326         ActionInvocation responseInvocation = new ActionInvocation(action);
327         upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
328 
329         ActionException ex = responseInvocation.getFailure();
330         assertNotNull(ex);
331 
332         assertEquals(ex.getMessage(), ErrorCode.ACTION_FAILED.getDescription() + ". Something is wrong.");
333 
334     }
335 
336     @Test
337     public void incomingRemoteCallNoContentType() throws Exception {
338 
339         // Register local device and its service
340         MockUpnpService upnpService = new MockUpnpService();
341         LocalDevice ld = ActionSampleData.createTestDevice();
342         LocalService service = ld.getServices()[0];
343         upnpService.getRegistry().addDevice(ld);
344 
345         Action action = service.getAction("GetTarget");
346 
347         URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
348         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
349         SoapActionType actionType = new SoapActionType(service.getServiceType(), action.getName());
350         request.getHeaders().add(UpnpHeader.Type.SOAPACTION, new SoapActionHeader(actionType));
351         // NO CONTENT TYPE!
352         request.setBody(UpnpMessage.BodyType.STRING, GET_REQUEST);
353 
354         ReceivingAction prot = new ReceivingAction(upnpService, request);
355 
356         prot.run();
357 
358         StreamResponseMessage response = prot.getOutputMessage();
359 
360         assertNotNull(response);
361         assertFalse(response.getOperation().isFailed());
362         assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
363         assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
364         assertEquals(
365             response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
366             new ServerHeader().getValue()
367         );
368 
369         IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
370         ActionInvocation responseInvocation = new ActionInvocation(action);
371         upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
372 
373         assertNotNull(responseInvocation.getOutput("RetTargetValue"));
374 
375     }
376 
377     @Test
378     public void incomingRemoteCallWrongContentType() throws Exception {
379 
380         MockUpnpService upnpService = new MockUpnpService();
381 
382         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, URI.create("/some/random/123/uri"));
383         request.getHeaders().add(
384                 UpnpHeader.Type.CONTENT_TYPE,
385                 new ContentTypeHeader(MimeType.valueOf("some/randomtype"))
386         );
387         request.setBody(UpnpMessage.BodyType.STRING, SET_REQUEST);
388 
389         ReceivingAction prot = new ReceivingAction(upnpService, request);
390 
391         prot.run();
392 
393         StreamResponseMessage response = prot.getOutputMessage();
394 
395         assertNotNull(response);
396         assertEquals(response.getOperation().getStatusCode(), UpnpResponse.Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode());
397     }
398 
399     @Test
400     public void incomingRemoteCallQueryStateVariable() throws Exception {
401 
402         // Register local device and its service
403         MockUpnpService upnpService = new MockUpnpService();
404         LocalDevice ld = ActionSampleData.createTestDevice();
405         LocalService service = ld.getServices()[0];
406         upnpService.getRegistry().addDevice(ld);
407 
408         Action action = service.getAction(QueryStateVariableAction.ACTION_NAME);
409 
410         URI controlURI = upnpService.getConfiguration().getNamespace().getControlPath(service);
411         StreamRequestMessage request = new StreamRequestMessage(UpnpRequest.Method.POST, controlURI);
412         request.getHeaders().add(
413                 UpnpHeader.Type.CONTENT_TYPE,
414                 new ContentTypeHeader(ContentTypeHeader.DEFAULT_CONTENT_TYPE_UTF8)
415         );
416         request.getHeaders().add(
417                 UpnpHeader.Type.SOAPACTION,
418                 new SoapActionHeader(
419                         new SoapActionType(
420                                 SoapActionType.MAGIC_CONTROL_NS, SoapActionType.MAGIC_CONTROL_TYPE, null, action.getName()
421                         )
422                 )
423 
424         );
425         request.setBody(UpnpMessage.BodyType.STRING, QUERY_STATE_VARIABLE_REQUEST);
426 
427         ReceivingAction prot = new ReceivingAction(upnpService, request);
428 
429         prot.run();
430 
431         StreamResponseMessage response = prot.getOutputMessage();
432 
433         assertNotNull(response);
434         assertFalse(response.getOperation().isFailed());
435         assertTrue(response.getHeaders().getFirstHeader(UpnpHeader.Type.CONTENT_TYPE, ContentTypeHeader.class).isUDACompliantXML());
436         assertNotNull(response.getHeaders().getFirstHeader(UpnpHeader.Type.EXT, EXTHeader.class));
437         assertEquals(
438             response.getHeaders().getFirstHeader(UpnpHeader.Type.SERVER, ServerHeader.class).getValue(),
439             new ServerHeader().getValue()
440         );
441 
442         IncomingActionResponseMessage responseMessage = new IncomingActionResponseMessage(response);
443         ActionInvocation responseInvocation = new ActionInvocation(action);
444         upnpService.getConfiguration().getSoapActionProcessor().readBody(responseMessage, responseInvocation);
445 
446         assertEquals(responseInvocation.getOutput()[0].getArgument().getName(), "return");
447         assertEquals(responseInvocation.getOutput()[0].toString(), "0");
448     }
449 
450 
451     protected void addMandatoryRequestHeaders(Service service, Action action, StreamRequestMessage request) {
452         request.getHeaders().add(
453                 UpnpHeader.Type.CONTENT_TYPE,
454                 new ContentTypeHeader(ContentTypeHeader.DEFAULT_CONTENT_TYPE_UTF8)
455         );
456 
457         SoapActionType actionType = new SoapActionType(service.getServiceType(), action.getName());
458         request.getHeaders().add(UpnpHeader.Type.SOAPACTION, new SoapActionHeader(actionType));
459         // Not mandatory but only for the tests
460         request.getHeaders().add(UpnpHeader.Type.USER_AGENT, new UserAgentHeader("foo/bar"));
461     }
462 
463 }