Skip to content

Commit

Permalink
[톰캣 구현하기 1 & 2단계] 달리(김동연) 미션 제출합니다 (#351)
Browse files Browse the repository at this point in the history
* feat: ioStream 학습

* feat: fileTest 학습

* docs: 기능 구현 목록 작성

* feat: get method 응답 기능 구현

* feat: queryString 파싱 기능 구현

* feat: 로그인 redirect 구현

* feat: 로그인 redirect 구현

* feat: Session 구현

* refactor : 구조 변경

* fix : 로그인 페이지 오류 수정

* style : 코드 컨벤션 준수 및 사용하지 않는 메서드 제거

* style : cookie header로 이동

* refactor: 중복된 file 코드 통합

* feat : auto-increment id 추가

* test : 테스트 추가

* refactor: equals 재정의
  • Loading branch information
waterricecake authored Sep 7, 2023
1 parent 68db530 commit 841e70a
Show file tree
Hide file tree
Showing 27 changed files with 968 additions and 58 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
# 톰캣 구현하기

## 요구사항

- [x] GET /index.html 응답
- [x] CSS 지원
- [x] Query String 파싱

- [x] HTTP Status Code 302
- [x] POST 방식으로 회원가입
- [x] Cookie에 JSESSIONID 값 저장하기
- [x] Session 구현하기

## 기능 목록
- [x] header에서 입력 url을 추출한다
- [x] resource에 해당 정적 html 파일을 가져온다
19 changes: 15 additions & 4 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package study;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -28,7 +36,9 @@ class FileTest {
final String fileName = "nextstep.txt";

// todo
final String actual = "";
URL url = getClass().getClassLoader().getResource(fileName);
final String actual = url.getFile();


assertThat(actual).endsWith(fileName);
}
Expand All @@ -40,14 +50,15 @@ class FileTest {
* File, Files 클래스를 사용하여 파일의 내용을 읽어보자.
*/
@Test
void 파일의_내용을_읽는다() {
void 파일의_내용을_읽는다() throws IOException, URISyntaxException {
final String fileName = "nextstep.txt";

// todo
final Path path = null;
URL url = getClass().getClassLoader().getResource(fileName);
final Path path = Path.of(url.toURI());

// todo
final List<String> actual = Collections.emptyList();
final List<String> actual = Files.readAllLines(path);

assertThat(actual).containsOnly("nextstep");
}
Expand Down
31 changes: 21 additions & 10 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ class OutputStream_학습_테스트 {
final byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
final OutputStream outputStream = new ByteArrayOutputStream(bytes.length);

/**
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/
for(byte b : bytes){
outputStream.write(b);
}

final String actual = outputStream.toString();

Expand All @@ -78,6 +77,7 @@ class OutputStream_학습_테스트 {
* flush를 사용해서 테스트를 통과시킨다.
* ByteArrayOutputStream과 어떤 차이가 있을까?
*/
outputStream.flush();

verify(outputStream, atLeastOnce()).flush();
outputStream.close();
Expand All @@ -96,6 +96,8 @@ class OutputStream_학습_테스트 {
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/
try(outputStream){
}

verify(outputStream, atLeastOnce()).close();
}
Expand Down Expand Up @@ -128,7 +130,9 @@ class InputStream_학습_테스트 {
* todo
* inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까?
*/
final String actual = "";

final String actual = new String(inputStream.readAllBytes());


assertThat(actual).isEqualTo("🤩");
assertThat(inputStream.read()).isEqualTo(-1);
Expand All @@ -148,6 +152,7 @@ class InputStream_학습_테스트 {
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/
try(inputStream){}

verify(inputStream, atLeastOnce()).close();
}
Expand All @@ -169,12 +174,12 @@ class FilterStream_학습_테스트 {
* 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까?
*/
@Test
void 필터인_BufferedInputStream_사용해보자() {
void 필터인_BufferedInputStream_사용해보자() throws IOException {
final String text = "필터에 연결해보자.";
final InputStream inputStream = new ByteArrayInputStream(text.getBytes());
final InputStream bufferedInputStream = null;
final InputStream bufferedInputStream = new BufferedInputStream(inputStream);

final byte[] actual = new byte[0];
final byte[] actual = bufferedInputStream.readAllBytes();

assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class);
assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes());
Expand All @@ -197,15 +202,21 @@ class InputStreamReader_학습_테스트 {
* 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다.
*/
@Test
void BufferedReader_사용하여_문자열을_읽어온다() {
void BufferedReader_사용하여_문자열을_읽어온다() throws IOException {
final String emoji = String.join("\r\n",
"😀😃😄😁😆😅😂🤣🥲☺️😊",
"😇🙂🙃😉😌😍🥰😘😗😙😚",
"😋😛😝😜🤪🤨🧐🤓😎🥸🤩",
"");
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());

final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
final StringBuilder actual = new StringBuilder();
int size = emoji.split("\r\n").length;
for(int i = 0 ; i < size ; i ++){
actual.append(bufferedReader.readLine());
actual.append("\r\n");
}

assertThat(actual).hasToString(emoji);
}
Expand Down
68 changes: 68 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/LoginController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package nextstep.jwp.controller;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import nextstep.jwp.db.InMemorySession;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.exception.UnauthorizedException;
import nextstep.jwp.model.AuthUser;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.request.Request;
import org.apache.coyote.http11.response.HttpStatus;
import org.apache.coyote.http11.response.Response;
import org.apache.coyote.http11.servlet.Servlet;

public class LoginController {

public static Response login(Request request){
Map<String, String> body = request.getBody();
AuthUser authUser = AuthUser.from(body);
User user = InMemoryUserRepository.findByAccount(authUser.getAccount()).orElseThrow(()->new UnauthorizedException("해당 유저가 없습니다."));
if(!user.checkPassword(authUser.getPassword())){
throw new UnauthorizedException("아이디 및 패스워드가 틀렸습니다.");
}
String jSessionId = InMemorySession.login(user);
Map<String,String> cookie = new HashMap<>();
if(!request.getCookie().containsKey("JSESSIONID")){
cookie.put("JSESSIONID",jSessionId);
}
return Response.builder()
.status(HttpStatus.FOUND)
.contentType("html")
.cookie(cookie)
.location("index.html")
.responseBody(getFile("index.html"))
.build();
}

public static Response signUp(Request request){
Map<String, String> requestBody = request.getBody();
final String account = requestBody.get("account");
final String password = requestBody.get("password");
final String email = requestBody.get("email");
User user = new User(account,password,email);
InMemoryUserRepository.save(user);
return Response.builder()
.status(HttpStatus.FOUND)
.contentType("html")
.location("index.html")
.responseBody(getFile("index.html"))
.build();
}


private static String getFile(String fileName){
try {
final var fileUrl = Servlet.class.getClassLoader().getResource("static/" + fileName);
final var fileBytes = Files.readAllBytes(new File(fileUrl.getFile()).toPath());
return new String(fileBytes);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NullPointerException e){
return "";
}
}
}
48 changes: 48 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/ViewController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package nextstep.jwp.controller;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.apache.coyote.http11.request.Request;
import org.apache.coyote.http11.response.HttpStatus;
import org.apache.coyote.http11.response.Response;
import org.apache.coyote.http11.servlet.Servlet;

public class ViewController {
public static Response getLogin(Request request){
return Response.builder()
.status(HttpStatus.OK)
.contentType("html")
.responseBody(getFile("login.html"))
.build();
}

public static Response getRegister(Request request){
return Response.builder()
.status(HttpStatus.OK)
.contentType("html")
.responseBody(getFile("register.html"))
.build();
}

public static Response getVoid(Request request){
return Response.builder()
.status(HttpStatus.OK)
.responseBody("Hello world!")
.contentType("html")
.build();
}


private static String getFile(String fileName){
try {
final var fileUrl = Servlet.class.getClassLoader().getResource("static/" + fileName);
final var fileBytes = Files.readAllBytes(new File(fileUrl.getFile()).toPath());
return new String(fileBytes);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NullPointerException e){
return "";
}
}
}
25 changes: 25 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/db/InMemorySession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package nextstep.jwp.db;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import nextstep.jwp.exception.UnauthorizedException;
import nextstep.jwp.model.User;

public class InMemorySession {
private static final Map<User, UUID> session = new HashMap<>();

public static String login(User user){
UUID uuid = UUID.randomUUID();
session.put(user,uuid);
return uuid.toString();
}
public static boolean isLogin(String id){
for(UUID uuid: session.values()){
if(uuid.toString().equals(id)){
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nextstep.jwp.db;

import java.util.concurrent.atomic.AtomicLong;
import nextstep.jwp.model.User;

import java.util.Map;
Expand All @@ -9,14 +10,14 @@
public class InMemoryUserRepository {

private static final Map<String, User> database = new ConcurrentHashMap<>();

private static final AtomicLong autoIncrementId = new AtomicLong(1L);
static {
final User user = new User(1L, "gugu", "password", "[email protected]");
final User user = new User(autoIncrementId.get(), "gugu", "password", "[email protected]");
database.put(user.getAccount(), user);
}

public static void save(User user) {
database.put(user.getAccount(), user);
database.put(user.getAccount(), user.putId(autoIncrementId.addAndGet(1L)));
}

public static Optional<User> findByAccount(String account) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nextstep.jwp.exception;

public class UnauthorizedException extends RuntimeException{
public UnauthorizedException(String message) {
super(message);
}
}
25 changes: 25 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/model/AuthUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package nextstep.jwp.model;

import java.util.Map;

public class AuthUser {
private final String account;
private final String password;

public AuthUser(String account, String password) {
this.account = account;
this.password = password;
}

public static AuthUser from(Map<String, String> query){
return new AuthUser(query.get("account"),query.get("password"));
}

public String getAccount() {
return account;
}

public String getPassword() {
return password;
}
}
Loading

0 comments on commit 841e70a

Please sign in to comment.