Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 444 – Python Web3 인터페이스

Author:
Chris McDonough <chrism at plope.com>, Armin Ronacher <armin.ronacher at active-4.com>
Discussions-To:
Web-SIG list
Status:
Deferred
Type:
Informational
Created:
19-Jul-2010

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 문서는 웹 서버와 Python 웹 애플리케이션 또는 프레임워크 간의 제안된 2세대 표준 인터페이스를 규정합니다.

PEP 보류

이 PEP에서 다루는 개념에 대한 추가 검토는 PEP의 목표를 추진하고 피드백을 수집 및 반영하는 데 관심이 있는 현재의 추진자가 없고, 이를 효과적으로 수행할 충분한 시간도 부족하여 보류되었습니다.

이 PEP가 처음 작성된 이후, Python 3.2 이상에서 WSGI를 사용할 수 있도록 한 보다 점진적인 업데이트로서 PEP 3333이 작성되었다는 점에 유의하십시오. 그러나 바이너리 데이터와 텍스트 데이터를 더욱 깔끔하게 분리하려는 Python 3의 목표를 진전시키는 대안 사양도 여전히 가치가 있을 수 있습니다.

근거와 목표

이 프로토콜과 사양은 PEP 333에 기술된 Web Services Gateway Interface (WSGI) 1.0 표준의 영향을 크게 받았습니다. Python 기반 웹 서버와 애플리케이션이 상호 운용되도록 하는 표준이 필요한 일반적인 근거는 PEP 333에 개괄되어 있습니다. 이 문서는 본질적으로 PEP 333을 템플릿으로 사용하며, 다른 표준을 구성하기 위해 여러 부분에서 그 문구를 변경합니다.

현재 Python에는 WSGI 1.0 프로토콜을 사용하는 매우 다양한 웹 애플리케이션 프레임워크가 있습니다. 그러나 언어의 변경으로 인해 WSGI 1.0 프로토콜은 Python 3과 호환되지 않습니다. 이 사양은 Python 2.6, 2.7 및 3.1+ 애플리케이션이 웹 서버와 통신할 수 있도록 하는 표준화된 WSGI 유사 프로토콜을 설명합니다. Web3은 분명히 WSGI의 파생물입니다. 하위 호환성이 어떠한 방식으로도 없음을 나타내기 위해 “WSGI”와 다른 이름만 사용합니다.

이 사양에 따라 작성된 애플리케이션과 서버는 Python 2.6.X, Python 2.7.X 및 Python 3.1+에서 제대로 작동하도록 작성되었습니다. Web3 사양을 구현하는 애플리케이션이나 서버를 Python 2.6 이전 버전과 Python 3.1 이전 버전 모두에서 작동하도록 쉽게 작성할 수는 없습니다.

Note

현재 로캘을 사용하여 ‘foo’의 값을 디코딩할 수 없을 때 KeyError로 실패하는 대신 os.environ['foo'] 표현이 (PEP 383 방식으로) 서로게이트를 반환하도록 http://bugs.python.org/issue4006 문제를 수정한 Python 3 버전이 무엇이든 그 버전이 진정한 최소 Python 3 버전입니다. 그러나 특히 Python 3.0은 지원되지 않습니다.

Note

Python 2.6은 bytes의 별칭과 b"foo" 리터럴 구문을 지원한 최초의 Python 버전입니다. 이것이 Web3에서 지원하는 최소 버전인 이유입니다.

설명 가능성과 문서화 가능성이 이 표준에서 이루어진 결정의 주요 기술적 원동력입니다.

WSGI와의 차이점

  • 프로토콜별 환경 이름에는 wsgi. 대신 web3. 접두사가 붙습니다. 예를 들어 web3.input이며 wsgi.input이 아닙니다.
  • 환경 딕셔너리에 존재하는 모든 환경 values는 네이티브 문자열이 아닌 bytes 인스턴스임을 명시적으로 나타냅니다. (그러나 환경 keys는 네이티브 문자열이며, 플랫폼에 관계없이 항상 str입니다.)
  • 애플리케이션이 반환하는 모든 값은 상태 코드, 헤더 이름과 값, 본문을 포함하여 bytes 인스턴스여야 합니다.
  • WSGI 1.0에서 app_iter를 가리키는 모든 부분에서 이 사양은 body를 가리킵니다.
  • start_response() 콜백이 없습니다(따라서 write() 호출 가능 객체나 exc_info 데이터도 없습니다).
  • web3.inputreadline() 함수는 크기 힌트 매개변수를 지원해야 합니다.
  • read()함수는 web3.input의 길이가 제한되어야 합니다. 크기 인자 없이 호출된 경우 콘텐츠 길이 헤더에 지정된 것보다 더 많이 읽어서는 안 됩니다. 콘텐츠 길이 헤더가 없는 경우 스트림은 읽기 시 아무것도 반환해서는 안 됩니다. 클라이언트에 지정된 것보다 더 많은 데이터를 요청해서는 안 됩니다.
  • 애플리케이션에서 출력을 생성하려면 더 많은 정보가 필요한 경우 미들웨어가 빈 문자열을 생성해야 한다는 요구 사항은 없습니다(예: “블록 경계의 미들웨어 처리” 없음).
  • “file_wrapper”에 전달되는 파일과 유사한 객체에는 바이트를 반환하는(텍스트는 반환하지 않는) __iter__가 있어야 합니다.
  • wsgi.file_wrapper는 지원되지 않습니다.
  • QUERY_STRING, SCRIPT_NAME, PATH_INFO 값은 서버가 environ에 배치해야 합니다(HTTP 요청에서 연결된 값이 수신되지 않은 경우 각각 빈 바이트 인스턴스로 배치해야 합니다).
  • 가능한 경우 원본 Web3 서버는 web3.path_infoweb3.script_name을 Web3 환경에 넣어야 합니다. 사용 가능한 경우 각각은 요청 URI에서 직접 파생된 CGI 대응 값의 원본 일반 7비트 ASCII URL 인코딩 변형입니다(%2F 세그먼트 표시자와 기타 메타 문자는 그대로 유지됩니다). 서버가 이 값 중 하나 또는 모두를 제공할 수 없는 경우, 제공할 수 없는 값은 환경에서 생략해야 합니다.
  • 이 요구 사항은 삭제되었습니다: “미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리며 반복을 반드시 차단해서는 안 됩니다. 미들웨어가 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 누적해야 하는 경우 빈 문자열을 반드시 생성해야 합니다.”
  • SERVER_PORT는 바이트 인스턴스여야 합니다(정수가 아니어야 합니다).
  • 서버는 응답 이터러블에서 길이를 추측하여 추가 Content-Length헤더를 삽입해서는 안 됩니다. 이는 모든 상황에서 애플리케이션 자체가 설정해야 합니다.
  • 원본 서버가 web3.async기능을 보유한다고 알리는 경우, 서버가 사용하는 Web3 애플리케이션 호출 가능 객체는 인자를 받지 않는 호출 가능 객체를 반환할 수 있습니다. 이 경우 원본 서버는 이 호출 가능 객체가 None이 아닌 응답을 반환할 때까지 주기적으로 호출해야 하며, 해당 응답은 일반적인 Web3 응답 튜플이어야 합니다.

사양 개요

Web3 인터페이스에는 “서버” 또는 “게이트웨이” 측과 “애플리케이션” 또는 “프레임워크” 측이라는 두 측면이 있습니다. 서버 측은 애플리케이션 측에서 제공하는 호출 가능 객체를 호출합니다. 해당 객체를 제공하는 방식의 구체적인 사항은 서버 또는 게이트웨이에 달려 있습니다. 일부 서버 또는 게이트웨이는 애플리케이션 배포자가 서버 또는 게이트웨이의 인스턴스를 생성하고 애플리케이션 객체를 제공하는 짧은 스크립트를 작성해야 할 것으로 가정합니다. 다른 서버와 게이트웨이는 구성 파일 또는 기타 메커니즘을 사용하여 애플리케이션 객체를 어디에서 가져오거나 다른 방식으로 획득해야 하는지 지정할 수 있습니다.

“순수” 서버/게이트웨이와 애플리케이션/프레임워크 외에도 이 사양의 양쪽 측면을 모두 구현하는 “미들웨어” 구성 요소를 생성할 수도 있습니다. 이러한 구성 요소는 자신을 포함하는 서버에 대해서는 애플리케이션으로, 포함된 애플리케이션에 대해서는 서버로 작동하며, 확장 API, 콘텐츠 변환, 탐색 및 기타 유용한 기능을 제공하는 데 사용할 수 있습니다.

이 사양 전체에서 “애플리케이션 호출 가능 객체”라는 용어는 “함수, 메서드 또는 __call__메서드가 있는 인스턴스”를 의미하는 데 사용합니다. 애플리케이션 호출 가능 객체를 구현하는 서버, 게이트웨이 또는 애플리케이션이 필요에 맞는 적절한 구현 기법을 선택합니다. 반대로 호출 가능 객체를 호출하는 서버, 게이트웨이 또는 애플리케이션은 제공된 호출 가능 객체의 종류에 어떠한 의존성도 가져서는 안 됩니다. 애플리케이션 호출 가능 객체는 호출만 해야 하며, 내부를 검사해서는 안 됩니다.

애플리케이션/프레임워크 측

애플리케이션 객체는 하나의 인자를 받는 단순한 호출 가능 객체입니다. “객체”라는 용어는 실제 객체 인스턴스를 요구하는 것으로 오해해서는 안 됩니다. 함수, 메서드 또는 __call__메서드를 가진 인스턴스는 모두 애플리케이션 객체로 사용할 수 있습니다. 거의 모든 서버/게이트웨이(CGI 제외)는 이러한 반복 요청을 수행하므로, 애플리케이션 객체는 두 번 이상 호출될 수 있어야 합니다. 실제 애플리케이션의 구현으로 이를 보장할 수 없다면, 호출할 때마다 새 인스턴스를 생성하는 함수로 감싸야 합니다.

