SlideShare a Scribd company logo
Java11時代のHTTPアクセス再入門
JJUG CCC 2019 Spring
@tamtam180
2019/05/18
アジェンダ
• 自己紹介
• おさらい
– HttpUrlConnectionの話
– Apache HttpClientの話
• java.net.http.HttpClientの話
アジェンダ
• 話さないこと
– HTTP/2の基本的な話
自己紹介
• Name: Kiyotaka Suzuki
• Twitter: @tamtam180
• Main works
– Square Enix (5.5 Year)
• PlayOnline, FF-XIV, etc,
– SmartNews (5 Year)
• Cross functional Expert Team
• Senior Software Engineer
• Advertising System, PoC, SRE, etc,..
• I Love OSS about Datastore
• Mad performance tuner
その前に
• 検証に便利なWebサイト
– https://http2.pro/api/v1
• HTTP/2のテスト
– https://httpbin.org/
• HTTPメソッド、ステータス、Cookie、Cache、etc
• このサイトでほぼ事足りる
– https://httpstat.us/
• HTTPステータス
– https://badssl.com/
• HTTPSのエラーテスト
– https://www.websocket.org/echo.html
• WebSocketのテスト
おさらい
• 今までの主なHTTPアクセス
– HttpUrlConnection
– Apache HttpClient
– OkHttp
おさらい
• HttpUrlConnection
– 同期処理だけ
– HTTP1.1まで
– Cookieは一応扱える
• OnMemory
– Basic認証も出来る
• QueryStringを作るのが大変(個人的感想)
• Responseのgzip処理とか大変
おさらい
• Apache HTTPClient
– 5系からHTTP/2が使える(5系はβバージョン)
– 3系から4系でプログラムの構造が大きく変更
• Commons HttpClientは3系, もう古い
– IO Model
• Blocking I/O
– Classic Java IO
– Non blocking
• Event Driven I/O with JavaNIO
おさらい
• Apache HTTPClient
– Core
• v4.4
• v5.0 beta
– Client
• v4.5
• v5.0 beta
– HttpAsyncClient
• v4.1
おさらい
• OkHttp (v3.14.1)
– HTTP/2対応
– Androidも対応
• Android5.0+ (API Level 21+)
– Java8+
– TLS1.3対応
おさらい
• java.net.http / HttpClient
– HTTP/2対応
– WebSocket対応
– 同期/非同期 処理対応
– Reactive Streamとして
Request/Responseを処理
– Builderパターン
おさらい HttpUrlConnection
おさらい: HttpUrlConnection
• GET
var url = new URL("https://httpbin.org/get");
var conn = (HttpURLConnection) url.openConnection();
try (InputStream in = conn.getInputStream()) {
System.out.println(conn.getResponseCode());
System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
}
conn.disconnect();
"headers": {
"Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
"Host": "httpbin.org",
"User-Agent": "Java/11.0.2"
}
おさらい: HttpUrlConnection
• メソッドを変更する(例えばHEAD)
conn.setRequestMethod("HEAD");
おさらい: HttpUrlConnection
• Request Headerを弄る
conn.setRequestProperty("User-Agent", "MyJava/11.0.0");
conn.setRequestProperty("Content-Type", "application/json");
おさらい: HttpUrlConnection
• BODYを送信する
conn.setDoOutput(true);
try (var writer = new OutputStreamWriter(conn.getOutputStream())) {
writer.write(new Gson().toJson(Map.of("hello", "world")));
writer.flush();
}
おさらい: HttpUrlConnection
• POST(組み合わせ)
var url = new URL("http://httpbin.org/post");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "MyJava/11.0.0");
conn.setRequestProperty("Content-Type", "application/json");
try (OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream())) {
writer.write(new Gson().toJson(Map.of("hello", "world")));
writer.flush();
}
try (InputStream in = conn.getInputStream()) {
System.out.println(conn.getResponseCode());
System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
}
conn.disconnect();
おさらい: HttpUrlConnection
• Cookie
– CookieManager, CookieHandlerを使う
– 世の中にあるサンプル、本当に動く??
– CookieManagerはThreadSafeじゃない
CookieManager cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);
System.out.println(conn.getResponseCode());
CookieHandler.getDefault().put(conn.getURL().toURI(), conn.getHeaderFields());
for (HttpCookie cookie : cookieManager.getCookieStore().get(conn.getURL().toURI())) {
System.out.println(cookie);
}
これが無いと動かないのだけど..
おさらい: HttpUrlConnection
• Proxy
var proxy = new Proxy(
Proxy.Type.SOCKS,
InetSocketAddress.createUnresolved("127.0.0.1", 7777));
HttpURLConnection conn = (HttpURLConnection)
url.openConnection(proxy);
ssh any-server –D7777
HTTP Proxyの場合は、 Proxy.Type.HTTP
おさらい: HttpUrlConnection
• Redirect
– デフォルトはredirectを追従
var url = new URL("https://httpbin.org/redirect/2");
var conn = (HttpURLConnection) url.openConnection();
conn.setInstanceFollowRedirects(true);
おさらい: HttpUrlConnection
• Basic認証
– Header直接指定
– java.net.Authenticator
• requestPasswordAuthenticationInstanceを
上書き
• getPasswordAuthenticationを上書き
おさらい: HttpUrlConnection
• Basic認証
– Header直接指定
var url = new URL("https://httpbin.org/basic-auth/test-user/test-pass");
var conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty(
"Authorization",
"Basic " + Base64.getEncoder().encodeToString("test-user:test-
pass".getBytes()));
おさらい: HttpUrlConnection
• Basic認証
– java.net.Authenticator その1
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() != RequestorType.SERVER) return null;
if (!"basic".equals(getRequestingScheme())) return null;
if ("httpbin.org".equalsIgnoreCase(getRequestingHost())) {
return new PasswordAuthentication("test-user", "test-pass".toCharArray());
}
return null;
}
});
おさらい: HttpUrlConnection
• Basic認証
– java.net.Authenticator その2
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication requestPasswordAuthenticationInstance(String
host, InetAddress addr, int port, String protocol, String prompt, String scheme,
URL url, RequestorType reqType) {
if (reqType != RequestorType.SERVER) return null;
if (!"basic".equals(scheme)) return null;
if ("httpbin.org".equalsIgnoreCase(host)) {
return new PasswordAuthentication("test-user", "test-pass".toCharArray());
}
return null;
}
});
おさらい: HttpUrlConnection
• SSL Errorを無視する通信
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
public X509Certificate[] getAcceptedIssuers() { return null; }
}}, null);
HttpsURLConnection.setDefaultHostnameVerifier((s, sslSession) -> true);
var url = new URL("https://expired.badssl.com/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory());
}
SSLContext
Verifier
SocketFactory
おさらい: HttpUrlConnection
• デバッグの方法
– Logger指定
– java.net.debug
おさらい: HttpUrlConnection
• Logger指定
[logging.properties]
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = ALL
sun.net.www.protocol.http.HttpURLConnection.level = ALL
System.setProperty("java.util.logging.config.file", "logging.properties");
おさらい: HttpUrlConnection
• javax.net.debug
– Helpを指定するとhelpが表示される
System.setProperty("javax.net.debug", ”help");
URL url = new URL("https://www.google.com");
var conn = (HttpURLConnection) url.openConnection();
conn.getInputStream();
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
https://www.ibm.com/support/knowledgecenter/en/SSYKE2_7.0.0/com.ibm.java.security.component.70.doc/security-
all
ssl:record,handshake,keygen,session,defaultctx,sslctx,sessioncache,keymanager,trustmanager,plugga
bility
handshake:data,verbose
plaintext,packet
指定可能な値
例: ssl:handshake,session
例: all
カンマ区切りかコロン区切り
Apache HttpClient
ApacheHC/GET
• GET
var clientBuilder = HttpClientBuilder.create();
try (CloseableHttpClient client = clientBuilder.build()) {
var getMethod = new HttpGet(URI.create("http://httpbin.org/get"));
try (CloseableHttpResponse resp = client.execute(getMethod)) {
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
System.out.println(resp.getStatusLine());
System.out.println(body);
}
}
ApacheHC/Accept Header
• 注意点
– デフォルトのヘッダはAccept Header無し
Host: httpbin.org
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.4 (Java/11.0.1)
Accept-Encoding: gzip,deflate
ApacheHC/Accept Header
• Accept Header
• (指定が無い場合は何でも受け付ける事を意
味する)
• と、書いてあるのに..
RFC7231: Section 5.3.2
A request without any Accept header field implies that the user
agent will accept any media type in response.
https://tools.ietf.org/html/rfc7231#section-5.3.2
ApacheHC/Accept Header
• サイトによっては正しく返却されない
– 例: http://httpstat.us/200
curl -H "Accept: */*" http://httpstat.us/200
# Content-Length: 6
200 OK
curl -H "Accept:" http://httpstat.us/200
# Content-Length: 0
ApacheHC/Accept Header
• 各ブラウザのAccept Header
– デフォルト値の一覧は以下のサイトを参照
https://developer.mozilla.org/en-
US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values
ApacheHC/Gzip
• Gzipはデフォルトで処理される
– gzipもdeflateも
curl -s http://httpbin.org/gzip | gzip -d
var clientBuilder = HttpClientBuilder.create();
try (CloseableHttpClient client = clientBuilder.build()) {
var getMethod = new HttpGet("http://httpbin.org/gzip");
try (var resp = client.execute(getMethod)) {
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
System.out.println(body);
}
}
本当はAccept Encodingを付ける必要がある。
httpbin.org/gzip や httpbin.org/deflate は
それを無視して送ってくるのでコードでは省略している。
ApacheHC
• Queryを作る時は URI Builder
var uri = new URIBuilder("http://httpbin.org/get")
.setCharset(StandardCharsets.UTF_8)
.addParameter("hello", "world")
.addParameter("sushi", "寿司")
.setScheme("https") // HTTPSに変更
.setFragment("hello")
.build();
https://httpbin.org/get?hello=world&sushi=%E5%AF%BF%E5%8F%B8#hello
ApacheHC
• Connection Pool
var connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(100);
var clientBuilder = HttpClientBuilder.create();
clientBuilder.setConnectionManager(connManager);
ApacheHC
• Request Headerをいじる
var clientBuilder = HttpClientBuilder.create();
var defaultHeaders = Arrays.asList(
new BasicHeader("Accept", "*/*")
);
clientBuilder.setDefaultHeaders(defaultHeaders);
var getMethod = new HttpGet(URI.create("http://httpbin.org/get"));
getMethod.setHeader("Accept", "*/*");
定数クラスがあるのでそれを使うと良い
HttpHeaders, ContentType
ApacheHC/POST
var clientBuilder = HttpClientBuilder.create();
try (CloseableHttpClient client = clientBuilder.build()) {
String jsonText = new Gson().toJson(Map.of("hello", "world"));
HttpEntity entity = EntityBuilder.create()
.setContentType(ContentType.APPLICATION_JSON)
.setText(jsonText).build();
HttpPost postMethod = new HttpPost(URI.create("http://httpbin.org/post"));
postMethod.setEntity(entity);
try (CloseableHttpResponse resp = client.execute(postMethod)) {
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
System.out.println(resp.getStatusLine());
System.out.println(body);
}
}
ApacheHC
• やり方色々
– Client
• HttpClientBuilder.create().build();
• HttpClients.custom().build();
• HttpClients.createDefault();
– Entity
• EntityBuilder
• 直接生成
BasicHttpEntity
BufferedHttpEntity
ByteArrayEntity
FileEntity
InputStreamEntity
SerializableEntity
StringEntity
ApacheHC
• やり方色々
– execute
• 単純に実行
• HttpContext
• ResponseHandler
ApacheHC
• Responseの処理はEntityUtilsが便利
– toString()
– toByteArray()
– writeTo()
– consume()
– ...
ApacheHC/Redirect
• デフォルトで追従
• OFFにする場合は、RequestConfig
var requestConfig = RequestConfig.custom()
.setRedirectsEnabled(false) //追従しない
.build();
var clientBuilder = HttpClientBuilder.create();
clientBuilder.setDefaultRequestConfig(requestConfig);
var getMethod = new
HttpGet(URI.create("http://httpbin.org/redirect/3"));
getMethod.setConfig(requestConfig);
ApacheHC/Retry
• DefaultHttpRequestRetryHandler
– ConnectTimeoutExceptionはRetryしない
– InterruptedIOExceptionのSubClassだけど
https://hc.apache.org/httpcomponents-client-
ga/httpclient/apidocs/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html
ApacheHC/Cookie
var cookieStore = new BasicCookieStore();
var clientBuilder = HttpClientBuilder.create();
clientBuilder.setDefaultCookieStore(cookieStore);
// return Set-Cookie
try (CloseableHttpClient client = clientBuilder.build()) {
var get = new HttpGet("https://httpbin.org/cookies/set/hello/world");
try (CloseableHttpResponse resp = client.execute(get)) {}
}
// send Cookie: hello and sushi
var cookie = new BasicClientCookie("sushi", " 🍣");
cookie.setDomain("httpbin.org");
cookieStore.addCookie(cookie);
try (CloseableHttpClient client = clientBuilder.build()) {
var get = new HttpGet("https://httpbin.org/cookies");
try (CloseableHttpResponse resp = client.execute(get)) {}
}
GET /cookies HTTP/1.1
Host: httpbin.org
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.4 (Java/11.0.1
Cookie: hello=world; sushi= 🍣
Accept-Encoding: gzip,deflate
• Proxy
• 認証有りProxy
ApacheHC/Proxy
var clientBuilder = HttpClientBuilder.create();
var proxy = new HttpHost(proxyHost, proxyPort, "http");
clientBuilder.setProxy(proxy);
clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
var credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(proxyUser, proxyPassword));
clientBuilder.setDefaultCredentialsProvider(credsProvider);
ApacheHC
• Basic認証
var u = URI.create("https://httpbin.org/basic-auth/kuma/pass");
var credProvider = new BasicCredentialsProvider();
credProvider.setCredentials(
new AuthScope(u.getHost(), u.getPort(), AuthScope.ANY_REALM,
AuthScope.ANY_SCHEME),
new UsernamePasswordCredentials("kuma", "pass"));
var clientBuilder = HttpClientBuilder.create();
clientBuilder.setDefaultCredentialsProvider(credProvider);
try (CloseableHttpClient client = clientBuilder.build()) {
var getMethod = new HttpGet(u);
try (CloseableHttpResponse resp = client.execute(getMethod)) {
System.out.println(resp.getStatusLine());
}
}
ApacheHC
• Basic認証 (ANYを使う:非推奨)
– 最低限、Host, Portは指定した方が良い
– Realmも本当は指定した方が良いけど
credProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials("kuma", "pass"));
ApacheHC/BadSSL
• BadSSLスルー
var sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
} }, new SecureRandom());
var clientBuilder = HttpClientBuilder.create();
clientBuilder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext));
try (CloseableHttpClient client = clientBuilder.build()) {
var getMethod = new HttpGet("https://expired.badssl.com/");
try (var resp = client.execute(getMethod)) {
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
System.out.println(body);
}
}
ApacheHC/Async
• HttpAsyncClient
– Basicな方法
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
var request1 = new HttpGet("https://httpbin.org/get");
Future<HttpResponse> future = client.execute(request1, null);
// any processing
var resp = future.get();
System.out.println(resp.getStatusLine());
client.close();
ApacheHC/Async
• HttpAsyncClient
– Consumer / Producer
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
var producer = HttpAsyncMethods.create(new HttpGet("https://httpbin.org/get"));
var consumer = new AsyncCharConsumer<HttpResponse>() {
HttpResponse response;
protected void onCharReceived(CharBuffer charBuffer, IOControl ioControl) { }
protected void onResponseReceived(HttpResponse httpResponse) {
this.response = httpResponse;
}
protected HttpResponse buildResult(HttpContext httpContext) { return this.response; }
};
ApacheHC/Async
• HttpAsyncClient
– Consumer / Producer
var latch = new CountDownLatch(1);
client.execute(producer, consumer, new FutureCallback<>() {
public void completed(HttpResponse httpResponse) { latch.countDown(); }
public void failed(Exception e) { latch.countDown(); }
public void cancelled() { latch.countDown(); }
});
latch.await();
System.out.println(consumer.response.getStatusLine());
client.close();
ApacheHC/Debug
• DEBUG
– Loggerを設定する
– Lobackの例
<logger name="org.apache.http" additivity="true">
<level value="DEBUG"/>
</logger>
ApacheHC/不満点
• HTTP/2は まだ 対応していない
• WebSocket対応もまだ
java.net.http
おさらい
• java.net.http / HttpClient
– Java11で正式APIになった
– HTTP/2対応
– WebSocket対応
– 同期/非同期 処理対応
– Reactive Streamとして
Request/Responseを処理
– Builderパターン
java.net.http
• Clientの作り方
もしくは
[Header]
GET /get HTTP/1.1
Content-Length: 0
Host: httpbin.org
User-Agent: Java-http-client/11.0.1
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://http2.pro/api/v1"))
.build();
var resp = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(resp.statusCode());
System.out.println(resp.body());
var client = HttpClient.newBuilder().build();
[Body]
{"http2":1,"protocol":"HT
TP¥/2.0","push":1,"user_a
gent":"Java-http-
client¥/11.0.1"}
java.net.http
• デフォルトは、
– HTTP/2を勝手にやってくれる
• サイトが対応していない場合はHTTP/1.1で通信
– Redirectは勝手にしてくれない
– GZip/Deflateは勝手に処理してくれない
• 便利なURIBuilderは存在しない
java.net.http
• ClientBuilder
– authenticator
– connectTimeout
– cookieHandler
– executor
– followRedirects
– priority
– proxy
– sslContext
– version
– sslParameters
java.net.http
• BodyHandlers
– ofString
– ofByteArray
– ofByteArrayConsumer
– ofFile
– ofFileDownload
– ofInputStream
– ofLines
– ofPublisher
– ...
java.net.http/redirect
• Redirectの設定
var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
NEVER Redirectしない
ALWAYS 常にRedirectする
NORMAL 常にRedirectする(HTTPS->HTTPは除く)
java.net.http/Header
• Request Headerの編集
var request = HttpRequest.newBuilder()
.header("Accept", "*/*")
.header("User-Agent", "JJUG/1.0")
.uri(URI.create("https://httpbin.org/get"))
.build();
java.net.http/POST
• POSTとBody送信
var bodyString = new Gson().toJson(Map.of("hello", "world"));
var client = HttpClient.newBuilder().build();
var request = HttpRequest.newBuilder()
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(bodyString))
.uri(URI.create("https://httpbin.org/post"))
.build();
var resp = client.send(
request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(resp.statusCode());
System.out.println(resp.body());
java.net.http
• BodyPublishers
– noBody
– ofString
– ofByteArray
– ofByteArrays
– ofFile
– ofInputStream
– ...
java.net.http/method
• 他のメソッド
– GET(), POST(), DELETE(), PUT()
– HEADやPATCHは?
• methodを使う
.method("HEAD", HttpRequest.BodyPublishers.noBody())
.method("PATCH", HttpRequest.BodyPublishers.ofString(bodyString))
java.net.http/async
• 非同期: client.sendAsync
var client = HttpClient.newBuilder().build();
var request = HttpRequest.newBuilder(URI.create("https://httpbin.org/get")).build();
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
// CompletableFutureなので処理を繋げる事ができる
future.thenApply(HttpResponse::body)
.exceptionally(e -> "Error!! :" + e.getMessage())
.thenAccept(System.out::println);
//future.get();
java.net.http/content-encoding
• BodySubscribers.mappingを使う
– ※JDKの不具合で動きません
https://bugs.openjdk.java.net/browse/JDK-8217264
private HttpResponse.BodySubscriber<InputStream> gzippedBodySubscriber(
HttpResponse.ResponseInfo responseInfo) {
// 本当はheaderのContent-Encodingを確認する
return HttpResponse.BodySubscribers.mapping(
HttpResponse.BodySubscribers.ofInputStream(),
this::decodeGzipStream);
}
private InputStream decodeGzipStream(InputStream gzippedStream) {
try {
return new GZIPInputStream(gzippedStream);
} catch (IOException ex) { throw new UncheckedIOException(ex); }
}
var resp = client.send(request, this::gzippedBodySubscriber);
java.net.http/content-encoding
• Workaround-1
var client = HttpClient.newBuilder().build();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/gzip"))
.build();
var resp = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
InputStream in = resp.body();
if ("gzip".equals(resp.headers().firstValue("Content-Encoding").get())) {
in = new GZIPInputStream(resp.body());
}
System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
java.net.http/content-encoding
• Workaround-2
var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
.thenApply(resp -> {
String ce = resp.headers().firstValue("Content-Encoding").get();
if ("gzip".equals(ce)) {
try {
return new GZIPInputStream(resp.body());
} catch (IOException e) { throw new UncheckedIOException(e); }
}
return resp.body();
});
var in = future.get();
System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
java.net.http/content-encoding
• Workaround others
https://stackoverflow.com/questions/53379087/wrapping-
bodysubscriberinputstream-in-gzipinputstream-leads-to-hang
java.net.http/custom handler
• 例: json
public static class JsonBodyHandler<T> implements HttpResponse.BodyHandler<T> {
private Class<T> type;
private JsonBodyHandler(Class<T> type) { this.type = type; }
public static <T> JsonBodyHandler<T> jsonBodyHandler(Class<T> type) {
return new JsonBodyHandler<>(type);
}
@Override
public HttpResponse.BodySubscriber<T> apply( HttpResponse.ResponseInfo info) {
return HttpResponse.BodySubscribers.mapping(
HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8),
s -> new Gson().fromJson(s, this.type));
}
}
var resp = client.send(request, JsonBodyHandler.jsonBodyHandler(MyJson.class));
MyJson body = resp.body();
java.net.http/query-param, form-encoded
• 用意されていない
– 便利なライブラリが他にあるのでそれを利用して
BodyにStringとして渡す
– (例) Spring関連のLibrary
– (例) Apache HttpClient 😨
• デフォルトでは無効化されている
java.net.http/cookie
var cookieHandler = new CookieManager();
var client = HttpClient.newBuilder().cookieHandler(cookieHandler).build();
var u = URI.create("https://httpbin.org/cookies/set/hello/world");
var request1 = HttpRequest.newBuilder(u).build();
client.send(request1, HttpResponse.BodyHandlers.ofString());
var cookie = new HttpCookie("sushi", "tenpura");
cookie.setDomain(u.getHost());
cookie.setPath("/");
cookie.setVersion(0);
cookieHandler.getCookieStore().add(u, cookie);
var request2 =
HttpRequest.newBuilder(URI.create("https://httpbin.org/cookies")).build();
var resp2 = client.send(request2, HttpResponse.BodyHandlers.ofString());
Version0が⼤事
Version0 -> Netscape style
Version1 -> RFC2965/2109
java.net.http/basic auth
• Basic認証
– HttpUrlConnectionのサンプルと同じ
– 利用クラスも同じなのでそちらを参照
var client = HttpClient.newBuilder()
.authenticator(authenticator)
.build();
java.net.http/download
• Download
var client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
var u = URI.create("https://github.com/AdoptOpenJDK/openjdk11-
binaries/releases/download/jdk-11.0.3%2B7/OpenJDK11U-
jdk_x64_linux_hotspot_11.0.3_7.tar.gz");
var request = HttpRequest.newBuilder().uri(u).build();
Path f = Paths.get("/tmp/").resolve(Path.of(u.getPath()).getFileName());
var resp = client.send(request, HttpResponse.BodyHandlers.ofFile(f));
System.out.println(resp.statusCode());
System.out.println(f.toFile().length());
java.net.http/proxy with authentication
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
var proxySelector = ProxySelector.of(
InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT));
var client = HttpClient.newBuilder()
.proxy(proxySelector)
.authenticator(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(PROXY_USER, PROXY_PASS);
}
return null;
}
}).build();
var u = URI.create("https://httpbin.org/get");
var request1 = HttpRequest.newBuilder(u).build();
var resp = client.send(request1, HttpResponse.BodyHandlers.ofString());
https://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html
jdk8u111より
これが無いとhttpsのurlの
認証処理がskipされる
Proxyの時だけ認証
java.net.http/badSSL
• SSLContextを設定できるので、ApacheHCと同じ
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
} }, new SecureRandom());
var client = HttpClient.newBuilder().sslContext(sslContext).build();
var u = URI.create("https://expired.badssl.com/");
var request1 = HttpRequest.newBuilder(u).build();
var resp = client.send(request1, HttpResponse.BodyHandlers.ofString());
java.net.http/server-push
https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
java.net.http/server-push
var httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
var pageRequest =
HttpRequest.newBuilder(URI.create("https://http2.golang.org/serverpush")).build();
AtomicInteger cnt = new AtomicInteger(1);
var futures = new CopyOnWriteArrayList<CompletableFuture<HttpResponse<String>>>();
HttpResponse.PushPromiseHandler<String> handler =
(initiatingRequest, pushPromiseRequest, acceptor) -> {
System.out.println("Promise request: " + pushPromiseRequest.uri());
var pushedFuture = acceptor.apply(HttpResponse.BodyHandlers.ofString());
pushedFuture = pushedFuture.thenApply(resp -> {
System.out.println("[" + cnt.getAndIncrement() + "] Pushed response: " + resp.uri());
return resp;
});
futures.add(pushedFuture);
}; applyPushPromise(
HttpRequest initiatingRequest,
HttpRequest pushPromiseRequest,
Function<HttpResponse.BodyHandler<String>,
CompletableFuture<HttpResponse<String>>> acceptor
)
java.net.http/server-push
var future = httpClient.sendAsync(
pageRequest,
HttpResponse.BodyHandlers.ofString(),
handler);
future.thenAccept(pageResponse -> {
System.out.println("Page response status code: " +
pageResponse.statusCode());
}).join();
var array = futures.toArray(new CompletableFuture[0]);
CompletableFuture.allOf(array).get();
Promise request: https://http2.golang.org/serverpush/static/jquery.min.js?1558076345232851844
Promise request: https://http2.golang.org/serverpush/static/godocs.js?1558076345232851844
Promise request: https://http2.golang.org/serverpush/static/playground.js?1558076345232851844
Promise request: https://http2.golang.org/serverpush/static/style.css?1558076345232851844
[1] Pushed response: https://http2.golang.org/serverpush/static/style.css?1558076345232851844
[2] Pushed response: https://http2.golang.org/serverpush/static/playground.js?1558076345232851844
[3] Pushed response: https://http2.golang.org/serverpush/static/godocs.js?1558076345232851844
Page response status code: 200
[4] Pushed response: https://http2.golang.org/serverpush/static/jquery.min.js?1558076345232851844
java.net.http/web-socket
var u = URI.create("wss://echo.websocket.org");
var client = HttpClient.newBuilder().build();
var listener = new WebSocket.Listener() {
public CompletionStage<?> onText(WebSocket webSocket,
CharSequence data, boolean last) {
System.out.println("receive:" + data + ", last=" + last);
return WebSocket.Listener.super.onText(webSocket, data, last);
}
};
WebSocket wsock = client.newWebSocketBuilder().buildAsync(u, listener).join();
wsock.sendText("hello world 1", false);
wsock.sendText("hello world 2", true);
TimeUnit.SECONDS.sleep(2);
wsock.sendClose(WebSocket.NORMAL_CLOSURE, "BYE");
java.net.http/debug
[class]
jdk.internal.net.http.common.Utils
jdk.internal.net.http.common.DebugLogger
jdk.internal.net.http.common.Log
jdk.internal.net.http.common.Logger
• システムプロパティの設定
jdk.internal.httpclient.debug=true
jdk.internal.httpclient.websocket.debug=true
jdk.internal.httpclient.hpack.debug=true
• もしくは
• もしくは
– LoggerName
java.net.http/debug
-Djava.net.HttpClient.log=
errors,requests,headers,frames[:control:data:window:all..],co
ntent,ssl,trace,channel
jdk.httpclient.HttpClient
java.net.http
• reactive streamの話をしていない?
java.net.http
• reactive streamの話をしていない?
– 今まで使っていたPublisher, SubScriberが
そうなのです。
public interface BodySubscriber<T> extends Flow.Subscriber<List<ByteBuffer>> {
CompletionStage<T> getBody();
void onSubscribe(Subscription subscription);
void onNext(List<ByteBuffer> item);
void onError(Throwable throwable);
void onComplete();
}
public interface BodyPublisher extends Flow.Publisher<ByteBuffer> {
long contentLength();
void subscribe(Subscriber<? super ByteBuffer> subscriber);
}
java.net.http
• もっといろいろな書き方が出来ます!!
– Javadocと公式サンプルを見て遊びましょう
続く(かも)

