diff --git a/src/ui/README.md b/src/ui/README.md index 1eb941d6c..16a0b60e6 100644 --- a/src/ui/README.md +++ b/src/ui/README.md @@ -27,8 +27,13 @@ Several "utility" endpoints are provided with useful functionality for various s | Method | Name | Description | | ------ | ------------------------------ | --------------------------------------------------------------------------- | -| `GET` | `/utility/stress/{iterations}` | Stress the CPU with the number of iterations increasing the CPU consumption | | `GET` | `/utility/status/{code}` | Returns HTTP response with given HTTP status code | +| `GET` | `/utility/headers` | Print the HTTP headers of the inbound request | +| `GET` | `/utility/panic` | Shutdown the application with an error code | +| `POST` | `/utility/echo` | Write back the POST payload sent | +| `POST` | `/utility/store` | Write the payload to a file and return a hash | +| `GET` | `/utility/store/{hash}` | Return the payload from the file system previously written | +| `GET` | `/utility/stress/{iterations}` | Stress the CPU with the number of iterations increasing the CPU consumption | ## Running diff --git a/src/ui/src/main/java/com/amazon/sample/ui/web/UtilityController.java b/src/ui/src/main/java/com/amazon/sample/ui/web/UtilityController.java index 1138edb48..234935b81 100644 --- a/src/ui/src/main/java/com/amazon/sample/ui/web/UtilityController.java +++ b/src/ui/src/main/java/com/amazon/sample/ui/web/UtilityController.java @@ -21,14 +21,35 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + @Controller @RequestMapping("/utility") public class UtilityController { + private final ApplicationContext context; + + public UtilityController(ApplicationContext context) { + this.context = context; + } + @GetMapping("/stress/{iterations}") @ResponseBody public double stress(@PathVariable int iterations) { @@ -51,4 +72,75 @@ private double monteCarloPi(int iterations) { public ResponseEntity status(@PathVariable int code) { return ResponseEntity.status(code).body("OK"); } + + @GetMapping(value = "/headers", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity>> statusHeaders(@RequestHeader HttpHeaders headers) { + return ResponseEntity.ok() + .body(headers); + } + + @GetMapping("/panic") + public ResponseEntity panic() { + Thread thread = new Thread(() -> { + try { + Thread.sleep(500); // Small delay to allow response to be sent + SpringApplication.exit(context, () -> 1); + System.exit(255); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + thread.start(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Shutting down..."); + } + + // function /echo that answer the hash provided as POST input + @PostMapping("/echo") + @ResponseBody + public ResponseEntity echo(@RequestBody String body) { + return ResponseEntity.ok() + .body(body); + } + + // function /store what take a POST hash to write to a locally created file + @PostMapping("/store") + @ResponseBody + public ResponseEntity store(@RequestBody String body) { + String filename = String.valueOf(Math.abs(body.hashCode())); // ensure positive number + try (java.io.FileWriter fileWriter = new java.io.FileWriter("/tmp/" + filename + ".json")) { + fileWriter.write(body); + return ResponseEntity.ok() + .body("{\"hash\": \"" + filename + "\"}"); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Error writing file: " + e.getMessage()); + } + } + + // function /store/{hash} that read a local hash file + @GetMapping("/store/{hash}") + @ResponseBody + public ResponseEntity store_hash(@PathVariable String hash) { + // Validate hash format - only allow numeric characters + if (!hash.matches("^[0-9]+$")) { + return ResponseEntity.badRequest() + .body("Invalid hash format"); + } + + // Normalize the path and verify it's within the intended directory + Path filePath = Paths.get("/tmp", hash + ".json").normalize(); + if (!filePath.startsWith("/tmp/")) { + return ResponseEntity.badRequest() + .body("Invalid path"); + } + + try { + String body = new String(Files.readAllBytes(filePath)); + return ResponseEntity.ok() + .body(body); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("Error reading file: " + e.getMessage()); + } + } }