Skip to content

Commit 45f8ec8

Browse files
authored
Merge pull request #11 from swnck/dev
Enhance HTTP request handling
2 parents c496b78 + aa3f1eb commit 45f8ec8

23 files changed

+672
-128
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@
2222
---
2323

2424
## Features
25-
- [ ] Support for
25+
- [x] Support for
2626
- [x] GET requests
2727
- [x] POST requests
28-
- [ ] PUT requests
29-
- [ ] DELETE requests
30-
- [ ] PATCH requests
28+
- [x] PUT requests
29+
- [x] DELETE requests
30+
- [x] PATCH requests
3131
- [ ] Proxy configuration
3232
- [ ] Class (entity) mapping
3333
- [ ] Support for body (form-urlencoded, multipart, binary)
3434
- [ ] Retry request many times
3535
- [x] Query parameters
36-
- [ ] Timeout configuration
36+
- [X] Timeout configuration
3737
- [x] CORS support
3838

3939
---

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
group = "io.github.swnck"
8-
version = "1.0.3"
8+
version = "1.0.4"
99

1010
repositories {
1111
mavenCentral()
@@ -17,6 +17,8 @@ dependencies {
1717

1818
implementation("org.slf4j:slf4j-api:2.0.17")
1919

20+
implementation("org.json:json:20250517")
21+
2022
compileOnly("org.projectlombok:lombok:1.18.38")
2123
annotationProcessor("org.projectlombok:lombok:1.18.38")
2224
}
Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,58 @@
11
package io.github.swnck;
22

3-
import io.github.swnck.request.GetRequest;
4-
import io.github.swnck.request.PostRequest;
3+
import io.github.swnck.request.*;
54
import lombok.Getter;
65

6+
/**
7+
* Provides static factory methods to generate various types of HTTP requests.
8+
* This class simplifies the creation of requests like GET, POST, PUT, PATCH, and DELETE,
9+
* with or without URLs, providing flexibility for diverse use cases.
10+
* Each method returns a specific request object tailored to the HTTP method being invoked.
11+
*/
712
@Getter
813
public class JxRequest {
914

1015
public static GetRequest get(String url) {
1116
return new GetRequest(url);
1217
}
1318

14-
public static PostRequest post(String url) {
15-
return new PostRequest(url);
16-
}
17-
1819
public static GetRequest get() {
1920
return new GetRequest();
2021
}
2122

23+
24+
public static PostRequest post(String url) {
25+
return new PostRequest(url);
26+
}
27+
2228
public static PostRequest post() {
23-
return new PostRequest();
29+
return new PostRequest();
30+
}
31+
32+
33+
public static DeleteRequest delete(String url) {
34+
return new DeleteRequest(url);
35+
}
36+
37+
public static DeleteRequest delete() {
38+
return new DeleteRequest();
39+
}
40+
41+
42+
public static PatchRequest patch(String url) {
43+
return new PatchRequest(url);
44+
}
45+
46+
public static PatchRequest patch() {
47+
return new PatchRequest();
48+
}
49+
50+
51+
public static PutRequest put(String url) {
52+
return new PutRequest(url);
53+
}
54+
55+
public static PutRequest put() {
56+
return new PutRequest();
2457
}
2558
}

src/main/java/io/github/swnck/JxResponse.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.github.swnck;
22

3+
import io.github.swnck.request.AbstractBody;
34
import io.github.swnck.request.AbstractRequest;
4-
import io.github.swnck.request.PostRequest;
55
import io.github.swnck.util.ContentType;
66
import io.github.swnck.util.StatusCode;
77
import lombok.Getter;
@@ -15,18 +15,22 @@
1515
import java.net.http.HttpRequest;
1616
import java.net.http.HttpResponse;
1717
import java.nio.charset.StandardCharsets;
18+
import java.time.Duration;
1819
import java.util.List;
1920
import java.util.Map;
20-
import java.util.Objects;
2121
import java.util.concurrent.CompletableFuture;
2222

23+
/**
24+
* Represents an HTTP response retrieved using an asynchronous HTTP client.
25+
* This class is designed to process a given request, initiate the corresponding HTTP call,
26+
* and handle the response data, including body, status code, headers, and timing.
27+
*/
2328
@Getter
2429
@Setter
2530
public class JxResponse {
2631
private static final HttpClient CLIENT = HttpClient.newBuilder().build();
2732
private static final Logger LOGGER = LoggerFactory.getLogger(JxResponse.class);
2833

29-
3034
private String body;
3135
private String contentType;
3236
private String uri;
@@ -36,15 +40,24 @@ public class JxResponse {
3640

3741
private Map<String, List<String>> headers;
3842

43+
/**
44+
* Constructs a JxResponse object by initiating and handling an asynchronous HTTP request based on the provided request object.
45+
* Handles the composition of the request URI with query parameters, method type, headers, and optional body content.
46+
* Completes the operation by storing the response details such as body, status code, headers, and execution duration.
47+
*
48+
* @param request the {@link AbstractRequest} object containing the details of the HTTP request,
49+
* including the URL, query parameters, headers, method, timeout, and body (if applicable).
50+
*/
3951
public JxResponse(AbstractRequest<?> request) {
40-
String urlWithParams = buildUrlWithParams(request.url, request.getQueryParams());
41-
String bodyContent = (request instanceof PostRequest) ? ((PostRequest) request).getBody() : null;
52+
String urlWithParams = buildUrlWithParams(request.getUrl(), request.getQueryParams());
53+
String bodyContent = (request instanceof AbstractBody<?>) ? ((AbstractBody<?>) request).getBody() : null;
4254

4355
HttpRequest.Builder requestBuilder;
4456

4557
try {
4658
requestBuilder = HttpRequest.newBuilder()
4759
.uri(new URI(urlWithParams))
60+
.timeout(Duration.ofMillis(request.getTimeoutMillis()))
4861
.method(request.getMethod().toString(),
4962
(bodyContent == null) ? HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofString(bodyContent));
5063
} catch (Exception e) {
@@ -53,7 +66,6 @@ public JxResponse(AbstractRequest<?> request) {
5366
}
5467

5568
request.getHeaders().forEach((key, value) -> requestBuilder.header(key, value.toString()));
56-
requestBuilder.header("Content-Type", Objects.requireNonNullElse(request.contentType, ContentType.TEXT_PLAIN).getMimeType());
5769

5870
HttpRequest httpRequest = requestBuilder.build();
5971

@@ -63,7 +75,8 @@ public JxResponse(AbstractRequest<?> request) {
6375

6476
response.thenAccept(httpResponse -> {
6577
this.body = httpResponse.body();
66-
this.contentType = httpResponse.headers().firstValue("Content-Type").orElse(ContentType.TEXT_PLAIN.getMimeType());
78+
this.contentType = httpResponse.headers().firstValue("Content-Type")
79+
.orElse(ContentType.TEXT_PLAIN.getMimeType());
6780
this.statusCode = httpResponse.statusCode();
6881
this.uri = httpResponse.uri().toString();
6982
this.headers = httpResponse.headers().map();
@@ -76,6 +89,15 @@ public JxResponse(AbstractRequest<?> request) {
7689
}).join();
7790
}
7891

92+
/**
93+
* Constructs a complete URL by appending query parameters to the base URL.
94+
* If the provided query parameters are null or empty, the original URL is returned as is.
95+
* Handles encoding of both keys and values in the query parameters to ensure proper URL formatting.
96+
*
97+
* @param url the base URL to which query parameters will be appended
98+
* @param queryParams a map containing query parameter keys and their corresponding values
99+
* @return the complete URL with query parameters appended and properly encoded
100+
*/
79101
private String buildUrlWithParams(String url, Map<String, Object> queryParams) {
80102
if (queryParams == null || queryParams.isEmpty()) return url;
81103
StringBuilder sb = new StringBuilder(url);

src/main/java/io/github/swnck/cors/Cors.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,41 @@
66
import java.util.Map;
77

88
/**
9-
* Cors is a builder class for creating CORS (Cross-Origin Resource Sharing) headers.
10-
* It allows you to specify the allowed origins, methods, headers, and whether credentials are allowed.
9+
* The Cors class provides a mechanism for configuring Cross-Origin Resource Sharing (CORS) headers
10+
* for an HTTP response. It allows customization of origins, HTTP methods, headers,
11+
* and other CORS-related configurations.
12+
* <p>
13+
* This class uses a builder-style pattern, enabling chained method calls for setting
14+
* various CORS options. The settings are maintained in an internal map, `corsMap`, where
15+
* the configuration key-value pairs are stored.
1116
*/
1217
@Getter
1318
public class Cors {
19+
/**
20+
* A map that stores CORS (Cross-Origin Resource Sharing) configuration settings.
21+
* The keys represent specific configuration options, such as allowed origins,
22+
* HTTP methods, headers, and other related parameters, while the values
23+
* represent their corresponding settings.
24+
* <p>
25+
* This map is used internally to maintain the CORS configurations applied
26+
* via various methods in the class. The settings stored in this map can
27+
* be applied to HTTP response headers to enable the desired CORS behavior.
28+
* <p>
29+
* The `corsMap` is populated and modified using methods provided in the
30+
* `Cors` class, allowing for dynamic and customizable CORS configurations.
31+
*/
1432
private final Map<String, Object> corsMap = new HashMap<>();
1533

34+
/**
35+
* Configures the allowed origin for Cross-Origin Resource Sharing (CORS).
36+
* This method sets the "Access-Control-Allow-Origin" header to the specified origin.
37+
* The wildcard character "*" can be used to allow all origins.
38+
*
39+
* @param origin the allowed origin for CORS. The value can be a specific origin (e.g., "https://example.com")
40+
* or "*" to allow requests from any origin. Must not be null.
41+
* @return the updated {@code Cors} instance, allowing for method chaining.
42+
* @throws IllegalArgumentException if the origin is null.
43+
*/
1644
public Cors allowOrigin(String origin) {
1745
if (origin == null) {
1846
throw new IllegalArgumentException("Origin cannot be null");
@@ -27,6 +55,15 @@ public Cors allowOrigin(String origin) {
2755
return this;
2856
}
2957

58+
/**
59+
* Configures the allowed HTTP methods for Cross-Origin Resource Sharing (CORS).
60+
* This method sets the "Access-Control-Allow-Methods" header to the specified methods.
61+
*
62+
* @param methods the HTTP methods to allow for CORS (e.g., "GET", "POST", "PUT").
63+
* Must not be null or empty.
64+
* @return the updated {@code Cors} instance, allowing for method chaining.
65+
* @throws IllegalArgumentException if the methods array is null or empty.
66+
*/
3067
public Cors allowMethods(String... methods) {
3168
if (methods == null || methods.length == 0) {
3269
throw new IllegalArgumentException("Methods cannot be null or empty");
@@ -36,6 +73,14 @@ public Cors allowMethods(String... methods) {
3673
return this;
3774
}
3875

76+
/**
77+
* Configures the allowed HTTP headers for Cross-Origin Resource Sharing (CORS).
78+
* This method sets the "Access-Control-Allow-Headers" header to the specified headers.
79+
*
80+
* @param headers the HTTP headers to allow for CORS. Must not be null or empty.
81+
* @return the updated {@code Cors} instance, allowing for method chaining.
82+
* @throws IllegalArgumentException if the headers array is null or empty.
83+
*/
3984
public Cors allowHeaders(String... headers) {
4085
if (headers == null || headers.length == 0) {
4186
throw new IllegalArgumentException("Headers cannot be null or empty");
@@ -45,6 +90,17 @@ public Cors allowHeaders(String... headers) {
4590
return this;
4691
}
4792

93+
/**
94+
* Configures whether credentials are allowed for Cross-Origin Resource Sharing (CORS).
95+
* This method sets the "Access-Control-Allow-Credentials" header to the specified value.
96+
* Setting this to true allows the sharing of credentials such as cookies, authentication headers, or
97+
* TLS client certificates in cross-origin requests.
98+
*
99+
* @param allowCredentials a boolean indicating whether credentials are allowed. If true,
100+
* the response indicates that the resource is prepared to
101+
* share credentials with the client.
102+
* @return the updated {@code Cors} instance, allowing for method chaining.
103+
*/
48104
public Cors allowCredentials(boolean allowCredentials) {
49105
this.corsMap.put("Access-Control-Allow-Credentials", allowCredentials);
50106
return this;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.github.swnck.request;
2+
3+
import io.github.swnck.util.Method;
4+
import lombok.Getter;
5+
6+
/**
7+
* AbstractBody is an abstract base class to handle HTTP request bodies in concrete implementations of various HTTP methods.
8+
* It provides mechanisms for setting and validating the body content of HTTP requests.
9+
*
10+
* @param <T> the type of the subclass extending AbstractBody
11+
*/
12+
@Getter
13+
public abstract class AbstractBody<T extends AbstractBody<T>> extends AbstractRequest<T> {
14+
15+
/**
16+
* The `body` variable represents the content of the HTTP request body.
17+
* It is used to store the data that will be transmitted in the request
18+
* for HTTP methods supporting a body, such as POST, PUT, PATCH, and DELETE.
19+
*/
20+
private String body;
21+
22+
/**
23+
* Constructs a new {@code AbstractBody} instance with the specified URL and HTTP method.
24+
* This constructor initializes the HTTP request body handler using the provided URL and method.
25+
*
26+
* @param url the URL to which the request will be sent; must not be null. If the URL does not
27+
* start with "http://" or "https://", "http://" will be prefixed.
28+
* @param method the HTTP method to be used for the request; must not be null. Supported methods
29+
* include GET, POST, DELETE, PUT, and PATCH.
30+
*/
31+
public AbstractBody(String url, Method method) {
32+
super(url, method);
33+
}
34+
35+
/**
36+
* Constructs a new {@code AbstractBody} instance with the specified HTTP method.
37+
* This constructor initializes the HTTP request body handler using the provided method.
38+
*
39+
* @param method the HTTP method to be used for the request; must not be null.
40+
* Supported methods include GET, POST, DELETE, PUT, and PATCH.
41+
* @throws IllegalArgumentException if the method is null.
42+
*/
43+
public AbstractBody(Method method) {
44+
super(method);
45+
}
46+
47+
/**
48+
* Sets the body of the HTTP request.
49+
*
50+
* @param body the content to be set as the body of the HTTP request; must not be null
51+
* @return the current instance of the subclass implementation for method chaining
52+
* @throws IllegalArgumentException if the provided body is null
53+
*/
54+
@SuppressWarnings("unchecked")
55+
public T setBody(String body) {
56+
if (body == null) {
57+
throw new IllegalArgumentException("AbstractBody cannot be null");
58+
}
59+
60+
this.body = body;
61+
return (T) this;
62+
}
63+
}

0 commit comments

Comments
 (0)