Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/main/java/app/handler/RegisterHandlerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@
import exception.ServiceException;
import http.HttpMethod;
import http.request.HttpRequest;
import web.dispatch.argument.QueryParameters;
import web.handler.SingleArgHandler;
import web.response.HandlerResponse;
import web.response.StaticViewResponse;

public class RegisterHandlerImpl extends SingleArgHandler<HttpRequest> {
public class RegisterHandlerImpl extends SingleArgHandler<QueryParameters> {
public RegisterHandlerImpl() {
super(HttpMethod.GET,
"/create");
}

@Override
public HandlerResponse handle(HttpRequest request) {
String userId = request.getQueryValue("userId").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "userId required"));
String password = request.getQueryValue("password").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "password required"));
String name = request.getQueryValue("name").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "name required"));
String email = request.getQueryValue("email").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "email required"));
public HandlerResponse handle(QueryParameters params) {
String userId = params.getQueryValue("userId").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "userId required"));
String password = params.getQueryValue("password").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "password required"));
String name = params.getQueryValue("name").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "name required"));
String email = params.getQueryValue("email").orElseThrow(()-> new ServiceException(ErrorCode.MISSING_REGISTER_TOKEN, "email required"));
Database.addUser(new User(userId, password, name, email));
return StaticViewResponse.of("/login");
}
Expand Down
30 changes: 17 additions & 13 deletions src/main/java/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
import exception.handler.ErrorExceptionHandler;
import exception.handler.ServiceExceptionHandler;
import exception.handler.UnhandledErrorHandler;
import http.request.HttpBufferedReaderRequestConverter;
import http.request.BufferedReaderHttpRequestConverter;
import http.request.HttpRequestConverter;
import http.response.HttpBufferedStreamResponseConverter;
import http.request.InputStreamHttpRequestConverter;
import http.response.HttpResponseBufferedStreamConverter;
import http.response.HttpResponseConverter;
import web.dispatch.ArgumentResolver;
import web.dispatch.argument.ArgumentResolver;
import web.dispatch.Dispatcher;
import web.dispatch.HandlerAdapter;
import web.dispatch.adapter.DefaultHandlerAdapter;
import web.dispatch.adapter.SingleArgHandlerAdapter;
import web.dispatch.argument.HttpRequestResolver;
import web.dispatch.argument.QueryParamsResolver;
import web.dispatch.argument.resolver.HttpRequestResolver;
import web.dispatch.argument.resolver.QueryParamsResolver;
import web.handler.StaticContentHandler;
import web.handler.WebHandler;
import web.renderer.StaticViewRenderer;
Expand All @@ -25,19 +26,22 @@

