JEP 408: Simple Web Server

JEP 408: Simple Web Server

Summary

정적 파일만 제공하는 최소 웹 서버를 시작하는 명령줄 도구를 제공합니다.
CGI 또는 서블릿과 유사한 기능을 사용할 수 없습니다. 이 도구는 특히 교육적 맥락에서 프로토타이핑, 임시 코딩 및 테스트 목적에 유용합니다.

Goals

  • 간편한 설정과 최소한의 기능으로 즉시 사용 가능한 정적 HTTP 파일 서버를 제공합니다.
  • 개발자 활성화 에너지를 줄이고 JDK에 더 쉽게 접근할 수 있도록 합니다.
  • 프로그래밍 방식 생성 및 사용자 지정을 위한 작은 API와 함께 명령줄을 통해 기본 구현을 제공합니다.

Non-Goals

  • 기능이 풍부하거나 상용 등급의 서버를 제공하는 것이 목표가 아닙니다.
    훨씬 더 나은 대안이 서버 프레임워크(예: Jetty, Netty, Grizzly) 및 프로덕션 서버(예: Apache Tomcat, Apache httpd 및 NGINX)의 형태로 존재합니다.
    이러한 본격적인 성능 최적화 기술은 구성하는 데 많은 노력이 필요하며, 이는 정확히 우리가 피하고 싶은 것입니다.

  • 인증, 액세스 제어 또는 암호화와 같은 보안 기능을 제공하는 것이 목표가 아닙니다. 서버는 테스트, 개발 및 디버깅 전용입니다.
    따라서 모든 기능을 갖춘 서버 응용 프로그램과의 혼동을 피하기 위해 그 디자인은 명시적으로 최소화됩니다.

Motivation

개발자를 위한 일반적인 통과 의례는 “Hello, world!”와 같은 웹상의 파일을 제공하는 것입니다.
HTML 파일. 대부분의 컴퓨터 과학 교육 과정은 학생들에게 로컬 테스트 서버 가 일반적으로 사용되는 웹 개발을 소개합니다.
개발자는 일반적으로 기본 서버 기능 이 있는 개발 도구 가 유용할 수 있는 기타 영역인 시스템 관리 및 웹 서비스에 대해서도 배웁니다.
이와 같은 교육 및 비공식 작업에는 즉시 사용 가능한 작은 서버가 바람직합니다. 사용 사례는 다음과 같습니다.

  • 로컬 테스트 서버를 사용하여 클라이언트-서버 설정을 시뮬레이션하는 웹 개발 테스트.
  • 정적 파일이 RESTful URL을 미러링하고 더미 데이터를 포함하는 디렉토리 구조에서 API 스텁으로 사용되는 웹 서비스 또는 애플리케이션 테스트.
  • 예를 들어 로컬 시스템에서 원격 서버의 디렉토리를 검색하기 위해 시스템 간에 파일을 비공식적으로 탐색하고 공유합니다.

이 모든 경우에 우리는 물론 웹 서버 프레임워크를 사용할 수 있지만 그 접근 방식은 활성화 에너지가 높습니다.
첫 번째 요청을 처리할 수 있습니다. 이러한 단계는 상당한 의식을 필요로 하는 단점이 있습니다.
도중에 어딘가에 갇히면 답답할 수 있으며 Java를 더 이상 사용하는 데 방해가 될 수도 있습니다.
명령줄에서 또는 몇 줄의 코드를 통해 실행되는 기본 웹 서버를 사용하면 이 의식을 건너뛸 수 있으므로 대신 당면한 작업에 집중할 수 있습니다.

Python, Ruby, PHP, Erlang 및 기타 여러 플랫폼 은 명령줄에서 실행되는 즉시 사용 가능한 서버를 제공합니다.
이러한 다양한 기존 대안은 이러한 유형의 도구에 대한 인식된 필요성을 보여줍니다.

Description