Note

이를 “애플리케이션” 객체라고 부르지만, 애플리케이션 개발자가 Web3를 웹 프로그래밍 API로 사용할 것이라는 의미로 오해해서는 안 됩니다. 애플리케이션 개발자는 기존의 고수준 프레임워크 서비스를 계속 사용하여 애플리케이션을 개발한다고 가정합니다. Web3는 프레임워크 및 서버 개발자를 위한 도구이며, 애플리케이션 개발자를 직접 지원하기 위한 것이 아닙니다.)

함수인 애플리케이션의 예(simple_app):

def simple_app(environ):
    """Simplest possible application object"""
    status = b'200 OK'
    headers = [(b'Content-type', b'text/plain')]
    body = [b'Hello world!\n']
    return body, status, headers

인스턴스인 애플리케이션의 예(simple_app):

class AppClass(object):

    """Produce the same output, but using an instance.  An
    instance of this class must be instantiated before it is
    passed to the server.  """

  def __call__(self, environ):
        status = b'200 OK'
        headers = [(b'Content-type', b'text/plain')]
        body = [b'Hello world!\n']
        return body, status, headers

simple_app = AppClass()

또는 서버가 비동기 실행을 지원하는 경우, 애플리케이션 호출 가능 객체는 튜플 대신 호출 가능 객체를 반환할 수 있습니다. 자세한 내용은 web3.async에 관한 정보를 참조하십시오.

서버/게이트웨이 측

서버 또는 게이트웨이는 애플리케이션을 대상으로 하는 HTTP 클라이언트의 각 요청마다 애플리케이션 호출 가능 객체를 한 번씩 호출합니다. 예를 들어, 다음은 애플리케이션 객체를 인자로 받는 함수로 구현된 간단한 CGI 게이트웨이입니다. 이 간단한 예제는 오류 처리가 제한적이라는 점에 유의하십시오. 기본적으로 처리되지 않은 예외는 sys.stderr에 덤프되고 웹 서버에 의해 기록되기 때문입니다.

import locale
import os
import sys

encoding = locale.getpreferredencoding()

stdout = sys.stdout

if hasattr(sys.stdout, 'buffer'):
    # Python 3 compatibility; we need to be able to push bytes out
    stdout = sys.stdout.buffer

def get_environ():
    d = {}
    for k, v in os.environ.items():
        # Python 3 compatibility
        if not isinstance(v, bytes):
            # We must explicitly encode the string to bytes under
            # Python 3.1+
            v = v.encode(encoding, 'surrogateescape')
        d[k] = v
    return d

def run_with_cgi(application):

    environ = get_environ()
    environ['web3.input']        = sys.stdin
    environ['web3.errors']       = sys.stderr
    environ['web3.version']      = (1, 0)
    environ['web3.multithread']  = False
    environ['web3.multiprocess'] = True
    environ['web3.run_once']     = True
    environ['web3.async']        = False

    if environ.get('HTTPS', b'off') in (b'on', b'1'):
        environ['web3.url_scheme'] = b'https'
    else:
        environ['web3.url_scheme'] = b'http'

    rv = application(environ)
    if hasattr(rv, '__call__'):
        raise TypeError('This webserver does not support asynchronous '
                        'responses.')
    body, status, headers = rv

    CLRF = b'\r\n'

    try:
        stdout.write(b'Status: ' + status + CRLF)
        for header_name, header_val in headers:
            stdout.write(header_name + b': ' + header_val + CRLF)
        stdout.write(CRLF)
        for chunk in body:
            stdout.write(chunk)
            stdout.flush()
    finally:
        if hasattr(body, 'close'):
            body.close()

미들웨어: 양쪽 역할을 모두 수행하는 구성 요소

하나의 객체가 일부 애플리케이션에 대해서는 서버 역할을 수행하는 동시에, 일부 서버에 대해서는 애플리케이션 역할을 수행할 수 있습니다. 이러한 “미들웨어” 구성 요소는 다음과 같은 기능을 수행할 수 있습니다.

  • 이에 따라 environ을 다시 작성한 후 대상 URL을 기준으로 요청을 서로 다른 애플리케이션 객체로 라우팅합니다.
  • 여러 애플리케이션 또는 프레임워크가 동일한 프로세스에서 나란히 실행되도록 합니다.
  • 네트워크를 통해 요청과 응답을 전달하여 부하를 분산하고 원격 처리를 수행합니다.
  • XSL 스타일시트를 적용하는 등의 콘텐츠 후처리를 수행합니다.

일반적으로 미들웨어의 존재는 인터페이스의 “서버/게이트웨이” 측과 “애플리케이션/프레임워크” 측 모두에 투명하며, 특별한 지원을 요구하지 않아야 합니다. 미들웨어를 애플리케이션에 통합하려는 사용자는 미들웨어 구성 요소를 애플리케이션인 것처럼 서버에 제공하고, 미들웨어 구성 요소가 서버인 것처럼 애플리케이션을 호출하도록 구성하기만 하면 됩니다. 물론 미들웨어가 감싸는 “애플리케이션”은 실제로 다른 애플리케이션을 감싸는 또 다른 미들웨어 구성 요소일 수 있으며, 이런 방식이 계속 이어져 이른바 “미들웨어 스택”을 구성할 수 있습니다.

미들웨어는 가능한 경우 비동기 실행을 지원해야 하며, 그렇지 않으면 자체적으로 비활성화되는 방식으로 대체해야 합니다.

다음은 X-Host헤더가 존재할 경우 HTTP_HOST키를 변경하고 모든 HTML 응답에 주석을 추가하는 미들웨어입니다.:

import time

def apply_filter(app, environ, filter_func):
    """Helper function that passes the return value from an
    application to a filter function when the results are
    ready.
    """
    app_response = app(environ)

    # synchronous response, filter now
    if not hasattr(app_response, '__call__'):
        return filter_func(*app_response)

    # asynchronous response.  filter when results are ready
    def polling_function():
        rv = app_response()
        if rv is not None:
            return filter_func(*rv)
    return polling_function

def proxy_and_timing_support(app):
    def new_application(environ):
        def filter_func(body, status, headers):
            now = time.time()
            for key, value in headers:
                if key.lower() == b'content-type' and \
                   value.split(b';')[0] == b'text/html':
                    # assumes ascii compatible encoding in body,
                    # but the middleware should actually parse the
                    # content type header and figure out the
                    # encoding when doing that.
                    body += ('<!-- Execution time: %.2fsec -->' %
                             (now - then)).encode('ascii')
                    break
            return body, status, headers
        then = time.time()
        host = environ.get('HTTP_X_HOST')
        if host is not None:
            environ['HTTP_HOST'] = host

        # use the apply_filter function that applies a given filter
        # function for both async and sync responses.
        return apply_filter(app, environ, filter_func)
    return new_application

app = proxy_and_timing_support(app)

사양 세부 사항

애플리케이션 호출 가능 객체는 하나의 위치 인자를 받아야 합니다. 설명을 위해 이 인자의 이름을 environ으로 지정했지만, 반드시 이 이름이어야 하는 것은 아닙니다. 서버 또는 게이트웨이는 위치 인자(키워드 인자가 아님)를 사용하여 애플리케이션 객체를 반드시 호출해야 합니다. (예를 들어 위에 나온 것처럼 body, status, headers = application(environ)이라고 호출합니다.)

environ매개변수는 CGI 스타일 환경 변수를 포함하는 딕셔너리 객체입니다. 이 객체는 반드시 내장 Python 딕셔너리(서브클래스, UserDict 또는 다른 딕셔너리 에뮬레이션이 아닌)이어야 하며, 애플리케이션은 원하는 어떤 방식으로든 딕셔너리를 수정할 수 있습니다. 딕셔너리에는 이후 섹션에서 설명하는 Web3 필수 변수도 포함되어야 하며, 아래에서 설명할 규칙에 따라 명명된 서버별 확장 변수가 포함될 수도 있습니다.

서버가 호출하면 애플리케이션 객체는 status, headersbody라는 세 요소를 생성하는 튜플을 반환해야 하며, 비동기 서버가 지원하는 경우에는 None 또는 이 세 요소의 튜플을 반환하는 인자 없는 호출 가능 객체를 반환해야 합니다.

status 요소는 b'999 Message here' 형식의 바이트 상태 값입니다.

headers는 HTTP 응답 헤더를 설명하는 (header_name, header_value) 쌍의 Python 리스트입니다. headers 구조는 리터럴 Python 리스트여야 하며, 2-튜플을 생성해야 합니다. header_nameheader_value는 모두 바이트 값이어야 합니다.

body는 0개 이상의 바이트 인스턴스를 생성하는 이터러블입니다. 이는 body로 바이트 인스턴스를 포함하는 리스트를 반환하거나, 바이트 인스턴스를 생성하는 제너레이터 함수를 body로 반환하거나, body가 이터러블인 클래스의 인스턴스가 되도록 하는 등 다양한 방식으로 구현할 수 있습니다. 어떤 방식으로 구현하든 애플리케이션 객체는 항상 0개 이상의 바이트 인스턴스를 생성하는 body 이터러블을 반환해야 합니다.

서버 또는 게이트웨이는 생성된 바이트를 버퍼링하지 않고 클라이언트로 전송해야 하며, 다음 바이트 집합을 요청하기 전에 각 바이트 집합의 전송을 완료해야 합니다. (즉, 애플리케이션은 자체적으로 버퍼링을 수행해야 합니다. 애플리케이션 출력을 처리하는 방법에 대한 자세한 내용은 아래의 Buffering and Streaming 섹션을 참조하십시오.)