public class AppConfig {
//Http
public HttpBufferedReaderRequestConverter httpBufferedReaderRequestConverter(){
return new HttpBufferedReaderRequestConverter();
public HttpRequestConverter httpRequestConverter(){
return inputStreamHttpRequestConverter();
}
public HttpResponseConverter httpResponseConverter(){
return httpResponseBufferedStreamConverter();
}

public HttpBufferedStreamResponseConverter httpBufferedStreamResponseConverter(){
return new HttpBufferedStreamResponseConverter();
public BufferedReaderHttpRequestConverter httpBufferedReaderRequestConverter(){
return new BufferedReaderHttpRequestConverter();
}

public HttpRequestConverter httpRequestConverter(){
return httpBufferedReaderRequestConverter();
public HttpResponseBufferedStreamConverter httpResponseBufferedStreamConverter(){
return new HttpResponseBufferedStreamConverter();
}
public HttpResponseConverter httpResponseConverter(){
return httpBufferedStreamResponseConverter();
public InputStreamHttpRequestConverter inputStreamHttpRequestConverter(){
return new InputStreamHttpRequestConverter();
}


Expand Down
42 changes: 42 additions & 0 deletions src/main/java/http/request/BufferedReaderHttpRequestConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package http.request;

import exception.ErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.Socket;

public class BufferedReaderHttpRequestConverter implements HttpRequestConverter {
private static final Logger logger = LoggerFactory.getLogger(BufferedReaderHttpRequestConverter.class);
public HttpRequest parseRequest(Socket connection){

try {
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(
connection.getInputStream()));

HttpRequest request = HttpRequest.from(bufferedReader.readLine());
request.setRequestAddress(connection.getInetAddress());

setHeaders(bufferedReader, request);

//TODO: Body 파싱 추가

return request;

} catch (IOException e) {
throw new ErrorException("HttpRequestParseError: IO Exception", e);
}
}

private void setHeaders(BufferedReader bufferedReader, HttpRequest request) throws IOException {
String line;
while ((line = bufferedReader.readLine())!= null) {
if(line.isEmpty()) break;
int idx = line.indexOf(':');
if(idx<0) throw new ErrorException("HttpRequestHeaderParseError");
request.setHeader(line.substring(0, idx).strip(), line.substring(idx+1).strip());
}
Comment on lines +33 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일관되지 않은 에러 처리

BufferedReaderHttpRequestConverter는 body 파싱을 구현하지 않았으면서 InputStreamHttpRequestConverter는 전체 body를 파싱합니다. 두 converter 중 어느 것을 실제 사용하는지(AppConfig에서 inputStreamHttpRequestConverter()로 설정) 명확하지 않으면 향후 유지보수 시 혼동이 발생할 수 있습니다.

BufferedReaderHttpRequestConverter는 더 이상 필요 없다면 제거하거나, 명확한 용도 구분을 문서화하세요."

}
}
44 changes: 0 additions & 44 deletions src/main/java/http/request/HttpBufferedReaderRequestConverter.java

This file was deleted.

83 changes: 25 additions & 58 deletions src/main/java/http/request/HttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,47 @@
import exception.ServiceException;
import http.HttpMethod;

import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class HttpRequest {
private final HttpMethod method;
private final Map<String, String> headers;
private final URI uri;
private final Map<String, String> headers; //TODO: Map<String, List<String>> 타입으로 변경
private String httpVersion;
private final URI uri;
private String contentType;

//TODO: Map<String, List<String>> 타입으로 변경
private Map<String, String> queryMap;

private byte[] body;

private InetAddress requestAddress;

private HttpRequest (HttpMethod method,
String target,
String httpVersion) {
this.method = method;
this.uri = URI.create(target);
this.httpVersion = httpVersion;
this.headers = new HashMap<>();
}

public byte[] getBody() {
return body;
}

public String getContentType() {
return contentType;
}
public String getHeader(String key){
return headers.get(key.toLowerCase());
}

public void setHeader(String key, String value){
Comment on lines +39 to 44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Content-Type 설정 로직의 일관성 문제

setHeader에서만 Content-Type을 별도로 저장하므로, 만약 나중에 헤더를 직접 수정하면 contentType 필드와 실제 헤더 값이 불일치할 수 있습니다.

개선 방안:
getter에서 매번 headers에서 읽거나, 모든 헤더 수정을 통제하는 방식으로 통일하는 것을 권장합니다."

headers.put(key.toLowerCase(), value);
if (key.equalsIgnoreCase("Content-Type")) {
this.contentType = value;
}
}

public List<String> getHeaders(){
Expand All @@ -46,31 +60,10 @@ public String getQueryString(){
return uri.getQuery();
}

public List<String> getQueryKeys(){
return queryMap.keySet().stream().toList();
}

public Optional<String> getQueryValue(String key){
if (queryMap == null) {
queryMap = parseQueryToMap(uri.getQuery());
}
return Optional.ofNullable(queryMap.get(key));
}

public HttpMethod getMethod(){
return this.method;
}

private HttpRequest (HttpMethod method,
String target,
String httpVersion) {
this.method = method;
this.uri = URI.create(target);
this.httpVersion = httpVersion;
this.headers = new HashMap<>();

}

public void setRequestAddress(InetAddress requestAddress) {
this.requestAddress = requestAddress;
}
Expand All @@ -93,33 +86,7 @@ public static HttpRequest from(String requestLine){
}
}


private Map<String, String> parseQueryToMap(String queryString) {
Map<String, String> map = new HashMap<>();
if (queryString == null || queryString.isBlank()) {
return map;
}

String[] pairs = queryString.strip().split("&");
for (String pair : pairs) {
if (pair.isEmpty()) continue;

String[] kv = pair.split("=", 2); // value에 '=' 들어가도 OK
String rawKey = kv[0];
String rawValue = kv.length == 2 ? kv[1] : "";

String key = urlDecode(rawKey);
String value = urlDecode(rawValue);
map.put(key, value);
}
return map;
}

private String urlDecode(String s) {
try {
return URLDecoder.decode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported", e);
}
public void setBody(byte[] body){
this.body = body;
}
}
Loading