Skip to content

Commit

Permalink
refactor: 컨트롤러 매핑 RequestMapping으로 분리, TargetPath 객체 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
0chil committed Sep 13, 2023
1 parent 5fdd9e8 commit 5092d78
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 81 deletions.
23 changes: 23 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/handler/HelloController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nextstep.jwp.handler;

import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;
import org.apache.coyote.http11.TargetPath;
import org.apache.coyote.http11.controller.Controller;
import org.apache.coyote.http11.header.ContentType;

public class HelloController implements Controller {

private static final TargetPath SUPPORTED_PATH = new TargetPath("/");

@Override
public boolean supports(HttpRequest httpRequest) {
return httpRequest.getTarget().getPath().equals(SUPPORTED_PATH) && httpRequest.getMethod().isGet();
}

@Override
public void handle(HttpRequest httpRequest, HttpResponse httpResponse) {
httpResponse.setContentType(new ContentType("text/html"));
httpResponse.setBody("Hello world!");
}
}
14 changes: 13 additions & 1 deletion tomcat/src/main/java/nextstep/jwp/handler/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import nextstep.jwp.model.User;
import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;
import org.apache.coyote.http11.TargetPath;
import org.apache.coyote.http11.body.FormData;
import org.apache.coyote.http11.controller.FileController;
import org.apache.coyote.http11.controller.GetAndPostController;
Expand All @@ -16,12 +17,23 @@
public class LoginController extends GetAndPostController {

private static final String SESSION_KEY = "JSESSIONID";
private static final String SESSION_USER_KEY = "user";

private static final String UNAUTHORIZED_LOCATION = "/401";
private static final String MAIN_LOCATION = "/index";
private static final String SESSION_USER_KEY = "user";

private static final TargetPath SUPPORTED_PATH = new TargetPath("/login").autoComplete();

private final FileController fileHandler = new FileController();

@Override
public boolean supports(HttpRequest httpRequest) {
if (httpRequest.getMethod().isGet() || httpRequest.getMethod().isPost()) {
return httpRequest.getTarget().getPath().autoComplete().equals(SUPPORTED_PATH);
}
return false;
}

@Override
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) {
boolean isSignedIn = httpRequest.getSession(SESSION_KEY)
Expand Down
11 changes: 11 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/handler/RegisterController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@
import nextstep.jwp.model.User;
import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;
import org.apache.coyote.http11.TargetPath;
import org.apache.coyote.http11.body.FormData;
import org.apache.coyote.http11.controller.FileController;
import org.apache.coyote.http11.controller.GetAndPostController;

public class RegisterController extends GetAndPostController {

private static final String MAIN_LOCATION = "/index";
private static final TargetPath SUPPORTED_PATH = new TargetPath("/register").autoComplete();

private final FileController fileHandler = new FileController();

@Override
public boolean supports(HttpRequest httpRequest) {
if (httpRequest.getMethod().isGet() || httpRequest.getMethod().isPost()) {
return httpRequest.getTarget().getPath().autoComplete().equals(SUPPORTED_PATH);
}
return false;
}

@Override
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) {
fileHandler.handle(httpRequest, httpResponse);
Expand Down
34 changes: 10 additions & 24 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
package org.apache.coyote.http11;

import nextstep.jwp.exception.UncheckedServletException;
import nextstep.jwp.handler.HelloController;
import nextstep.jwp.handler.LoginController;
import nextstep.jwp.handler.RegisterController;
import org.apache.coyote.Processor;
import org.apache.coyote.http11.controller.Controller;
import org.apache.coyote.http11.controller.FileController;
import org.apache.coyote.http11.controller.RequestMapping;
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.util.Map;
import java.util.List;

public class Http11Processor implements Runnable, Processor {

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

private static final Controller DEFAULT_CONTROLLER = new FileController();
private static final LoginController LOGIN_CONTROLLER = new LoginController();
private static final RegisterController REGISTER_CONTROLLER = new RegisterController();
private static final Map<String, Controller> PREDEFINED_CONTROLLERS = Map.of(
"/", (request, response) -> response.setBody("Hello world!"),
"/login", LOGIN_CONTROLLER,
"/login.html", LOGIN_CONTROLLER,
"/register", REGISTER_CONTROLLER,
"/register.html", REGISTER_CONTROLLER
);
private static final String WHITE_SPACE = " ";

private final Socket connection;
private final RequestMapping requestMapping = new RequestMapping(List.of(
new HelloController(),
new LoginController(),
new FileController(),
new RegisterController()
));

public Http11Processor(final Socket connection) {
this.connection = connection;
Expand All @@ -50,21 +45,12 @@ public void process(final Socket connection) {

HttpRequest httpRequest = HttpRequest.from(bufferedReader);
HttpResponse httpResponse = new HttpResponse();
handle(httpRequest, httpResponse);
requestMapping.handle(httpRequest, httpResponse);

outputStream.write(httpResponse.toLine().getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
log.error(e.getMessage(), e);
}
}

private void handle(final HttpRequest httpRequest, final HttpResponse httpResponse) {
if (PREDEFINED_CONTROLLERS.containsKey(httpRequest.getTarget().getPath())) {
PREDEFINED_CONTROLLERS.get(httpRequest.getTarget().getPath())
.handle(httpRequest, httpResponse);
return;
}
DEFAULT_CONTROLLER.handle(httpRequest, httpResponse);
}
}
6 changes: 3 additions & 3 deletions tomcat/src/main/java/org/apache/coyote/http11/HttpTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ public class HttpTarget {

private static final String EMPTY_VALUE = "";

private final String path;
private final TargetPath path;
private final Map<String, String> queries;

public HttpTarget(final String target) {
this.path = removeQueriesFrom(target);
this.path = new TargetPath(removeQueriesFrom(target));
this.queries = getQueriesFrom(target);
}

Expand All @@ -40,7 +40,7 @@ private static String removeQueriesFrom(final String target) {
return target.substring(0, queryStringStart);
}

public String getPath() {
public TargetPath getPath() {
return path;
}

Expand Down
75 changes: 75 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/http11/TargetPath.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.apache.coyote.http11;

import java.io.File;
import java.net.URL;
import java.util.Objects;

public class TargetPath {

private static final String DEFAULT_PAGE = "index";
private static final String DEFAULT_EXTENSION = ".html";
private static final String STATIC_RESOURCES_PATH = "static";

private final String path;

public TargetPath(String path) {
this.path = path;
}

private boolean endsWithRoot(String path) {
return path.endsWith("/");
}

private boolean hasExtensionIn(String path) {
int lastSlash = path.lastIndexOf('/');
String lastSubPath = path.substring(lastSlash + 1);
if (lastSubPath.contains(".")) {
return lastSubPath.lastIndexOf('.') > 0;
}
return false;
}

public String getPath() {
return path;
}

public TargetPath autoComplete() {
if (endsWithRoot(path)) {
return new TargetPath(path + DEFAULT_PAGE + DEFAULT_EXTENSION);
}
if (!hasExtensionIn(path)) {
return new TargetPath(path + DEFAULT_EXTENSION);
}
return new TargetPath(path);
}

public String getExtension() {
int lastSlash = autoComplete().path.lastIndexOf('/');
String lastSubPath = autoComplete().path.substring(lastSlash + 1);
int extensionStart = lastSubPath.lastIndexOf('.');
return lastSubPath.substring(extensionStart + 1);
}

public File asStaticFile() {
URL resource = getClass().getClassLoader().getResource(STATIC_RESOURCES_PATH + autoComplete().path);
if (resource == null) {
return new File("/");
}
return new File(resource.getPath());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

TargetPath that = (TargetPath) o;

return Objects.equals(path, that.path);
}

@Override
public int hashCode() {
return path != null ? path.hashCode() : 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@

public interface Controller {

boolean supports(HttpRequest httpRequest);

void handle(HttpRequest httpRequest, HttpResponse httpResponse);
}
Original file line number Diff line number Diff line change
@@ -1,86 +1,52 @@
package org.apache.coyote.http11.controller;

import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;
import org.apache.coyote.http11.TargetPath;
import org.apache.coyote.http11.header.ContentType;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Map;
import java.util.Objects;
import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;
import org.apache.coyote.http11.header.ContentType;

public class FileController implements Controller {

private static final ClassLoader CLASS_LOADER = FileController.class.getClassLoader();

private static final String STATIC_RESOURCES_PATH = "static";
private static final String DEFAULT_INDEX = "index";
private static final String DEFAULT_EXTENSION = ".html";

private static final Map<String, ContentType> CONTENT_TYPES_BY_EXTENSION = Map.of(
"html", new ContentType("text/html"),
"css", new ContentType("text/css")
);

@Override
public boolean supports(final HttpRequest httpRequest) {
TargetPath targetPath = httpRequest.getTarget().getPath().autoComplete();
return targetPath.asStaticFile().exists();
}

@Override
public void handle(final HttpRequest httpRequest, final HttpResponse httpResponse) {
try {
String targetPath = fillExtensionIfDoesNotExist(httpRequest.getTarget().getPath());
TargetPath targetPath = httpRequest.getTarget().getPath().autoComplete();
File staticFile = targetPath.asStaticFile();
validatePresenceOf(staticFile);

File resource = getResourceFileFrom(targetPath);
String body = Files.readString(resource.toPath());
httpResponse.setContentType(getContentTypeFrom(targetPath));
httpResponse.setBody(body);
httpResponse.setContentType(getContentTypeBy(targetPath.getExtension()));
httpResponse.setBody(Files.readString(staticFile.toPath()));
} catch (IOException exception) {
throw new IllegalArgumentException("파일 읽기에 실패했습니다");
}
}

private String fillExtensionIfDoesNotExist(String target) {
if (Objects.isNull(getExtensionOf(target))) {
return target + DEFAULT_EXTENSION;
}
return target;
}

private ContentType getContentTypeFrom(final String target) {
String extension = getExtensionOf(target);
private ContentType getContentTypeBy(final String extension) {
if (CONTENT_TYPES_BY_EXTENSION.containsKey(extension)) {
return CONTENT_TYPES_BY_EXTENSION.get(extension);
}
return new ContentType("text/html");
}

private File getResourceFileFrom(final String target) {
String relativePath = getRelativePathFrom(target);
URL resource = CLASS_LOADER.getResource(relativePath);
validatePresenceOf(resource);

String absolutePath = resource.getPath();
return new File(absolutePath);
}

private String getRelativePathFrom(final String target) {
if (target.endsWith("/")) {
return STATIC_RESOURCES_PATH + target + DEFAULT_INDEX;
}
return STATIC_RESOURCES_PATH + target;
}

private void validatePresenceOf(final URL resource) {
if (Objects.isNull(resource)) {
private void validatePresenceOf(final File file) {
if (!file.exists()) {
throw new IllegalArgumentException("리소스를 찾지 못했습니다.");
}
}

private String getExtensionOf(String path) {
int lastSlash = path.lastIndexOf('/');
String lastSubPath = path.substring(lastSlash + 1);
if (lastSubPath.contains(".")) {
int extensionStart = lastSubPath.lastIndexOf('.');
return lastSubPath.substring(extensionStart + 1);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.apache.coyote.http11.controller;

import org.apache.coyote.http11.HttpRequest;
import org.apache.coyote.http11.HttpResponse;

import java.util.List;

public class RequestMapping {

private final List<Controller> controllers;

public RequestMapping(List<Controller> controllers) {
this.controllers = controllers;
}

public void handle(final HttpRequest httpRequest, final HttpResponse httpResponse) {
getControllerFor(httpRequest).handle(httpRequest, httpResponse);
}

private Controller getControllerFor(final HttpRequest httpRequest) {
return controllers.stream()
.filter(it -> it.supports(httpRequest))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("적절한 컨트롤러를 찾지 못했습니다"));
}
}
Loading

0 comments on commit 5092d78

Please sign in to comment.