Popular New Releases in HTTP Client
guzzle
Release 7.4.1
vue-resource
Flurl
Flurl.Http 3.2.2
httplug
2.3.0
vue-axios
3.4.1
Popular Libraries in HTTP Client
by square java
39764 Apache-2.0
A type-safe HTTP client for Android and the JVM
by guzzle php
21585 MIT
Guzzle, an extensible PHP HTTP client
by pagekit javascript
10108 MIT
The HTTP client for Vue.js
by tmenier csharp
2938 MIT
Fluent URL builder and testable HTTP client for .NET
by php-http php
2310 MIT
HTTPlug, the HTTP client abstraction for PHP
by imcvampire javascript
1906 MIT
A small wrapper for integrating axios to Vuejs
by symfony php
1472 MIT
The HttpClient component provides powerful methods to fetch HTTP resources synchronously or asynchronously.
by micropython python
1373 NOASSERTION
Core Python libraries ported to MicroPython
by googleapis java
1227 Apache-2.0
Google HTTP Client Library for Java
Trending New libraries in HTTP Client
by AttoJS typescript
516 MIT
⚡️ Vue 3 composition API for data fetching, supports SWR, polling, error retry, cache request, pagination, etc. ⚡️ 一个能轻松帮你管理请求状态(支持SWR,轮询,错误重试,缓存,分页等)的 Vue 3 请求库
by donavon javascript
272 MIT
A tiny modern data fetching solution
by encode python
250 BSD-3-Clause
A minimal HTTP client. ⚙️
by mssroboto javascript
193
En este repositorio encontrarás el material del curso diseño para programadores.
by elastic java
133 Apache-2.0
Official Elasticsearch Java Client
by yusanshi python
123 MIT
Implementations of some methods in news recommendation.
by ourzora typescript
99 GPL-3.0
NFT Data Fetching Hooks with Zora contract data
by jiejieTop c
97 Apache-2.0
A high-performance, high-stability, cross-platform HTTP client.
by kkbruce csharp
83 MIT
ASP.NET Core 專案練習集合,ASP.NET Core Practice Projects
Top Authors in HTTP Client
1
4 Libraries
31
2
3 Libraries
1663
3
3 Libraries
28
4
3 Libraries
17
5
3 Libraries
8
6
2 Libraries
4
7
2 Libraries
43
8
2 Libraries
472
9
2 Libraries
17
10
2 Libraries
4
1
4 Libraries
31
2
3 Libraries
1663
3
3 Libraries
28
4
3 Libraries
17
5
3 Libraries
8
6
2 Libraries
4
7
2 Libraries
43
8
2 Libraries
472
9
2 Libraries
17
10
2 Libraries
4
Trending Kits in HTTP Client
No Trending Kits are available at this moment for HTTP Client
Trending Discussions on HTTP Client
Trouble with On-Behalf-Of flow with standalone Blazor WASM, AAD, .NET Core 6 Web API calling MS Graph
How to change the http client used by pouchDB?
AWS Object Lambda Access Point timing out when writing response when run from within VPC
How can I prevent java.net.http.HttpClient from making an upgrade request?
Nested logic app retries - parent throwing 504
Obtaining a scoped service in a custom delegating handler in Blazor Server
How to send PHP input stream data using curl?
Laravel - Correct way to catch cURL exceception
OkHttp client authentication failing to validate server and send client certificate in the same request
Domain Driven Design: Define boundaries from Domain Model
QUESTION
Trouble with On-Behalf-Of flow with standalone Blazor WASM, AAD, .NET Core 6 Web API calling MS Graph
Asked 2022-Mar-23 at 00:09I have a standalone Blazor WASM site (client), a separate .NET 6 web API (server) with protected endpoints and I'm trying to call MS Graph from the API.
I've read just about every article I could find on the configuration required to make this work and I'm stuck with the incremental consent failing. I get the following error when trying to access a server API which uses MS Graph:
Configuration...Error acquiring a token for a downstream web API - MsalUiRequiredException message is: AADSTS65001: The user or administrator has not consented to use the application with ID '[redacted]' named '[redacted]'. Send an interactive authorization request for this user and resource.
Created AAD app for Web API (server), added secret for Graph configuration, set the app URI and created
access_as_user
scope under "Expose an API" in AAD.Added the client ID (from the following step) to the
knownClientApplications
section in the manifest for the server app registration in AAD.For API Permissions I added Graph scopes
User.Read
,User.Read.All
, andGroup.Read.All
and provided admin consent in the AAD UI.Configured
appsettings.json
in the API to add the Graph APIBaseUrl
and above scopes from step 2 along with the correct AzureAD domain,TenantId
,ClientId
, andClientSecret
values for MSAL to function.Configured MSAL on the server:
1builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
2 .AddMicrosoftIdentityWebApi(builder.Configuration)
3 .EnableTokenAcquisitionToCallDownstreamApi()
4 .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
5 .AddInMemoryTokenCaches();
6
Created AAD app for Blazor WASM, used SPA auth w/redirect to
https://localhost:7014/authentication/login-callback
and set the API permissions toapi://[redacted]/access_as_user
only.Created custom authorization message handler according to this article.
1builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
2 .AddMicrosoftIdentityWebApi(builder.Configuration)
3 .EnableTokenAcquisitionToCallDownstreamApi()
4 .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
5 .AddInMemoryTokenCaches();
6public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation)
7{
8 ConfigureHandler(
9 authorizedUrls: new[]
10 {
11 "https://localhost:7069"
12 },
13 scopes: new[]
14 {
15 "api://[redacted]/.default"
16 });
17}
18
- Configured MSAL on the client:
1builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
2 .AddMicrosoftIdentityWebApi(builder.Configuration)
3 .EnableTokenAcquisitionToCallDownstreamApi()
4 .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
5 .AddInMemoryTokenCaches();
6public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation)
7{
8 ConfigureHandler(
9 authorizedUrls: new[]
10 {
11 "https://localhost:7069"
12 },
13 scopes: new[]
14 {
15 "api://[redacted]/.default"
16 });
17}
18builder.Services.AddMsalAuthentication(options =>
19{
20 builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
21 options.ProviderOptions.DefaultAccessTokenScopes.Add("api://[redacted]/.default");
22 options.ProviderOptions.LoginMode = "redirect";
23}
24
- Set up named HTTP client on the Blazor client with custom message handler:
1builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
2 .AddMicrosoftIdentityWebApi(builder.Configuration)
3 .EnableTokenAcquisitionToCallDownstreamApi()
4 .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
5 .AddInMemoryTokenCaches();
6public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation)
7{
8 ConfigureHandler(
9 authorizedUrls: new[]
10 {
11 "https://localhost:7069"
12 },
13 scopes: new[]
14 {
15 "api://[redacted]/.default"
16 });
17}
18builder.Services.AddMsalAuthentication(options =>
19{
20 builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
21 options.ProviderOptions.DefaultAccessTokenScopes.Add("api://[redacted]/.default");
22 options.ProviderOptions.LoginMode = "redirect";
23}
24var baseAddress = builder.Configuration["PublicApiUrl"];
25
26builder.Services.AddHttpClient("PublicApi", client =>
27{
28 client.BaseAddress = new Uri(baseAddress);
29}).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
30
31builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("PublicApi"));
32builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
33
- I can authenticate as an AAD user to the Blazor client.
- I can access protected endpoints (using policy-based authorization) hosted on the server which don't have a dependency on MS Graph.
Following this article's guidance about incremental consent, specifically the "Static permissions" section, I would assume granting admin consent for Graph on the server's app registration would suffice?
All of the documentation showing Blazor WASM with a protected API calling a protected API (Graph) assume the Blazor client is also hosted by the API server. Is it even possible to use on-behalf-of flow in my case? If it was hosted I could see the API calling the Blazor navigation subsystem to perform an incremental consent redirect but when they're separated, I can only imagine the static permissions is the way to go.
Is it necessary to set the
DefaultAccessTokenScopes
in the client?
ANSWER
Answered 2022-Mar-10 at 22:30The issue here is use of the AddMicrosoftGraph
method when the API application is being built.
The GraphServiceClient
created by AddMicrosoftGraph
will have default access to delegated permissions which are assigned to users as opposed to application permissions which are assigned to applications. This is why the MsalUiRequiredException is being thrown which is usually resolved by prompting the user to login.
You can read more about delegated vs application permissions here.
What you can do instead is use the AddMicrosoftGraphAppOnly
method to create a GraphServiceClient
that will use credentials specific to your API to retrieve the relevant data needed from the Microsoft Graph API.
1builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
2 .AddMicrosoftIdentityWebApi(builder.Configuration)
3 .EnableTokenAcquisitionToCallDownstreamApi()
4 .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
5 .AddInMemoryTokenCaches();
6public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation)
7{
8 ConfigureHandler(
9 authorizedUrls: new[]
10 {
11 "https://localhost:7069"
12 },
13 scopes: new[]
14 {
15 "api://[redacted]/.default"
16 });
17}
18builder.Services.AddMsalAuthentication(options =>
19{
20 builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
21 options.ProviderOptions.DefaultAccessTokenScopes.Add("api://[redacted]/.default");
22 options.ProviderOptions.LoginMode = "redirect";
23}
24var baseAddress = builder.Configuration["PublicApiUrl"];
25
26builder.Services.AddHttpClient("PublicApi", client =>
27{
28 client.BaseAddress = new Uri(baseAddress);
29}).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
30
31builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("PublicApi"));
32builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
33builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
34 .AddMicrosoftIdentityWebApi(builder.Configuration)
35 .EnableTokenAcquisitionToCallDownstreamApi()
36 .AddMicrosoftGraphAppOnly(
37 authenticationProvider => new GraphServiceClient(authenticationProvider))
38 .AddInMemoryTokenCaches();
39
So long as you have the relevant settings and secrets provided in the AzureAd section of your appsettings.json
file the GraphServiceClient
injected into your application should now be able to access the data you need.
You can read more about app configuration with the AzureAd settings in your appsettings.json
file here.
QUESTION
How to change the http client used by pouchDB?
Asked 2022-Jan-14 at 14:53I am using PouchDB and CouchDB in an ionic application. While I can successfully sync local and remote databases on Chrome and Android, I get unauthorized error on Safari / iOS when I run the sync command. Below is a simplified version of my database service provider.
1import PouchDB from 'pouchdb';
2import PouchDBAuthentication from 'pouchdb-authentication';
3
4@Injectable()
5export class CouchDbServiceProvider {
6 private db: any;
7 private remote: any;
8 constructor() {
9 PouchDB.plugin(PouchDBAuthentication);
10 this.db = new PouchDB('localdb', {skip_setup: true});
11 }
12 ...
13 login(credentials) {
14 let couchDBurl = 'URL of my couchDB database';
15 this.remote = new PouchDB(couchDBurl);
16 this.remote.logIn(credentials.username, credentials.password, function (err, response) {
17 if (err) { concole.log('login error') }
18 else {
19 let options = { live: true, retry: true, continuous: true };
20 this.db.sync(this.remote, options).on('error', (err_) => { console.log('sync error')});
21 }
22 })
23 }
24 ...
25}
26
In the code above, this.remote.logIn(...)
is successful but this.db.sync(...)
fails. I have checked the requests via the network tab of developer tools and I believe the issue is that the cookie that's retruned in the response header of this.remote.logIn(...)
is not used by the subsequent calls (thus the unauthorized error). The issue is fixed once third-party cookies are enabled on Safari, which is not an option on iOS.
How can I fix this problem?
One potential solution I'm considering is overriding fetch
to use native http client (i.e., an instance of HTTP
from @ionic-native/http
). It seems modifying http clients is a possibility (e.g., according to this conversation) but I'm not sure how to achieve that.
ANSWER
Answered 2022-Jan-11 at 00:41Changing the HTTP plumbing sounds like a really bad idea - time cost, mainly - unless you just absolutely have to use sessions/cookies...If you don't, read on.
as noted here regarding pouchDB Security, I tried using pouchdb-authentication when it was actively maintained and went another route due to multiple issues (I don't recall specifics, it was 6 years ago).
Do note the last commit to pouchdb-authentication seems to be 3 years ago. Although inactivity is not an negative indicator on the surface - a project may have simply reached a solid conclusion - installing pouchdb-authentication yields this
1import PouchDB from 'pouchdb';
2import PouchDBAuthentication from 'pouchdb-authentication';
3
4@Injectable()
5export class CouchDbServiceProvider {
6 private db: any;
7 private remote: any;
8 constructor() {
9 PouchDB.plugin(PouchDBAuthentication);
10 this.db = new PouchDB('localdb', {skip_setup: true});
11 }
12 ...
13 login(credentials) {
14 let couchDBurl = 'URL of my couchDB database';
15 this.remote = new PouchDB(couchDBurl);
16 this.remote.logIn(credentials.username, credentials.password, function (err, response) {
17 if (err) { concole.log('login error') }
18 else {
19 let options = { live: true, retry: true, continuous: true };
20 this.db.sync(this.remote, options).on('error', (err_) => { console.log('sync error')});
21 }
22 })
23 }
24 ...
25}
26found 6 vulnerabilities (2 moderate, 3 high, 1 critical)
27
That plus the lack of love given to plugin over the last few years makes for a dangerous technical debt to add for a new project.
If possible simply send credentials using the auth
option when creating (or opening) a remote database, e.g.
1import PouchDB from 'pouchdb';
2import PouchDBAuthentication from 'pouchdb-authentication';
3
4@Injectable()
5export class CouchDbServiceProvider {
6 private db: any;
7 private remote: any;
8 constructor() {
9 PouchDB.plugin(PouchDBAuthentication);
10 this.db = new PouchDB('localdb', {skip_setup: true});
11 }
12 ...
13 login(credentials) {
14 let couchDBurl = 'URL of my couchDB database';
15 this.remote = new PouchDB(couchDBurl);
16 this.remote.logIn(credentials.username, credentials.password, function (err, response) {
17 if (err) { concole.log('login error') }
18 else {
19 let options = { live: true, retry: true, continuous: true };
20 this.db.sync(this.remote, options).on('error', (err_) => { console.log('sync error')});
21 }
22 })
23 }
24 ...
25}
26found 6 vulnerabilities (2 moderate, 3 high, 1 critical)
27const credentials = { username: 'foo', passwd: 'bar' };
28this.remote = new PouchDB(couchDBurl, { auth: credentials });
29
I don't recall why but I wrote code that is in essence what follows below, and have reused it ad nauseum because it just works with the fetch
option
1import PouchDB from 'pouchdb';
2import PouchDBAuthentication from 'pouchdb-authentication';
3
4@Injectable()
5export class CouchDbServiceProvider {
6 private db: any;
7 private remote: any;
8 constructor() {
9 PouchDB.plugin(PouchDBAuthentication);
10 this.db = new PouchDB('localdb', {skip_setup: true});
11 }
12 ...
13 login(credentials) {
14 let couchDBurl = 'URL of my couchDB database';
15 this.remote = new PouchDB(couchDBurl);
16 this.remote.logIn(credentials.username, credentials.password, function (err, response) {
17 if (err) { concole.log('login error') }
18 else {
19 let options = { live: true, retry: true, continuous: true };
20 this.db.sync(this.remote, options).on('error', (err_) => { console.log('sync error')});
21 }
22 })
23 }
24 ...
25}
26found 6 vulnerabilities (2 moderate, 3 high, 1 critical)
27const credentials = { username: 'foo', passwd: 'bar' };
28this.remote = new PouchDB(couchDBurl, { auth: credentials });
29const user = { name: 'foo', pass: 'bar' };
30const options = { fetch: function (url, opts) {
31 opts.headers.set('Authorization', 'Basic ' + window.btoa(user.name+':'+user.pass));
32 return PouchDB.fetch(url, opts);
33 }
34};
35
36this.remote = new PouchDB(couchDBurl, options);
37
I believe I chose this approach due to the nature of my authentication workflow discussed in the first link of this answer.
QUESTION
AWS Object Lambda Access Point timing out when writing response when run from within VPC
Asked 2022-Jan-13 at 13:39I've got an AWS Object Lambda Access Point. (These are sort of like a proxy lambda function which can intercept S3 requests and transform them.) It runs fine when not run inside a VPC (so I think IAM is fine). A later iteration will want to access private resources so I want it running inside a VPC.
The flow of one of these lambdas (at least when transforming a GET request) is:
- Get invoked
- Download the object that was requested using a HTTP client (you get a pre-signed URL to grant access (
getObjectContext.inputS3Url
in the payload)) - Do your transformation
- Write the result using
s3.Client.WriteGetObjectResponse
It's the last step that isn't working for me.
In my VPC I've added a gateway endpoint for S3 (for S3 either gateway or interface endpoints are supported; gateways are free. This works fine to fetch the object (step 2), I can download the object and work on it. I think that download happens through the gateway endpoint. So far so good.
But after doing the processing it times out when trying to write the response (step 4). In the logs it looks like this:
1POST /WriteGetObjectResponse?x-id=WriteGetObjectResponse HTTP/1.1
2Host: io-cell002.s3-object-lambda.eu-west-2.amazonaws.com
3...
4DEBUG retrying request s3-object-lambda/WriteGetObjectResponse, attempt 2
5...
6time="2022-01-02T22:25:39Z" level=error msg="Error writing to S3: operation error S3: WriteGetObjectResponse, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded"
7
Which smells to me like I can't connect to the endpoint at a network level.
I tried adding an interface endpoint for Lambda (this is the only option returned - see screenshot down below), but that doesn't seem to make any difference. Perhaps this doesn't cover s3-object-lambda.<region>.amazonaws.com
? Or maybe it wasn't being used - not sure how to tell that.
I also tried adding an interface endpoint for S3, and removing the gateway one referenced above. This caused the Lambda to not be able to even retrieve the input object from S3 in step 2, with an i/o timeout
.
(What does also work is adding a NAT gateway to the VPC, but I'd rather avoid the cost of this and AFAICT it shouldn't be necessary.)
Any help getting this working with a VPC / without NAT would be gratefully received!
ANSWER
Answered 2022-Jan-13 at 13:39The short answer is: You can run your object lambda function in a VPC as long as you allow it to route to s3-object-lambda..amazonaws.com through the internet, e.g. through a NAT gateway. You were on the right track and basically figured it out in your question already.
The S3 gateway interface endpoint is necessary to enable download of the input object.
When writing the result, the request goes to s3-object-lambda
, which is technically a different service than S3 (at least on network level). AWS currently doesn't provide an interface endpoint for s3-object-lambda and the S3 gateway endpoint doesn't cover it either (which can be verified by comparing the IP address WriteGetObjectResponse
request goes to and the routes created by the gateway endpoint).
So the only way is to route WriteGetObjectResponse
requests via opened access to the internet. For future reference, one way to set this up is with a NAT gateway. Quoting AWS docs:
- The NAT gateway must be in a public subnet with a route table that routes internet traffic to an internet gateway.
- Your instance must be in a private subnet with a route table that routes internet traffic to the NAT gateway.
- Check that there are no other route table entries that route all or part of the internet traffic to another device instead of the NAT gateway.
In other words:
- Provision a public NAT Gateway in a public subnet and allocate it an elastic IP
- Make sure the public subnet has an internet gateway and the default route (
0.0.0.0/0
) points to it. - Set up a default route (
0.0.0.0/0
) from the subnet hosting your lambda and point it to the NAT Gateway.
You're right that a NAT Gateway is priced by the hour, unfortunately, and you need one per subnet.
In theory, you could at least limit the egress with a security group to IP addresses of the s3-object-lambda
service, but I'm not aware these IP ranges are published anywhere.
QUESTION
How can I prevent java.net.http.HttpClient from making an upgrade request?
Asked 2022-Jan-12 at 11:05Upon making a call from a Java project to a Python rest API, I am met with an error that states "Unsupported upgrade request."
I'm making a simple GET request from Java, to the endpoint written in Python, and at some point Java decides it wants to request to upgrade the connection to a websocket. My issue is, I never reference websockets in my Java project whatsoever, and when I debug and look at the value of headers in the request, it does not show any headers at all, but at some point before it hits the network it decides it wants to do an upgrade request. I haven't sniffed my own traffic to confirm the existence of the header yet; but the problem does not exist when I use OKHTTP instead of java.net.http.HttpClient
This code results in the Unsupported Upgrade Request error returned by the Python API.
1HttpRequest request = HttpRequest.newBuilder()
2 .uri(URI.create("http://127.0.0.1:8000/"))
3 .method("GET", HttpRequest.BodyPublishers.noBody())
4 .build();
5HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
6System.out.println(response.body());
7
This code works just fine
1HttpRequest request = HttpRequest.newBuilder()
2 .uri(URI.create("http://127.0.0.1:8000/"))
3 .method("GET", HttpRequest.BodyPublishers.noBody())
4 .build();
5HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
6System.out.println(response.body());
7OkHttpClient client = new OkHttpClient();
8
9Request request = new Request.Builder()
10 .url("http://127.0.0.1:8000/")
11 .get()
12 .build();
13
14Response response = client.newCall(request).execute();
15
Is anyone familiar with disabling the upgrade request in the native HTTP client java provides?
ANSWER
Answered 2022-Jan-12 at 11:05Requests shouldn't be upgraded to websocket - but the default for the HttpClient is to request an upgrade to HTTP/2.
If you don't want the HttpClient to request an upgrade to HTTP/2 you can set the version in the request to Version.HTTP_1_1
, or set the default version in the HttpClient to Version.HTTP_1_1
.
See: HttpRequest.Builder::version or HttpClient.Builder::version
QUESTION
Nested logic app retries - parent throwing 504
Asked 2021-Nov-29 at 16:47I have a nested logic app which takes some time for 4 retries in case of a failure. According to the documentation, the default HTTP timeout is 100 seconds. I'm able to increase the HTTP client timeout value in my code which triggers the parent logic app, but in case of a failure in the child logic app, it is retried 4 times and takes much longer. Meanwhile, the parent logic app responds with a 504 (gateway timeout). There are some more actions to take care of after the child logic app returns a response, so I can't make it asynchronous and return 202 to the code trigger. Is there a way to increase the timeout in the nested logic app without making it async?
E.g. - My nested logic app retried 4 times and failed after 4 minutes
However, my code already receives a response of 504 after 2 minutes 9 seconds of triggering the parent logic app
The HTTP client which triggers the parent logic app has a timeout of 20 minutes. I verified that this timeout value is working, because without it, we were receiving the timeout reponse in 1 minute 40 seconds (100 seconds), which is default HTTP trigger timeout. I'm under the impression that if the nested logic app also doesn't respond within 100 seconds of being triggered, the parent throws a timeout because it didn't receive a response. Is there a way to work around this?
ANSWER
Answered 2021-Sep-16 at 12:03It might not be possible without asynchronous patterns or async calls but One of the workarounds can be using HTTP+Webhooks where you can set the timeout value.
You can research this more from HERE.
QUESTION
Obtaining a scoped service in a custom delegating handler in Blazor Server
Asked 2021-Nov-23 at 10:00I'm attempting to set up our Blazor Server application to use a custom delegating handler that will attach a bearer token to all outgoing requests to our APIs. The delegating handler uses a token service that handles the retrieval of the token as well as the process for refreshing it if it's expired. The code looks like this:
1public class HttpAuthorizationHandler : DelegatingHandler
2{
3 private ITokenService _tokenService { get; set; }
4
5 public HttpAuthorizationHandler(ITokenService tokenService)
6 {
7 _tokenService = tokenService;
8 }
9
10 protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11 {
12 var token = await _tokenService.TryGetToken();
13
14 request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
15
16 return await base.SendAsync(request, cancellationToken);
17 }
18}
19
I've registered the token service as a scoped service in Startup.cs, with the understanding that the same instance of the service will stay alive in the DI container for the lifetime of the request. If I'm understanding that correctly, this means that I can assign the token values to the service in my App.razor page thusly and pick them up with any subsequent call to the token service:
1public class HttpAuthorizationHandler : DelegatingHandler
2{
3 private ITokenService _tokenService { get; set; }
4
5 public HttpAuthorizationHandler(ITokenService tokenService)
6 {
7 _tokenService = tokenService;
8 }
9
10 protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11 {
12 var token = await _tokenService.TryGetToken();
13
14 request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
15
16 return await base.SendAsync(request, cancellationToken);
17 }
18}
19@code {
20 [Parameter]
21 public string AccessToken { get; set; }
22 [Parameter]
23 public string RefreshToken { get; set; }
24
25 [Inject] private ITokenService _tokenService { get; set; }
26
27 protected override void OnInitialized()
28 {
29 _tokenService.AccessToken = AccessToken.NullIfEmpty() ?? _tokenService.AccessToken;
30 _tokenService.RefreshToken = RefreshToken.NullIfEmpty() ?? _tokenService.RefreshToken;
31 }
32
This seems to work just fine for everything except the delegating handler - the values turn up just fine in a scoped configuration for any other request made to the token service, but when injecting the token service into the delegating handler the token value always comes up null.
Unsurprisingly, if I register the service as a singleton, the values propagate to the delegating handler just fine, but that's obviously not a solution. For some reason the DI scope for the handler appears to be different from that of the rest of the application, and I have no idea why. Any ideas? Appreciate the help in advance.
EDIT
With some help from Simply Ged and Andrew Lock, I was able to get quite a bit closer using the IHttpContextAccessor to grab the instance of the ITokenService associated with the request. I was ecstatic when I first logged in and saw it working, but once my excitement wore down I noticed it would stop working correctly a short time later. It turns out that this method only synchronizes the instances on the initial request - after that time, the DI's awkward pipeline management kicks in and maintains the same service instance for every subsequent request, and they all fall out of alignment again. Here's an example of the debug logs from my testing:
1public class HttpAuthorizationHandler : DelegatingHandler
2{
3 private ITokenService _tokenService { get; set; }
4
5 public HttpAuthorizationHandler(ITokenService tokenService)
6 {
7 _tokenService = tokenService;
8 }
9
10 protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11 {
12 var token = await _tokenService.TryGetToken();
13
14 request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
15
16 return await base.SendAsync(request, cancellationToken);
17 }
18}
19@code {
20 [Parameter]
21 public string AccessToken { get; set; }
22 [Parameter]
23 public string RefreshToken { get; set; }
24
25 [Inject] private ITokenService _tokenService { get; set; }
26
27 protected override void OnInitialized()
28 {
29 _tokenService.AccessToken = AccessToken.NullIfEmpty() ?? _tokenService.AccessToken;
30 _tokenService.RefreshToken = RefreshToken.NullIfEmpty() ?? _tokenService.RefreshToken;
31 }
32App.razor: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
33AuthStateProvider: e12f6c80-5cee-44a0-a8a0-d6b783057339 ├ Initial request - all IDs match
34AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
35AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
36AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
37AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
38AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ├ Clicking around a bunch
39AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
40AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
41AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
42App.razor: cab70e54-1907-462d-8918-dbd771fabe76 ┐
43AuthStateProvider: cab70e54-1907-462d-8918-dbd771fabe76 ├ Load a new page - ah, crap...
44AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
45AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
46AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
47App.razor: 3c55ff9c-5511-40a1-9fb8-cd00f9fc11c6 │
48AuthStateProvider: 3c55ff9c-5511-40a1-9fb8-cd00f9fc11c6 ├ Dang it all to heck
49AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
50AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
51AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
52
Andrew Lock's excellent blog post on this topic goes into some very useful detail on why this kind of thing happens - apparently the dependency injection manages the lifetime of the http request pipelines separately from the lifetimes of the http clients themselves, so you can't rely on the pipeline being a part of the same context as the request. He proposes the use of the IHttpContextAccessor as a solution to this problem, but it does not appear to solve the problem in this particular case. I suspect his blog post refers to an earlier version of ASP.NET Core, the behavior of which is not applicable to Blazor apps. In fact, as Alex pointed out, Microsoft specifically advises against using the IHttpContextAccessor for shared state.
ANSWER
Answered 2021-Nov-03 at 00:57As per the comment, there is an excellent blog post by Andrew Lock that talks about this.
TL;DR
You'll need to use IHttpContextAccessor
to resolve the scoped service and get the instance you want.
Example
All credit for this example should be given to Andrew Lock and his blog post, but for quick reference readers of this question, you need to inject the IHttpContextAccessor
into your DelegatingHandler
and use that to access the correct scoped service.
1public class HttpAuthorizationHandler : DelegatingHandler
2{
3 private ITokenService _tokenService { get; set; }
4
5 public HttpAuthorizationHandler(ITokenService tokenService)
6 {
7 _tokenService = tokenService;
8 }
9
10 protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11 {
12 var token = await _tokenService.TryGetToken();
13
14 request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
15
16 return await base.SendAsync(request, cancellationToken);
17 }
18}
19@code {
20 [Parameter]
21 public string AccessToken { get; set; }
22 [Parameter]
23 public string RefreshToken { get; set; }
24
25 [Inject] private ITokenService _tokenService { get; set; }
26
27 protected override void OnInitialized()
28 {
29 _tokenService.AccessToken = AccessToken.NullIfEmpty() ?? _tokenService.AccessToken;
30 _tokenService.RefreshToken = RefreshToken.NullIfEmpty() ?? _tokenService.RefreshToken;
31 }
32App.razor: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
33AuthStateProvider: e12f6c80-5cee-44a0-a8a0-d6b783057339 ├ Initial request - all IDs match
34AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
35AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
36AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
37AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
38AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ├ Clicking around a bunch
39AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
40AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
41AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
42App.razor: cab70e54-1907-462d-8918-dbd771fabe76 ┐
43AuthStateProvider: cab70e54-1907-462d-8918-dbd771fabe76 ├ Load a new page - ah, crap...
44AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
45AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┐
46AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
47App.razor: 3c55ff9c-5511-40a1-9fb8-cd00f9fc11c6 │
48AuthStateProvider: 3c55ff9c-5511-40a1-9fb8-cd00f9fc11c6 ├ Dang it all to heck
49AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
50AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 │
51AuthHandler: e12f6c80-5cee-44a0-a8a0-d6b783057339 ┘
52public class ScopedMessageHander: DelegatingHandler
53{
54 private readonly ILogger<ScopedMessageHander> _logger;
55 private readonly IHttpContextAccessor _accessor;
56
57 public ScopedMessageHander(ILogger<ScopedMessageHander> logger, IHttpContextAccessor accessor)
58 {
59 _logger = logger;
60 _accessor = accessor;
61 }
62
63 protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
64 {
65 // The HttpContext will be null if used outside a request context, so check in practice!
66 var httpConext = _accessor.HttpContext;
67
68 // retrieve the service from the Request DI Scope
69 var service = _accessor.HttpContext.RequestServices.GetRequiredService<ScopedService>();
70
71 ...
72 }
73}
74
QUESTION
How to send PHP input stream data using curl?
Asked 2021-Nov-18 at 08:42On my local machine, I want to use cURL as an HTTP client like so:
1curl -X POST -F 'email=derp@example.com' -F 'password=blink182' http://example.com
2
The above curl statement uses the HTTP POST method which can be retrieved in PHP like so:
1curl -X POST -F 'email=derp@example.com' -F 'password=blink182' http://example.com
2echo $_POST['email']; // derp@example.com
3echo $_POST['password']; // blink182
4
However, what I really wanted is the data from the PHP input stream php:://input
and not from the POST method $_POST
.
The PHP input stream can be retrieved in PHP like so:
1curl -X POST -F 'email=derp@example.com' -F 'password=blink182' http://example.com
2echo $_POST['email']; // derp@example.com
3echo $_POST['password']; // blink182
4$input = json_decode( file_get_contents( 'php://input' ) );
5echo $input->email; // derp@example.com
6echo $input->password; // blink182
7
Which brings me to my question, how to send PHP input stream data using curl?
ANSWER
Answered 2021-Nov-18 at 08:42From the PHP website:
php://input
is a read-only stream that allows you to read raw data from the request body.php://input
is not available withenctype="multipart/form-data"
.
So if you specify the Content-Type
as application/json
using -H "Content-Type: application/json"
you ought to get it in the input stream
complete example:
1curl -X POST -F 'email=derp@example.com' -F 'password=blink182' http://example.com
2echo $_POST['email']; // derp@example.com
3echo $_POST['password']; // blink182
4$input = json_decode( file_get_contents( 'php://input' ) );
5echo $input->email; // derp@example.com
6echo $input->password; // blink182
7curl -X POST https://reqbin.com/echo/post/json
8 -H 'Content-Type: application/json'
9 -d '{"email":"derp@example.com","password":"blink182"}'
10
QUESTION
Laravel - Correct way to catch cURL exceception
Asked 2021-Oct-29 at 13:25I am building a simple REST API package using cURL and would like to catch an error and then return a view. I am able to throw an error if I dd($e) but if I try and return a view it just continues with the code after the catch function. Shouldn't PHP kill the process and just go to the login view?
1try{
2 $response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
3 'username' => $this->username,
4 'password' => $this->password
5 ]);
6
7} catch(\Illuminate\Http\Client\ConnectionException $e) {
8 return view('auth.login');
9}
10
If I get a cURL timeout exception I just want to go back to the login page for now. If I put in a bogus IP address obviously it will timeout after 2 seconds, which is what I am testing.
Using Laravel Http client, how can I catch that error and display the auth login view?
ANSWER
Answered 2021-Oct-23 at 09:47Could you try this please?
1try{
2 $response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
3 'username' => $this->username,
4 'password' => $this->password
5 ]);
6
7} catch(\Illuminate\Http\Client\ConnectionException $e) {
8 return view('auth.login');
9}
10try {
11 $response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
12 'username' => $this->username,
13 'password' => $this->password
14 ]);
15} catch(\Illuminate\Http\Client\ConnectionException $e) {
16 return view('auth.login')->with('errorMessage', $e->getMessage());
17}
18
And you can show the error on the frontend, like below;
1try{
2 $response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
3 'username' => $this->username,
4 'password' => $this->password
5 ]);
6
7} catch(\Illuminate\Http\Client\ConnectionException $e) {
8 return view('auth.login');
9}
10try {
11 $response = Http::timeout(2)->asForm()->post('https://' . $this->ip_address, [
12 'username' => $this->username,
13 'password' => $this->password
14 ]);
15} catch(\Illuminate\Http\Client\ConnectionException $e) {
16 return view('auth.login')->with('errorMessage', $e->getMessage());
17}
18@if(!empty($errorMessage))
19 <div class="alert alert-danger"> {{ $errorMessage }}</div>
20@endif
21
QUESTION
OkHttp client authentication failing to validate server and send client certificate in the same request
Asked 2021-Oct-19 at 08:20EDIT I'm able to build my OkHttp client to where it includes both the client cert in the Client.SSLContext.KeyManager, and the trusted certs in the Client.SSLContext.TrustManager
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30
However, my client still isn't sending my client certificate (server cert is validated through the trust store successfully). Getting this in the ssl debug logs
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30No X.509 certificate for client authentication, use empty Certificate message instead
31
Here's what my SSLContext looks like on the HttpClient. Seems like that should send the client cert named "cureskeystore" in the request?
keystore.jks
is built with the following commands
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30No X.509 certificate for client authentication, use empty Certificate message instead
31openssl pkcs12 -export \
32 -name curesKeyStore \
33 -in clientCert.crt \
34 -inkey privateKey.pem \
35 -certfile clientCert.crt \
36 -out chain.p12 \
37 -passout pass:${STORE_PASSWORD}
38
39keytool -importkeystore \
40 -srckeystore chain.p12 \
41 -srcstoretype pkcs12 \
42 -destkeystore keystore.jks \
43 -deststoretype pkcs12 \
44 -storepass ${STORE_PASSWORD} \
45 -srcstorepass ${STORE_PASSWORD} > /dev/null 2>&1
46
I have also tried creating a store with the client cert + -CAfile
with the root and intermediate certs:
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30No X.509 certificate for client authentication, use empty Certificate message instead
31openssl pkcs12 -export \
32 -name curesKeyStore \
33 -in clientCert.crt \
34 -inkey privateKey.pem \
35 -certfile clientCert.crt \
36 -out chain.p12 \
37 -passout pass:${STORE_PASSWORD}
38
39keytool -importkeystore \
40 -srckeystore chain.p12 \
41 -srcstoretype pkcs12 \
42 -destkeystore keystore.jks \
43 -deststoretype pkcs12 \
44 -storepass ${STORE_PASSWORD} \
45 -srcstorepass ${STORE_PASSWORD} > /dev/null 2>&1
46# client cert with CAcerts included
47openssl pkcs12 -export -chain \
48 -in clientCert.crt \
49 -inkey privateKey.pem \
50 -out keystore.p12 \
51 -name p12KeyStore \
52 -CAfile caCerts.crt \
53 -caname root \
54 -passout pass:${STORE_PASSWORD}
55
56keytool -importkeystore \
57 -srcstoretype PKCS12 \
58 -destkeystore keystore.jks \
59 -srckeystore keystore.p12 \
60 -alias p12KeyStore \
61 -storepass ${STORE_PASSWORD} \
62 -srcstorepass ${STORE_PASSWORD}
63
Another possible issue is the CertificateRequest not matching my client certificate.
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30No X.509 certificate for client authentication, use empty Certificate message instead
31openssl pkcs12 -export \
32 -name curesKeyStore \
33 -in clientCert.crt \
34 -inkey privateKey.pem \
35 -certfile clientCert.crt \
36 -out chain.p12 \
37 -passout pass:${STORE_PASSWORD}
38
39keytool -importkeystore \
40 -srckeystore chain.p12 \
41 -srcstoretype pkcs12 \
42 -destkeystore keystore.jks \
43 -deststoretype pkcs12 \
44 -storepass ${STORE_PASSWORD} \
45 -srcstorepass ${STORE_PASSWORD} > /dev/null 2>&1
46# client cert with CAcerts included
47openssl pkcs12 -export -chain \
48 -in clientCert.crt \
49 -inkey privateKey.pem \
50 -out keystore.p12 \
51 -name p12KeyStore \
52 -CAfile caCerts.crt \
53 -caname root \
54 -passout pass:${STORE_PASSWORD}
55
56keytool -importkeystore \
57 -srcstoretype PKCS12 \
58 -destkeystore keystore.jks \
59 -srckeystore keystore.p12 \
60 -alias p12KeyStore \
61 -storepass ${STORE_PASSWORD} \
62 -srcstorepass ${STORE_PASSWORD}
63javax.net.ssl|DEBUG|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:671|Consuming CertificateRequest handshake message (
64"CertificateRequest": {
65 "certificate types": [ecdsa_sign, rsa_sign, dss_sign]
66 "supported signature algorithms": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
67 "certificate authorities": [redacted, but does not include Entrust]
68}
69)
70javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
71javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp256r1_sha256
72javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
73javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp384r1_sha384
74javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
75javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp521r1_sha512
76javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
77javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha256
78javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
79javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha384
80javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
81javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha512
82javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
83javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha256
84javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
85javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha384
86javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
87javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha512
88javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
89javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha256
90javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
91javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha384
92javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
93javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha512
94javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
95javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha256
96javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
97javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha224
98javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
99javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_sha224
100javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
101javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha224
102javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
103javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha1
104javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
105javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha1
106javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
107javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha1
108javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:774|No available authentication scheme
109
My certificate's signing algorithm is SHA256withRSA
. Is that not the same as rsa_pkcs1_sha256
?
Also, my client certificate is signed by Entrust, which is not listed in the certificate authorities for the server's CertificateRequest.
EDIT: I made some requests to a different HTTPS server that does not include certificate authorities
in its CertificateRequest to the client. I verified that SSL can find the expected client certificate and sends it back to the server as expected. So it seems like this is an issue with the server request not including my CA in their list of accepted certificate authorities
. Reaching out to the server to request an update.
ANSWER
Answered 2021-Oct-19 at 08:20Okay; it has developed your problem is that when the server requests your client-cert/auth, it specifies a CA list that doesn't include the CA(s?) used by your cert-and-chain, even though when presented with your cert-and-chain the server accepts it. After commenting about writing a wrapper KeyManager, I realized it would be easy enough to test, and the following example works for me to send a client cert different from what the server asked for. I used SSLSocket directly for simplicity, but anything (like OkHttp) using the same SSLContext or SSLSocketFactory should work. Tested in 8u301 (but I can check some others if you want) against OpenSSL commandline, which lets me request client cert for CA X but when I submit a cert from CA Y it only logs the verification error without aborting the connection.
1// Create keyManagerFactory with keystore.jks
2KeyStore clientStore = KeyStore.getInstance(KeyStore.getDefaultType());
3clientStore.load(new FileInputStream(new File("keystore.jks")), storePassword.toCharArray());
4
5KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
6keyManagerFactory.init(clientStore, storePassword.toCharArray());
7KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
8
9// Create trustManagerFactory with default cacerts truststore
10TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
11 TrustManagerFactory.getDefaultAlgorithm());
12trustManagerFactory.init((KeyStore) null);
13trustManagers = trustManagerFactory.getTrustManagers();
14if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
15 throw new IllegalStateException("Unexpected default trust managers:"
16 + Arrays.toString(trustManagers));
17 }
18trustManager = trustManagers[0];
19
20// Create sslContext from keyManagers (from custom keystore with client key) and default trustManagers
21sslContext = SSLContext.getInstance("TLS");
22sslContext.init(keyManagers, trustManagers, null);
23sslSocketFactory = sslContext.getSocketFactory();
24defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
25
26okClient = new OkHttpClient
27 .Builder()
28 .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager)
29 .build();
30No X.509 certificate for client authentication, use empty Certificate message instead
31openssl pkcs12 -export \
32 -name curesKeyStore \
33 -in clientCert.crt \
34 -inkey privateKey.pem \
35 -certfile clientCert.crt \
36 -out chain.p12 \
37 -passout pass:${STORE_PASSWORD}
38
39keytool -importkeystore \
40 -srckeystore chain.p12 \
41 -srcstoretype pkcs12 \
42 -destkeystore keystore.jks \
43 -deststoretype pkcs12 \
44 -storepass ${STORE_PASSWORD} \
45 -srcstorepass ${STORE_PASSWORD} > /dev/null 2>&1
46# client cert with CAcerts included
47openssl pkcs12 -export -chain \
48 -in clientCert.crt \
49 -inkey privateKey.pem \
50 -out keystore.p12 \
51 -name p12KeyStore \
52 -CAfile caCerts.crt \
53 -caname root \
54 -passout pass:${STORE_PASSWORD}
55
56keytool -importkeystore \
57 -srcstoretype PKCS12 \
58 -destkeystore keystore.jks \
59 -srckeystore keystore.p12 \
60 -alias p12KeyStore \
61 -storepass ${STORE_PASSWORD} \
62 -srcstorepass ${STORE_PASSWORD}
63javax.net.ssl|DEBUG|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:671|Consuming CertificateRequest handshake message (
64"CertificateRequest": {
65 "certificate types": [ecdsa_sign, rsa_sign, dss_sign]
66 "supported signature algorithms": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
67 "certificate authorities": [redacted, but does not include Entrust]
68}
69)
70javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
71javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp256r1_sha256
72javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
73javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp384r1_sha384
74javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for EC
75javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_secp521r1_sha512
76javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
77javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha256
78javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
79javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha384
80javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
81javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_rsae_sha512
82javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
83javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha256
84javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
85javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.619 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha384
86javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSASSA-PSS
87javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pss_pss_sha512
88javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
89javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha256
90javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
91javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha384
92javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
93javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha512
94javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
95javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha256
96javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
97javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha224
98javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
99javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_sha224
100javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
101javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha224
102javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|X509Authentication.java:213|No X.509 cert selected for EC
103javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.620 EDT|CertificateRequest.java:764|Unavailable authentication scheme: ecdsa_sha1
104javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
105javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: rsa_pkcs1_sha1
106javax.net.ssl|ALL|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|X509Authentication.java:213|No X.509 cert selected for DSA
107javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:764|Unavailable authentication scheme: dsa_sha1
108javax.net.ssl|WARNING|24|XNIO-1 task-1|2021-10-18 11:07:18.621 EDT|CertificateRequest.java:774|No available authentication scheme
109public class SO69577136KeyManagerIgnoreCAs {
110 public static void main (String[] args) throws Exception {
111 // keystore.p12 pw truststore.p12 pw host port [Y: wrap KM to ignore issuers]
112 KeyStore st = KeyStore.getInstance("PKCS12");
113 try( InputStream is = new FileInputStream(args[0]) ){ st.load(is,args[1].toCharArray()); }
114 KeyManagerFactory kf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
115 kf.init(st, args[1].toCharArray());
116 KeyManager[] km = kf.getKeyManagers();
117 try( InputStream is = new FileInputStream(args[2]) ){ st.load(is,args[3].toCharArray()); }
118 TrustManagerFactory tf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
119 tf.init(st);
120 TrustManager[] tm = tf.getTrustManagers();
121
122 if( args.length>6 && args[6].startsWith("Y") ){
123 X509ExtendedKeyManager orig = (X509ExtendedKeyManager)km[0]; // exception if wrong type
124 km[0] = new X509ExtendedKeyManager(){
125
126 @Override
127 public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
128 return orig.chooseClientAlias(keyType, null, socket);
129 }
130
131 @Override
132 public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
133 // not implemented
134 return null;
135 }
136
137 @Override
138 public X509Certificate[] getCertificateChain(String alias) {
139 return orig.getCertificateChain(alias);
140 }
141
142 @Override
143 public String[] getClientAliases(String keyType, Principal[] issuers) {
144 // shouldn't actually be used AFAICT but just in case
145 return orig.getClientAliases(keyType, issuers);
146 }
147
148 @Override
149 public PrivateKey getPrivateKey(String alias) {
150 return orig.getPrivateKey(alias);
151 }
152
153 @Override
154 public String[] getServerAliases(String keyType, Principal[] issuers) {
155 // not implemented
156 return null;
157 }
158
159 public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
160 return orig.chooseEngineClientAlias(keyType, null, engine);
161 // could just forward to chooseClientAlias(socket=null), that's what underlying does
162 }
163
164 public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
165 // not implemented
166 return null;
167 }
168 };
169 }
170 SSLContext ctx = SSLContext.getInstance("TLS");
171 ctx.init(km, tm, null /* default */);
172 SSLSocketFactory sf = ctx.getSocketFactory();
173 SSLSocket ss = (SSLSocket) sf.createSocket(args[4], Integer.parseInt(args[5]));
174 ss.startHandshake();
175 System.out.println ("successful");
176 }
177}
178
QUESTION
Domain Driven Design: Define boundaries from Domain Model
Asked 2021-Oct-06 at 07:49I am working on a project and I am trying to utilize the concepts of Domain Driven Design.
I have the following domain model: (it is simplified for this question)
Let me explain the system first.
This system is for monitoring data coming from gateways. In this case there are two gateways, but there might be more in reality. Each gateway has its own implementation, so its own entities.
An example of the system is as follows:
A company has a project to monitor ship data.
So they have two gateways. A gateway with type
Field-Device-Gateway and a gateway with type
HTTP-Client-Gateway.
The first gateway (field-device-gateway) can have multiple field devices. A field device is a small device onboard on a ship. This device receives all data coming from devices on board of the ship. This is through a source (like an address) that has to be setup in the system.
The second gateway (http-client-gateway) can have multiple HTTP clients. Each client may have multiple routes.
So, a gateway also has variables. A variable is a configuration for getting a specific set of data. So, on the field-device-gateway might be a variable specifying to get integer data from a specific device
, from a specific field device source
, from a specific field device
.
The system will make a request to the field device with the new variables. The field device then knows what data to send. It will be received by the system and stored in the database.
So, what am I asking?
Currently, everything is coupled. I need to define boundaries and then aggregates, but I just don't know where to start.
If I would not create boundaries, this will just become an enormous coupled mess and makes it hard to make aggregates.
So, what would the boundaries be? And what about the aggregates, are there even aggregates? Is everything its own aggregate?
And if everything is its own aggregate, how do I enforce some business logic like: A variable can only exist if there is a gateway, project and company.
ANSWER
Answered 2021-Oct-06 at 07:49Before thinking about boundaries, you have to go back to domains and subdomains.
The problem space is your domains (and subdomains) and your solution space is your Bounded Contexts
.
The first important issue in the application of the DDD in a project is to identify your sub-domains and in particular to divide them into the 3 following "families":
- Core Domain
- Supporting Subdomain
- Generic Subdomain
This cutting is driven by this sub-domain families and by the domain expert.
A Bounded Context
is a language boundary where context is king. We only know the meaning of a concept thanks to its context (which is also a cultural context).
How to avoid making mistakes in the division of Bounded Contexts
?
- When there are technical or architectural considerations
- When you try to split a
Bounded Context
to distribute tasks to free developers - If you have two objects that don't have the same definitions, then they must be in two different
Bounded Contexts
If you think to all this, you may easily separate your objects (your language) into these Bounded Contexts
.
For your Aggregate
's problem :
- Try to decide for each class if it is an
Entity
or aValue Object
(orDomain Service
but try to avoid). - Your
Aggregates
are "some" of your main Entities which can drive otherEntities
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in HTTP Client
Tutorials and Learning Resources are not available at this moment for HTTP Client