Skip to content

Commit

Permalink
refactor: 리팩터링
Browse files Browse the repository at this point in the history
  • Loading branch information
eunbii0213 committed Sep 4, 2023
1 parent dfc2fcc commit 6dc7f09
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 138 deletions.
211 changes: 104 additions & 107 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@
public class Http11Processor implements Runnable, Processor {

private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);

private static final char COLON = ':';
private static final String EMPTY_JSESSION = "";
private static final String CSS = ".css";
private static final String JS_ICO_CSS_REGEX = ".*\\.(js|ico|css)$";
private final Socket connection;


public Http11Processor(final Socket connection) {
this.connection = connection;
}
Expand All @@ -44,33 +48,21 @@ public void process(final Socket connection) {

BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String requestLine = br.readLine(); // HTTP 요청 라인을 읽음 (예: "GET /index.html HTTP/1.1")
final String httpMethod = parseHttpMethod(requestLine);
final Map<String, String> headers = parseRequestHeaders(br);
final String httpMethod = parseHttpMethod(requestLine); // HTTP method를 읽음 (예: GET)
final Map<String, String> headers = parseRequestHeaders(br); // header를 읽음

//post method일 때만 requestBody 읽음
if ("POST".equals(httpMethod)) {
if ("POST".equals(httpMethod)) { //post method일 때만 requestBody 읽어오고 user를 등록
final String requestBody = readRequestBody(br, headers);
// 이제 requestBody 변수에 POST 요청의 request body가 저장되어 있음
registerUser(requestBody);
}

String jsessionId="";
if (isContainJsessionId(headers)) {
jsessionId = parseJsessionId(headers);
}

// 파싱된 HTTP 요청에서 경로 추출
final String path = parseHttpRequest(requestLine);
String jsessionId = getJsessionId(headers);

// 경로를 기반으로 정적 파일을 읽고 응답 생성
final String parsedPath = parsePath(path, httpMethod, jsessionId);
final String responseBody = readStaticFile(path, httpMethod, parsedPath);

//css인 경우 content type을 다르게 준다
final String contentType = getContentType(path);

// JSESSIONID가 있는 경우, 없는 경우 다르게 response를 준다
final var response = getResponse(path, contentType, responseBody, headers, parsedPath);
final String path = parseHttpRequest(requestLine); // 파싱된 HTTP 요청에서 경로 추출
final String parsedPath = parsePath(path, jsessionId); // 경로를 기반으로 정적 파일을 읽고 응답 생성
final String responseBody = readStaticFile(parsedPath);
final String contentType = getContentType(path); // css인 경우 content type을 다르게 준다
final var response = getResponse(path, contentType, responseBody, headers, parsedPath, jsessionId); // JSESSIONID가 있는 경우, 없는 경우 다르게 response를 준다

outputStream.write(response.getBytes());
outputStream.flush();
Expand All @@ -79,9 +71,15 @@ public void process(final Socket connection) {
}
}

private String getResponse(String path, final String contentType, final String responseBody, final Map<String, String> headers, final String parsedPath) {
private String getJsessionId(final Map<String, String> headers) {
if (isContainJsessionId(headers)) {
return parseJsessionId(headers);
}
return UUID.randomUUID().toString();
}

private String getResponse(final String path, final String contentType, final String responseBody, final Map<String, String> headers, final String parsedPath, final String jsessionId) {
if (path.contains("/login?") && parsedPath.equals("/index.html") && !isContainJsessionId(headers)) {
final String jsessionId = UUID.randomUUID().toString();
return String.join("\r\n",
"HTTP/1.1 200 OK ",
"Set-Cookie: " + "JSESSIONID=" + jsessionId + " ",
Expand Down Expand Up @@ -127,10 +125,10 @@ private String parseJsessionId(final Map<String, String> headers) {
}
}

return "";
return "";
}

private String readRequestBody(BufferedReader br, Map<String, String> headers) throws IOException {
private String readRequestBody(final BufferedReader br, final Map<String, String> headers) throws IOException {
final int contentLength = Integer.parseInt(headers.get("Content-Length"));
final char[] buffer = new char[contentLength];
br.read(buffer, 0, contentLength);
Expand All @@ -139,44 +137,50 @@ private String readRequestBody(BufferedReader br, Map<String, String> headers) t
}

private Map<String, String> parseRequestHeaders(final BufferedReader br) throws IOException {
//request Header들 읽어오기
Map<String, String> headers = new HashMap<>();
final List<String> lines = readAllLines(br);

String lineForParse;
for (int i = 0; i < getEmptyLineIndex(lines); i++) {
lineForParse = lines.get(i);
int colonIndex = lineForParse.indexOf(COLON);
if (colonIndex != -1) {
String key = lineForParse.substring(0, colonIndex).trim();
String value = lineForParse.substring(colonIndex + 1).trim();
headers.put(key, value);
}
}
return headers;
}

private List<String> readAllLines(final BufferedReader br) throws IOException {
final List<String> lines = new ArrayList<>();
String line;
while (!(line = br.readLine()).equals("")) {
while (!(line = br.readLine()).equals(EMPTY_JSESSION)) {
lines.add(line);
}
return lines;
}

int emptyLineIndex = lines.indexOf("");
private int getEmptyLineIndex(final List<String> lines) {
int emptyLineIndex = lines.indexOf(EMPTY_JSESSION);
if (emptyLineIndex == -1) {
emptyLineIndex = lines.size();
}

String line2;
for (int i = 0; i < emptyLineIndex; i++) {
line2 = lines.get(i);
int colonIndex = line2.indexOf(':');
if (colonIndex != -1) {
String key = line2.substring(0, colonIndex).trim();
String value = line2.substring(colonIndex + 1).trim();
headers.put(key, value);
}
}
return headers;
return emptyLineIndex;
}

private String getContentType(final String path) {
final String contentType;
if (path.endsWith(".css")) {
contentType = "text/css;charset=utf-8";
if (path.endsWith(CSS)) {
contentType = "text/css;";
} else {
contentType = "text/html;charset=utf-8";
contentType = "text/html;";
}
return contentType;
}

private String parseHttpRequest(String requestLine) throws IOException {
private String parseHttpRequest(final String requestLine) throws IOException {
// 요청 라인을 공백으로 분리하여 경로를 추출
String[] requestParts = requestLine.split(" ");

Expand All @@ -187,81 +191,50 @@ private String parseHttpRequest(String requestLine) throws IOException {
}
}

private String parseHttpMethod(String requestLine) throws IOException {
private String parseHttpMethod(final String requestLine) throws IOException {
// 요청 라인을 공백으로 분리하여 경로를 추출
String[] requestParts = requestLine.split(" ");
return requestParts[0];
}

private String readStaticFile(String path, String httpMethod, String parsedPath) throws IOException {
// 경로를 기반으로 정적 파일을 읽고 그 내용을 반환하는 로직을 작성해야 합니다.
// 이 예제에서는 간단하게 파일을 읽어오는 방법을 보여줍니다.

// 클래스 패스에서 정적 파일을 읽을 수 있도록 리소스 로더를 사용
private String readStaticFile(final String parsedPath) throws IOException {
ClassLoader classLoader = getClass().getClassLoader();
InputStream resourceInputStream = classLoader.getResourceAsStream("static" + parsedPath);

StringBuilder content = new StringBuilder();
if (resourceInputStream != null) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceInputStream))) {

String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
}
}

// content-type header 추가한 http 응답 변환
return content.toString();
}

private String parsePath(String path, String httpMethod, String jsessionId) {
if (path.equals("/")) {
return "/index.html";
}

if (path.equals("/login")) {
if(SessionManager.validJsession(jsessionId) && !jsessionId.equals("")) {
path = "/index.html";
} else {
path = "/login.html";
}
}

if (path.startsWith("/login?")) {
if (isValidUser(path, jsessionId)) {
path = "/index.html";
} else {
path = "/401.html";
private String parsePath(final String path, final String jsessionId) {
if (path.contains("/login")) {
if (path.contains("?") && !isValidUser(path, jsessionId)) {
return "/401.html";
} else if (!SessionManager.validJsession(jsessionId)) {
return "/login.html";
}
} else if (path.equals("/register")) {
return "/register.html";
} else if (path.matches(JS_ICO_CSS_REGEX)) {
return path;
}

if (path.equals("/register")) {
path = "/register.html";
}

if (path.startsWith("/register") && httpMethod.equals("POST")) {
path = "/index.html";
}

return path;
return "/index.html";
}

private boolean isValidUser(final String path, String jsessionId) {
final StringTokenizer st = new StringTokenizer(path, "&");
String parsedAccount = "";
String parsedPassword = "";

while (st.hasMoreTokens()) {
final String token = st.nextToken();

if (token.startsWith("/login?account=")) {
parsedAccount = token.substring("/login?account=".length());
} else if (token.startsWith("password=")) {
parsedPassword = token.substring("password=".length());
}
}
private boolean isValidUser(final String path, final String jsessionId) {
String noUrlPath = path.replace("/login?","");
Map<String, String> loginData = parseLoginData(noUrlPath);
String parsedAccount = loginData.get("account");
String parsedPassword = loginData.get("password");

final Optional<User> maybeUser = InMemoryUserRepository.findByAccount(parsedAccount);
if (maybeUser.isPresent()) {
Expand All @@ -276,26 +249,50 @@ private boolean isValidUser(final String path, String jsessionId) {
return false;
}

private void registerUser(final String requestBody) throws IOException {
final StringTokenizer st = new StringTokenizer(requestBody, "&");
String parsedAccount = "";
String parsedEmail = "";
String parsedPassword = "";
private Map<String, String> parseLoginData(final String path) {
Map<String, String> loginData = new HashMap<>();
final StringTokenizer st = new StringTokenizer(path, "&");

while (st.hasMoreTokens()) {
final String token = st.nextToken();
if (token.startsWith("account=")) {
parsedAccount = token.substring("account=".length());
} else if (token.startsWith("email=")) {
parsedEmail = token.substring("email=".length());
} else if (token.startsWith("password=")) {
parsedPassword = token.substring("password=".length());
String token = st.nextToken();
int equalsIndex = token.indexOf('=');

if (equalsIndex != -1) {
String key = token.substring(0, equalsIndex);
String value = token.substring(equalsIndex + 1);
loginData.put(key, value);
}
}

return loginData;
}

private void registerUser(final String requestBody) throws IOException {
Map<String, String> userData = parseRequestBody(requestBody);
String parsedAccount = userData.get("account");
String parsedEmail = userData.get("email");
String parsedPassword = userData.get("password");

final User user = new User(parsedAccount, parsedPassword, parsedEmail);
InMemoryUserRepository.save(user);

log.info("유저 저장 성공!");
}

private Map<String, String> parseRequestBody(final String requestBody) {
Map<String, String> userData = new HashMap<>();
StringTokenizer st = new StringTokenizer(requestBody, "&");

while (st.hasMoreTokens()) {
String token = st.nextToken();
int equalsIndex = token.indexOf('=');

if (equalsIndex != -1) {
String key = token.substring(0, equalsIndex);
String value = token.substring(equalsIndex + 1);
userData.put(key, value);
}
}

return userData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,22 @@
class Http11ProcessorTest {

@Test
void process() {
void index() throws IOException {
// given
final var socket = new StubSocket();
final var processor = new Http11Processor(socket);
final URL resource = getClass().getClassLoader().getResource("static/index.html");

// when
processor.process(socket);

// 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-8charset=utf-8 ",
"Content-Length: 5564 ",
"",
"Hello world!");

assertThat(socket.output()).isEqualTo(expected);
}

@Test
void index() throws IOException {
// given
final String httpRequest= String.join("\r\n",
"GET /index.html HTTP/1.1 ",
"Host: localhost:8080 ",
"Connection: keep-alive ",
"",
"");

final var socket = new StubSocket(httpRequest);
final Http11Processor processor = new Http11Processor(socket);

// when
processor.process(socket);

// 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" +
"\r\n"+
new String(Files.readAllBytes(new File(resource.getFile()).toPath()));
new String(Files.readAllBytes(new File(resource.getFile()).toPath())));

assertThat(socket.output()).isEqualTo(expected);
}
Expand Down

0 comments on commit 6dc7f09

Please sign in to comment.