001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.transport.http;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.OutputStream;
023: import java.io.PushbackInputStream;
024: import java.net.HttpURLConnection;
025: import java.net.Proxy;
026: import java.net.URL;
027: import java.util.ArrayList;
028: import java.util.Collections;
029: import java.util.HashMap;
030: import java.util.List;
031: import java.util.Map;
032:
033: import org.apache.cxf.Bus;
034: import org.apache.cxf.bus.CXFBusImpl;
035: import org.apache.cxf.configuration.security.AuthorizationPolicy;
036: import org.apache.cxf.helpers.CastUtils;
037: import org.apache.cxf.message.Exchange;
038: import org.apache.cxf.message.Message;
039: import org.apache.cxf.message.MessageImpl;
040: import org.apache.cxf.service.model.EndpointInfo;
041: import org.apache.cxf.transport.Destination;
042: import org.apache.cxf.transport.DestinationFactory;
043: import org.apache.cxf.transport.DestinationFactoryManager;
044: import org.apache.cxf.transport.MessageObserver;
045: import org.apache.cxf.ws.addressing.EndpointReferenceType;
046: import org.easymock.classextension.EasyMock;
047: import org.easymock.classextension.IMocksControl;
048: import org.junit.After;
049: import org.junit.Assert;
050: import org.junit.Before;
051: import org.junit.Test;
052:
053: import static org.apache.cxf.message.Message.DECOUPLED_CHANNEL_MESSAGE;
054:
055: /**
056: */
057: public class HTTPConduitURLEasyMockTest extends Assert {
058:
059: private enum ResponseStyle {
060: NONE, BACK_CHANNEL, DECOUPLED
061: };
062:
063: private enum ResponseDelimiter {
064: LENGTH, CHUNKED, EOF
065: };
066:
067: private static final String NOWHERE = "http://nada.nothing.nowhere.null/";
068: private static final String PAYLOAD = "message payload";
069: private IMocksControl control;
070: private EndpointInfo endpointInfo;
071: private HttpURLConnectionFactory connectionFactory;
072: private HttpURLConnection connection;
073: private Proxy proxy;
074: private Message inMessage;
075: private MessageObserver observer;
076: private OutputStream os;
077: private InputStream is;
078:
079: /**
080: * This is an extension to the HTTPConduit that replaces
081: * the dynamic assignment of the HttpURLConnectionFactory,
082: * and we just use the EasyMocked version for this test.
083: */
084: private class HTTPTestConduit extends HTTPConduit {
085: HTTPTestConduit(Bus associatedBus, EndpointInfo endpoint,
086: EndpointReferenceType epr,
087: HttpURLConnectionFactory testFactory)
088: throws IOException {
089: super (associatedBus, endpoint, epr);
090: connectionFactory = testFactory;
091: }
092:
093: @Override
094: protected void retrieveConnectionFactory() {
095: // do nothing. i.e do not change the connectionFactory field.
096: }
097: }
098:
099: /**
100: * @throws java.lang.Exception
101: */
102: @Before
103: public void setUp() throws Exception {
104: }
105:
106: /**
107: * @throws java.lang.Exception
108: */
109: @After
110: public void tearDown() throws Exception {
111: // avoid intermittent spurious failures on EasyMock detecting finalize
112: // calls by mocking up only class data members (no local variables)
113: // and explicitly making available for GC post-verify
114: connectionFactory = null;
115: connection = null;
116: proxy = null;
117: inMessage = null;
118: observer = null;
119: os = null;
120: is = null;
121: }
122:
123: @Test
124: public void testSend() throws Exception {
125: control = EasyMock.createNiceControl();
126: HTTPConduit conduit = setUpConduit(true, false, false);
127: Message message = new MessageImpl();
128: conduit.prepare(message);
129: verifySentMessage(conduit, message);
130: finalVerify();
131: }
132:
133: @Test
134: public void testSendWithHeaders() throws Exception {
135: control = EasyMock.createNiceControl();
136: HTTPConduit conduit = setUpConduit(true, false, false);
137: Message message = new MessageImpl();
138: setUpHeaders(message);
139: conduit.prepare(message);
140: verifySentMessage(conduit, message, true);
141: finalVerify();
142: }
143:
144: @Test
145: public void testSendHttpConnection() throws Exception {
146: control = EasyMock.createNiceControl();
147: HTTPConduit conduit = setUpConduit(true, false, false);
148: Message message = new MessageImpl();
149: conduit.prepare(message);
150: verifySentMessage(conduit, message);
151: finalVerify();
152: }
153:
154: @Test
155: public void testSendHttpConnectionAutoRedirect() throws Exception {
156: control = EasyMock.createNiceControl();
157: HTTPConduit conduit = setUpConduit(true, true, false);
158: Message message = new MessageImpl();
159: conduit.prepare(message);
160: verifySentMessage(conduit, message);
161: finalVerify();
162: }
163:
164: @Test
165: public void testSendOnewayExplicitLenghtPartialResponse()
166: throws Exception {
167: control = EasyMock.createNiceControl();
168: HTTPConduit conduit = setUpConduit(true, false, true);
169: Message message = new MessageImpl();
170: conduit.prepare(message);
171: verifySentMessage(conduit, message, ResponseStyle.NONE,
172: ResponseDelimiter.LENGTH, false); // non-empty response
173: finalVerify();
174: }
175:
176: @Test
177: public void testSendOnewayChunkedPartialResponse() throws Exception {
178: control = EasyMock.createNiceControl();
179: HTTPConduit conduit = setUpConduit(true, false, true);
180: Message message = new MessageImpl();
181: conduit.prepare(message);
182: verifySentMessage(conduit, message, ResponseStyle.NONE,
183: ResponseDelimiter.CHUNKED, false); // non-empty response
184: finalVerify();
185: }
186:
187: @Test
188: public void testSendOnewayChunkedEmptyPartialResponse()
189: throws Exception {
190: control = EasyMock.createNiceControl();
191: HTTPConduit conduit = setUpConduit(true, false, true);
192: Message message = new MessageImpl();
193: conduit.prepare(message);
194: verifySentMessage(conduit, message, ResponseStyle.NONE,
195: ResponseDelimiter.CHUNKED, true); // empty response
196: finalVerify();
197: }
198:
199: @Test
200: public void testSendOnewayEOFTerminatedPartialResponse()
201: throws Exception {
202: control = EasyMock.createNiceControl();
203: HTTPConduit conduit = setUpConduit(true, false, true);
204: Message message = new MessageImpl();
205: conduit.prepare(message);
206: verifySentMessage(conduit, message, ResponseStyle.NONE,
207: ResponseDelimiter.EOF, false); // non-empty response
208: finalVerify();
209: }
210:
211: @Test
212: public void testSendOnewayEOFTerminatedEmptyPartialResponse()
213: throws Exception {
214: control = EasyMock.createNiceControl();
215: HTTPConduit conduit = setUpConduit(true, false, true);
216: Message message = new MessageImpl();
217: conduit.prepare(message);
218: verifySentMessage(conduit, message, ResponseStyle.NONE,
219: ResponseDelimiter.EOF, true); // empty response
220: finalVerify();
221: }
222:
223: @Test
224: public void testSendDecoupledExplicitLenghtPartialResponse()
225: throws Exception {
226: control = EasyMock.createNiceControl();
227: HTTPConduit conduit = setUpConduit(true, false, true);
228: Message message = new MessageImpl();
229: conduit.prepare(message);
230: verifySentMessage(conduit, message, ResponseStyle.DECOUPLED,
231: ResponseDelimiter.LENGTH, false); // non-empty response
232: finalVerify();
233: }
234:
235: @Test
236: public void testSendDecoupledChunkedPartialResponse()
237: throws Exception {
238: control = EasyMock.createNiceControl();
239: HTTPConduit conduit = setUpConduit(true, false, true);
240: Message message = new MessageImpl();
241: conduit.prepare(message);
242: verifySentMessage(conduit, message, ResponseStyle.DECOUPLED,
243: ResponseDelimiter.CHUNKED, false); // non-empty response
244: finalVerify();
245: }
246:
247: @Test
248: public void testSendDecoupledChunkedEmptyPartialResponse()
249: throws Exception {
250: control = EasyMock.createNiceControl();
251: HTTPConduit conduit = setUpConduit(true, false, true);
252: Message message = new MessageImpl();
253: conduit.prepare(message);
254: verifySentMessage(conduit, message, ResponseStyle.DECOUPLED,
255: ResponseDelimiter.CHUNKED, true); // empty response
256: finalVerify();
257: }
258:
259: @Test
260: public void testSendDecoupledEOFTerminatedPartialResponse()
261: throws Exception {
262: control = EasyMock.createNiceControl();
263: HTTPConduit conduit = setUpConduit(true, false, true);
264: Message message = new MessageImpl();
265: conduit.prepare(message);
266: verifySentMessage(conduit, message, ResponseStyle.DECOUPLED,
267: ResponseDelimiter.EOF, false); // non-empty response
268: finalVerify();
269: }
270:
271: @Test
272: public void testSendDecoupledEOFTerminatedEmptyPartialResponse()
273: throws Exception {
274: control = EasyMock.createNiceControl();
275: HTTPConduit conduit = setUpConduit(true, false, true);
276: Message message = new MessageImpl();
277: conduit.prepare(message);
278: verifySentMessage(conduit, message, ResponseStyle.DECOUPLED,
279: ResponseDelimiter.EOF, true); // empty response
280: finalVerify();
281: }
282:
283: private void setUpHeaders(Message message) {
284: Map<String, List<String>> headers = new HashMap<String, List<String>>();
285: List<String> contentTypes = new ArrayList<String>();
286: contentTypes.add("text/xml");
287: contentTypes.add("charset=utf8");
288: headers.put("content-type", contentTypes);
289: message.put(Message.PROTOCOL_HEADERS, headers);
290:
291: AuthorizationPolicy authPolicy = new AuthorizationPolicy();
292: authPolicy.setUserName("BJ");
293: authPolicy.setPassword("value");
294: message.put(AuthorizationPolicy.class, authPolicy);
295: }
296:
297: private void setUpOneway(Message message) {
298: Exchange exchange = control.createMock(Exchange.class);
299: message.setExchange(exchange);
300: exchange.isOneWay();
301: EasyMock.expectLastCall().andReturn(true);
302: }
303:
304: private HTTPConduit setUpConduit(boolean send,
305: boolean autoRedirect, boolean decoupled) throws Exception {
306: endpointInfo = new EndpointInfo();
307: endpointInfo.setAddress(NOWHERE + "bar/foo");
308: connectionFactory = control
309: .createMock(HttpURLConnectionFactory.class);
310:
311: if (send) {
312: //proxy = control.createMock(Proxy.class);
313: proxy = null;
314: connection = control.createMock(HttpURLConnection.class);
315: connectionFactory.createConnection(EasyMock.eq(proxy),
316: EasyMock.eq(new URL(NOWHERE + "bar/foo")));
317:
318: EasyMock.expectLastCall().andReturn(connection);
319: connection.setDoOutput(true);
320: EasyMock.expectLastCall();
321:
322: connection.setRequestMethod("POST");
323: EasyMock.expectLastCall();
324:
325: if (!autoRedirect) {
326: connection.getRequestMethod();
327: EasyMock.expectLastCall().andReturn("POST");
328: connection.setChunkedStreamingMode(-1);
329: EasyMock.expectLastCall();
330: }
331:
332: connection.setConnectTimeout(303030);
333: EasyMock.expectLastCall();
334: connection.setReadTimeout(404040);
335: EasyMock.expectLastCall();
336: connection.setUseCaches(false);
337: EasyMock.expectLastCall();
338:
339: }
340:
341: CXFBusImpl bus = new CXFBusImpl();
342: URL decoupledURL = null;
343: if (decoupled) {
344: decoupledURL = new URL(NOWHERE + "response");
345: DestinationFactoryManager mgr = control
346: .createMock(DestinationFactoryManager.class);
347: DestinationFactory factory = control
348: .createMock(DestinationFactory.class);
349: Destination destination = control
350: .createMock(Destination.class);
351:
352: bus.setExtension(mgr, DestinationFactoryManager.class);
353: mgr.getDestinationFactoryForUri(decoupledURL.toString());
354: EasyMock.expectLastCall().andReturn(factory);
355: factory.getDestination(EasyMock.isA(EndpointInfo.class));
356: EasyMock.expectLastCall().andReturn(destination);
357: destination.setMessageObserver(EasyMock
358: .isA(HTTPConduit.InterposedMessageObserver.class));
359: }
360:
361: control.replay();
362:
363: HTTPConduit conduit = new HTTPTestConduit(bus, endpointInfo,
364: null, connectionFactory);
365: conduit.finalizeConfig();
366:
367: if (send) {
368: conduit.getClient().setConnectionTimeout(303030);
369: conduit.getClient().setReceiveTimeout(404040);
370: conduit.getClient().setAutoRedirect(autoRedirect);
371: if (!autoRedirect) {
372: conduit.getClient().setAllowChunking(true);
373: }
374: }
375:
376: if (decoupled) {
377: conduit.getClient().setDecoupledEndpoint(
378: decoupledURL.toString());
379: assertNotNull("expected back channel", conduit
380: .getBackChannel());
381: } else {
382: assertNull("unexpected back channel", conduit
383: .getBackChannel());
384: }
385:
386: observer = new MessageObserver() {
387: public void onMessage(Message m) {
388: inMessage = m;
389: }
390: };
391: conduit.setMessageObserver(observer);
392: return conduit;
393: }
394:
395: private void verifySentMessage(HTTPConduit conduit, Message message)
396: throws IOException {
397: verifySentMessage(conduit, message, false);
398: }
399:
400: private void verifySentMessage(HTTPConduit conduit,
401: Message message, boolean expectHeaders) throws IOException {
402: verifySentMessage(conduit, message, expectHeaders,
403: ResponseStyle.BACK_CHANNEL);
404: }
405:
406: private void verifySentMessage(HTTPConduit conduit,
407: Message message, boolean expectHeaders, ResponseStyle style)
408: throws IOException {
409: verifySentMessage(conduit, message, expectHeaders, style,
410: ResponseDelimiter.LENGTH, false);
411: }
412:
413: private void verifySentMessage(HTTPConduit conduit,
414: Message message, ResponseStyle style,
415: ResponseDelimiter delimiter, boolean emptyResponse)
416: throws IOException {
417: verifySentMessage(conduit, message, false, style, delimiter,
418: emptyResponse);
419: }
420:
421: private void verifySentMessage(HTTPConduit conduit,
422: Message message, boolean expectHeaders,
423: ResponseStyle style, ResponseDelimiter delimiter,
424: boolean emptyResponse) throws IOException {
425: control.verify();
426: control.reset();
427:
428: OutputStream wrappedOS = verifyRequestHeaders(message,
429: expectHeaders);
430:
431: os.write(PAYLOAD.getBytes(), 0, PAYLOAD.length());
432: EasyMock.expectLastCall();
433:
434: os.flush();
435: EasyMock.expectLastCall();
436: os.flush();
437: EasyMock.expectLastCall();
438: os.close();
439: EasyMock.expectLastCall();
440:
441: if (style == ResponseStyle.NONE) {
442: setUpOneway(message);
443: }
444:
445: verifyHandleResponse(style, delimiter);
446:
447: control.replay();
448:
449: wrappedOS.flush();
450: wrappedOS.flush();
451: wrappedOS.close();
452:
453: assertNotNull("expected in message", inMessage);
454: Map<?, ?> headerMap = (Map<?, ?>) inMessage
455: .get(Message.PROTOCOL_HEADERS);
456: assertEquals("unexpected response headers", headerMap.size(), 0);
457: Integer expectedResponseCode = style == ResponseStyle.BACK_CHANNEL ? HttpURLConnection.HTTP_OK
458: : HttpURLConnection.HTTP_ACCEPTED;
459: assertEquals("unexpected response code", expectedResponseCode,
460: inMessage.get(Message.RESPONSE_CODE));
461: if (!emptyResponse) {
462: assertTrue("unexpected content formats", inMessage
463: .getContentFormats().contains(InputStream.class));
464: InputStream content = inMessage
465: .getContent(InputStream.class);
466: if (!(content instanceof PushbackInputStream)) {
467: assertSame("unexpected content", is, content);
468: }
469: }
470:
471: if (style == ResponseStyle.DECOUPLED) {
472: verifyDecoupledResponse(conduit);
473: }
474:
475: conduit.close();
476:
477: finalVerify();
478: }
479:
480: private OutputStream verifyRequestHeaders(Message message,
481: boolean expectHeaders) throws IOException {
482: Map<String, List<String>> headers = CastUtils
483: .cast((Map<?, ?>) message.get(Message.PROTOCOL_HEADERS));
484: assertNotNull("expected request headers set", headers);
485: assertTrue("expected output stream format", message
486: .getContentFormats().contains(OutputStream.class));
487:
488: connection.getRequestMethod();
489: EasyMock.expectLastCall().andReturn("POST");
490:
491: os = EasyMock.createMock(OutputStream.class);
492: connection.getOutputStream();
493: EasyMock.expectLastCall().andReturn(os);
494:
495: message.put(HTTPConduit.KEY_HTTP_CONNECTION, connection);
496: if (expectHeaders) {
497: connection.addRequestProperty(EasyMock.eq("Authorization"),
498: EasyMock.eq("Basic Qko6dmFsdWU="));
499: EasyMock.expectLastCall();
500: connection.addRequestProperty(EasyMock.eq("content-type"),
501: EasyMock.eq("text/xml"));
502: EasyMock.expectLastCall();
503: connection.addRequestProperty(EasyMock.eq("content-type"),
504: EasyMock.eq("charset=utf8"));
505: EasyMock.expectLastCall();
506: }
507:
508: control.replay();
509:
510: OutputStream wrappedOS = message.getContent(OutputStream.class);
511: assertNotNull("expected output stream", wrappedOS);
512:
513: wrappedOS.write(PAYLOAD.getBytes());
514:
515: control.verify();
516: control.reset();
517:
518: return wrappedOS;
519: }
520:
521: private void verifyHandleResponse(ResponseStyle style,
522: ResponseDelimiter delimiter) throws IOException {
523: verifyHandleResponse(style, delimiter, false);
524: }
525:
526: private void verifyHandleResponse(ResponseStyle style,
527: ResponseDelimiter delimiter, boolean emptyResponse)
528: throws IOException {
529: connection.getHeaderFields();
530: EasyMock.expectLastCall().andReturn(Collections.EMPTY_MAP);
531: int responseCode = style == ResponseStyle.BACK_CHANNEL ? HttpURLConnection.HTTP_OK
532: : HttpURLConnection.HTTP_ACCEPTED;
533: connection.getResponseCode();
534: EasyMock.expectLastCall().andReturn(responseCode).anyTimes();
535: is = EasyMock.createMock(InputStream.class);
536: connection.getInputStream();
537: EasyMock.expectLastCall().andReturn(is).anyTimes();
538: switch (style) {
539: case NONE:
540: case DECOUPLED:
541: connection.getContentLength();
542: if (delimiter == ResponseDelimiter.CHUNKED
543: || delimiter == ResponseDelimiter.EOF) {
544: EasyMock.expectLastCall().andReturn(-1);
545: if (delimiter == ResponseDelimiter.CHUNKED) {
546: connection.getHeaderField("Transfer-Encoding");
547: EasyMock.expectLastCall().andReturn("chunked");
548: } else if (delimiter == ResponseDelimiter.EOF) {
549: connection.getHeaderField("Connection");
550: EasyMock.expectLastCall().andReturn("close");
551: }
552: is.read();
553: EasyMock.expectLastCall().andReturn(
554: emptyResponse ? -1 : (int) '<');
555: } else {
556: EasyMock.expectLastCall().andReturn(123);
557: }
558: if (emptyResponse) {
559: is.close();
560: EasyMock.expectLastCall();
561: }
562: break;
563:
564: case BACK_CHANNEL:
565: connection.getErrorStream();
566: EasyMock.expectLastCall().andReturn(null);
567: break;
568:
569: default:
570: break;
571: }
572: }
573:
574: private void verifyDecoupledResponse(HTTPConduit conduit)
575: throws IOException {
576: Message incoming = new MessageImpl();
577: conduit.getDecoupledObserver().onMessage(incoming);
578: assertSame("expected pass thru onMessage() notification",
579: inMessage, incoming);
580: assertEquals("unexpected response code",
581: HttpURLConnection.HTTP_OK, inMessage
582: .get(Message.RESPONSE_CODE));
583: assertEquals("expected DECOUPLED_CHANNEL_MESSAGE flag set",
584: Boolean.TRUE, inMessage.get(DECOUPLED_CHANNEL_MESSAGE));
585: assertEquals("unexpected HTTP_REQUEST set", false, inMessage
586: .containsKey(AbstractHTTPDestination.HTTP_REQUEST));
587: assertEquals("unexpected HTTP_RESPONSE set", false, inMessage
588: .containsKey(AbstractHTTPDestination.HTTP_RESPONSE));
589: assertEquals(
590: "unexpected Message.ASYNC_POST_RESPONSE_DISPATCH set",
591: false,
592: inMessage
593: .containsKey(Message.ASYNC_POST_RESPONSE_DISPATCH));
594: }
595:
596: private void finalVerify() {
597: if (control != null) {
598: control.verify();
599: control = null;
600: }
601: }
602:
603: }
|