서버 또는 게이트웨이는 생성된 바이트를 이진 바이트 시퀀스로 처리해야 하며, 특히 줄 끝이 변경되지 않도록 해야 합니다. 기록할 문자열이 클라이언트에 적합한 형식인지 확인할 책임은 애플리케이션에 있습니다. (서버 또는 게이트웨이는 바이트 범위 전송과 같은 HTTP 기능을 구현하기 위해 HTTP 전송 인코딩을 적용하거나 다른 변환을 수행할 수 있습니다. 자세한 내용은 아래의 Other HTTP Features를 참조하십시오.)

애플리케이션이 반환한 body 이터러블에 close() 메서드가 있으면, 서버 또는 게이트웨이는 현재 요청이 정상적으로 완료되었는지 오류로 인해 조기에 종료되었는지와 관계없이 해당 요청의 완료 시 그 메서드를 반드시 호출해야 합니다. 이는 애플리케이션의 리소스 해제를 지원하며 PEP 325의 제너레이터 지원 및 close() 메서드를 사용하는 기타 일반적인 이터러블을 보완하기 위한 것입니다.

마지막으로, 서버와 게이트웨이는 애플리케이션이 반환한 body이터러블의 다른 어떤 속성도 직접 사용해서는 안 됩니다.

environ 변수

environ 딕셔너리에는 공통 게이트웨이 인터페이스 사양 [2]에 정의된 다양한 CGI 환경 변수가 포함되어야 합니다.

다음 CGI 변수는 반드시 존재해야 합니다. 각 키는 네이티브 문자열입니다. 각 값은 바이트 인스턴스입니다.

Note

Python 3.1 이상에서 “네이티브 문자열”은 surrogateescape오류 처리기를 사용해 디코딩한 str형식이며, os.environ.__getitem__이 수행하는 방식과 같습니다. Python 2.6 및 2.7에서 “네이티브 문자열”은 바이트 집합을 나타내는 str형식입니다.

REQUEST_METHOD
"GET" 또는 "POST"와 같은 HTTP 요청 메서드입니다.
SCRIPT_NAME
애플리케이션 객체에 해당하여 애플리케이션이 자신의 가상 “위치”를 알 수 있도록 하는 요청 URL의 “경로”의 초기 부분입니다. 애플리케이션이 서버의 “루트”에 해당하는 경우 이는 빈 바이트 인스턴스일 수 있습니다. SCRIPT_NAME은 슬래시 문자(/)로 구분된 URL 인코딩 세그먼트 시퀀스를 나타내는 바이트 인스턴스입니다. CGI에 따라 %2F 문자는 PATH_INFO 내에서 리터럴 슬래시 문자로 디코딩된다고 가정합니다.
PATH_INFO
애플리케이션 내 요청 대상의 가상 “위치”를 지정하는 요청 URL의 “경로”의 나머지 부분입니다. 요청 URL이 애플리케이션 루트를 대상으로 하고 후행 슬래시가 없는 경우 이는 바이트 인스턴스일 수도 있습니다. PATH_INFO는 슬래시 문자(/)로 구분된 URL 인코딩 세그먼트 시퀀스를 나타내는 바이트 인스턴스입니다. CGI에 따라 %2F 문자는 PATH_INFO 내에서 리터럴 슬래시 문자로 디코딩된다고 가정합니다.
QUERY_STRING
요청 URL에서 "?" 뒤에 오는 부분(있는 경우) 또는 빈 바이트 인스턴스입니다.
SERVER_NAME, SERVER_PORT
이 변수들은 SCRIPT_NAMEPATH_INFO(또는 해당 원시 표현)와 결합하여 URL을 완성하는 데 사용할 수 있습니다. 그러나 HTTP_HOST가 있으면 요청 URL을 재구성할 때 SERVER_NAME보다 우선하여 사용해야 합니다. 자세한 내용은 아래의 URL Reconstruction 섹션을 참조하십시오. SERVER_PORT는 정수가 아니라 바이트 인스턴스여야 합니다.
SERVER_PROTOCOL
클라이언트가 요청을 전송하는 데 사용한 프로토콜의 버전입니다. 일반적으로 "HTTP/1.0" 또는 "HTTP/1.1"과 같은 형식이며, 애플리케이션이 HTTP 요청 헤더를 어떻게 처리할지 결정하는 데 사용할 수 있습니다. (이 변수는 요청에 사용된 프로토콜을 나타내며 서버의 응답에 사용될 프로토콜을 반드시 의미하지는 않으므로 REQUEST_PROTOCOL이라고 부르는 편이 좋을 것입니다. 그러나 CGI와의 하위 호환성을 위해 기존 이름을 유지해야 합니다.)

다음 CGI 값은 Web3 환경에 존재할 수도 있습니다. 각 키는 네이티브 문자열입니다. 각 값은 바이트 인스턴스입니다.

CONTENT_TYPE
HTTP 요청의 Content-Type 필드 내용입니다.
CONTENT_LENGTH
HTTP 요청의 Content-Length 필드 내용입니다.
HTTP_ 변수
클라이언트가 제공한 HTTP 요청 헤더에 해당하는 변수입니다(즉, 이름이 "HTTP_"로 시작하는 변수입니다). 이러한 변수의 존재 여부는 요청에 해당 HTTP 헤더가 존재하는지 여부와 일치해야 합니다.

서버 또는 게이트웨이는 적용 가능한 다른 CGI 변수를 가능한 한 많이 제공하도록 시도해야 하며, 각 변수의 키는 문자열로, 값은 바이트 인스턴스로 제공해야 합니다. 또한 SSL을 사용 중인 경우 서버 또는 게이트웨이는 HTTPS=onSSL_PROTOCOL과 같은 적용 가능한 Apache SSL 환경 변수 [4]를 가능한 한 많이 제공하도록 시도해야 합니다. 그러나 위에 나열된 것 이외의 CGI 변수를 사용하는 애플리케이션은 관련 확장을 지원하지 않는 웹 서버로 이식할 수 없습니다. (예를 들어 파일을 게시하지 않는 웹 서버는 의미 있는 DOCUMENT_ROOT 또는 PATH_TRANSLATED를 제공할 수 없습니다.)

Web3를 준수하는 서버 또는 게이트웨이는 제공하는 변수와 적절한 경우 해당 변수의 정의를 문서화해야 합니다. 애플리케이션은 필요한 변수가 존재하는지 확인해야 하며, 그러한 변수가 없을 경우를 대비한 대체 계획을 마련해야 합니다.

CGI 변수의 은 존재하는 경우 반드시 bytes 인스턴스여야 합니다. CGI 변수의 값이 bytes 이외의 어떤 타입이든 되는 것은 이 사양을 위반하는 것입니다. Python 2에서는 이것이 해당 값의 타입이 str 이라는 의미입니다. Python 3에서는 이것이 해당 값의 타입이 bytes 라는 의미입니다.

그러나 environ의 모든 CGI 및 비 CGI 변수의 는 “네이티브 문자열”이어야 합니다(Python 2와 Python 3 모두 타입은 str 입니다).

CGI에서 정의한 변수 외에도 environ 딕셔너리는 임의의 운영 체제 “환경 변수”를 포함할 수도 있으며, 다음 Web3 정의 변수를 포함해야 합니다.

Variable Value
web3.version The tuple (1, 0), representing Web3 version 1.0.
web3.url_scheme A bytes value representing the “scheme” portion of the URL at which the application is being invoked. Normally, this will have the value b"http" or b"https", as appropriate.
web3.input An input stream (file-like object) from which bytes constituting the HTTP request body can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client’s request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.)
web3.errors An output stream (file-like object) to which error output text can be written, for the purpose of recording program or other errors in a standardized and possibly centralized location. This should be a “text mode” stream; i.e., applications should use "\n" as a line ending, and assume that it will be converted to the correct line ending by the server/gateway. Applications may not send bytes to the ‘write’ method of this stream; they may only send text.

For many servers, web3.errors will be the server’s main error log. Alternatively, this may be sys.stderr, or a log file of some sort. The server’s documentation should include an explanation of how to configure this or where to find the recorded output. A server or gateway may supply different error streams to different applications, if this is desired.

web3.multithread This value should evaluate true if the application object may be simultaneously invoked by another thread in the same process, and should evaluate false otherwise.
web3.multiprocess This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise.
web3.run_once This value should evaluate true if the server or gateway expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a gateway based on CGI (or something similar).
web3.script_name The non-URL-decoded SCRIPT_NAME value. Through a historical inequity, by virtue of the CGI specification, SCRIPT_NAME is present within the environment as an already URL-decoded string. This is the original URL-encoded value derived from the request URI. If the server cannot provide this value, it must omit it from the environ.
web3.path_info The non-URL-decoded PATH_INFO value. Through a historical inequity, by virtue of the CGI specification, PATH_INFO is present within the environment as an already URL-decoded string. This is the original URL-encoded value derived from the request URI. If the server cannot provide this value, it must omit it from the environ.
web3.async This is True if the webserver supports async invocation. In that case an application is allowed to return a callable instead of a tuple with the response. The exact semantics are not specified by this specification.

마지막으로 environ 딕셔너리는 서버에서 정의한 변수도 포함할 수 있습니다. 이러한 변수의 이름은 소문자, 숫자, 마침표 및 밑줄만으로 구성된 네이티브 문자열이어야 하며, 해당 변수를 정의하는 서버 또는 게이트웨이에 고유한 이름을 접두사로 사용해야 합니다. 예를 들어 mod_web3mod_web3.some_variable 과 같은 이름의 변수를 정의할 수 있습니다.

입력 스트림

