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

Python 개선 제안 한국어 번역

PEP 333 – Python 웹 서버 게이트웨이 인터페이스 v1.0

Author:
Phillip J. Eby <pje at telecommunity.com>
Discussions-To:
Web-SIG list
Status:
Final
Type:
Informational
Created:
07-Dec-2003
Post-History:
07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004, 27-Sep-2010
Superseded-By:
3333

Table of Contents

번역·라이선스 안내

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

서문

참고: Python 3.x를 지원하고 커뮤니티의 정오표, 추가 사항 및 명확한 설명을 포함하는 이 사양의 업데이트된 버전은 대신 PEP 3333을 참조하십시오.

초록

이 문서는 웹 서버와 Python 웹 애플리케이션 또는 프레임워크 간의 제안된 표준 인터페이스를 규정하여, 다양한 웹 서버에서 웹 애플리케이션의 이식성을 높이는 것을 목적으로 합니다.

근거 및 목표

현재 Python에는 Zope, Quixote, Webware, SkunkWeb, PSO 및 Twisted Web과 같은 매우 다양한 웹 애플리케이션 프레임워크가 있으며, 이는 몇 가지 예에 불과합니다 [1]. 이렇게 선택지가 다양하다는 점은 새로운 Python 사용자에게 문제가 될 수 있습니다. 일반적으로 웹 프레임워크의 선택이 사용할 수 있는 웹 서버의 선택을 제한하고, 그 반대도 마찬가지이기 때문입니다.

이와 대조적으로 Java에는 그만큼 많은 웹 애플리케이션 프레임워크가 제공되지만, Java의 “servlet” API를 사용하면 어떤 Java 웹 애플리케이션 프레임워크로 작성된 애플리케이션이든 servlet API를 지원하는 모든 웹 서버에서 실행할 수 있습니다.

Python용 웹 서버에서 이러한 API를 사용할 수 있고 널리 사용하게 된다면, 해당 서버가 Python으로 작성되었든(예: Medusa), Python을 내장하든(예: mod_python), 게이트웨이 프로토콜을 통해 Python을 호출하든(예: CGI, FastCGI 등), 프레임워크의 선택과 웹 서버의 선택을 분리할 수 있습니다. 이를 통해 사용자는 자신에게 적합한 조합을 자유롭게 선택하고, 프레임워크 및 서버 개발자는 자신이 선호하는 전문 분야에 집중할 수 있습니다.

따라서 이 PEP는 웹 서버와 웹 애플리케이션 또는 프레임워크 간의 단순하고 범용적인 인터페이스인 Python 웹 서버 게이트웨이 인터페이스(WSGI)를 제안합니다.

그러나 WSGI 사양이 존재한다는 사실만으로는 Python 웹 애플리케이션을 위한 서버와 프레임워크의 현재 상태를 해결할 수 없습니다. 실질적인 효과가 있으려면 서버 및 프레임워크 작성자와 유지 관리자가 실제로 WSGI를 구현해야 합니다.

그러나 기존 서버나 프레임워크 중 WSGI를 지원하는 것이 없으므로, WSGI 지원을 구현하는 작성자에게 즉각적인 보상은 거의 없습니다. 따라서 WSGI는 쉽게 구현할 수 있어야 하며, 작성자가 인터페이스에 처음 투자하는 비용을 합리적으로 낮출 수 있어야 합니다.

따라서 인터페이스의 서버 측과 프레임워크 측 모두에서 구현이 단순해야 한다는 점은 WSGI 인터페이스의 유용성에 절대적으로 중요하며, 그러므로 모든 설계 결정의 주요 기준이 됩니다.

그러나 프레임워크 작성자가 쉽게 구현할 수 있어야 한다는 것과 웹 애플리케이션 작성자가 쉽게 사용할 수 있어야 한다는 것은 같은 의미가 아니라는 점에 유의하십시오. WSGI는 프레임워크 작성자에게 절대적으로 “꾸밈없는” 인터페이스를 제공합니다. 응답 객체와 쿠키 처리 같은 부가 기능은 기존 프레임워크가 이러한 문제를 처리하는 데 방해만 되기 때문입니다. 다시 말해 WSGI의 목표는 기존 서버와 애플리케이션 또는 프레임워크를 쉽게 연결하도록 돕는 것이지, 새로운 웹 프레임워크를 만드는 것이 아닙니다.

또한 이 목표로 인해 WSGI는 Python의 배포된 버전에서 이미 제공되지 않는 어떤 것도 요구할 수 없습니다. 따라서 이 사양은 새로운 표준 라이브러리 모듈을 제안하거나 요구하지 않으며, WSGI의 어떤 부분도 2.2.2보다 높은 Python 버전을 요구하지 않습니다. (다만 향후 Python 버전의 표준 라이브러리에서 제공되는 웹 서버에 이 인터페이스 지원을 포함하는 것은 좋은 생각일 것입니다.)

기존 및 향후 프레임워크와 서버에서 쉽게 구현할 수 있어야 하는 것에 더해, 요청 전처리기, 응답 후처리기 및 기타 WSGI 기반 “미들웨어” 구성 요소도 쉽게 만들 수 있어야 합니다. 이러한 구성 요소는 자신을 포함하는 서버에는 애플리케이션처럼 보이면서, 자신이 포함하는 애플리케이션에는 서버로 동작합니다.

미들웨어가 단순하면서도 견고하고 WSGI가 서버와 프레임워크에서 널리 사용된다면, 느슨하게 결합된 WSGI 미들웨어 구성 요소로 이루어진 완전히 새로운 종류의 Python 웹 애플리케이션 프레임워크가 가능해집니다. 실제로 기존 프레임워크 작성자는 프레임워크의 기존 서비스를 이러한 방식으로 제공하도록 리팩터링하여, 단일체형 프레임워크라기보다는 WSGI와 함께 사용되는 라이브러리에 가까운 형태를 선택할 수도 있습니다. 그러면 애플리케이션 개발자는 하나의 프레임워크가 가진 모든 장단점을 받아들여야 하는 대신, 특정 기능에 가장 적합한 구성 요소를 선택할 수 있습니다.

물론 이 글을 작성하는 현재로서는 그날이 분명 아직 상당히 멀리 있습니다. 그때까지 WSGI가 어떤 프레임워크든 어떤 서버에서든 사용할 수 있도록 하는 것은 충분히 달성할 만한 단기 목표입니다.

마지막으로, 현재 버전의 WSGI는 웹 서버나 서버 게이트웨이에서 사용할 애플리케이션을 “배포”하기 위한 특정 메커니즘을 규정하지 않는다는 점을 언급해야 합니다. 현재로서는 이는 필연적으로 서버 또는 게이트웨이의 구현에 따라 정의됩니다. 충분한 수의 서버와 프레임워크가 WSGI를 구현하여 다양한 배포 요구 사항에 대한 실제 경험을 제공한 후에는, WSGI 서버와 애플리케이션 프레임워크를 위한 배포 표준을 설명하는 또 다른 PEP를 만드는 것이 타당할 수 있습니다.

사양 개요

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

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

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

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

애플리케이션 객체는 두 인자를 받는 호출 가능 객체일 뿐입니다. “객체”라는 용어는 실제 객체 인스턴스를 요구하는 것으로 오해해서는 안 됩니다. 함수, 메서드, 클래스 또는 __call__ 메서드를 포함하는 인스턴스는 모두 애플리케이션 객체로 사용할 수 있습니다. 거의 모든 서버/게이트웨이(CGI 제외)는 이러한 반복 요청을 수행하므로, 애플리케이션 객체는 두 번 이상 호출될 수 있어야 합니다.

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