단순 웹 서버는 단일 디렉토리 계층을 제공하기 위한 최소 HTTP 서버입니다.
2006년부터 JDK에 포함된 패키지 의 웹 서버 구현을 기반으로 합니다 com.sun.net.httpserver.
패키지는 공식적으로 지원되며 API로 확장하여 서버 생성을 단순화하고 요청 처리를 향상시킵니다.
Simple Web Server는 전용 명령줄 도구 jwebserver를 통해 또는 API를 통해 프로그래밍 방식으로 사용할 수 있습니다.

Command-line tool

다음 명령은 단순 웹 서버를 시작합니다.

1
$ jwebserver

시작이 성공 jwebserver하면 로컬 주소와 제공되는 디렉토리의 절대 경로를 나열하는 메시지를 System.out에 인쇄합니다. 예를 들어:

1
2
3
4
$ jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /cwd and subdirectories on 127.0.0.1 port 8000
URL: http://127.0.0.1:8000/

기본적으로 서버는 포그라운드에서 실행되고 루프백 주소 및 포트 8000에 바인딩됩니다. 이는 -b및 -p옵션을 사용하여 변경할 수 있습니다. 예를 들어, 포트 9000에서 서버를 실행하려면 다음을 사용하십시오.

1
$ jwebserver -p 9000

예를 들어, 서버를 모든 인터페이스에 바인딩하려면:

1
2
3
$ jwebserver -b 0.0.0.0
Serving /cwd and subdirectories on 0.0.0.0 (all interfaces) port 8000
URL: http://123.456.7.891:8000/

기본적으로 파일은 현재 디렉토리에서 제공됩니다. 옵션 으로 다른 디렉토리를 지정할 수 있습니다 -d.

멱등원 HEAD 및 GET 요청만 제공됩니다. 다른 모든 요청은 501 - Not Implemented또는 405 - Not Allowed응답을 받습니다. GET 요청은 다음과 같이 제공되는 디렉터리에 매핑됩니다.

  • 요청된 리소스가 파일인 경우 해당 콘텐츠가 제공됩니다.
  • 요청된 리소스가 인덱스 파일이 포함된 디렉터리인 경우 인덱스 파일의 콘텐츠가 제공됩니다.
  • 그렇지 않으면 디렉토리의 모든 파일 및 하위 디렉토리의 이름이 나열됩니다. 심볼릭 링크 및 숨겨진 파일은 나열되거나 제공되지 않습니다.

Simple Web Server는 HTTP/1.1만 지원합니다. HTTPS 지원이 없습니다.

MIME 유형은 자동으로 구성됩니다. 예를 들어 .html파일은 로 제공 text/html되고 .java파일은 로 제공됩니다 text/plain.

기본적으로 모든 요청은 콘솔에 기록됩니다. 출력은 다음과 같습니다.

1
127.0.0.1 - - [10/Feb/2021:14:34:11 +0000] "GET /some/subdirectory/ HTTP/1.1" 200 -

로깅 출력은 옵션으로 변경할 수 있습니다 -o. 기본 설정은 info입니다. 설정 에는 verbose요청 및 응답 헤더와 요청된 리소스의 절대 경로가 추가로 포함됩니다.

성공적으로 시작된 서버는 중지될 때까지 실행됩니다. Ctrl+CUnix 플랫폼에서 서버는 SIGINT 신호( 터미널 창에서) 를 전송하여 중지할 수 있습니다.

이 옵션은 JEP 293-h 의 지침을 따르는 모든 옵션을 나열하는 도움말 메시지를 표시합니다 . 매뉴얼 페이지도 제공됩니다 .jwebserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Options:
-h or -? or --help
Prints the help message and exits.

-b addr or --bind-address addr
Specifies the address to bind to. Default: 127.0.0.1 or ::1 (loopback). For
all interfaces use -b 0.0.0.0 or -b ::.

-d dir or --directory dir
Specifies the directory to serve. Default: current directory.

-o level or --output level
Specifies the output format. none | info | verbose. Default: info.

-p port or --port port
Specifies the port to listen on. Default: 8000.