서버가 제공하는 입력 스트림(web3.input)은 다음 메서드를 지원해야 합니다.

메서드 참고 사항
read(size) 1,4
readline([size]) 1,2,4
readlines([size]) 1,3,4
__iter__() 4

각 메서드의 의미는 위 표에 나열된 다음 참고 사항을 제외하면 Python 라이브러리 레퍼런스에 문서화된 내용과 같습니다.

  1. 서버는 클라이언트가 지정한 Content-Length 를 초과하여 읽을 필요가 없으며, 애플리케이션이 해당 지점을 넘어 읽으려고 할 경우 파일 끝 조건을 시뮬레이션할 수 있습니다. 애플리케이션은 CONTENT_LENGTH 변수에 지정된 것보다 많은 데이터를 읽으려고 시도해서는 안 됩니다.
  2. 구현은 readline() 의 선택적 size 인자를 지원해야 합니다.
  3. 애플리케이션은 readlines()size 인자를 제공하지 않아도 되며, 서버 또는 게이트웨이는 제공된 size 인자의 값을 무시해도 됩니다.
  4. read, readline__iter__ 메서드는 bytes 인스턴스를 반환해야 합니다. readlines 메서드는 bytes 인스턴스를 포함하는 시퀀스를 반환해야 합니다.

위 표에 나열된 메서드는 이 사양을 준수하는 모든 서버에서 지원되어야 합니다. 이 사양을 준수하는 애플리케이션은 input객체의 다른 어떤 메서드나 속성도 사용해서는 안 됩니다. 특히, 애플리케이션은 이 스트림에 close() 메서드가 있더라도 스트림을 닫으려고 시도해서는 안 됩니다.

입력 스트림은 요청의 콘텐츠 길이보다 더 많이 읽으려는 시도를 조용히 무시해야 합니다. 콘텐츠 길이가 지정되지 않은 경우 스트림은 아무것도 반환하지 않는 더미 스트림이어야 합니다.

오류 스트림

서버가 제공하는 오류 스트림(web3.errors)은 다음 메서드를 지원해야 합니다.

메서드 스트림 참고 사항
flush() errors 1
write(str) errors 2
writelines(seq) errors 2

각 메서드의 의미는 Python Library Reference에 문서화된 대로이지만, 위 표에 나열된 다음 참고 사항은 예외입니다.

  1. errors스트림은 되감을 수 없으므로 서버와 게이트웨이는 버퍼링하지 않고 쓰기 작업을 즉시 전달해도 됩니다. 이 경우 flush() 메서드는 아무 작업도 하지 않아도 됩니다. 그러나 이식 가능한 애플리케이션은 출력이 버퍼링되지 않거나 flush() 메서드가 아무 작업도 하지 않는다고 가정할 수 없습니다. 출력이 실제로 기록되었음을 보장해야 하는 경우 flush()를 호출해야 합니다. (예를 들어 동일한 오류 로그에 여러 프로세스가 기록하는 데이터가 서로 섞이는 것을 최소화하려는 경우입니다.)
  2. write() 메서드는 문자열 인자를 받아들여야 하지만, bytes 인자를 반드시 받아들일 필요는 없습니다. writelines() 메서드는 전부 문자열로 구성된 시퀀스 인자를 받아들여야 하지만, 시퀀스의 멤버로 bytes 인스턴스를 반드시 받아들일 필요는 없습니다.

위 표에 나열된 메서드는 이 사양을 준수하는 모든 서버에서 지원되어야 합니다. 이 사양을 준수하는 애플리케이션은 errors객체의 다른 어떤 메서드나 속성도 사용해서는 안 됩니다. 특히, 애플리케이션은 이 스트림에 close() 메서드가 있더라도 스트림을 닫으려고 시도해서는 안 됩니다.

Web3 애플리케이션이 반환하는 값

Web3 애플리케이션은 (status, headers, body) 형식의 튜플을 반환합니다. 서버가 비동기 애플리케이션(web3.async)을 지원하는 경우 응답은 인자를 받지 않는 호출 가능 객체일 수 있습니다.

status 값은 게이트웨이 또는 서버가 b'200 OK'b'404 Not Found'와 같은 HTTP “status” 바이트 인스턴스로 간주합니다. 즉, Status-Code와 Reason-Phrase로 구성된 문자열이며, 이 순서로 단일 공백으로 구분되고 앞뒤에 공백이나 다른 문자가 없어야 합니다. (자세한 내용은 RFC 2616의 Section 6.1.1을 참조하십시오.) 문자열에는 제어 문자가 포함되어서는 안 되며, 캐리지 리턴, 줄 바꿈 또는 이들의 조합으로 끝나서는 안 됩니다.

headers 값은 게이트웨이 또는 서버가 (header_name, header_value) 튜플로 이루어진 파이썬 리터럴 리스트로 간주합니다. 각 header_name은 유효한 HTTP 헤더 필드 이름을 나타내는 바이트 인스턴스여야 하며(RFC 2616의 Section 4.2에 정의됨), 끝에 콜론이나 다른 구두점이 없어야 합니다. 각 header_value는 바이트 인스턴스여야 하며, 캐리지 리턴이나 줄 바꿈을 포함한 어떤 제어 문자도 내부나 끝에 포함해서는 안 됩니다. (이러한 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이 및 중간 응답 처리기가 수행해야 하는 구문 분석의 복잡성을 최소화하기 위한 것입니다.)

일반적으로 올바른 헤더가 클라이언트로 전송되도록 보장할 책임은 서버 또는 게이트웨이에 있습니다. 애플리케이션이 HTTP 또는 현재 적용되는 기타 관련 사양에서 요구하는 헤더를 생략하면 서버 또는 게이트웨이는 해당 헤더를 추가해야 합니다. 예를 들어 HTTP Date:Server: 헤더는 일반적으로 서버 또는 게이트웨이가 제공합니다. 그러나 애플리케이션이 동일한 이름의 값을 생성한 경우 게이트웨이는 해당 값을 덮어써서는 안 됩니다.

(서버 또는 게이트웨이 작성자를 위한 알림: HTTP 헤더 이름은 대소문자를 구분하지 않으므로 애플리케이션이 제공한 헤더를 검사할 때 이를 반드시 고려해야 합니다!)

애플리케이션과 미들웨어는 HTTP/1.1의 “hop-by-hop” 기능이나 헤더, HTTP/1.0의 이에 상응하는 기능, 또는 클라이언트와 웹 서버 간 연결의 지속성에 영향을 미치는 헤더를 사용해서는 안 됩니다. 이러한 기능은 실제 웹 서버만 사용할 수 있으며, 서버 또는 게이트웨이는 애플리케이션이 이를 전송하려는 시도를 치명적 오류로 간주해야 하며, 애플리케이션의 headers 구조에서 반환값으로 제공된 경우 오류를 발생시켜야 합니다. (“hop-by-hop” 기능과 헤더에 대한 자세한 내용은 아래의 Other HTTP Features 섹션을 참조하십시오.)

Python 버전 간 호환성 처리

Python 2.6/2.7과 Python 3.1+ 모두에서 실행되는 Web3 코드를 작성하려면 개발자가 주의를 기울여야 합니다. 일반적으로 Web3 사양은 Python 2의 str 타입과 Python 3의 bytes 타입 사이에 어느 정도 동등성이 있다고 가정합니다. 예를 들어 Python 2에서는 Web3 environ에 있는 값이 str 타입의 인스턴스이고, Python 3에서는 bytes 타입의 인스턴스입니다. Python 3의 bytes 타입에는 Python 2의 str 타입이 제공하는 모든 메서드가 없으며, 제공되는 일부 메서드도 Python 2의 str 타입과 다르게 동작합니다. 따라서 Web3 미들웨어와 애플리케이션이 Python 버전 전반에서 작동하도록 하려면 개발자는 다음을 수행해야 합니다:

  1. 텍스트 값과 바이트 값이 비교에서 동등하다고 가정하지 마십시오. 그렇게 하면 코드가 Python 2에서는 작동할 수 있지만 Python 3에서는 제대로 작동하지 않습니다. 예를 들어 somebytes == 'abc'와 같이 작성하지 마십시오. 이는 Python 2에서는 때때로 참이지만 Python 3에서는 결코 참이 아닙니다. Python 3에서는 바이트 시퀀스가 문자열과 같은 것으로 비교되지 않기 때문입니다. 대신 항상 바이트 값을 바이트 값과 비교하십시오. 예를 들어 “somebytes == b’abc‘“와 같이 하십시오. 이렇게 작성한 코드는 Python 2.6, 2.7 및 3.1과 호환되며 동일하게 작동합니다. b'abc' 앞에 있으면 Python 3에 해당 값이 리터럴 바이트 인스턴스임을 알립니다. Python 2에서는 하위 호환성을 위한 형식적 장치입니다.
  2. 인수가 바이트 인스턴스이기도 함을 보장하지 않은 채 바이트와 유사한 항목의 __contains__ 메서드를 직접 또는 간접적으로 사용하지 마십시오. 그렇게 하면 코드가 Python 2에서는 작동할 수 있지만 Python 3에서는 제대로 작동하지 않습니다. 예를 들어 Python 3에서는 'abc' in somebytes'TypeError를 발생시키지만, Python 2.6과 2.7에서는 True를 반환합니다. 그러나 b'abc' in somebytes는 두 버전에서 동일하게 작동합니다. Python 3.2에서는 바이트 타입이 __mod__ 구현을 얻을 수 있다는 소문이 있으므로 이 제한이 부분적으로 없어질 수 있습니다.
  3. __getitem__은 사용해서는 안 됩니다.
  4. 바이트의 인스턴스에서 (직접적이든 간접적이든) format 메서드나 __mod__ 메서드를 사용하려고 하지 마십시오. Python 2에서는 Python 3의 bytes와 동등하게 취급하는 str 형식이 이러한 메서드를 지원하지만, 실제 Python 3의 bytes 인스턴스는 이러한 메서드를 지원하지 않습니다. 이러한 메서드를 사용하면 코드가 Python 2에서는 작동하지만 Python 3에서는 작동하지 않습니다.
  5. 바이트 값과 문자열 값을 연결하려고 하지 마십시오. 이는 Python 2에서는 작동할 수 있지만 Python 3에서는 작동하지 않습니다. 예를 들어 'abc' + somebytes를 실행하면 Python 2에서는 작동하지만 Python 3에서는 TypeError가 발생합니다. 대신 항상 동일한 형식의 두 항목을 연결하는지 확인하십시오. 예를 들어 b'abc' + somebytes와 같이 하십시오.