More Related Content

What's hot

FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎
ken_kitahara
 
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
Preferred Networks
 
実装して理解するLINE LoginとOpenID Connect入門
実装して理解するLINE LoginとOpenID Connect入門実装して理解するLINE LoginとOpenID Connect入門
実装して理解するLINE LoginとOpenID Connect入門
Naohiro Fujie
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
土岐 孝平
 
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Ryosuke Uchitate
 
Go言語で作る webアプリ@gocon 2013 spring
Go言語で作る webアプリ@gocon 2013 springGo言語で作る webアプリ@gocon 2013 spring
Go言語で作る webアプリ@gocon 2013 springTakuya Ueda
 
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
Kuniyasu Suzaki
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
Taku Miyakawa
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
NTT DATA Technology & Innovation
 
はじめてのElasticsearchクラスタ
はじめてのElasticsearchクラスタはじめてのElasticsearchクラスタ
はじめてのElasticsearchクラスタ
Satoyuki Tsukano
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
NTT DATA Technology & Innovation
 
今さら聞けないXSS
今さら聞けないXSS今さら聞けないXSS
今さら聞けないXSS
Sota Sugiura
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
Akihiko Horiuchi
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
sonickun
 
第六回渋谷Java Java8のJVM監視を考える
第六回渋谷Java Java8のJVM監視を考える第六回渋谷Java Java8のJVM監視を考える
第六回渋谷Java Java8のJVM監視を考えるchonaso
 
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
Hinemos
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
murachue
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
Masatoshi Tada
 