-version or --version
Prints the version information and exits.

To stop the server, press Ctrl + C.

API

명령줄 도구가 유용하지만 Simple Web Server의 구성 요소(예: 서버, 처리기 및 필터)를 기존 코드와 함께 사용하거나 처리기의 동작을 추가로 사용자 지정하려는 경우 어떻게 해야 합니까?
명령줄에서 일부 구성이 가능하지만 생성 및 사용자 지정을 위한 간결하고 직관적인 프로그래밍 방식 솔루션은 서버 구성 요소의 유용성을 향상시킵니다.
명령줄 도구의 단순성과 현재 com.sun.net.httpserverAPI의 직접 작성 접근 방식 사이의 간극을 메우기 위해 서버 생성 및 맞춤형 요청 처리를 위한 새로운 API를 정의합니다.

1
2
3
4
5
6
7
8
9
10
11
package com.sun.net.httpserver;

public final class SimpleFileServer {
public static HttpServer createFileServer(InetSocketAddress addr,
Path rootDirectory,
OutputLevel outputLevel) {...}
public static HttpHandler createFileHandler(Path rootDirectory) {...}
public static Filter createOutputFilter(OutputStream out,
OutputLevel outputLevel) {...}
...
}

이 클래스를 사용하면 다음에서 몇 줄의 코드로 최소한의 사용자 정의 서버를 시작할 수 있습니다

1
2
3
jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8080),
...> Path.of("/some/path"), OutputLevel.VERBOSE);
jshell> server.start()

사용자 정의된 파일 서버 처리기를 기존 서버에 추가할 수 있습니다.

1
2
3
4
5
jshell> var server = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/store/", new SomePutHandler());
jshell> var handler = SimpleFileServer.createFileHandler(Path.of("/some/path"));
jshell> server.createContext("/browse/", handler);
jshell> server.start();

생성하는 동안 사용자 정의된 출력 필터를 서버에 추가할 수 있습니다.

1
2
3
4
5
6
7
jshell> var filter = SimpleFileServer.createOutputFilter(System.out,
...> OutputLevel.INFO);
jshell> var server = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/store/", new SomePutHandler(), filter);
jshell> server.start();


Enhanced request handling

Simple Web Server의 핵심 기능은 해당 처리기에 의해 제공됩니다.
기존 코드와 함께 사용하기 위해 이 핸들러를 확장하는 것을 지원하기 HttpHandlers위해 핸들러 생성 및 사용자 정의를 위한
두 개의 정적 메서드와 Filter요청 적응을 위한 클래스의 새 메서드가 있는 새 클래스를 도입합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.sun.net.httpserver;

public final class HttpHandlers {
public static HttpHandler handleOrElse(Predicate<Request> handlerTest,
HttpHandler handler,
HttpHandler fallbackHandler) {...}
public static HttpHandler of(int statusCode, Headers headers, String body) {...}
{...}
}

public abstract class Filter {
public static Filter adaptRequest(String description,
UnaryOperator<Request> requestOperator) {...}
{...}
}

handleOrElse다른 핸들러로 조건부 핸들러를 보완하는 반면, 팩토리 메소드 of를 사용하면 사전 설정된 응답 상태로 핸들러를 생성할 수 있습니다.
에서 얻은 전처리 필터를 adaptRequest사용하여 요청을 처리하기 전에 요청의 특정 속성을 검사하고 조정할 수 있습니다.
이러한 방법의 사용 사례에는 요청 방법을 기반으로 하는 교환 위임, 항상 특정 응답을 반환하는 “미리 준비된 응답” 처리기 생성 또는 모든 수신 요청에 헤더 추가가 포함됩니다.

HttpExchange기존 API는 교환의 전체 및 변경 가능한 상태를 설명하는 클래스 의 인스턴스가 나타내는 요청-응답 쌍의 일부로 HTTP 요청을 캡처합니다.
이 상태가 모두 핸들러 사용자 정의 및 적응에 의미가 있는 것은 아닙니다. 따라서 Request변경할 수 없는 요청 상태에 대한 제한된 보기를 제공하기 위해 더 간단한 인터페이스를 도입합니다.

