HttpTransport.java
// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
/** HTTP abstraction so the retry and error logic can be tested without sockets. */
public interface HttpTransport {
/** One HTTP attempt. */
final class Request {
public final String method;
public final String url;
public final Map<String, String> headers;
public final Multipart body;
public final int connectTimeoutMs;
public final int readTimeoutMs;
public Request(String method, String url, Map<String, String> headers, Multipart body,
int connectTimeoutMs, int readTimeoutMs) {
this.method = method;
this.url = url;
this.headers = Collections.unmodifiableMap(new LinkedHashMap<String, String>(headers));
this.body = body;
this.connectTimeoutMs = connectTimeoutMs;
this.readTimeoutMs = readTimeoutMs;
}
}
/**
* Phase times of one attempt on the {@link System#nanoTime()} clock, 0 when unknown, and sizes,
* -1 when unknown (SDK 2.1.1 timing log).
*/
final class Metrics {
public long startNanos;
/** The request body was handed to the socket. */
public long uploadDoneNanos;
/** Status line and headers of the answer were read. */
public long headersNanos;
/** The body was read and decompressed. */
public long bodyDoneNanos;
public long uploadBytes = -1;
/** Body bytes as received (compressed when {@link #contentEncoding} is set). */
public long wireBytes = -1;
/** Body bytes after decompression. */
public long bodyBytes = -1;
/** Content-Encoding of the answer in lower case, null for none. */
public String contentEncoding;
}
/** Response of one attempt. Header names are lower case. */
final class Response {
public final int status;
public final Map<String, String> headers;
public final String body;
/** Phase times and sizes of the attempt, null when the transport does not measure them. */
public final Metrics metrics;
public Response(int status, Map<String, String> headers, String body) {
this(status, headers, body, null);
}
public Response(int status, Map<String, String> headers, String body, Metrics metrics) {
this.metrics = metrics;
this.status = status;
Map<String, String> h = new LinkedHashMap<String, String>();
if (headers != null) {
for (Map.Entry<String, String> e : headers.entrySet()) {
if (e.getKey() != null) {
h.put(e.getKey().toLowerCase(Locale.ROOT), e.getValue());
}
}
}
this.headers = Collections.unmodifiableMap(h);
this.body = body == null ? "" : body;
}
public String header(String name) {
return headers.get(name.toLowerCase(Locale.ROOT));
}
}
/**
* Executes one attempt. Implementations register an abort handle with {@code token} and throw
* {@link IOException} on transport failure (SocketTimeoutException for timeouts).
*/
Response execute(Request request, CancelToken token) throws IOException;
}