Helidon Webclient does not seem to work with a proxy - helidon

I am facing some trouble with the WebClient when using proxy, i.e. the code below does not work
WebClient webClient = WebClient.builder().baseUri("BASEURL").proxy(getProxy()).build();
Single<WebClientResponse> res = webClient.get().path("/MY/SUB/PATH").addHeader("Authorization", "Bearer " + MY_TOKEN).request();
WebClientResponse webClientRes = res.get();
String resContent = webClientRes.content().as(String.class).get();
public Proxy getProxy(){
return Proxy.builder().type(Proxy.ProxyType.HTTP).host(host).port(port).password("SECRET_PASSWORD".toCharArray()).username(username).build();
}
However the if we use Apache HttpClient the code works (working code below)
HttpHost proxy = new HttpHost(host, port);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(username, "SECRET_PASSWORD"));
HttpGet request = new HttpGet("BASEURL" + "/MY/SUB/PATH");
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + MY_TOKEN);
CloseableHttpClient httpClient = HttpClients.custom().setProxy(proxy).setDefaultCredentialsProvider(credentialsProvider).build();
String resContent = EntityUtils.toString(httpClient.execute(request).getEntity());
could anyone let us know if we are overlooking something basic?
We are using helidon MP 2.5.2

When you set a proxy in WebClient, it will use absolute URI in the request because of changes made in https://github.com/helidon-io/helidon/issues/2302 and https://github.com/helidon-io/helidon/issues/3438. The use of absolute URI was implemented because of section 5.1.2 Request-URI in https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html which states:
The absoluteURI form is REQUIRED when the request is being made to a
proxy.
The problem is that some hosts have issue processing a request with absolute URI as they expect relative URI instead. I have encountered while working on issue https://github.com/helidon-io/helidon/issues/4644 where my testcase has a client that is connecting to KeyCloak as an OIDC server, and KeyCloak will return a 404 because it cannot handle the absoluteURI.
There is a special Webclient config property called relative-uris that you can use to force the request URI to use the relative form rather than absolute. So you can try adding config() in your WebClient.builder() and set that property like this:
.config(Config.create(ConfigSources.create(Map.of("relative-uris", "true")))
where Config needs to be imported as io.helidon.config.Config and ConfigSources as io.helidon.config.ConfigSources. As an alternative, you can also add something like this in your application.yaml:
force-relative-uris:
relative-uris: true
and add config() in the WebClient.builder() like this:
.config(config.get("force-relative-uris"))
where config is instantiated prior to WebClient.builder() like this:
Config config = Config.create();
In the upcoming Helidon v2.5.5 (and v3.0.3), there will be a new relativeUris(boolean relativeUris) in WebClient.builder() so that you don’t have to use config() as in my examples above, which is slightly cumbersome.

Related

NetCore 3.1 PostAsync CustomHeaders not working

I have several RESTful services that working with each other. In one scenario I want to post some data from one service to another service and I want to attach some information in Header of the request. I saw several cases to do this and in the end I came up with this workaround:
var httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(3);
var httpRequestMessage = new HttpRequestMessage {
Method = HttpMethod.Post,
RequestUri = new Uri(service2Address),
Content = new StringContent(JsonConvert.SerializeObject(obj))
};
httpRequestMessage.Headers.Add("myCustomHeaderKey", "myCustomHeaderValue");
var response = await httpClient.SendAsync(httpRequestMessage);
var responseString = await response.Content.ReadAsStringAsync();
With these lines of code, a Post request sent, but in service2 when I want to get the headers from request, there is no sign of myCustomHeaderKey in headers collection. I inspect Request.Headers in Visual Studio Watch and even try to get custom header with Request.Headers["myCustomHeaderKey"]. So what's wrong here?
EDIT 1
This implementation in based on this tutorial.
I have developed code like yours. Have created Two Asp.net core 3.1 project with standart template. One service is starting localhost:44320 and other localhost:44300
localhost:44320/PostService wrote the your codes.
Then get this url with browser. localhost:44320/weatherforecast/IncomeService function is like below
Finally i put breakpoint to where get request header. Result is like below
There is a not a problem. Maybe you use change request header middleware. Or if you are using something like nginx. this problem maybe nginx configuration.

Using HttpClient to read encrypted URL on Windows Phone

I was reading a normal URL from my app, and it was working fine with the code:
string returnedTaskTResult = await new HttpClient().GetStringAsync(url);
then I used a new url, this time encrypted, and now is returning the exception:
System.InvalidOperationException: An invalid request URI was provided.
The request URI must either be an absolute URI or BaseAddress must be
set.
Why is this happening and how o fix it?
When you use GetStringAsync, you need to use a absolute URI. It sounds like your encrypted URI is relative. Try this...
var client = new HttpClient() { BaseAddress = new Uri("http://yourhosthere.com");
}
string returnedTaskTResult = await client.GetStringAsync(url);

How can I configure Apache HttpClient 4.x to use a specific Websphere SSL alias?

We have an issue in our environment when using Websphere to attempt to connect to an external system with HttpClient 4.x (current version is 4.2.1). Connecting to the external system is fine with their certificate being installed in Websphere with no additional configuration of HttpClient. However, when they enabled mutual authentication, it no longer works and we get a SSLPeerUnverifiedException exception:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated,
at com.ibm.jsse2.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:105),
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128),
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572),
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180),
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294),
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640),
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:1066),
I was provided the following code sample, and I was wondering if there's any way to configure HttpClient to use an explicit alias like this code sample does. I've tried to find good documentation on using SSL mutual authentication with HttpClient 4 and haven't been able to find much.
Here's the code sample:
private HttpURLConnection getConnection(String server, String machine,
String port) throws Exception {
URL u = new URL(server);
HttpsURLConnection connection = (HttpsURLConnection) u.openConnection();
String alias = "CellDefaultSSLSettings";
final HashMap connectionInfo = new HashMap();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION,
JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST, machine);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, port);
javax.net.ssl.SSLSocketFactory sslFact = JSSEHelper.getInstance()
.getSSLSocketFactory(alias, connectionInfo, null);
connection.setSSLSocketFactory(sslFact);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
return connection;
}
Basically, how do I make HttpClient use "CellDefaultSSLSettings"?
Fundamentally this problem has nothing to do with HttpClient. HttpClient can be configured to establish HTTPS connections using any custom SSLContext or SSLSocketFactory instance. This is basically about how to use JSSE APIs to configure SSLContext in the right way. In your particular case JSSEHelper does all the hard work for you.
// JSSE socket factory
javax.net.ssl.SSLSocketFactory jssesf = JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null);
// HC socket factory
SSLSocketFactory hcsf = new SSLSocketFactory(jssesf, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
This will give a connection socket factory that can be registered with the connection manager.
HttpClient 4.3 also comes with SSLContextBuilder class which can be used to assemble custom SSL configurations using fluid builder API.
https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java
oleg's answer helped me out.
What I did was extend the DefaultHttpClient, and each constructor takes a String argument for the destination URL and calls a method setupScheme:
private void setupScheme(final String url) throws Exception {
Scheme scheme = new Scheme("https", 443, retrieveWebsphereSSLConnectionFactory(url));
getConnectionManager().getSchemeRegistry().register(scheme);
}
The method retrieveWebsphereSSLConnectionFactory essentially combines the code from the sample with the code oleg provided:
private SchemeSocketFactory retrieveWebsphereSSLConnectionFactory(final String url)
throws SSLException, URISyntaxException {
final String alias = "CellDefaultSSLSettings";
final HashMap<String, String> connectionInfo = new HashMap<String, String>();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION, JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST,
URIUtils.extractHost(new URI(url)).getHostName());
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, "443");
return new SSLSocketFactory(JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null),
SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
}