1
2
3
4
5
6
7
8
public interface Request {
URI getRequestURI();
String getRequestMethod();
Headers getRequestHeaders();
default Request with(String headerName, List<String> headerValues)
{...}
}

이를 통해 기존 핸들러를 직접 사용자 지정할 수 있습니다. 예를 들면 다음과 같습니다.

1
2
3
4
5
6
7
jshell> var h = HttpHandlers.handleOrElse(r -> r.getRequestMethod().equals("PUT"),
...> new SomePutHandler(), new SomeHandler());
jshell> var f = Filter.adaptRequest("Add Foo header", r -> r.with("Foo", List.of("Bar")));
jshell> var s = HttpServer.create(new InetSocketAddress(8080),
...> 10, "/", h, f);
jshell> s.start();

Alternatives

우리는 명령줄 도구에 대한 대안을 고려했습니다.

  • java -m jdk.httpserver 참고: 처음에는 Simple Web Server가 java -m jdk.httpserver전용 명령줄 도구가 아닌 명령으로 실행되었습니다.
    이것은 여전히 가능하지만 (사실 jwebserver내부에서 명령을 사용함 java -m …) 편의성과 접근성을 향상시키기 위해 전용 도구를 도입하기로 결정했습니다.

프로토타입을 만드는 동안 몇 가지 API 대안을 고려했습니다.

  • 새로운 클래스 — 인터페이스 DelegatingHandler를 구현하는 별도의 클래스에 사용자 정의 방법을 번들로 제공합니다.
    HttpHandler더 많은 기능을 추가하지 않고 새로운 유형을 도입하는 대가를 치르게 되므로 이 옵션을 폐기했습니다. 이 새로운 유형도 발견하기 어려울 것입니다.
    반면 HttpHandlers에 클래스는 클래스의 정적 도우미 메서드 또는 팩토리가 새 클래스에 번들로 제공되는 아웃보딩 패턴을 사용합니다.
    거의 동일한 이름을 사용하면 클래스를 쉽게 찾을 수 있고, 새로운 API 포인트에 대한 이해와 사용이 용이하며, 위임의 구현 세부 정보를 숨길 수 있습니다.

  • HttpHandler서비스로 — 서비스 HttpHandler로 전환하고 내부 파일 서버 핸들러 구현을 제공합니다. 개발자는 사용자 지정 처리기를 제공하거나 기본 공급자를 사용할 수 있습니다.
    이 접근 방식의 단점은 우리가 제공하고자 하는 작은 기능 집합에 대해 사용하기가 더 어렵고 오히려 정교하다는 것입니다.

  • Filter대신 HttpHandler— 핸들러가 아닌 필터만 사용하여 요청을 처리합니다. 필터는 일반적으로 사전 또는 사후 처리입니다.
    즉, 인증 또는 로깅과 같이 처리기가 호출되기 전이나 후에 요청에 액세스합니다. 그러나 핸들러를 완전히 대체하도록 설계되지 않았습니다.
    이러한 방식으로 사용하는 것은 직관적이지 않고 방법을 찾기가 더 어려울 것입니다.

Testing

명령줄 도구의 핵심 기능은 API에서 제공하므로 대부분의 테스트 작업은 API에 중점을 둡니다.
API 포인트는 단위 테스트 및 기존 테스트 프레임워크와 분리하여 테스트할 수 있습니다.
특히 파일 시스템 액세스 및 URI 삭제에 중점을 둘 것입니다. 명령줄 도구의 명령 및 온전성 테스트로 API 테스트를 보완합니다.

Risks and Assumptions

이 간단한 서버는 테스트, 개발 및 디버깅 목적으로만 사용됩니다. 이 범위 내에서 서버의 일반적인 보안 문제가 적용되며 다음 보안 모범 사례와 철저한 테스트를 통해 해결됩니다.

참조