Skip to content

Commit c4f703b

Browse files
authored
feat: Add headers, panic, echo and store utilities (#728)
1 parent 94ab967 commit c4f703b

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

src/ui/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ Several "utility" endpoints are provided with useful functionality for various s
2727

2828
| Method | Name | Description |
2929
| ------ | ------------------------------ | --------------------------------------------------------------------------- |
30-
| `GET` | `/utility/stress/{iterations}` | Stress the CPU with the number of iterations increasing the CPU consumption |
3130
| `GET` | `/utility/status/{code}` | Returns HTTP response with given HTTP status code |
31+
| `GET` | `/utility/headers` | Print the HTTP headers of the inbound request |
32+
| `GET` | `/utility/panic` | Shutdown the application with an error code |
33+
| `POST` | `/utility/echo` | Write back the POST payload sent |
34+
| `POST` | `/utility/store` | Write the payload to a file and return a hash |
35+
| `GET` | `/utility/store/{hash}` | Return the payload from the file system previously written |
36+
| `GET` | `/utility/stress/{iterations}` | Stress the CPU with the number of iterations increasing the CPU consumption |
3237

3338
## Running
3439

src/ui/src/main/java/com/amazon/sample/ui/web/UtilityController.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,35 @@
2121
import org.springframework.http.ResponseEntity;
2222
import org.springframework.stereotype.Controller;
2323
import org.springframework.web.bind.annotation.GetMapping;
24+
import org.springframework.web.bind.annotation.PostMapping;
25+
import org.springframework.web.bind.annotation.RequestBody;
2426
import org.springframework.web.bind.annotation.PathVariable;
27+
import org.springframework.web.bind.annotation.RequestHeader;
2528
import org.springframework.web.bind.annotation.RequestMapping;
2629
import org.springframework.web.bind.annotation.ResponseBody;
2730

31+
import java.nio.file.Files;
32+
import java.nio.file.Paths;
33+
import java.nio.file.Path;
34+
import java.util.List;
35+
import java.util.Map;
36+
37+
import org.springframework.boot.SpringApplication;
38+
import org.springframework.context.ApplicationContext;
39+
import org.springframework.http.HttpHeaders;
40+
import org.springframework.http.HttpStatus;
41+
import org.springframework.http.MediaType;
42+
2843
@Controller
2944
@RequestMapping("/utility")
3045
public class UtilityController {
3146

47+
private final ApplicationContext context;
48+
49+
public UtilityController(ApplicationContext context) {
50+
this.context = context;
51+
}
52+
3253
@GetMapping("/stress/{iterations}")
3354
@ResponseBody
3455
public double stress(@PathVariable int iterations) {
@@ -51,4 +72,75 @@ private double monteCarloPi(int iterations) {
5172
public ResponseEntity<String> status(@PathVariable int code) {
5273
return ResponseEntity.status(code).body("OK");
5374
}
75+
76+
@GetMapping(value = "/headers", produces = MediaType.APPLICATION_JSON_VALUE)
77+
public ResponseEntity<Map<String, List<String>>> statusHeaders(@RequestHeader HttpHeaders headers) {
78+
return ResponseEntity.ok()
79+
.body(headers);
80+
}
81+
82+
@GetMapping("/panic")
83+
public ResponseEntity<String> panic() {
84+
Thread thread = new Thread(() -> {
85+
try {
86+
Thread.sleep(500); // Small delay to allow response to be sent
87+
SpringApplication.exit(context, () -> 1);
88+
System.exit(255);
89+
} catch (InterruptedException e) {
90+
Thread.currentThread().interrupt();
91+
}
92+
});
93+
thread.start();
94+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Shutting down...");
95+
}
96+
97+
// function /echo that answer the hash provided as POST input
98+
@PostMapping("/echo")
99+
@ResponseBody
100+
public ResponseEntity<String> echo(@RequestBody String body) {
101+
return ResponseEntity.ok()
102+
.body(body);
103+
}
104+
105+
// function /store what take a POST hash to write to a locally created file
106+
@PostMapping("/store")
107+
@ResponseBody
108+
public ResponseEntity<String> store(@RequestBody String body) {
109+
String filename = String.valueOf(Math.abs(body.hashCode())); // ensure positive number
110+
try (java.io.FileWriter fileWriter = new java.io.FileWriter("/tmp/" + filename + ".json")) {
111+
fileWriter.write(body);
112+
return ResponseEntity.ok()
113+
.body("{\"hash\": \"" + filename + "\"}");
114+
} catch (Exception e) {
115+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
116+
.body("Error writing file: " + e.getMessage());
117+
}
118+
}
119+
120+
// function /store/{hash} that read a local hash file
121+
@GetMapping("/store/{hash}")
122+
@ResponseBody
123+
public ResponseEntity<String> store_hash(@PathVariable String hash) {
124+
// Validate hash format - only allow numeric characters
125+
if (!hash.matches("^[0-9]+$")) {
126+
return ResponseEntity.badRequest()
127+
.body("Invalid hash format");
128+
}
129+
130+
// Normalize the path and verify it's within the intended directory
131+
Path filePath = Paths.get("/tmp", hash + ".json").normalize();
132+
if (!filePath.startsWith("/tmp/")) {
133+
return ResponseEntity.badRequest()
134+
.body("Invalid path");
135+
}
136+
137+
try {
138+
String body = new String(Files.readAllBytes(filePath));
139+
return ResponseEntity.ok()
140+
.body(body);
141+
} catch (Exception e) {
142+
return ResponseEntity.status(HttpStatus.NOT_FOUND)
143+
.body("Error reading file: " + e.getMessage());
144+
}
145+
}
54146
}

0 commit comments

Comments
 (0)