Skip to content

Commit d90c3b9

Browse files
committed
feat: add HTTP boilerplate and tests
1 parent f9b8e34 commit d90c3b9

File tree

7 files changed

+461
-25
lines changed

7 files changed

+461
-25
lines changed

.github/workflows/classroom-autograding.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ env:
99
GRADING_REPO: cbfacademy/autograding-java-exercises
1010
GRADING_REPO_PATH: grading-repo
1111
STUDENT_REPO_PATH: student-repo
12-
MAX_TESTS: 0 # TODO: Update this to the actual number of tests available for the exercise
12+
MAX_TESTS: 28
1313

1414
jobs:
1515
autograding:

README.md

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,49 @@ The goal of these programming exercises is to practise:
1111

1212
**<ins>Exercise 1</ins>**
1313

14-
Analyse the following HTTP request:
14+
Complete a Java class that constructs `HttpRequest` objects with specific properties.
1515

16-
```
17-
GET https://www.google.com HTTP/1.1
18-
Host: cs.unibg.it
19-
User Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124
20-
(KHTML, like Gecko) Safari/125
21-
Accept: ext/xml, application/xml, application/xhtml+xml, text/html;q=0.9,
22-
text/plain;q=0.8, image/png,*,*;q=0.5
23-
Accept-Language: it
24-
Keep-Alive: 300
25-
Connection: keep-alive
26-
```
16+
**Task:** Implement the `build()` method in the `HttpRequestBuilder` class to return a configured [HttpRequest](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpRequest.html#newBuilder()) object. Use the [HttpRequest.build()](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpRequest.html#newBuilder()) method to create a [Builder](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpRequest.Builder.html) instance and build a request with the following properties:
2717

28-
1. What is the requested URL?
29-
2. Which version of HTTP is used?
30-
3. Does the browser ask for a persistent or a non-persistent connection?
31-
4. What is, in your opinion, the utility in indicating the type (and version) of browser used by
32-
the client in the HTTP Request?
18+
- **Method:** GET
19+
- **HTTP Version:** HTTP_1_1
20+
- **User-Agent:** "Mozilla/5.0 (Java Exercise Client)"
21+
- **Accept:** "text/html,application/json,*/*;q=0.8"
22+
- **Timeout:** 30 seconds
23+
24+
**Example usage:**
25+
```java
26+
HttpRequest request = HttpRequestBuilder.build("https://www.example.com/api/data");
27+
28+
System.out.println("Method: " + request.method());
29+
System.out.println("URI: " + request.uri());
30+
System.out.println("Headers: " + request.headers().map());
31+
```
3332

3433
**<ins>Exercise 2</ins>**
3534

36-
An HTTP client sends the following message:
35+
Complete a Java class that processes `HttpResponse` objects and extracts key information into a `Map`.
3736

38-
```
39-
GET http://cs.unibg.it /index.html HTTP/1.1
40-
User-agent: Mozilla/4.0
41-
Accept: text/html, image/gif, image/jpeg
42-
If-modified-since: 27 Feb 2017 08:10:00
37+
**Task:** Implement the `parse()` method in the `HttpResponseParser` class to return a `Map<String, String>` containing:
38+
39+
- **"URL"**: Request URL (from the response's request)
40+
- **"Status"**: HTTP status code as a string
41+
- **"Server"**: Server header value (only if present)
42+
- **"Content-Type"**: Content-Type header value (only if present)
43+
- **"Content-Length"**: Content-Length header value (only if present)
44+
45+
**Example usage:**
46+
```java
47+
Map<String, String> responseData = HttpResponseParser.parse(response);
48+
49+
System.out.println("URL: " + responseData.get("URL"));
50+
System.out.println("Status: " + responseData.get("Status"));
51+
if (responseData.containsKey("Server")) {
52+
System.out.println("Server: " + responseData.get("Server"));
53+
}
4354
```
4455

45-
Write down two feasible responses of the HTTP server (only the status line).
56+
**Note:** Use Java's built-in `java.net.http` package (available in Java 11+) for the `HttpRequest` and `HttpResponse` classes.
4657

4758
## :spider_web: HTML
4859

@@ -78,4 +89,10 @@ To verify that your form is structured as expected, run the tests:
7889
./mvnw clean test
7990
```
8091

92+
To run the HTTP exercises demo:
93+
94+
```shell
95+
./mvnw exec:java -Dexec.mainClass="com.cbfacademy.App"
96+
```
97+
8198
**Learn more:** [Anatomy of an HTML Document](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics#anatomy_of_an_html_document)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.cbfacademy;
2+
3+
public class App {
4+
public static void main(String[] args) {
5+
System.out.println("Hello, World!");
6+
}
7+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.cbfacademy;
2+
3+
import java.net.http.HttpRequest;
4+
5+
/**
6+
* Exercise 1: HTTP Request Builder
7+
* A utility class for constructing HttpRequest objects with specific properties.
8+
*/
9+
public class HttpRequestBuilder {
10+
11+
/**
12+
* Builds an HttpRequest object with predefined properties.
13+
* Method: GET, HTTP Version: HTTP_1_1, User-Agent: Mozilla/5.0 (Java Exercise Client),
14+
* Accept: text/html,application/json,star/star;q=0.8, Timeout: 30 seconds
15+
*
16+
* @param url The URL for the HTTP request
17+
* @return HttpRequest object configured with the specified properties
18+
* @throws RuntimeException if the request cannot be built
19+
*/
20+
public static HttpRequest build(String url) {
21+
throw new UnsupportedOperationException("Not implemented");
22+
}
23+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.cbfacademy;
2+
3+
import java.net.http.HttpResponse;
4+
import java.util.Map;
5+
import java.util.Optional;
6+
7+
/**
8+
* A utility class for parsing HttpResponse objects.
9+
*/
10+
public class HttpResponseParser {
11+
12+
/**
13+
* Parses the HttpResponse object and returns a map containing key information:
14+
* - "URL": Response URL
15+
* - "Status": HTTP status code as a string
16+
* - "Server": Server header value (if present)
17+
* - "Content-Type": Content-Type header value (if present)
18+
* - "Content-Length": Content-Length header value (if present)
19+
*
20+
* @param response The HttpResponse object to parse
21+
* @return Map containing the extracted response information
22+
*/
23+
public static Map<String, String> parse(HttpResponse<String> response) {
24+
throw new UnsupportedOperationException("Not implemented");
25+
}
26+
27+
/**
28+
* Helper method to add a response header value to the response data map, if present
29+
*
30+
* @param response The HttpResponse object
31+
* @param headerName The name of the header to extract
32+
* @param responseData The map to add the header value to
33+
*/
34+
private static void addHeader(HttpResponse<String> response, String headerName, Map<String, String> responseData) {
35+
Optional<String> headerValue = response.headers().firstValue(headerName);
36+
if (headerValue.isPresent()) {
37+
responseData.put(headerName, headerValue.get());
38+
}
39+
}
40+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package com.cbfacademy;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertNotSame;
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
10+
import java.net.URI;
11+
import java.net.http.HttpClient;
12+
import java.net.http.HttpRequest;
13+
import java.time.Duration;
14+
import java.util.Optional;
15+
16+
import org.junit.jupiter.api.DisplayName;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.ValueSource;
20+
21+
@DisplayName("HttpRequestBuilder Tests")
22+
public class HttpRequestBuilderTest {
23+
24+
@Test
25+
@DisplayName("Should build HttpRequest with correct method")
26+
void shouldBuildRequestWithGetMethod() {
27+
String testUrl = "https://example.com";
28+
HttpRequest request = HttpRequestBuilder.build(testUrl);
29+
30+
assertEquals("GET", request.method(), "Request method should be GET");
31+
}
32+
33+
@Test
34+
@DisplayName("Should build HttpRequest with correct URI")
35+
void shouldBuildRequestWithCorrectUri() {
36+
String testUrl = "https://example.com/api/data";
37+
HttpRequest request = HttpRequestBuilder.build(testUrl);
38+
39+
assertEquals(URI.create(testUrl), request.uri(), "Request URI should match the provided URL");
40+
}
41+
42+
@Test
43+
@DisplayName("Should build HttpRequest with HTTP_1_1 version")
44+
void shouldBuildRequestWithHttp11Version() {
45+
String testUrl = "https://example.com";
46+
HttpRequest request = HttpRequestBuilder.build(testUrl);
47+
48+
Optional<HttpClient.Version> version = request.version();
49+
assertTrue(version.isPresent(), "HTTP version should be present");
50+
assertEquals(HttpClient.Version.HTTP_1_1, version.get(), "HTTP version should be HTTP_1_1");
51+
}
52+
53+
@Test
54+
@DisplayName("Should build HttpRequest with correct User-Agent header")
55+
void shouldBuildRequestWithCorrectUserAgent() {
56+
String testUrl = "https://example.com";
57+
HttpRequest request = HttpRequestBuilder.build(testUrl);
58+
59+
Optional<String> userAgent = request.headers().firstValue("User-Agent");
60+
assertTrue(userAgent.isPresent(), "User-Agent header should be present");
61+
assertEquals("Mozilla/5.0 (Java Exercise Client)", userAgent.get(),
62+
"User-Agent should match expected value");
63+
}
64+
65+
@Test
66+
@DisplayName("Should build HttpRequest with correct Accept header")
67+
void shouldBuildRequestWithCorrectAcceptHeader() {
68+
String testUrl = "https://example.com";
69+
HttpRequest request = HttpRequestBuilder.build(testUrl);
70+
71+
Optional<String> accept = request.headers().firstValue("Accept");
72+
assertTrue(accept.isPresent(), "Accept header should be present");
73+
assertEquals("text/html,application/json,*/*;q=0.8", accept.get(),
74+
"Accept header should match expected value");
75+
}
76+
77+
@Test
78+
@DisplayName("Should build HttpRequest with 30 second timeout")
79+
void shouldBuildRequestWithCorrectTimeout() {
80+
String testUrl = "https://example.com";
81+
HttpRequest request = HttpRequestBuilder.build(testUrl);
82+
83+
Optional<Duration> timeout = request.timeout();
84+
assertTrue(timeout.isPresent(), "Timeout should be present");
85+
assertEquals(Duration.ofSeconds(30), timeout.get(), "Timeout should be 30 seconds");
86+
}
87+
88+
@ParameterizedTest
89+
@ValueSource(strings = {
90+
"https://www.google.com",
91+
"https://api.github.com/users",
92+
"https://httpbin.org/get",
93+
"https://example.com/path/to/resource?param=value"
94+
})
95+
@DisplayName("Should handle various URL formats correctly")
96+
void shouldHandleVariousUrlFormats(String url) {
97+
HttpRequest request = HttpRequestBuilder.build(url);
98+
99+
assertNotNull(request, "Request should not be null");
100+
assertEquals(URI.create(url), request.uri(), "Request URI should match input URL");
101+
assertEquals("GET", request.method(), "Method should always be GET");
102+
}
103+
104+
@Test
105+
@DisplayName("Should build request with all required headers present")
106+
void shouldBuildRequestWithAllRequiredHeaders() {
107+
String testUrl = "https://example.com";
108+
HttpRequest request = HttpRequestBuilder.build(testUrl);
109+
110+
// Verify that both required headers are present
111+
assertTrue(request.headers().firstValue("User-Agent").isPresent(),
112+
"User-Agent header should be present");
113+
assertTrue(request.headers().firstValue("Accept").isPresent(),
114+
"Accept header should be present");
115+
116+
// Verify no restricted headers are set (Connection header should not be manually set)
117+
assertFalse(request.headers().firstValue("Connection").isPresent(),
118+
"Connection header should not be manually set");
119+
}
120+
121+
@Test
122+
@DisplayName("Should create different request instances for different URLs")
123+
void shouldCreateDifferentRequestInstances() {
124+
String url1 = "https://example.com/path1";
125+
String url2 = "https://example.com/path2";
126+
127+
HttpRequest request1 = HttpRequestBuilder.build(url1);
128+
HttpRequest request2 = HttpRequestBuilder.build(url2);
129+
130+
assertNotSame(request1, request2, "Different requests should be separate instances");
131+
assertNotEquals(request1.uri(), request2.uri(), "Different requests should have different URIs");
132+
}
133+
}

0 commit comments

Comments
 (0)