Web3은 애플리케이션이 반환하는 모든 값과 같은 다른 위치에서도 바이트 값을 필요로 합니다.

요컨대 Python 2와 Python 3 간 Web3 애플리케이션 코드의 호환성을 보장하려면, Python 2에서는 환경의 CGI 및 서버 변수 값을 실제로는 더 많은 기능을 제공하는 API를 갖고 있더라도 Python 3의 bytes API를 사용하는 것처럼 취급하십시오. Web3 애플리케이션이 반환하는 모든 문자열 유사 값에도 동일하게 적용하십시오.

버퍼링 및 스트리밍

일반적으로 애플리케이션은 적당한 크기의 출력을 버퍼링한 후 한 번에 모두 전송함으로써 최상의 처리량을 달성합니다. 이는 기존 프레임워크에서 일반적으로 사용하는 방식입니다. 출력은 StringIO 또는 이와 유사한 객체에 버퍼링한 다음 응답 헤더와 함께 한 번에 모두 전송합니다.

Web3에서 이에 해당하는 방식은 애플리케이션이 응답 본문을 단일 문자열로 포함하는 단일 요소 body 이터러블(예: 리스트)을 간단히 반환하는 것입니다. 이는 텍스트가 메모리에 쉽게 들어가는 HTML 페이지를 렌더링하는 대부분의 애플리케이션 함수에 권장되는 방식입니다.

그러나 대용량 파일이나 HTTP 스트리밍의 특수한 용도(예: 멀티파트 “서버 푸시”)의 경우에는 애플리케이션이 더 작은 블록으로 출력을 제공해야 할 수 있습니다(예: 대용량 파일을 메모리에 로드하지 않기 위해). 응답의 일부를 생성하는 데 시간이 오래 걸릴 수 있지만, 그보다 앞선 응답 부분을 미리 전송하는 것이 유용한 경우도 있습니다.

이러한 경우 애플리케이션은 일반적으로 블록 단위로 출력을 생성하는 body 이터레이터(대개 제너레이터 이터레이터)를 반환합니다. 이러한 블록은 멀티파트 경계(“서버 푸시”의 경우)와 일치하도록 나눌 수도 있고, 시간이 오래 걸리는 작업(예: 디스크에 있는 파일의 다음 블록 읽기) 직전에 나눌 수도 있습니다.

Web3 서버, 게이트웨이 및 미들웨어는 어떤 블록의 전송도 지연해서는 안 되며, 해당 블록을 클라이언트에 완전히 전송하거나 애플리케이션이 다음 블록을 생성하는 동안에도 전송을 계속할 것임을 보장해야 합니다. 서버/게이트웨이 또는 미들웨어는 다음 세 가지 방법 중 하나로 이 보장을 제공할 수 있습니다.

  1. 애플리케이션에 제어를 반환하기 전에 전체 블록을 운영 체제에 전송하고(모든 O/S 버퍼를 플러시하도록 요청하면서) 반환하거나,
  2. 애플리케이션이 다음 블록을 생성하는 동안에도 해당 블록의 전송이 계속되도록 별도의 스레드를 사용하십시오.
  3. (미들웨어만 해당) 전체 블록을 상위 게이트웨이/서버로 전송하십시오.

이러한 보장을 제공함으로써 Web3은 애플리케이션이 출력 데이터의 임의의 지점에서 전송이 중단되지 않도록 보장할 수 있게 합니다. 이는 멀티파트 경계 사이의 데이터가 클라이언트에 완전히 전송되어야 하는 멀티파트 “서버 푸시” 스트리밍 등이 제대로 작동하는 데 매우 중요합니다.

유니코드 관련 문제

HTTP는 유니코드를 직접 지원하지 않으며 이 인터페이스도 마찬가지입니다. 모든 인코딩/디코딩은 애플리케이션이 처리해야 하며, 서버로 전달되거나 서버에서 전달되는 모든 값은 Python 3의 bytes형식 또는 Python 2의 str형식 인스턴스여야 하며, Python 2의 unicode나 Python 3의 str 객체여서는 안 됩니다.

이 사양에서 언급하는 모든 “바이트 인스턴스”는 반드시 다음 조건을 충족해야 합니다.

  • Python 2에서는 str 형식이어야 합니다.
  • Python 3에서는 bytes 형식이어야 합니다.

모든 “bytes 인스턴스”는 사용해서는 안 됩니다:

  • Python 2에서는 unicode 타입이어서는 안 됩니다.
  • Python 3에서는 str 타입이어서는 안 됩니다.

바이트 유사 객체가 필요한 곳에서 텍스트 유사 객체를 사용한 결과는 정의되지 않습니다.

Web3 애플리케이션이 상태 또는 응답 헤더로 반환하는 값은 인코딩과 관련하여 RFC 2616따라야 합니다. 즉, 반환되는 바이트에는 ISO-8859-1 문자의 문자 스트림이 포함되어야 하거나, 문자 스트림이 RFC 2047 MIME 인코딩을 사용해야 합니다.

네이티브 바이트 유사 타입이 없는 Python 플랫폼(예: IronPython 등)에서 바이트 데이터를 표현하는 데 일반적으로 텍스트 유사 문자열을 대신 사용하는 경우에는 “bytes 인스턴스”의 정의를 변경할 수 있습니다. 해당 “bytes 인스턴스”는 ISO-8859-1 인코딩으로 표현할 수 있는 코드 포인트(\u0000부터 \u00FF까지, 양 끝 포함)만 포함하는 네이티브 문자열이어야 합니다. 이러한 플랫폼에서 실행되는 애플리케이션이 다른 유니코드 문자나 코드 포인트를 포함하는 문자열을 제공하면 치명적인 오류가 발생합니다. 마찬가지로 해당 플랫폼의 서버와 게이트웨이는 다른 유니코드 문자를 포함하는 문자열을 애플리케이션에 제공해서는 안 됩니다.

HTTP 1.1 Expect/Continue

HTTP 1.1을 구현하는 서버와 게이트웨이는 HTTP 1.1의 “expect/continue” 메커니즘을 투명하게 지원해야 합니다. 이는 다음과 같은 여러 방법 중 하나로 수행할 수 있습니다.

  1. Expect: 100-continue 요청을 포함하는 요청에 즉시 “100 Continue” 응답을 보내고 정상적으로 진행합니다.
  2. 요청을 정상적으로 처리하되, 애플리케이션이 입력 스트림에서 처음 읽기를 시도할 때 “100 Continue” 응답을 전송하는 web3.input 스트림을 애플리케이션에 제공합니다. 그러면 읽기 요청은 클라이언트가 응답할 때까지 차단된 상태로 유지되어야 합니다.
  3. 클라이언트가 서버가 expect/continue를 지원하지 않는다고 판단하고 요청 본문을 자체적으로 전송할 때까지 기다립니다. (이는 최적의 방법이 아니며 권장되지 않습니다.)

이러한 동작 제한은 HTTP 1.0 요청이나 애플리케이션 객체를 대상으로 하지 않는 요청에는 적용되지 않는다는 점에 유의하십시오. HTTP 1.1 Expect/Continue에 대한 자세한 내용은 RFC 2616의 8.2.3절 및 10.1.1절을 참조하십시오.

기타 HTTP 기능

일반적으로 서버와 게이트웨이는 “모르는 척”하고 애플리케이션이 출력에 대해 완전한 제어권을 갖도록 해야 합니다. 애플리케이션 응답의 실질적인 의미를 변경하지 않는 변경만 수행해야 합니다. 애플리케이션 개발자는 추가 기능을 제공하는 미들웨어 구성 요소를 언제든지 추가할 수 있으므로, 서버 및 게이트웨이 개발자는 구현에 신중해야 합니다. 어떤 의미에서 서버는 자신을 HTTP “게이트웨이 서버”와 같은 것으로 간주하고, 애플리케이션은 HTTP “오리진 서버”로 간주해야 합니다. (이러한 용어의 정의는 RFC 2616의 1.3절을 참조하십시오.)

그러나 Web3 서버와 애플리케이션은 HTTP를 통해 통신하지 않으므로, RFC 2616에서 “hop-by-hop” 헤더라고 부르는 것은 Web3 내부 통신에 적용되지 않습니다. Web3 애플리케이션은 어떤 “hop-by-hop” 헤더도 생성해서는 안 되며, 그러한 헤더를 생성해야 하는 HTTP 기능을 사용하려고 시도해서도 안 되고, environ 딕셔너리에 들어오는 “hop-by-hop” 헤더의 내용에 의존해서도 안 됩니다. Web3 서버는 지원되는 모든 수신 “hop-by-hop” 헤더를 자체적으로 처리해야 하며, 여기에는 해당하는 경우 청크 인코딩을 포함하여 수신 Transfer-Encoding을 디코딩하는 작업 등이 있습니다.