Java EE 8新機能解説 -Bean Validation 2.0編-
Java EE 8新機能解説 -Bean Validation 2.0編-Java EE 8新機能解説 -Bean Validation 2.0編-
Java EE 8新機能解説 -Bean Validation 2.0編-
Masatoshi Tada
 

What's hot (20)

FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎FridaによるAndroidアプリの動的解析とフッキングの基礎
FridaによるAndroidアプリの動的解析とフッキングの基礎
 
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
Kubernetes Service Account As Multi-Cloud Identity / Cloud Native Security Co...
 
実装して理解するLINE LoginとOpenID Connect入門
実装して理解するLINE LoginとOpenID Connect入門実装して理解するLINE LoginとOpenID Connect入門
実装して理解するLINE LoginとOpenID Connect入門
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!Amazon Cognito使って認証したい?それならSpring Security使いましょう!
Amazon Cognito使って認証したい?それならSpring Security使いましょう!
 
Go言語で作る webアプリ@gocon 2013 spring
Go言語で作る webアプリ@gocon 2013 springGo言語で作る webアプリ@gocon 2013 spring
Go言語で作る webアプリ@gocon 2013 spring
 
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
 
はじめてのElasticsearchクラスタ
はじめてのElasticsearchクラスタはじめてのElasticsearchクラスタ
はじめてのElasticsearchクラスタ
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
今さら聞けないXSS
今さら聞けないXSS今さら聞けないXSS
今さら聞けないXSS
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
 
