본문 바로가기

java

[Java] http 요청과 응답 구현하기 - 2(apache tomcat의 http 처리)

개요

지난 글에서 library를 이용하지 않고 Socket을 이용해 'http' 프로토콜 요청을 구현해보았다. 이 때 apache tomcat 기반의 타겟 서버가 http message 규칙에 맞는 요청을 적절히 처리하여 응답한 것을 확인할 수 있었다. 그렇다면 apache tomcat은 어떻게 http 요청을 해석할 수 있었을까?

 

http 응답을 위한 call stack 뜯어보기

yourkit 이라는 툴을 활용해 health check 서버가 SocketClient의 요청을 어떻게 처리했는지 콜스택을 살펴보자. call stack 특성상 가장 하위의 메서드부터 실행되어 응답되는 구조다.

Thread가 실행되고, NIO를 통해 socket 연결 수립 후 실행된다. 이후 AbstractProtocl의 static inner class인 ConnectionHandler가 처리를 하면서 어떤 Protocol을 사용해야 하는지 선택한다.

public abstract class AbstractProtocol<S> {
    //...
    
    protected static class ConnectionHandler<S> {
    	public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
        	//...
            if (processor == null) {
            	processor = getProtocol().createProcessor();
            }
        }
    }
}

그런데 getProtocol.createProcessor()를 들어가보면, 기본 Processor가 Http11Processor로 초기화되어 있다. NioEndpoint.Poller 클래스가 프로토콜 처리기(ProtocolHandler)를 통해 다양한 프로토콜을 처리하도록 지원하고 있지만, apache tomcat이 기본적으로 http 프로토콜을 다루는 서버라 default로 설정되어 있는 것이다.

이후 초기화되어 있는 Http11Processor.service() 메서드 내에서 prepareRequestProtocol() 메서드를 호출하여 client의 output stream으로 보내진 데이터를 받아 http message 규칙에 맞게 parsing하여 'RequestInfo' 인스턴스에 저장한다.

public class Http11Processor extends AbstractProcessor {
    //...

    @Override
    public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        
        // Process the Protocol component of the request line
        // Need to know if this is an HTTP 0.9 request before trying to
        // parse headers.
        prepareRequestProtocol();
    
    }
}

 

RequestInfo 객체는 Request 객체를 final instance variable로 갖고 있는데, Request 객체가 갖고 있는 필드는 다음과 같다.

public class RequestInfo {
    // Instance Variables
    private final Request req;
    
}

public final class Request {
    private int serverPort = -1;
    private final MessageBytes serverNameMB = MessageBytes.newInstance();

    private int remotePort;
    private int localPort;
    
    //...
}

 

마치며 - apache 웹서버의 요청 프로토콜 전처리는 완료되었다. 실제 Spring 로직은 어떻게 수행되는가?

client가 보내온 http 요청 정보를 Request 정보에 담고, 이후 call stack을 보면 ServletRequest/ServletResponse 처리까지 하는 것을 볼 수 있다. 이후 org.springframework.web 패키지에서 로직을 처리하기 시작한다. 다음 글에서는 Spring이 http 요청을 어떻게 처리하고 있는지 알아보고자 한다.