-
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
[톰캣 구현하기 1, 2단계] 애쉬(정설희) 미션 제출합니다. #338
Changes from all commits
ace4a6f
d52fc20
e0e14af
2cd41e2
323f043
d2ede1b
f436723
8a4dbf8
688b5b2
7679807
c6f0672
8148e2d
cdfbf0d
4cc32ec
7b2e322
e1d25ef
dd7b3cc
817fa61
7c7b720
7776940
3ac5300
57d1cd9
0109872
f2d5a75
1c30838
f17aa67
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 |
---|---|---|
@@ -1,11 +1,28 @@ | ||
package nextstep; | ||
|
||
import java.util.Set; | ||
|
||
import org.apache.catalina.HandlerMapping; | ||
import org.apache.catalina.startup.Tomcat; | ||
|
||
import nextstep.jwp.handler.BaseRequestHandler; | ||
import nextstep.jwp.handler.LoginRequestHandler; | ||
import nextstep.jwp.handler.RegisterRequestHandler; | ||
import nextstep.jwp.handler.StaticContentRequestHandler; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
final var tomcat = new Tomcat(); | ||
tomcat.start(); | ||
} | ||
public static void main(String[] args) { | ||
|
||
final var handlers = Set.of( | ||
new BaseRequestHandler(), | ||
new LoginRequestHandler(), | ||
new RegisterRequestHandler() | ||
); | ||
final var defaultHandler = new StaticContentRequestHandler(); | ||
final var handlerMapping = new HandlerMapping(handlers, defaultHandler); | ||
|
||
final var tomcat = new Tomcat(handlerMapping); | ||
tomcat.start(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,30 @@ | ||
package nextstep.jwp.db; | ||
|
||
import nextstep.jwp.model.User; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
import nextstep.jwp.model.User; | ||
|
||
public class InMemoryUserRepository { | ||
|
||
private static final Map<String, User> database = new ConcurrentHashMap<>(); | ||
private static final Map<String, User> database = new ConcurrentHashMap<>(); | ||
private static final AtomicLong idCounter = new AtomicLong(); | ||
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. AtomicLong 배워갑니다 😯😯 |
||
|
||
static { | ||
final User user = new User(1L, "gugu", "password", "[email protected]"); | ||
database.put(user.getAccount(), user); | ||
} | ||
static { | ||
final User user = new User(idCounter.incrementAndGet(), "gugu", "password", "[email protected]"); | ||
database.put(user.getAccount(), user); | ||
} | ||
|
||
public static void save(User user) { | ||
database.put(user.getAccount(), user); | ||
} | ||
private InMemoryUserRepository() { | ||
} | ||
|
||
public static Optional<User> findByAccount(String account) { | ||
return Optional.ofNullable(database.get(account)); | ||
} | ||
public static void save(User user) { | ||
database.put(user.getAccount(), user.addId(idCounter.incrementAndGet())); | ||
} | ||
|
||
private InMemoryUserRepository() {} | ||
public static Optional<User> findByAccount(String account) { | ||
return Optional.ofNullable(database.get(account)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import org.apache.catalina.RequestHandler; | ||
import org.apache.coyote.MimeType; | ||
import org.apache.coyote.request.Request; | ||
import org.apache.coyote.response.Response; | ||
|
||
public class BaseRequestHandler extends RequestHandler { | ||
|
||
private static final String REQUEST_PATH = "/"; | ||
private static final String RESPONSE_BODY = "Hello world!"; | ||
|
||
public BaseRequestHandler() { | ||
super(REQUEST_PATH); | ||
} | ||
|
||
@Override | ||
protected Response doGet(final Request request) { | ||
return Response.ok(RESPONSE_BODY, MimeType.HTML); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import org.apache.catalina.RequestHandler; | ||
import org.apache.catalina.session.Session; | ||
import org.apache.catalina.session.SessionManager; | ||
import org.apache.coyote.Cookie; | ||
import org.apache.coyote.MimeType; | ||
import org.apache.coyote.request.Request; | ||
import org.apache.coyote.response.Response; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.model.User; | ||
|
||
public class LoginRequestHandler extends RequestHandler { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(LoginRequestHandler.class); | ||
|
||
private static final String REQUEST_PATH = "/login"; | ||
private static final String LOGIN_PAGE_PATH = "/login.html"; | ||
private static final String REDIRECT_LOCATION = "/index.html"; | ||
Comment on lines
+20
to
+22
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. url은 상수 대신 직접 쓰는 게 읽기 편하다고 생각하는 편이긴 한데, |
||
|
||
public LoginRequestHandler() { | ||
super(LoginRequestHandler.REQUEST_PATH); | ||
} | ||
|
||
@Override | ||
protected Response doGet(final Request request) { | ||
if (isSessionExist(request)) { | ||
return Response.redirect(REDIRECT_LOCATION); | ||
} | ||
return Response.ok(ResourceProvider.provide(LOGIN_PAGE_PATH), MimeType.fromPath(LOGIN_PAGE_PATH)); | ||
} | ||
|
||
private boolean isSessionExist(final Request request) { | ||
final var sessionId = request.findSession(); | ||
if (sessionId == null) { | ||
return false; | ||
} | ||
return SessionManager.findById(sessionId) != null; | ||
} | ||
|
||
@Override | ||
protected Response doPost(final Request request) { | ||
final var account = request.findBodyField("account"); | ||
final var password = request.findBodyField("password"); | ||
return login(account, password); | ||
} | ||
|
||
private Response login(final String account, final String password) { | ||
if (isInvalidInput(account, password)) { | ||
return Response.badRequest(); | ||
} | ||
|
||
final var user = InMemoryUserRepository.findByAccount(account); | ||
if (user.isEmpty() || !user.get().checkPassword(password)) { | ||
return Response.unauthorized(); | ||
} | ||
|
||
final var session = createSession(user.get()); | ||
final var cookie = Cookie.session(session.getId()); | ||
|
||
log.info("[LOGIN SUCCESS] account: {}", account); | ||
return Response.redirect(REDIRECT_LOCATION) | ||
.addCookie(cookie); | ||
} | ||
|
||
private boolean isInvalidInput(final String account, final String password) { | ||
return isBlank(account) || isBlank(password); | ||
} | ||
|
||
private boolean isBlank(final String value) { | ||
return value == null || value.isBlank(); | ||
} | ||
|
||
private Session createSession(final User user) { | ||
final var session = Session.create(); | ||
session.setAttribute("user", user); | ||
SessionManager.add(session); | ||
return session; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import org.apache.catalina.RequestHandler; | ||
import org.apache.coyote.MimeType; | ||
import org.apache.coyote.request.Request; | ||
import org.apache.coyote.response.Response; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.model.User; | ||
|
||
public class RegisterRequestHandler extends RequestHandler { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(RegisterRequestHandler.class); | ||
|
||
private static final String REQUEST_PATH = "/register"; | ||
private static final String PAGE_PATH = "/register.html"; | ||
|
||
public RegisterRequestHandler() { | ||
super(REQUEST_PATH); | ||
} | ||
|
||
@Override | ||
protected Response doGet(final Request request) { | ||
return Response.ok(ResourceProvider.provide(PAGE_PATH), MimeType.fromPath(PAGE_PATH)); | ||
} | ||
|
||
@Override | ||
protected Response doPost(final Request request) { | ||
final var account = request.findBodyField("account"); | ||
final var password = request.findBodyField("password"); | ||
final var email = request.findBodyField("email"); | ||
|
||
return register(account, password, email); | ||
} | ||
|
||
private Response register(final String account, final String password, final String email) { | ||
if (isInvalidInput(account, password, email) || isDuplicatedAccount(account)) { | ||
return Response.badRequest(); | ||
} | ||
|
||
InMemoryUserRepository.save(new User(account, password, email)); | ||
log.info("[REGISTER SUCCESS] account: {}", account); | ||
return Response.redirect("/index.html"); | ||
} | ||
|
||
private boolean isInvalidInput(final String account, final String password, final String email) { | ||
return isBlank(account) || isBlank(password) || isBlank(email); | ||
} | ||
|
||
private boolean isBlank(final String value) { | ||
return value == null || value.isBlank(); | ||
} | ||
|
||
private boolean isDuplicatedAccount(final String account) { | ||
return InMemoryUserRepository.findByAccount(account).isPresent(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
|
||
public class ResourceProvider { | ||
|
||
private static final String RESOURCE_ROOT_PATH = "static"; | ||
|
||
public static String provide(final String path) { | ||
final var resource = ResourceProvider.class.getClassLoader() | ||
.getResource(RESOURCE_ROOT_PATH + path); | ||
|
||
if (resource == null) { | ||
throw new IllegalArgumentException("해당하는 자원을 찾을 수 없습니다."); | ||
} | ||
|
||
final var file = new File(resource.getPath()).toPath(); | ||
|
||
if (Files.isRegularFile(file)) { | ||
try { | ||
return new String(Files.readAllBytes(file)); | ||
} catch (IOException e) { | ||
throw new IllegalArgumentException("파일을 읽을 수 없습니다."); | ||
} | ||
} | ||
|
||
throw new IllegalArgumentException("올바르지 않은 파일입니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import org.apache.catalina.RequestHandler; | ||
import org.apache.coyote.MimeType; | ||
import org.apache.coyote.request.Request; | ||
import org.apache.coyote.response.Response; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class StaticContentRequestHandler extends RequestHandler { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(StaticContentRequestHandler.class); | ||
private static final String REQUEST_PATH = "/*"; | ||
|
||
public StaticContentRequestHandler() { | ||
super(REQUEST_PATH); | ||
} | ||
|
||
@Override | ||
public Response handle(final Request request) { | ||
final String requestPath = request.getPath(); | ||
if (requestPath == null) { | ||
return Response.notFound(); | ||
} | ||
try { | ||
final var responseBody = ResourceProvider.provide(requestPath); | ||
final var mimeType = MimeType.fromPath(requestPath); | ||
return Response.ok(responseBody, mimeType); | ||
} catch (IllegalArgumentException e) { | ||
log.warn("{}: {}", request.getPath(), e.getMessage()); | ||
return Response.notFound(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.apache.catalina; | ||
|
||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apache.coyote.request.Request; | ||
import org.apache.coyote.response.Response; | ||
|
||
public class HandlerMapping { | ||
|
||
private final Map<String, RequestHandler> handlers; | ||
private final RequestHandler defaultHandler; | ||
|
||
public HandlerMapping(final Set<RequestHandler> handlers, final RequestHandler defaultHandler) { | ||
this.handlers = handlers.stream() | ||
.collect(Collectors.toMap( | ||
RequestHandler::getRequestPath, | ||
handler -> handler | ||
)); | ||
this.defaultHandler = defaultHandler; | ||
} | ||
Comment on lines
+15
to
+22
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. 관련 코멘트 에 답변 달았습니다 ! |
||
|
||
public Response handle(final Request request) { | ||
final String requestPath = request.getPath(); | ||
if (requestPath == null) { | ||
return Response.notFound(); | ||
} | ||
final var handler = handlers.get(requestPath); | ||
if (handler == null) { | ||
return defaultHandler.handle(request); | ||
} | ||
return handler.handle(request); | ||
} | ||
} |
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.
어플리케이션 실행할 때 주입해주는 거 너무 좋은데요 ..
애쉬 코드 모두 훔치고 싶어지네요 🥹