I am trying to migrate from restTemplate to webClient.
Everything was fine until I reached restTemplate config with ClientHttpRequestFactory.
I paste here the old and the new codes.
------Old code with restTemplate-------
private HttpComponentsClientHttpRequestFactory buildRequestFactory() {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPassword));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
};
SSLContext sslContext = null;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
}
SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = clientBuilder
.setSSLSocketFactory(connectionFactory)
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
#Override
public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context)
throws HttpException {
if (target.getHostName().equals(noproxy)) {
return null;
}
return super.determineProxy(target, request, context);
}
})
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
#Bean(name = "gatewayRestTemplate")
public RestTemplate gatewayRestTemplateConfig() {
RestTemplate restTemplate = new RestTemplate(converters());
restTemplate.setRequestFactory(buildRequestFactory());
return restTemplate;
}
------New code with webClient-------
private ClientHttpConnector buildClientConnector() {
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
org.apache.hc.core5.http.HttpHost proxy = new org.apache.hc.core5.http.HttpHost(proxyHost, proxyPort);
org.apache.hc.client5.http.auth.CredentialsProvider credsProvider = new org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider();
((org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider) credsProvider).setCredentials(new org.apache.hc.client5.http.auth.AuthScope(proxyHost, proxyPort),
new org.apache.hc.client5.http.auth.UsernamePasswordCredentials(proxyUser, proxyPassword.toCharArray()));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new DefaultAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
};
SSLContext sslContext = null;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
}
org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory connectionFactory =
new org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
org.apache.hc.core5.http.config.Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
// .<org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory>create().register("https", connectionFactory)
.<ConnectionSocketFactory>create().register("https", connectionFactory)
// .register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpAsyncClient client = clientBuilder
.setConnectionManager((AsyncClientConnectionManager) connectionManager)
.setRoutePlanner(new org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner(proxy) {
#Override
protected org.apache.hc.core5.http.HttpHost determineProxy(org.apache.hc.core5.http.HttpHost target, org.apache.hc.core5.http.protocol.HttpContext context) throws org.apache.hc.core5.http.HttpException {
if (target.getHostName().equals(noproxy)) {
return null;
}
return super.determineProxy(target, context);
}
})
.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
return connector;
}
#Primary
#Bean(name = "defaultWebClient")
public WebClient defaultWebClientConfig() {
WebClient webClient = WebClient.builder()
.clientConnector(buildClientConnector())
.build();
return webClient;
}
When I run the project, I get this exception:
Caused by: java.lang.ClassCastException: class org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager cannot be cast to class org.apache.hc.client5.http.nio.AsyncClientConnectionManager (org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager and org.apache.hc.client5.http.nio.AsyncClientConnectionManager are in unnamed module of loader 'app')
Based on Migration to Apache HttpClient 5.0 async APIs, I solved my problem. The idea is to use ClientTlsStrategyBuilder when setting sslContext.
private ClientHttpConnector buildClientConnector() {
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
org.apache.hc.core5.http.HttpHost proxy = new org.apache.hc.core5.http.HttpHost(proxyHost, proxyPort);
org.apache.hc.client5.http.auth.CredentialsProvider credsProvider = new org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider();
((org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider) credsProvider).setCredentials(new org.apache.hc.client5.http.auth.AuthScope(proxyHost, proxyPort),
new org.apache.hc.client5.http.auth.UsernamePasswordCredentials(proxyUser, proxyPassword.toCharArray()));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new DefaultAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
SSLContext sslContext;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
}
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
.setSslContext(sslContext)
.setHostnameVerifier(new NoopHostnameVerifier())
.build())
.build();
CloseableHttpAsyncClient client = clientBuilder
.setConnectionManager(connectionManager)
.setRoutePlanner(new org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner(proxy) {
#Override
protected org.apache.hc.core5.http.HttpHost determineProxy(org.apache.hc.core5.http.HttpHost target, org.apache.hc.core5.http.protocol.HttpContext context) throws org.apache.hc.core5.http.HttpException {
if (target.getHostName().equals(noproxy)) {
return null;
}
return super.determineProxy(target, context);
}
})
.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
return connector;
}
#Primary
#Bean(name = "defaultWebClient")
public WebClient defaultWebClientConfig() {
WebClient webClient = WebClient.builder()
.clientConnector(buildClientConnector())
.build();
return webClient;
}
If you want to use HttpClient connector. Please use below code for
webclient. The above answers any of them not worked, below solution is
working fine for me.
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create().secure(t ->
t.sslContext(sslContext) );
WebClient webClient = WebClient.builder()
.baseUrl("any-url")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
I tried to call the rest endpoint from browser and is working fine, but with rest template, i am not getting any response.
URL: https://www1.nseindia.com/marketinfo/sym_map/symbolCount.jsp?symbol=INFY
public class Test {
public static void main(String[] args) throws Exception {
try {
String jksPath = "C:\\ssl_server.jks";
String pass = "123456";
SSLContext ssl = SSLContextBuilder.create()
.loadTrustMaterial(ResourceUtils.getFile(jksPath), pass.toCharArray()).build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ssl, new LHVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
requestFactory.setConnectionRequestTimeout(15000);
requestFactory.setReadTimeout(15000);
System.out.println("Start: " + new Date());
HttpHeaders headers = new HttpHeaders();
headers.set(org.apache.http.HttpHeaders.ACCEPT, "*/*");
HttpEntity<?> httpEntity = new HttpEntity<>(headers);
ResponseEntity<Object> response = new RestTemplate(requestFactory).exchange(
"https://www1.nseindia.com/marketinfo/sym_map/symbolCount.jsp?symbol=INFY", HttpMethod.GET,
httpEntity, Object.class);
} catch (Exception e) {
System.out.println("Exception: " + new Date());
e.printStackTrace();
}
System.out.println("done");
}
}
class LHVerifier implements HostnameVerifier {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
I'm getting this json response with some timeout message in the beginning and the class throwing Not valid jSON exception.
Json Response
failed to connect: timeout I should not be here {Valid json}
but when I test through postman , I don't see that frustrating message.
I'm using spring RestTemplate` to call the service
final HttpEntity<String> entity = new HttpEntity<String>(headers);
final ResponseEntity<String> exchange = restTemplate.exchange(uri.toUriString(),
HttpMethod.GET, entity, String.class);
Even though I increase timeout values, it's the same
#PostConstruct
public void init() {
restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
protected boolean hasError(HttpStatus statusCode) {
return false;
}
});
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(10000);
factory.setConnectTimeout(10000);
return factory;
}
This is my camel route
from("direct:processRequest").process(new RecipientListBean()).recipientList(header("recipients"))
.aggregationStrategy(aggregationStrategy)
.parallelProcessing()
.to("bean:transformerBean");
I'm consuming a RESTful Web Service using Spring Boot and restTemplate. The service is secured with 2 way ssl. So fare I have made this code to configure the connection. The code works but what I'm looking for is the best way to implement 2 way SSL with springs restTemplate and httpClient or another alternative using Spring Boot
#Configuration()
public class RestClientConfig {
private static final Logger log = LoggerFactory.getLogger(RestClientConfig.class);
#Bean()
#Qualifier("SSLRestOperations")
public RestOperations restOperations(ClientHttpRequestFactory clientHttpRequestFactory) throws Exception {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
return restTemplate;
}
#Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// timeout
requestFactory.setReadTimeout(60 * 1000);
requestFactory.setConnectTimeout(60 * 1000);
return requestFactory;
}
#Bean
public HttpClient httpClient(#Value("${keystore.file}") Resource file, #Value("${keystore.pass}") Password password) throws Exception {
String keystorePassword = password.getDescrambled();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream instream = file.getInputStream();
InputStream instreamKey = file.getInputStream();
try {
keyStore.load(instreamKey, keystorePassword.toCharArray());
trustStore.load(instream, keystorePassword.toCharArray());
kmf.init(keyStore, keystorePassword.toCharArray());
tmf.init(trustStore);
} finally {
instream.close();
instreamKey.close();
}
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.loadKeyMaterial(trustStore, keystorePassword.toCharArray()).build();
SSLSocketFactory socketFactory = sslcontext.getSocketFactory();
SSLSocket socket = (SSLSocket) socketFactory.createSocket();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1.2" }, null, new NoopHostnameVerifier());
return HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()) // HostnameVerifier NoopHostnameVerifier
.setSSLSocketFactory(sslsf)
.build();
}
Can anybody provide me with a code sample to access the rest service URL secured with HTTPS using the Spring Rest template?
I have the certificate, username and password. Basic Authentication is used on the server-side and I want to create a client that can connect to that server using a provided certificate, username and password (if needed).
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File(keyStoreFile)),
keyStorePassword.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
.build(),
NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
MyRecord record = restTemplate.getForObject(uri, MyRecord.class);
LOG.debug(record.toString());
Here is some code that will give you the general idea.
You need to create a custom ClientHttpRequestFactory in order to trust the certificate.
It looks like this:
final ClientHttpRequestFactory clientHttpRequestFactory =
new MyCustomClientHttpRequestFactory(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER, serverInfo);
restTemplate.setRequestFactory(clientHttpRequestFactory);
This is the implementation for MyCustomClientHttpRequestFactory:
public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
private final HostnameVerifier hostNameVerifier;
private final ServerInfo serverInfo;
public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier,
final ServerInfo serverInfo) {
this.hostNameVerifier = hostNameVerifier;
this.serverInfo = serverInfo;
}
#Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
throws IOException {
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier);
((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext()
.getSocketFactory());
}
super.prepareConnection(connection, httpMethod);
}
private SSLContext initSSLContext() {
try {
System.setProperty("https.protocols", "TLSv1");
// Set ssl trust manager. Verify against our server thumbprint
final SSLContext ctx = SSLContext.getInstance("TLSv1");
final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo);
final ThumbprintTrustManager thumbPrintTrustManager =
new ThumbprintTrustManager(null, verifier);
ctx.init(null, new TrustManager[] { thumbPrintTrustManager }, null);
return ctx;
} catch (final Exception ex) {
LOGGER.error(
"An exception was thrown while trying to initialize HTTP security manager.", ex);
return null;
}
}
In this case my serverInfo object contains the thumbprint of the server.
You need to implement the TrustManager interface to get
the SslThumbprintVerifier or any other method you want to verify your certificate (you can also decide to also always return true).
The value org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER allows all host names.
If you need to verify the host name,
you will need to implement it differently.
I'm not sure about the user and password and how you implemented it.
Often,
you need to add a header to the restTemplate named Authorization
with a value that looks like this: Base: <encoded user+password>.
The user+password must be Base64 encoded.
This is a solution with no deprecated class or method :
(Java 8 approved)
CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
Important information : Using NoopHostnameVerifier is a security risk
One point from me. I used a mutual cert authentication with spring-boot microservices. The following is working for me, key points here are
keyManagerFactory.init(...) and sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()) lines of code without them, at least for me, things did not work. Certificates are packaged by PKCS12.
#Value("${server.ssl.key-store-password}")
private String keyStorePassword;
#Value("${server.ssl.key-store-type}")
private String keyStoreType;
#Value("${server.ssl.key-store}")
private Resource resource;
private RestTemplate getRestTemplate() throws Exception {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() throws Exception {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
private HttpClient httpClient() throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore trustStore = KeyStore.getInstance(keyStoreType);
if (resource.exists()) {
InputStream inputStream = resource.getInputStream();
try {
if (inputStream != null) {
trustStore.load(inputStream, keyStorePassword.toCharArray());
keyManagerFactory.init(trustStore, keyStorePassword.toCharArray());
}
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} else {
throw new RuntimeException("Cannot find resource: " + resource.getFilename());
}
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null, getDefaultHostnameVerifier());
return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
}
Here is what I ended up with for the similar problem. The idea is the same as in #Avi's answer, but I also wanted to avoid the static "System.setProperty("https.protocols", "TLSv1");", so that any adjustments won't affect the system. Inspired by an answer from here http://www.coderanch.com/t/637177/Security/Disabling-handshake-message-Java
public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier((hostname, session) -> true);
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
/**
* We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
* see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
*/
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[] {"SSLv3"});
return socket;
}
}
}
You need to configure a raw HttpClient with SSL support, something like this:
#Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect()
throws ClientProtocolException, IOException {
CloseableHttpClient httpClient
= HttpClients.custom()
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpComponentsClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
ResponseEntity<String> response
= new RestTemplate(requestFactory).exchange(
urlOverHttps, HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode().value(), equalTo(200));
}
from: Baeldung