Adding authentication to security header in WCF to consume Metro WSIT service

I use this simple way to attach username and password to the SOAP request header. This works fine inside Java boundaries, but I want to be able to call it with my WCF client. How do I do this?
I've tried the following code, but it does not include the credentials in the header:
wsClient.ClientCredentials.UserName.UserName = "Hello";
wsClient.ClientCredentials.UserName.Password = "World";
Thanks in advance!
That is quite awful non-standardized way. It uses custom HTTP Headers so you cannot expect that built in WCF mechanism will magically support such approach. How should WCF know that you want to add custom non-standard HTTP header to HTTP request (not SOAP header)?
Use this:
var proxy = new YourServiceClient();
using (var scope = new OperationContextScope(proxy.InnerChannel))
{
var prop = new HttpRequestMessageProperty();
prop.Headers.Add("UserName", "Hello");
prop.Headers.Add("Password", "World");
OperationContext context = OperationContext.Current;
context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = prop;
proxy.CallYourOperation();
}

WCF: Passing Kerboros token via HTTP call instead of HTTPS

I want to pass Kerberos token via HTTP call to a server using WCF.
I have a piece of code that successfully does this. But it only works if I make a request to HTTPS URI.
var httpBinding = new WebHttpBinding(WebHttpSecurityMode.Transport) { MaxReceivedMessageSize = Int32.MaxValue };
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
httpBinding.Security.Transport.Realm = "MyCompany.com";
var endPoint = new EndpointAddress("https:xxxxxxxx.com/my/service); // This works
var endPoint = new EndpointAddress("http:xxxxxxxx.com/my/service); // This does not work
var channelFactory = new ChannelFactory<IMyServiceContract>(httpBinding, endPoint);
channelFactory.Endpoint.Behaviors.Add(new WebHttpBehavior());
_channel = channelFactory.CreateChannel();
_channel.ConsumeService();
If I make a request via the channel, and if the end point is https. It works and I can verify the Kerberos Token is in the HTTP requst.
If the service end point is HTTP, it gives error:
System.ArgumentException : The provided URI scheme 'http' is invalid; expected 'https'.
Parameter name: via
Can someone let me know how to Configure WCF so that it send Kerboros token with HTTP URI.
Regards,
Kevin
When you don't want to use HTTPS you must set your security mode to WebHttpSecurityMode.TransportCredentialOnly. If you use WebHttpSecurityMode.Transport it demands HTTPS.