Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 69 additions & 29 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,78 +131,105 @@ public HttpURLConnection createConnection() throws IOException {
return con;
}

public static class HttpGetRequest extends HttpRequest {
protected HttpGetRequest(String url) {
super(url, "GET");
protected void checkResponseCode(HttpURLConnection con) throws IOException {
int code = con.getResponseCode();
if (code / 100 != 2) {
if (!ignoreHttpCode && !toleratedHttpCodes.contains(code)) {
try {
throw new ResponseCodeException(this.url, code, NetworkUtils.readFullyAsString(con));
} catch (IOException e) {
throw new ResponseCodeException(this.url, code, e);
}
}
}
}

public static abstract class HttpSimpleRequest extends HttpRequest {
Comment thread
CiiLu marked this conversation as resolved.
protected HttpSimpleRequest(String url, String method) {
super(url, method);
}

@Override
public String getString() throws IOException {
return getStringWithRetry(() -> {
HttpURLConnection con = createConnection();
con = resolveConnection(con);
return IOUtils.readFullyAsString("gzip".equals(con.getContentEncoding()) ? IOUtils.wrapFromGZip(con.getInputStream()) : con.getInputStream());
checkResponseCode(con);

return IOUtils.readFullyAsString("gzip".equals(con.getContentEncoding())
? IOUtils.wrapFromGZip(con.getInputStream())
: con.getInputStream());
Comment on lines +159 to +161

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read tolerated GET/DELETE error bodies

For GET/DELETE requests that opt into ignoreHttpCode() or ignoreHttpErrorCode(404), checkResponseCode(con) returns on a non-2xx response, but this direct getInputStream() path still throws for 4xx/5xx responses instead of consuming the error stream. This makes the new DELETE support unusable for APIs where callers intentionally tolerate an error response body; use the existing NetworkUtils.readFullyAsString(con) helper here as the POST path does.

Useful? React with 👍 / 👎.

}, retryTimes);
}
}

public static final class HttpPostRequest extends HttpRequest {
private byte[] bytes;
public static class HttpGetRequest extends HttpSimpleRequest {
protected HttpGetRequest(String url) { super(url, "GET"); }
}

public static class HttpDeleteRequest extends HttpSimpleRequest {
protected HttpDeleteRequest(String url) { super(url, "DELETE"); }
}

private HttpPostRequest(String url) {
super(url, "POST");
@SuppressWarnings("unchecked")
public static abstract class HttpEntityRequest<T extends HttpEntityRequest<T>> extends HttpRequest {
protected byte[] bytes;

protected HttpEntityRequest(String url, String method) {
super(url, method);
}

public HttpPostRequest contentType(String contentType) {
public T contentType(String contentType) {
headers.put("Content-Type", contentType);
return this;
return (T) this;
}

public HttpPostRequest json(Object payload) throws JsonParseException {
public T json(Object payload) throws JsonParseException {
return string(payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
}

public HttpPostRequest form(Map<String, String> params) {
public T form(Map<String, String> params) {
return string(NetworkUtils.withQuery("", params), "application/x-www-form-urlencoded");
}

@SafeVarargs
public final HttpPostRequest form(Pair<String, String>... params) {
public final T form(Pair<String, String>... params) {
return form(mapOf(params));
}

public HttpPostRequest string(String payload, String contentType) {
public T string(String payload, String contentType) {
bytes = payload.getBytes(UTF_8);
header("Content-Length", "" + bytes.length);
header("Content-Length", String.valueOf(bytes.length));
contentType(contentType + "; charset=utf-8");
return this;
return (T) this;
}

@Override
public String getString() throws IOException {
return getStringWithRetry(() -> {
HttpURLConnection con = createConnection();
con.setDoOutput(true);

try (OutputStream os = con.getOutputStream()) {
os.write(bytes);
}

URL url = new URL(this.url);

if (con.getResponseCode() / 100 != 2) {
if (!ignoreHttpCode && !toleratedHttpCodes.contains(con.getResponseCode())) {
try {
throw new ResponseCodeException(url.toString(), con.getResponseCode(), NetworkUtils.readFullyAsString(con));
} catch (IOException e) {
throw new ResponseCodeException(url.toString(), con.getResponseCode(), e);
}
if (bytes != null) {
try (OutputStream os = con.getOutputStream()) {
os.write(bytes);
}
}

checkResponseCode(con);
return NetworkUtils.readFullyAsString(con);
}, retryTimes);
}
}

public static final class HttpPostRequest extends HttpEntityRequest<HttpPostRequest> {
private HttpPostRequest(String url) { super(url, "POST"); }
}

public static final class HttpPutRequest extends HttpEntityRequest<HttpPutRequest> {
private HttpPutRequest(String url) { super(url, "PUT"); }
}

public static HttpGetRequest GET(String url) {
return new HttpGetRequest(url);
}
Expand All @@ -216,6 +243,19 @@ public static HttpPostRequest POST(String url) throws MalformedURLException {
return new HttpPostRequest(url);
}

public static HttpPutRequest PUT(String url) throws MalformedURLException {
return new HttpPutRequest(url);
}

public static HttpDeleteRequest DELETE(String url) {
return new HttpDeleteRequest(url);
}

@SafeVarargs
public static HttpDeleteRequest DELETE(String url, Pair<String, String>... query) {
return DELETE(NetworkUtils.withQuery(url, mapOf(query)));
}

private static String getStringWithRetry(ExceptionalSupplier<String, IOException> supplier, int retryTimes) throws IOException {
Throwable exception = null;
for (int i = 0; i < retryTimes; i++) {
Expand Down