HTTP/2 入門
HTTP/2 入門HTTP/2 入門
HTTP/2 入門
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
第六回渋谷Java Java8のJVM監視を考える
第六回渋谷Java Java8のJVM監視を考える第六回渋谷Java Java8のJVM監視を考える
第六回渋谷Java Java8のJVM監視を考える
 
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
Docker管理もHinemosで! ~監視・ジョブ機能を併せ持つ唯一のOSS「Hinemos」のご紹介~
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
Java EE 8新機能解説 -Bean Validation 2.0編-
Java EE 8新機能解説 -Bean Validation 2.0編-Java EE 8新機能解説 -Bean Validation 2.0編-
Java EE 8新機能解説 -Bean Validation 2.0編-
 

Similar to Introduction httpClient on Java11 / Java11時代のHTTPアクセス再入門

Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
Dongmin Yu
 
StormCrawler at Bristech
StormCrawler at BristechStormCrawler at Bristech
StormCrawler at Bristech
Julien Nioche
 
Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"
Fwdays
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기
NAVER D2
 
Web Standards Support in WebKit
Web Standards Support in WebKitWeb Standards Support in WebKit
Web Standards Support in WebKit
Joone Hur
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
Fastly
 
Connecting to the network
Connecting to the networkConnecting to the network
Connecting to the networkMu Chun Wang
 