다음은 두 가지 애플리케이션 객체의 예입니다. 하나는 함수이고 다른 하나는 클래스입니다.:

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


class AppClass:
    """Produce the same output, but using a class

    (Note: 'AppClass' is the "application" here, so calling it
    returns an instance of 'AppClass', which is then the iterable
    return value of the "application callable" as required by
    the spec.

    If we wanted to use *instances* of 'AppClass' as application
    objects instead, we would have to implement a '__call__'
    method, which would be invoked to execute the application,
    and we would need to create an instance for use by the
    server or gateway.
    """

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

서버/게이트웨이 측

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

import os, sys

def run_with_cgi(application):

    environ = dict(os.environ.items())
    environ['wsgi.input']        = sys.stdin
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True

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

    headers_set = []
    headers_sent = []

    def write(data):
        if not headers_set:
             raise AssertionError("write() before start_response()")

        elif not headers_sent:
             # Before the first output, send the stored headers
             status, response_headers = headers_sent[:] = headers_set
             sys.stdout.write('Status: %s\r\n' % status)
             for header in response_headers:
                 sys.stdout.write('%s: %s\r\n' % header)
             sys.stdout.write('\r\n')

        sys.stdout.write(data)
        sys.stdout.flush()

    def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[0], exc_info[1], exc_info[2]
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        headers_set[:] = [status, response_headers]
        return write

    result = application(environ, start_response)
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result, 'close'):
            result.close()

미들웨어: 양쪽 측면에서 작동하는 구성 요소

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

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

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

대부분의 경우 미들웨어는 WSGI의 서버 측과 애플리케이션 측 모두의 제한 사항과 요구 사항을 준수해야 합니다. 그러나 일부 경우에는 미들웨어에 대한 요구 사항이 “순수한” 서버나 애플리케이션에 대한 요구 사항보다 더 엄격하며, 이러한 사항은 사양에서 언급합니다.

여기에는 Joe Strout의 piglatin.py를 사용하여 text/plain 응답을 피그 라틴으로 변환하는 미들웨어 구성 요소의 (익살스러운) 예가 있습니다. (참고: “실제” 미들웨어 구성 요소라면 콘텐츠 유형을 확인하는 더 견고한 방법을 사용하고 콘텐츠 인코딩도 확인해야 할 것입니다. 또한 이 간단한 예에서는 단어가 블록 경계에 걸쳐 분할될 가능성을 무시합니다.)

from piglatin import piglatin

class LatinIter:

    """Transform iterated output to piglatin, if it's okay to do so

    Note that the "okayness" can change until the application yields
    its first non-empty string, so 'transform_ok' has to be a mutable
    truth value.
    """

    def __init__(self, result, transform_ok):
        if hasattr(result, 'close'):
            self.close = result.close
        self._next = iter(result).next
        self.transform_ok = transform_ok

    def __iter__(self):
        return self

    def next(self):
        if self.transform_ok:
            return piglatin(self._next())
        else:
            return self._next()

class Latinator:

    # by default, don't transform output
    transform = False

    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):

        transform_ok = []

        def start_latin(status, response_headers, exc_info=None):

            # Reset ok flag, in case this is a repeat call
            del transform_ok[:]

            for name, value in response_headers:
                if name.lower() == 'content-type' and value == 'text/plain':
                    transform_ok.append(True)
                    # Strip content-length if present, else it'll be wrong
                    response_headers = [(name, value)
                        for name, value in response_headers
                            if name.lower() != 'content-length'
                    ]
                    break

            write = start_response(status, response_headers, exc_info)

            if transform_ok:
                def write_latin(data):
                    write(piglatin(data))
                return write_latin
            else:
                return write

        return LatinIter(self.application(environ, start_latin), transform_ok)


# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
run_with_cgi(Latinator(foo_app))

사양 세부 사항

애플리케이션 객체는 두 개의 위치 인자를 받아야 합니다. 설명을 위해 이 인자들의 이름을 environstart_response로 정했지만, 반드시 이러한 이름을 사용할 필요는 없습니다. 서버나 게이트웨이는 위치 인자(키워드 인자가 아님)를 사용하여 애플리케이션 객체를 반드시 호출해야 합니다. (예를 들어 위에 보인 것처럼 result = application(environ, start_response)를 호출해야 합니다.)

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

start_response 매개변수는 필수 위치 인자 두 개와 선택적 인자 하나를 받는 호출 가능 객체입니다. 설명을 위해 이 인자들의 이름을 status, response_headers, exc_info로 정했지만, 반드시 이러한 이름을 사용할 필요는 없으며, 애플리케이션은 위치 인자를 사용하여 start_response 호출 가능 객체를 반드시 호출해야 합니다(예: start_response(status, response_headers)).

status 매개변수는 "999 Message here" 형식의 상태 문자열이며, response_headers는 HTTP 응답 헤더를 설명하는 (header_name, header_value) 튜플의 목록입니다. 선택적 exc_info 매개변수는 The start_response() CallableError Handling 절에서 아래에 설명합니다. 이는 애플리케이션이 오류를 포착하여 브라우저에 오류 메시지를 표시하려고 할 때만 사용합니다.

start_response 호출 가능 객체는 HTTP 응답 본문의 일부로 작성할 문자열이라는 하나의 위치 매개변수를 받는 write(body_data) 호출 가능 객체를 반환해야 합니다. (참고: write() 호출 가능 객체는 기존 일부 프레임워크의 명령형 출력 API를 지원하기 위해서만 제공되며, 피할 수 있다면 새 애플리케이션이나 프레임워크에서는 사용하지 않아야 합니다. 자세한 내용은 Buffering and Streaming 절을 참조하십시오.)

서버가 호출하면 애플리케이션 객체는 0개 이상의 문자열을 생성하는 이터러블을 반환해야 합니다. 이는 문자열 목록을 반환하거나, 문자열을 생성하는 제너레이터 함수인 애플리케이션을 사용하거나, 인스턴스가 이터러블인 클래스로 애플리케이션을 작성하는 등 다양한 방식으로 수행할 수 있습니다. 어떤 방식으로 수행하든 애플리케이션 객체는 항상 0개 이상의 문자열을 생성하는 이터러블을 반환해야 합니다.

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

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

len(iterable) 호출이 성공하면 서버는 그 결과가 정확하다고 신뢰할 수 있어야 합니다. 즉, 애플리케이션이 반환한 이터러블에 작동하는 __len__() 메서드가 있으면 해당 메서드는 정확한 결과를 반드시 반환해야 합니다. (이것이 일반적으로 어떻게 사용되는지는 Handling the Content-Length Header 절을 참조하십시오.)

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

(참고: 애플리케이션은 이터러블이 첫 번째 본문 문자열을 생성하기 전에 start_response() 호출 가능 객체를 반드시 호출해야 하며, 그래야 서버가 본문 콘텐츠보다 먼저 헤더를 전송할 수 있습니다. 그러나 이 호출은 이터러블의 첫 번째 반복에서 수행될 수도 있으므로, 서버는 이터러블에 대한 반복을 시작하기 전에 start_response()가 호출되었다고 가정해서는 안 됩니다.)

마지막으로, 서버와 게이트웨이는 애플리케이션이 반환한 이터러블의 다른 속성을 직접 사용해서는 안 되며, 해당 이터러블이 wsgi.file_wrapper가 반환하는 “파일 래퍼”와 같이 해당 서버 또는 게이트웨이에 특화된 타입의 인스턴스인 경우는 예외입니다(Optional Platform-Specific File Handling 참조). 일반적인 경우에는 여기에 지정된 속성 또는 예를 들어 PEP 234 반복 API를 통해 접근되는 속성만 허용됩니다.

