From fed02f6f5f4308400e55c160d9495cad010f5bfb Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 11:11:09 +0900 Subject: [PATCH 01/18] fix: remove implementation logback-classic on gradle (#501) --- study/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/study/build.gradle b/study/build.gradle index 5c69542f84..87a1f0313c 100644 --- a/study/build.gradle +++ b/study/build.gradle @@ -19,7 +19,6 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'ch.qos.logback:logback-classic:1.5.7' implementation 'org.apache.commons:commons-lang3:3.14.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1' From 7e9135698878932274ddc1f523ba817ed9c56c70 Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 13:51:07 +0900 Subject: [PATCH 02/18] fix: add threads min-spare configuration on properties (#502) --- study/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..e3503a5fb9 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -6,4 +6,5 @@ server: accept-count: 1 max-connections: 1 threads: + min-spare: 2 max: 2 From 7b81c9c2da19782b25740c438c5f4b2f3d24569f Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 10:54:31 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20/index.html=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bb14184757..428ecd654e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,9 +4,14 @@ import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.net.Socket; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; public class Http11Processor implements Runnable, Processor { @@ -27,9 +32,30 @@ public void run() { @Override public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream()) { + final var outputStream = connection.getOutputStream(); + final var bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { + + String firstLine = bufferedReader.readLine(); + + if (firstLine == null) { + return; + } + + String page = getPage(firstLine); + String responseBody = ""; - final var responseBody = "Hello world!"; + if (page.equals("/")) { + responseBody = "Hello world!"; + } else { + URL url = getClass().getClassLoader().getResource("static" + page); + + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + } final var response = String.join("\r\n", "HTTP/1.1 200 OK ", @@ -42,6 +68,12 @@ public void process(final Socket connection) { outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } } + + private String getPage(String firstLine) { + return firstLine.split(" ")[1]; + } } From f15f3738e9d11e8c602c2dd62dc1dd04fc805167 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 12:55:02 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20css=20=EC=A7=80=EC=9B=90=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 428ecd654e..bdb7527f2c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -43,26 +43,46 @@ public void process(final Socket connection) { String page = getPage(firstLine); String responseBody = ""; - + String response = ""; if (page.equals("/")) { responseBody = "Hello world!"; - } else { + response = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: text/html;charset=utf-8 ", + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody); + } + else if(page.startsWith("/css/")) { URL url = getClass().getClassLoader().getResource("static" + page); - if (url == null) { return; } Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); + response = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: text/css;charset=utf-8 ", + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody); } + else { + URL url = getClass().getClassLoader().getResource("static" + page); + if (url == null) { + return; + } - final var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: text/html;charset=utf-8 ", + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody); + } outputStream.write(response.getBytes()); outputStream.flush(); From 927cc606af5c724ee614cb57ff7b6dc18ec27315 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 16:20:42 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20Query=20String=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bdb7527f2c..1eafef5509 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,6 +1,8 @@ package org.apache.coyote.http11; +import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; +import com.techcourse.model.User; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,6 @@ public void process(final Socket connection) { final var bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { String firstLine = bufferedReader.readLine(); - if (firstLine == null) { return; } @@ -46,14 +47,28 @@ public void process(final Socket connection) { String response = ""; if (page.equals("/")) { responseBody = "Hello world!"; - response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - } - else if(page.startsWith("/css/")) { + response = generateResponse(responseBody, "text/html"); + } else if (page.startsWith("/login?")) { + int index = page.indexOf("?"); + String paths = page.substring(0, index); + String queryString = page.substring(index + 1); + String account = queryString.split("&")[0].split("=")[1]; + String password = queryString.split("&")[1].split("=")[1]; + + User user = InMemoryUserRepository.findByAccount(account).get(); + if (user.checkPassword(password)) { + log.info("user : {}", user); + } + + URL url = getClass().getClassLoader().getResource("static" + paths + ".html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generateResponse(responseBody, "text/html"); + } else if (page.startsWith("/css/")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { return; @@ -61,14 +76,8 @@ else if(page.startsWith("/css/")) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/css;charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - } - else { + response = generateResponse(responseBody, "text/css"); + } else { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { return; @@ -76,12 +85,7 @@ else if(page.startsWith("/css/")) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); + response = generateResponse(responseBody, "text/html"); } outputStream.write(response.getBytes()); @@ -96,4 +100,13 @@ else if(page.startsWith("/css/")) { private String getPage(String firstLine) { return firstLine.split(" ")[1]; } + + private String generateResponse(String responseBody, String contentType) { + return String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: " + contentType + ";charset=utf-8 ", + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody); + } } From d310bcb51de81f2e8e8d6aa784dadc8e01464d04 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 17:05:13 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=8B=A4?= =?UTF-8?q?=EB=A5=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EB=A0=89=ED=8A=B8=20=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 1eafef5509..f506cc6441 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -47,28 +47,53 @@ public void process(final Socket connection) { String response = ""; if (page.equals("/")) { responseBody = "Hello world!"; - response = generateResponse(responseBody, "text/html"); + response = generate200Response(responseBody, "text/html"); } else if (page.startsWith("/login?")) { int index = page.indexOf("?"); - String paths = page.substring(0, index); String queryString = page.substring(index + 1); String account = queryString.split("&")[0].split("=")[1]; String password = queryString.split("&")[1].split("=")[1]; - User user = InMemoryUserRepository.findByAccount(account).get(); + if (user.checkPassword(password)) { log.info("user : {}", user); + URL url = getClass().getClassLoader().getResource("static/index.html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate302Response(responseBody, "text/html", "/index.html"); + } else { + URL url = getClass().getClassLoader().getResource("static/401.html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate302Response(responseBody, "text/html", "/401.html"); } - - URL url = getClass().getClassLoader().getResource("static" + paths + ".html"); + } else if (page.startsWith("/css/")) { + URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { return; } Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generateResponse(responseBody, "text/html"); - } else if (page.startsWith("/css/")) { + response = generate200Response(responseBody, "text/css"); + } else if (page.contains(".js")) { + URL url = getClass().getClassLoader().getResource("static" + page); + if (url == null) { + return; + } + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate200Response(responseBody, "text/javascript"); + System.out.println("response = " + response); + } else if (page.endsWith(".html")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { return; @@ -76,16 +101,16 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generateResponse(responseBody, "text/css"); + response = generate200Response(responseBody, "text/html"); } else { - URL url = getClass().getClassLoader().getResource("static" + page); + URL url = getClass().getClassLoader().getResource("static" + page + ".html"); if (url == null) { return; } Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generateResponse(responseBody, "text/html"); + response = generate200Response(responseBody, "text/html"); } outputStream.write(response.getBytes()); @@ -101,7 +126,7 @@ private String getPage(String firstLine) { return firstLine.split(" ")[1]; } - private String generateResponse(String responseBody, String contentType) { + private String generate200Response(String responseBody, String contentType) { return String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + contentType + ";charset=utf-8 ", @@ -109,4 +134,15 @@ private String generateResponse(String responseBody, String contentType) { "", responseBody); } + + private String generate302Response(String responseBody, String contentType, String redirectUrl) { + return String.join("\r\n", + "HTTP/1.1 302 Found", + "Location: " + redirectUrl, + "Content-Type: " + contentType + ";charset=utf-8", + "Content-Length: " + responseBody.getBytes().length, + "", + responseBody); + } + } From a3c31776136c672df26fbbe1ac3b015593185b1b Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 19:33:33 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20Post=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index f506cc6441..e30d99826b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -14,6 +14,8 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; public class Http11Processor implements Runnable, Processor { @@ -42,7 +44,20 @@ public void process(final Socket connection) { return; } - String page = getPage(firstLine); + Map httpRequestHeaders = new HashMap<>(); + + while (!firstLine.isEmpty()) { + String line = bufferedReader.readLine(); + if (line.isEmpty()) { + break; + } + String[] headerParts = line.split(": "); + httpRequestHeaders.put(headerParts[0], headerParts[1]); + } + + String httpMethod = firstLine.split(" ")[0]; + String page = firstLine.split(" ")[1]; + String responseBody = ""; String response = ""; if (page.equals("/")) { @@ -75,6 +90,24 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(path)); response = generate302Response(responseBody, "text/html", "/401.html"); } + } else if (page.equals("/register") && httpMethod.equals("POST")) { + int contentLength = Integer.parseInt(httpRequestHeaders.get("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String requestBody = new String(buffer); + String account = requestBody.split("&")[0].split("=")[1]; + String email = requestBody.split("&")[1].split("=")[1]; + String password = requestBody.split("&")[2].split("=")[1]; + InMemoryUserRepository.save(new User(account, email, password)); + + URL url = getClass().getClassLoader().getResource("static/index.html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate302Response(responseBody, "text/html", "/index.html"); } else if (page.startsWith("/css/")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { @@ -92,7 +125,6 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); response = generate200Response(responseBody, "text/javascript"); - System.out.println("response = " + response); } else if (page.endsWith(".html")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { @@ -122,10 +154,6 @@ public void process(final Socket connection) { } } - private String getPage(String firstLine) { - return firstLine.split(" ")[1]; - } - private String generate200Response(String responseBody, String contentType) { return String.join("\r\n", "HTTP/1.1 200 OK ", From 2d0e5bdecd8aeb5a78473356ddce8c263c4983e9 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 19:40:00 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20Pos?= =?UTF-8?q?t=EB=A1=9C=20=EC=A0=84=EC=86=A1=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/Http11Processor.java | 13 ++++++++----- tomcat/src/main/resources/static/login.html | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index e30d99826b..f9ab6c7008 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -63,11 +63,14 @@ public void process(final Socket connection) { if (page.equals("/")) { responseBody = "Hello world!"; response = generate200Response(responseBody, "text/html"); - } else if (page.startsWith("/login?")) { - int index = page.indexOf("?"); - String queryString = page.substring(index + 1); - String account = queryString.split("&")[0].split("=")[1]; - String password = queryString.split("&")[1].split("=")[1]; + } else if (page.startsWith("/login") && httpMethod.equals("POST")) { + int contentLength = Integer.parseInt(httpRequestHeaders.get("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String requestBody = new String(buffer); + String account = requestBody.split("&")[0].split("=")[1]; + String password = requestBody.split("&")[1].split("=")[1]; + User user = InMemoryUserRepository.findByAccount(account).get(); if (user.checkPassword(password)) { diff --git a/tomcat/src/main/resources/static/login.html b/tomcat/src/main/resources/static/login.html index f4ed9de875..bc933357f2 100644 --- a/tomcat/src/main/resources/static/login.html +++ b/tomcat/src/main/resources/static/login.html @@ -20,7 +20,7 @@

로그인

-
+
From 77c6b4250fddfc28fb0d7522194ba4687d207947 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Wed, 4 Sep 2024 20:44:40 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20Cookie=EC=97=90=20JSESSIONID=20=EA=B0=92=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index f9ab6c7008..2e9d471ba8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -16,6 +16,7 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class Http11Processor implements Runnable, Processor { @@ -80,9 +81,12 @@ public void process(final Socket connection) { return; } + UUID jSessionId = UUID.randomUUID(); + Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate302Response(responseBody, "text/html", "/index.html"); + response = generate302ResponseWithCookie(responseBody, "text/html", + "/index.html", jSessionId.toString()); } else { URL url = getClass().getClassLoader().getResource("static/401.html"); if (url == null) { @@ -176,4 +180,16 @@ private String generate302Response(String responseBody, String contentType, Stri responseBody); } + private String generate302ResponseWithCookie(String responseBody, String contentType, + String redirectUrl, String jSessionID) { + return String.join("\r\n", + "HTTP/1.1 302 Found", + "Location: " + redirectUrl, + "Content-Type: " + contentType + ";charset=utf-8", + "Content-Length: " + responseBody.getBytes().length, + "Set-Cookie: JSESSIONID=" + jSessionID + "; Path=/; HttpOnly", + "", + responseBody); + } + } From 41b82f7250e79625040484e3ad8069fa460f8df8 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Thu, 5 Sep 2024 17:22:20 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20=EC=84=B8=EC=85=98=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EC=B2=B4=ED=81=AC=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/catalina/Manager.java | 35 ++++++--------- .../apache/coyote/http11/Http11Processor.java | 43 +++++++++++++++++++ .../org/apache/coyote/http11/Session.java | 30 +++++++++++++ .../apache/coyote/http11/SessionManager.java | 29 +++++++++++++ 4 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Session.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java diff --git a/tomcat/src/main/java/org/apache/catalina/Manager.java b/tomcat/src/main/java/org/apache/catalina/Manager.java index e69410f6a9..03ad07e471 100644 --- a/tomcat/src/main/java/org/apache/catalina/Manager.java +++ b/tomcat/src/main/java/org/apache/catalina/Manager.java @@ -1,18 +1,16 @@ package org.apache.catalina; -import jakarta.servlet.http.HttpSession; +import org.apache.coyote.http11.Session; import java.io.IOException; /** - * A Manager manages the pool of Sessions that are associated with a - * particular Container. Different Manager implementations may support - * value-added features such as the persistent storage of session data, - * as well as migrating sessions for distributable web applications. + * A Manager manages the pool of Sessions that are associated with a particular Container. Different Manager + * implementations may support value-added features such as the persistent storage of session data, as well as migrating + * sessions for distributable web applications. *

- * In order for a Manager implementation to successfully operate - * with a Context implementation that implements reloading, it - * must obey the following constraints: + * In order for a Manager implementation to successfully operate with a Context implementation + * that implements reloading, it must obey the following constraints: *

    *
  • Must implement Lifecycle so that the Context can indicate * that a restart is required. @@ -29,28 +27,23 @@ public interface Manager { * * @param session Session to be added */ - void add(HttpSession session); + void add(Session session); /** - * Return the active Session, associated with this Manager, with the - * specified session id (if any); otherwise return null. + * Return the active Session, associated with this Manager, with the specified session id (if any); otherwise return + * null. * * @param id The session id for the session to be returned - * - * @exception IllegalStateException if a new session cannot be - * instantiated for any reason - * @exception IOException if an input/output error occurs while - * processing this request - * - * @return the request session or {@code null} if a session with the - * requested ID could not be found + * @return the request session or {@code null} if a session with the requested ID could not be found + * @throws IllegalStateException if a new session cannot be instantiated for any reason + * @throws IOException if an input/output error occurs while processing this request */ - HttpSession findSession(String id) throws IOException; + Session findSession(String id) throws IOException; /** * Remove this Session from the active Sessions for this Manager. * * @param session Session to be removed */ - void remove(HttpSession session); + void remove(Session session); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 2e9d471ba8..4aaf44982f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -76,6 +76,8 @@ public void process(final Socket connection) { if (user.checkPassword(password)) { log.info("user : {}", user); + SessionManager sessionManager = new SessionManager(); + URL url = getClass().getClassLoader().getResource("static/index.html"); if (url == null) { return; @@ -83,6 +85,10 @@ public void process(final Socket connection) { UUID jSessionId = UUID.randomUUID(); + Session session = new Session(jSessionId.toString()); + + session.setAttribute("user", user); + sessionManager.add(session); Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); response = generate302ResponseWithCookie(responseBody, "text/html", @@ -97,6 +103,43 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(path)); response = generate302Response(responseBody, "text/html", "/401.html"); } + } else if (page.startsWith("/login") && httpMethod.equals("GET")) { + SessionManager sessionManager = new SessionManager(); + + if (httpRequestHeaders.containsKey("Cookie") && + httpRequestHeaders.get("Cookie").startsWith("JSESSIONID=")) { + String jSessionId = httpRequestHeaders.get("Cookie").split("=")[1]; + Session session = sessionManager.findSession(jSessionId); + + if (session != null && session.getAttribute("user") != null) { + URL url = getClass().getClassLoader().getResource("static/index.html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate200Response(responseBody, "text/html"); + } else { + URL url = getClass().getClassLoader().getResource("static" + page + ".html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate200Response(responseBody, "text/html"); + } + } else { + URL url = getClass().getClassLoader().getResource("static" + page + ".html"); + if (url == null) { + return; + } + + Path path = Path.of(url.toURI()); + responseBody = new String(Files.readAllBytes(path)); + response = generate200Response(responseBody, "text/html"); + } } else if (page.equals("/register") && httpMethod.equals("POST")) { int contentLength = Integer.parseInt(httpRequestHeaders.get("Content-Length")); char[] buffer = new char[contentLength]; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Session.java b/tomcat/src/main/java/org/apache/coyote/http11/Session.java new file mode 100644 index 0000000000..1679c17d28 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Session.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11; + +import java.util.HashMap; +import java.util.Map; + +public class Session { + + private static final Map values = new HashMap<>(); + private final String id; + + public Session(final String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public Object getAttribute(final String name) { + return values.get(name); + } + + public void setAttribute(final String name, final Object value) { + values.put(name, value); + } + + public void removeAttribute(final String name) { + values.remove(name); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java b/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java new file mode 100644 index 0000000000..0383d6998b --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java @@ -0,0 +1,29 @@ +package org.apache.coyote.http11; + +import org.apache.catalina.Manager; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class SessionManager implements Manager { + + private static final Map SESSIONS = new HashMap<>(); + + public SessionManager() { + } + + @Override + public void add(Session session) { + SESSIONS.put(session.getId(), new Session(session.getId())); + } + + @Override + public Session findSession(String id) throws IOException { + return SESSIONS.get(id); + } + + @Override + public void remove(Session session) { + SESSIONS.remove(session.getId()); + } +} From 0974368d2779b45c7f131745ba66c5e74e6fbf8f Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Thu, 5 Sep 2024 19:43:10 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat:=20httpRequest=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EA=B0=80=20=EC=9A=94=EC=B2=AD=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 38 ++------- .../coyote/http11/request/HttpRequest.java | 77 +++++++++++++++++++ .../http11/request/HttpRequestParser.java | 12 +++ 3 files changed, 97 insertions(+), 30 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 4aaf44982f..3a3de4a85c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,17 +4,15 @@ import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.http11.request.HttpRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.net.Socket; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -37,27 +35,13 @@ public void run() { @Override public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream(); - final var bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { + final var outputStream = connection.getOutputStream()) { - String firstLine = bufferedReader.readLine(); - if (firstLine == null) { - return; - } - - Map httpRequestHeaders = new HashMap<>(); - - while (!firstLine.isEmpty()) { - String line = bufferedReader.readLine(); - if (line.isEmpty()) { - break; - } - String[] headerParts = line.split(": "); - httpRequestHeaders.put(headerParts[0], headerParts[1]); - } + HttpRequest httpRequest = HttpRequest.from(inputStream); - String httpMethod = firstLine.split(" ")[0]; - String page = firstLine.split(" ")[1]; + Map httpRequestHeaders = httpRequest.getHeaders(); + String httpMethod = httpRequest.getHttpMethod(); + String page = httpRequest.getPath(); String responseBody = ""; String response = ""; @@ -65,10 +49,7 @@ public void process(final Socket connection) { responseBody = "Hello world!"; response = generate200Response(responseBody, "text/html"); } else if (page.startsWith("/login") && httpMethod.equals("POST")) { - int contentLength = Integer.parseInt(httpRequestHeaders.get("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String requestBody = new String(buffer); + String requestBody = httpRequest.getBody(); String account = requestBody.split("&")[0].split("=")[1]; String password = requestBody.split("&")[1].split("=")[1]; @@ -141,10 +122,7 @@ public void process(final Socket connection) { response = generate200Response(responseBody, "text/html"); } } else if (page.equals("/register") && httpMethod.equals("POST")) { - int contentLength = Integer.parseInt(httpRequestHeaders.get("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String requestBody = new String(buffer); + String requestBody = httpRequest.getBody(); String account = requestBody.split("&")[0].split("=")[1]; String email = requestBody.split("&")[1].split("=")[1]; String password = requestBody.split("&")[2].split("=")[1]; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java new file mode 100644 index 0000000000..d2f95eedac --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -0,0 +1,77 @@ +package org.apache.coyote.http11.request; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + + private static final String HEADER_SPLIT_DELIMITER = ": "; + + private final String method; + private final String path; + private final String version; + private final Map headers; + private final String body; + + public HttpRequest(String method, String path, String version, Map headers, String body) { + this.method = method; + this.path = path; + this.version = version; + this.headers = headers; + this.body = body; + } + + public static HttpRequest from(InputStream inputStream) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String startLine = bufferedReader.readLine(); + + String[] parsedStartLine = HttpRequestParser.parseStartLine(startLine); + String method = parsedStartLine[0]; + String path = parsedStartLine[1]; + String version = parsedStartLine[2]; + + Map headers = new HashMap<>(); + while (!startLine.isEmpty()) { + String line = bufferedReader.readLine(); + if (line.isEmpty()) { + break; + } + String[] headerParts = line.split(HEADER_SPLIT_DELIMITER, 2); + headers.put(headerParts[0], headerParts[1]); + } + + if (method.equals("POST")) { + int contentLength = Integer.parseInt(headers.get("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String body = new String(buffer); + return new HttpRequest(method, path, version, headers, body); + } + + return new HttpRequest(method, path, version, headers, null); + } + + public String getHttpMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public Map getHeaders() { + return headers; + } + + public String getBody() { + return body; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java new file mode 100644 index 0000000000..9e04e37860 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java @@ -0,0 +1,12 @@ +package org.apache.coyote.http11.request; + +public class HttpRequestParser { + + public static String[] parseStartLine(String startLine) { + String[] parsedStartLine = startLine.split(" "); + if (parsedStartLine.length != 3) { + throw new IllegalArgumentException("Invalid start line: " + startLine); + } + return parsedStartLine; + } +} From 553ce122d24fda79c504518fb1c008bea24abe46 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Thu, 5 Sep 2024 22:27:09 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20httpResponse=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EA=B0=80=20=EC=9A=94=EC=B2=AD=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 67 ++++++------------- .../coyote/http11/response/HttpResponse.java | 57 ++++++++++++++++ 2 files changed, 76 insertions(+), 48 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 3a3de4a85c..f4ec015d8b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -5,6 +5,7 @@ import com.techcourse.model.User; import org.apache.coyote.Processor; import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; @@ -39,15 +40,16 @@ public void process(final Socket connection) { HttpRequest httpRequest = HttpRequest.from(inputStream); + String version = httpRequest.getVersion(); Map httpRequestHeaders = httpRequest.getHeaders(); String httpMethod = httpRequest.getHttpMethod(); String page = httpRequest.getPath(); + HttpResponse httpResponse; String responseBody = ""; - String response = ""; if (page.equals("/")) { responseBody = "Hello world!"; - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } else if (page.startsWith("/login") && httpMethod.equals("POST")) { String requestBody = httpRequest.getBody(); String account = requestBody.split("&")[0].split("=")[1]; @@ -72,8 +74,9 @@ public void process(final Socket connection) { sessionManager.add(session); Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate302ResponseWithCookie(responseBody, "text/html", - "/index.html", jSessionId.toString()); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + httpResponse.addHeader("Location", "/index.html"); + httpResponse.addHeader("Set-Cookie", "JSESSIONID=" + jSessionId); } else { URL url = getClass().getClassLoader().getResource("static/401.html"); if (url == null) { @@ -82,7 +85,8 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate302Response(responseBody, "text/html", "/401.html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + httpResponse.addHeader("Location", "/401.html"); } } else if (page.startsWith("/login") && httpMethod.equals("GET")) { SessionManager sessionManager = new SessionManager(); @@ -100,7 +104,7 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } else { URL url = getClass().getClassLoader().getResource("static" + page + ".html"); if (url == null) { @@ -109,7 +113,7 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } } else { URL url = getClass().getClassLoader().getResource("static" + page + ".html"); @@ -119,7 +123,7 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } } else if (page.equals("/register") && httpMethod.equals("POST")) { String requestBody = httpRequest.getBody(); @@ -135,7 +139,8 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate302Response(responseBody, "text/html", "/index.html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + httpResponse.addHeader("Location", "/index.html"); } else if (page.startsWith("/css/")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { @@ -144,7 +149,7 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/css"); + httpResponse = HttpResponse.of(version,200, "text/css", responseBody); } else if (page.contains(".js")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { @@ -152,7 +157,7 @@ public void process(final Socket connection) { } Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/javascript"); + httpResponse = HttpResponse.of(version,200, "text/javascript", responseBody); } else if (page.endsWith(".html")) { URL url = getClass().getClassLoader().getResource("static" + page); if (url == null) { @@ -161,7 +166,7 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } else { URL url = getClass().getClassLoader().getResource("static" + page + ".html"); if (url == null) { @@ -170,47 +175,13 @@ public void process(final Socket connection) { Path path = Path.of(url.toURI()); responseBody = new String(Files.readAllBytes(path)); - response = generate200Response(responseBody, "text/html"); + httpResponse = HttpResponse.of(version,200, "text/html", responseBody); } - - outputStream.write(response.getBytes()); - outputStream.flush(); + httpResponse.send(outputStream); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); } catch (URISyntaxException e) { throw new RuntimeException(e); } } - - private String generate200Response(String responseBody, String contentType) { - return String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + ";charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - } - - private String generate302Response(String responseBody, String contentType, String redirectUrl) { - return String.join("\r\n", - "HTTP/1.1 302 Found", - "Location: " + redirectUrl, - "Content-Type: " + contentType + ";charset=utf-8", - "Content-Length: " + responseBody.getBytes().length, - "", - responseBody); - } - - private String generate302ResponseWithCookie(String responseBody, String contentType, - String redirectUrl, String jSessionID) { - return String.join("\r\n", - "HTTP/1.1 302 Found", - "Location: " + redirectUrl, - "Content-Type: " + contentType + ";charset=utf-8", - "Content-Length: " + responseBody.getBytes().length, - "Set-Cookie: JSESSIONID=" + jSessionID + "; Path=/; HttpOnly", - "", - responseBody); - } - } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java new file mode 100644 index 0000000000..ccb45a1bcf --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -0,0 +1,57 @@ +package org.apache.coyote.http11.response; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class HttpResponse { + + private final String version; + private final int statusCode; + private final String statusMessage; + private final Map headers; + private final String body; + + public HttpResponse(String version, int statusCode, String statusMessage, Map headers, + String body) { + this.version = version; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.headers = headers; + this.body = body; + } + + public static HttpResponse of(String version, int statusCode, + String contentType, String body) { + Map headers = new HashMap<>(); + headers.put("Content-Type", contentType); + headers.put("Content-Length", body.getBytes().length); + + return new HttpResponse(version, statusCode, "OK", headers, body); + } + + public void addHeader(String key, String value) { + headers.put(key, value); + } + + public void send(OutputStream outputStream) { + try { + StringBuilder sb = new StringBuilder(); + sb.append(version).append(" ").append(statusCode).append(" ").append(statusMessage).append(" \r\n"); + for (Entry entry : headers.entrySet()) { + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue()); + sb.append(" \r\n"); + } + sb.append("\r\n"); + sb.append(body); + outputStream.write(sb.toString().getBytes()); + outputStream.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} From 39ed56436356a9012ece546bd3838a7805f06d7d Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 13:59:11 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat:=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EB=B0=94=EC=9D=B4=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EB=A1=9C=EB=94=A9=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 112 ++++-------------- .../http11/controller/ResourceLoader.java | 20 ++++ 2 files changed, 44 insertions(+), 88 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/ResourceLoader.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index f4ec015d8b..5f5d21eddb 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,6 +4,7 @@ import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.http11.controller.ResourceLoader; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; import org.slf4j.Logger; @@ -11,9 +12,6 @@ import java.io.IOException; import java.net.Socket; import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; import java.util.UUID; @@ -46,10 +44,10 @@ public void process(final Socket connection) { String page = httpRequest.getPath(); HttpResponse httpResponse; - String responseBody = ""; + String responseBody; if (page.equals("/")) { responseBody = "Hello world!"; - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } else if (page.startsWith("/login") && httpMethod.equals("POST")) { String requestBody = httpRequest.getBody(); String account = requestBody.split("&")[0].split("=")[1]; @@ -60,32 +58,18 @@ public void process(final Socket connection) { if (user.checkPassword(password)) { log.info("user : {}", user); SessionManager sessionManager = new SessionManager(); - - URL url = getClass().getClassLoader().getResource("static/index.html"); - if (url == null) { - return; - } - UUID jSessionId = UUID.randomUUID(); - Session session = new Session(jSessionId.toString()); - session.setAttribute("user", user); sessionManager.add(session); - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + + responseBody = new String(ResourceLoader.loadResource("static/index.html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); httpResponse.addHeader("Location", "/index.html"); httpResponse.addHeader("Set-Cookie", "JSESSIONID=" + jSessionId); } else { - URL url = getClass().getClassLoader().getResource("static/401.html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static/401.html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); httpResponse.addHeader("Location", "/401.html"); } } else if (page.startsWith("/login") && httpMethod.equals("GET")) { @@ -97,33 +81,15 @@ public void process(final Socket connection) { Session session = sessionManager.findSession(jSessionId); if (session != null && session.getAttribute("user") != null) { - URL url = getClass().getClassLoader().getResource("static/index.html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static/index.html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } else { - URL url = getClass().getClassLoader().getResource("static" + page + ".html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page + ".html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } } else { - URL url = getClass().getClassLoader().getResource("static" + page + ".html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page + ".html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } } else if (page.equals("/register") && httpMethod.equals("POST")) { String requestBody = httpRequest.getBody(); @@ -131,51 +97,21 @@ public void process(final Socket connection) { String email = requestBody.split("&")[1].split("=")[1]; String password = requestBody.split("&")[2].split("=")[1]; InMemoryUserRepository.save(new User(account, email, password)); - - URL url = getClass().getClassLoader().getResource("static/index.html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static/index.html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); httpResponse.addHeader("Location", "/index.html"); } else if (page.startsWith("/css/")) { - URL url = getClass().getClassLoader().getResource("static" + page); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/css", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page)); + httpResponse = HttpResponse.of(version, 200, "text/css", responseBody); } else if (page.contains(".js")) { - URL url = getClass().getClassLoader().getResource("static" + page); - if (url == null) { - return; - } - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/javascript", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page)); + httpResponse = HttpResponse.of(version, 200, "text/javascript", responseBody); } else if (page.endsWith(".html")) { - URL url = getClass().getClassLoader().getResource("static" + page); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page)); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } else { - URL url = getClass().getClassLoader().getResource("static" + page + ".html"); - if (url == null) { - return; - } - - Path path = Path.of(url.toURI()); - responseBody = new String(Files.readAllBytes(path)); - httpResponse = HttpResponse.of(version,200, "text/html", responseBody); + responseBody = new String(ResourceLoader.loadResource("static" + page + ".html")); + httpResponse = HttpResponse.of(version, 200, "text/html", responseBody); } httpResponse.send(outputStream); } catch (IOException | UncheckedServletException e) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/ResourceLoader.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/ResourceLoader.java new file mode 100644 index 0000000000..bce0bb3c39 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/ResourceLoader.java @@ -0,0 +1,20 @@ +package org.apache.coyote.http11.controller; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ResourceLoader { + + public static byte[] loadResource(String resourceName) throws URISyntaxException, IOException { + URL url = ResourceLoader.class.getClassLoader().getResource(resourceName); + if (url == null) { + throw new IllegalArgumentException("존재하지 않는 리소스 입니다." + resourceName); + } + + Path path = Path.of(url.toURI()); + return Files.readAllBytes(path); + } +} From 03057fc2ae576fcdfc6ec31fb872ace14f91e94e Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 14:00:08 +0900 Subject: [PATCH 14/18] =?UTF-8?q?feat:=20content-type=EC=97=90=20charset?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/response/HttpResponse.java | 2 +- .../java/org/apache/coyote/http11/Http11ProcessorTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index ccb45a1bcf..7eea9700d7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -26,7 +26,7 @@ public HttpResponse(String version, int statusCode, String statusMessage, Map headers = new HashMap<>(); - headers.put("Content-Type", contentType); + headers.put("Content-Type", contentType + ";charset=utf-8"); headers.put("Content-Length", body.getBytes().length); return new HttpResponse(version, statusCode, "OK", headers, body); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..fe20ebd29f 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -24,8 +24,8 @@ void process() { // then var expected = String.join("\r\n", "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", "Content-Length: 12 ", + "Content-Type: text/html;charset=utf-8 ", "", "Hello world!"); @@ -51,8 +51,8 @@ void index() throws IOException { // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); var expected = "HTTP/1.1 200 OK \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + "Content-Length: 5564 \r\n" + + "Content-Type: text/html;charset=utf-8 \r\n" + "\r\n"+ new String(Files.readAllBytes(new File(resource.getFile()).toPath())); From 807d2e1b34fd003bea911f5da851c77a98cd3502 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 14:21:37 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat:=200=EB=8B=A8=EA=B3=84=20=ED=9C=B4?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8B=B1=20=EC=A0=9C=EA=B1=B0=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/com/example/GreetingController.java | 2 +- .../example/cachecontrol/CacheWebConfig.java | 1 + .../CacheWebRequestInterceptor.java | 22 +++++++++++++++++++ .../{templates => static}/index.html | 0 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java rename study/src/main/resources/{templates => static}/index.html (100%) diff --git a/study/src/main/java/cache/com/example/GreetingController.java b/study/src/main/java/cache/com/example/GreetingController.java index c0053cda42..7fed5a9f3f 100644 --- a/study/src/main/java/cache/com/example/GreetingController.java +++ b/study/src/main/java/cache/com/example/GreetingController.java @@ -12,7 +12,7 @@ public class GreetingController { @GetMapping("/") public String index() { - return "index"; + return "index.html"; } /** diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java index 305b1f1e1e..430644c9d6 100644 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java +++ b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java @@ -9,5 +9,6 @@ public class CacheWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(new CacheWebRequestInterceptor()); } } diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java new file mode 100644 index 0000000000..fe4d728cb7 --- /dev/null +++ b/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java @@ -0,0 +1,22 @@ +package cache.com.example.cachecontrol; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.CacheControl; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +@Component +public class CacheWebRequestInterceptor implements HandlerInterceptor { + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, + ModelAndView modelAndView) throws Exception { + final String cacheControl = CacheControl + .noCache() + .cachePrivate() + .getHeaderValue(); + response.addHeader("Cache-Control", cacheControl); + } +} diff --git a/study/src/main/resources/templates/index.html b/study/src/main/resources/static/index.html similarity index 100% rename from study/src/main/resources/templates/index.html rename to study/src/main/resources/static/index.html From faafaa6f0b40fc8dff9b4bfd59bcf7f32cc1b308 Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 14:33:08 +0900 Subject: [PATCH 16/18] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20Http=20Comp?= =?UTF-8?q?ression=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/main/resources/application.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index e3503a5fb9..7277616bc7 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -8,3 +8,6 @@ server: threads: min-spare: 2 max: 2 + compression: + enabled: true + min-response-size: 10 \ No newline at end of file From 27471e50638df1c7fdb6d40b10abeb6a2c6df4fb Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 15:36:59 +0900 Subject: [PATCH 17/18] =?UTF-8?q?feat:=202=EB=8B=A8=EA=B3=84=20ETag/If-Non?= =?UTF-8?q?e-Match=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cache/com/example/GreetingController.java | 5 ++--- .../com/example/etag/EtagFilterConfiguration.java | 14 ++++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/study/src/main/java/cache/com/example/GreetingController.java b/study/src/main/java/cache/com/example/GreetingController.java index 7fed5a9f3f..4ed8036e67 100644 --- a/study/src/main/java/cache/com/example/GreetingController.java +++ b/study/src/main/java/cache/com/example/GreetingController.java @@ -1,12 +1,11 @@ package cache.com.example; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import jakarta.servlet.http.HttpServletResponse; - @Controller public class GreetingController { @@ -30,7 +29,7 @@ public String cacheControl(final HttpServletResponse response) { @GetMapping("/etag") public String etag() { - return "index"; + return "index.html"; } @GetMapping("/resource-versioning") diff --git a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java index 41ef7a3d9a..89efd40b24 100644 --- a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java +++ b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java @@ -1,12 +1,18 @@ package cache.com.example.etag; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.ShallowEtagHeaderFilter; @Configuration public class EtagFilterConfiguration { -// @Bean -// public FilterRegistrationBean shallowEtagHeaderFilter() { -// return null; -// } + @Bean + public FilterRegistrationBean shallowEtagHeaderFilter() { + FilterRegistrationBean filterFilterRegistrationBean + = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); + filterFilterRegistrationBean.addUrlPatterns("/etag"); + return filterFilterRegistrationBean; + } } From ccffb6b3005a5335a68aae26867d172da049b0ab Mon Sep 17 00:00:00 2001 From: tkdgur0906 Date: Fri, 6 Sep 2024 23:39:53 +0900 Subject: [PATCH 18/18] =?UTF-8?q?feat:=203=EB=8B=A8=EA=B3=84=20=EC=BA=90?= =?UTF-8?q?=EC=8B=9C=20=EB=AC=B4=ED=9A=A8=ED=99=94=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/cachecontrol/CacheWebConfig.java | 9 +++++++- .../CacheWebRequestInterceptor.java | 22 ------------------- .../example/etag/EtagFilterConfiguration.java | 10 +++++++++ .../version/CacheBustingWebConfig.java | 5 ++++- 4 files changed, 22 insertions(+), 24 deletions(-) delete mode 100644 study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java index 430644c9d6..c36fc0cc20 100644 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java +++ b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java @@ -1,14 +1,21 @@ package cache.com.example.cachecontrol; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.WebContentInterceptor; @Configuration public class CacheWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(final InterceptorRegistry registry) { - registry.addInterceptor(new CacheWebRequestInterceptor()); + WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); + webContentInterceptor.addCacheMapping( + CacheControl.noCache().cachePrivate(), + "/**" + ); + registry.addInterceptor(webContentInterceptor); } } diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java deleted file mode 100644 index fe4d728cb7..0000000000 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebRequestInterceptor.java +++ /dev/null @@ -1,22 +0,0 @@ -package cache.com.example.cachecontrol; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.CacheControl; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -@Component -public class CacheWebRequestInterceptor implements HandlerInterceptor { - - @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, - ModelAndView modelAndView) throws Exception { - final String cacheControl = CacheControl - .noCache() - .cachePrivate() - .getHeaderValue(); - response.addHeader("Cache-Control", cacheControl); - } -} diff --git a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java index 89efd40b24..a0e10ae8b1 100644 --- a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java +++ b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java @@ -1,18 +1,28 @@ package cache.com.example.etag; +import cache.com.example.version.ResourceVersion; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.ShallowEtagHeaderFilter; +import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES; + @Configuration public class EtagFilterConfiguration { + private final ResourceVersion resourceVersion; + + public EtagFilterConfiguration(ResourceVersion resourceVersion) { + this.resourceVersion = resourceVersion; + } + @Bean public FilterRegistrationBean shallowEtagHeaderFilter() { FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); filterFilterRegistrationBean.addUrlPatterns("/etag"); + filterFilterRegistrationBean.addUrlPatterns(PREFIX_STATIC_RESOURCES + "/" + resourceVersion.getVersion() + "/*"); return filterFilterRegistrationBean; } } diff --git a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java index 6da6d2c795..6b8f137d59 100644 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java @@ -2,8 +2,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.time.Duration; @Configuration public class CacheBustingWebConfig implements WebMvcConfigurer { @@ -20,6 +22,7 @@ public CacheBustingWebConfig(ResourceVersion version) { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") - .addResourceLocations("classpath:/static/"); + .addResourceLocations("classpath:/static/") + .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic()); } }