手把手教你如何串接 Log 到各種網路服務
手把手教你如何串接 Log 到各種網路服務手把手教你如何串接 Log 到各種網路服務
手把手教你如何串接 Log 到各種網路服務
Mu Chun Wang
 
Protocol handler in Gecko
Protocol handler in GeckoProtocol handler in Gecko
Protocol handler in Gecko
Chih-Hsuan Kuo
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy
Fwdays
 
Connecting to Web Services on Android
Connecting to Web Services on AndroidConnecting to Web Services on Android
Connecting to Web Services on Android
sullis
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
Jennifer Bourey
 
Docker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic StackDocker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic Stack
Jakub Hajek
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek
PROIDEA
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
soft-shake.ch
 
Google Back To Front: From Gears to App Engine and Beyond
Google Back To Front: From Gears to App Engine and BeyondGoogle Back To Front: From Gears to App Engine and Beyond
Google Back To Front: From Gears to App Engine and Beyond
dion
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteer
VodqaBLR
 
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better NetworkingITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
Istanbul Tech Talks
 
Building and Scaling Node.js Applications
Building and Scaling Node.js ApplicationsBuilding and Scaling Node.js Applications
Building and Scaling Node.js Applications
Ohad Kravchick
 

Similar to Introduction httpClient on Java11 / Java11時代のHTTPアクセス再入門 (20)

Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
 
StormCrawler at Bristech
StormCrawler at BristechStormCrawler at Bristech
StormCrawler at Bristech
 
Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기
 
Web Standards Support in WebKit
Web Standards Support in WebKitWeb Standards Support in WebKit
Web Standards Support in WebKit
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
Connecting to the network
Connecting to the networkConnecting to the network
Connecting to the network
 
手把手教你如何串接 Log 到各種網路服務
手把手教你如何串接 Log 到各種網路服務手把手教你如何串接 Log 到各種網路服務
手把手教你如何串接 Log 到各種網路服務
 
Protocol handler in Gecko
Protocol handler in GeckoProtocol handler in Gecko
Protocol handler in Gecko
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy
 
Connecting to Web Services on Android
Connecting to Web Services on AndroidConnecting to Web Services on Android
Connecting to Web Services on Android
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
 
Docker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic StackDocker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic Stack
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Google Back To Front: From Gears to App Engine and Beyond
Google Back To Front: From Gears to App Engine and BeyondGoogle Back To Front: From Gears to App Engine and Beyond
Google Back To Front: From Gears to App Engine and Beyond
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteer
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better NetworkingITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
ITT 2014 - Erik Hellmann - Android Programming - Smarter and Better Networking
 
Building and Scaling Node.js Applications
Building and Scaling Node.js ApplicationsBuilding and Scaling Node.js Applications
Building and Scaling Node.js Applications
 

More from tamtam180

Japanese font test
Japanese font testJapanese font test
Japanese font test
tamtam180
 
Getting Started GraalVM (再アップロード)
Getting Started GraalVM (再アップロード)Getting Started GraalVM (再アップロード)
Getting Started GraalVM (再アップロード)
tamtam180
 
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
tamtam180
 
jjugccc2018 app review postmortem
jjugccc2018 app review postmortemjjugccc2018 app review postmortem
jjugccc2018 app review postmortem
tamtam180
 
PipelineDBとは?
PipelineDBとは?PipelineDBとは?
PipelineDBとは?
tamtam180
 
動画共有ツール
動画共有ツール動画共有ツール
動画共有ツールtamtam180
 
Hive undocumented feature
Hive undocumented featureHive undocumented feature
Hive undocumented featuretamtam180
 

More from tamtam180 (7)

Japanese font test
Japanese font testJapanese font test
Japanese font test
 
Getting Started GraalVM (再アップロード)
Getting Started GraalVM (再アップロード)Getting Started GraalVM (再アップロード)
Getting Started GraalVM (再アップロード)
 
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
 
jjugccc2018 app review postmortem
jjugccc2018 app review postmortemjjugccc2018 app review postmortem
jjugccc2018 app review postmortem
 
PipelineDBとは?
PipelineDBとは?PipelineDBとは?
PipelineDBとは?
 
動画共有ツール
動画共有ツール動画共有ツール
動画共有ツール
 
Hive undocumented feature
Hive undocumented featureHive undocumented feature
Hive undocumented feature
 

Recently uploaded

FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Jeffrey Haguewood
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 

Recently uploaded (20)

FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 