environ 변수

environ 딕셔너리에는 Common Gateway Interface 사양 [2] 에 정의된 다음 CGI 환경 변수가 포함되어야 합니다. 다음 변수는 반드시 존재해야 합니다. 단, 해당 값이 빈 문자열이라면 아래에 달리 명시된 경우를 제외하고 생략할 수 있습니다.

REQUEST_METHOD
"GET" 또는 "POST"와 같은 HTTP 요청 메서드입니다. 이는 결코 빈 문자열일 수 없으므로 항상 필수입니다.
SCRIPT_NAME
애플리케이션 객체에 해당하는 요청 URL의 “path” 초기 부분으로, 애플리케이션이 자신의 가상 “location”을 알 수 있도록 합니다. 애플리케이션이 서버의 “루트”에 해당한다면 이 값은 빈 문자열일 수 있습니다.
PATH_INFO
요청 대상이 애플리케이션 내에서 위치한 가상 “location”을 나타내는 요청 URL의 “path” 나머지 부분입니다. 요청 URL이 애플리케이션 루트를 대상으로 하며 뒤에 슬래시가 붙지 않았다면 이 값은 빈 문자열일 수 있습니다.
QUERY_STRING
요청 URL에서 "?"뒤에 오는 부분입니다(있는 경우). 비어 있거나 존재하지 않을 수 있습니다.
CONTENT_TYPE
HTTP 요청에 포함된 Content-Type 필드의 내용입니다. 비어 있거나 존재하지 않을 수 있습니다.
CONTENT_LENGTH
HTTP 요청에 포함된 Content-Length 필드의 내용입니다. 비어 있거나 존재하지 않을 수 있습니다.
SERVER_NAME, SERVER_PORT
SCRIPT_NAMEPATH_INFO와 결합하면 이 변수를 사용하여 URL을 완성할 수 있습니다. 그러나 요청 URL을 재구성할 때는 HTTP_HOST가 존재하는 경우 SERVER_NAME보다 우선하여 사용해야 합니다. 자세한 내용은 아래의 URL Reconstruction 절을 참조하십시오. SERVER_NAMESERVER_PORT는 결코 빈 문자열일 수 없으므로 항상 필수입니다.
SERVER_PROTOCOL
클라이언트가 요청을 보내는 데 사용한 프로토콜 버전입니다. 일반적으로 이는 "HTTP/1.0" 또는 "HTTP/1.1"과 같은 것이며, 애플리케이션에서 HTTP 요청 헤더를 처리하는 방법을 결정하는 데 사용할 수 있습니다. (이 변수는 요청에서 사용된 프로토콜을 나타내며 서버 응답에 사용될 프로토콜과 반드시 같지는 않으므로, 아마도 REQUEST_PROTOCOL이라고 불러야 할 것입니다. 그러나 CGI와의 하위 호환성을 위해 기존 이름을 유지해야 합니다.)
HTTP_ 변수
클라이언트가 제공한 HTTP 요청 헤더에 해당하는 변수입니다(즉, 이름이 "HTTP_"로 시작하는 변수입니다). 이러한 변수의 존재 여부는 요청에 해당 HTTP 헤더가 존재하는지 여부와 일치해야 합니다.

서버 또는 게이트웨이는 해당하는 다른 CGI 변수를 가능한 한 많이 제공하도록 should시도해야 합니다. 또한 SSL을 사용 중이라면 서버 또는 게이트웨이는 해당되는 Apache SSL 환경 변수 [3]를 가능한 한 많이 제공해야 하며, HTTPS=onSSL_PROTOCOL 등이 이에 해당합니다. 그러나 위에 나열된 변수 이외의 CGI 변수를 사용하는 애플리케이션은 관련 확장을 지원하지 않는 웹 서버로 이식할 수 없습니다. (예를 들어, 파일을 공개하지 않는 웹 서버는 의미 있는 DOCUMENT_ROOT 또는 PATH_TRANSLATED를 제공할 수 없습니다.)

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

참고: 인증이 수행되지 않은 경우의 REMOTE_USER와 같은 누락된 변수는 environ 딕셔너리에서 제외해야 합니다. 또한 CGI에서 정의한 변수는 존재하는 경우 반드시 문자열이어야 합니다. CGI 변수의 값이 str이외의 유형인 것은 이 사양을 위반하는 것입니다.

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

Variable Value
wsgi.version The tuple (1, 0), representing WSGI version 1.0.
wsgi.url_scheme A string representing the “scheme” portion of the URL at which the application is being invoked. Normally, this will have the value "http" or "https", as appropriate.
wsgi.input An input stream (file-like object) from which 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.)
wsgi.errors An output stream (file-like object) to which error output 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.

For many servers, wsgi.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.

wsgi.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.
wsgi.multiprocess This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise.
wsgi.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).

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

입력 및 오류 스트림

서버가 제공하는 입력 및 오류 스트림은 다음 메서드를 지원해야 합니다.

메서드 스트림 참고
read(size) input 1
readline() input 1, 2
readlines(hint) input 1, 3
__iter__() input
flush() errors 4
write(str) errors
writelines(seq) errors

각 메서드의 의미는 Python Library Reference에 문서화된 내용과 같지만, 위 표에 나열된 다음 주의 사항은 예외입니다:

  1. 서버는 클라이언트가 지정한 Content-Length를 초과하여 읽을 필요가 없으며, 애플리케이션이 해당 지점을 넘어 읽으려고 시도하면 파일 끝 조건을 시뮬레이션할 수 있습니다. 애플리케이션은 CONTENT_LENGTH 변수에 지정된 것보다 많은 데이터를 읽으려고 해서는 안 됩니다.
  2. 선택적 readline()의 “size” 인자는 지원되지 않습니다. 서버 작성자가 구현하기 복잡할 수 있으며 실제로 자주 사용되지 않기 때문입니다.
  3. readlines()hint 인자는 호출자와 구현자 모두에게 선택 사항임에 유의하십시오. 애플리케이션은 이를 제공하지 않아도 되며, 서버 또는 게이트웨이는 이를 무시해도 됩니다.
  4. errors 스트림은 되감기할 수 없을 수 있으므로, 서버와 게이트웨이는 버퍼링 없이 쓰기 작업을 즉시 전달할 수 있습니다. 이 경우 flush() 메서드는 아무 작업도 하지 않을 수 있습니다. 그러나 이식 가능한 애플리케이션은 출력이 버퍼링되지 않거나 flush()가 아무 작업도 하지 않는다고 가정할 수 없습니다. 출력이 실제로 기록되었음을 확인해야 한다면 flush()를 호출해야 합니다. (예를 들어, 동일한 오류 로그에 쓰는 여러 프로세스의 데이터가 서로 섞이는 것을 최소화하기 위해서입니다.)

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

start_response() 호출 가능 객체

애플리케이션 객체에 전달되는 두 번째 매개변수는 start_response(status, response_headers, exc_info=None) 형식의 호출 가능 객체입니다. (모든 WSGI 호출 가능 객체와 마찬가지로 인자는 키워드가 아니라 위치로 제공해야 합니다.) start_response 호출 가능 객체는 HTTP 응답을 시작하는 데 사용되며, write(body_data) 호출 가능 객체를 반환해야 합니다 (아래의 Buffering and Streaming 절을 참조하십시오).

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

