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
- 초록
- PEP 보류
- 근거와 목표
- WSGI와의 차이점
- 명세 개요
- 명세 세부 사항
- 구현/애플리케이션 참고 사항
- 미해결 질문
- 논쟁점
- 참고 문헌
- Copyright
번역·라이선스 안내
이 비공식 한국어 번역은 원문 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 명세를 구현하는 애플리케이션이든 서버든, 2.6보다 이전의 Python 2 버전이나 3.1보다 이전의 Python 3 버전에서 동작하도록 쉽게 작성할 수는 없습니다.
Note
http://bugs.python.org/issue4006 을 수정하여, ‘foo’의 값을 현재 로케일로 디코딩할 수 없을 때 KeyError로 실패하는 대신 os.environ['foo']가 (PEP 383 방식의) 서로게이트를 반환하도록 만든 Python 3 버전이 진정한 최소 Python 3 버전입니다. 그러나 특히 Python 3.0은 지원되지 않습니다.
Note
Python 2.6은 bytes에 대한 별칭과 b"foo" 리터럴 문법을 지원한 첫 번째 Python 버전입니다. 이것이 바로 해당 버전이 Web3가 지원하는 최소 버전인 이유입니다.
설명 가능성과 문서화 가능성이 이 표준에서 내려진 결정들의 주된 기술적 동인입니다.
WSGI와의 차이점
- 모든 프로토콜 전용 환경 이름에는
wsgi.대신web3.이 접두사로 붙습니다. 예를 들어wsgi.input대신web3.input을 사용합니다. - 환경 딕셔너리의 값으로 존재하는 모든 값은 네이티브 문자열이 아니라 명시적으로 bytes 인스턴스입니다. (그러나 환경 키는 네이티브 문자열이며, 플랫폼에 관계없이 항상
str입니다.) - 애플리케이션이 반환하는 모든 값은 상태 코드, 헤더 이름과 값, 본문을 포함하여 bytes 인스턴스여야 합니다.
- WSGI 1.0이
app_iter를 언급한 모든 곳에서, 이 명세는body를 언급합니다. start_response()콜백이 없습니다(따라서write()호출 가능 객체나exc_info데이터도 없습니다).web3.input의readline()함수는 크기 힌트 매개변수를 지원해야 합니다.web3.input의read()함수는 길이 제한이 있어야 합니다. 크기 인자 없이 호출하는 경우 콘텐츠 길이 헤더에 지정된 것보다 더 많이 읽어서는 안 됩니다. 콘텐츠 길이 헤더가 없는 경우 스트림은 읽기 시 아무것도 반환해서는 안 됩니다. 클라이언트에게 지정된 것보다 더 많은 데이터를 요청해서는 절대 안 됩니다.- 미들웨어가 출력을 생성하기 위해 애플리케이션으로부터 더 많은 정보가 필요한 경우에 빈 문자열을 yield해야 한다는 요구 사항은 없습니다(예: “블록 경계의 미들웨어 처리” 항목이 없습니다).
- “file_wrapper”에 전달되는 파일류 객체에는 바이트를 반환하는(텍스트는 절대 반환하지 않는)
__iter__가 있어야 합니다. wsgi.file_wrapper는 지원되지 않습니다.QUERY_STRING,SCRIPT_NAME,PATH_INFO값은 서버가 environ에 반드시 넣어야 합니다(HTTP 요청에서 해당 값을 받지 못한 경우 각각 빈 바이트 인스턴스로 넣습니다).web3.path_info와web3.script_name은 가능하다면 원본 Web3 서버가 Web3 환경에 넣어야 합니다. 사용 가능한 경우 각각은 요청 URI로부터 직접 도출된, 대응하는 CGI 값의 원래 형태인 일반 7비트 ASCII URL 인코딩 변형입니다(%2F 세그먼트 표식과 기타 메타 문자는 그대로 유지됩니다). 서버가 이 값 중 하나(또는 둘 다)를 제공할 수 없는 경우, 제공할 수 없는 값은 환경에서 생략해야 합니다.- 다음 요구 사항은 제거되었습니다: “미들웨어 컴포넌트는 애플리케이션 이터러블로부터 여러 값을 기다리느라 이터레이션을 블록해서는 안 됩니다. 미들웨어가 어떤 출력이라도 생성하기 전에 애플리케이션으로부터 더 많은 데이터를 모아야 한다면, 반드시 빈 문자열을 yield해야 합니다.”
SERVER_PORT는 바이트 인스턴스여야 합니다(정수가 아닙니다).- 서버는 응답 이터러블로부터 길이를 추측하여 추가적인
Content-Length헤더를 삽입해서는 안 됩니다. 이 헤더는 모든 상황에서 애플리케이션 자체가 설정해야 합니다. - 원본 서버가
web3.async기능을 가지고 있다고 알리는 경우, 서버가 사용하는 Web3 애플리케이션 호출 가능 객체는 인자를 받지 않는 호출 가능 객체를 반환할 수 있습니다. 그렇게 하는 경우 원본 서버는 이 호출 가능 객체가None이 아닌 응답을 반환할 때까지 주기적으로 호출해야 하며, 그 응답은 일반적인 Web3 응답 튜플이어야 합니다.
명세 개요
Web3 인터페이스에는 “서버” 또는 “게이트웨이” 측과 “애플리케이션” 또는 “프레임워크” 측이라는 두 측면이 있습니다. 서버 측은 애플리케이션 측이 제공하는 호출 가능 객체를 호출합니다. 그 객체를 제공하는 구체적인 방식은 서버나 게이트웨이에 달려 있습니다. 일부 서버나 게이트웨이는 애플리케이션 배포자에게 서버나 게이트웨이의 인스턴스를 생성하고 거기에 애플리케이션 객체를 제공하는 짧은 스크립트를 작성하도록 요구할 것으로 가정합니다. 다른 서버와 게이트웨이는 설정 파일이나 기타 메커니즘을 사용하여 애플리케이션 객체를 어디에서 임포트하거나 달리 얻어야 하는지 지정할 수 있습니다.
“순수한” 서버/게이트웨이 및 애플리케이션/프레임워크 외에도, 이 명세의 양쪽 측면을 모두 구현하는 “미들웨어” 컴포넌트를 만드는 것도 가능합니다. 이러한 구성 요소는 자신을 포함하는 서버에 대해서는 애플리케이션으로, 자신이 포함하는 애플리케이션에 대해서는 서버로 동작하며, 확장 API, 콘텐츠 변환, 내비게이션 및 기타 유용한 함수를 제공하는 데 사용될 수 있습니다.
이 명세 전체에서 “애플리케이션 호출 가능 객체”라는 용어는 “함수, 메서드 또는 __call__ 메서드를 가진 인스턴스”를 의미하는 것으로 사용합니다. 애플리케이션 호출 가능 객체를 구현하는 서버, 게이트웨이 또는 애플리케이션이 자신의 필요에 맞는 적절한 구현 기법을 선택합니다. 반대로, 호출 가능 객체를 호출하는 서버, 게이트웨이 또는 애플리케이션은 어떤 종류의 호출 가능 객체가 자신에게 제공되었는지에 대해 어떠한 의존성도 가져서는 안 됩니다. 애플리케이션 호출 가능 객체는 호출만 해야 하며, 내부를 검사(introspect)해서는 안 됩니다.
애플리케이션/프레임워크 측
애플리케이션 객체는 인자 하나를 받는 단순한 호출 가능 객체입니다. “객체”라는 용어를 실제 객체 인스턴스가 필요하다는 뜻으로 오해해서는 안 됩니다. 함수, 메서드, 또는 __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, headers, body 세 요소를 산출하는 튜플을 반환하거나, 비동기 서버가 지원하는 경우 None 또는 그 세 요소로 이루어진 튜플을 반환하는 인자 없는 호출 가능 객체를 반환해야 합니다.
status 요소는 b'999 Message here' 형식의 바이트열 상태입니다.
headers는 HTTP 응답 헤더를 설명하는 (header_name, header_value) 쌍의 Python 리스트입니다. headers 구조는 리터럴 Python 리스트여야 하며, 2-튜플을 산출해야 합니다. header_name과 header_value는 모두 bytes 값이어야 합니다.
body는 0개 이상의 bytes 인스턴스를 산출하는 이터러블입니다. 이는 다양한 방법으로 구현할 수 있습니다. 예를 들어 bytes 인스턴스를 담은 리스트를 body로 반환하거나, bytes 인스턴스를 산출하는 제너레이터 함수를 body로 반환하거나, body가 이터러블한 클래스의 인스턴스가 되도록 하는 방법이 있습니다. 어떤 방법으로 구현하든 애플리케이션 객체는 항상 0개 이상의 bytes 인스턴스를 산출하는 body 이터러블을 반환해야 합니다.
서버나 게이트웨이는 산출된 바이트를 버퍼링 없이 클라이언트로 전송해야 하며, 다음 바이트 묶음을 요청하기 전에 각 바이트 묶음의 전송을 완료해야 합니다. (다시 말해, 버퍼링은 애플리케이션이 직접 수행해야 합니다. 애플리케이션 출력을 어떻게 처리해야 하는지에 대한 자세한 내용은 아래의 Buffering and Streaming 절을 참조하십시오.)
서버나 게이트웨이는 산출된 바이트를 바이너리 바이트 시퀀스로 취급해야 합니다. 특히 줄 끝 문자가 변경되지 않도록 보장해야 합니다. 기록할 문자열이 클라이언트에 적합한 형식인지 보장하는 것은 애플리케이션의 책임입니다. (서버나 게이트웨이는 HTTP 전송 인코딩을 적용하거나, 바이트 범위 전송과 같은 HTTP 기능을 구현하기 위해 기타 변환을 수행할 수 있습니다. 자세한 내용은 아래의 Other HTTP Features를 참조하십시오.)
애플리케이션이 반환한 body 이터러블에 close() 메서드가 있다면, 서버나 게이트웨이는 요청이 정상적으로 완료되었든 오류로 인해 일찍 종료되었든 현재 요청이 완료되는 시점에 그 메서드를 반드시 호출해야 합니다. 이는 애플리케이션의 리소스 해제를 지원하기 위한 것이며, PEP 325의 제너레이터 지원 및 close() 메서드를 가진 다른 일반적인 이터러블을 보완하기 위한 것입니다.
마지막으로, 서버와 게이트웨이는 애플리케이션이 반환한 body 이터러블의 다른 어떤 속성도 직접 사용해서는 안 됩니다.
environ 변수
environ 딕셔너리는 Common Gateway Interface(공통 게이트웨이 인터페이스) 명세 [2]에 정의된 다양한 CGI 환경 변수를 포함해야 합니다.
다음 CGI 변수는 반드시 존재해야 합니다. 각 키는 네이티브 문자열입니다. 각 값은 bytes 인스턴스입니다.
Note
Python 3.1+에서 “네이티브 문자열”이란 os.environ.__getitem__이 하는 것처럼 surrogateescape 오류 핸들러를 사용해 디코딩된 str 타입입니다. Python 2.6과 2.7에서 “네이티브 문자열”이란 바이트의 집합을 나타내는 str 타입입니다.
REQUEST_METHOD"GET"또는"POST"와 같은 HTTP 요청 메서드입니다.SCRIPT_NAME- 요청 URL의 “경로” 중 애플리케이션 객체에 해당하는 앞부분으로, 애플리케이션이 자신의 가상 “위치”를 알 수 있게 해 줍니다. 애플리케이션이 서버의 “루트”에 해당하는 경우 이 값은 빈 bytes 인스턴스일 수 있습니다. SCRIPT_NAME은 슬래시 문자(
/)로 구분된 URL 인코딩 세그먼트의 시퀀스를 나타내는 bytes 인스턴스입니다. CGI에 따라%2F문자는PATH_INFO안에서 리터럴 슬래시 문자로 디코딩된다고 가정합니다. PATH_INFO- 요청 URL의 “경로” 중 나머지 부분으로, 애플리케이션 안에서 요청 대상의 가상 “위치”를 가리킵니다. 요청 URL이 애플리케이션 루트를 대상으로 하고 끝에 슬래시가 없다면 이 값은 bytes 인스턴스일 수 있습니다. PATH_INFO는 슬래시 문자(
/)로 구분된 URL 인코딩 세그먼트의 시퀀스를 나타내는 bytes 인스턴스입니다. CGI에 따라%2F문자는PATH_INFO안에서 리터럴 슬래시 문자로 디코딩된다고 가정합니다. QUERY_STRING- 요청 URL에서
"?"뒤에 오는 부분(bytes)이며, 그런 부분이 없으면 빈 bytes 인스턴스입니다. SERVER_NAME,SERVER_PORT- 이 변수들을
SCRIPT_NAME과PATH_INFO(또는 이에 해당하는 원시 값)와 결합하면 URL을 완성할 수 있습니다. 다만 요청 URL을 재구성할 때HTTP_HOST가 있다면SERVER_NAME보다 이를 우선하여 사용해야 한다는 점에 유의하십시오. 자세한 내용은 아래의 URL Reconstruction 절을 참고하십시오.SERVER_PORT는 정수가 아니라 bytes 인스턴스여야 합니다. SERVER_PROTOCOL- 클라이언트가 요청을 보낼 때 사용한 프로토콜의 버전입니다. 일반적으로
"HTTP/1.0"이나"HTTP/1.1"같은 값이며, 애플리케이션은 이 값을 바탕으로 HTTP 요청 헤더를 어떻게 처리할지 결정할 수 있습니다. (이 변수는 요청에 사용된 프로토콜을 나타낼 뿐 서버 응답에 사용될 프로토콜과 반드시 같지는 않으므로REQUEST_PROTOCOL이라고 부르는 편이 더 적절할 것입니다. 그러나 CGI와의 호환성을 위해 기존 이름을 유지해야 합니다.)
다음 CGI 값들이 Web3 환경에 존재할 수 있습니다. 각 키는 네이티브 문자열입니다. 각 값은 bytes 인스턴스입니다.
CONTENT_TYPE- HTTP 요청에 포함된
Content-Type필드의 내용입니다. CONTENT_LENGTH- HTTP 요청에 포함된
Content-Length필드의 내용입니다. HTTP_변수- 클라이언트가 제공한 HTTP 요청 헤더에 해당하는 변수입니다(즉, 이름이
"HTTP_"로 시작하는 변수입니다). 이 변수들의 존재 여부는 요청에 해당 HTTP 헤더가 있는지 여부와 일치해야 합니다.
서버나 게이트웨이는 적용 가능한 다른 CGI 변수를 가능한 한 많이 제공하도록 시도해야 합니다. 이때 각 변수의 키는 문자열이고 값은 bytes 인스턴스입니다. 또한 SSL을 사용하는 경우 서버나 게이트웨이는 HTTPS=on이나 SSL_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.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_web3는 mod_web3.some_variable 같은 이름의 변수를 정의할 수 있습니다.
입력 스트림
서버가 제공하는 입력 스트림(web3.input)은 다음 메서드를 지원해야 합니다:
| 메서드 | 참고 사항 |
|---|---|
read(size) |
1,4 |
readline([size]) |
1,2,4 |
readlines([size]) |
1,3,4 |
__iter__() |
4 |
각 메서드의 의미는 Python 라이브러리 레퍼런스에 문서화된 것과 같으며, 위 표에 나열된 다음 참고 사항만 예외입니다:
- 서버는 클라이언트가 지정한
Content-Length를 넘어서 읽을 필요가 없으며, 애플리케이션이 그 지점을 넘어서 읽으려고 하면 파일 끝(end-of-file) 상태를 흉내 내도 됩니다. 애플리케이션은CONTENT_LENGTH변수에 지정된 것보다 많은 데이터를 읽으려고 해서는 안 됩니다. - 구현은
readline()의 선택적 인자인size를 지원해야 합니다. - 애플리케이션은
readlines()에size인자를 전달하지 않아도 되며, 서버나 게이트웨이는 전달된size인자의 값을 무시해도 됩니다. read,readline,__iter__메서드는 bytes 인스턴스를 반환해야 합니다.readlines메서드는 bytes 인스턴스를 담은 시퀀스를 반환해야 합니다.
위 표에 나열된 메서드는 이 명세를 따르는 모든 서버가 반드시 지원해야 합니다. 이 명세를 따르는 애플리케이션은 input 객체의 다른 메서드나 속성을 절대 사용해서는 안 됩니다. 특히 애플리케이션은 이 스트림에 close() 메서드가 있더라도 스트림을 닫으려고 절대 시도해서는 안 됩니다.
입력 스트림은 요청의 콘텐츠 길이보다 더 많이 읽으려는 시도를 조용히 무시해야 합니다. 콘텐츠 길이가 지정되지 않은 경우, 스트림은 아무것도 반환하지 않는 더미 스트림이어야 합니다.
오류 스트림
서버가 제공하는 오류 스트림(web3.errors)은 다음 메서드를 지원해야 합니다:
| 메서드 | 스트림 | 참고 사항 |
|---|---|---|
flush() |
errors |
1 |
write(str) |
errors |
2 |
writelines(seq) |
errors |
2 |
각 메서드의 의미는 위 표에 나열된 다음 참고 사항을 제외하면 Python 라이브러리 레퍼런스에 문서화된 것과 같습니다:
errors스트림은 되감을 수 없으므로, 서버와 게이트웨이는 버퍼링 없이 쓰기 작업을 즉시 전달해도 됩니다. 이 경우flush()메서드는 아무 작업도 하지 않아도 됩니다. 그러나 이식 가능한 애플리케이션은 출력이 버퍼링되지 않는다거나flush()가 아무 작업도 하지 않는다고 가정할 수 없습니다. 출력이 실제로 기록되었음을 보장해야 한다면flush()를 호출해야 합니다. (예를 들어, 같은 오류 로그에 기록하는 여러 프로세스의 데이터가 뒤섞이는 것을 최소화하기 위해서입니다.)write()메서드는 문자열 인자를 받아들여야 하지만, 바이트열 인자를 반드시 받아들일 필요는 없습니다.writelines()메서드는 전부 문자열로 이루어진 시퀀스 인자를 받아들여야 하지만, 바이트열 인스턴스를 시퀀스의 요소로 반드시 받아들일 필요는 없습니다.
위 표에 나열된 메서드는 이 명세를 따르는 모든 서버가 반드시 지원해야 합니다. 이 명세를 따르는 애플리케이션은 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의 섹션 6.1.1을 참조하십시오.) 이 문자열에는 제어 문자가 포함되어서는 안 되며, 캐리지 리턴이나 라인피드 또는 그 조합으로 끝나서도 안 됩니다.
게이트웨이나 서버는 headers 값이 (header_name, header_value) 튜플로 이루어진 리터럴 Python 리스트라고 가정합니다. 각 header_name은 유효한 HTTP 헤더 필드 이름(RFC 2616의 섹션 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 버전에서 동작하도록 보장하려면, 개발자는 다음 사항을 지켜야 합니다:
- 텍스트 값과 바이트 값이 비교에서 동등하다고 가정하지 마십시오. 그렇게 하면 코드가 Python 2에서는 동작할 수 있지만, Python 3에서는 제대로 동작하지 않습니다. 예를 들어,
somebytes == 'abc'와 같이 작성하지 마십시오. 이는 Python 2에서는 때때로 참이 되지만 Python 3에서는 절대 참이 되지 않습니다. Python 3에서 바이트 시퀀스는 결코 문자열과 같다고 비교되지 않기 때문입니다. 대신 항상 바이트 값은 바이트 값과 비교하십시오. 예를 들면 “somebytes == b’abc‘“와 같습니다. 이렇게 작성한 코드는 Python 2.6, 2.7, 3.1과 호환되며 모두에서 동일하게 동작합니다.'abc'앞의b는 Python 3에 해당 값이 리터럴 bytes 인스턴스임을 알리며, Python 2에서는 상위 호환성을 위한 플라세보입니다. - 인자도 바이트 인스턴스인지 확인하지 않은 채로, 바이트류로 쓰려는 항목의
__contains__메서드를 (직접이든 간접이든) 사용하지 마십시오. 그렇게 하면 코드가 Python 2에서는 동작할 수 있지만, Python 3에서는 제대로 동작하지 않습니다. 예를 들어,'abc' in somebytes'는 Python 3에서는TypeError를 발생시키지만, Python 2.6과 2.7에서는True를 반환합니다. 하지만b'abc' in somebytes는 두 버전에서 똑같이 동작합니다. Python 3.2에서는 bytes 타입이__mod__구현을 얻게 될 수도 있다는 소문이 있으므로, 이 제약이 부분적으로 해제될 수도 있습니다. __getitem__은 사용해서는 안 됩니다.- bytes 인스턴스의
format메서드나__mod__메서드를 (직접적으로든 간접적으로든) 사용하려 하지 마십시오. Python 2에서 우리가 Python 3의bytes와 동등하게 취급하는str타입은 이러한 메서드를 지원하지만, 실제 Python 3의bytes인스턴스는 이러한 메서드를 지원하지 않습니다. 이러한 메서드를 사용하면 코드가 Python 2에서는 동작하지만 Python 3에서는 동작하지 않습니다. - bytes 값과 문자열 값을 이어 붙이려 하지 마십시오. 이는 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 서버, 게이트웨이, 미들웨어는 어떤 블록의 전송도 지연해서는 안 되며, 블록을 클라이언트에 완전히 전송하거나 애플리케이션이 다음 블록을 생성하는 동안에도 전송을 계속할 것을 보장하거나 둘 중 하나를 반드시 해야 합니다. 서버/게이트웨이나 미들웨어는 다음 세 가지 방법 중 하나로 이 보장을 제공할 수 있습니다:
- 애플리케이션에 제어를 돌려주기 전에 블록 전체를 운영 체제에 보내십시오(그리고 모든 O/S 버퍼를 플러시하도록 요청하십시오). 또는
- 다른 스레드를 사용하여 애플리케이션이 다음 블록을 생성하는 동안에도 블록이 계속 전송되도록 하십시오.
- (미들웨어만 해당) 블록 전체를 상위 게이트웨이/서버에 보내십시오.
이 보장을 제공함으로써, Web3는 애플리케이션이 출력 데이터의 임의의 지점에서 전송이 멈추지 않도록 보장할 수 있게 합니다. 이는 예를 들어 멀티파트 경계 사이의 데이터가 클라이언트에 온전히 전송되어야 하는 멀티파트 “서버 푸시” 스트리밍이 제대로 동작하는 데 매우 중요합니다.
유니코드 문제
HTTP는 유니코드를 직접 지원하지 않으며, 이 인터페이스도 마찬가지입니다. 모든 인코딩/디코딩은 애플리케이션이 처리해야 하며, 서버로 전달되거나 서버에서 전달되는 모든 값은 Python 2의 unicode나 Python 3의 str 객체가 아니라, Python 3의 bytes 타입이거나 Python 2의 str 타입 인스턴스여야 합니다.
이 명세에서 언급하는 모든 “bytes 인스턴스”는 반드시 다음을 만족해야 합니다:
- 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” 메커니즘을 투명하게 지원해야 합니다. 이는 다음 여러 방법 중 하나로 할 수 있습니다:
Expect: 100-continue요청이 들어 있는 요청에 즉시 “100 Continue” 응답을 보내고 평소대로 진행합니다.- 요청을 평소대로 진행하되, 애플리케이션이 입력 스트림에서 처음 읽으려고 할 때 “100 Continue” 응답을 보내는
web3.input스트림을 애플리케이션에 제공합니다. 이때 읽기 요청은 클라이언트가 응답할 때까지 블록된 상태로 있어야 합니다. - 클라이언트가 서버에서 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-Match 및 If-Modified-Since 요청 헤더와 Last-Modified 및 ETag 응답 헤더로 캐시 유효성 검사를 처리할 수 있음이 분명합니다. 그러나 서버가 이를 반드시 해야 하는 것은 아니며, 서버/게이트웨이는 이러한 유효성 검사를 할 필요가 없으므로 애플리케이션이 이 기능을 지원하려면 캐시 유효성 검사를 스스로 수행해야 합니다.
마찬가지로, 서버는 애플리케이션의 응답을 다시 인코딩하거나 전송 인코딩할 수 있지만, 애플리케이션은 적절한 콘텐츠 인코딩을 스스로 사용해야 하며, 전송 인코딩을 적용해서는 안 됩니다. 클라이언트가 요청하고 애플리케이션이 바이트 범위를 네이티브로 지원하지 않으면, 서버는 애플리케이션 응답의 바이트 범위를 전송할 수 있습니다. 그러나 이 경우에도, 원한다면 애플리케이션이 이 기능을 자체 함수로 직접 수행하는 것이 바람직합니다.
애플리케이션에 대한 이러한 제약이 모든 애플리케이션이 모든 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을 (bytes 객체로) 재구성하려면 다음 알고리즘을 사용할 수 있습니다:
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은 허용되는 전송 값에 관해 기술했고 여러 값을 이런저런 인코딩으로 디코딩하는 것이 가장 좋을 수 있다고 제안했지만, 그 전에 서버가 어떠한 디코딩도 수행하도록 요구한 적은 없습니다
Python 3에는 바이트를 표현하는 데 대신 사용할 수 있는 문자열 같은 타입이 없고, 그 대신 bytes 타입이 있습니다. Python 3.1 이상에서 bytes 타입은 Python 2의 str과 상당히 비슷하게 동작합니다. 하지만 str.__mod__에 해당하는 동작이 없고, 이터레이션 프로토콜, 포함 여부 검사, 시퀀스 처리, 동등 비교 방식도 다릅니다.
어느 경우든 Python 3에는 Python 2의 str 타입과 똑같이 동작하는 타입이 없으며, 적절한 타입을 만들 수 있게 해 주는 “String ABC”(문자열 ABC) 같은 것이 존재하지 않으므로 그런 타입을 만들 방법도 없습니다. 이러한 설계상의 비호환성 때문에 기존 WSGI 1.0 서버, 미들웨어, 애플리케이션은 2to3로 변환한 후에도 Python 3에서 동작하지 않습니다.
Python 2와 Python 3 모두에서 실행되는 WSGI 애플리케이션을 작성할 수 있도록 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 미들웨어가 만들어질 것으로 가정합니다. 이 미들웨어를 통해 기존 WSGI 1.0 애플리케이션을 Web3 스택에서 실행할 수 있습니다. 이 미들웨어는 Python 3에서 실행될 때 Python 3 str 타입과 HTTP 요청이 나타내는 바이트 값을 서로 대응시켜야 하며, 그에 수반되는 모든 인코딩 추측(또는 설정)도 필요로 합니다.
Note
이러한 미들웨어는 앞으로 Python 3 str과 HTTP 바이트 값을 동등하게 취급하는 대신, 아직 만들어지지 않은 “ebytes” 타입(일명 “bytes-with-benefits”)을 사용할 수도 있습니다. 특히 String ABC(문자열 ABC) 제안이 Python 코어에 채택되어 구현된다면 더욱 그렇습니다.
반대로, Python 2 플랫폼에서 Web3 애플리케이션을 WSGI 1.0 스택 뒤에서 실행할 수 있게 해 주는 WSGI 1.0 미들웨어가 만들어질 것으로 가정합니다.
바이트로서의 environ 값과 응답 값
가벼운 용도로 미들웨어와 애플리케이션을 작성하는 개발자는 환경 값과 응답 값으로 바이트를 사용하는 것을 불편하게 여길 수 있습니다. 특히 이들은 ('%s' % bytes_val)이나 bytes_val.format('123') 같은 일반적인 문자열 서식 함수를 사용할 수 없습니다. Python 3처럼 두 타입이 서로 다른 플랫폼에서는 바이트가 문자열과 같은 API를 갖지 않기 때문입니다. 마찬가지로 그러한 플랫폼에서는 바이트를 텍스트와 바꿔 가며 사용하는 것에 대한 표준 라이브러리의 HTTP 관련 API 지원이 고르지 않을 수 있습니다. 바이트가 불편하거나 라이브러리 API와 호환되지 않는 곳에서는 미들웨어와 애플리케이션 작성자가 그러한 바이트를 명시적으로 텍스트로 디코딩해야 합니다. 이는 미들웨어 작성자에게 특히 불편합니다. 환경 값을 문자열로 다루려면 암묵적인 인코딩에서 디코딩해야 하고, environ 값을 변경해야 한다면 그 값을 environ에 넣기 전에 다시 바이트 스트림으로 인코딩해야 합니다. 명세가 environ 값으로 바이트를 사용하는 것은 가벼운 용도의 개발자에게 불편할 수 있지만, 여러 가지 이점을 제공합니다.
HTTP는 근본적으로 바이트 지향 프로토콜이므로, 애플리케이션에 전달하는 HTTP 값과 서버 값을 바이트 타입으로 나타내는 것이 현실에 가장 가깝게 부합합니다. environ 값이 반드시 문자열이어야 한다고 규정하면, 각 서버는 HTTP 환경이 제공하는 다양한 값의 인코딩을 휴리스틱으로 추측해야 합니다. 모든 값을 문자열로 사용하면 가벼운 용도의 미들웨어 작성자에게는 편의성이 높아질 수 있지만, 값을 의미 있는 비서로게이트(non-surrogate) 문자열로 디코딩할 수 없을 때 모호함과 혼란도 초래합니다.
environ 값으로 바이트를 사용하면, 참여하는 서버가 인코딩 설정 매개변수를 전달받아야 한다고 명세에서 규정해야 할 가능성 자체를 피할 수 있습니다. environ 값을 문자열로 취급하여 바이트로부터 디코딩해야 한다면, 결국 애플리케이션 배포자가 제시하는 정책상의 단서로서 설정 매개변수가 필요해질 수 있습니다. 그러한 정책은 다양한 상황에서 적절한 디코딩 전략을 추측하는 데 사용되며, 결과적으로 특정 애플리케이션 인코딩 정책을 강제할 부담을 서버에 지우게 됩니다. 서버가 둘 이상의 애플리케이션을 서비스해야 한다면 그러한 설정은 금세 복잡해질 것입니다. 또한 많은 정책은 선언적으로 표현하는 것이 불가능할 것입니다.
실제로 HTTP는 복잡하고 레거시 문제가 가득한 프로토콜이어서, 이를 해석하려면 복잡한 휴리스틱 집합이 필요합니다. 이 프로토콜이 이러한 복잡성으로부터 우리를 보호해 줄 수 있다면 좋겠지만, 현실에 걸맞은 수준의 제어권을 애플리케이션 작성자에게 제공하면서 이를 신뢰성 있게 해낼 수는 없습니다. Python 애플리케이션은 레거시 휴리스틱으로 파싱해야 할 뿐만 아니라 기존의 어떤 HTTP 명세에도 부합하지 않는 환경 내 데이터를 자주 다뤄야 합니다. 이러한 상황은 달갑지 않지만 꾸준히 발생하므로, 이를 애플리케이션 개발자에게 숨기는 것은 불가능하며 바람직하지도 않습니다. HTTP 명세 위반이 감지되었을 때 적절한 조치를 결정할 수 있는 사람은 애플리케이션 개발자뿐이기 때문입니다.
일부는 environ 값으로 바이트와 문자열 값을 혼용하자고 주장해 왔습니다. 이 제안은 그러한 전략을 피합니다. environ 값으로 바이트만 사용하면 이 명세 전체를 머릿속에 온전히 담을 수 있습니다. 어떤 값이 문자열이고 어떤 값이 바이트인지 추측할 필요가 없습니다.
모든 environ 값이 문자열이더라도 개발자는 이 프로토콜을 머릿속에 담을 수 있겠지만, 이 명세는 그 전략을 사용하지 않습니다. 아마도 이 점이 바이트 사용과 관련하여 가장 논쟁이 많은 부분이 될 것입니다. 바이트를 옹호하자면, 개발자는 계약 자체가 최선이 아니더라도 일관된 계약을 가진 프로토콜을 선호하는 경우가 많습니다. 서로게이트를 포함한 값이 이미 애플리케이션의 I/O 경계를 넘어선 뒤에 문제를 일으킬 때까지 인코딩 문제를 개발자에게 숨긴다면, 훨씬 이른 시점에 “여기 바이트가 있으니 디코딩하십시오”라는 식으로 문제를 제시하는 경우보다 애플리케이션이 세운 가정을 바로잡는 데 훨씬 더 많은 작업이 필요합니다. 이는 “바이트는 불편하다”라는 가정에 대한 반론의 인자이기도 합니다. 예외적인 경우에 신경 쓰지 않는 가벼운 애플리케이션 개발자에게는 바이트를 제공하는 것이 불편할 수 있지만, 복잡하고 지저분한 상황을 처리해야 하는 애플리케이션 개발자에게는 바이트가 매우 편리합니다. 바이트를 사용하면 책임이 명확히 분리된 상태에서 적절한 수준의 제어가 가능하기 때문입니다.
프로토콜이 바이트를 사용한다면, environ과 반환값에서 바이트만으로 작업하는 일을 더 쾌적하게 해 주는 라이브러리가 만들어질 것으로 예상됩니다. 예를 들어 “WebOb”와 “Werkzeug”라는 WSGI 1.0 라이브러리에 상응하는 라이브러리입니다. 이러한 라이브러리는 편의성과 제어 사이의 간극을 메울 것입니다. 명세는 단순하고 규칙적으로 유지되면서도, 가볍게 작성하는 개발자는 Web3 미들웨어와 애플리케이션 구성 요소를 편리하게 만들 수 있습니다. 이는 인코딩 정책을 프로토콜에 박아 넣는 방식의 합리적인 대안으로 보입니다. 그러한 라이브러리는 프로토콜과 독립적으로 여럿 만들어질 수 있고, 애플리케이션 개발자는 그중에서 특정 작업에 알맞은 수준의 제어와 편의성을 제공하는 라이브러리를 고를 수 있기 때문입니다.
모든 값에 바이트를 사용하는 방식의 몇 가지 대안은 다음과 같습니다:
- 서버가 CGI 및 서버 environ 값을 나타내는 모든 값을 손실이 없는
latin-1인코딩으로 디코딩하여 문자열로 만들게 합니다. 디코딩할 수 없는 바이트는 결과 문자열 안에 몰래 담습니다. - 모든 CGI 및 서버 environ 값을
surrogateescape오류 핸들러와 함께utf-8인코딩을 사용해 문자열로 인코딩합니다. 이 방식은 현존하는 어떤 Python 2에서도 동작하지 않습니다. - 일반적인 용도에 따라 어떤 값은 바이트로, 다른 값은 문자열로 인코딩합니다.
애플리케이션이 CONTENT_LENGTH를 넘어서 web3.input을 읽을 수 있어야 함
[5]에서 Graham Dumpleton은 두 가지를 주장합니다. 첫째, wsgi.input은 데이터가 끝났음을 나타내는 표시로 빈 문자열을 반환해야 합니다. 둘째, 애플리케이션은 EOF 표시로 빈 문자열에만 의존하여 CONTENT_LENGTH에 지정된 바이트 수를 넘어서 읽을 수 있어야 합니다. WSGI는 애플리케이션이 “올바르게 동작하여, CONTENT_LENGTH에 지정된 모든 데이터를 읽고 나면 그 데이터를 처리하고 응답을 반환한다는 것에 의존합니다. 그러면 같은 소켓 연결을 이후의 요청에 사용할 수 있습니다.” Graham은 WSGI 어댑터가 원시 소켓 연결을 반드시 감싸도록 요구하기를 원합니다: “이 래퍼 객체는 지금까지 읽은 데이터의 양을 세어야 하며, 그 양이 CONTENT_LENGTH에 정의된 양에 도달하면 이후의 읽기는 대신 빈 문자열을 반환해야 합니다.” 이는 청크 인코딩과 입력 필터를 지원하는 데 유용할 수 있습니다.
길이를 알 수 없는 web3.input
environ['web3.input']에 내용이 있지만 그 길이를 알 수 없다는 것을 나타내는 방법은 문서화되어 있지 않습니다.
web3.input의 read()는 크기 없이 호출하는 규약을 지원해야 함
[5]에서 Graham Dumpleton은 wsgi.input의 read() 메서드가 인자 없이도 호출할 수 있는 호출 가능 객체여야 하며, 그 결과는 “사용 가능한 모든 요청 내용”이어야 한다고 주장합니다. 논의가 필요합니다.
Armin의 의견: 구현이 이를 지원하도록 요구하게끔 명세를 변경했습니다. 과거에 이미 이 문제로 너무 많은 고생을 했습니다. 하지만 논의에는 열려 있습니다.
입력 필터는 environ CONTENT_LENGTH를 -1로 설정해야 함
[5]에서 Graham Dumpleton은 입력 필터가 입력을 변경했음을 나타내기 위해 environ['CONTENT_LENGTH']를 -1로 설정할 수 있다고 제안합니다.
2-튜플의 리터럴 리스트인 headers
왜 애플리케이션이 2-튜플의 리터럴 리스트인 headers 구조를 반환하도록 합니까? headers가 스택을 따라 올라가는 동안 이터러블이라는 성질은 유지되어야 한다고 생각하지만, 언제나 그 자리에서 변경할 수 있어야 할 필요는 없다고 생각합니다. 그 요구 사항을 완화할 수 있겠습니까?
Armin의 의견: 강력히 찬성합니다
미들웨어가 블록하지 않아야 한다는 요구 사항 제거
다음 요구 사항은 제거되었습니다: “미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리며 이터레이션을 블록해서는 안 됩니다. 미들웨어가 어떤 출력을 생성하기 전에 애플리케이션으로부터 더 많은 데이터를 모아야 한다면, 빈 문자열을 yield 해야 합니다.” 이 요구 사항은 비동기 애플리케이션과 서버를 지원하기 위해 존재했습니다(PEP 333의 “Middleware Handling of Block Boundaries”를 참조하십시오). 이제 비동기 애플리케이션은 web3.async를 지원하는 프로토콜이 명시적으로 처리합니다(Web3 애플리케이션 호출 가능 객체는 그 자체로 호출 가능 객체를 반환할 수 있습니다).
web3.script_name 및 web3.path_info
이 명세에 따르면 오리진 서버는 이 값들을 환경에 넣어야 합니다. SCRIPT_NAME 및 PATH_INFO와 달리, 이 값들은 요청 URI에서 얻은 원래의 URL 인코딩된 형태여야 합니다. 이 값들을 원래 어떻게 계산해야 하는지, 그리고 서버가 URL 재작성을 수행하는 경우 그 값이 무엇이어야 하는지를 정해야 할 것으로 보입니다.
긴 응답 헤더
Bob Brewer는 Web-SIG에서 다음과 같이 지적합니다 [6]:
각 header_value에는 캐리지 리턴이나 라인 피드를 비롯한 어떠한 제어 문자도 중간에든 끝에든 포함되어서는 안 됩니다. (이 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이, 중간 응답 처리기가 수행해야 하는 파싱의 복잡도를 최소화하기 위한 것입니다.) (PEP 333)
이해할 수 있는 일이지만, HTTP 헤더는 (대부분) *TEXT로 정의되어 있으며, “*TEXT의 단어는 RFC 2047의 규칙에 따라 인코딩된 경우에만 ISO-8859-1 이외의 문자 집합에 속한 문자를 포함할 수 있습니다(MAY).” [2] 그리고 RFC 2047은 “‘encoded-word’는 75자를 넘을 수 없습니다… 75자짜리 ‘encoded-word’ 하나에 들어가는 것보다 많은 텍스트를 인코딩하고 싶다면, (CRLF SPACE로 구분된) 여러 개의 ‘encoded-word’를 사용할 수 있습니다.”라고 규정합니다. [3] 이는 HTTP 헤더 폴딩 규칙도 만족합니다: “헤더 필드는 추가되는 각 줄 앞에 최소 하나의 SP 또는 HT를 두어 여러 줄에 걸쳐 확장할 수 있습니다.” (PEP 333)
따라서 제가 HTTP를 읽은 바로는, 어딘가의 어떤 코드가 다소 긴 인코딩된 응답 헤더 값에 줄바꿈을 넣어야 합니다. 세 가지 선택지가 있다고 봅니다:
- 현재 상태를 유지하고, ISO-8859-1 문자 집합에 속하지 않는 75자를 넘는 단어가 포함된 응답 헤더 값을 허용하지 않습니다.
- WSGI 응답 헤더에 줄바꿈 문자를 허용합니다.
- WSGI 서버가 HTTP로 값을 보내기 전에 인코딩과 폴딩을 수행하도록 요구하거나 강력히 권장합니다.
요청 트레일러와 청크 전송 인코딩
요청 콘텐츠에 청크 전송 인코딩을 사용하는 경우, RFC는 요청 트레일러를 허용합니다. 트레일러는 요청 헤더와 비슷하지만 마지막 빈 데이터 청크 뒤에 옵니다. 이 트레일러는 청크 데이터 스트림의 길이가 유한하고 스트림을 모두 읽어 들였을 때만 사용할 수 있습니다. 현재 WSGI와 Web3 모두 트레일러를 지원하지 않습니다.
참고 문헌
Copyright
This document has been placed in the public domain.