이러한 원칙을 다양한 HTTP 기능에 적용하면, 서버가 If-None-MatchIf-Modified-Since 요청 헤더와 Last-ModifiedETag 응답 헤더를 통해 캐시 검증을 처리할 수 있음이 분명합니다. 그러나 이를 반드시 수행해야 하는 것은 아니며, 애플리케이션이 해당 기능을 지원하려면 자체적으로 캐시 검증을 수행해야 합니다. 서버/게이트웨이는 이러한 검증을 수행할 필요가 없기 때문입니다.

마찬가지로 서버는 애플리케이션의 응답을 다시 인코딩하거나 전송 인코딩할 수 있지만, 애플리케이션은 적절한 콘텐츠 인코딩을 자체적으로 사용해야 하며, 전송 인코딩을 적용해서는 안 됩니다. 클라이언트가 요청하고 애플리케이션이 바이트 범위를 기본적으로 지원하지 않는 경우, 서버는 애플리케이션 응답의 바이트 범위를 전송할 수 있습니다. 그러나 다시 말해, 원하는 경우 애플리케이션이 이 기능을 자체적으로 수행해야 합니다.

애플리케이션에 대한 이러한 제한이 모든 애플리케이션이 모든 HTTP 기능을 다시 구현해야 한다는 의미는 아니라는 점에 유의하십시오. 많은 HTTP 기능은 미들웨어 구성 요소를 통해 부분적으로 또는 완전히 구현할 수 있으므로, 서버와 애플리케이션 작성자가 동일한 기능을 반복해서 구현하지 않아도 됩니다.

스레드 지원

스레드 지원 여부 역시 서버에 따라 달라집니다. 여러 요청을 병렬로 처리할 수 있는 서버는 애플리케이션을 단일 스레드 방식으로 실행할 수 있는 옵션도 제공해야 하며, 이를 통해 스레드 안전하지 않은 애플리케이션이나 프레임워크도 해당 서버와 함께 사용할 수 있어야 합니다.

구현/애플리케이션 참고 사항

서버 확장 API

일부 서버 작성자는 애플리케이션 또는 프레임워크 작성자가 특수한 용도로 사용할 수 있는 더욱 고급 API를 공개하고자 할 수 있습니다. 예를 들어, mod_python을 기반으로 하는 게이트웨이는 Apache API의 일부를 Web3 확장으로 공개하고자 할 수 있습니다.

가장 단순한 경우에는 mod_python.some_api와 같은 environ변수를 정의하는 것만으로 충분합니다. 그러나 많은 경우 미들웨어가 존재할 가능성 때문에 이 작업이 어려워질 수 있습니다. 예를 들어, environ변수에서 찾을 수 있는 것과 동일한 HTTP 헤더에 대한 액세스를 제공하는 API는 미들웨어가 environ을 수정한 경우 다른 데이터를 반환할 수 있습니다.

일반적으로 Web3 기능의 일부를 복제하거나 대체하거나 우회하는 확장 API는 미들웨어 구성 요소와 호환되지 않을 위험이 있습니다. 서버/게이트웨이 개발자는 아무도 미들웨어를 사용하지 않을 것이라고 않는다고 가정해서는 안 됩니다. 일부 프레임워크 개발자는 특히 자신의 프레임워크가 다양한 종류의 미들웨어로서 거의 전적으로 작동하도록 구성하기 때문입니다.

따라서 최대한의 호환성을 제공하려면, 일부 Web3 기능을 대체하는 확장 API를 제공하는 서버와 게이트웨이는 해당 API를 대체하는 API 부분을 사용하여 호출되도록 설계해야 합니다. 예를 들어, HTTP 요청 헤더에 액세스하는 확장 API는 애플리케이션이 현재 environ을 전달하도록 요구해야 하며, 이를 통해 서버/게이트웨이는 API를 통해 액세스할 수 있는 HTTP 헤더가 미들웨어에 의해 변경되지 않았는지 확인할 수 있습니다. 확장 API가 HTTP 헤더의 내용에 관해 항상 environ과 일치한다고 보장할 수 없다면, 오류를 발생시키거나, 헤더 컬렉션 대신 None을 반환하거나, 해당 API에 적절한 방법을 사용하는 등 애플리케이션에 대한 서비스 제공을 거부해야 합니다.

이러한 지침은 파싱된 쿠키, 폼 변수, 세션 등의 정보를 environ에 추가하는 미들웨어에도 적용됩니다. 구체적으로 이러한 미들웨어는 값을 단순히 environ에 넣는 대신 environ을 대상으로 작동하는 함수로 해당 기능을 제공해야 합니다. 이렇게 하면 미들웨어가 URL 재작성이나 기타 environ수정 작업을 수행한 후에 environ에서 정보가 계산되도록 보장하는 데 도움이 됩니다.

이러한 “안전한 확장” 규칙은 서버/게이트웨이 개발자와 미들웨어 개발자 모두가 반드시 따라야 합니다. 이를 따르지 않으면 향후 미들웨어 개발자가 애플리케이션이 해당 확장을 사용하여 자신의 중재를 우회하지 못하도록 environ에서 모든 확장 API를 삭제해야 하는 상황이 발생할 수 있기 때문입니다!

애플리케이션 구성

이 사양은 서버가 호출할 애플리케이션을 선택하거나 가져오는 방법을 정의하지 않습니다. 이러한 구성 옵션 및 기타 구성 옵션은 서버에 따라 크게 달라지는 사항입니다. 서버/게이트웨이 작성자는 특정 애플리케이션 객체를 실행하도록 서버를 구성하는 방법과 사용할 옵션(예: 스레딩 옵션)을 문서화해야 합니다.

반면 프레임워크 작성자는 자신의 프레임워크 기능을 래핑하는 애플리케이션 객체를 생성하는 방법을 문서화해야 합니다. 서버와 애플리케이션 프레임워크를 모두 선택한 사용자는 두 요소를 서로 연결해야 합니다. 그러나 프레임워크와 서버가 공통 인터페이스를 가지므로, 이는 새로운 서버/프레임워크 조합마다 상당한 엔지니어링 작업을 수행하는 일이 아니라 단순한 기계적 작업이어야 합니다.

마지막으로 일부 애플리케이션, 프레임워크 및 미들웨어는 간단한 문자열 구성 옵션을 받기 위해 environ딕셔너리를 사용하고자 할 수 있습니다. 서버와 게이트웨이는 애플리케이션 배포자가 environ에 배치할 이름-값 쌍을 지정할 수 있도록 하여 이를 지원해야 합니다. 가장 단순한 경우에는 운영 체제가 제공하는 모든 환경 변수를 os.environ에서 environ딕셔너리로 복사하는 것만으로 이러한 지원을 구성할 수 있습니다. 원칙적으로 배포자는 이러한 변수를 서버 외부에서 구성할 수 있으며, CGI의 경우 서버 구성 파일을 통해 설정할 수 있기 때문입니다.

모든 서버가 이러한 변수의 쉬운 구성을 지원하는 것은 아니므로, 애플리케이션은 이러한 필수 변수의 수를 최소화하도록 노력해야 합니다. 물론 최악의 경우에도 애플리케이션을 배포하는 사람은 필요한 구성 값을 제공하는 스크립트를 작성할 수 있습니다:

from the_app import application

def new_app(environ):
    environ['the_app.configval1'] = b'something'
    return application(environ)

그러나 기존 애플리케이션과 프레임워크의 대부분은 애플리케이션 또는 프레임워크별 구성 파일의 위치를 나타내기 위해 environ에서 단일 구성 값만 필요로 할 것입니다. (물론 애플리케이션은 호출할 때마다 구성을 다시 읽지 않도록 해당 구성을 캐시해야 합니다.)

URL 재구성

애플리케이션이 요청의 전체 URL(바이트 객체)을 재구성하려는 경우 다음 알고리즘을 사용하여 재구성할 수 있습니다.:

host = environ.get('HTTP_HOST')

scheme = environ['web3.url_scheme']
port = environ['SERVER_PORT']
query = environ['QUERY_STRING']

url = scheme + b'://'

if host:
    url += host
else:
    url += environ['SERVER_NAME']

    if scheme == b'https':
        if port != b'443':
           url += b':' + port
    else:
        if port != b'80':
           url += b':' + port

if 'web3.script_name' in url:
    url += url_quote(environ['web3.script_name'])
else:
    url += environ['SCRIPT_NAME']
if 'web3.path_info' in environ:
    url += url_quote(environ['web3.path_info'])
else:
    url += environ['PATH_INFO']
if query:
    url += b'?' + query

이렇게 재구성된 URL은 클라이언트가 요청한 URI와 정확히 같지 않을 수 있다는 점에 유의하십시오. 예를 들어 서버 재작성 규칙이 클라이언트가 원래 요청한 URL을 수정하여 정규 형식으로 만들었을 수 있습니다.

미해결 질문

  • file_wrapper 대체 현재 이 부분에는 아무것도 명시되어 있지 않지만, 인밴드 시그널링의 기존 시스템이 처리 과정에서 미들웨어가 응답이 파일 래퍼인지 알아낼 방법을 제공하지 않는다면 제대로 작동하지 않는다는 점은 분명합니다.

논쟁점

다음은 이 사양과 관련하여 발생할 수 있는 잠재적인 논쟁점입니다.

WSGI 1.0 호환성

WSGI 1.0 사양을 사용하여 작성된 구성 요소는 이 사양을 사용하여 작성된 구성 요소와 투명하게 상호 운용되지 않습니다. 이는 이 제안의 목표와 WSGI 1.0의 목표가 직접적으로 일치하지 않기 때문입니다.