response_headers 인자는 (header_name, header_value) 튜플의 목록입니다. 이는 Python 목록이어야 합니다. 즉, type(response_headers) is ListType이어야 하며, 서버는 원하는 방식으로 그 내용을 변경할 수 있습니다. 각 header_name은 뒤에 콜론이나 다른 구두점이 없는 유효한 HTTP 헤더 필드 이름이어야 합니다 (RFC 2616의 Section 4.2에 정의됨).

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

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

(서버/게이트웨이 작성자를 위한 참고 사항: HTTP 헤더 이름은 대소문자를 구분하지 않으므로, 애플리케이션이 제공한 헤더를 검사할 때 이 점을 반드시 고려하십시오!)

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

start_response 호출 가능 객체는 응답 헤더를 실제로 전송해서는 안 됩니다. 대신, 서버 또는 게이트웨이가 비어 있지 않은 문자열을 생성하는 애플리케이션 반환 값의 첫 번째 반복 후에 오직 이를 전송할 수 있도록 저장하거나, 애플리케이션이 write() 호출 가능 객체를 처음 호출할 때 전송할 수 있도록 저장해야 합니다. 다시 말해, 실제 본문 데이터가 준비될 때까지 또는 애플리케이션이 반환한 이터러블이 소진될 때까지 응답 헤더를 전송해서는 안 됩니다. (이 규칙의 유일한 가능한 예외는 응답 헤더에 Content-Length가 0으로 명시된 경우입니다.)

응답 헤더 전송을 지연하는 이유는 버퍼링된 애플리케이션과 비동기 애플리케이션이 가능한 마지막 순간까지 원래 의도한 출력을 오류 출력으로 대체할 수 있도록 하기 위해서입니다. 예를 들어 애플리케이션 버퍼에서 본문을 생성하는 동안 오류가 발생하면, 애플리케이션은 응답 상태를 “200 OK”에서 “500 Internal Error”로 변경해야 할 수 있습니다.

exc_info 인자가 제공되는 경우 Python의 sys.exc_info() 튜플이어야 합니다. 이 인자는 애플리케이션이 오류 처리기에 의해 start_response를 호출하는 경우에만 제공해야 합니다. exc_info가 제공되고 아직 HTTP 헤더가 출력되지 않았다면, start_response는 현재 저장된 HTTP 응답 헤더를 새로 제공된 헤더로 대체해야 하며, 이를 통해 오류가 발생했을 때 애플리케이션이 출력에 대한 “마음”을 바꿀 수 있도록 해야 합니다.

그러나 exc_info가 제공되었고 HTTP 헤더가 이미 전송된 경우, start_response는 오류를 발생시켜야 하며 exc_info 튜플을 다시 발생시켜야 합니다. 즉,:

raise exc_info[0], exc_info[1], exc_info[2]

이렇게 하면 애플리케이션이 가로챈 예외가 다시 발생하며, 원칙적으로 애플리케이션을 중단해야 합니다. (HTTP 헤더가 이미 전송된 후에 애플리케이션이 브라우저로 오류 출력을 시도하는 것은 안전하지 않습니다.) 애플리케이션이 exc_info와 함께 start_response를 호출했다면, start_response에서 발생한 예외를 가로채서는 안 됩니다. 대신 해당 예외가 서버나 게이트웨이로 전파되도록 허용해야 합니다. 자세한 내용은 아래의 Error Handling을 참조하십시오.

애플리케이션은 exc_info 인자가 제공된 경우에만 start_response를 두 번 이상 호출할 수 있습니다. 보다 정확히 말하면, 현재 애플리케이션 호출 중에 이미 start_response가 호출된 경우 exc_info 인자 없이 start_response를 호출하는 것은 치명적 오류입니다. (올바른 로직의 예시는 위의 CGI 게이트웨이 예제를 참조하십시오.)

참고: start_response를 구현하는 서버, 게이트웨이 또는 미들웨어는 관련 트레이스백과 프레임을 통해 순환 참조가 생성되지 않도록 함수 실행 기간을 넘어 exc_info 매개변수에 대한 참조를 보유하지 않도록 해야 합니다. 이를 수행하는 가장 간단한 방법은 다음과 같은 방식입니다.:

def start_response(status, response_headers, exc_info=None):
    if exc_info:
         try:
             # do stuff w/exc_info here
         finally:
             exc_info = None    # Avoid circular ref.

CGI 게이트웨이 예제는 이 기법을 보여 주는 또 다른 예시를 제공합니다.

Content-Length 헤더 처리

애플리케이션이 Content-Length 헤더를 제공하지 않는 경우, 서버나 게이트웨이는 이를 처리하는 여러 방법 중 하나를 선택할 수 있습니다. 이러한 방법 중 가장 간단한 것은 응답이 완료될 때 클라이언트 연결을 닫는 것입니다.

그러나 일부 상황에서는 서버나 게이트웨이가 Content-Length 헤더를 생성하거나, 적어도 클라이언트 연결을 닫을 필요를 피할 수 있습니다. 애플리케이션이 write() 호출 가능 객체를 호출하지 않고, len()이 1인 이터러블을 반환하면, 서버는 이터러블이 생성한 첫 번째 문자열의 길이를 가져와 Content-Length를 자동으로 결정할 수 있습니다.

또한 서버와 클라이언트가 모두 HTTP/1.1 “chunked encoding”을 지원한다면, 서버는 이터러블에서 생성된 각 write() 호출 또는 문자열마다 청크를 전송하는 데 청크 인코딩을 사용할 수 있으며, 이를 통해 각 청크에 대한 Content-Length 헤더를 생성할 수 있습니다. 이를 통해 서버는 원하는 경우 클라이언트 연결을 유지할 수 있습니다. 이 작업을 수행할 때 서버는 RFC 2616을 완전히 준수해야 하며, 그렇지 않으면 Content-Length가 없는 상황을 처리하기 위한 다른 전략 중 하나로 대체해야 합니다.

(참고: 애플리케이션과 미들웨어는 청크 분할이나 gzip 압축과 같은 어떤 종류의 Transfer-Encoding도 출력에 적용해서는 안 됩니다. 이러한 인코딩은 “hop-by-hop” 작업이므로 실제 웹 서버/게이트웨이의 영역입니다. 자세한 내용은 아래의 Other HTTP Features를 참조하십시오.)

버퍼링 및 스트리밍

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

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

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

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

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

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

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

블록 경계 처리 미들웨어

비동기 애플리케이션과 서버를 더 잘 지원하기 위해 미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리며 반복을 차단해서는 안 됩니다. 미들웨어가 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 누적해야 한다면, 빈 문자열을 생성해야 합니다.

다시 말해, 미들웨어 구성 요소는 기반 애플리케이션이 값을 생성할 때마다 적어도 하나의 값을 생성해야 합니다. 미들웨어가 다른 값을 생성할 수 없다면 빈 문자열을 생성해야 합니다.

이 요구 사항은 비동기 애플리케이션과 서버가 협력하여 일정 수의 애플리케이션 인스턴스를 동시에 실행하는 데 필요한 스레드 수를 줄일 수 있도록 합니다.

또한 이 요구 사항은 기반 애플리케이션이 이터러블을 반환하는 즉시 미들웨어가 반드시 이터러블을 반환해야 함을 의미합니다. 또한 미들웨어가 기반 애플리케이션에 의해 생성된 데이터를 전송하기 위해 write() 호출 가능 객체를 사용하는 것은 금지됩니다. 미들웨어는 기반 애플리케이션이 미들웨어가 제공한 write() 호출 가능 객체를 사용하여 보낸 데이터를 전송할 때만 상위 서버의 write() 호출 가능 객체를 사용할 수 있습니다.

write() 호출 가능 객체

