-
Notifications
You must be signed in to change notification settings - Fork 309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[톰캣 구현하기 - 3, 4단계] 민트(유재민) 미션 제출합니다. #462
Changes from all commits
3f0294c
8305190
0d43834
cb4b214
e0a48ae
3ac7fc4
49c7bc1
f7f4b30
68e6b8b
f93afb6
a4e4859
75cc474
0c52760
33143c2
14f4173
f10daee
e9eb960
47be420
b9c41d4
ce51f43
a734474
5fe3478
632d976
eb08f63
7da6384
c09577b
d0be660
4ce0385
718dc00
281c806
f27518e
405a942
f252d96
f8b86c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,6 @@ handlebars: | |
server: | ||
tomcat: | ||
accept-count: 1 | ||
max-connections: 1 | ||
max-connections: 2 | ||
threads: | ||
max: 2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,36 @@ | ||
package org.apache.coyote.http11; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.exception.UncheckedServletException; | ||
import nextstep.jwp.model.User; | ||
import org.apache.coyote.Processor; | ||
import org.apache.coyote.http11.common.FileReader; | ||
import org.apache.coyote.http11.controller.AuthController; | ||
import org.apache.coyote.http11.controller.Controller; | ||
import org.apache.coyote.http11.controller.DefaultController; | ||
import org.apache.coyote.http11.controller.RegisterController; | ||
import org.apache.coyote.http11.controller.RequestMapping; | ||
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.BufferedOutputStream; | ||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.UncheckedIOException; | ||
import java.net.Socket; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Map.Entry; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import static org.apache.coyote.http11.common.HttpHeaderType.CONTENT_LENGTH; | ||
import static org.apache.coyote.http11.common.HttpHeaderType.CONTENT_TYPE; | ||
|
||
public class Http11Processor implements Runnable, Processor { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); | ||
private static final String SAFARI_CHROME_ACCEPT_HEADER_DEFAULT_VALUE = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"; | ||
|
||
private final Socket connection; | ||
private final SessionManager sessionManager = new SessionManager(); | ||
private final HttpRequestReader requestReader = new HttpRequestReader(); | ||
private final RequestMapping requestMapping = new RequestMapping( | ||
new AuthController(), | ||
new RegisterController(), | ||
new DefaultController() | ||
); | ||
|
||
public Http11Processor(final Socket connection) { | ||
this.connection = connection; | ||
|
@@ -51,186 +50,21 @@ public void process(final Socket connection) { | |
InputStreamReader inputStreamReader = new InputStreamReader(inputStream); | ||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); | ||
|
||
List<String> headers = new ArrayList<>(); | ||
|
||
String header = ""; | ||
while (!(header = bufferedReader.readLine()).equals("")) { | ||
headers.add(header); | ||
} | ||
String[] splitStatusLine = Objects.requireNonNull(headers.get(0)).split(" "); | ||
String requestMethod = splitStatusLine[0]; | ||
String requestUri = splitStatusLine[1]; | ||
|
||
String requestAcceptHeader = findHeader("Accept", headers); | ||
String contentTypeHeader = getContentTypeHeaderFrom(requestAcceptHeader); | ||
|
||
RequestHandler requestHandler; | ||
if (requestMethod.equalsIgnoreCase("POST")) { | ||
int contentLength = getContentLength(headers); | ||
String requestBody = readRequestBody(bufferedReader, contentLength); | ||
requestHandler = handlePostRequest(requestUri, requestBody); | ||
} else { | ||
String cookieHeader = findHeader("Cookie", headers); | ||
requestHandler = handleGetRequest(requestMethod, requestUri, cookieHeader); | ||
} | ||
HttpRequest httpRequest = requestReader.readHttpRequest(bufferedReader); | ||
HttpResponse httpResponse = new HttpResponse(httpRequest.httpVersion()); | ||
|
||
String responseBody = readFile(requestHandler.getResponseFilePath()); | ||
Controller controller = requestMapping.getController(httpRequest); | ||
controller.service(httpRequest, httpResponse); | ||
|
||
List<String> responseHeaders = new ArrayList<>(); | ||
responseHeaders.add("HTTP/1.1 " + requestHandler.getHttpStatus() + " "); | ||
responseHeaders.add(contentTypeHeader); | ||
responseHeaders.add("Content-Length: " + responseBody.getBytes().length + " "); | ||
for (Entry<String, String> headerEntry : requestHandler.getHeaders().entrySet()) { | ||
responseHeaders.add(headerEntry.getKey() + ": " + headerEntry.getValue()); | ||
} | ||
String responseHeader = String.join("\r\n", responseHeaders); | ||
httpResponse.setBody(FileReader.readFile(httpResponse.getResponseFileName())); | ||
httpResponse.addHeader(CONTENT_LENGTH.getValue(), String.valueOf(httpResponse.getBody().getBytes().length)); | ||
httpResponse.addHeader(CONTENT_TYPE.getValue(), httpRequest.getContentTypeByAcceptHeader()); | ||
|
||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); | ||
|
||
var response = String.join("\r\n", responseHeader, "", responseBody); | ||
|
||
bufferedOutputStream.write(response.getBytes()); | ||
bufferedOutputStream.write(httpResponse.format().getBytes()); | ||
bufferedOutputStream.flush(); | ||
} catch (IOException | UncheckedServletException e) { | ||
} catch (Exception e) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 왜 Exception으로 변경하셨을까용? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Controller.service()에서 발생할 수 있는 에러로 여러 종류가 있을 거라고 생각했습니다. |
||
log.error(e.getMessage(), e); | ||
} | ||
} | ||
|
||
private static String findHeader(String key, List<String> headers) { | ||
return headers.stream() | ||
.filter(it -> it.startsWith(key + ": ")) | ||
.findFirst() | ||
.orElse("Accept: " + SAFARI_CHROME_ACCEPT_HEADER_DEFAULT_VALUE); | ||
} | ||
|
||
private static String getContentTypeHeaderFrom(String requestAcceptHeader) { | ||
String[] splitAcceptHeader = requestAcceptHeader.split(" "); | ||
String headerValue = splitAcceptHeader[1]; | ||
String[] acceptTypes = headerValue.split(";"); | ||
String[] splitAcceptTypes = acceptTypes[0].split(","); | ||
|
||
if (Arrays.asList(splitAcceptTypes).contains("text/css")) { | ||
return "Content-Type: text/css;charset=utf-8 "; | ||
} | ||
return "Content-Type: text/html;charset=utf-8 "; | ||
} | ||
|
||
private int getContentLength(List<String> headers) { | ||
Optional<String> contentLengthHeader = headers.stream() | ||
.filter(it -> it.startsWith("Content-Length")) | ||
.findFirst(); | ||
|
||
if (contentLengthHeader.isEmpty()) { | ||
return -1; | ||
} | ||
int index = contentLengthHeader.get().indexOf(" "); | ||
return Integer.parseInt(contentLengthHeader.get().substring(index + 1)); | ||
} | ||
|
||
private static String readRequestBody(BufferedReader bufferedReader, int contentLength) throws IOException { | ||
char[] buffer = new char[contentLength]; | ||
bufferedReader.read(buffer, 0, contentLength); | ||
return new String(buffer); | ||
} | ||
|
||
private RequestHandler handlePostRequest(String requestUri, String requestBody) { | ||
String[] splitRequestBody = requestBody.split("&"); | ||
if (requestUri.equals("/login")) { | ||
return handleLoginRequest(splitRequestBody); | ||
} | ||
if (requestUri.equals("/register")) { | ||
return handleRegisterRequest(splitRequestBody); | ||
} | ||
return RequestHandler.of("GET", "404 Not Found", "static/404.html"); | ||
} | ||
|
||
private RequestHandler handleLoginRequest(String[] splitQueryString) { | ||
Optional<String> account = getValueOf("account", splitQueryString); | ||
Optional<String> password = getValueOf("password", splitQueryString); | ||
|
||
if (account.isEmpty() || password.isEmpty()) { | ||
return RequestHandler.of("GET", "400 Bad Request", "static/401.html"); | ||
} | ||
|
||
Optional<User> findUser = InMemoryUserRepository.findByAccount(account.get()); | ||
if (findUser.isPresent() && findUser.get().checkPassword(password.get())) { | ||
User user = findUser.get(); | ||
log.info(user.toString()); | ||
Session session = new Session(UUID.randomUUID().toString()); | ||
session.setAttribute("user", user); | ||
sessionManager.add(session); | ||
RequestHandler requestHandler = RequestHandler.of("GET", "302 Found", "static/index.html"); | ||
requestHandler.addHeader("Set-Cookie", "JSESSIONID=" + session.getId()); | ||
return requestHandler; | ||
} | ||
return RequestHandler.of("GET", "401 Unauthorized", "static/401.html"); | ||
} | ||
|
||
private Optional<String> getValueOf(String key, String[] splitQueryString) { | ||
return Arrays.stream(splitQueryString) | ||
.filter(it -> equalsKey(key, it)) | ||
.map(it -> it.substring(it.indexOf("=") + 1)) | ||
.findFirst(); | ||
} | ||
|
||
private boolean equalsKey(String expected, String actual) { | ||
String[] splitActual = actual.split("="); | ||
return splitActual[0].equals(expected); | ||
} | ||
|
||
private RequestHandler handleRegisterRequest(String[] splitQueryString) { | ||
Optional<String> account = getValueOf("account", splitQueryString); | ||
Optional<String> email = getValueOf("email", splitQueryString); | ||
Optional<String> password = getValueOf("password", splitQueryString); | ||
|
||
if (account.isEmpty() || email.isEmpty() || password.isEmpty()) { | ||
return RequestHandler.of("GET", "400 Bad Request", "static/register.html"); | ||
} | ||
|
||
InMemoryUserRepository.save(new User(account.get(), password.get(), email.get())); | ||
return RequestHandler.of("GET", "302 Found", "static/index.html"); | ||
} | ||
|
||
private RequestHandler handleGetRequest(String requestMethod, String requestUri, String cookie) throws IOException { | ||
if (!requestMethod.equalsIgnoreCase("GET")) { | ||
throw new IllegalArgumentException("GET 요청만 처리 가능합니다."); | ||
} | ||
|
||
if (requestUri.equals("/login.html") || requestUri.equals("/login")) { | ||
return handleLoginPageRequest(cookie); | ||
} | ||
|
||
String fileName = "static" + requestUri; | ||
return RequestHandler.of("GET", "200 OK", fileName); | ||
} | ||
|
||
private RequestHandler handleLoginPageRequest(String cookie) throws IOException { | ||
Optional<String> sessionId = getSessionId(cookie); | ||
if (sessionId.isEmpty()) { | ||
return RequestHandler.of("GET", "200 OK", "static/login.html"); | ||
} | ||
Session session = sessionManager.findSession(sessionId.get()); | ||
User user = getUser(session); | ||
if (InMemoryUserRepository.existsByAccount(user.getAccount())) { | ||
return RequestHandler.of("GET", "302 Found", "static/index.html"); | ||
} | ||
return RequestHandler.of("GET", "200 OK", "static/login.html"); | ||
} | ||
|
||
private Optional<String> getSessionId(String cookieHeader) { | ||
String[] splitCookie = cookieHeader.split(" "); | ||
return getValueOf("JSESSIONID", splitCookie); | ||
} | ||
|
||
private User getUser(Session session) { | ||
return (User) session.getAttribute("user"); | ||
} | ||
|
||
private String readFile(String filePath) { | ||
try (Stream<String> lines = Files.lines(Paths.get(filePath))) { | ||
return lines.collect(Collectors.joining("\n", "", "\n")); | ||
} catch (IOException | UncheckedIOException e) { | ||
return "Hello world!"; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확실히 훨씬 간결해졌네요!👍