30 import com.sun.xml.internal.ws.api.WSBinding; |
30 import com.sun.xml.internal.ws.api.WSBinding; |
31 import com.sun.xml.internal.ws.api.ha.StickyFeature; |
31 import com.sun.xml.internal.ws.api.ha.StickyFeature; |
32 import com.sun.xml.internal.ws.api.message.Packet; |
32 import com.sun.xml.internal.ws.api.message.Packet; |
33 import com.sun.xml.internal.ws.api.pipe.*; |
33 import com.sun.xml.internal.ws.api.pipe.*; |
34 import com.sun.xml.internal.ws.api.pipe.helper.AbstractTubeImpl; |
34 import com.sun.xml.internal.ws.api.pipe.helper.AbstractTubeImpl; |
|
35 import com.sun.xml.internal.ws.client.ClientTransportException; |
35 import com.sun.xml.internal.ws.developer.HttpConfigFeature; |
36 import com.sun.xml.internal.ws.developer.HttpConfigFeature; |
|
37 import com.sun.xml.internal.ws.resources.ClientMessages; |
36 import com.sun.xml.internal.ws.transport.Headers; |
38 import com.sun.xml.internal.ws.transport.Headers; |
37 import com.sun.xml.internal.ws.util.ByteArrayBuffer; |
39 import com.sun.xml.internal.ws.util.ByteArrayBuffer; |
38 import com.sun.xml.internal.ws.client.ClientTransportException; |
|
39 import com.sun.xml.internal.ws.resources.ClientMessages; |
|
40 import com.sun.xml.internal.ws.util.RuntimeVersion; |
40 import com.sun.xml.internal.ws.util.RuntimeVersion; |
41 import com.sun.xml.internal.ws.util.StreamUtils; |
41 import com.sun.xml.internal.ws.util.StreamUtils; |
42 |
42 |
43 import javax.xml.bind.DatatypeConverter; |
43 import javax.xml.bind.DatatypeConverter; |
44 import javax.xml.bind.JAXBContext; |
|
45 import javax.xml.bind.JAXBException; |
|
46 import javax.xml.ws.BindingProvider; |
44 import javax.xml.ws.BindingProvider; |
47 import javax.xml.ws.WebServiceException; |
45 import javax.xml.ws.WebServiceException; |
48 import javax.xml.ws.WebServiceFeature; |
46 import javax.xml.ws.WebServiceFeature; |
|
47 import javax.xml.ws.handler.MessageContext; |
49 import javax.xml.ws.soap.SOAPBinding; |
48 import javax.xml.ws.soap.SOAPBinding; |
50 import javax.xml.ws.handler.MessageContext; |
49 import java.io.*; |
51 |
|
52 import java.io.ByteArrayOutputStream; |
|
53 import java.io.IOException; |
|
54 import java.io.InputStream; |
|
55 import java.io.OutputStream; |
|
56 import java.io.PrintWriter; |
|
57 import java.net.CookieHandler; |
50 import java.net.CookieHandler; |
58 import java.util.Collections; |
51 import java.net.HttpURLConnection; |
59 import java.util.List; |
52 import java.util.*; |
|
53 import java.util.Map.Entry; |
|
54 import java.util.logging.Level; |
60 import java.util.logging.Logger; |
55 import java.util.logging.Logger; |
61 import java.util.logging.Level; |
|
62 import java.util.Map; |
|
63 import java.util.Map.Entry; |
|
64 import java.net.HttpURLConnection; |
|
65 |
56 |
66 /** |
57 /** |
67 * {@link Tube} that sends a request to a remote HTTP server. |
58 * {@link Tube} that sends a request to a remote HTTP server. |
68 * |
59 * |
69 * TODO: need to create separate HTTP transport pipes for binding. SOAP1.1, SOAP1.2, |
60 * TODO: need to create separate HTTP transport pipes for binding. SOAP1.1, SOAP1.2, |
70 * TODO: XML/HTTP differ in handling status codes. |
61 * TODO: XML/HTTP differ in handling status codes. |
71 * |
62 * |
72 * @author Jitendra Kotamraju |
63 * @author Jitendra Kotamraju |
73 */ |
64 */ |
74 public class HttpTransportPipe extends AbstractTubeImpl { |
65 public class HttpTransportPipe extends AbstractTubeImpl { |
75 private static final Logger LOGGER = Logger.getLogger(HttpTransportPipe.class.getName()); |
66 |
|
67 private static final List<String> USER_AGENT = Collections.singletonList(RuntimeVersion.VERSION.toString()); |
|
68 private static final Logger LOGGER = Logger.getLogger(HttpTransportPipe.class.getName()); |
|
69 |
|
70 /** |
|
71 * Dumps what goes across HTTP transport. |
|
72 */ |
|
73 public static boolean dump; |
76 |
74 |
77 private final Codec codec; |
75 private final Codec codec; |
78 private final WSBinding binding; |
76 private final WSBinding binding; |
79 private static final List<String> USER_AGENT = Collections.singletonList(RuntimeVersion.VERSION.toString()); |
|
80 private final CookieHandler cookieJar; // shared object among the tubes |
77 private final CookieHandler cookieJar; // shared object among the tubes |
81 private final boolean sticky; |
78 private final boolean sticky; |
82 |
79 |
83 // Need to use JAXB first to register DatatypeConverter |
|
84 static { |
80 static { |
|
81 boolean b; |
85 try { |
82 try { |
86 JAXBContext.newInstance().createUnmarshaller(); |
83 b = Boolean.getBoolean(HttpTransportPipe.class.getName()+".dump"); |
87 } catch(JAXBException je) { |
84 } catch( Throwable t ) { |
88 // Nothing much can be done. Intentionally left empty |
85 b = false; |
89 } |
86 } |
|
87 dump = b; |
90 } |
88 } |
91 |
89 |
92 public HttpTransportPipe(Codec codec, WSBinding binding) { |
90 public HttpTransportPipe(Codec codec, WSBinding binding) { |
93 this.codec = codec; |
91 this.codec = codec; |
94 this.binding = binding; |
92 this.binding = binding; |
118 private HttpTransportPipe(HttpTransportPipe that, TubeCloner cloner) { |
116 private HttpTransportPipe(HttpTransportPipe that, TubeCloner cloner) { |
119 this(that.codec.copy(), that.binding); |
117 this(that.codec.copy(), that.binding); |
120 cloner.add(that,this); |
118 cloner.add(that,this); |
121 } |
119 } |
122 |
120 |
|
121 @Override |
123 public NextAction processException(@NotNull Throwable t) { |
122 public NextAction processException(@NotNull Throwable t) { |
124 return doThrow(t); |
123 return doThrow(t); |
125 } |
124 } |
126 |
125 |
|
126 @Override |
127 public NextAction processRequest(@NotNull Packet request) { |
127 public NextAction processRequest(@NotNull Packet request) { |
128 return doReturnWith(process(request)); |
128 return doReturnWith(process(request)); |
129 } |
129 } |
130 |
130 |
|
131 @Override |
131 public NextAction processResponse(@NotNull Packet response) { |
132 public NextAction processResponse(@NotNull Packet response) { |
132 return doReturnWith(response); |
133 return doReturnWith(response); |
133 } |
134 } |
134 |
135 |
135 protected HttpClientTransport getTransport(Packet request, Map<String, List<String>> reqHeaders) { |
136 protected HttpClientTransport getTransport(Packet request, Map<String, List<String>> reqHeaders) { |
313 return code == 500 || code == 400; |
315 return code == 500 || code == 400; |
314 } |
316 } |
315 |
317 |
316 private void addCookies(Packet context, Map<String, List<String>> reqHeaders) throws IOException { |
318 private void addCookies(Packet context, Map<String, List<String>> reqHeaders) throws IOException { |
317 Boolean shouldMaintainSessionProperty = |
319 Boolean shouldMaintainSessionProperty = |
318 (Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY); |
320 (Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY); |
319 if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) { |
321 if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) { |
320 return; // explicitly turned off |
322 return; // explicitly turned off |
321 } |
323 } |
322 if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) { |
324 if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) { |
323 Map<String, List<String>> cookies = cookieJar.get(context.endpointAddress.getURI(),reqHeaders); |
325 Map<String, List<String>> rememberedCookies = cookieJar.get(context.endpointAddress.getURI(), reqHeaders); |
324 List<String> cookieList = cookies.get("Cookie"); |
326 processCookieHeaders(reqHeaders, rememberedCookies, "Cookie"); |
325 if (cookieList != null && !cookieList.isEmpty()) { |
327 processCookieHeaders(reqHeaders, rememberedCookies, "Cookie2"); |
326 reqHeaders.put("Cookie", cookieList); |
328 } |
327 } |
329 } |
328 cookieList = cookies.get("Cookie2"); |
330 |
329 if (cookieList != null && !cookieList.isEmpty()) { |
331 private void processCookieHeaders(Map<String, List<String>> requestHeaders, Map<String, List<String>> rememberedCookies, String cookieHeader) { |
330 reqHeaders.put("Cookie2", cookieList); |
332 List<String> jarCookies = rememberedCookies.get(cookieHeader); |
331 } |
333 if (jarCookies != null && !jarCookies.isEmpty()) { |
|
334 List<String> resultCookies = mergeUserCookies(jarCookies, requestHeaders.get(cookieHeader)); |
|
335 requestHeaders.put(cookieHeader, resultCookies); |
|
336 } |
|
337 } |
|
338 |
|
339 private List<String> mergeUserCookies(List<String> rememberedCookies, List<String> userCookies) { |
|
340 |
|
341 // nothing to merge |
|
342 if (userCookies == null || userCookies.isEmpty()) { |
|
343 return rememberedCookies; |
|
344 } |
|
345 |
|
346 Map<String, String> map = new HashMap<String, String>(); |
|
347 cookieListToMap(rememberedCookies, map); |
|
348 cookieListToMap(userCookies, map); |
|
349 |
|
350 return new ArrayList<String>(map.values()); |
|
351 } |
|
352 |
|
353 private void cookieListToMap(List<String> cookieList, Map<String, String> targetMap) { |
|
354 for(String cookie : cookieList) { |
|
355 int index = cookie.indexOf("="); |
|
356 String cookieName = cookie.substring(0, index); |
|
357 targetMap.put(cookieName, cookie); |
332 } |
358 } |
333 } |
359 } |
334 |
360 |
335 private void recordCookies(Packet context, HttpClientTransport con) throws IOException { |
361 private void recordCookies(Packet context, HttpClientTransport con) throws IOException { |
336 Boolean shouldMaintainSessionProperty = |
362 Boolean shouldMaintainSessionProperty = |
337 (Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY); |
363 (Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY); |
338 if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) { |
364 if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) { |
339 return; // explicitly turned off |
365 return; // explicitly turned off |
340 } |
366 } |
341 if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) { |
367 if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) { |
342 cookieJar.put(context.endpointAddress.getURI(), con.getHeaders()); |
368 cookieJar.put(context.endpointAddress.getURI(), con.getHeaders()); |
361 * write SOAPAction header if the soapAction parameter is non-null or BindingProvider properties set. |
387 * write SOAPAction header if the soapAction parameter is non-null or BindingProvider properties set. |
362 * BindingProvider properties take precedence. |
388 * BindingProvider properties take precedence. |
363 */ |
389 */ |
364 private void writeSOAPAction(Map<String, List<String>> reqHeaders, String soapAction) { |
390 private void writeSOAPAction(Map<String, List<String>> reqHeaders, String soapAction) { |
365 //dont write SOAPAction HTTP header for SOAP 1.2 messages. |
391 //dont write SOAPAction HTTP header for SOAP 1.2 messages. |
366 if(SOAPVersion.SOAP_12.equals(binding.getSOAPVersion())) |
392 if(SOAPVersion.SOAP_12.equals(binding.getSOAPVersion())) { |
367 return; |
393 return; |
368 if (soapAction != null) |
394 } |
|
395 if (soapAction != null) { |
369 reqHeaders.put("SOAPAction", Collections.singletonList(soapAction)); |
396 reqHeaders.put("SOAPAction", Collections.singletonList(soapAction)); |
370 else |
397 } else { |
371 reqHeaders.put("SOAPAction", Collections.singletonList("\"\"")); |
398 reqHeaders.put("SOAPAction", Collections.singletonList("\"\"")); |
372 } |
399 } |
373 |
400 } |
|
401 |
|
402 @Override |
374 public void preDestroy() { |
403 public void preDestroy() { |
375 // nothing to do. Intentionally left empty. |
404 // nothing to do. Intentionally left empty. |
376 } |
405 } |
377 |
406 |
|
407 @Override |
378 public HttpTransportPipe copy(TubeCloner cloner) { |
408 public HttpTransportPipe copy(TubeCloner cloner) { |
379 return new HttpTransportPipe(this,cloner); |
409 return new HttpTransportPipe(this,cloner); |
380 } |
410 } |
381 |
411 |
382 |
412 |