일부 기존 애플리케이션 프레임워크 API는 WSGI와 다른 방식으로 버퍼링되지 않은 출력을 지원합니다. 구체적으로 이러한 API는 버퍼링되지 않은 데이터 블록을 기록하기 위한 어떤 종류의 “write” 함수 또는 메서드를 제공하거나, 버퍼링된 “write” 함수와 버퍼를 플러시하기 위한 “flush” 메커니즘을 제공합니다.

안타깝게도 이러한 API는 스레드나 기타 특수한 메커니즘을 사용하지 않는 한 WSGI의 “이터러블” 애플리케이션 반환값을 기반으로 구현할 수 없습니다.

따라서 이러한 프레임워크가 명령형 API를 계속 사용할 수 있도록 WSGI는 start_response 호출 가능 객체가 반환하는 특수한 write() 호출 가능 객체를 포함합니다.

새로운 WSGI 애플리케이션과 프레임워크는 피할 수 있다면 write() 호출 가능 객체를 사용해서는 안 됩니다. write() 호출 가능 객체는 명령형 스트리밍 API를 지원하기 위한 엄밀한 의미의 편법입니다. 일반적으로 애플리케이션은 반환된 이터러블을 통해 출력을 생성해야 합니다. 이렇게 하면 웹 서버가 동일한 Python 스레드에서 다른 작업을 인터리브할 수 있어 서버 전체에 더 나은 처리량을 제공할 가능성이 있기 때문입니다.

write() 호출 가능 객체는 start_response() 호출 가능 객체가 반환하며, 단일 매개변수인 HTTP 응답 본문의 일부로 작성될 문자열을 받습니다. 이 문자열은 출력 이터러블이 생성한 것과 정확히 동일하게 처리됩니다. 즉, write()가 반환되기 전에 전달된 문자열이 클라이언트에 완전히 전송되었거나 애플리케이션이 계속 진행하는 동안 전송을 위해 버퍼링되었음을 보장해야 합니다.

애플리케이션은 응답 본문의 전부 또는 일부를 생성하기 위해 write()를 사용하는 경우에도 이터러블 객체를 반드시 반환해야 합니다. 반환된 이터러블은 비어 있을 수 있습니다(즉, 비어 있지 않은 문자열을 생성하지 않을 수 있습니다). 하지만 실제로 비어 있지 않은 문자열을 생성한다면 해당 출력은 서버 또는 게이트웨이가 정상적으로 처리해야 합니다(즉, 즉시 전송하거나 대기열에 넣어야 합니다). 애플리케이션은 반환 이터러블 내부에서 write()호출해서는 안 되며, 따라서 이터러블이 생성한 모든 문자열은 write()에 전달된 모든 문자열이 클라이언트로 전송된 후에 전송됩니다.

유니코드 문제

HTTP는 유니코드를 직접 지원하지 않으며 이 인터페이스도 지원하지 않습니다. 모든 인코딩 및 디코딩은 애플리케이션이 처리해야 하며, 서버에 전달되거나 서버에서 전달되는 모든 문자열은 유니코드 객체가 아닌 표준 Python 바이트 문자열이어야 합니다. 문자열 객체가 필요한 곳에 유니코드 객체를 사용한 결과는 정의되지 않습니다.

또한 상태 또는 응답 헤더로 start_response()에 전달되는 문자열은 인코딩과 관련하여 RFC 2616따라야 합니다. 즉, ISO-8859-1 문자이거나 RFC 2047MIME 인코딩을 사용해야 합니다.

str 또는 StringType 타입이 실제로 유니코드 기반인 Python 플랫폼(예: Jython, IronPython, Python 3000 등)에서는 이 명세에서 언급하는 모든 “문자열”이 ISO-8859-1 인코딩으로 표현할 수 있는 코드 포인트(\u0000부터 \u00FF까지, 양 끝값 포함)만 포함해야 합니다. 애플리케이션이 다른 유니코드 문자나 코드 포인트를 포함하는 문자열을 제공하면 치명적인 오류입니다. 마찬가지로 서버와 게이트웨이는 다른 유니코드 문자를 포함하는 문자열을 애플리케이션에 제공해서는 안 됩니다.

다시 말해, 이 사양에서 언급하는 모든 문자열은 str 또는 StringType 타입이어야 하며, unicode 또는 UnicodeType 타입이어서는 안 됩니다. 또한 특정 플랫폼에서 str/StringType 객체의 문자당 비트 수가 8비트를 초과하더라도, 이 명세에서 “문자열”이라고 하는 모든 값에는 하위 8비트만 사용할 수 있습니다.

오류 처리

일반적으로 애플리케이션은 자체 내부 오류를 가급적 포착하여 브라우저에 유용한 메시지를 표시해야 합니다. (이 문맥에서 “유용한”이 무엇을 의미하는지는 애플리케이션이 결정합니다.)

그러나 이러한 메시지를 표시하려면 애플리케이션이 아직 브라우저에 어떤 데이터도 실제로 전송하지 않았어야 하며, 그렇지 않으면 응답이 손상될 위험이 있습니다. 따라서 WSGI는 애플리케이션이 오류 메시지를 전송하도록 허용하거나 자동으로 중단되도록 하는 메커니즘을 제공합니다. 바로 start_responseexc_info 인자입니다. 다음은 그 사용 예입니다.:

try:
    # regular application code here
    status = "200 Froody"
    response_headers = [("content-type", "text/plain")]
    start_response(status, response_headers)
    return ["normal body goes here"]
except:
    # XXX should trap runtime issues like MemoryError, KeyboardInterrupt
    #     in a separate handler before this bare 'except:'...
    status = "500 Oops"
    response_headers = [("content-type", "text/plain")]
    start_response(status, response_headers, sys.exc_info())
    return ["error body goes here"]

예외가 발생했을 때 출력이 작성되지 않았다면 start_response호출은 정상적으로 반환되고, 애플리케이션은 브라우저로 전송할 오류 본문을 반환합니다. 그러나 이미 브라우저에 출력이 전송되었다면 start_response는 제공된 예외를 다시 발생시킵니다. 이 예외는 애플리케이션이 포착해서는 안 되며, 따라서 애플리케이션은 중단됩니다. 그러면 서버 또는 게이트웨이는 이 치명적인 예외를 포착하고 응답을 중단할 수 있습니다.