Introduction httpClient on Java11 / Java11時代のHTTPアクセス再入門

  • 2. アジェンダ • 自己紹介 • おさらい – HttpUrlConnectionの話 – Apache HttpClientの話 • java.net.http.HttpClientの話
  • 4. 自己紹介 • Name: Kiyotaka Suzuki • Twitter: @tamtam180 • Main works – Square Enix (5.5 Year) • PlayOnline, FF-XIV, etc, – SmartNews (5 Year) • Cross functional Expert Team • Senior Software Engineer • Advertising System, PoC, SRE, etc,.. • I Love OSS about Datastore • Mad performance tuner
  • 5. その前に • 検証に便利なWebサイト – https://http2.pro/api/v1 • HTTP/2のテスト – https://httpbin.org/ • HTTPメソッド、ステータス、Cookie、Cache、etc • このサイトでほぼ事足りる – https://httpstat.us/ • HTTPステータス – https://badssl.com/ • HTTPSのエラーテスト – https://www.websocket.org/echo.html • WebSocketのテスト
  • 7. おさらい • HttpUrlConnection – 同期処理だけ – HTTP1.1まで – Cookieは一応扱える • OnMemory – Basic認証も出来る • QueryStringを作るのが大変(個人的感想) • Responseのgzip処理とか大変
  • 8. おさらい • Apache HTTPClient – 5系からHTTP/2が使える(5系はβバージョン) – 3系から4系でプログラムの構造が大きく変更 • Commons HttpClientは3系, もう古い – IO Model • Blocking I/O – Classic Java IO – Non blocking • Event Driven I/O with JavaNIO
  • 9. おさらい • Apache HTTPClient – Core • v4.4 • v5.0 beta – Client • v4.5 • v5.0 beta – HttpAsyncClient • v4.1
  • 10. おさらい • OkHttp (v3.14.1) – HTTP/2対応 – Androidも対応 • Android5.0+ (API Level 21+) – Java8+ – TLS1.3対応
  • 11. おさらい • java.net.http / HttpClient – HTTP/2対応 – WebSocket対応 – 同期/非同期 処理対応 – Reactive Streamとして Request/Responseを処理 – Builderパターン
  • 13. おさらい: HttpUrlConnection • GET var url = new URL("https://httpbin.org/get"); var conn = (HttpURLConnection) url.openConnection(); try (InputStream in = conn.getInputStream()) { System.out.println(conn.getResponseCode()); System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8)); } conn.disconnect(); "headers": { "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", "Host": "httpbin.org", "User-Agent": "Java/11.0.2" }
  • 15. おさらい: HttpUrlConnection • Request Headerを弄る conn.setRequestProperty("User-Agent", "MyJava/11.0.0"); conn.setRequestProperty("Content-Type", "application/json");
  • 16. おさらい: HttpUrlConnection • BODYを送信する conn.setDoOutput(true); try (var writer = new OutputStreamWriter(conn.getOutputStream())) { writer.write(new Gson().toJson(Map.of("hello", "world"))); writer.flush(); }
  • 17. おさらい: HttpUrlConnection • POST(組み合わせ) var url = new URL("http://httpbin.org/post"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("User-Agent", "MyJava/11.0.0"); conn.setRequestProperty("Content-Type", "application/json"); try (OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream())) { writer.write(new Gson().toJson(Map.of("hello", "world"))); writer.flush(); } try (InputStream in = conn.getInputStream()) { System.out.println(conn.getResponseCode()); System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8)); } conn.disconnect();
  • 18. おさらい: HttpUrlConnection • Cookie – CookieManager, CookieHandlerを使う – 世の中にあるサンプル、本当に動く?? – CookieManagerはThreadSafeじゃない CookieManager cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(cookieManager); System.out.println(conn.getResponseCode()); CookieHandler.getDefault().put(conn.getURL().toURI(), conn.getHeaderFields()); for (HttpCookie cookie : cookieManager.getCookieStore().get(conn.getURL().toURI())) { System.out.println(cookie); } これが無いと動かないのだけど..
  • 19. おさらい: HttpUrlConnection • Proxy var proxy = new Proxy( Proxy.Type.SOCKS, InetSocketAddress.createUnresolved("127.0.0.1", 7777)); HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy); ssh any-server –D7777 HTTP Proxyの場合は、 Proxy.Type.HTTP
  • 20. おさらい: HttpUrlConnection • Redirect – デフォルトはredirectを追従 var url = new URL("https://httpbin.org/redirect/2"); var conn = (HttpURLConnection) url.openConnection(); conn.setInstanceFollowRedirects(true);
  • 21. おさらい: HttpUrlConnection • Basic認証 – Header直接指定 – java.net.Authenticator • requestPasswordAuthenticationInstanceを 上書き • getPasswordAuthenticationを上書き
  • 22. おさらい: HttpUrlConnection • Basic認証 – Header直接指定 var url = new URL("https://httpbin.org/basic-auth/test-user/test-pass"); var conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty( "Authorization", "Basic " + Base64.getEncoder().encodeToString("test-user:test- pass".getBytes()));
  • 23. おさらい: HttpUrlConnection • Basic認証 – java.net.Authenticator その1 Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() != RequestorType.SERVER) return null; if (!"basic".equals(getRequestingScheme())) return null; if ("httpbin.org".equalsIgnoreCase(getRequestingHost())) { return new PasswordAuthentication("test-user", "test-pass".toCharArray()); } return null; } });
  • 24. おさらい: HttpUrlConnection • Basic認証 – java.net.Authenticator その2 Authenticator.setDefault(new Authenticator() { @Override public PasswordAuthentication requestPasswordAuthenticationInstance(String host, InetAddress addr, int port, String protocol, String prompt, String scheme, URL url, RequestorType reqType) { if (reqType != RequestorType.SERVER) return null; if (!"basic".equals(scheme)) return null; if ("httpbin.org".equalsIgnoreCase(host)) { return new PasswordAuthentication("test-user", "test-pass".toCharArray()); } return null; } });
  • 25. おさらい: HttpUrlConnection • SSL Errorを無視する通信 SSLContext sslcontext = SSLContext.getInstance("SSL"); sslcontext.init(null, new TrustManager[]{new X509TrustManager() { public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {} public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} public X509Certificate[] getAcceptedIssuers() { return null; } }}, null); HttpsURLConnection.setDefaultHostnameVerifier((s, sslSession) -> true); var url = new URL("https://expired.badssl.com/"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory()); } SSLContext Verifier SocketFactory
  • 27. おさらい: HttpUrlConnection • Logger指定 [logging.properties] handlers = java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level = ALL sun.net.www.protocol.http.HttpURLConnection.level = ALL System.setProperty("java.util.logging.config.file", "logging.properties");
  • 28. おさらい: HttpUrlConnection • javax.net.debug – Helpを指定するとhelpが表示される System.setProperty("javax.net.debug", ”help"); URL url = new URL("https://www.google.com"); var conn = (HttpURLConnection) url.openConnection(); conn.getInputStream(); https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/jsse/JSSERefGuide.html https://www.ibm.com/support/knowledgecenter/en/SSYKE2_7.0.0/com.ibm.java.security.component.70.doc/security- all ssl:record,handshake,keygen,session,defaultctx,sslctx,sessioncache,keymanager,trustmanager,plugga bility handshake:data,verbose plaintext,packet 指定可能な値 例: ssl:handshake,session 例: all カンマ区切りかコロン区切り
  • 30. ApacheHC/GET • GET var clientBuilder = HttpClientBuilder.create(); try (CloseableHttpClient client = clientBuilder.build()) { var getMethod = new HttpGet(URI.create("http://httpbin.org/get")); try (CloseableHttpResponse resp = client.execute(getMethod)) { String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); System.out.println(resp.getStatusLine()); System.out.println(body); } }
  • 31. ApacheHC/Accept Header • 注意点 – デフォルトのヘッダはAccept Header無し Host: httpbin.org Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.4 (Java/11.0.1) Accept-Encoding: gzip,deflate
  • 32. ApacheHC/Accept Header • Accept Header • (指定が無い場合は何でも受け付ける事を意 味する) • と、書いてあるのに.. RFC7231: Section 5.3.2 A request without any Accept header field implies that the user agent will accept any media type in response. https://tools.ietf.org/html/rfc7231#section-5.3.2
  • 33. ApacheHC/Accept Header • サイトによっては正しく返却されない – 例: http://httpstat.us/200 curl -H "Accept: */*" http://httpstat.us/200 # Content-Length: 6 200 OK curl -H "Accept:" http://httpstat.us/200 # Content-Length: 0
  • 34. ApacheHC/Accept Header • 各ブラウザのAccept Header – デフォルト値の一覧は以下のサイトを参照 https://developer.mozilla.org/en- US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values
  • 35. ApacheHC/Gzip • Gzipはデフォルトで処理される – gzipもdeflateも curl -s http://httpbin.org/gzip | gzip -d var clientBuilder = HttpClientBuilder.create(); try (CloseableHttpClient client = clientBuilder.build()) { var getMethod = new HttpGet("http://httpbin.org/gzip"); try (var resp = client.execute(getMethod)) { String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); System.out.println(body); } } 本当はAccept Encodingを付ける必要がある。 httpbin.org/gzip や httpbin.org/deflate は それを無視して送ってくるのでコードでは省略している。
  • 36. ApacheHC • Queryを作る時は URI Builder var uri = new URIBuilder("http://httpbin.org/get") .setCharset(StandardCharsets.UTF_8) .addParameter("hello", "world") .addParameter("sushi", "寿司") .setScheme("https") // HTTPSに変更 .setFragment("hello") .build(); https://httpbin.org/get?hello=world&sushi=%E5%AF%BF%E5%8F%B8#hello
  • 37. ApacheHC • Connection Pool var connManager = new PoolingHttpClientConnectionManager(); connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(100); var clientBuilder = HttpClientBuilder.create(); clientBuilder.setConnectionManager(connManager);
  • 38. ApacheHC • Request Headerをいじる var clientBuilder = HttpClientBuilder.create(); var defaultHeaders = Arrays.asList( new BasicHeader("Accept", "*/*") ); clientBuilder.setDefaultHeaders(defaultHeaders); var getMethod = new HttpGet(URI.create("http://httpbin.org/get")); getMethod.setHeader("Accept", "*/*"); 定数クラスがあるのでそれを使うと良い HttpHeaders, ContentType
  • 39. ApacheHC/POST var clientBuilder = HttpClientBuilder.create(); try (CloseableHttpClient client = clientBuilder.build()) { String jsonText = new Gson().toJson(Map.of("hello", "world")); HttpEntity entity = EntityBuilder.create() .setContentType(ContentType.APPLICATION_JSON) .setText(jsonText).build(); HttpPost postMethod = new HttpPost(URI.create("http://httpbin.org/post")); postMethod.setEntity(entity); try (CloseableHttpResponse resp = client.execute(postMethod)) { String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); System.out.println(resp.getStatusLine()); System.out.println(body); } }
  • 40. ApacheHC • やり方色々 – Client • HttpClientBuilder.create().build(); • HttpClients.custom().build(); • HttpClients.createDefault(); – Entity • EntityBuilder • 直接生成 BasicHttpEntity BufferedHttpEntity ByteArrayEntity FileEntity InputStreamEntity SerializableEntity StringEntity
  • 41. ApacheHC • やり方色々 – execute • 単純に実行 • HttpContext • ResponseHandler
  • 42. ApacheHC • Responseの処理はEntityUtilsが便利 – toString() – toByteArray() – writeTo() – consume() – ...
  • 43. ApacheHC/Redirect • デフォルトで追従 • OFFにする場合は、RequestConfig var requestConfig = RequestConfig.custom() .setRedirectsEnabled(false) //追従しない .build(); var clientBuilder = HttpClientBuilder.create(); clientBuilder.setDefaultRequestConfig(requestConfig); var getMethod = new HttpGet(URI.create("http://httpbin.org/redirect/3")); getMethod.setConfig(requestConfig);
  • 44. ApacheHC/Retry • DefaultHttpRequestRetryHandler – ConnectTimeoutExceptionはRetryしない – InterruptedIOExceptionのSubClassだけど https://hc.apache.org/httpcomponents-client- ga/httpclient/apidocs/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html
  • 45. ApacheHC/Cookie var cookieStore = new BasicCookieStore(); var clientBuilder = HttpClientBuilder.create(); clientBuilder.setDefaultCookieStore(cookieStore); // return Set-Cookie try (CloseableHttpClient client = clientBuilder.build()) { var get = new HttpGet("https://httpbin.org/cookies/set/hello/world"); try (CloseableHttpResponse resp = client.execute(get)) {} } // send Cookie: hello and sushi var cookie = new BasicClientCookie("sushi", " 🍣"); cookie.setDomain("httpbin.org"); cookieStore.addCookie(cookie); try (CloseableHttpClient client = clientBuilder.build()) { var get = new HttpGet("https://httpbin.org/cookies"); try (CloseableHttpResponse resp = client.execute(get)) {} } GET /cookies HTTP/1.1 Host: httpbin.org Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.4 (Java/11.0.1 Cookie: hello=world; sushi= 🍣 Accept-Encoding: gzip,deflate
  • 46. • Proxy • 認証有りProxy ApacheHC/Proxy var clientBuilder = HttpClientBuilder.create(); var proxy = new HttpHost(proxyHost, proxyPort, "http"); clientBuilder.setProxy(proxy); clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); var credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(proxyUser, proxyPassword)); clientBuilder.setDefaultCredentialsProvider(credsProvider);
  • 47. ApacheHC • Basic認証 var u = URI.create("https://httpbin.org/basic-auth/kuma/pass"); var credProvider = new BasicCredentialsProvider(); credProvider.setCredentials( new AuthScope(u.getHost(), u.getPort(), AuthScope.ANY_REALM, AuthScope.ANY_SCHEME), new UsernamePasswordCredentials("kuma", "pass")); var clientBuilder = HttpClientBuilder.create(); clientBuilder.setDefaultCredentialsProvider(credProvider); try (CloseableHttpClient client = clientBuilder.build()) { var getMethod = new HttpGet(u); try (CloseableHttpResponse resp = client.execute(getMethod)) { System.out.println(resp.getStatusLine()); } }
  • 48. ApacheHC • Basic認証 (ANYを使う:非推奨) – 最低限、Host, Portは指定した方が良い – Realmも本当は指定した方が良いけど credProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials("kuma", "pass"));
  • 49. ApacheHC/BadSSL • BadSSLスルー var sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }, new SecureRandom()); var clientBuilder = HttpClientBuilder.create(); clientBuilder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)); try (CloseableHttpClient client = clientBuilder.build()) { var getMethod = new HttpGet("https://expired.badssl.com/"); try (var resp = client.execute(getMethod)) { String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); System.out.println(body); } }
  • 50. ApacheHC/Async • HttpAsyncClient – Basicな方法 CloseableHttpAsyncClient client = HttpAsyncClients.createDefault(); client.start(); var request1 = new HttpGet("https://httpbin.org/get"); Future<HttpResponse> future = client.execute(request1, null); // any processing var resp = future.get(); System.out.println(resp.getStatusLine()); client.close();
  • 51. ApacheHC/Async • HttpAsyncClient – Consumer / Producer CloseableHttpAsyncClient client = HttpAsyncClients.createDefault(); client.start(); var producer = HttpAsyncMethods.create(new HttpGet("https://httpbin.org/get")); var consumer = new AsyncCharConsumer<HttpResponse>() { HttpResponse response; protected void onCharReceived(CharBuffer charBuffer, IOControl ioControl) { } protected void onResponseReceived(HttpResponse httpResponse) { this.response = httpResponse; } protected HttpResponse buildResult(HttpContext httpContext) { return this.response; } };
  • 52. ApacheHC/Async • HttpAsyncClient – Consumer / Producer var latch = new CountDownLatch(1); client.execute(producer, consumer, new FutureCallback<>() { public void completed(HttpResponse httpResponse) { latch.countDown(); } public void failed(Exception e) { latch.countDown(); } public void cancelled() { latch.countDown(); } }); latch.await(); System.out.println(consumer.response.getStatusLine()); client.close();
  • 53. ApacheHC/Debug • DEBUG – Loggerを設定する – Lobackの例 <logger name="org.apache.http" additivity="true"> <level value="DEBUG"/> </logger>
  • 54. ApacheHC/不満点 • HTTP/2は まだ 対応していない • WebSocket対応もまだ
  • 56. おさらい • java.net.http / HttpClient – Java11で正式APIになった – HTTP/2対応 – WebSocket対応 – 同期/非同期 処理対応 – Reactive Streamとして Request/Responseを処理 – Builderパターン
  • 57. java.net.http • Clientの作り方 もしくは [Header] GET /get HTTP/1.1 Content-Length: 0 Host: httpbin.org User-Agent: Java-http-client/11.0.1 var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://http2.pro/api/v1")) .build(); var resp = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); System.out.println(resp.statusCode()); System.out.println(resp.body()); var client = HttpClient.newBuilder().build(); [Body] {"http2":1,"protocol":"HT TP¥/2.0","push":1,"user_a gent":"Java-http- client¥/11.0.1"}
  • 58. java.net.http • デフォルトは、 – HTTP/2を勝手にやってくれる • サイトが対応していない場合はHTTP/1.1で通信 – Redirectは勝手にしてくれない – GZip/Deflateは勝手に処理してくれない • 便利なURIBuilderは存在しない
  • 59. java.net.http • ClientBuilder – authenticator – connectTimeout – cookieHandler – executor – followRedirects – priority – proxy – sslContext – version – sslParameters
  • 60. java.net.http • BodyHandlers – ofString – ofByteArray – ofByteArrayConsumer – ofFile – ofFileDownload – ofInputStream – ofLines – ofPublisher – ...
  • 61. java.net.http/redirect • Redirectの設定 var client = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) .build(); NEVER Redirectしない ALWAYS 常にRedirectする NORMAL 常にRedirectする(HTTPS->HTTPは除く)
  • 62. java.net.http/Header • Request Headerの編集 var request = HttpRequest.newBuilder() .header("Accept", "*/*") .header("User-Agent", "JJUG/1.0") .uri(URI.create("https://httpbin.org/get")) .build();
  • 63. java.net.http/POST • POSTとBody送信 var bodyString = new Gson().toJson(Map.of("hello", "world")); var client = HttpClient.newBuilder().build(); var request = HttpRequest.newBuilder() .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(bodyString)) .uri(URI.create("https://httpbin.org/post")) .build(); var resp = client.send( request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); System.out.println(resp.statusCode()); System.out.println(resp.body());
  • 64. java.net.http • BodyPublishers – noBody – ofString – ofByteArray – ofByteArrays – ofFile – ofInputStream – ...
  • 65. java.net.http/method • 他のメソッド – GET(), POST(), DELETE(), PUT() – HEADやPATCHは? • methodを使う .method("HEAD", HttpRequest.BodyPublishers.noBody()) .method("PATCH", HttpRequest.BodyPublishers.ofString(bodyString))
  • 66. java.net.http/async • 非同期: client.sendAsync var client = HttpClient.newBuilder().build(); var request = HttpRequest.newBuilder(URI.create("https://httpbin.org/get")).build(); CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); // CompletableFutureなので処理を繋げる事ができる future.thenApply(HttpResponse::body) .exceptionally(e -> "Error!! :" + e.getMessage()) .thenAccept(System.out::println); //future.get();
  • 67. java.net.http/content-encoding • BodySubscribers.mappingを使う – ※JDKの不具合で動きません https://bugs.openjdk.java.net/browse/JDK-8217264 private HttpResponse.BodySubscriber<InputStream> gzippedBodySubscriber( HttpResponse.ResponseInfo responseInfo) { // 本当はheaderのContent-Encodingを確認する return HttpResponse.BodySubscribers.mapping( HttpResponse.BodySubscribers.ofInputStream(), this::decodeGzipStream); } private InputStream decodeGzipStream(InputStream gzippedStream) { try { return new GZIPInputStream(gzippedStream); } catch (IOException ex) { throw new UncheckedIOException(ex); } } var resp = client.send(request, this::gzippedBodySubscriber);
  • 68. java.net.http/content-encoding • Workaround-1 var client = HttpClient.newBuilder().build(); var request = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/gzip")) .build(); var resp = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); InputStream in = resp.body(); if ("gzip".equals(resp.headers().firstValue("Content-Encoding").get())) { in = new GZIPInputStream(resp.body()); } System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
  • 69. java.net.http/content-encoding • Workaround-2 var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) .thenApply(resp -> { String ce = resp.headers().firstValue("Content-Encoding").get(); if ("gzip".equals(ce)) { try { return new GZIPInputStream(resp.body()); } catch (IOException e) { throw new UncheckedIOException(e); } } return resp.body(); }); var in = future.get(); System.out.println(IOUtils.toString(in, StandardCharsets.UTF_8));
  • 71. java.net.http/custom handler • 例: json public static class JsonBodyHandler<T> implements HttpResponse.BodyHandler<T> { private Class<T> type; private JsonBodyHandler(Class<T> type) { this.type = type; } public static <T> JsonBodyHandler<T> jsonBodyHandler(Class<T> type) { return new JsonBodyHandler<>(type); } @Override public HttpResponse.BodySubscriber<T> apply( HttpResponse.ResponseInfo info) { return HttpResponse.BodySubscribers.mapping( HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), s -> new Gson().fromJson(s, this.type)); } } var resp = client.send(request, JsonBodyHandler.jsonBodyHandler(MyJson.class)); MyJson body = resp.body();
  • 72. java.net.http/query-param, form-encoded • 用意されていない – 便利なライブラリが他にあるのでそれを利用して BodyにStringとして渡す – (例) Spring関連のLibrary – (例) Apache HttpClient 😨
  • 73. • デフォルトでは無効化されている java.net.http/cookie var cookieHandler = new CookieManager(); var client = HttpClient.newBuilder().cookieHandler(cookieHandler).build(); var u = URI.create("https://httpbin.org/cookies/set/hello/world"); var request1 = HttpRequest.newBuilder(u).build(); client.send(request1, HttpResponse.BodyHandlers.ofString()); var cookie = new HttpCookie("sushi", "tenpura"); cookie.setDomain(u.getHost()); cookie.setPath("/"); cookie.setVersion(0); cookieHandler.getCookieStore().add(u, cookie); var request2 = HttpRequest.newBuilder(URI.create("https://httpbin.org/cookies")).build(); var resp2 = client.send(request2, HttpResponse.BodyHandlers.ofString()); Version0が⼤事 Version0 -> Netscape style Version1 -> RFC2965/2109
  • 74. java.net.http/basic auth • Basic認証 – HttpUrlConnectionのサンプルと同じ – 利用クラスも同じなのでそちらを参照 var client = HttpClient.newBuilder() .authenticator(authenticator) .build();
  • 75. java.net.http/download • Download var client = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .build(); var u = URI.create("https://github.com/AdoptOpenJDK/openjdk11- binaries/releases/download/jdk-11.0.3%2B7/OpenJDK11U- jdk_x64_linux_hotspot_11.0.3_7.tar.gz"); var request = HttpRequest.newBuilder().uri(u).build(); Path f = Paths.get("/tmp/").resolve(Path.of(u.getPath()).getFileName()); var resp = client.send(request, HttpResponse.BodyHandlers.ofFile(f)); System.out.println(resp.statusCode()); System.out.println(f.toFile().length());
  • 76. java.net.http/proxy with authentication System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); var proxySelector = ProxySelector.of( InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT)); var client = HttpClient.newBuilder() .proxy(proxySelector) .authenticator(new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() == RequestorType.PROXY) { return new PasswordAuthentication(PROXY_USER, PROXY_PASS); } return null; } }).build(); var u = URI.create("https://httpbin.org/get"); var request1 = HttpRequest.newBuilder(u).build(); var resp = client.send(request1, HttpResponse.BodyHandlers.ofString()); https://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html jdk8u111より これが無いとhttpsのurlの 認証処理がskipされる Proxyの時だけ認証
  • 77. java.net.http/badSSL • SSLContextを設定できるので、ApacheHCと同じ var sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }, new SecureRandom()); var client = HttpClient.newBuilder().sslContext(sslContext).build(); var u = URI.create("https://expired.badssl.com/"); var request1 = HttpRequest.newBuilder(u).build(); var resp = client.send(request1, HttpResponse.BodyHandlers.ofString());
  • 79. java.net.http/server-push var httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build(); var pageRequest = HttpRequest.newBuilder(URI.create("https://http2.golang.org/serverpush")).build(); AtomicInteger cnt = new AtomicInteger(1); var futures = new CopyOnWriteArrayList<CompletableFuture<HttpResponse<String>>>(); HttpResponse.PushPromiseHandler<String> handler = (initiatingRequest, pushPromiseRequest, acceptor) -> { System.out.println("Promise request: " + pushPromiseRequest.uri()); var pushedFuture = acceptor.apply(HttpResponse.BodyHandlers.ofString()); pushedFuture = pushedFuture.thenApply(resp -> { System.out.println("[" + cnt.getAndIncrement() + "] Pushed response: " + resp.uri()); return resp; }); futures.add(pushedFuture); }; applyPushPromise( HttpRequest initiatingRequest, HttpRequest pushPromiseRequest, Function<HttpResponse.BodyHandler<String>, CompletableFuture<HttpResponse<String>>> acceptor )
  • 80. java.net.http/server-push var future = httpClient.sendAsync( pageRequest, HttpResponse.BodyHandlers.ofString(), handler); future.thenAccept(pageResponse -> { System.out.println("Page response status code: " + pageResponse.statusCode()); }).join(); var array = futures.toArray(new CompletableFuture[0]); CompletableFuture.allOf(array).get(); Promise request: https://http2.golang.org/serverpush/static/jquery.min.js?1558076345232851844 Promise request: https://http2.golang.org/serverpush/static/godocs.js?1558076345232851844 Promise request: https://http2.golang.org/serverpush/static/playground.js?1558076345232851844 Promise request: https://http2.golang.org/serverpush/static/style.css?1558076345232851844 [1] Pushed response: https://http2.golang.org/serverpush/static/style.css?1558076345232851844 [2] Pushed response: https://http2.golang.org/serverpush/static/playground.js?1558076345232851844 [3] Pushed response: https://http2.golang.org/serverpush/static/godocs.js?1558076345232851844 Page response status code: 200 [4] Pushed response: https://http2.golang.org/serverpush/static/jquery.min.js?1558076345232851844
  • 81. java.net.http/web-socket var u = URI.create("wss://echo.websocket.org"); var client = HttpClient.newBuilder().build(); var listener = new WebSocket.Listener() { public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { System.out.println("receive:" + data + ", last=" + last); return WebSocket.Listener.super.onText(webSocket, data, last); } }; WebSocket wsock = client.newWebSocketBuilder().buildAsync(u, listener).join(); wsock.sendText("hello world 1", false); wsock.sendText("hello world 2", true); TimeUnit.SECONDS.sleep(2); wsock.sendClose(WebSocket.NORMAL_CLOSURE, "BYE");
  • 83. • もしくは • もしくは – LoggerName java.net.http/debug -Djava.net.HttpClient.log= errors,requests,headers,frames[:control:data:window:all..],co ntent,ssl,trace,channel jdk.httpclient.HttpClient
  • 85. java.net.http • reactive streamの話をしていない? – 今まで使っていたPublisher, SubScriberが そうなのです。 public interface BodySubscriber<T> extends Flow.Subscriber<List<ByteBuffer>> { CompletionStage<T> getBody(); void onSubscribe(Subscription subscription); void onNext(List<ByteBuffer> item); void onError(Throwable throwable); void onComplete(); } public interface BodyPublisher extends Flow.Publisher<ByteBuffer> { long contentLength(); void subscribe(Subscriber<? super ByteBuffer> subscriber); }