WSGI 1.0은 Python 2.2부터 2.7 사이의 버전에 대해 사양 수준의 하위 호환성을 제공해야 합니다. 그러나 이 사양은 비교적 최신 버전의 Python 2(2.6 및 2.7)와 비교적 최신 버전의 Python 3(3.1) 사이의 호환성을 제공하기 위해 Python 2.5 이하와의 호환성을 포기합니다.

현재 WSGI 1.0 사양을 사용하여 Python 2와 Python 3 모두에서 안정적으로 작동하는 구성 요소를 작성하는 것은 불가능합니다. 사양이 environ의 CGI 및 서버 변수 값과 start_response를 통해 반환되는 값이 Python 2 문자열 API를 사용하여 접근할 수 있는 바이트 시퀀스를 나타낸다고 암묵적으로 전제하기 때문입니다. 모든 Python 2 버전에서 이러한 데이터 형식이 바이트를 나타내는 합리적인 방법이었고, WSGI 1.0은 Python 3이 존재하기 전에 고안되었기 때문에 그러한 것을 전제합니다.

Python 3의 str 형식은 Python 2의 str 형식이 제공하는 전체 API를 지원하지만, Python 3의 str 형식은 바이트 시퀀스를 나타내지 않고 대신 텍스트를 나타냅니다. 따라서 이를 사용하여 environ 값을 나타내려면 environ 바이트 시퀀스를 어떤 인코딩을 통해 텍스트로 디코딩해야 합니다. 디코딩을 터널링 메커니즘으로 사용하는 것 이외의 의미를 갖도록 바이트를 텍스트로 디코딩하려면, WSGI의 범위를 서버와 게이트웨이의 디코딩 정책 및 메커니즘에 대한 지식까지 포함하도록 확장해야 하므로 그렇게 할 수 없습니다. WSGI 1.0은 인코딩과 디코딩을 전혀 다루지 않았습니다. WSGI 1.0은 허용 가능한 전송 값에 관해 설명하고 다양한 값을 하나의 인코딩 또는 다른 인코딩으로 디코딩하는 것이 가장 적합할 수 있다고 제안했지만, 서버가 그 전에 어떤 디코딩이든 perform을 수행하도록 요구한 적은 없습니다.

Python 3에는 바이트를 대신 나타내는 데 사용할 수 있는 문자열 유사 형식이 없습니다. 대신 bytes 형식이 있습니다. bytes 형식은 Python 3.1 이상에서 Python 2의 str 형식과 상당히 유사하게 작동하지만, str.__mod__와 동등한 동작 및 해당 이터레이션 프로토콜이 없으며, 포함 관계, 시퀀스 처리 및 동등성 비교도 다릅니다.

어느 경우든 Python 3에는 Python 2의 str 형식과 똑같이 동작하는 형식이 없으며, 적합한 형식을 만들 수 있도록 해 주는 “String ABC”라는 것이 존재하지 않으므로 그러한 형식을 만드는 방법도 존재하지 않습니다. 이러한 설계상의 비호환성으로 인해 기존 WSGI 1.0 서버, 미들웨어 및 애플리케이션은 2to3를 통해 실행한 후에도 Python 3에서 작동하지 않습니다.

WSGI 애플리케이션을 Python 2와 Python 3 모두에서 실행할 수 있도록 WSGI 사양을 갱신하는 것에 관한 기존 Web-SIG 논의는 Python 2의 str 형식(바이트 시퀀스를 나타냄)과 Python 3의 str 형식(텍스트를 나타냄) 사이에 사양 수준의 동등성을 만드는 방향으로 진행되는 경향이 있습니다. 이러한 형식들이 서로 다른 역할을 하므로 그러한 동등성은 여러 영역에서 긴장된 문제가 됩니다. Python 3의 bytes 형식 API와 Python 2의 str 형식 API의 하위 집합 사이에는 더 직접적이라고 할 수 있는 동등성이 존재합니다. 이 사양은 이러한 하위 집합 동등성을 활용합니다.

한편 Python 2와 Python 3 간의 호환성 문제를 제외하더라도, Web-SIG의 여러 논의에서 지적했듯이 WSGI 1.0 사양은 지나치게 일반적이며, 구현 복잡성을 대가로 .write를 통한 비동기 애플리케이션 지원을 제공합니다. 이 사양은 WSGI 1.0과 Python 3 사이의 근본적인 비호환성을 자연스러운 분기점으로 활용하여, 비동기 애플리케이션에 대한 특화된 지원을 변경함으로써 복잡성이 줄어든 사양을 만듭니다.

오래된 WSGI 1.0 애플리케이션에 하위 호환성을 제공하여 Web3 스택에서 실행할 수 있도록, 기존 WSGI 1.0 애플리케이션의 “앞에서” 사용할 수 있는 Web3 미들웨어가 만들어져 해당 애플리케이션을 Web3 스택에서 실행할 수 있게 될 것으로 예상합니다. 이 미들웨어는 Python 3에서 실행될 때 Python 3의 str 유형과 HTTP 요청이 나타내는 바이트 값 사이의 동등성을 확립하고, 이에 수반되는 모든 인코딩 추측(또는 구성)을 수행해야 합니다.

Note

이러한 미들웨어는 향후 Python 3의 str과 HTTP 바이트 값 사이의 동등성을 설정하는 대신 아직 만들어지지 않은 “ebytes” 타입(일명 “bytes-with-benefits”)을 사용할 수도 있으며, 특히 String ABC 제안이 Python 코어에 수용되어 구현되는 경우에는 더욱 그렇습니다.

반대로 Python 2 플랫폼에서 Web3 애플리케이션을 WSGI 1.0 스택 뒤에서 실행할 수 있도록 하는 WSGI 1.0 미들웨어가 만들어질 것으로 예상합니다.

바이트로서의 Environ 및 응답 값

일반적인 미들웨어 및 애플리케이션 작성자는 바이트를 환경 값과 응답 값으로 사용하는 것이 불편하다고 여길 수 있습니다. 특히 bytes는 Python 3처럼 두 유형이 서로 다른 플랫폼에서 문자열과 동일한 API를 제공하지 않으므로, ('%s' % bytes_val) 또는 bytes_val.format('123')와 같은 일반적인 문자열 서식 지정 함수를 사용할 수 없습니다. 마찬가지로 이러한 플랫폼에서는 바이트를 텍스트와 상호 교환하여 사용하기 위한 표준 라이브러리의 HTTP 관련 API 지원이 불완전할 수 있습니다. 바이트가 불편하거나 라이브러리 API와 호환되지 않는 곳에서는 미들웨어 및 애플리케이션 작성자가 해당 바이트를 명시적으로 텍스트로 디코딩해야 합니다. 이는 미들웨어 작성자에게 특히 불편합니다. 환경 값을 문자열로 다루려면 암시된 인코딩에서 값을 디코딩해야 하며, environ 값을 변경해야 하는 경우에는 environ에 넣기 전에 해당 값을 바이트 스트림으로 인코딩해야 합니다. 명세에서 바이트를 environ 값으로 사용하는 것이 일반적인 개발자에게는 불편할 수 있지만, 이는 여러 이점을 제공합니다.

HTTP는 근본적으로 바이트 지향 프로토콜이므로, 애플리케이션에 HTTP 및 서버 값을 나타내는 데 바이트 유형을 사용하는 것이 현실과 가장 잘 부합합니다. environ 값이 문자열이어야 한다면 각 서버는 HTTP 환경에서 제공되는 다양한 값의 인코딩을 추측하기 위해 휴리스틱을 사용해야 합니다. 모든 값을 문자열로 사용하면 일반적인 미들웨어 작성자의 편의성이 높아질 수 있지만, 어떤 값을 의미 있는 비서로게이트 문자열로 디코딩할 수 없을 때 모호함과 혼란도 초래합니다.

environ 값으로 바이트를 사용하면 참여 서버에 인코딩 구성 매개변수를 알려야 한다고 명세에서 규정할 가능성을 피할 수 있습니다. environ 값을 문자열로 취급하여 바이트에서 디코딩해야 한다면, 애플리케이션 배포자가 제시하는 정책상의 단서로서 구성 매개변수가 결국 필요해질 수 있습니다. 이러한 정책은 다양한 상황에서 적절한 디코딩 전략을 추측하는 데 사용되어, 특정 애플리케이션 인코딩 정책을 시행할 책임을 사실상 서버에 부과하게 됩니다. 서버가 둘 이상의 애플리케이션을 제공해야 한다면 이러한 구성은 빠르게 복잡해질 것입니다. 또한 많은 정책은 선언적으로 표현할 수 없을 것입니다.

실제로 HTTP는 복잡하고 레거시 문제가 많은 프로토콜이며, 의미를 파악하려면 복잡한 휴리스틱 집합이 필요합니다. 이 프로토콜이 이러한 복잡성으로부터 우리를 보호하도록 할 수 있다면 좋겠지만, 현실에 상응하는 수준의 제어권을 애플리케이션 작성자에게 제공하면서 안정적으로 그렇게 할 수는 없습니다. Python 애플리케이션은 레거시 휴리스틱으로 분석해야 할 뿐만 아니라 기존의 어떤 HTTP 명세에도 부합하지 않는 환경에 포함된 데이터를 자주 처리해야 합니다. 이러한 상황은 불쾌하지만 정기적으로 발생하므로 애플리케이션 개발자에게서 이를 숨기는 것은 불가능하고 바람직하지도 않습니다. HTTP 명세 위반이 감지되었을 때 적절한 조치를 결정할 수 있는 사람은 애플리케이션 개발자뿐이기 때문입니다.

일부 사람들은 environ 으로 바이트와 문자열 값을 혼합하여 사용하자고 주장했습니다. 이 제안은 그러한 전략을 피합니다. environ 값에 바이트만 사용하면 이 명세 전체를 머릿속에 온전히 담을 수 있으므로, 어떤 값이 문자열이고 어떤 값이 바이트인지 추측할 필요가 없습니다.