서버는 애플리케이션 또는 반환 값의 이터레이션을 중단시키는 모든 예외를 가급적 포착하고 기록해야 합니다. 애플리케이션 오류가 발생했을 때 부분 응답이 이미 브라우저에 기록되었다면, 이미 전송된 헤더가 서버가 정상적으로 수정할 수 있는 text/* 콘텐츠 타입을 나타내는 경우 서버 또는 게이트웨이는 출력에 오류 메시지를 추가하려고 시도할 수 있습니다.

일부 미들웨어는 추가적인 예외 처리 서비스를 제공하거나 애플리케이션 오류 메시지를 가로채서 대체하려 할 수 있습니다. 이러한 경우 미들웨어는 start_response에 제공된 exc_info다시 발생시키지 않고, 대신 미들웨어별 예외를 발생시키거나 제공된 인자를 저장한 후 예외 없이 단순히 반환할 수 있습니다. 그러면 애플리케이션은 오류 본문 이터러블을 반환하거나 write()를 호출하게 되며, 미들웨어는 오류 출력을 가로채서 수정할 수 있습니다. 이러한 기법은 애플리케이션 작성자가 다음을 준수하는 한 작동합니다.

  1. 오류 응답을 시작할 때 항상 exc_info를 제공하십시오.
  2. exc_info가 제공되는 동안 start_response에 의해 발생한 오류를 가로채지 마십시오.

HTTP 1.1 Expect/Continue

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

  1. Expect: 100-continue 요청을 포함하는 요청에 즉시 “100 Continue” 응답을 보내고 정상적으로 진행합니다.
  2. 요청을 정상적으로 처리하되, 애플리케이션이 입력 스트림에서 처음 읽기를 시도할 때 “100 Continue” 응답을 보내는 wsgi.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절을 참조하십시오.)

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

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

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

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

스레드 지원

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

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

서버 확장 API

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

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

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

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

마찬가지로 확장 API가 응답 데이터나 헤더를 작성하는 대체 수단을 제공한다면, 애플리케이션이 확장 서비스를 얻기 전에 start_response 호출 가능 객체를 전달하도록 요구해야 합니다. 전달된 객체가 서버/게이트웨이가 원래 애플리케이션에 제공한 객체와 동일하지 않다면, 올바른 작동을 보장할 수 없으므로 애플리케이션에 확장 서비스를 제공하는 것을 거부해야 합니다.

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

향후 미들웨어 개발자가 확장 기능을 사용하는 애플리케이션에 의해 자신의 중재가 우회되지 않도록 environ에서 모든 확장 API를 삭제해야 하는 상황을 피하려면, 서버/게이트웨이 개발자와 미들웨어 개발자 모두 이러한 “안전한 확장” 규칙을 반드시 따라야 합니다!

애플리케이션 구성

이 명세는 서버가 호출할 애플리케이션을 선택하거나 얻는 방법을 정의하지 않습니다. 이러한 구성 옵션과 기타 구성 옵션은 서버별로 크게 달라지는 사항입니다. 서버/게이트웨이 작성자는 특정 애플리케이션 객체를 어떤 옵션(스레딩 옵션 등)으로 실행하도록 서버를 구성하는지 문서화해야 합니다.

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

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

모든 서버가 이러한 변수를 쉽게 구성하는 기능을 지원하지는 않으므로, 애플리케이션은 이러한 필수 변수의 수를 최소한으로 유지하도록 노력해야 합니다. 물론 최악의 경우에도 애플리케이션을 배포하는 사용자는 필요한 구성 값을 제공하는 스크립트를 만들 수 있습니다.:

from the_app import application

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

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

URL 재구성

애플리케이션이 요청의 완전한 URL을 재구성하려는 경우 Ian Bicking이 제공한 다음 알고리즘을 사용하여 수행할 수 있습니다.:

from urllib import quote
url = environ['wsgi.url_scheme']+'://'

if environ.get('HTTP_HOST'):
    url += environ['HTTP_HOST']
else:
    url += environ['SERVER_NAME']

    if environ['wsgi.url_scheme'] == 'https':
        if environ['SERVER_PORT'] != '443':
           url += ':' + environ['SERVER_PORT']
    else:
        if environ['SERVER_PORT'] != '80':
           url += ':' + environ['SERVER_PORT']

url += quote(environ.get('SCRIPT_NAME', ''))
url += quote(environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
    url += '?' + environ['QUERY_STRING']

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

이전 버전(<2.2)의 Python 지원

일부 서버, 게이트웨이 또는 애플리케이션은 이전 버전(<2.2)의 Python을 지원하고자 할 수 있습니다. 이는 Jython이 대상 플랫폼인 경우 특히 중요합니다. 현재 작성 시점을 기준으로 프로덕션에 사용할 수 있는 Jython 2.2 버전이 아직 제공되지 않기 때문입니다.

서버와 게이트웨이의 경우 이는 비교적 간단합니다. Python 2.2 이전 버전을 대상으로 하는 서버와 게이트웨이는 애플리케이션이 반환하는 모든 이터러블을 순회할 때 표준 “for” 루프만 사용하도록 제한하면 됩니다. 이는 2.2 이전의 이터레이터 프로토콜(아래에서 더 자세히 설명합니다)과 “오늘날의” 이터레이터 프로토콜( PEP 234 참조) 모두와 소스 수준의 호환성을 보장하는 유일한 방법입니다.

(이 기법은 Python으로 작성된 서버, 게이트웨이 또는 미들웨어에만 적용된다는 점에 유의하십시오. 다른 언어에서 이터레이터 프로토콜을 올바르게 사용하는 방법에 대한 논의는 이 PEP의 범위를 벗어납니다.)

애플리케이션의 경우 Python의 2.2 이전 버전을 지원하는 작업은 약간 더 복잡합니다.

  • 파일 객체를 반환하고 그것이 이터러블로 작동할 것이라고 예상해서는 안 됩니다. Python 2.2 이전에는 파일이 이터러블이 아니었기 때문입니다. (일반적으로도 이렇게 해서는 안 됩니다. 대부분의 경우 성능이 상당히 떨어지기 때문입니다!) wsgi.file_wrapper나 애플리케이션별 파일 래퍼 클래스를 사용하십시오. (wsgi.file_wrapper에 대한 자세한 내용과 파일을 이터러블로 래핑하는 데 사용할 수 있는 예제 클래스는 Optional Platform-Specific File Handling 을 참조하십시오.)
  • 사용자 지정 이터러블을 반환하는 경우, 해당 이터러블은 2.2 이전의 이터레이터 프로토콜을 반드시 구현해야 합니다. 즉, 정수 키를 허용하는 __getitem__ 메서드를 제공하고, 소진되면 IndexError를 발생시켜야 합니다. (내장 시퀀스 타입도 이 프로토콜을 구현하므로 허용됩니다.)

마지막으로 Python의 2.2 이전 버전을 지원하고자 하며 애플리케이션 반환 값을 순회하거나, 직접 이터러블을 반환하거나(또는 둘 다 수행하는) 미들웨어는 위의 적절한 권장 사항을 따라야 합니다.

(참고: Python의 2.2 이전 버전을 지원하려면 서버, 게이트웨이, 애플리케이션 또는 미들웨어가 대상 버전에서 사용할 수 있는 언어 기능만 사용하고, TrueFalse대신 1과 0을 사용하는 등의 작업도 수행해야 한다는 점은 말할 필요도 없습니다.)

플랫폼별 파일 처리(선택 사항)

일부 운영 환경은 Unix의 sendfile() 호출과 같은 특수한 고성능 파일 전송 기능을 제공합니다. 서버와 게이트웨이는 environ의 선택적 wsgi.file_wrapper 키를 통해 이 기능을 노출할 수도 있습니다. 애플리케이션은 이 “파일 래퍼”를 사용하여 파일 또는 파일과 유사한 객체를 자신이 반환할 이터러블로 변환할 수 있습니다. 예를 들면 다음과 같습니다.:

if 'wsgi.file_wrapper' in environ:
    return environ['wsgi.file_wrapper'](filelike, block_size)
else:
    return iter(lambda: filelike.read(block_size), '')

서버 또는 게이트웨이가 wsgi.file_wrapper를 제공하는 경우, 필수 위치 인자 하나와 선택적 위치 인자 하나를 허용하는 호출 가능 객체여야 합니다. 첫 번째 매개변수는 전송할 파일과 유사한 객체이고, 두 번째 매개변수는 선택적 블록 크기 “제안값”입니다. 서버 또는 게이트웨이는 이 값을 사용할 필요가 없습니다. 호출 가능 객체는 이터러블 객체를 반드시 반환해야 하며, 서버 또는 게이트웨이가 애플리케이션으로부터 실제로 이터러블을 반환 값으로 받을 때까지는 어떤 데이터 전송도 절대로 수행해서는 안 됩니다. (그렇지 않으면 미들웨어가 응답 데이터를 해석하거나 재정의할 수 없게 됩니다.)

애플리케이션이 제공한 객체가 “파일과 유사한” 것으로 간주되려면 선택적 크기 인자를 받는 read() 메서드가 있어야 합니다. 이 객체에는 close() 메서드가 있을 수 있으며, 있는 경우 wsgi.file_wrapper가 반환하는 이터러블에도 원래 파일과 유사한 객체의 close() 메서드를 호출하는 close() 메서드가 있어야 합니다. “파일과 유사한” 객체에 Python 내장 파일 객체의 메서드 또는 속성과 일치하는 이름의 다른 메서드나 속성(예: fileno())이 있는 경우, wsgi.file_wrapper는 이러한 메서드 또는 속성이 내장 파일 객체와 동일한 의미를 가진다고 간주할 수 있습니다.

플랫폼별 파일 처리의 실제 구현은 반드시 애플리케이션이 반환한 후에 이루어져야 하며, 서버 또는 게이트웨이는 래퍼 객체가 반환되었는지 확인합니다. (다시 말해 미들웨어, 오류 처리기 등으로 인해 생성된 래퍼가 실제로 사용된다는 보장은 없습니다.)

close()처리를 제외하면, 애플리케이션에서 파일 래퍼를 반환하는 의미는 애플리케이션이 iter(filelike.read, '')를 반환한 경우와 같아야 합니다. 다시 말해 전송은 전송이 시작되는 시점에 “파일” 내의 현재 위치에서 시작하여 끝에 도달할 때까지 계속되어야 합니다.

물론 플랫폼별 파일 전송 API는 일반적으로 임의의 “파일과 유사한” 객체를 허용하지 않습니다. 따라서 wsgi.file_wrapper는 파일과 유사한 객체가 자신이 지원하는 플랫폼별 API에 사용하기에 적합한지 확인하기 위해 제공된 객체에서 fileno()(Unix 계열 운영 체제) 또는 java.nio.FileChannel(Jython에서)과 같은 항목을 검사해야 합니다.

객체가 플랫폼 API에 적합하지 않더라도, wsgi.file_wrapper는 애플리케이션에서 파일 래퍼를 사용하는 경우 플랫폼 간 이식성을 보장할 수 있도록 read()close()를 래핑하는 이터러블을 반드시 반환해야 합니다. 다음은 이전 버전(2.2 이전)과 최신 Python 모두에 적합한 간단한 플랫폼 독립적 파일 래퍼 클래스입니다:

class FileWrapper:

    def __init__(self, filelike, blksize=8192):
        self.filelike = filelike
        self.blksize = blksize
        if hasattr(filelike, 'close'):
            self.close = filelike.close

    def __getitem__(self, key):
        data = self.filelike.read(self.blksize)
        if data:
            return data
        raise IndexError

다음은 플랫폼별 API에 대한 액세스를 제공하기 위해 이를 사용하는 서버/게이트웨이의 코드 일부입니다:

environ['wsgi.file_wrapper'] = FileWrapper
result = application(environ, start_response)

try:
    if isinstance(result, FileWrapper):
        # check if result.filelike is usable w/platform-specific
        # API, and if so, use that API to transmit the result.
        # If not, fall through to normal iterable handling
        # loop below.

    for data in result:
        # etc.

finally:
    if hasattr(result, 'close'):
        result.close()

질문과 답변

  1. environ은 딕셔너리여야 합니까? 서브클래스를 사용하는 데 무엇이 문제입니까?

    딕셔너리를 요구하는 근거는 서버 간 이식성을 극대화하기 위한 것입니다. 대안은 딕셔너리 메서드의 일부를 표준이자 이식 가능한 인터페이스로 정의하는 것입니다. 그러나 실제로는 대부분의 서버가 딕셔너리로 자신의 요구 사항을 충족할 수 있을 것이며, 따라서 프레임워크 작성자는 딕셔너리의 모든 기능을 사용할 수 있을 것으로 기대하게 됩니다. 대부분의 경우 해당 기능이 제공될 것이기 때문입니다. 그러나 어떤 서버가 딕셔너리를 사용하지 않기로 선택하면, 해당 서버가 명세를 “준수”하더라도 상호 운용성 문제가 발생합니다. 따라서 딕셔너리를 필수로 만들면 명세가 단순해지고 상호 운용성이 보장됩니다.

    서버 또는 프레임워크 개발자가 특수 서비스를 사용자 지정 변수로 environ 딕셔너리 내부에 제공하는 것은 허용됩니다. 이러한 부가 가치 서비스를 제공할 때는 이 방법을 권장합니다.

  2. write()를 호출하고 문자열을 yield하거나 이터러블을 반환할 수 있습니까? 한 가지 방법만 선택해야 하지 않습니까?

    반복 방식만 지원한다면 “push”를 사용할 수 있다고 가정하는 현재 프레임워크에 문제가 발생합니다. 그러나 write()를 통한 push만 지원하면, 대용량 파일과 같은 것을 전송할 때 서버 성능이 저하됩니다(모든 출력이 전송될 때까지 작업 스레드가 새 요청 처리를 시작할 수 없는 경우). 따라서 이 절충안은 애플리케이션 프레임워크가 상황에 맞게 두 방식을 모두 지원하도록 하면서도, push만 지원하는 방식에 비해 서버 구현자에게 약간의 부담만 추가합니다.

  3. close()는 어떤 용도입니까?

    애플리케이션 객체를 실행하는 동안 쓰기 작업을 수행할 때 애플리케이션은 try/finally 블록을 사용하여 리소스가 해제되도록 보장할 수 있습니다. 그러나 애플리케이션이 이터러블을 반환하면 사용된 리소스는 해당 이터러블이 가비지 컬렉션될 때까지 해제되지 않습니다. close()관용구를 사용하면 애플리케이션이 요청이 끝날 때 중요한 리소스를 해제할 수 있으며, PEP 325에서 제안된 제너레이터의 try/finally 지원과도 하위 호환됩니다.

  4. 이 인터페이스는 왜 이렇게 저수준입니까? 기능 X를 원합니다! (예: 쿠키, 세션, 영속성 등)

    이것은 또 하나의 Python 웹 프레임워크가 아닙니다. 단지 프레임워크가 웹 서버와 통신하고, 그 반대도 가능하게 하는 방법일 뿐입니다. 이러한 기능을 원한다면 원하는 기능을 제공하는 웹 프레임워크를 선택해야 합니다. 또한 해당 프레임워크에서 WSGI 애플리케이션을 만들 수 있다면, 대부분의 WSGI 지원 서버에서 이를 실행할 수 있어야 합니다. 또한 일부 WSGI 서버는 environ 딕셔너리에 제공되는 객체를 통해 추가 서비스를 제공할 수 있습니다. 자세한 내용은 해당 서버 문서를 참조하십시오. (물론 이러한 확장 기능을 사용하는 애플리케이션은 다른 WSGI 기반 서버로 이식할 수 없습니다.)

  5. 기존의 좋은 HTTP 헤더 대신 CGI 변수를 사용하는 이유는 무엇입니까? 그리고 이를 WSGI가 정의한 변수와 섞는 이유는 무엇입니까?

    기존의 많은 웹 프레임워크는 CGI 명세를 기반으로 구축되었으며, 기존 웹 서버는 CGI 변수를 생성하는 방법을 알고 있습니다. 반면 수신 HTTP 정보를 표현하는 대안적인 방법은 분산되어 있고 시장 점유율이 낮습니다. 따라서 CGI “표준”을 사용하면 기존 구현을 활용할 수 있는 좋은 방법이 됩니다. WSGI 변수와 혼합하는 경우에는 이를 분리하려면 딕셔너리 인자 두 개를 전달해야 할 뿐이며, 실질적인 이점은 제공되지 않습니다.

  6. 상태 문자열은 어떻습니까? "200 OK"대신 200을 전달하여 숫자만 사용할 수는 없습니까?

    이렇게 하면 서버나 게이트웨이에 숫자 상태와 해당 메시지의 표를 갖추도록 요구하게 되어 서버나 게이트웨이가 복잡해집니다. 반면 애플리케이션이나 프레임워크 작성자는 자신이 사용하는 특정 응답 코드에 맞는 추가 텍스트를 입력하기 쉽고, 기존 프레임워크에는 필요한 메시지가 들어 있는 표가 이미 있는 경우가 많습니다. 따라서 종합적으로 보면 서버나 게이트웨이보다는 애플리케이션이나 프레임워크가 책임지도록 하는 편이 더 나아 보입니다.

  7. wsgi.run_once는 왜 앱을 한 번만 실행한다고 보장되지 않습니까?

    이는 애플리케이션에 “드물게 실행되도록 준비하십시오”라고 제안하는 것에 불과하기 때문입니다. 이는 캐싱, 세션 등의 작업에 여러 작동 모드를 제공하는 애플리케이션 프레임워크를 위한 것입니다. “여러 번 실행” 모드에서는 이러한 프레임워크가 캐시를 미리 로드할 수 있으며, 각 요청 후에 로그나 세션 데이터 등을 디스크에 기록하지 않을 수도 있습니다. “한 번 실행” 모드에서는 이러한 프레임워크가 미리 로드하는 작업을 피하고 각 요청 후에 필요한 모든 기록을 반영합니다.

    그러나 후자의 모드에서 애플리케이션이나 프레임워크가 올바르게 작동하는지 테스트하려면 이를 두 번 이상 호출해야 할 수도 있습니다(또는 적어도 그렇게 하는 편이 편리할 수 있습니다). 따라서 애플리케이션은 wsgi.run_onceTrue로 설정되어 호출되었다는 이유만으로 다시 실행되지 않을 것이라고 확신해서는 안 됩니다.

  8. 기능 X(딕셔너리, 호출 가능 객체 등)는 애플리케이션 코드에서 사용하기에 어색한데, 대신 객체를 사용하면 안 됩니까?

    WSGI의 이러한 모든 구현 선택은 기능을 서로 분리하도록 특별히 의도된 것입니다. 이러한 기능을 캡슐화된 객체로 다시 결합하면 서버나 게이트웨이를 작성하기가 다소 어려워지고, 전체 기능 중 작은 부분만 대체하거나 수정하는 미들웨어를 작성하기는 훨씬 더 어려워집니다.

    본질적으로 미들웨어는 “책임 연쇄” 패턴을 원합니다. 이에 따라 일부 함수의 “처리기”로 동작하면서 다른 함수는 변경되지 않은 상태로 둘 수 있습니다. 인터페이스의 확장성을 유지하려면 일반적인 Python 객체로 이를 구현하기가 어렵습니다. 예를 들어 확장 기능(향후 WSGI 버전에서 정의되는 속성 등)이 그대로 전달되도록 하려면 __getattr____getattribute__재정의를 사용해야 합니다.

    이러한 유형의 코드는 100% 올바르게 작성하기가 매우 어려운 것으로 악명 높으며, 직접 작성하려는 사람도 거의 없을 것입니다. 따라서 이들은 다른 사람의 구현을 복사하겠지만, 복사해 온 사람이 또 다른 예외 상황을 수정했을 때 이를 업데이트하지 못할 것입니다.

    더욱이 이렇게 필요한 상용구 코드는 순전히 불필요한 군더더기이며, 애플리케이션 프레임워크 개발자를 위해 조금 더 보기 좋은 API를 지원하도록 미들웨어 개발자가 부담하는 개발자 세금입니다. 그러나 애플리케이션 프레임워크 개발자는 일반적으로 WSGI를 지원하기 위해 하나의 프레임워크만 업데이트하며, 전체 프레임워크 중 매우 제한된 부분만 업데이트합니다. 이는 이들에게 첫 번째(어쩌면 유일한) WSGI 구현일 가능성이 높으므로, 이들은 이 사양을 곁에 두고 구현할 가능성이 높습니다. 따라서 객체 속성 등을 사용하여 API를 “더 보기 좋게” 만드는 노력은 이러한 사용자에게는 낭비가 될 가능성이 높습니다.

    직접 웹 애플리케이션 프로그래밍에 사용할, 웹 프레임워크 개발과는 다른, 더 보기 좋거나 그 밖의 방식으로 개선된 WSGI 인터페이스를 원하는 분들은 애플리케이션 개발자가 편리하게 사용할 수 있도록 WSGI를 감싸는 API나 프레임워크를 개발하시기 바랍니다. 이렇게 하면 WSGI는 서버 및 미들웨어 작성자에게 편리한 저수준 인터페이스로 남으면서도 애플리케이션 개발자에게 “어색한” 인터페이스가 되지 않을 수 있습니다.

제안됨/논의 중

이러한 항목은 현재 Web-SIG 및 다른 곳에서 논의 중이거나 PEP 작성자의 “할 일” 목록에 있습니다.

  • wsgi.input은 파일 대신 이터레이터여야 합니까? 이렇게 하면 비동기 애플리케이션과 청크 인코딩 입력 스트림에 도움이 될 것입니다.
  • 입력을 사용할 수 있게 되거나 콜백이 발생할 때까지 애플리케이션 출력의 이터레이션을 일시 중지하기 위한 선택적 확장 기능이 논의되고 있습니다.
  • 동기식 및 비동기식 앱과 서버, 관련 스레딩 모델, 그리고 이러한 영역의 문제와 설계 목표에 관한 절을 추가하십시오.

감사의 말

사려 깊은 피드백으로 이 개정 초안이 가능하도록 해 주신 Web-SIG 메일링 리스트의 많은 분께 감사드립니다. 특히 다음 분께 감사드립니다:

  • mod_python의 저자인 Gregory “Grisha” Trubetskoy 님은 첫 번째 초안이 “plain old CGI”보다 나은 점을 제공하지 않는다고 혹평하여, 제가 더 나은 접근 방식을 찾도록 독려해 주셨습니다.
  • 멀티스레딩 및 멀티프로세스 옵션을 제대로 명세하도록 계속 재촉했으며, 서버가 애플리케이션에 사용자 지정 확장 데이터를 제공할 수 있는 메커니즘을 마련하도록 거듭 요구한 Ian Bicking입니다.
  • 상태와 헤더를 받아 write 함수를 반환하는 start_response 함수라는 개념을 고안한 Tony Lownds입니다. 그의 의견은 예외 처리 기능의 설계를 이끄는 데에도 기여했으며, 특히 애플리케이션 오류 메시지를 재정의하는 미들웨어를 허용하는 부분에 영향을 주었습니다.
  • 사양이 확정되기 훨씬 전에 WSGI-on-Jython을 구현하려고 용감하게 시도하여 “이전 버전의 Python 지원” 절을 구성하는 데 기여했으며, 선택 사항인 wsgi.file_wrapper 기능에도 기여한 Alan Kennedy입니다.
  • HTTP RFC 준수와 관련된 문제를 찾기 위해 사양을 광범위하게 검토했으며, 특히 그가 지적하기 전까지는 존재하는지도 몰랐던 HTTP/1.1 기능과 관련하여 검토한 Mark Nottingham입니다.

참고 문헌