学而不思则罔,思而不学则殆
【Android】OkHttp简单入门
官方文档
https://github.com/square/okhttp
测试服务端
测试服务端https://github.com/aJanefish/ZyServer
导入JAR
根据需要选择导入的版本
//Kotlin
implementation "com.squareup.okhttp3:okhttp:4.9.0"
//Java
implementation "com.squareup.okhttp3:okhttp:3.14.9"
使用范例
同步请求+GET
String run() throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://localhost:3434/okhttp")
.build();
try (Response response = client.newCall(request).execute()) {
String body = response.body().string();
ResponseShow.show(response, body);
return body;
}
}
请求头:
GET http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: zy/test
响应头:
http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:08:51 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":""}
同步请求+POST+JSON
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post() throws IOException {
String json = "{\"name\":\"zy\",\"age\":18}";
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url("http://localhost:3434/okhttp")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
String content = response.body().string();
ResponseShow.show(response, content);
return content;
}
}
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 22
Content-Type: application/json; charset=UTF-8
Host: localhost:3434
User-Agent: zy/test
{"name":"zy","age":18}
http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:15:47 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"{\"name\":\"zy\",\"age\":18}"}
添加头部Headers
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
//.url("https://api.github.com/repos/square/okhttp/issues")
.url("http://localhost:3434/okhttp")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
GET http://localhost:3434/okhttp HTTP/1.1
Accept: application/json; q=0.5
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: OkHttp Headers.java
http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:38:45 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":""}
异步请求
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
//.url("http://publicobject.com/helloworld.txt")
.url("http://localhost:3434/okhttp")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseShow.show(response);
}
});
}
GET http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: zy/test
http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:46:00 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"GET from Server"}
Authenticate - 身份验证
public final class Authenticate {
private final OkHttpClient client;
public Authenticate() {
// Give up, we've already attempted to authenticate.
Authenticator authenticator = new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
if (response.request().header("Authorization") != null) {
return null; // Give up, we've already attempted to authenticate.
}
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder().header("Authorization", credential).build();
}
};
client = new OkHttpClient.Builder().authenticator(authenticator).build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
public static void main(String... args) throws Exception {
new Authenticate().run();
}
}
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
----------------------------------------------
http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 13:48:40 GMT
Content-Type: text/plain
Content-Length: 1226
Last-Modified: Thu, 12 Jun 2014 01:53:57 GMT
Connection: keep-alive
ETag: "53990835-4ca"
Accept-Ranges: bytes
@@@@@\
@@@@@@@@
@@@@@@@@@@@@@@@@@@@.
@/ \@@@@@@@@@@@@@@@@.
@ @@@@@@@@@@@@@@@@+
@\ /@@@@@@@@"*@@/^@/
\@@@@@@@@@@/ " "
@@@@@@@@
@@@@@/
:@@@.
.@@@@@@@: +@@ `@@ @@` @@ @@
.@@@@'@@@@: +@@ `@@ @@` @@ @@
@@@ @@@ +@@ `@@ @@` @@ @@
.@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@
@@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@
@@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@
@@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+
@@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@#
@@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+
@@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@
@@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@
@@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@
@@:
@@:
@@:
这种情况下会先返回401状态码,然后在携带Authorization的头部带上账号和密码。详情可以查看HTTP协议分享
缓存
public final class CacheResponse {
private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient.Builder()
.cache(cache)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
String response1Body;
try (Response response1 = client.newCall(request).execute()) {
if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse());
}
System.out.println("------------------------------------------------");
String response2Body;
try (Response response2 = client.newCall(request).execute()) {
if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
System.out.println("Response 2 cache response: " + response2.cacheResponse());
System.out.println("Response 2 network response: " + response2.networkResponse());
}
System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
public static void main(String... args) throws Exception {
new CacheResponse(new File("CacheResponse.tmp")).run();
}
}
Response 1 response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 cache response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 network response: null
------------------------------------------------
Response 2 response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 cache response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 network response: null
Response 2 equals Response 1? true
缓存文件如下:
文件成对出现,xxx.0 保存响应头 xxx.1 保存正文
CancelCall 取消请求
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
final long startNanos = System.nanoTime();
final Call call = client.newCall(request);
// Schedule a job to cancel the call in 1 second.
executor.schedule(new Runnable() {
@Override
public void run() {
System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
call.cancel();//1秒后取消请求
System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
}
}, 1, TimeUnit.SECONDS);
System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
try (Response response = call.execute()) {
System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
(System.nanoTime() - startNanos) / 1e9f, response);
} catch (IOException e) {
System.out.printf("%.2f Call failed as expected: %s%n",
(System.nanoTime() - startNanos) / 1e9f, e);
e.printStackTrace();
}
}
发送一个请求,服务端会延时两秒,客户端一秒后取消请求。
0.00 Executing call.
1.01 Canceling call.
1.01 Canceled call.
1.01 Call failed as expected: java.io.IOException: Canceled
java.io.IOException: Canceled
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:119)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
at okhttp3.recipes.CancelCall.run(CancelCall.java:51)
at okhttp3.recipes.CancelCall.main(CancelCall.java:62)
查看取消请求的源码:
//RetryAndFollowUpInterceptor.java
private volatile boolean canceled;
public void cancel() {
canceled = true;
StreamAllocation streamAllocation = this.streamAllocation;
if (streamAllocation != null) streamAllocation.cancel();
}
@Override public Response intercept(Chain chain) throws IOException {
...
while (true) {
if (canceled) {//取消请求,抛出IOException异常
streamAllocation.release();
throw new IOException("Canceled");
}
...
}
}
追一下cancel的最终调用的是Socket.close方法:
//RealConnection.java
public void cancel() {
// Close the raw socket so we don't end up doing synchronous I/O.
closeQuietly(rawSocket);
}
public static void closeQuietly(Socket socket) {
if (socket != null) {
try {
socket.close();
} catch (AssertionError e) {
if (!isAndroidGetsocknameError(e)) throw e;
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}
证书
private final OkHttpClient client;
public CertificatePinning() {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")//错误的
//.add("publicobject.com", "sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=")//正确的
.build();
client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/robots.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
}
}
错误的证书:
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Peer certificate chain:
sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=: CN=privateobject.com
sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=: CN=DST Root CA X3, O=Digital Signature Trust Co.
Pinned certificates for publicobject.com:
sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=
at okhttp3.CertificatePinner.check(CertificatePinner.java:204)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:312)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:268)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:256)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:134)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:113)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
at okhttp3.recipes.CertificatePinning.run(CertificatePinning.java:44)
at okhttp3.recipes.CertificatePinning.main(CertificatePinning.java:54)
正确的证书:
http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 14:13:13 GMT
Content-Type: text/plain
Content-Length: 43
Last-Modified: Tue, 09 Jun 2009 00:00:00 GMT
Connection: keep-alive
ETag: "4a2da600-2b"
Accept-Ranges: bytes
User-agent: *
Disallow: /glazedlists/wiki/
----------------------------------------------
sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=
sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg
CheckHandshake
/**
* Rejects otherwise-trusted certificates.
*/
private static final Interceptor CHECK_HANDSHAKE_INTERCEPTOR = new Interceptor() {
Set<String> blacklist = Collections.singleton(
"sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=");
@Override
public Response intercept(Chain chain) throws IOException {
for (Certificate certificate : chain.connection().handshake().peerCertificates()) {
String pin = CertificatePinner.pin(certificate);
System.out.println("pin:" + pin);
if (blacklist.contains(pin)) {
throw new IOException("Blacklisted peer certificate: " + pin);//抛出异常
}
}
return chain.proceed(chain.request());
}
};
private final OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(CHECK_HANDSHAKE_INTERCEPTOR)
.build();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
pin:sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=
pin:sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=
----------------------------------------------
http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 14:15:18 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes
\\ //
\\ .ooo. //
.@@@@@@@@@.
:@@@@@@@@@@@@@:
:@@. '@@@@@' .@@:
@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@
:@@ :@@@@@@@@@@@@@@@@@. @@:
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@@@@@@@@@@@@@@@
'@@@@@@@@@@@@@@@'
@@@@ @@@@
@@@@ @@@@
@@@@ @@@@
'@@' '@@'
:@@@.
.@@@@@@@: +@@ `@@ @@` @@ @@
.@@@@'@@@@: +@@ `@@ @@` @@ @@
@@@ @@@ +@@ `@@ @@` @@ @@
.@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@
@@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@
@@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@
@@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+
@@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@#
@@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+
@@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@
@@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@
@@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@
@@:
@@:
@@:
----------------------------------------------
自定义超时时间
自定义超时时间,三个维度,连接超时,写入超时,读取超时。
private final OkHttpClient client;
public ConfigureTimeouts() throws Exception {
client = new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("Response completed: " + response);
}
}
超时过后抛出异常:
Exception in thread "main" java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:345)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:217)
at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:212)
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
at okhttp3.recipes.ConfigureTimeouts.run(ConfigureTimeouts.java:40)
at okhttp3.recipes.ConfigureTimeouts.main(ConfigureTimeouts.java:46)
这三个维度的时间默认都是10s:
//OkHttpClient.java
public static final class Builder {
...
public Builder() {
...
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
}
设置时间:
//RealConnection.java
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
...
rawSocket.setSoTimeout(readTimeout);//设置读取时间
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
...
}
//Platform.java
public void connectSocket(Socket socket, InetSocketAddress address,
int connectTimeout) throws IOException {
socket.connect(address, connectTimeout);//设置连接时间
}
//RealConnection.java
/**
* To make an HTTPS connection over an HTTP proxy, send an unencrypted CONNECT request to create
* the proxy connection. This may need to be retried if the proxy requires authorization.
*/
private Request createTunnel(int readTimeout, int writeTimeout, Request tunnelRequest,
HttpUrl url) throws IOException {
// Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
String requestLine = "CONNECT " + Util.hostHeader(url, true) + " HTTP/1.1";
while (true) {
Http1Codec tunnelConnection = new Http1Codec(null, null, source, sink);
source.timeout().timeout(readTimeout, MILLISECONDS); //设置读取时间
sink.timeout().timeout(writeTimeout, MILLISECONDS);//设置写入时间
...
}
}
LoggingInterceptors - 自定义拦截
private static final Logger logger = Logger.getLogger(LoggingInterceptors.class.getName());
private final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
public void run() throws Exception {
Request request = new Request.Builder().url("https://publicobject.com/helloworld.txt").build();
Response response = client.newCall(request).execute();
response.body().close();
}
//自定义普通拦截器
private static class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
long t1 = System.nanoTime();
Request request = chain.request();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
request.url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
效果:
九月 24, 2020 7:18:59 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Sending request https://publicobject.com/helloworld.txt on null
九月 24, 2020 7:19:01 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Received response for https://publicobject.com/helloworld.txt in 2651.0ms
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 23:19:02 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes
测试访问百度:
九月 24, 2020 7:19:56 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Sending request https://www.baidu.com/ on null
九月 24, 2020 7:19:56 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Received response for https://www.baidu.com/ in 211.6ms
Accept-Ranges: bytes
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 227
Content-Type: text/html
Date: Wed, 23 Sep 2020 23:19:57 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BD_NOT_HTTPS=1; path=/; Max-Age=300
Set-Cookie: BIDUPSID=11E0C713AFEC643D67097169E2DA6820; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1600903197; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=11E0C713AFEC643D84B10074D8C34F7E:FG=1; max-age=31536000; expires=Thu, 23-Sep-21 23:19:57 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Strict-Transport-Security: max-age=0
Traceid: 1600903197025404673012799908543957356225
X-Ua-Compatible: IE=Edge,chrome=1
PerCallSettings
不同的请求不同的设置
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
.build();
// Copy to customize OkHttp for this request.
OkHttpClient client1 = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
try (Response response = client1.newCall(request).execute()) {
System.out.println("Response 1 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 1 failed: " + e);
}
// Copy to customize OkHttp for this request.
OkHttpClient client2 = client.newBuilder()
.readTimeout(3000, TimeUnit.MILLISECONDS)
.build();
try (Response response = client2.newCall(request).execute()) {
System.out.println("Response 2 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 2 failed: " + e);
}
}
结果是第一次请求(500<1000)会抛出异常,第二场正常(3000>1000)
Response 1 failed: java.net.SocketTimeoutException: timeout
Response 2 succeeded: Response{protocol=http/1.1, code=200, message=OK, url=http://httpbin.org/delay/1}
PostFile
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
File file = new File("DiyREADME.md");
Request request = new Request.Builder()
//.url("https://api.github.com/markdown/raw")
.url("http://localhost:3434/okhttp")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
文件内容如下:
请求头:
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 136
Content-Type: text/x-markdown; charset=utf-8
Host: localhost:3434
User-Agent: zy/test
OkHttp
======
An HTTP & HTTP/2 client for Android and Java applications. For more information see [the website][1] and [the wiki][2].
响应头:
http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:36:45 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"POST from Server"}
PostForm
发送表单
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody formBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
//.url("https://en.wikipedia.org/w/index.php")
.url("http://localhost:3434/okhttp")
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 22
Content-Type: application/x-www-form-urlencoded
Host: localhost:3434
User-Agent: zy/test
search=Jurassic%20Park
http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:40:59 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :search=Jurassic%20Park"}
PostMultipart
post多个表单
private static final String IMGUR_CLIENT_ID = "9199fdef135c122";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "李二狗的生活")
.addFormDataPart("name", "zy")
.addFormDataPart("age", "18")
.addFormDataPart("sex", "man")
.addFormDataPart("image", "logo-square.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
//.url("https://api.imgur.com/3/image")
.url("http://localhost:3434/okhttp")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
请求头:
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Authorization: Client-ID 9199fdef135c122
Connection: keep-alive
Content-Length: 4404
Content-Type: multipart/form-data; boundary=d51a0e43-79fd-46aa-b46a-49a83afec105
Host: localhost:3434
User-Agent: zy/test
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="title"
Content-Length: 18
李二狗的生活
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="name"
Content-Length: 2
zy
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="age"
Content-Length: 2
18
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="sex"
Content-Length: 3
man
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="image"; filename="logo-square.png"
Content-Type: image/png
Content-Length: 3740
�PNG
IHDR ? ? tEXtSoftware Adobe ImageReadyq�e< $iTXtXML:com.adobe.xmp <?xpacket begin="�?" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS6 (Macintosh)" xmpMM:InstanceID="xmp.iid:FE3A4788A97A11E29AEC94190CEBECFC" xmpMM:DocumentID="xmp.did:FE3A4789A97A11E29AEC94190CEBECFC"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:FE3A4786A97A11E29AEC94190CEBECFC" stRef:documentID="xmp.did:FE3A4787A97A11E29AEC94190CEBECFC"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>? IDATx��]�UU���Cm&�D'P���h��?*3mQ��??&-\?%��J�raA�PjVcE����C$?R�H�δ�u��???;�}?=?5?�ֿf�s��g�}��???35���F�P?�
eB�B�P�P(*ʄ
�B�P�P&T(��Ai6����z���[:��?=�,���CKO?P$ASSSe2��+,�i����y��-]m?K7?Stu?.Kʀ]|���u(�� GX����`��ku*(�� {X������?*UDŽ,��B��:�Ȅ�*h,ޥ�AQ
�m=8a?,��b?�OP�W��u��?,����]��YzX��?L?$Y�\���r���z�x�zK߳�����[���SS�c���5A�@?Fgfis?�?Yz�N=E?$a+⽞H��S����L��?�AcL$ L��h�����Lط��:,�v+��-}��?:=-}?:?K?,]�SPQ? �
�xdZ-ʹt?87�� :�E?.�?�I�g��?:?
e����!?'��O�|?��?
��8�����ܷ\,��4A
/?>Kc�fMr?,�b��D����T�q��?.>I�n��g��?&�jC6@=��?-??A{w��
����+K�J�������,��bwW�f�g�a��:���Ǖ���X)��[�_
���Ӛ�`?)T�]ܚR��?0?4C}6�W�c�?2�tL��Ju
%H?�Ip~�I�_?&?<��Ź�T?Oup"U�nBrt�fN�e�T̅E2?|��MG7T}?"��L�}�ϢV$��?0?�qM�� mI��k��L�U�q��\���>��%?.>&%,?/y?;?s��/���RqU�\��VRZ��d~�k+U??? +�q��? �a??g8?7������N?B?6�~Ԙ�R?>c�̊��h�H��~n"m*?$�ioO/o��?���W��/��*1?4? V�gՎ?����i��+h�w�TS?i3as��:�Ȅ��M��qXbrσS(ʚ d??W�c 5�b?
?Q]���B������I[L����N��F�~B�BQ>�B�P&T(?
?2�B�L�P(?
eB�B!?? :?iL\E? IEND�B`?
--d51a0e43-79fd-46aa-b46a-49a83afec105--
最后一个是分段图片,有乱码。是分段传输,每一段都有自己的Content-Disposition,Content-Type,Content-Length等
响应头:
http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:50:26 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"title\"\r\nContent-Length: 18\r\n\r\n鏉庝簩鐙楃殑鐢熸椿\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"name\"\r\nContent-Length: 2\r\n\r\nzy\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"age\"\r\nContent-Length: 2\r\n\r\n18\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"sex\"\r\nContent-Length: 3\r\n\r\nman\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"image\"; filename=\"logo-square.png\"\r\nContent-Type: image/png\r\nContent-Length: 3740\r\n\r\n塒NG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0000�\u0000\u0000\u0000<\b\u0006\u0000\u0000\u0000�\u0013輯\u0000\u0000\u0000\u0019tEXtSoftware\u0000Adobe ImageReadyq蒭<\u0000\u0000\u0003$iTXtXML:com.adobe.xmp\u0000\u0000\u0000\u0000\u0000<?xpacket begin=\"锘�\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> <x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 \"> <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> <rdf:Description rdf:about=\"\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\" xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\" xmlns:stRef=\"http://ns.adobe.com/xap/1.0/sType/ResourceRef#\" xmp:CreatorTool=\"Adobe Photoshop CS6 (Macintosh)\" xmpMM:InstanceID=\"xmp.iid:FE3A4788A97A11E29AEC94190CEBECFC\" xmpMM:DocumentID=\"xmp.did:FE3A4789A97A11E29AEC94190CEBECFC\"> <xmpMM:DerivedFrom stRef:instanceID=\"xmp.iid:FE3A4786A97A11E29AEC94190CEBECFC\" stRef:documentID=\"xmp.did:FE3A4787A97A11E29AEC94190CEBECFC\"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end=\"r\"?>\u0013�\u0016\u0011\u0000\u0000\u000b\u000eIDATx陟]\u000b擴U\u0019廾菴\u0001m&〥'P\u0014抛鑘吋\u0016�*\u00103mQ墮�\u0019�&-\\\u0010�%欓J宺aA匬j\u0016Vc\u0005E\u0016\u0001\u0001C$�\u0014\u0013R襀堊埼纯u境�?�;鐪}�=�5�分縡焥髻g焳隹���35眄鞦�\u000e輙\b\u0014\neB匓橮(\u0013*\u0014蕜\n匓橮&T(\u0014徘Ai6抟意瀦伐蟍:且�\u0012=罂,鬋KO�\u0014P$ASSSe2+,輎│你y弗\u0013-]m�\u0006K7�\u0014St\u0005u�.K\u000b蕗\u0001]|菀飖\u001a(獫\tGX含屒`ku*(獣\t{X嚎\u0002漆圮�*\u0014U莿\u0013,剑B柒\u0003:\u001d\u0014杖劊*h,蕙覣Q\n\u001d=8a�\u0017,綉b?嶰P侇u触�,接\u0004啫]柖YzX�\u0014L�$Y馶摼卹ē鍠z弞K叱舯垍鎇栨甏S\u0014S\u001d峜嘛涒�\b\u001e5A怈�\u000bFg\u0018fis\u0004\u0003�\u0012蚧�\u0016Yz籒=E�$a\u001c\u001e+饨濰┹S敞殯L\u0010暢�\u0004慉cL\u0010$\u0000L丛h個\u0001參銵胤堶:,+售-}找\u000f�:=-}�\u0004\u0011:�\u0010K�,]PQ�\u0000顨\n�\u0017\r鈞d\u0016\u0006\u0004Z-痛t�87菀\t:\u0005\u0015欵�\u001f.\u0015骞�\u0017艻蚲篷�:�\ne曼鲀!�'兼O�\u000e♀�\n桡8瞟獍乙嬡穃,4A\n\u0015\u0016赖\u001e讔6丄Э\t�<m&H藗�\u0003诌蓣Y:乙洊朰�/~\u001bBj琮_Ix�~&�\u0003哯�扞f鐱?希蕩`�>|G郇\u0011麶耘飃饼�-瓀陉乡楚M鼏愿覮\u0010z\b7詶,綒�?u�\u0012\f帻\u000e贝擙賖閣蕜��\u001f[鶧潞�,5\u0015��\u0014�3,�%�5蠐掆<�'�\u001bSo灔)�\u000e樌\r\u0012JY\u0004\u000e<#闙臭i柉2仜(\t>h�\u001cL鄎2D\u0014\u0010忹9\u0013\u001d氨�\u0004馏慷4躌3�?`i糞鰾擇蔽岢瀗傡\u001c鹘贤聞譡歛仜壤婾\u001d屠'�\u0013珄�\"鱋菿<獉mC羮に�/g檇Ws5?乱jq就�(穤鬐F4�\u001aQo\f欮Z�\u0001!\u0001穜�\b1恈8叛b)�J壷拝\u0001!誺堛cM\u0010╭嬅��\u0004ls�\u0010\u0016�\u001f�\"\u001a\r藵\t_蟥麩\u0012麸^G]倸t�)L\b軆�&媍鳷/隣u\u0013{覜L\u0010\u001a\u0007\u001cGU鞔\b暪-侫�:ㄌ+8櫏D\u001c�\u0005獊惀ア雾柧c榄場�9\u0004^,\u0011荗ql\u001ax�\u0017y緸鉽幱熱烨Q;\u000b拽8�&�(朾躏\"q|\u0018'\u0013V鼰櫪g鴄Ko髄w綪� 9�4A栎=T崧�\u001bx廲,龜鐝p\fF怕\u0000氰\u0004塺姤谢�\u0014�\r折媱�\u001bl�6峤N\u0016迳\\t钺竾鐴倒炃薡\u000f齳囊\u001e@}\u001f舚\u000e%.躆?�=ae\u0000/j;W鍯y\u000eai2�\r/�>K\u000b\u0013\u0018c爁Mr�,隻D緞鳈T愔�\u0016.>I苙兂g趿�&鴍C6@=\u001f尿�-�?A{w差\n\u001e攲痪+K翵羶苓黎駠,棵bw\u0005W鄁猤漚海:枨晭报X\u0018)鲲[峗\n�&緲\u0017羳蠬�&$飌弙W�8\u0013\"觚�2a醼=)�$蒲\u00180�+交W滰祄b�6癘\u0019硫n狽绝B\t�\u00063\u001c尕阗6杂�<瘷\u0016褊'M�\u000e\t\u0017岔瀖遤\u0002讖�$<W櫚�\u0000昮\u0001�<0\u0016|勄\u0012嫺/捀@旓务搌溔+)<覽\u0011[\u001ai絔沜y詤+h_娘2\u0012i3%汜\u0011T莭{Hx�?�6嗛灠脖噷q?ツj撋k勥M~Q�\u001f抒灚:懘i犧d傘[=誫\t\u0018N緬皀\\傯 Q�>糋蘚os$�\u0001G\u0005蕭�\u0010徍=L鶀R�\u0005&枭B=B\u0014逝无1奈<顡缘鉩}n�\u0018鱬\u001e罖攌話象顖瀑\u001f耞�=傕擁%ǒPg乀?\u0002V顠p縂]\f跊\u0015 \u0019稾歮傪5`<US悒X=蟾G飫\u0013試\t;[�\u000fP捲�\u001dt7~A\u0000!\u000eNi甲�?�\u0006rt畈L�=襒撎簸駍>�\u000bdP溋2鰗O{\\+3)�\u000fq�(焛::02徂蝕�\u001f\u001a!晇s_�:餛鎾@=�\u0000\u000bB�\u0017咊何tt臈\u001d蕽\t7涾魻Ra毶8�1幾xJ�\u0010{E\u0019\u0001膕\u0004撓獭_版\u001d\u001b窕敹\r\u001e�\u000e帎凱城鸶)G&淧纖矪,D\u0017�;\u0013猽4wH)u剁礢Dy�(肔�(�\u0003Mn顔籦~梣�>\u0001颭掙\u001bK煈�<磤飂炐娻;=2G{D\u001fg罷&,3褥凁\u001f�$翇ΓI�迗v鏒▉賭靾茦:忕�\u0002⑼(圇r擓\u000fW\u0018媴炡�2SX�\u001a\u001c舴耻楉� \f丆胞Te买\u0005\"餺\u0010莝濄lc}�\tb=C繞�:筢\u001f�:\u0000\u0011%�\u0013鬵V律�樕�\u001f} �\t展K澟`~\u0002c\b$鄗O�8)f歀�\b\u0016\u0006\u0004H膃估捄樝RG[穆jg聖\u0015~瘺蓪!n\"覮Κ譆)偞\u001e�\u0010~�1\u001et剁僺�\u001f,#cb)%dc\u0016)俋S勍M\u0017啑�1D斍q倿樵镮fZk2)@Q諸<穼[滵�\u001a鶢�'\u001e\u0007p1�5'褴N觵@@.繼{勮骽\u0013M廷灺a徿G\u0004Y�\u0010罕\t撇�\r3uE糤Z&p\u0018B~a2駹X蒿埞�\u0011距}1礅c耡柶gI[)=眬卵逴\\碂�\"|�-�?a!$\u000bEb偮槻卪譺鎅e倶袣埮{\u0001ハOL�?剠\u0012壻璡t鰭�\u0007e賁�6櫶鰊1\u0002#厝叓檰マ�>f骙wr�9嘄\r眹嬘毊`�)T頬\u0012軞R化�0�4C}6鱓籧陙\u0019\u0010�2胻LJu\n%H?嘔p~矷鯻�\u0011&�<粭殴乀?Ou\u0018p\"U韓Brt唂N阤嶵\u001d虆E2�\u001b|倦MG7T}�\"牦㎜竲幭\u000e$優�0�\u0012鰍M睒 mI\u0018邕k嗒麹乁\u0011\u0017莙杏\\惍鈯>敁{\b%�.>\u001f&%,�/y�;�\u0006\u001a\u0018s起/槸峈q\u0005U芢屡VRZ崲d~\u000f+U�?� +\u0004猶\u001f�\t瀉\u001b�\u001b�\u0005g8\u0019�7\u0017煣仫嚺\u0002砃�B�6蓗詷躌�>c偺姵竓濰W~n\u0015\u001e\"m*�$猧oO/o顶�\u0005睉梂垺\u0005/杵*1\u001a�4\u0019�\tV鷊諑�\u001f尹燉i\u001b+h渨鑄S�\u0002i3as\u0005嵟\u0013:\u001d\u0014杖劙圡珋qXbr蟽S(蕷\t\u0001d�?W芻\u00005鬮�\n妀fB\u0000f铳e刃茙{o暯W檱Y\\�\u0010�Y\u001a焤@�\bL利J堹�\t\u001cF擓Fe5a紛2F�#f餺脨y\r�Q]墳\u0019\u000b\u0002麭嫬饷I[L愪N篁F獈B匓Q>戗B&T(�\t\u0015\n�2≒(�\t\u0015\neB匓!�?\u0001\u0006\u0000:�\u001diL\\E�\u0000\u0000\u0000\u0000IEND瓸`�\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105--\r\n"}
PostString
发送字符串
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
//.url("https://api.github.com/markdown/raw")
.url("http://localhost:3434/okhttp")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
try (Response response = client.newCall(request).execute()) {
ResponseShow.show(response);
}
}
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 88
Content-Type: text/x-markdown; charset=utf-8
Host: localhost:3434
User-Agent: zy/test
Releases
--------
* _1.0_ May 6, 2013
* _1.1_ June 15, 2013
* _1.2_ August 11, 2013
http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:56:09 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)
{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :Releases\n--------\n\n * _1.0_ May 6, 2013\n * _1.1_ June 15, 2013\n * _1.2_ August 11, 2013\n"}
PrintEvents
打印请求过程数据,可以用来做网络监控在项目中。
private final OkHttpClient client = new OkHttpClient.Builder()
.eventListenerFactory(PrintingEventListener.FACTORY)
.build();
public void run() throws Exception {
Request washingtonPostRequest = new Request.Builder()
.url("https://www.washingtonpost.com/")
.build();
client.newCall(washingtonPostRequest).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
}
@Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody body = response.body()) {
// Consume and discard the response body.
body.source().readByteString();
}
}
});
Request newYorkTimesRequest = new Request.Builder()
.url("https://www.nytimes.com/")
.build();
client.newCall(newYorkTimesRequest).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
}
@Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody body = response.body()) {
// Consume and discard the response body.
body.source().readByteString();
}
}
});
}
public static void main(String... args) throws Exception {
new PrintEvents().run();
}
private static final class PrintingEventListener extends EventListener {
private static final Factory FACTORY = new Factory() {
final AtomicLong nextCallId = new AtomicLong(1L);
@Override public EventListener create(Call call) {
long callId = nextCallId.getAndIncrement();
System.out.printf("%04d %s%n", callId, call.request().url());
return new PrintingEventListener(callId, System.nanoTime());
}
};
final long callId;
final long callStartNanos;
PrintingEventListener(long callId, long callStartNanos) {
this.callId = callId;
this.callStartNanos = callStartNanos;
}
private void printEvent(String name) {
long elapsedNanos = System.nanoTime() - callStartNanos;
System.out.printf("%04d %.3f %s%n", callId, elapsedNanos / 1000000000d, name);
}
@Override public void callStart(Call call) {
printEvent("callStart");
}
@Override public void dnsStart(Call call, String domainName) {
printEvent("dnsStart");
}
@Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
printEvent("dnsEnd");
}
@Override public void connectStart(
Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
printEvent("connectStart");
}
@Override public void secureConnectStart(Call call) {
printEvent("secureConnectStart");
}
@Override public void secureConnectEnd(Call call, Handshake handshake) {
printEvent("secureConnectEnd");
}
@Override public void connectEnd(
Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
printEvent("connectEnd");
}
@Override public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy,
Protocol protocol, IOException ioe) {
printEvent("connectFailed");
}
@Override public void connectionAcquired(Call call, Connection connection) {
printEvent("connectionAcquired");
}
@Override public void connectionReleased(Call call, Connection connection) {
printEvent("connectionReleased");
}
@Override public void requestHeadersStart(Call call) {
printEvent("requestHeadersStart");
}
@Override public void requestHeadersEnd(Call call, Request request) {
printEvent("requestHeadersEnd");
}
@Override public void requestBodyStart(Call call) {
printEvent("requestBodyStart");
}
@Override public void requestBodyEnd(Call call, long byteCount) {
printEvent("requestBodyEnd");
}
@Override public void responseHeadersStart(Call call) {
printEvent("responseHeadersStart");
}
@Override public void responseHeadersEnd(Call call, Response response) {
printEvent("responseHeadersEnd");
}
@Override public void responseBodyStart(Call call) {
printEvent("responseBodyStart");
}
@Override public void responseBodyEnd(Call call, long byteCount) {
printEvent("responseBodyEnd");
}
@Override public void callEnd(Call call) {
printEvent("callEnd");
}
@Override public void callFailed(Call call, IOException ioe) {
printEvent("callFailed");
}
}
0001 https://www.washingtonpost.com/
0001 0.001 callStart
0002 https://www.nytimes.com/
0002 0.000 callStart
0001 0.012 dnsStart
0002 0.009 dnsStart
0001 0.018 dnsEnd
0002 0.015 dnsEnd
0002 0.021 connectStart
0001 0.025 connectStart
0002 10.023 connectFailed
0001 10.027 connectFailed
0001 10.034 callFailed
0002 10.031 callFailed
0001 http://localhost:3434/okhttp
0001 0.000 callStart
0002 https://www.nytimes.com/
0002 0.000 callStart
0001 0.012 dnsStart
0002 0.008 dnsStart
0001 0.017 dnsEnd
0002 0.014 dnsEnd
0001 0.023 connectStart
0002 0.020 connectStart
0001 0.030 connectEnd
0001 0.030 connectionAcquired
0001 0.031 requestHeadersStart
0001 0.032 requestHeadersEnd
0001 0.034 responseHeadersStart
0001 0.037 responseHeadersEnd
0001 0.037 responseBodyStart
0001 0.041 responseBodyEnd
0001 0.042 connectionReleased
0001 0.042 callEnd
可以用来统计api中每个环节的耗时问题,提供数据,是否能够优化等。
服务端简单实现
public enum OkHttpTestHandler implements Route {
GET {
@Override
public Object handle(Request request, Response response) throws Exception {
System.out.println("GET...");
String body = request.body();
RequestShow.show(request, body);
return OkHttpResp.create(200, "OK", "GET from Server,Your Msg is :" + body);
}
},
POST {
@Override
public Object handle(Request request, Response response) throws Exception {
try {
System.out.println("post...");
String body = request.body();
System.out.println("body:" + body);
RequestShow.show(request, body);
return OkHttpResp.create(200, "OK", "POST from Server,Your Msg is :" + body);
} catch (Exception e) {
return OkHttpResp.create(404, e.toString());
}
}
};
}