[Final] PEP 3333 - Python Web Server Gateway Interface v1.0.1
원문 링크: PEP 3333 - Python Web Server Gateway Interface v1.0.1
상태: Final 유형: Informational 작성일: 26-Sep-2010
PEP 3333 – Python Web Server Gateway Interface v1.0.1 번역 및 요약
서문 (PEP 333 독자를 위한)
이 문서는 기존 PEP 333의 업데이트된 버전으로, Python 3에서의 사용성을 개선하고, WSGI 프로토콜에 대한 몇 가지 오랜 사실상의 개정 사항을 통합하기 위해 약간 수정되었습니다. 코드 샘플 또한 Python 3에 맞춰 포팅되었습니다.
절차상의 이유로 별도의 PEP로 지정되었지만, Python 2.x 환경에서 기존의 규격 준수 서버나 애플리케이션의 유효성을 무효화하는 변경 사항은 없습니다. 만약 2.x 애플리케이션 또는 서버가 PEP 333을 준수한다면, 이 PEP 또한 준수하는 것입니다.
하지만 Python 3 환경에서는 애플리케이션 또는 서버가 “문자열 타입에 대한 참고 사항(A Note On String Types)” 및 “유니코드 문제(Unicode Issues)” 섹션에 명시된 규칙도 따라야 합니다.
이 문서와 PEP 333 간의 자세한 변경 사항은 SVN 개정 기록 (개정 84854부터)에서 확인할 수 있습니다.
개요
이 문서는 다양한 웹 서버에서 웹 애플리케이션의 이식성을 증진하기 위해 웹 서버와 Python 웹 애플리케이션 또는 프레임워크 간의 표준 인터페이스를 제안합니다.
WSGI의 도입 배경 및 목표 (PEP 333에서 발췌)
현재 Python은 Zope, Quixote, Webware, SkunkWeb, PSO, Twisted Web 등 다양한 웹 애플리케이션 프레임워크를 자랑합니다. 이러한 선택의 폭은 새로운 Python 사용자에게 문제가 될 수 있습니다. 일반적으로 웹 프레임워크의 선택이 사용 가능한 웹 서버의 선택을 제한하고, 그 반대도 마찬가지이기 때문입니다.
대조적으로, Java는 비슷한 수의 웹 애플리케이션 프레임워크가 있지만, Java의 “servlet” API 덕분에 어떤 Java 웹 애플리케이션 프레임워크로 작성된 애플리케이션이든 servlet API를 지원하는 모든 웹 서버에서 실행될 수 있습니다.
Python 웹 서버 (Python으로 작성된 Medusa, Python을 내장한 mod_python, 또는 CGI, FastCGI 등 게이트웨이 프로토콜을 통해 Python을 호출하는 서버 등)에서 이러한 API가 널리 사용 가능해진다면, 프레임워크 선택과 웹 서버 선택이 분리되어 사용자는 자신에게 적합한 조합을 자유롭게 선택할 수 있게 될 것입니다. 또한 프레임워크 및 서버 개발자는 각자의 전문 분야에 집중할 수 있게 됩니다.
따라서 이 PEP는 웹 서버와 웹 애플리케이션 또는 프레임워크 간의 간단하고 범용적인 인터페이스인 Python Web Server Gateway Interface (WSGI)를 제안합니다.
WSGI 사양의 존재만으로는 Python 웹 애플리케이션을 위한 기존 서버 및 프레임워크의 상태를 해결할 수 없습니다. 효과를 얻으려면 서버 및 프레임워크 개발자가 실제로 WSGI를 구현해야 합니다.
하지만 현재 WSGI를 지원하는 서버나 프레임워크가 없기 때문에, WSGI를 구현하는 개발자에게는 즉각적인 보상이 거의 없습니다. 따라서 WSGI는 구현하기 쉬워야 하며, 개발자의 초기 인터페이스 투자 비용이 합리적으로 낮을 수 있도록 해야 합니다.
이러한 이유로, 인터페이스의 서버 및 프레임워크 양쪽에서 구현의 단순성은 WSGI 인터페이스의 유용성에 절대적으로 중요하며, 모든 설계 결정의 주요 기준이 됩니다.
다만, 프레임워크 개발자에게 구현의 단순성이 웹 애플리케이션 개발자에게 사용의 용이성과 같지는 않다는 점에 유의해야 합니다. WSGI는 응답 객체 및 쿠키 처리와 같은 부가 기능이 기존 프레임워크의 문제 처리 방식에 방해가 될 수 있으므로, 프레임워크 개발자에게는 “꾸밈없는(no frills)” 인터페이스를 제공합니다. WSGI의 목표는 새로운 웹 프레임워크를 만드는 것이 아니라, 기존 서버와 애플리케이션 또는 프레임워크 간의 쉬운 상호 연결을 촉진하는 것입니다.
또한 이 목표는 WSGI가 현재 배포된 Python 버전에서 이미 사용할 수 없는 것을 요구하는 것을 배제합니다. 따라서 새로운 표준 라이브러리 모듈은 이 사양에서 제안되거나 요구되지 않으며, WSGI는 Python 2.2 이상의 버전을 요구하지 않습니다. (그러나 향후 Python 버전에서 표준 라이브러리에 의해 제공되는 웹 서버에서 이 인터페이스에 대한 지원을 포함하는 것이 좋습니다.)
기존 및 미래의 프레임워크 및 서버의 구현 용이성 외에도, 요청 전처리기, 응답 후처리기 및 기타 WSGI 기반 “미들웨어(middleware)” 구성 요소를 쉽게 생성할 수 있어야 합니다. 이러한 구성 요소는 상위 서버에게는 애플리케이션처럼 보이고, 하위 애플리케이션에게는 서버 역할을 수행합니다.
미들웨어가 간단하고 견고하며 WSGI가 서버 및 프레임워크에서 널리 사용 가능하다면, 완전히 새로운 종류의 Python 웹 애플리케이션 프레임워크(느슨하게 결합된 WSGI 미들웨어 구성 요소로 구성된)의 가능성을 열어줄 수 있습니다. 심지어 기존 프레임워크 개발자들은 자신들의 프레임워크의 기존 서비스를 이러한 방식으로 제공하도록 리팩터링하여, 모놀리식 프레임워크보다는 WSGI와 함께 사용되는 라이브러리에 더 가깝게 만들 수 있습니다. 이렇게 되면 애플리케이션 개발자는 단일 프레임워크의 모든 장단점에 얽매이지 않고 특정 기능에 대해 “최고의 구성 요소”를 선택할 수 있게 됩니다.
물론, 이 글이 작성되는 시점에는 그 날이 아직 멀리 떨어져 있습니다. 그동안 WSGI의 충분한 단기 목표는 모든 프레임워크가 모든 서버에서 사용될 수 있도록 하는 것입니다.
마지막으로, 현재 버전의 WSGI는 웹 서버 또는 서버 게이트웨이와 함께 사용할 애플리케이션 “배포(deploying)”를 위한 특정 메커니즘을 규정하지 않습니다. 현재로서는 이는 서버 또는 게이트웨이에 의해 구현 정의됩니다. 충분한 수의 서버 및 프레임워크가 WSGI를 구현하여 다양한 배포 요구 사항에 대한 현장 경험을 제공한 후에는 WSGI 서버 및 애플리케이션 프레임워크를 위한 배포 표준을 설명하는 또 다른 PEP를 만드는 것이 합리적일 수 있습니다.
사양 개요
WSGI 인터페이스는 “서버” 또는 “게이트웨이” 측과 “애플리케이션” 또는 “프레임워크” 측의 두 가지 측면을 가지고 있습니다. 서버 측은 애플리케이션 측에서 제공하는 호출 가능한 객체(callable object)를 호출합니다. 이 객체가 제공되는 구체적인 방법은 서버 또는 게이트웨이에 달려 있습니다. 일부 서버 또는 게이트웨이는 애플리케이션 배포자가 서버 또는 게이트웨이 인스턴스를 생성하고 애플리케이션 객체를 제공하기 위한 짧은 스크립트를 작성해야 할 것으로 예상됩니다. 다른 서버 및 게이트웨이는 구성 파일 또는 다른 메커니즘을 사용하여 애플리케이션 객체를 어디에서 가져오거나 얻을지 지정할 수 있습니다.
“순수한” 서버/게이트웨이 및 애플리케이션/프레임워크 외에도, 이 사양의 양측을 모두 구현하는 “미들웨어(middleware)” 구성 요소를 생성하는 것도 가능합니다. 이러한 구성 요소는 상위 서버에게는 애플리케이션으로 작동하고, 내장된 애플리케이션에게는 서버로 작동하며, 확장된 API, 콘텐츠 변환, 탐색 및 기타 유용한 기능을 제공하는 데 사용될 수 있습니다.
이 사양 전체에서 “호출 가능한 객체(a callable)”라는 용어는 “함수, 메서드, 클래스, 또는 __call__
메서드를 가진 인스턴스”를 의미합니다. 호출 가능한 객체를 구현하는 서버, 게이트웨이 또는 애플리케이션은 자신의 필요에 맞는 적절한 구현 기술을 선택해야 합니다. 반대로, 호출 가능한 객체를 호출하는 서버, 게이트웨이 또는 애플리케이션은 제공된 호출 가능한 객체의 종류에 의존해서는 안 됩니다. 호출 가능한 객체는 호출만 되어야 하며, 내부를 검사해서는 안 됩니다.
문자열 타입에 대한 참고 사항 (A Note On String Types)
일반적으로 HTTP는 바이트(bytes)를 다루므로, 이 사양은 주로 바이트 처리에 관한 것입니다. 그러나 이러한 바이트의 내용은 종종 어떤 종류의 텍스트 해석을 가지며, Python에서는 문자열이 텍스트를 처리하는 가장 편리한 방법입니다.
하지만 많은 Python 버전 및 구현에서 문자열은 바이트가 아닌 유니코드입니다. 이는 HTTP 컨텍스트에서 바이트와 텍스트 간의 올바른 변환과 사용 가능한 API 사이의 신중한 균형을 요구합니다. 특히 str
타입이 다른 Python 구현 간에 코드를 이식할 때 중요합니다.
따라서 WSGI는 두 가지 종류의 “문자열”을 정의합니다.
- “Native” strings (항상
str
타입으로 구현됨): 요청/응답 헤더 및 메타데이터에 사용됩니다. - “Bytestrings” (Python 3에서는
bytes
타입, 다른 버전에서는str
타입으로 구현됨): 요청 및 응답 본문(예: POST/PUT 입력 데이터 및 HTML 페이지 출력)에 사용됩니다.
혼동하지 마세요: Python의 str
타입이 실제로 내부적으로 유니코드이더라도, “native string”의 내용은 여전히 Latin-1 인코딩을 통해 바이트로 변환될 수 있어야 합니다! (자세한 내용은 이 문서의 “유니코드 문제(Unicode Issues)” 섹션을 참조하세요.)
요컨대, 이 문서에서 “string”이라는 단어를 볼 때는 “native string”, 즉 내부적으로 바이트 또는 유니코드로 구현되었는지와 관계없이 str
타입의 객체를 의미합니다. “bytestring”에 대한 참조를 볼 때는 “Python 3에서는 bytes
타입의 객체, Python 2에서는 str
타입의 객체”로 읽어야 합니다.
따라서 HTTP가 어떤 의미에서는 “실제로 그저 바이트”이더라도, Python의 기본 str
타입을 사용하여 얻을 수 있는 많은 API 편의성이 있습니다.
애플리케이션/프레임워크 측 (The Application/Framework Side)
애플리케이션 객체는 단순히 두 개의 인수를 받는 호출 가능한 객체입니다. “객체”라는 용어는 실제 객체 인스턴스를 요구하는 것으로 오해해서는 안 됩니다. 함수, 메서드, 클래스, 또는 __call__
메서드를 가진 인스턴스 모두 애플리케이션 객체로 사용될 수 있습니다. 사실상 모든 서버/게이트웨이(CGI 제외)가 이러한 반복 요청을 할 것이므로, 애플리케이션 객체는 여러 번 호출될 수 있어야 합니다.
(참고: 이를 “애플리케이션” 객체라고 부르지만, 애플리케이션 개발자가 WSGI를 웹 프로그래밍 API로 사용할 것이라고 해석해서는 안 됩니다! 애플리케이션 개발자는 기존의 고수준 프레임워크 서비스를 사용하여 애플리케이션을 개발할 것으로 가정됩니다. WSGI는 프레임워크 및 서버 개발자를 위한 도구이며, 애플리케이션 개발자를 직접 지원하기 위한 것이 아닙니다.)
다음은 두 가지 예제 애플리케이션 객체입니다. 하나는 함수이고 다른 하나는 클래스입니다.
HELLO_WORLD = b"Hello world!\n"
def simple_app(environ, start_response):
"""가장 간단한 애플리케이션 객체"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [HELLO_WORLD]
class AppClass:
"""동일한 출력을 생성하지만 클래스를 사용합니다."""
# (참고: 여기서는 'AppClass' 자체가 "애플리케이션"이므로,
# 이를 호출하면 'AppClass'의 인스턴스가 반환되며, 이 인스턴스가
# 사양에서 요구하는 "애플리케이션 호출 가능 객체"의 반복 가능한
# 반환 값입니다. 만약 'AppClass'의 *인스턴스*를 애플리케이션
# 객체로 사용하고 싶다면, 애플리케이션 실행을 위해 호출될
# '__call__' 메서드를 구현해야 하며, 서버나 게이트웨이가
# 사용할 인스턴스를 생성해야 합니다.)
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
서버/게이트웨이 측 (The Server/Gateway Side)
서버 또는 게이트웨이는 HTTP 클라이언트로부터 애플리케이션을 대상으로 하는 요청을 받을 때마다 애플리케이션 호출 가능 객체를 한 번 호출합니다. 예를 들어, 애플리케이션 객체를 인수로 받는 함수로 구현된 간단한 CGI 게이트웨이가 있습니다. 이 간단한 예제는 제한된 오류 처리를 가지고 있는데, 기본적으로 잡히지 않은 예외는 sys.stderr
로 덤프되고 웹 서버에 의해 로깅되기 때문입니다.
import os, sys
enc, esc = sys.getfilesystemencoding(), 'surrogateescape'
def unicode_to_wsgi(u):
# 환경 변수를 WSGI "bytes-as-unicode" 문자열로 변환합니다.
return u.encode(enc, esc).decode('iso-8859-1')
def wsgi_to_bytes(s):
return s.encode('iso-8859-1')
def run_with_cgi(application):
environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
environ['wsgi.input'] = sys.stdin.buffer
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):
out = sys.stdout.buffer
if not headers_set:
raise AssertionError("write() before start_response()")
elif not headers_sent:
# 첫 출력이 있기 전에 저장된 헤더를 보냅니다.
status, response_headers = headers_sent[:] = headers_set
out.write(wsgi_to_bytes('Status: %s\r\n' % status))
for header in response_headers:
out.write(wsgi_to_bytes('%s: %s\r\n' % header))
out.write(wsgi_to_bytes('\r\n'))
out.write(data)
out.flush()
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
# 헤더가 이미 전송되었다면 원래 예외를 다시 발생시킵니다.
raise exc_info[1].with_traceback(exc_info[2])
finally:
exc_info = None # 순환 참조를 피합니다.
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
# 참고: 헤더에 대한 오류 검사는 여기서 *헤더가 설정된 후*에 발생해야 합니다.
# 이렇게 하면 오류가 발생할 경우 start_response는 exc_info를 설정한 채로만
# 다시 호출될 수 있습니다.
return write
result = application(environ, start_response)
try:
for data in result:
if data: # 본문이 나타날 때까지 헤더를 보내지 않습니다.
write(data)
if not headers_sent:
write(b'') # 본문이 비어있었다면 지금 헤더를 보냅니다.
finally:
if hasattr(result, 'close'):
result.close()
미들웨어: 양측 역할을 수행하는 구성 요소 (Middleware: Components that Play Both Sides)
단일 객체가 일부 애플리케이션에 대해 서버 역할을 하고, 동시에 일부 서버에 대해 애플리케이션 역할을 수행할 수 있습니다. 이러한 “미들웨어(middleware)” 구성 요소는 다음과 같은 기능을 수행할 수 있습니다.
- 대상 URL에 따라 요청을 다른 애플리케이션 객체로 라우팅하고, 그에 따라
environ
을 재작성합니다. - 여러 애플리케이션 또는 프레임워크가 동일한 프로세스에서 나란히 실행될 수 있도록 합니다.
- 네트워크를 통해 요청과 응답을 전달하여 로드 밸런싱 및 원격 처리를 수행합니다.
- XSL 스타일시트 적용과 같은 콘텐츠 후처리를 수행합니다.
일반적으로 미들웨어의 존재는 인터페이스의 “서버/게이트웨이” 및 “애플리케이션/프레임워크” 양측에 투명하며, 특별한 지원을 필요로 하지 않습니다. 애플리케이션에 미들웨어를 통합하려는 사용자는 마치 애플리케이션인 것처럼 미들웨어 구성 요소를 서버에 제공하고, 미들웨어 구성 요소가 서버인 것처럼 애플리케이션을 호출하도록 구성합니다. 물론 미들웨어가 래핑하는 “애플리케이션”은 실제로 또 다른 미들웨어 구성 요소가 또 다른 애플리케이션을 래핑하는 것일 수 있으며, 이러한 방식으로 “미들웨어 스택”을 형성합니다.
대부분의 경우 미들웨어는 WSGI의 서버 및 애플리케이션 측의 제한 및 요구 사항을 준수해야 합니다. 그러나 어떤 경우에는 미들웨어에 대한 요구 사항이 “순수한” 서버 또는 애플리케이션보다 더 엄격하며, 이러한 사항은 사양에 명시됩니다.
다음은 Joe Strout의 piglatin.py
를 사용하여 text/plain
응답을 Pig Latin으로 변환하는 (농담조의) 미들웨어 구성 요소의 예입니다. (참고: “진짜” 미들웨어 구성 요소는 콘텐츠 타입을 확인하는 더 강력한 방법을 사용하고, 콘텐츠 인코딩도 확인해야 합니다. 또한 이 간단한 예제는 단어가 블록 경계를 넘어 분할될 가능성을 무시합니다.)
from piglatin import piglatin
class LatinIter:
"""변환이 가능한 경우 반복된 출력을 piglatin으로 변환합니다.
변환 가능성은 애플리케이션이 첫 번째 비어있지 않은 bytestring을 yield하기 전까지
변경될 수 있으므로, 'transform_ok'는 변경 가능한 참 값이어야 합니다.
"""
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):
data = self._next()
if self.transform_ok:
return piglatin(data) # Py3에서 바이트 안전해야 합니다.
else:
return data
class Latinator:
# 기본적으로 출력을 변환하지 않습니다.
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):
# 반복 호출일 경우를 대비하여 ok 플래그를 재설정합니다.
del transform_ok[:]
for name, value in response_headers:
if name.lower() == 'content-type' and value == 'text/plain':
transform_ok.append(True)
# Content-Length가 있다면 제거합니다. 그렇지 않으면 잘못됩니다.
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)) # Py3에서 바이트 안전해야 합니다.
return write_latin
else:
return write
return write
return LatinIter(self.application(environ, start_latin), transform_ok)
# Latinator의 제어 하에 foo_app을 실행하고, 예제 CGI 게이트웨이를 사용합니다.
# from foo_app import foo_app
# run_with_cgi(Latinator(foo_app))
사양 세부 정보 (Specification Details)
애플리케이션 객체는 두 개의 위치 인수를 받아야 합니다. 설명을 위해 이들을 environ
및 start_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
매개변수는 아래 “start_response() Callable” 및 “오류 처리(Error Handling)” 섹션에서 설명합니다. 이 매개변수는 애플리케이션이 오류를 잡아서 브라우저에 오류 메시지를 표시하려고 할 때만 사용됩니다.
start_response
호출 가능 객체는 HTTP 응답 본문의 일부로 작성될 바이트스트링을 하나의 위치 매개변수로 받는 write(body_data)
호출 가능 객체를 반환해야 합니다. (참고: write()
호출 가능 객체는 특정 기존 프레임워크의 명령형 출력 API를 지원하기 위해 제공됩니다. 가능하다면 새 애플리케이션이나 프레임워크에서는 사용하지 않아야 합니다. 자세한 내용은 “버퍼링 및 스트리밍(Buffering and Streaming)” 섹션을 참조하세요.)
서버에 의해 호출될 때, 애플리케이션 객체는 0개 이상의 바이트스트링을 yield하는 반복 가능한 객체를 반환해야 합니다. 이는 다양한 방식으로 달성될 수 있습니다. 예를 들어, 바이트스트링 목록을 반환하거나, 바이트스트링을 yield하는 제너레이터 함수이거나, 인스턴스가 반복 가능한 클래스일 수 있습니다. 어떻게 달성되든, 애플리케이션 객체는 항상 0개 이상의 바이트스트링을 yield하는 반복 가능한 객체를 반환해야 합니다.
서버 또는 게이트웨이는 yield된 바이트스트링을 버퍼링되지 않은 방식으로 클라이언트에 전송해야 하며, 다음 바이트스트링을 요청하기 전에 각 바이트스트링의 전송을 완료해야 합니다. (즉, 애플리케이션은 자체 버퍼링을 수행해야 합니다. 애플리케이션 출력이 어떻게 처리되어야 하는지에 대한 자세한 내용은 아래 “버퍼링 및 스트리밍(Buffering and Streaming)” 섹션을 참조하세요.)
서버 또는 게이트웨이는 yield된 바이트스트링을 이진 바이트 시퀀스로 처리해야 합니다. 특히 줄바꿈 문자가 변경되지 않도록 해야 합니다. 애플리케이션은 작성될 바이트스트링이 클라이언트에 적합한 형식인지 확인해야 합니다. (서버 또는 게이트웨이는 HTTP 전송 인코딩을 적용하거나, 바이트 범위 전송과 같은 HTTP 기능을 구현하기 위해 다른 변환을 수행할 수 있습니다. 자세한 내용은 아래 “기타 HTTP 기능(Other HTTP Features)”을 참조하세요.)
len(iterable)
호출이 성공하면 서버는 결과가 정확하다는 것을 신뢰할 수 있어야 합니다. 즉, 애플리케이션이 반환한 반복 가능한 객체가 작동하는 __len__()
메서드를 제공한다면, 정확한 결과를 반환해야 합니다. (이것이 일반적으로 어떻게 사용되는지에 대한 정보는 “Content-Length 헤더 처리(Handling the Content-Length Header)” 섹션을 참조하세요.)
애플리케이션이 반환한 반복 가능한 객체에 close()
메서드가 있다면, 서버 또는 게이트웨이는 현재 요청이 완료될 때(요청이 정상적으로 완료되었든, 반복 중 애플리케이션 오류 또는 브라우저의 조기 연결 해제로 인해 조기에 종료되었든 관계없이) 해당 메서드를 호출해야 합니다. (close()
메서드 요구 사항은 애플리케이션에 의한 리소스 해제를 지원하기 위한 것입니다. 이 프로토콜은 PEP 342의 제너레이터 지원 및 close()
메서드를 가진 다른 일반적인 반복 가능한 객체들을 보완하기 위한 것입니다.)
제너레이터 또는 다른 사용자 정의 이터레이터를 반환하는 애플리케이션은 서버에 의해 조기에 닫힐 수 있으므로 전체 이터레이터가 소비될 것이라고 가정해서는 안 됩니다.
(참고: 애플리케이션은 반복 가능한 객체가 첫 번째 본문 바이트스트링을 yield하기 전에 start_response()
호출 가능 객체를 호출해야 합니다. 그래야 서버가 본문 콘텐츠가 나오기 전에 헤더를 보낼 수 있습니다. 그러나 이 호출은 반복 가능한 객체의 첫 번째 반복에서 수행될 수 있으므로, 서버는 반복 가능한 객체를 반복하기 시작하기 전에 start_response()
가 호출되었다고 가정해서는 안 됩니다.)
마지막으로, 서버 및 게이트웨이는 애플리케이션이 반환한 반복 가능한 객체의 다른 속성을 직접 사용해서는 안 됩니다. 단, wsgi.file_wrapper
가 반환하는 “파일 래퍼”와 같이 해당 서버 또는 게이트웨이에 특정한 타입의 인스턴스인 경우는 예외입니다(선택적 플랫폼별 파일 처리(Optional Platform-Specific File Handling) 참조). 일반적인 경우, 여기에 지정되거나 PEP 234 반복 API 등을 통해 접근되는 속성만 허용됩니다.
environ 변수 (environ Variables)
environ
딕셔너리는 Common Gateway Interface 사양에 정의된 이러한 CGI 환경 변수를 포함해야 합니다. 다음 변수들은 값이 빈 문자열인 경우를 제외하고는 항상 존재해야 합니다(아래에 별도로 명시된 경우 제외).
REQUEST_METHOD
: “GET” 또는 “POST”와 같은 HTTP 요청 메서드. 이는 절대로 빈 문자열일 수 없으므로 항상 필요합니다.SCRIPT_NAME
: 애플리케이션 객체에 해당하는 요청 URL “경로”의 초기 부분으로, 애플리케이션이 자신의 가상 “위치”를 알 수 있도록 합니다. 애플리케이션이 서버의 “루트”에 해당한다면 빈 문자열일 수 있습니다.PATH_INFO
: 요청 URL “경로”의 나머지 부분으로, 애플리케이션 내에서 요청 대상의 가상 “위치”를 지정합니다. 요청 URL이 애플리케이션 루트를 대상으로 하고 후행 슬래시가 없다면 빈 문자열일 수 있습니다.QUERY_STRING
: 요청 URL에서 “?” 뒤에 오는 부분 (있다면). 비어 있거나 없을 수 있습니다.CONTENT_TYPE
: HTTP 요청의Content-Type
필드 내용. 비어 있거나 없을 수 있습니다.CONTENT_LENGTH
: HTTP 요청의Content-Length
필드 내용. 비어 있거나 없을 수 있습니다.SERVER_NAME
,SERVER_PORT
:HTTP_HOST
가 설정되지 않은 경우, 이 변수들을 결합하여 기본값을 결정할 수 있습니다. 자세한 내용은 아래 “URL 재구성(URL Reconstruction)” 섹션을 참조하세요.SERVER_NAME
및SERVER_PORT
는 필수 문자열이며 절대로 비어 있으면 안 됩니다.SERVER_PROTOCOL
: 클라이언트가 요청을 보내는 데 사용한 프로토콜 버전. 일반적으로 “HTTP/1.0” 또는 “HTTP/1.1”과 같으며, 애플리케이션이 HTTP 요청 헤더를 어떻게 처리할지 결정하는 데 사용될 수 있습니다. (이 변수는 요청에 사용된 프로토콜을 나타내므로REQUEST_PROTOCOL
이라고 불려야 할 것입니다. 서버의 응답에 사용될 프로토콜과 반드시 같지는 않기 때문입니다. 그러나 CGI와의 호환성을 위해 기존 이름을 유지해야 합니다.)HTTP_
변수: 클라이언트가 제공한 HTTP 요청 헤더에 해당하는 변수 (즉, 이름이 “HTTP_“로 시작하는 변수). 이러한 변수의 존재 여부는 요청에서 적절한 HTTP 헤더의 존재 여부와 일치해야 합니다.
서버 또는 게이트웨이는 적용 가능한 한 많은 다른 CGI 변수를 제공하려고 시도해야 합니다. 또한 SSL이 사용 중인 경우, 서버 또는 게이트웨이는 HTTPS=on
및 SSL_PROTOCOL
과 같은 Apache SSL 환경 변수 중 적용 가능한 것을 최대한 제공해야 합니다. 그러나 위에 나열된 것 이외의 CGI 변수를 사용하는 애플리케이션은 관련 확장을 지원하지 않는 웹 서버에는 이식할 수 없습니다. (예를 들어, 파일을 게시하지 않는 웹 서버는 의미 있는 DOCUMENT_ROOT
또는 PATH_TRANSLATED
를 제공할 수 없습니다.)
WSGI를 준수하는 서버 또는 게이트웨이는 제공하는 변수와 필요에 따라 그 정의를 문서화해야 합니다. 애플리케이션은 필요한 변수의 존재 여부를 확인하고, 해당 변수가 없는 경우를 대비한 대체 계획을 가지고 있어야 합니다.
참고: 누락된 변수(예: 인증이 발생하지 않은 경우의 REMOTE_USER
)는 environ
딕셔너리에서 제외해야 합니다. 또한 CGI 정의 변수는 존재하는 경우 “native strings”여야 합니다. 어떤 CGI 변수의 값이 str
타입이 아닌 다른 타입인 것은 이 사양에 대한 위반입니다.
CGI 정의 변수 외에도, environ
딕셔너리는 임의의 운영 체제 “환경 변수”를 포함할 수 있으며, 다음 WSGI 정의 변수를 포함해야 합니다.
변수 | 값 |
Status wsgi.version | 튜플 (1, 0) 으로, WSGI 버전 1.0을 나타냅니다. |
wsgi.url_scheme | 애플리케이션이 호출되는 URL의 “scheme” 부분을 나타내는 문자열. 일반적으로 상황에 따라 “http” 또는 “https” 값을 가집니다. |
wsgi.input | HTTP 요청 본문 바이트를 읽을 수 있는 입력 스트림 (파일과 유사한 객체). (서버 또는 게이트웨이는 애플리케이션의 요청에 따라 온디맨드(on-demand)로 읽기를 수행할 수 있거나, 클라이언트의 요청 본문을 미리 읽어 메모리 또는 디스크에 버퍼링하거나, 자신의 선호에 따라 이러한 입력 스트림을 제공하는 다른 기술을 사용할 수 있습니다.) |
wsgi.errors | 프로그램 또는 기타 오류를 표준화되고 잠재적으로 중앙 집중화된 위치에 기록하기 위해 오류 출력을 쓸 수 있는 출력 스트림 (파일과 유사한 객체). 이는 “텍스트 모드” 스트림이어야 합니다. 즉, 애플리케이션은 줄바꿈으로 “\n”을 사용하고, 서버/게이트웨이에 의해 올바른 줄바꿈으로 변환될 것이라고 가정해야 합니다. (str 타입이 유니코드인 플랫폼에서 오류 스트림은 오류를 발생시키지 않고 임의의 유니코드를 받아들이고 기록해야 합니다. 그러나 스트림의 인코딩으로 렌더링할 수 없는 문자를 대체하는 것은 허용됩니다.) |
wsgi.multithread | 동일한 프로세스 내에서 다른 스레드에 의해 애플리케이션 객체가 동시에 호출될 수 있다면 참(True)으로 평가되어야 하며, 그렇지 않다면 거짓(False)으로 평가되어야 합니다. |
wsgi.multiprocess | 동등한 애플리케이션 객체가 다른 프로세스에 의해 동시에 호출될 수 있다면 참(True)으로 평가되어야 하며, 그렇지 않다면 거짓(False)으로 평가되어야 합니다. |
wsgi.run_once | 서버 또는 게이트웨이가 포함된 프로세스의 수명 동안 애플리케이션이 한 번만 호출될 것으로 예상한다면 (그러나 보장하지는 않음!) 참(True)으로 평가되어야 합니다. 일반적으로 이는 CGI(또는 유사한 것) 기반의 게이트웨이에서만 참입니다. |
마지막으로, environ
딕셔너리는 서버 정의 변수도 포함할 수 있습니다. 이러한 변수는 소문자, 숫자, 점, 밑줄만 사용하여 명명되어야 하며, 정의하는 서버 또는 게이트웨이에 고유한 이름으로 접두사가 붙어야 합니다. 예를 들어, mod_python
은 mod_python.some_variable
과 같은 이름의 변수를 정의할 수 있습니다.
입력 및 오류 스트림 (Input and Error Streams)
서버가 제공하는 입력 및 오류 스트림은 다음 메서드를 지원해야 합니다.
메서드 | 스트림 | 참고 사항 |
---|---|---|
wsgi.version |
(1, 0) |
WSGI 버전 1.0을 나타내는 튜플입니다. |
wsgi.url_scheme |
"http" 또는 "https" |
애플리케이션이 호출되는 URL의 “scheme” 부분을 나타내는 문자열입니다. |
wsgi.input |
입력 스트림 객체 | HTTP 요청 본문 바이트를 읽을 수 있는 파일과 유사한 객체입니다. 서버는 이 스트림을 온디맨드로 읽거나, 미리 읽어 버퍼링할 수 있습니다. |
wsgi.errors |
출력 스트림 객체 | 프로그램 또는 기타 오류를 기록할 수 있는 파일과 유사한 출력 스트림입니다. “텍스트 모드” 스트림이어야 하며, 애플리케이션은 "\n" 을 줄바꿈으로 사용해야 합니다. |
wsgi.multithread |
True 또는 False |
동일한 프로세스 내에서 다른 스레드에 의해 애플리케이션 객체가 동시에 호출될 수 있다면 True 입니다. |
wsgi.multiprocess |
True 또는 False |
동등한 애플리케이션 객체가 다른 프로세스에 의해 동시에 호출될 수 있다면 True 입니다. |
wsgi.run_once |
True 또는 False |
서버 또는 게이트웨이가 포함된 프로세스의 수명 동안 애플리케이션이 한 번만 호출될 것으로 예상한다면 True 입니다 (보장하지는 않음). 주로 CGI 기반 게이트웨이에서 True 입니다. |
start_response()
호출 가능 객체 (The start_response()
Callable)
애플리케이션 객체에 전달되는 두 번째 매개변수는 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-Code와 Reason-Phrase로 구성된 문자열이며, 순서대로 단일 공백으로 구분되며 주변에 공백이나 다른 문자가 없습니다. (자세한 내용은 RFC 2616, 섹션 6.1.1 참조.) 문자열에는 제어 문자가 포함되어서는 안 되며, 캐리지 리턴, 라인 피드 또는 이들의 조합으로 종료되어서는 안 됩니다.
response_headers
인수는 (header_name, header_value)
튜플 목록입니다. 이는 Python 리스트여야 하며(type(response_headers)
는 ListType
), 서버는 그 내용을 원하는 방식으로 변경할 수 있습니다. 각 header_name
은 후행 콜론이나 기타 문장 부호 없이 유효한 HTTP 헤더 필드 이름(RFC 2616, 섹션 4.2에 정의됨)이어야 합니다.
각 header_value
는 내장되거나 끝에 캐리지 리턴 또는 라인 피드를 포함한 어떤 제어 문자도 포함해서는 안 됩니다. (이러한 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이 및 중간 응답 처리기가 수행해야 하는 파싱의 복잡성을 최소화하기 위한 것입니다.)
일반적으로 서버 또는 게이트웨이는 클라이언트에 올바른 헤더가 전송되도록 할 책임이 있습니다. 애플리케이션이 HTTP(또는 효력이 있는 기타 관련 사양)에서 요구하는 헤더를 생략하는 경우, 서버 또는 게이트웨이가 이를 추가해야 합니다. 예를 들어, HTTP Date:
및 Server:
헤더는 일반적으로 서버 또는 게이트웨이에서 제공됩니다.
(서버/게이트웨이 개발자를 위한 알림: HTTP 헤더 이름은 대소문자를 구분하지 않으므로, 애플리케이션에서 제공하는 헤더를 검사할 때 이 점을 고려해야 합니다!)
애플리케이션과 미들웨어는 HTTP/1.1의 “hop-by-hop” 기능 또는 헤더, HTTP/1.0의 동등한 기능, 또는 클라이언트와 웹 서버 간의 연결 지속성에 영향을 미치는 헤더를 사용하는 것이 금지됩니다. 이러한 기능은 실제 웹 서버의 전유물이며, 서버 또는 게이트웨이는 애플리케이션이 이를 보내려고 시도하는 것을 치명적인 오류로 간주해야 하며, start_response()
에 제공되는 경우 오류를 발생시켜야 합니다. (“hop-by-hop” 기능 및 헤더에 대한 자세한 내용은 아래 “기타 HTTP 기능(Other HTTP Features)” 섹션을 참조하세요.)
서버는 start_response
가 호출될 때 헤더의 오류를 확인해야 합니다. 그래야 애플리케이션이 아직 실행 중일 때 오류를 발생시킬 수 있습니다.
그러나 start_response
호출 가능 객체는 실제로 응답 헤더를 전송해서는 안 됩니다. 대신, 애플리케이션 반환 값의 첫 번째 반복이 비어있지 않은 바이트스트링을 yield하거나 애플리케이션이 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[1].with_traceback(exc_info[2])
이는 애플리케이션이 잡은 예외를 다시 발생시키고, 원칙적으로 애플리케이션을 중단시켜야 합니다. (HTTP 헤더가 이미 전송된 후에는 애플리케이션이 브라우저에 오류 출력을 시도하는 것은 안전하지 않습니다.) 애플리케이션은 exc_info
를 사용하여 start_response
를 호출한 경우 start_response
가 발생시키는 예외를 잡아서는 안 됩니다. 대신, 이러한 예외가 서버 또는 게이트웨이로 전파되도록 허용해야 합니다. 자세한 내용은 아래 “오류 처리(Error Handling)”를 참조하세요.
애플리케이션은 exc_info
인수가 제공되는 경우에만 start_response
를 여러 번 호출할 수 있습니다. 더 정확히 말하면, 현재 애플리케이션 호출 내에서 start_response
가 이미 호출된 경우 exc_info
인수 없이 start_response
를 호출하는 것은 치명적인 오류입니다. 여기에는 start_response
의 첫 호출이 오류를 발생시킨 경우도 포함됩니다. (정확한 논리 예시는 위 CGI 게이트웨이 예시를 참조하세요.)
참고: start_response
를 구현하는 서버, 게이트웨이 또는 미들웨어는 추적 및 관련 프레임을 통해 순환 참조가 생성되는 것을 피하기 위해 함수의 실행 기간을 넘어 exc_info
매개변수에 대한 참조를 유지하지 않도록 해야 합니다. 이를 수행하는 가장 간단한 방법은 다음과 같습니다.
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
# 여기서 exc_info로 작업 수행
finally:
exc_info = None # 순환 참조 방지
예제 CGI 게이트웨이는 이 기술의 또 다른 예시를 제공합니다.
Content-Length 헤더 처리 (Handling the Content-Length Header)
애플리케이션이 Content-Length
헤더를 제공하는 경우, 서버는 헤더가 허용하는 것보다 많은 바이트를 클라이언트에 전송해서는 안 되며, 충분한 데이터가 전송되었을 때 응답 반복을 중지하거나, 애플리케이션이 그 지점을 넘어 write()
를 시도하면 오류를 발생시켜야 합니다. (물론, 애플리케이션이 명시된 Content-Length
를 충족하기에 충분한 데이터를 제공하지 않으면, 서버는 연결을 닫고 오류를 기록하거나 보고해야 합니다.)
애플리케이션이 Content-Length
헤더를 제공하지 않으면, 서버 또는 게이트웨이는 이를 처리하기 위해 여러 접근 방식 중 하나를 선택할 수 있습니다. 이 중 가장 간단한 방법은 응답이 완료될 때 클라이언트 연결을 닫는 것입니다.
그러나 특정 상황에서 서버 또는 게이트웨이는 Content-Length
헤더를 생성하거나, 적어도 클라이언트 연결을 닫을 필요를 피할 수 있습니다. 애플리케이션이 write()
호출 가능 객체를 호출하지 않고 len()
이 1인 반복 가능한 객체를 반환하는 경우, 서버는 반복 가능한 객체에서 yield되는 첫 번째 바이트스트링의 길이를 사용하여 Content-Length
를 자동으로 결정할 수 있습니다.
또한 서버와 클라이언트 모두 HTTP/1.1 “청크 인코딩(chunked encoding)”을 지원한다면, 서버는 청크 인코딩을 사용하여 write()
호출 또는 반복 가능한 객체에서 yield되는 각 바이트스트링에 대해 청크를 보낼 수 있으며, 따라서 각 청크에 대한 Content-Length
헤더를 생성합니다. 이는 서버가 클라이언트 연결을 유지하고 싶다면 그렇게 할 수 있도록 합니다. 서버는 이를 수행할 때 RFC 2616을 완전히 준수해야 하며, 그렇지 않으면 Content-Length
가 없을 때 처리하기 위한 다른 전략 중 하나로 돌아가야 합니다.
(참고: 애플리케이션과 미들웨어는 청킹 또는 gzip과 같은 어떤 종류의 Transfer-Encoding
도 출력에 적용해서는 안 됩니다. “hop-by-hop” 작업으로서, 이러한 인코딩은 실제 웹 서버/게이트웨이의 영역입니다. 자세한 내용은 아래 “기타 HTTP 기능(Other HTTP Features)”을 참조하세요.)
버퍼링 및 스트리밍 (Buffering and Streaming)
일반적으로 애플리케이션은 (적당한 크기의) 출력을 버퍼링하고 한 번에 모두 보내는 것이 가장 좋은 처리량을 달성합니다. 이는 Zope와 같은 기존 프레임워크에서 일반적인 접근 방식입니다. 출력은 StringIO
또는 유사한 객체에 버퍼링된 다음, 응답 헤더와 함께 한 번에 전송됩니다.
WSGI의 해당 접근 방식은 애플리케이션이 응답 본문을 단일 바이트스트링으로 포함하는 단일 요소 반복 가능한 객체(예: 리스트)를 단순히 반환하는 것입니다. 이는 HTML 페이지를 렌더링하고 그 텍스트가 메모리에 쉽게 들어가는 대다수의 애플리케이션 기능에 권장되는 접근 방식입니다.
그러나 대용량 파일의 경우, 또는 HTTP 스트리밍의 특수 용도(예: 멀티파트 “서버 푸시”)의 경우, 애플리케이션은 출력을 더 작은 블록으로 제공해야 할 수 있습니다(예: 대용량 파일을 메모리에 로드하는 것을 피하기 위해). 또한 응답의 일부가 생성하는 데 시간이 오래 걸리지만, 그 앞부분을 먼저 보내는 것이 유용한 경우도 있습니다.
이러한 경우, 애플리케이션은 일반적으로 출력을 블록별로 생성하는 이터레이터(종종 제너레이터-이터레이터)를 반환합니다. 이러한 블록은 멀티파트 경계와 일치하도록 분할되거나(“서버 푸시”의 경우), 시간이 오래 걸리는 작업(예: 디스크에 있는 파일의 다른 블록 읽기) 직전에 분할될 수 있습니다.
WSGI 서버, 게이트웨이 및 미들웨어는 어떤 블록의 전송도 지연해서는 안 됩니다. 애플리케이션이 다음 블록을 생성하는 동안에도 블록을 클라이언트에 완전히 전송하거나, 전송을 계속할 것을 보장해야 합니다. 서버/게이트웨이 또는 미들웨어는 세 가지 방법 중 하나로 이 보장을 제공할 수 있습니다.
- 애플리케이션에 제어를 반환하기 전에 전체 블록을 운영 체제에 전송하고 (모든 OS 버퍼를 플러시하도록 요청).
- 애플리케이션이 다음 블록을 생성하는 동안에도 블록이 계속 전송되도록 다른 스레드를 사용.
- (미들웨어만) 전체 블록을 상위 게이트웨이/서버로 전송.
이러한 보장을 제공함으로써 WSGI는 애플리케이션이 출력 데이터의 임의 지점에서 전송이 중단되지 않도록 보장할 수 있도록 합니다. 이는 멀티파트 경계 사이의 데이터가 클라이언트에 완전히 전송되어야 하는 멀티파트 “서버 푸시” 스트리밍과 같은 기능에 매우 중요합니다.
미들웨어의 블록 경계 처리 (Middleware Handling of Block Boundaries)
비동기 애플리케이션 및 서버를 더 잘 지원하기 위해 미들웨어 구성 요소는 애플리케이션 반복 가능한 객체에서 여러 값을 기다리면서 반복을 차단해서는 안 됩니다. 미들웨어가 어떤 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 축적해야 하는 경우, 빈 바이트스트링을 yield해야 합니다.
이 요구 사항을 다른 방식으로 말하자면, 미들웨어 구성 요소는 기본 애플리케이션이 값을 yield할 때마다 적어도 하나의 값을 yield해야 합니다. 미들웨어가 다른 값을 yield할 수 없는 경우, 빈 바이트스트링을 yield해야 합니다.
이 요구 사항은 비동기 애플리케이션 및 서버가 주어진 수의 애플리케이션 인스턴스를 동시에 실행하는 데 필요한 스레드 수를 줄이기 위해 협력할 수 있도록 보장합니다.
또한 이 요구 사항은 미들웨어가 기본 애플리케이션이 반복 가능한 객체를 반환하자마자 반복 가능한 객체를 반환해야 함을 의미합니다. 미들웨어가 기본 애플리케이션이 yield하는 데이터를 전송하기 위해 write()
호출 가능 객체를 사용하는 것도 금지됩니다. 미들웨어는 기본 애플리케이션이 미들웨어에서 제공하는 write()
호출 가능 객체를 사용하여 보낸 데이터를 전송하기 위해서만 상위 서버의 write()
호출 가능 객체를 사용할 수 있습니다.
write()
호출 가능 객체 (The write()
Callable)
일부 기존 애플리케이션 프레임워크 API는 WSGI와 다른 방식으로 버퍼링되지 않은 출력을 지원합니다. 구체적으로, 버퍼링되지 않은 데이터 블록을 작성하기 위한 “write” 함수 또는 메서드를 제공하거나, 버퍼링된 “write” 함수와 버퍼를 플러시하는 “flush” 메커니즘을 제공합니다.
불행히도, 이러한 API는 스레드 또는 다른 특별한 메커니즘이 사용되지 않는 한 WSGI의 “반복 가능한” 애플리케이션 반환 값으로는 구현될 수 없습니다.
따라서 이러한 프레임워크가 명령형 API를 계속 사용할 수 있도록 WSGI는 start_response
호출 가능 객체가 반환하는 특별한 write()
호출 가능 객체를 포함합니다.
새로운 WSGI 애플리케이션 및 프레임워크는 가능하다면 write()
호출 가능 객체를 사용하지 않아야 합니다. write()
호출 가능 객체는 명령형 스트리밍 API를 지원하기 위한 엄격한 임시방편(hack)입니다. 일반적으로 애플리케이션은 반환된 반복 가능한 객체를 통해 출력을 생성해야 합니다. 이렇게 하면 웹 서버가 동일한 Python 스레드에서 다른 작업을 번갈아 수행할 수 있어, 전체 서버의 처리량을 향상시킬 수 있습니다.
write()
호출 가능 객체는 start_response()
호출 가능 객체에 의해 반환되며, 하나의 매개변수(HTTP 응답 본문의 일부로 작성될 바이트스트링)를 받습니다. 이 바이트스트링은 출력 반복 가능한 객체에 의해 yield된 것처럼 정확히 처리됩니다. 즉, write()
가 반환되기 전에, 전달된 바이트스트링이 클라이언트에 완전히 전송되었거나, 애플리케이션이 계속 진행하는 동안 전송을 위해 버퍼링되었음을 보장해야 합니다.
애플리케이션은 응답 본문의 전부 또는 일부를 생성하기 위해 write()
를 사용하더라도 반복 가능한 객체를 반환해야 합니다. 반환된 반복 가능한 객체는 비어 있을 수 있지만(즉, 비어있지 않은 바이트스트링을 yield하지 않음), 비어있지 않은 바이트스트링을 yield하는 경우, 해당 출력은 서버 또는 게이트웨이에서 정상적으로 처리되어야 합니다(즉, 즉시 전송되거나 대기열에 추가되어야 합니다). 애플리케이션은 반환 반복 가능한 객체 내에서 write()
를 호출해서는 안 되며, 따라서 반복 가능한 객체에 의해 yield된 모든 바이트스트링은 write()
에 전달된 모든 바이트스트링이 클라이언트에 전송된 후에 전송됩니다.
유니코드 문제 (Unicode Issues)
HTTP는 유니코드를 직접 지원하지 않으며, 이 인터페이스도 마찬가지입니다. 모든 인코딩/디코딩은 애플리케이션이 처리해야 합니다. 서버로 전달되거나 서버로부터 전달되는 모든 문자열은 str
또는 bytes
타입이어야 하며, unicode
타입이어서는 안 됩니다. 문자열 객체가 필요한 곳에 유니코드 객체를 사용하는 결과는 정의되지 않습니다.
또한 start_response()
에 상태 또는 응답 헤더로 전달되는 문자열은 인코딩과 관련하여 RFC 2616을 따라야 합니다. 즉, ISO-8859-1 문자이거나 RFC 2047 MIME 인코딩을 사용해야 합니다.
str
또는 StringType
타입이 실제로 유니코드 기반인 Python 플랫폼(예: Jython, IronPython, Python 3 등)에서는 이 사양에서 언급된 모든 “문자열”은 ISO-8859-1 인코딩으로 표현할 수 있는 코드 포인트(포함 \u0000
부터 \u00FF
까지)만 포함해야 합니다. 애플리케이션이 다른 유니코드 문자나 코드 포인트를 포함하는 문자열을 제공하는 것은 치명적인 오류입니다. 마찬가지로, 서버 및 게이트웨이는 다른 유니코드 문자를 포함하는 문자열을 애플리케이션에 제공해서는 안 됩니다.
다시 말하지만, 이 사양에서 “문자열”로 언급된 모든 객체는 str
또는 StringType
타입이어야 하며, unicode
또는 UnicodeType
타입이어서는 안 됩니다. 또한 주어진 플랫폼이 str
/StringType
객체에서 문자당 8비트 이상을 허용하더라도, 이 사양에서 “문자열”로 언급된 모든 값에 대해서는 하위 8비트만 사용될 수 있습니다.
이 사양에서 “bytestrings”로 언급된 값(즉, wsgi.input
에서 읽거나 write()
에 전달되거나 애플리케이션에 의해 yield되는 값)의 경우, Python 3에서는 bytes
타입이어야 하며, 이전 Python 버전에서는 str
타입이어야 합니다.
오류 처리 (Error Handling)
일반적으로 애플리케이션은 자체 내부 오류를 포착하여 브라우저에 유용한 메시지를 표시하려고 노력해야 합니다. (이 맥락에서 “유용한”의 의미는 애플리케이션이 결정할 문제입니다.)
그러나 이러한 메시지를 표시하려면 애플리케이션이 아직 브라우저에 데이터를 보내지 않았어야 합니다. 그렇지 않으면 응답이 손상될 위험이 있습니다. 따라서 WSGI는 애플리케이션이 오류 메시지를 보내거나 자동으로 중단될 수 있도록 하는 메커니즘을 제공합니다. 바로 start_response
의 exc_info
인수입니다. 다음은 그 사용 예시입니다.
try:
# 여기에 일반 애플리케이션 코드
status = "200 Froody"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers)
return ["normal body goes here"]
except:
# XXX MemoryError, KeyboardInterrupt와 같은 런타임 문제는
# 이 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()
를 호출하여 미들웨어가 오류 출력을 캡처하고 수정할 수 있도록 합니다. 이러한 기술은 애플리케이션 개발자가 다음을 따르는 한 작동합니다.
- 오류 응답을 시작할 때 항상
exc_info
를 제공 exc_info
가 제공될 때start_response
가 발생시키는 오류를 절대 포착하지 않음
HTTP 1.1 Expect/Continue (HTTP 1.1 Expect/Continue)
HTTP 1.1을 구현하는 서버 및 게이트웨이는 HTTP 1.1의 “expect/continue” 메커니즘에 대한 투명한 지원을 제공해야 합니다. 이는 여러 방법 중 하나로 수행될 수 있습니다.
Expect: 100-continue
요청을 포함하는 요청에 즉시 “100 Continue” 응답으로 응답하고 정상적으로 진행합니다.- 요청을 정상적으로 진행하지만, 애플리케이션이 입력 스트림에서 처음 읽기를 시도할 때 “100 Continue” 응답을 보내는
wsgi.input
스트림을 애플리케이션에 제공합니다. 그러면 읽기 요청은 클라이언트가 응답할 때까지 차단된 상태로 유지되어야 합니다. - 클라이언트가 서버가 expect/continue를 지원하지 않는다고 판단하고 자체적으로 요청 본문을 보낼 때까지 기다립니다. (이는 최적이 아니며 권장되지 않습니다.)
이러한 동작 제한은 HTTP 1.0 요청이나 애플리케이션 객체를 대상으로 하지 않는 요청에는 적용되지 않습니다. HTTP 1.1 Expect/Continue에 대한 자세한 내용은 RFC 2616, 섹션 8.2.3 및 10.1.1을 참조하세요.
기타 HTTP 기능 (Other HTTP Features)
일반적으로 서버 및 게이트웨이는 “바보처럼” 작동하고 애플리케이션이 출력에 대해 완전한 제어를 할 수 있도록 허용해야 합니다. 응답의 유효한 의미를 변경하지 않는 변경 사항만 수행해야 합니다. 애플리케이션 개발자는 추가 기능을 제공하기 위해 미들웨어 구성 요소를 항상 추가할 수 있으므로, 서버/게이트웨이 개발자는 구현에 보수적이어야 합니다. 어떤 의미에서 서버는 자신을 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-Match
및 If-Modified-Since
요청 헤더와 Last-Modified
및 ETag
응답 헤더를 통해 캐시 유효성 검사를 처리할 수 있음이 명확해집니다. 그러나 이를 수행할 필요는 없으며, 서버/게이트웨이가 그러한 유효성 검사를 수행할 필요가 없으므로, 애플리케이션이 해당 기능을 지원하려면 자체적으로 캐시 유효성 검사를 수행해야 합니다.
마찬가지로, 서버는 애플리케이션의 응답을 재인코딩하거나 전송 인코딩할 수 있지만, 애플리케이션은 자체적으로 적절한 콘텐츠 인코딩을 사용해야 하며, 전송 인코딩을 적용해서는 안 됩니다. 서버는 클라이언트가 요청하고 애플리케이션이 바이트 범위를 기본적으로 지원하지 않는 경우 애플리케이션 응답의 바이트 범위를 전송할 수 있습니다. 다시 말하지만, 원하는 경우 애플리케이션은 자체적으로 이 기능을 수행해야 합니다.
애플리케이션에 대한 이러한 제한 사항이 모든 애플리케이션이 모든 HTTP 기능을 재구현해야 한다는 것을 의미하지는 않습니다. 많은 HTTP 기능은 미들웨어 구성 요소에 의해 부분적으로 또는 완전히 구현될 수 있으므로, 서버 및 애플리케이션 개발자 모두가 동일한 기능을 반복해서 구현할 필요가 없습니다.
스레드 지원 (Thread Support)
스레드 지원 또는 지원 부족은 서버에 따라 다릅니다. 여러 요청을 병렬로 실행할 수 있는 서버는 스레드 안전하지 않은 애플리케이션 또는 프레임워크도 해당 서버와 함께 사용할 수 있도록 단일 스레드 방식으로 애플리케이션을 실행하는 옵션을 제공해야 합니다.
구현/애플리케이션 노트 (Implementation/Application Notes)
서버 확장 API (Server Extension APIs)
일부 서버 개발자는 애플리케이션 또는 프레임워크 개발자가 특수 목적을 위해 사용할 수 있는 더 고급 API를 노출하고자 할 수 있습니다. 예를 들어, mod_python
기반의 게이트웨이는 Apache API의 일부를 WSGI 확장으로 노출할 수 있습니다.
가장 간단한 경우, 이는 mod_python.some_api
와 같은 environ
변수를 정의하는 것 이상의 것을 요구하지 않습니다. 그러나 많은 경우 미들웨어의 존재 가능성으로 인해 어려울 수 있습니다. 예를 들어, environ
변수에서 발견되는 것과 동일한 HTTP 헤더에 대한 접근을 제공하는 API는 environ
이 미들웨어에 의해 수정된 경우 다른 데이터를 반환할 수 있습니다.
일반적으로 WSGI 기능의 일부를 복제, 대체 또는 우회하는 모든 확장 API는 미들웨어 구성 요소와 호환되지 않을 위험이 있습니다. 서버/게이트웨이 개발자는 아무도 미들웨어를 사용하지 않을 것이라고 가정해서는 안 됩니다. 일부 프레임워크 개발자는 프레임워크를 거의 전적으로 다양한 종류의 미들웨어로 기능하도록 구성하거나 재구성할 계획이기 때문입니다.
따라서 최대 호환성을 제공하기 위해 WSGI 기능의 일부를 대체하는 확장 API를 제공하는 서버 및 게이트웨이는 해당 API가 대체하는 API 부분을 사용하여 호출되도록 해당 API를 설계해야 합니다. 예를 들어, HTTP 요청 헤더에 접근하기 위한 확장 API는 애플리케이션이 현재 environ
을 전달하도록 요구해야 합니다. 그래야 서버/게이트웨이가 API를 통해 접근 가능한 HTTP 헤더가 미들웨어에 의해 변경되지 않았음을 확인할 수 있습니다. 확장 API가 HTTP 헤더 내용에 대해 environ
과 항상 일치할 것이라고 보장할 수 없다면, 예를 들어 오류를 발생시키거나, 헤더 컬렉션 대신 None
을 반환하거나, API에 적절한 방식으로 애플리케이션에 서비스를 거부해야 합니다.
마찬가지로, 확장 API가 응답 데이터 또는 헤더를 작성하는 대체 수단을 제공하는 경우, 애플리케이션이 확장된 서비스를 얻기 전에 start_response
호출 가능 객체가 전달되도록 요구해야 합니다. 전달된 객체가 서버/게이트웨이가 원래 애플리케이션에 제공한 것과 동일하지 않다면, 올바른 작동을 보장할 수 없으며 애플리케이션에 확장된 서비스를 제공하는 것을 거부해야 합니다.
이러한 지침은 파싱된 쿠키, 폼 변수, 세션 등과 같은 정보를 environ
에 추가하는 미들웨어에도 적용됩니다. 특히, 이러한 미들웨어는 단순히 값을 environ
에 채워 넣는 대신, environ
에서 작동하는 함수로 이러한 기능을 제공해야 합니다. 이는 미들웨어가 URL 재작성 또는 기타 environ
수정을 수행한 후에 environ
에서 정보가 계산되도록 보장하는 데 도움이 됩니다.
미들웨어 개발자가 해당 확장을 사용하는 애플리케이션에 의해 중재가 우회되는 것을 방지하기 위해 environ
에서 모든 확장 API를 삭제해야 하는 미래를 피하려면, 서버/게이트웨이 및 미들웨어 개발자 모두 이러한 “안전한 확장” 규칙을 따르는 것이 매우 중요합니다!
애플리케이션 구성 (Application Configuration)
이 사양은 서버가 호출할 애플리케이션을 선택하거나 얻는 방법을 정의하지 않습니다. 이러한 및 기타 구성 옵션은 서버에 매우 특정한 문제입니다. 서버/게이트웨이 개발자는 특정 애플리케이션 객체를 실행하도록 서버를 구성하는 방법과 어떤 옵션(예: 스레딩 옵션)을 사용할지 문서화할 것으로 예상됩니다.
반면에 프레임워크 개발자는 프레임워크의 기능을 래핑하는 애플리케이션 객체를 생성하는 방법을 문서화해야 합니다. 서버와 애플리케이션 프레임워크를 모두 선택한 사용자는 둘을 연결해야 합니다. 그러나 이제 프레임워크와 서버 모두 공통 인터페이스를 가지므로, 이는 새로운 서버/프레임워크 쌍마다 상당한 엔지니어링 노력이 아니라 단순히 기계적인 문제여야 합니다.
마지막으로, 일부 애플리케이션, 프레임워크 및 미들웨어는 간단한 문자열 구성 옵션을 받기 위해 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 Reconstruction)
애플리케이션이 요청의 완전한 URL을 재구성하고자 한다면, Ian Bicking이 기여한 다음 알고리즘을 사용하여 그렇게 할 수 있습니다.
from urllib.parse 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 버전 지원 (Supporting Older (<2.2) Versions of Python)
일부 서버, 게이트웨이 또는 애플리케이션은 이전(<2.2) Python 버전을 지원하고자 할 수 있습니다. 현재 작성 시점에 Jython 2.2의 프로덕션 준비 버전이 아직 제공되지 않으므로 Jython이 대상 플랫폼이라면 특히 중요합니다.
서버 및 게이트웨이의 경우, 이는 비교적 간단합니다. Python 2.2 이전 버전을 대상으로 하는 서버 및 게이트웨이는 애플리케이션이 반환하는 모든 반복 가능한 객체를 반복하기 위해 표준 “for” 루프만 사용하도록 자체적으로 제한해야 합니다. 이것이 2.2 이전 이터레이터 프로토콜(아래에서 더 자세히 논의됨)과 “오늘날의” 이터레이터 프로토콜(PEP 234 참조) 모두와 소스 레벨 호환성을 보장하는 유일한 방법입니다.
(이 기술은 Python으로 작성된 서버, 게이트웨이 또는 미들웨어에만 적용됩니다. 다른 언어에서 이터레이터 프로토콜을 올바르게 사용하는 방법에 대한 논의는 이 PEP의 범위를 벗어납니다.)
애플리케이션의 경우, 2.2 이전 Python 버전 지원은 약간 더 복잡합니다.
- 파일 객체를 반환하고 반복 가능한 객체로 작동할 것으로 기대해서는 안 됩니다. Python 2.2 이전에는 파일이 반복 가능하지 않았기 때문입니다. (일반적으로 어쨌든 이렇게 해서는 안 됩니다. 대부분의 경우 성능이 매우 좋지 않을 것입니다!)
wsgi.file_wrapper
또는 애플리케이션별 파일 래퍼 클래스를 사용하세요. (wsgi.file_wrapper
에 대한 자세한 내용과 파일을 반복 가능한 객체로 래핑하는 데 사용할 수 있는 예제 클래스는 “선택적 플랫폼별 파일 처리(Optional Platform-Specific File Handling)”를 참조하세요.) - 사용자 정의 반복 가능한 객체를 반환하는 경우, 2.2 이전 이터레이터 프로토콜을 구현해야 합니다. 즉, 정수 키를 받고 소진될 때
IndexError
를 발생시키는__getitem__
메서드를 제공해야 합니다. (내장 시퀀스 타입도 이 프로토콜을 구현하므로 허용됩니다.)
마지막으로, 2.2 이전 Python 버전을 지원하고자 하고, 애플리케이션 반환 값을 반복하거나 자체적으로 반복 가능한 객체를 반환하는 미들웨어는 위의 적절한 권장 사항을 따라야 합니다.
(참고: 2.2 이전 Python 버전을 지원하려면 모든 서버, 게이트웨이, 애플리케이션 또는 미들웨어는 대상 버전에서 사용할 수 있는 언어 기능만 사용하고, True
및 False
대신 1
및 0
을 사용해야 한다는 것은 말할 필요도 없습니다.)
선택적 플랫폼별 파일 처리 (Optional Platform-Specific File Handling)
일부 운영 환경은 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
를 제공하는 경우, 이는 하나의 필수 위치 매개변수와 하나의 선택적 위치 매개변수를 받는 호출 가능한 객체여야 합니다. 첫 번째 매개변수는 전송될 파일과 유사한 객체이며, 두 번째 매개변수는 선택적인 블록 크기 “제안”(서버/게이트웨이가 사용할 필요는 없음)입니다. 호출 가능한 객체는 반복 가능한 객체를 반환해야 하며, 서버/게이트웨이가 애플리케이션의 반환 값으로 반복 가능한 객체를 실제로 받기 전까지는 데이터 전송을 수행해서는 안 됩니다. (그렇지 않으면 미들웨어가 응답 데이터를 해석하거나 재정의할 수 없게 됩니다.)
“파일과 유사한” 것으로 간주되려면, 애플리케이션이 제공하는 객체는 선택적 size
인수를 받는 read()
메서드를 가져야 합니다. close()
메서드를 가질 수 있으며, 그럴 경우 wsgi.file_wrapper
가 반환하는 반복 가능한 객체는 원래 파일과 유사한 객체의 close()
메서드를 호출하는 close()
메서드를 가져야 합니다. “파일과 유사한” 객체가 Python 내장 파일 객체의 이름과 일치하는 다른 메서드나 속성(예: fileno()
)을 가지고 있다면, wsgi.file_wrapper
는 이러한 메서드나 속성이 내장 파일 객체와 동일한 의미를 가진다고 가정할 수 있습니다.
플랫폼별 파일 처리의 실제 구현은 애플리케이션이 반환되고 서버 또는 게이트웨이가 래퍼 객체가 반환되었는지 확인한 후에 발생해야 합니다. (다시 말하지만, 미들웨어, 오류 핸들러 등의 존재로 인해 생성된 래퍼가 실제로 사용될 것이라고 보장되지 않습니다.)
close()
처리 외에, 애플리케이션에서 파일 래퍼를 반환하는 의미는 애플리케이션이 iter(filelike.read, '')
를 반환한 것과 동일해야 합니다. 즉, 전송은 전송이 시작될 때 “파일” 내의 현재 위치에서 시작하여 끝에 도달하거나 Content-Length
바이트가 작성될 때까지 계속되어야 합니다. (애플리케이션이 Content-Length
를 제공하지 않으면, 서버는 기본 파일 구현에 대한 지식을 사용하여 파일에서 하나를 생성할 수 있습니다.)
물론, 플랫폼별 파일 전송 API는 일반적으로 임의의 “파일과 유사한” 객체를 허용하지 않습니다. 따라서 wsgi.file_wrapper
는 파일과 유사한 객체가 지원하는 플랫폼별 API와 함께 사용하기에 적합한지 판단하기 위해 fileno()
(Unix와 유사한 OS) 또는 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):
# result.filelike이 플랫폼별 API와 함께 사용할 수 있는지 확인하고,
# 그렇다면 해당 API를 사용하여 결과를 전송합니다.
# 그렇지 않으면 아래 일반적인 반복 가능한 처리 루프로 폴백합니다.
for data in result:
# etc.
finally:
if hasattr(result, 'close'):
result.close()
질의응답 (Questions and Answers)
-
environ
이 딕셔너리여야 하는 이유는 무엇입니까? 서브클래스를 사용하면 무엇이 잘못됩니까? 딕셔너리를 요구하는 이유는 서버 간 이식성을 극대화하기 위함입니다. 대안은 딕셔너리의 메서드 중 일부를 표준 및 이식 가능한 인터페이스로 정의하는 것이지만, 실제로는 대부분의 서버가 딕셔너리로 충분하다고 생각하고 프레임워크 개발자는 딕셔너리의 모든 기능을 기대할 것입니다. 그러나 일부 서버가 딕셔너리를 사용하지 않기로 선택하면, 해당 서버가 사양을 “준수”하더라도 상호 운용성 문제가 발생합니다. 따라서 딕셔너리를 의무화하면 사양이 단순화되고 상호 운용성이 보장됩니다. -
write()
를 호출하고 바이트스트링/반복 가능한 객체를 yield/반환할 수 있는 이유는 무엇입니까? 한 가지 방법만 선택해야 하지 않습니까? 반복 방식만 지원한다면 “푸시(push)” 가용성을 가정하는 현재 프레임워크는 어려움을 겪습니다. 그러나write()
를 통한 푸시만 지원한다면, 대용량 파일 전송 시 서버 성능이 저하됩니다(작업자 스레드가 모든 출력이 전송될 때까지 새 요청 작업을 시작할 수 없는 경우). 따라서 이 절충안은 애플리케이션 프레임워크가 두 가지 접근 방식을 적절히 지원할 수 있도록 허용하며, 푸시 전용 접근 방식보다 서버 구현자에게 약간의 부담만 더 줍니다. -
close()
는 무엇을 위한 것입니까? 애플리케이션 객체 실행 중에 쓰기 작업이 수행될 때, 애플리케이션은try/finally
블록을 사용하여 리소스가 해제되도록 할 수 있습니다. 그러나 애플리케이션이 반복 가능한 객체를 반환하면, 사용된 모든 리소스는 해당 반복 가능한 객체가 가비지 수집될 때까지 해제되지 않습니다.close()
이디엄은 애플리케이션이 요청이 끝날 때 중요한 리소스를 해제할 수 있도록 하며, PEP 325에서 제안된 제너레이터의try/finally
지원과 향후 호환됩니다. -
이 인터페이스는 왜 이렇게 저수준입니까? 저는 기능 X(예: 쿠키, 세션, 영속성 등)를 원합니다! 이것은 또 다른 Python 웹 프레임워크가 아닙니다. 프레임워크가 웹 서버와 통신하는 방식이며, 그 반대도 마찬가지입니다. 이러한 기능을 원한다면, 원하는 기능을 제공하는 웹 프레임워크를 선택해야 합니다. 그리고 해당 프레임워크가 WSGI 애플리케이션을 생성할 수 있도록 한다면, 대부분의 WSGI 지원 서버에서 실행할 수 있어야 합니다. 또한 일부 WSGI 서버는
environ
딕셔너리에 제공되는 객체를 통해 추가 서비스를 제공할 수 있습니다. 자세한 내용은 해당 서버 문서를 참조하세요. (물론, 이러한 확장을 사용하는 애플리케이션은 다른 WSGI 기반 서버에는 이식할 수 없습니다.) -
왜 일반 HTTP 헤더 대신 CGI 변수를 사용합니까? 그리고 WSGI 정의 변수와 왜 섞여 있습니까? 많은 기존 웹 프레임워크는 CGI 사양에 크게 의존하고 있으며, 기존 웹 서버는 CGI 변수를 생성하는 방법을 알고 있습니다. 대조적으로, 인바운드 HTTP 정보를 나타내는 다른 방법들은 파편화되어 있고 시장 점유율이 낮습니다. 따라서 CGI “표준”을 사용하는 것이 기존 구현을 활용하는 좋은 방법으로 보입니다. WSGI 변수와 섞는 것에 대해서는, 이를 분리하면 두 개의 딕셔너리 인수를 전달해야 하며, 실제적인 이점은 없습니다.
-
상태 문자열은 어떻습니까? “200 OK” 대신 200만 전달할 수 없습니까? 이렇게 하면 서버 또는 게이트웨이가 숫자 상태와 해당 메시지 테이블을 가져야 하므로 복잡해집니다. 대조적으로, 애플리케이션 또는 프레임워크 개발자는 자신이 사용하는 특정 응답 코드에 맞는 추가 텍스트를 입력하기 쉽고, 기존 프레임워크는 종종 필요한 메시지를 포함하는 테이블을 이미 가지고 있습니다. 따라서 균형을 고려할 때 서버 또는 게이트웨이 대신 애플리케이션/프레임워크가 책임을 지는 것이 더 낫습니다.
-
wsgi.run_once
가 앱을 한 번만 실행한다고 보장하지 않는 이유는 무엇입니까? 이는 애플리케이션에게 “자주 실행되지 않도록 준비하라”는 단순한 제안이기 때문입니다. 이는 캐싱, 세션 등에 대한 여러 작동 모드를 가진 애플리케이션 프레임워크를 위한 것입니다. “다중 실행” 모드에서는 이러한 프레임워크가 캐시를 미리 로드할 수 있으며, 각 요청 후에 로그나 세션 데이터를 디스크에 쓰지 않을 수 있습니다. “단일 실행” 모드에서는 이러한 프레임워크가 미리 로드를 피하고 각 요청 후에 필요한 모든 쓰기 작업을 플러시합니다.그러나 후자 모드에서 올바른 작동을 확인하기 위해 애플리케이션 또는 프레임워크를 테스트하려면 여러 번 호출해야 할 수 있습니다(또는 적어도 편리할 수 있음). 따라서 애플리케이션은
wsgi.run_once
가True
로 설정되었다고 해서 다시 실행되지 않을 것이라고 확실히 가정해서는 안 됩니다. -
기능 X(딕셔너리, 호출 가능한 객체 등)는 애플리케이션 코드에서 사용하기에 보기 흉합니다. 대신 객체를 사용하지 않는 이유는 무엇입니까? WSGI의 모든 이러한 구현 선택은 기능을 서로 분리하기 위해 특별히 의도된 것입니다. 이러한 기능을 캡슐화된 객체로 재결합하면 서버 또는 게이트웨이를 작성하는 것이 다소 어려워지고, 전체 기능의 작은 부분만 대체하거나 수정하는 미들웨어를 작성하는 것이 훨씬 더 어려워집니다.
본질적으로 미들웨어는 “책임 연쇄(Chain of Responsibility)” 패턴을 가지기를 원합니다. 이를 통해 일부 함수에 대한 “핸들러” 역할을 하면서 다른 함수는 변경되지 않은 채로 유지할 수 있습니다. 인터페이스가 확장 가능하게 유지되려면 일반 Python 객체로는 이를 수행하기 어렵습니다. 예를 들어, 확장(예: 미래 WSGI 버전에 의해 정의된 속성)이 전달되도록 하려면
__getattr__
또는__getattribute__
오버라이드를 사용해야 합니다.이러한 유형의 코드는 100% 정확하게 작성하기가 매우 어렵고, 직접 작성하고 싶어하는 사람이 거의 없습니다. 따라서 다른 사람의 구현을 복사하겠지만, 복사한 사람이 또 다른 엣지 케이스를 수정할 때 업데이트하지 못할 것입니다.
더 나아가, 이 필요한 상용구(boilerplate)는 순전히 부가적인 것이며, 애플리케이션 프레임워크 개발자를 위한 약간 더 예쁜 API를 지원하기 위해 미들웨어 개발자들이 지불하는 개발자 세금입니다. 그러나 애플리케이션 프레임워크 개발자는 일반적으로 하나의 프레임워크만 WSGI를 지원하도록 업데이트할 것이며, 프레임워크 전체의 매우 제한된 부분에서만 업데이트할 것입니다. 이는 그들의 첫 번째(그리고 아마도 유일한) WSGI 구현이 될 가능성이 높으므로, 이 사양을 손쉽게 사용하여 구현할 것입니다. 따라서 객체 속성 등으로 API를 “더 예쁘게” 만드는 노력은 이 대상에게는 낭비될 가능성이 높습니다.
직접적인 웹 애플리케이션 프로그래밍(웹 프레임워크 개발과 반대)에 사용하기 위해 더 예쁘거나 개선된 WSGI 인터페이스를 원하는 사람들에게는 애플리케이션 개발자가 편리하게 사용할 수 있도록 WSGI를 래핑하는 API 또는 프레임워크를 개발하도록 권장합니다. 이렇게 하면 WSGI는 서버 및 미들웨어 개발자에게는 편리하게 저수준으로 유지되면서도 애플리케이션 개발자에게 “보기 흉하게” 보이지 않을 수 있습니다.
제안/논의 중 (Proposed/Under Discussion)
다음 항목들은 현재 Web-SIG 및 다른 곳에서 논의 중이거나 PEP 작성자의 “할 일” 목록에 있습니다.
wsgi.input
이 파일 대신 이터레이터여야 합니까? 이는 비동기 애플리케이션 및 청크 인코딩 입력 스트림에 도움이 될 것입니다.- 입력이 사용 가능할 때까지 또는 콜백이 발생할 때까지 애플리케이션 출력 반복을 일시 중지하는 선택적 확장이 논의 중입니다.
- 동기 대 비동기 앱 및 서버, 관련 스레딩 모델, 그리고 이러한 영역의 문제/설계 목표에 대한 섹션을 추가합니다.
감사 (Acknowledgements)
이 개정된 초안을 가능하게 한 Web-SIG 메일링 리스트의 많은 분들의 사려 깊은 피드백에 감사드립니다. 특히:
- Gregory “Grisha” Trubetskoy (
mod_python
의 저자): 첫 초안이 “평범한 CGI”보다 이점을 제공하지 않는다고 비판하여, 더 나은 접근 방식을 찾도록 격려해주셨습니다. - Ian Bicking: 멀티스레딩 및 다중 프로세스 옵션을 제대로 지정하도록 설득하고, 서버가 애플리케이션에 사용자 정의 확장 데이터를 제공하는 메커니즘을 제공하도록 졸라주셨습니다.
- Tony Lownds: 상태와 헤더를 받아
write
함수를 반환하는start_response
함수 개념을 제시했습니다. 그의 의견은 특히 애플리케이션 오류 메시지를 재정의하는 미들웨어를 허용하는 영역에서 예외 처리 기능 설계에도 영향을 주었습니다. - Alan Kennedy: (사양이 최종 확정되기 훨씬 전에) WSGI-on-Jython을 구현하려는 용감한 시도로 “이전 Python 버전 지원” 섹션뿐만 아니라 선택적
wsgi.file_wrapper
기능 및 초기 바이트/유니코드 결정에도 영향을 주었습니다. - Mark Nottingham: HTTP RFC 준수 문제, 특히 그가 지적하기 전까지는 존재조차 몰랐던 HTTP/1.1 기능과 관련하여 사양을 광범위하게 검토했습니다.
- Graham Dumpleton: (저의 게으름과 어리석음에도 불구하고) WSGI의 Python 3 버전을 만들기 위해 지칠 줄 모르고 노력했으며, “native strings” 대 “byte strings” 개념을 제안하고, 수많은 HTTP,
wsgi.input
및 기타 개정 사항을 신중하게 해결했습니다. 이 새로운 PEP의 대부분의 공로가 그에게 있습니다.
참조 (References)
- Python Wiki “Web Programming” 토픽 (https://wiki.python.org/moin/WebProgramming)
- The Common Gateway Interface Specification, v 1.1, 3rd Draft (https://datatracker.ietf.org/doc/html/draft-coar-cgi-v11-03)
- mod_ssl Reference, “Environment Variables” (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25)
- Procedural issues regarding modifications to PEP 333 (https://mail.python.org/pipermail/python-dev/2010-September/104114.html)
- SVN revision history for PEP 3333, showing differences from PEP 333 (http://svn.python.org/view/peps/trunk/pep-3333.txt?r1=84854&r2=HEAD)
저작권 (Copyright)
이 문서는 퍼블릭 도메인에 공개되었습니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments