Java Source Code: org.apache.cxf.transport.http.HTTPConduitTest


   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: package org.apache.cxf.transport.http;
  21: 
  22: 
  23: import java.io.IOException;
  24: import java.io.InputStream;
  25: import java.io.OutputStream;
  26: import java.net.HttpURLConnection;
  27: import java.net.Proxy;
  28: import java.net.URL;
  29: import java.net.URLConnection;
  30: import java.util.ArrayList;
  31: import java.util.Collections;
  32: import java.util.HashMap;
  33: import java.util.List;
  34: import java.util.Map;
  35: 
  36: import junit.framework.TestCase;
  37: 
  38: import org.apache.cxf.helpers.CastUtils;
  39: import org.apache.cxf.message.Message;
  40: import org.apache.cxf.message.MessageImpl;
  41: import org.apache.cxf.service.model.EndpointInfo;
  42: import org.apache.cxf.transport.Conduit;
  43: import org.apache.cxf.transport.Destination;
  44: import org.apache.cxf.transport.MessageObserver;
  45: import org.apache.cxf.ws.addressing.EndpointReferenceType;
  46: import org.apache.cxf.wsdl.EndpointReferenceUtils;
  47: import org.easymock.classextension.EasyMock;
  48: import org.easymock.classextension.IMocksControl;
  49: import org.mortbay.http.HttpHandler;
  50: import org.mortbay.http.handler.AbstractHttpHandler;
  51: import org.mortbay.util.MultiMap;
  52: 
  53:	  public class HTTPConduitTest extends TestCase {
  54:    private static final String NOWHERE = "http://nada.nothing.nowhere.null/";
  55:    private static final String PAYLOAD = "message payload";
  56:    private EndpointReferenceType target;
  57:    private EndpointInfo endpointInfo;
  58:    private URLConnectionFactory connectionFactory;
  59:    private URLConnection connection;
  60:    private Proxy proxy;
  61:    private Message inMessage;
  62:    private MessageObserver observer;
  63:    private OutputStream os;
  64:    private InputStream is;
  65:    private TestServerEngine decoupledEngine;
  66:    private MultiMap parameters;
  67:    private IMocksControl control;
  68:    
  69:	      public void setUp() throws Exception {
  70:        control = EasyMock.createNiceControl();
  71:    }
  72:
  73:	      public void tearDown() {
  74:        // avoid intermittent spurious failures on EasyMock detecting finalize
  75:        // calls by mocking up only class data members (no local variables)
  76:        // and explicitly making available for GC post-verify
  77:        finalVerify();
  78:        connectionFactory = null;
  79:        connection = null;
  80:        proxy = null;
  81:        inMessage = null;
  82:        observer = null;
  83:        os = null;
  84:        is = null;
  85:        parameters = null;
  86:        decoupledEngine = null;
  87:    }
  88:
  89:	      public void testGetTarget() throws Exception {
  90:        HTTPConduit conduit = setUpConduit(false);
  91:        EndpointReferenceType ref = conduit.getTarget();
  92:        assertNotNull("unexpected null target", ref);
  93:        assertEquals("unexpected target",
  94:                     EndpointReferenceUtils.getAddress(ref),
  95:                     EndpointReferenceUtils.getAddress(target));
  96:        assertEquals("unexpected URL",
  97:                     conduit.getURL().getPath(),
  98:                     "/bar/foo");
  99:    }
 100:    
 101:	      public void testSend() throws Exception {
 102:        HTTPConduit conduit = setUpConduit(true, false, false);
 103:        Message message = new MessageImpl();
 104:        conduit.send(message);
 105:        verifySentMessage(conduit, message);
 106:    }
 107:    
 108:	      public void testSendWithHeaders() throws Exception {
 109:        HTTPConduit conduit = setUpConduit(true, false, false);
 110:        Message message = new MessageImpl();
 111:        setUpHeaders(message);
 112:        conduit.send(message);
 113:        verifySentMessage(conduit, message, true);
 114:    }
 115:    
 116:	      public void testSendHttpConnection() throws Exception {
 117:        HTTPConduit conduit = setUpConduit(true, true, false);
 118:        Message message = new MessageImpl();
 119:        conduit.send(message);
 120:        verifySentMessage(conduit, message);
 121:    }
 122:
 123:	      public void testSendHttpConnectionAutoRedirect() throws Exception {
 124:        HTTPConduit conduit = setUpConduit(true, true, true);
 125:        Message message = new MessageImpl();
 126:        conduit.send(message);
 127:        verifySentMessage(conduit, message);
 128:    }
 129:    
 130:	      public void testSendDecoupled() throws Exception {
 131:        HTTPConduit conduit = setUpConduit(true, false, false, true);
 132:        Message message = new MessageImpl();
 133:        conduit.send(message);
 134:        verifySentMessage(conduit, message, false, true);
 135:    }
 136:    
 137:	      private void setUpHeaders(Message message) {
 138:        Map<String, List<String>> headers = new HashMap<String, List<String>>();
 139:        List<String> contentTypes = new ArrayList<String>();
 140:        contentTypes.add("text/xml");
 141:        contentTypes.add("charset=utf8");
 142:        headers.put("content-type", contentTypes);
 143:        message.put(Message.PROTOCOL_HEADERS, headers);        
 144:        message.put(Message.USERNAME, "BJ");
 145:        message.put(Message.PASSWORD, "value");
 146:    }
 147:
 148:	      private HTTPConduit setUpConduit(boolean send) throws Exception {
 149:        return setUpConduit(send, false, false);
 150:    }
 151:    
 152:    private HTTPConduit setUpConduit(boolean send,
 153:                                     boolean httpConnection,
 154:	                                       boolean autoRedirect) throws Exception {
 155:        return setUpConduit(send, httpConnection, autoRedirect, false);
 156:    }
 157:    
 158:    private HTTPConduit setUpConduit(boolean send,
 159:                                     boolean httpConnection,
 160:                                     boolean autoRedirect,
 161:	                                       boolean decoupled) throws Exception {
 162:        endpointInfo = control.createMock(EndpointInfo.class);
 163:        target = getEPR("bar/foo");
 164:        connectionFactory = control.createMock(URLConnectionFactory.class);
 165:        endpointInfo.getAddress();
 166:        EasyMock.expectLastCall().andReturn(NOWHERE + "bar/foo").times(2);
 167:	          if (send) {
 168:            //proxy = control.createMock(Proxy.class);
 169:            proxy =  null;
 170:            connection =
 171:                control.createMock(httpConnection ? HttpURLConnection.class : URLConnection.class);
 172:            connectionFactory.createConnection(
 173:                                      EasyMock.eq(proxy), 
 174:                                      EasyMock.eq(new URL(NOWHERE + "bar/foo")));
 175:            EasyMock.expectLastCall().andReturn(connection);
 176:            connection.setDoOutput(true);
 177:            EasyMock.expectLastCall();
 178:            
 179:	              if (httpConnection) {
 180:                ((HttpURLConnection)connection).setRequestMethod("POST");                
 181:            }
 182:            
 183:            connection.setConnectTimeout(303030);
 184:            EasyMock.expectLastCall();
 185:            connection.setReadTimeout(404040);
 186:            EasyMock.expectLastCall();
 187:            connection.setUseCaches(false);
 188:            EasyMock.expectLastCall();
 189:            
 190:            
 191:
 192:	              if (httpConnection) {
 193:                ((HttpURLConnection)connection).setInstanceFollowRedirects(autoRedirect);
 194:                EasyMock.expectLastCall();                
 195:	                  if (!autoRedirect) {
 196:                    ((HttpURLConnection)connection).getRequestMethod();
 197:                    EasyMock.expectLastCall().andReturn("POST");
 198:                    ((HttpURLConnection)connection).setChunkedStreamingMode(2048);
 199:                    EasyMock.expectLastCall();                    
 200:                }
 201:            }
 202:            
 203:	              if (decoupled) {
 204:                decoupledEngine = new TestServerEngine();
 205:                parameters = control.createMock(MultiMap.class);
 206:            }            
 207:            
 208:        }
 209:               
 210:        
 211:        control.replay();
 212:        
 213:        HTTPConduit conduit = new HTTPConduit(null, 
 214:                                              endpointInfo,
 215:                                              null,
 216:                                              connectionFactory,
 217:                                              decoupledEngine);
 218:        conduit.retrieveConnectionFactory();
 219:
 220:	          if (send) {
 221:            conduit.getClient().setConnectionTimeout(303030);
 222:            conduit.getClient().setReceiveTimeout(404040);
 223:	              if (httpConnection) {
 224:                conduit.getClient().setAutoRedirect(autoRedirect);
 225:	                  if (!autoRedirect) {
 226:                    conduit.getClient().setAllowChunking(true);
 227:                } 
 228:            }
 229:        }
 230:
 231:	          if (decoupled) {
 232:            URL decoupledURL = null;
 233:	              if (decoupled) {
 234:                decoupledURL = new URL(NOWHERE + "response");
 235:                conduit.getClient().setDecoupledEndpoint(decoupledURL.toString());
 236:            } 
 237:        }
 238:       
 239:
 240:	          observer = new MessageObserver() {
 241:	              public void onMessage(Message m) {
 242:                inMessage = m;
 243:            }
 244:        };
 245:        conduit.setMessageObserver(observer);
 246:        return conduit;
 247:    }
 248:    
 249:    private void verifySentMessage(Conduit conduit, Message message)
 250:	          throws IOException {
 251:        verifySentMessage(conduit, message, false);
 252:    }
 253:
 254:    private void verifySentMessage(Conduit conduit,
 255:                                   Message message,
 256:                                   boolean expectHeaders)
 257:	          throws IOException {
 258:        verifySentMessage(conduit, message, expectHeaders, false);
 259:    }
 260:    
 261:    private void verifySentMessage(Conduit conduit,
 262:                                   Message message,
 263:                                   boolean expectHeaders,
 264:                                   boolean decoupled)
 265:	          throws IOException {
 266:        control.verify();
 267:        control.reset();
 268:                
 269:        OutputStream wrappedOS = verifyRequestHeaders(message, expectHeaders);
 270:        
 271:	          if (connection instanceof HttpURLConnection) {
 272:            ((HttpURLConnection)connection).getRequestMethod();
 273:            EasyMock.expectLastCall().andReturn("POST");
 274:        }
 275:        
 276:        
 277:        os = EasyMock.createMock(OutputStream.class);
 278:        connection.getOutputStream();
 279:        EasyMock.expectLastCall().andReturn(os);
 280:        os.write(PAYLOAD.getBytes(), 0, PAYLOAD.length());
 281:        EasyMock.expectLastCall();
 282:
 283:        URL decoupledURL = null;
 284:	          if (decoupled) {
 285:            decoupledURL = new URL(NOWHERE + "response");
 286:        } 
 287:        
 288:        os.flush();
 289:        EasyMock.expectLastCall();
 290:        os.flush();
 291:        EasyMock.expectLastCall();
 292:        os.close();
 293:        EasyMock.expectLastCall();
 294:        
 295:        verifyHandleResponse(decoupled);
 296:        
 297:        control.replay();
 298:        
 299:        Destination backChannel = null;
 300:        AbstractHttpHandler decoupledHandler = null;
 301:	          if (decoupled) {
 302:            decoupledEngine.verifyCallCounts(new int[]{0, 0, 0});
 303:            backChannel = conduit.getBackChannel();
 304:            assertNotNull("expected back channel", backChannel);
 305:            decoupledEngine.verifyCallCounts(new int[]{1, 0, 1});
 306:            decoupledHandler = decoupledEngine.servants.get(decoupledURL);
 307:            assertNotNull("expected servant registered", decoupledHandler);
 308:            MessageObserver decoupledObserver =
 309:                ((HTTPConduit.DecoupledDestination)backChannel).getMessageObserver();
 310:            assertSame("unexpected decoupled destination",
 311:                       observer,       
 312:                       decoupledObserver);
 313:        } else {
 314:            backChannel = conduit.getBackChannel();
 315:            assertNull("unexpected back channel", backChannel);
 316:        }
 317:        
 318:        wrappedOS.flush();
 319:        wrappedOS.flush();
 320:        wrappedOS.close();
 321:        
 322:        assertNotNull("expected in message", inMessage);
 323:        Map<?, ?> headerMap = (Map<?, ?>) inMessage.get(Message.PROTOCOL_HEADERS);
 324:        assertEquals("unexpected response headers", headerMap.size(), 0);
 325:        Integer expectedResponseCode = decoupled 
 326:                                       ? HttpURLConnection.HTTP_ACCEPTED
 327:                                       : HttpURLConnection.HTTP_OK;
 328:        assertEquals("unexpected response code",
 329:                     expectedResponseCode,
 330:                     inMessage.get(Message.RESPONSE_CODE));
 331:        assertTrue("unexpected content formats",
 332:                   inMessage.getContentFormats().contains(InputStream.class));
 333:        assertSame("unexpected content", is, inMessage.getContent(InputStream.class));
 334:        
 335:	          if (decoupled) {
 336:            verifyDecoupledResponse(decoupledHandler);
 337:        }
 338:        
 339:        conduit.close();
 340:	          if (decoupled) {
 341:            decoupledEngine.verifyCallCounts(new int[]{1, 1, 2});
 342:        }
 343:        
 344:        finalVerify();
 345:    }
 346:
 347:    private OutputStream verifyRequestHeaders(Message message, boolean expectHeaders)
 348:	          throws IOException {
 349:        Map<String, List<String>> headers =
 350:            CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
 351:        assertNotNull("expected request headers set", headers);
 352:        assertTrue("expected output stream format",
 353:                   message.getContentFormats().contains(OutputStream.class));
 354:        OutputStream wrappedOS = message.getContent(OutputStream.class);
 355:        assertNotNull("expected output stream", wrappedOS);
 356:        
 357:        wrappedOS.write(PAYLOAD.getBytes());
 358:        
 359:        message.put(HTTPConduit.HTTP_CONNECTION, connection);
 360:	          if (expectHeaders) {
 361:            connection.addRequestProperty(EasyMock.eq("Authorization"),
 362:                                          EasyMock.eq("Basic Qko6dmFsdWU="));            
 363:            EasyMock.expectLastCall();
 364:            connection.addRequestProperty(EasyMock.eq("content-type"),
 365:                                          EasyMock.eq("text/xml"));
 366:            EasyMock.expectLastCall();
 367:            connection.addRequestProperty(EasyMock.eq("content-type"),
 368:                                          EasyMock.eq("charset=utf8"));
 369:            EasyMock.expectLastCall();
 370:        }
 371:        return wrappedOS;
 372:    }
 373:    
 374:	      private void verifyHandleResponse(boolean decoupled) throws IOException {
 375:        connection.getHeaderFields();
 376:        EasyMock.expectLastCall().andReturn(Collections.EMPTY_MAP);
 377:        int responseCode = decoupled 
 378:                           ? HttpURLConnection.HTTP_ACCEPTED
 379:                           : HttpURLConnection.HTTP_OK;
 380:	          if (connection instanceof HttpURLConnection) {
 381:            ((HttpURLConnection)connection).getResponseCode();
 382:            EasyMock.expectLastCall().andReturn(responseCode);
 383:            ((HttpURLConnection)connection).getErrorStream();
 384:            EasyMock.expectLastCall().andReturn(null);
 385:        } else {
 386:            connection.getHeaderField(Message.RESPONSE_CODE);
 387:            String responseString = Integer.toString(responseCode);
 388:            EasyMock.expectLastCall().andReturn(responseString).times(2);
 389:        }
 390:        is = EasyMock.createMock(InputStream.class);
 391:        connection.getInputStream();
 392:        EasyMock.expectLastCall().andReturn(is);
 393:    }
 394:    
 395:    private void verifyDecoupledResponse(AbstractHttpHandler decoupledHandler)
 396:	          throws IOException {
 397:        inMessage = null;
 398:        is = EasyMock.createMock(InputStream.class);
 399:        os = EasyMock.createMock(OutputStream.class);
 400:        TestHttpRequest decoupledRequest = new TestHttpRequest(is, parameters);
 401:        TestHttpResponse decoupledResponse = new TestHttpResponse(os);
 402:        decoupledHandler.handle("pathInContext",
 403:                                "pathParams",
 404:                                decoupledRequest,
 405:                                decoupledResponse);
 406:        assertNotNull("expected decoupled in message", inMessage);
 407:        assertNotNull("expected response headers",
 408:                      inMessage.get(Message.PROTOCOL_HEADERS));
 409:        assertEquals("unexpected response code",
 410:                     HttpURLConnection.HTTP_OK,
 411:                     inMessage.get(Message.RESPONSE_CODE));
 412:
 413:        assertEquals("unexpected getInputStream count",
 414:                     1,
 415:                     decoupledRequest.getInputStreamCallCount());
 416:        //assertEquals("unexpected getParameters counts",
 417:        //             1,
 418:        //             decoupledRequest.getParametersCallCount());
 419:        assertTrue("unexpected content formats",
 420:                   inMessage.getContentFormats().contains(InputStream.class));
 421:        InputStream decoupledIS = inMessage.getContent(InputStream.class);
 422:        assertNotNull("unexpected content", decoupledIS);
 423:        
 424:        decoupledIS.close();
 425:        assertEquals("unexpected setHandled count",
 426:                     1,
 427:                     decoupledRequest.getHandledCallCount());
 428:        assertEquals("unexpected setHandled count",
 429:                     1,
 430:                     decoupledResponse.getCommitCallCount());
 431:        
 432:        inMessage.setContent(InputStream.class, is);
 433:
 434:    }
 435:
 436:	      private void finalVerify() {
 437:	          if (control != null) {
 438:            control.verify();
 439:            control = null;
 440:        }
 441:    }
 442:    
 443:	      static EndpointReferenceType getEPR(String s) {
 444:        return EndpointReferenceUtils.getEndpointReference(NOWHERE + s);
 445:    }
 446:    
 447:    /**
 448:     * EasyMock does not seem able to properly mock calls to ServerEngine -
 449:     * expectations set seem to be ignored.
 450:     */
 451:	      private class TestServerEngine implements ServerEngine {
 452:        private int callCounts[] = {0, 0, 0};
 453:        private Map<URL, AbstractHttpHandler> servants =
 454:            new HashMap<URL, AbstractHttpHandler>();
 455:        
 456:	          public void addServant(URL url, AbstractHttpHandler handler) {
 457:            callCounts[0]++;
 458:            servants.put(url, handler);
 459:        }
 460:
 461:	          public void removeServant(URL url) {
 462:            callCounts[1]++;
 463:            servants.remove(url);
 464:        }
 465:
 466:	          public HttpHandler getServant(URL url) {
 467:            callCounts[2]++;
 468:            return servants.get(url);
 469:        }
 470:
 471:	          void verifyCallCounts(int expectedCallCounts[]) {
 472:            assertEquals("unexpected addServant call count",
 473:                         expectedCallCounts[0],
 474:                         callCounts[0]);
 475:            assertEquals("unexpected removeServant call count",
 476:                         expectedCallCounts[1],
 477:                         callCounts[1]);
 478:            assertEquals("unexpected getServant call count",
 479:                         expectedCallCounts[2],
 480:                         callCounts[2]);
 481:        }
 482:    }
 483:}