모든 environ 값이 문자열이라면 이 프로토콜 역시 개발자의 머릿속에 들어갈 수 있겠지만, 이 명세는 그러한 전략을 사용하지 않습니다. 이는 바이트 사용과 관련하여 가장 큰 논쟁점이 될 가능성이 높습니다. 바이트를 옹호하자면, 개발자는 계약 자체가 최적이 아니더라도 일관된 계약을 갖는 프로토콜을 선호하는 경우가 많습니다. 값에 서로게이트가 포함되어 애플리케이션의 I/O 경계를 이미 넘어선 후 문제가 발생할 때까지 인코딩 문제를 개발자에게 숨긴다면, “여기 바이트가 있으니 디코딩하십시오”라고 훨씬 일찍 문제를 제시하는 경우보다 애플리케이션이 만든 가정을 수정하기 위해 훨씬 더 많은 작업을 해야 합니다. 이는 “바이트는 불편하다”는 가정에 대한 반론이기도 합니다. 가장자리 사례에 관심이 없는 일반적인 애플리케이션 개발자에게 바이트를 제시하는 것은 불편할 수 있지만, 복잡하고 지저분한 상황을 처리해야 하는 애플리케이션 개발자에게는 바이트가 매우 편리합니다. 바이트를 사용하면 책임을 명확히 분리하면서 적절한 수준의 제어권을 확보할 수 있기 때문입니다.

프로토콜이 바이트를 사용한다면, environ 및 반환 값 내에서 바이트만을 사용하여 작업하는 일을 더 편리하게 해 주는 라이브러리가 만들어질 것으로 간주합니다. 예를 들어 “WebOb” 및 “Werkzeug”라는 WSGI 1.0 라이브러리의 유사체가 해당합니다. 이러한 라이브러리는 편의성과 제어 사이의 간극을 메워, 사양을 단순하고 일관되게 유지하면서도 일반 작성자가 Web3 미들웨어 및 애플리케이션 구성 요소를 편리하게 만들 수 있도록 합니다. 이는 인코딩 정책을 프로토콜에 내장하는 것에 대한 합리적인 대안으로 보입니다. 이러한 라이브러리를 프로토콜과 독립적으로 여러 개 만들 수 있고, 애플리케이션 개발자는 특정 작업에 적합한 수준의 제어와 편의성을 제공하는 라이브러리를 선택할 수 있기 때문입니다.

모든 바이트를 사용하는 것에 대한 몇 가지 대안은 다음과 같습니다.

  • CGI 및 서버 environ 값을 나타내는 모든 값을 손실 없는 latin-1 인코딩을 사용하여 문자열로 디코딩합니다. 디코딩할 수 없는 바이트를 결과 문자열 안에 몰래 포함합니다.
  • surrogateescape 오류 처리기와 함께 utf-8 인코딩을 사용하여 모든 CGI 및 서버 environ 값을 문자열로 인코딩합니다. 이는 기존의 어떤 Python 2에서도 작동하지 않습니다.
  • 일반적인 사용 방식에 따라 일부 값은 바이트로, 다른 값은 문자열로 인코딩합니다.

애플리케이션은 web3.inputCONTENT_LENGTH 이후까지 읽을 수 있어야 합니다

[5]에서 Graham Dumpleton은 wsgi.input이 데이터가 더 이상 없음을 나타내는 표식으로 빈 문자열을 반환하도록 요구해야 하며, 애플리케이션이 CONTENT_LENGTH에 지정된 바이트 수를 넘어서 읽을 수 있어야 하고, 빈 문자열만 EOF 표식으로 사용해야 한다고 주장합니다. WSGI는 애플리케이션이 “올바르게 동작하여 CONTENT_LENGTH에 지정된 모든 데이터를 읽은 후 해당 데이터를 처리하고 응답을 반환한다는 사실에 의존합니다. 동일한 소켓 연결을 이후 요청에 사용할 수도 있습니다.” Graham은 WSGI 어댑터가 원시 소켓 연결을 래핑하도록 요구하기를 원합니다. “이 래퍼 객체는 읽힌 데이터의 양을 계산해야 하며, 데이터 양이 CONTENT_LENGTH에 정의된 값에 도달하면 이후의 모든 읽기는 대신 빈 문자열을 반환해야 합니다.” 이는 청크 인코딩 및 입력 필터를 지원하는 데 유용할 수 있습니다.

web3.input의 알 수 없는 길이

environ['web3.input']에 콘텐츠가 있지만 콘텐츠 길이를 알 수 없음을 나타내는 문서화된 방법은 없습니다.

web3.inputread()는 크기 없는 호출 규약을 지원해야 합니다

Graham Dumpleton은 [5]에서 wsgi.inputread() 메서드가 인자 없이 호출 가능 객체여야 하며, 그 결과가 “사용 가능한 모든 요청 콘텐츠”여야 한다고 주장합니다. 논의가 필요합니다.

Armin의 의견: 구현에서 이를 요구하도록 사양을 변경했습니다. 과거에 이미 이 문제로 너무 많은 어려움을 겪었습니다. 그래도 논의의 여지는 열어 둡니다.

입력 필터는 environ의 CONTENT_LENGTH를 -1로 설정해야 합니다

[5]에서 Graham Dumpleton은 입력 필터가 입력을 변경했음을 나타내기 위해 environ['CONTENT_LENGTH']를 -1로 설정할 수 있다고 제안합니다.

두 항목 튜플의 리터럴 리스트인 headers

애플리케이션이 두 항목 튜플의 리터럴 리스트인 headers 구조를 반환하도록 하는 이유는 무엇입니까? headers가 스택 위로 이동하는 동안에도 반복 가능성을 유지해야 한다고 생각하지만, 항상 제자리에서 이를 변경할 수 있어야 한다고는 생각하지 않습니다. 이 요구 사항을 완화할 수 있습니까?

Armin의 의견: 강력히 찬성합니다.

미들웨어가 블로킹하지 않아야 한다는 요구 사항 제거

다음 요구 사항이 제거되었습니다. “미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리면서 반복을 차단해서는 안 됩니다. 미들웨어가 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 축적해야 한다면, 빈 문자열을 yield해야 합니다.” 이 요구 사항은 비동기 애플리케이션과 서버를 지원하기 위해 존재했습니다(PEP 333의 “미들웨어의 블록 경계 처리”를 참조하십시오). 이제 비동기 애플리케이션은 web3.async 가능 프로토콜을 통해 명시적으로 처리됩니다(Web3 애플리케이션 호출 가능 객체 자체가 호출 가능 객체를 반환할 수 있습니다).

web3.script_nameweb3.path_info

이 사양에 따라 원본 서버는 이러한 값을 환경에 배치해야 합니다. SCRIPT_NAMEPATH_INFO와 달리, 이러한 값은 요청 URI에서 파생된 원래의 URL-encoded 변형이어야 합니다. 이러한 값이 원래 어떻게 계산되어야 하는지, 그리고 서버가 URL 재작성을 수행하는 경우 그 값이 무엇이어야 하는지 알아내야 할 것 같습니다.

긴 응답 헤더

Web-SIG의 Bob Brewer 메모 [6]:

각 header_value에는 중간에 포함되어 있거나 끝에 있더라도 캐리지 리턴이나 줄 바꿈을 포함한 어떤 제어 문자도 포함되어서는 안 됩니다. (이러한 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이 및 중간 응답 처리기가 수행해야 하는 구문 분석의 복잡성을 최소화하기 위한 것입니다.) (PEP 333)

이는 이해할 수 있지만, HTTP 헤더는 (대부분) *TEXT로 정의되며, “*TEXT의 ‘단어’에는 RFC 2047의 규칙에 따라 인코딩된 경우에만 ISO-8859-1 이외의 문자 집합에 속하는 문자가 포함될 수 있습니다.” [2] 또한 RFC 2047은 “‘encoded-word’는 75자보다 길 수 없습니다… 75자의 ‘encoded-word’에 들어갈 수 있는 것보다 더 많은 텍스트를 인코딩해야 하는 경우, 여러 개의 ‘encoded-word’(CRLF SPACE로 구분됨)를 사용할 수 있습니다.” [3] 이는 HTTP 헤더 접기 규칙도 충족합니다. “각 추가 줄 앞에 하나 이상의 SP 또는 HT를 넣으면 헤더 필드를 여러 줄에 걸쳐 확장할 수 있습니다.” (PEP 333)

따라서 제가 HTTP를 해석하는 방식에 따르면, 어딘가의 코드가 길고 인코딩된 응답 헤더 값에 줄 바꿈을 삽입해야 합니다. 세 가지 선택지가 있다고 봅니다.

  1. 현재 상태를 유지하고, ISO-8859-1 문자 집합에 속하지 않으며 75자를 초과하는 단어가 포함된 응답 헤더 값을 허용하지 않습니다.
  2. WSGI 응답 헤더에 줄 바꿈 문자를 허용합니다.
  3. WSGI 서버가 HTTP를 통해 값을 전송하기 전에 인코딩하고 접도록 요구하거나 강력히 권고합니다.

요청 트레일러 및 청크 분할 전송 인코딩

요청 콘텐츠에 청크 전송 인코딩(chunked transfer encoding)을 사용할 때, RFC는 요청 트레일러(request trailer)가 존재하는 것을 허용합니다. 이는 요청 헤더와 비슷하지만 마지막 널 데이터 청크 뒤에 옵니다. 이 트레일러들은 청크 데이터 스트림이 유한 길이이고 모두 읽혀 들어온 경우에만 사용할 수 있습니다. WSGI와 Web3 모두 현재 이를 지원하지 않습니다.

참고 자료