[Deferred] PEP 444 - Python Web3 Interface
원문 링크: PEP 444 - Python Web3 Interface
상태: Deferred 유형: Informational 작성일: 19-Jul-2010
PEP 444 – Python Web3 인터페이스
저자: Chris McDonough, Armin Ronacher 논의: Web-SIG list 상태: 연기됨 (Deferred) 유형: 정보 제공 (Informational) 생성일: 2010년 7월 19일
요약 (Abstract)
이 문서는 웹 서버와 Python 웹 애플리케이션 또는 프레임워크 간에 제안된 2세대 표준 인터페이스를 정의합니다.
PEP 연기 (PEP Deferral)
이 PEP에서 다루는 개념에 대한 추가 탐색은 현재 PEP의 목표를 추진하고 피드백을 수집 및 통합하는 데 관심이 있는 담당자가 부족하고, 이를 효과적으로 수행할 충분한 시간이 없기 때문에 연기되었습니다.
이 PEP가 처음 생성된 이후, PEP 3333이 Python 3.2 이상에서 WSGI를 사용할 수 있도록 하는 점진적인 업데이트로 생성되었습니다. 그러나 바이너리 데이터와 텍스트 데이터의 더 깔끔한 분리라는 Python 3의 목표를 더욱 발전시키는 대체 사양도 여전히 가치 있을 수 있습니다.
배경 및 목표 (Rationale and Goals)
이 프로토콜 및 사양은 PEP 333에 설명된 웹 서비스 게이트웨이 인터페이스 (WSGI) 1.0 표준의 영향을 크게 받았습니다. Python 기반 웹 서버와 애플리케이션이 상호 운용될 수 있도록 하는 모든 표준의 상위 수준 배경은 PEP 333에 요약되어 있습니다. 이 문서는 기본적으로 PEP 333을 템플릿으로 사용하여, 다른 표준을 형성하기 위해 여러 부분의 문구를 변경합니다.
Python은 현재 WSGI 1.0 프로토콜을 사용하는 다양한 웹 애플리케이션 프레임워크를 자랑합니다. 그러나 언어 변경으로 인해 WSGI 1.0 프로토콜은 Python 3와 호환되지 않습니다. 이 사양은 Python 2.6, 2.7 및 3.1+ 애플리케이션이 웹 서버와 통신할 수 있도록 하는 표준화된 WSGI와 유사한 프로토콜을 설명합니다. Web3는 명백히 WSGI의 파생물입니다. “WSGI”와 다른 이름을 사용하는 이유는 이전 버전과 전혀 하위 호환되지 않음을 나타내기 위함입니다.
이 사양에 따라 작성된 애플리케이션과 서버는 Python 2.6.X, Python 2.7.X 및 Python 3.1+에서 제대로 작동하도록 의도되었습니다. Web3 사양을 구현하는 애플리케이션이나 서버는 Python 2.6 이전 버전이나 Python 3.1 이전 버전에서 작동하도록 쉽게 작성될 수 없습니다.
참고: os.environ['foo']
가 현재 로케일을 사용하여 foo
의 값을 디코딩할 수 없을 때 KeyError
대신 서러게이트(surrogates)를 반환하도록 (PEP 383처럼) 수정된 Python 3 버전이 진정한 최소 Python 3 버전입니다. 특히, Python 3.0은 지원되지 않습니다.
참고: Python 2.6은 bytes
의 별칭과 b"foo"
리터럴 구문을 지원하는 첫 번째 Python 버전입니다. 이것이 Web3가 지원하는 최소 버전인 이유입니다.
표준 내에서 결정된 사항에 대한 주요 기술적 동기는 설명 가능성(Explicability)과 문서화 가능성(documentability)입니다.
WSGI와의 차이점 (Differences from WSGI)
- 모든 프로토콜별 환경 이름은
wsgi.
대신web3.
으로 시작합니다 (예:wsgi.input
대신web3.input
). - 환경 딕셔너리 값으로 존재하는 모든 값은 네이티브 문자열 대신 명시적으로
bytes
인스턴스입니다. (그러나 환경 키는 플랫폼에 관계없이 항상str
인 네이티브 문자열입니다). - 애플리케이션에서 반환되는 모든 값은 상태 코드, 헤더 이름 및 값, 본문을 포함하여
bytes
인스턴스여야 합니다. - WSGI 1.0이
app_iter
를 참조하는 곳마다, 이 사양은body
를 참조합니다. start_response()
콜백(따라서write()
호출 가능 객체 및exc_info
데이터)이 없습니다.web3.input
의readline()
함수는 크기 힌트(size hint) 매개변수를 지원해야 합니다.web3.input
의read()
함수는 길이를 제한해야 합니다. 크기 인수 없이 호출하면Content-Length
헤더가 지정하는 것보다 더 많이 읽지 않아야 합니다.Content-Length
헤더가 없으면 스트림은read()
시 아무것도 반환하지 않아야 합니다. 클라이언트에서 지정된 것보다 더 많은 데이터를 요청해서는 안 됩니다.- 출력을 생성하기 위해 애플리케이션에서 더 많은 정보가 필요한 경우(예: “미들웨어의 블록 경계 처리” 없음) 미들웨어가 빈 문자열을
yield
할 필요가 없습니다. file_wrapper
에 전달되는 파일과 유사한 객체는bytes
를 반환하는__iter__
를 가져야 합니다 (텍스트는 안 됩니다).wsgi.file_wrapper
는 지원되지 않습니다.QUERY_STRING
,SCRIPT_NAME
,PATH_INFO
값은 서버에 의해environ
에 배치되어야 합니다 (HTTP 요청에서 관련 값을 받지 못한 경우 각각 빈bytes
인스턴스로).web3.path_info
및web3.script_name
은 가능하다면 원본 Web3 서버에 의해 Web3 환경에 배치되어야 합니다. 사용 가능한 경우, 각각은 요청 URI에서 직접 파생된 CGI와 동등한, 원래의 일반 7비트 ASCII, URL 인코딩된 변형입니다 (%2F
세그먼트 마커 및 기타 메타 문자 포함). 서버가 이러한 값 중 하나(또는 둘 다)를 제공할 수 없는 경우, 제공할 수 없는 값을 환경에서 생략해야 합니다.- 이 요구사항은 제거되었습니다: “미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리면서 반복을 차단해서는 안 됩니다. 미들웨어가 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 축적해야 하는 경우, 빈 문자열을 yield해야 합니다.”
SERVER_PORT
는bytes
인스턴스여야 합니다 (정수 아님).- 서버는 응답 이터러블에서 길이를 추측하여 추가
Content-Length
헤더를 삽입해서는 안 됩니다. 이것은 모든 상황에서 애플리케이션 자체에 의해 설정되어야 합니다. - 원본 서버가
web3.async
기능을 광고하는 경우, 서버에서 사용하는 Web3 애플리케이션 호출 가능 객체는 인수를 받지 않는 호출 가능 객체를 반환할 수 있습니다. 이 경우, 이 호출 가능 객체는None
이 아닌 응답(정상적인 Web3 응답 튜플이어야 함)을 반환할 때까지 원본 서버에 의해 주기적으로 호출됩니다.
사양 개요 (Specification Overview)
Web3 인터페이스는 “서버” 또는 “게이트웨이” 측과 “애플리케이션” 또는 “프레임워크” 측으로 나뉩니다. 서버 측은 애플리케이션 측에서 제공하는 호출 가능 객체를 호출합니다. 이 객체가 제공되는 방식은 서버 또는 게이트웨이에 달려 있습니다. 일부 서버 또는 게이트웨이는 애플리케이션 배포자가 서버 또는 게이트웨이의 인스턴스를 생성하고 애플리케이션 객체를 제공하기 위해 짧은 스크립트를 작성해야 할 것이라고 가정합니다. 다른 서버 및 게이트웨이는 구성 파일 또는 기타 메커니즘을 사용하여 애플리케이션 객체를 가져올 위치 또는 얻는 방법을 지정할 수 있습니다.
“순수한” 서버/게이트웨이 및 애플리케이션/프레임워크 외에도, 이 사양의 양쪽 측면을 모두 구현하는 “미들웨어” 구성 요소를 생성하는 것도 가능합니다. 이러한 구성 요소는 포함하는 서버에 대해 애플리케이션으로 작동하고, 포함된 애플리케이션에 대해 서버로 작동하며, 확장된 API, 콘텐츠 변환, 탐색 및 기타 유용한 기능을 제공하는 데 사용될 수 있습니다.
이 사양 전체에서 “애플리케이션 호출 가능 객체”라는 용어는 “함수, 메서드 또는 __call__
메서드를 가진 인스턴스”를 의미합니다. 서버, 게이트웨이 또는 애플리케이션 호출 가능 객체를 구현하는 애플리케이션은 요구 사항에 맞는 적절한 구현 기술을 선택해야 합니다. 반대로, 호출 가능 객체를 호출하는 서버, 게이트웨이 또는 애플리케이션은 제공된 호출 가능 객체의 종류에 의존해서는 안 됩니다. 애플리케이션 호출 가능 객체는 호출만 되어야 하며, 내부 검사를 해서는 안 됩니다.
애플리케이션/프레임워크 측 (The Application/Framework Side)
애플리케이션 객체는 단순히 하나의 인수를 받는 호출 가능 객체입니다. “객체”라는 용어를 실제 객체 인스턴스가 필요하다는 의미로 오해해서는 안 됩니다. 함수, 메서드 또는 __call__
메서드를 가진 인스턴스 모두 애플리케이션 객체로 사용 가능합니다. 거의 모든 서버/게이트웨이(CGI 제외)가 반복적인 요청을 할 것이므로, 애플리케이션 객체는 여러 번 호출될 수 있어야 합니다. 실제 애플리케이션의 구현으로 이를 보장할 수 없는 경우, 각 호출에서 새 인스턴스를 생성하는 함수로 래핑해야 합니다.
참고: “애플리케이션” 객체라고 부르지만, 애플리케이션 개발자가 Web3를 웹 프로그래밍 API로 사용할 것이라는 의미로 해석해서는 안 됩니다. 애플리케이션 개발자는 애플리케이션을 개발하기 위해 기존의 고수준 프레임워크 서비스를 계속 사용할 것이라고 가정합니다. Web3는 프레임워크 및 서버 개발자를 위한 도구이며, 애플리케이션 개발자를 직접 지원하기 위한 것이 아닙니다.
함수인 애플리케이션의 예(simple_app
):
def simple_app(environ):
"""가장 간단한 애플리케이션 객체"""
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):
"""동일한 출력을 생성하지만 인스턴스를 사용합니다.
이 클래스의 인스턴스는 서버에 전달되기 전에 인스턴스화되어야 합니다.
"""
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
에 대한 자세한 내용은 해당 정보를 참조하십시오.
서버/게이트웨이 측 (The Server/Gateway Side)
서버 또는 게이트웨이는 HTTP 클라이언트로부터 애플리케이션을 대상으로 하는 각 요청에 대해 애플리케이션 호출 가능 객체를 한 번 호출합니다. 다음은 애플리케이션 객체를 인수로 받는 함수로 구현된 간단한 CGI 게이트웨이의 예시입니다. 이 간단한 예시는 제한된 오류 처리를 가지고 있습니다. 기본적으로 포착되지 않은 예외는 sys.stderr
로 덤프되고 웹 서버에 의해 기록됩니다.
import locale
import os
import sys
encoding = locale.getpreferredencoding()
stdout = sys.stdout
if hasattr(sys.stdout, 'buffer'): # Python 3 호환성; 바이트를 내보낼 수 있어야 합니다.
stdout = sys.stdout.buffer
def get_environ():
d = {}
for k, v in os.environ.items():
# Python 3 호환성
if not isinstance(v, bytes): # 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('이 웹 서버는 비동기 응답을 지원하지 않습니다.')
body, status, headers = rv
CRLF = 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()
미들웨어: 양측 역할을 수행하는 구성 요소 (Middleware: Components that Play Both Sides)
단일 객체는 일부 애플리케이션에 대해 서버 역할을 수행하는 동시에, 일부 서버에 대해 애플리케이션 역할을 할 수 있습니다. 이러한 “미들웨어” 구성 요소는 다음과 같은 기능을 수행할 수 있습니다.
- 대상 URL에 따라 요청을 다른 애플리케이션 객체로 라우팅하고, 그에 따라
environ
을 다시 작성합니다. - 여러 애플리케이션 또는 프레임워크가 동일한 프로세스에서 나란히 실행되도록 합니다.
- 네트워크를 통해 요청과 응답을 전달하여 로드 밸런싱 및 원격 처리를 수행합니다.
- XSL 스타일시트 적용과 같은 콘텐츠 후처리(postprocessing)를 수행합니다.
미들웨어의 존재는 인터페이스의 “서버/게이트웨이” 및 “애플리케이션/프레임워크” 측 모두에 대해 일반적으로 투명하며, 특별한 지원이 필요하지 않습니다. 애플리케이션에 미들웨어를 통합하려는 사용자는 미들웨어 구성 요소를 마치 애플리케이션인 것처럼 서버에 제공하고, 미들웨어 구성 요소를 마치 서버인 것처럼 애플리케이션을 호출하도록 구성합니다. 물론, 미들웨어가 래핑하는 “애플리케이션”은 실제로 다른 미들웨어 구성 요소가 다른 애플리케이션을 래핑하는 것일 수 있으며, 이를 “미들웨어 스택”이라고 합니다.
미들웨어는 가능하다면 비동기 실행을 지원하거나, 그렇지 않으면 자체적으로 비활성화해야 합니다.
다음은 X-Host
헤더가 존재하면 HTTP_HOST
키를 변경하고 모든 HTML 응답에 주석을 추가하는 미들웨어입니다.
import time
def apply_filter(app, environ, filter_func):
"""애플리케이션의 반환 값을 결과가 준비되면 필터 함수로 전달하는 헬퍼 함수입니다."""
app_response = app(environ)
# 동기식 응답, 지금 필터링
if not hasattr(app_response, '__call__'):
return filter_func(*app_response)
# 비동기식 응답. 결과가 준비되면 필터링
def polling_function():
rv = app_response()
if rv is not None:
return filter_func(*rv)
return polling_function
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':
# 본문이 아스키 호환 인코딩이라고 가정하지만,
# 미들웨어는 실제로 콘텐츠 타입 헤더를 구문 분석하고
# 인코딩을 알아내야 합니다.
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
# 비동기 및 동기 응답 모두에 대해 주어진 필터 함수를 적용하는 apply_filter 함수를 사용합니다.
return apply_filter(app, environ, filter_func)
return new_application
# app = proxy_and_timing_support(app)
사양 세부 사항 (Specification Details)
애플리케이션 호출 가능 객체는 하나의 위치 인수를 받아야 합니다. 설명을 위해 environ
이라고 이름 붙였지만, 이 이름이어야 하는 것은 아닙니다. 서버 또는 게이트웨이는 위치 인수(키워드 인수 아님)를 사용하여 애플리케이션 객체를 호출해야 합니다. (예: 위에서 보여준 대로 body, status, headers = application(environ)
를 호출하여).
environ
매개변수는 CGI 스타일 환경 변수를 포함하는 딕셔너리 객체입니다. 이 객체는 내장 Python 딕셔너리여야 하며 (하위 클래스, UserDict
또는 다른 딕셔너리 에뮬레이션 아님), 애플리케이션은 원하는 방식으로 딕셔너리를 수정할 수 있습니다. 딕셔너리에는 특정 Web3 필수 변수(나중에 설명)도 포함되어야 하며, 아래에서 설명할 규칙에 따라 명명된 서버별 확장 변수도 포함될 수 있습니다.
서버에 의해 호출될 때, 애플리케이션 객체는 세 가지 요소를 포함하는 튜플(status
, headers
, body
)을 반환해야 합니다. 또는 비동기 서버가 지원하는 경우, None
또는 세 가지 요소의 튜플을 반환하는 인수가 없는 호출 가능 객체를 반환할 수 있습니다.
status
요소는 b'999 Message here'
형식의 bytes
로 된 상태입니다.
headers
는 HTTP 응답 헤더를 설명하는 (header_name, header_value)
쌍의 Python 리스트입니다. headers
구조는 리터럴 Python 리스트여야 하며, 두 개의 튜플을 반환해야 합니다. header_name
과 header_value
모두 bytes
값이어야 합니다.
body
는 0개 이상의 bytes
인스턴스를 반환하는 이터러블입니다. 이는 bytes
인스턴스를 포함하는 리스트를 body
로 반환하거나, bytes
인스턴스를 생성하는 제너레이터 함수를 body
로 반환하거나, body
가 이터러블 클래스의 인스턴스인 등 다양한 방법으로 달성할 수 있습니다. 어떻게 달성되든 관계없이 애플리케이션 객체는 항상 0개 이상의 bytes
인스턴스를 반환하는 body
이터러블을 반환해야 합니다.
서버 또는 게이트웨이는 생성된 bytes
를 버퍼링되지 않은 방식으로 클라이언트에 전송해야 하며, 다음 bytes
세트를 요청하기 전에 각 bytes
세트의 전송을 완료해야 합니다. (다시 말해, 애플리케이션은 자체 버퍼링을 수행해야 합니다. 애플리케이션 출력이 어떻게 처리되어야 하는지에 대한 자세한 내용은 아래의 “버퍼링 및 스트리밍” 섹션을 참조하십시오.)
서버 또는 게이트웨이는 생성된 bytes
를 이진 바이트 시퀀스로 처리해야 합니다. 특히, 줄 끝이 변경되지 않도록 해야 합니다. 애플리케이션은 작성할 문자열이 클라이언트에 적합한 형식인지 확인해야 합니다. (서버 또는 게이트웨이는 HTTP 전송 인코딩을 적용하거나, 바이트 범위 전송과 같은 HTTP 기능을 구현하기 위해 다른 변환을 수행할 수 있습니다. 자세한 내용은 아래 “기타 HTTP 기능”을 참조하십시오.)
애플리케이션에서 반환된 body
이터러블에 close()
메서드가 있는 경우, 서버 또는 게이트웨이는 현재 요청이 정상적으로 완료되었든 오류로 인해 조기에 종료되었든 관계없이 해당 메서드를 호출해야 합니다. 이는 애플리케이션의 리소스 해제를 지원하기 위한 것이며, PEP 325의 제너레이터 지원 및 close()
메서드가 있는 기타 일반적인 이터러블을 보완하기 위한 것입니다.
마지막으로, 서버 및 게이트웨이는 애플리케이션에서 반환된 body
이터러블의 다른 속성을 직접 사용해서는 안 됩니다.
environ
변수 (environ
Variables)
environ
딕셔너리는 Common Gateway Interface 사양에 정의된 대로 다양한 CGI 환경 변수를 포함해야 합니다.
다음 CGI 변수가 존재해야 합니다. 각 키는 네이티브 문자열입니다. 각 값은 bytes
인스턴스입니다.
참고: 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
(또는 해당 원시(raw) 등가물)와 결합하여 이러한 변수는 URL을 완성하는 데 사용될 수 있습니다. 그러나, 존재하는 경우HTTP_HOST
는 요청 URL을 재구성하기 위해SERVER_NAME
보다 우선적으로 사용되어야 합니다. 자세한 내용은 아래의 “URL 재구성” 섹션을 참조하십시오.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이 사용 중인 경우 서버 또는 게이트웨이는 적용 가능한 한 많은 Apache SSL 환경 변수를 제공해야 합니다 (예: HTTPS=on
및 SSL_PROTOCOL
). 그러나 위에 나열된 것 외의 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 정의 변수를 포함해야 합니다.
| 변수 | 값 |
| :—————– | :———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— | web3.version
은 (1, 0)
튜플입니다. Web3 버전 1.0을 나타냅니다.
web3.url_scheme
: 애플리케이션이 호출되는 URL의 “스키마” 부분을 나타내는bytes
값입니다. 일반적으로b"http"
또는b"https"
값을 가집니다.web3.input
: HTTP 요청 본문을 구성하는bytes
를 읽을 수 있는 입력 스트림(파일과 유사한 객체)입니다. (서버 또는 게이트웨이는 애플리케이션 요청에 따라 온디맨드(on-demand)로 읽기를 수행하거나, 클라이언트의 요청 본문을 미리 읽고 메모리 또는 디스크에 버퍼링하거나, 선호도에 따라 이러한 입력 스트림을 제공하는 다른 기술을 사용할 수 있습니다.)-
web3.errors
: 프로그램 또는 기타 오류를 표준화되고 중앙 집중화된 위치에 기록하기 위해 오류 출력 텍스트를 쓸 수 있는 출력 스트림(파일과 유사한 객체)입니다. 이는 “텍스트 모드” 스트림이어야 합니다. 즉, 애플리케이션은 줄 끝으로\n
을 사용해야 하며, 서버/게이트웨이에 의해 올바른 줄 끝으로 변환될 것이라고 가정해야 합니다. 애플리케이션은 이 스트림의write
메서드에bytes
를 보낼 수 없으며, 텍스트만 보낼 수 있습니다.많은 서버에서
web3.errors
는 서버의 주 오류 로그가 될 것입니다. 또는sys.stderr
또는 일종의 로그 파일일 수 있습니다. 서버 설명서는 이를 구성하는 방법 또는 기록된 출력을 찾는 방법에 대한 설명을 포함해야 합니다. 서버 또는 게이트웨이는 원하는 경우 다른 애플리케이션에 다른 오류 스트림을 제공할 수 있습니다. web3.multithread
: 동일한 프로세스 내의 다른 스레드에 의해 애플리케이션 객체가 동시에 호출될 수 있는 경우 이 값은 참으로 평가되어야 하며, 그렇지 않은 경우 거짓으로 평가되어야 합니다.web3.multiprocess
: 동등한 애플리케이션 객체가 다른 프로세스에 의해 동시에 호출될 수 있는 경우 이 값은 참으로 평가되어야 하며, 그렇지 않은 경우 거짓으로 평가되어야 합니다.web3.run_once
: 서버 또는 게이트웨이가 애플리케이션이 포함하는 프로세스의 수명 동안 한 번만 호출될 것이라고 예상하는 (그러나 보장하지는 않는!) 경우 이 값은 참으로 평가되어야 합니다. 일반적으로, 이것은 CGI(또는 이와 유사한 것) 기반 게이트웨이에서만 참입니다.web3.script_name
: URL 디코딩되지 않은SCRIPT_NAME
값입니다. CGI 사양의 역사적 불평등으로 인해SCRIPT_NAME
은 이미 URL 디코딩된 문자열로 환경에 존재합니다. 이것은 요청 URI에서 파생된 원래의 URL 인코딩된 값입니다. 서버가 이 값을 제공할 수 없는 경우,environ
에서 이를 생략해야 합니다.web3.path_info
: URL 디코딩되지 않은PATH_INFO
값입니다. CGI 사양의 역사적 불평등으로 인해PATH_INFO
는 이미 URL 디코딩된 문자열로 환경에 존재합니다. 이것은 요청 URI에서 파생된 원래의 URL 인코딩된 값입니다. 서버가 이 값을 제공할 수 없는 경우,environ
에서 이를 생략해야 합니다.web3.async
: 웹 서버가 비동기 호출을 지원하는 경우True
입니다. 이 경우 애플리케이션은 응답 튜플 대신 호출 가능 객체를 반환할 수 있습니다. 정확한 의미는 이 사양에서 지정되지 않습니다.
마지막으로, environ
딕셔너리에는 서버 정의 변수도 포함될 수 있습니다. 이러한 변수는 소문자, 숫자, 점, 밑줄로만 구성된 네이티브 문자열 이름을 가져야 하며, 정의하는 서버 또는 게이트웨이에 고유한 이름으로 접두사가 붙어야 합니다. 예를 들어, mod_web3
는 mod_web3.some_variable
과 같은 이름의 변수를 정의할 수 있습니다.
입력 스트림 (Input Stream)
서버에서 제공하는 입력 스트림(web3.input
)은 다음 메서드를 지원해야 합니다.
메서드 | 참고 |
---|---|
read(size) |
1,4 |
readline([size]) |
1,2,4 |
readlines([size]) |
1,3,4 |
__iter__() |
4 |
각 메서드의 의미는 Python 라이브러리 참조에 문서화된 바와 같으며, 위 표에 나열된 참고 사항은 예외입니다.
- 서버는 클라이언트가 지정한
Content-Length
를 초과하여 읽을 필요가 없으며, 애플리케이션이 해당 지점을 초과하여 읽으려고 시도하면 파일 끝 조건을 시뮬레이션할 수 있습니다. 애플리케이션은CONTENT_LENGTH
변수에 지정된 것보다 더 많은 데이터를 읽으려고 시도해서는 안 됩니다. - 구현은
readline()
의 선택적size
인수를 지원해야 합니다. - 애플리케이션은
readlines()
에size
인수를 제공하지 않을 수 있으며, 서버 또는 게이트웨이는 제공된size
인수의 값을 무시할 수 있습니다. read
,readline
및__iter__
메서드는bytes
인스턴스를 반환해야 합니다.readlines
메서드는bytes
인스턴스를 포함하는 시퀀스를 반환해야 합니다.
위에 나열된 메서드는 이 사양을 준수하는 모든 서버에서 지원되어야 합니다. 이 사양을 준수하는 애플리케이션은 입력 객체의 다른 메서드나 속성을 사용해서는 안 됩니다. 특히, 애플리케이션은 close()
메서드를 가지고 있더라도 이 스트림을 닫으려고 시도해서는 안 됩니다.
입력 스트림은 요청의 Content-Length
보다 더 많이 읽으려는 시도를 조용히 무시해야 합니다. Content-Length
가 지정되지 않은 경우 스트림은 아무것도 반환하지 않는 더미 스트림이어야 합니다.
오류 스트림 (Error Stream)
서버에서 제공하는 오류 스트림(web3.errors
)은 다음 메서드를 지원해야 합니다.
메서드 | 스트림 | 참고 |
---|---|---|
flush() |
errors | 1 |
write(str) |
errors | 2 |
writelines(seq) |
errors | 2 |
각 메서드의 의미는 Python 라이브러리 참조에 문서화된 바와 같으며, 위 표에 나열된 참고 사항은 예외입니다.
- 오류 스트림은 되감을 수 없으므로, 서버 및 게이트웨이는 버퍼링 없이 쓰기 작업을 즉시 전달할 수 있습니다. 이 경우
flush()
메서드는 아무것도 하지 않을 수 있습니다. 그러나 이식 가능한 애플리케이션은 출력이 버퍼링되지 않거나flush()
가 아무것도 하지 않는다고 가정할 수 없습니다. 출력이 실제로 기록되었는지 확인해야 하는 경우flush()
를 호출해야 합니다. (예: 동일한 오류 로그에 쓰는 여러 프로세스의 데이터 혼합을 최소화하기 위해.) write()
메서드는 문자열 인수를 받아야 하지만, 반드시bytes
인수를 받아야 하는 것은 아닙니다.writelines()
메서드는 전적으로 문자열로 구성된 시퀀스 인수를 받아야 하지만, 시퀀스의 멤버로bytes
인스턴스를 반드시 받아야 하는 것은 아닙니다.
위에 나열된 메서드는 이 사양을 준수하는 모든 서버에서 지원되어야 합니다. 이 사양을 준수하는 애플리케이션은 errors
객체의 다른 메서드나 속성을 사용해서는 안 됩니다. 특히, 애플리케이션은 close()
메서드를 가지고 있더라도 이 스트림을 닫으려고 시도해서는 안 됩니다.
Web3 애플리케이션에서 반환되는 값 (Values Returned by A Web3 Application)
Web3 애플리케이션은 (status, headers, body)
형식의 튜플을 반환합니다. 서버가 비동기 애플리케이션(web3.async
)을 지원하는 경우, 응답은 호출 가능 객체(인수를 받지 않음)일 수 있습니다.
status
값은 게이트웨이 또는 서버에 의해 b'200 OK'
또는 b'404 Not Found'
와 같은 HTTP “상태” bytes
인스턴스로 가정됩니다. 즉, 상태 코드와 이유 구문(Reason-Phrase)으로 구성된 문자열이며, 그 순서대로 단일 공백으로 구분되고 주위에 공백이나 다른 문자가 없습니다. (자세한 내용은 RFC 2616, 섹션 6.1.1을 참조하십시오.) 문자열에는 제어 문자가 포함되어서는 안 되며, 캐리지 리턴, 라인 피드 또는 이들의 조합으로 끝나서도 안 됩니다.
headers
값은 게이트웨이 또는 서버에 의해 (header_name, header_value)
튜플의 리터럴 Python 리스트로 가정됩니다. 각 header_name
은 후행 콜론이나 다른 구두점 없이 유효한 HTTP 헤더 필드 이름(RFC 2616, 섹션 4.2에 정의됨)을 나타내는 bytes
인스턴스여야 합니다. 각 header_value
는 bytes
인스턴스여야 하며, 내부에 또는 끝에 캐리지 리턴이나 라인 피드를 포함하여 제어 문자를 포함해서는 안 됩니다. (이러한 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이 및 중간 응답 프로세서가 수행해야 하는 구문 분석의 복잡성을 최소화하기 위한 것입니다.)
일반적으로, 서버 또는 게이트웨이는 올바른 헤더가 클라이언트에 전송되도록 할 책임이 있습니다. 애플리케이션이 HTTP(또는 기타 관련 사양)에서 요구하는 헤더를 생략하는 경우, 서버 또는 게이트웨이는 이를 추가해야 합니다. 예를 들어, HTTP Date:
및 Server:
헤더는 일반적으로 서버 또는 게이트웨이에서 제공됩니다. 그러나 게이트웨이는 애플리케이션에서 동일한 이름으로 발행된 값을 재정의해서는 안 됩니다.
(서버/게이트웨이 작성자를 위한 알림: HTTP 헤더 이름은 대소문자를 구분하지 않으므로, 애플리케이션에서 제공하는 헤더를 검사할 때 이를 고려해야 합니다!)
애플리케이션 및 미들웨어는 HTTP/1.1 “홉-바이-홉(hop-by-hop)” 기능 또는 헤더, HTTP/1.0의 동등한 기능, 또는 클라이언트와 웹 서버 간의 연결 지속성에 영향을 미치는 헤더를 사용하는 것이 금지됩니다. 이러한 기능은 실제 웹 서버의 전유물이며, 서버 또는 게이트웨이는 애플리케이션이 이를 보내려고 시도하는 것을 치명적인 오류로 간주하고, 헤더 구조에서 애플리케이션의 반환 값으로 제공되는 경우 오류를 발생시켜야 합니다. (“홉-바이-홉” 기능 및 헤더에 대한 자세한 내용은 아래의 “기타 HTTP 기능” 섹션을 참조하십시오.)
Python 버전 간 호환성 처리 (Dealing with Compatibility Across Python Versions)
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 버전 간에 작동하도록 보장하려면 개발자는 다음을 수행해야 합니다.
- 텍스트 값과
bytes
값 간의 비교 동등성을 가정하지 마십시오. 그렇게 하면 코드가 Python 2에서는 작동할 수 있지만, Python 3에서는 제대로 작동하지 않습니다. 예를 들어,somebytes == 'abc'
와 같이 작성하지 마십시오. 이는 Python 2에서는 때때로 참일 수 있지만, Python 3에서는 결코 참이 아닙니다. Python 3에서는 바이트 시퀀스가 문자열과 같다고 비교되지 않기 때문입니다. 대신, 항상bytes
값을bytes
값과 비교하십시오 (예:somebytes == b'abc'
). 이렇게 하는 코드는 Python 2.6, 2.7 및 3.1과 호환되며 동일하게 작동합니다.'abc'
앞의b
는 Python 3에 해당 값이 리터럴bytes
인스턴스임을 알립니다. Python 2에서는 이전 버전과의 호환성을 위한 위약입니다. - 인수가
bytes
인스턴스임을 확인하지 않고bytes
와 유사해야 하는 항목의__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 애플리케이션에서 반환되는 모든 문자열과 유사한 값에 대해서도 마찬가지입니다.
버퍼링 및 스트리밍 (Buffering and Streaming)
일반적으로 애플리케이션은 (적당한 크기의) 출력을 버퍼링하고 한꺼번에 모두 보내서 최상의 처리량을 달성합니다. 이는 기존 프레임워크에서 일반적인 접근 방식입니다. 출력은 StringIO
또는 유사한 객체에 버퍼링된 다음, 응답 헤더와 함께 한꺼번에 전송됩니다.
Web3에서 이에 상응하는 접근 방식은 애플리케이션이 응답 본문을 단일 문자열로 포함하는 단일 요소 본문 이터러블(예: 리스트)을 단순히 반환하는 것입니다. 이는 텍스트가 메모리에 쉽게 들어가는 HTML 페이지를 렌더링하는 대다수의 애플리케이션 기능에 권장되는 접근 방식입니다.
그러나 큰 파일의 경우 또는 HTTP 스트리밍의 특수 용도(예: 멀티파트 “서버 푸시”)의 경우, 애플리케이션은 더 작은 블록으로 출력을 제공해야 할 수 있습니다 (예: 큰 파일을 메모리에 로드하는 것을 피하기 위해). 응답의 일부를 생성하는 데 시간이 많이 걸리지만, 그 앞에 오는 응답 부분을 미리 보내는 것이 유용한 경우도 있습니다.
이러한 경우, 애플리케이션은 일반적으로 출력을 블록 단위로 생성하는 본문 이터레이터(종종 제너레이터-이터레이터)를 반환합니다. 이러한 블록은 멀티파트 경계와 일치하도록 분할되거나(“서버 푸시”의 경우), 시간이 많이 걸리는 작업(예: 디스크 파일의 다른 블록 읽기) 직전에 분할될 수 있습니다.
Web3 서버, 게이트웨이 및 미들웨어는 어떤 블록의 전송도 지연해서는 안 됩니다. 애플리케이션이 다음 블록을 생성하는 동안에도 전체 블록을 클라이언트에 전송하거나, 전송을 계속할 것을 보장해야 합니다. 서버/게이트웨이 또는 미들웨어는 다음 세 가지 방법 중 하나로 이 보장을 제공할 수 있습니다.
- 애플리케이션에 제어권을 반환하기 전에 전체 블록을 운영 체제에 보내고 (모든 O/S 버퍼를 플러시하도록 요청)
- 애플리케이션이 다음 블록을 생성하는 동안 블록이 계속 전송되도록 다른 스레드를 사용합니다.
- (미들웨어만) 전체 블록을 상위 게이트웨이/서버로 보냅니다.
이 보장을 제공함으로써 Web3는 애플리케이션이 출력 데이터의 임의 지점에서 전송이 멈추지 않도록 보장합니다. 이는 멀티파트 “서버 푸시” 스트리밍과 같이 멀티파트 경계 사이의 데이터가 클라이언트에 완전히 전송되어야 하는 경우에 특히 중요합니다.
유니코드 문제 (Unicode Issues)
HTTP는 유니코드를 직접 지원하지 않으며, 이 인터페이스도 마찬가지입니다. 모든 인코딩/디코딩은 애플리케이션에서 처리해야 합니다. 서버로 또는 서버에서 전달되는 모든 값은 Python 3의 bytes
유형 또는 Python 2의 str
유형의 인스턴스여야 하며, Python 2 unicode
또는 Python 3 str
객체가 아닙니다.
이 사양에서 언급된 모든 “바이트 인스턴스”는 다음을 충족해야 합니다.
- Python 2에서는
str
유형이어야 합니다. - Python 3에서는
bytes
유형이어야 합니다.
모든 “바이트 인스턴스”는 다음을 충족해서는 안 됩니다.
- Python 2에서는
unicode
유형이어야 합니다. - Python 3에서는
str
유형이어야 합니다.
bytes
와 유사한 객체가 필요한 곳에 텍스트와 유사한 객체를 사용하면 정의되지 않은 결과가 발생합니다.
Web3 앱에서 상태 또는 응답 헤더로 반환되는 값은 인코딩과 관련하여 RFC 2616을 따라야 합니다. 즉, 반환된 바이트는 ISO-8859-1 문자의 문자 스트림을 포함해야 하거나, 문자 스트림은 RFC 2047 MIME 인코딩을 사용해야 합니다.
네이티브 바이트와 유사한 유형이 없는 Python 플랫폼(예: IronPython 등)에서는 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 기능 (Other HTTP Features)
일반적으로 서버 및 게이트웨이는 “바보처럼 행동”하고 애플리케이션이 출력에 대해 완전한 제어를 가질 수 있도록 허용해야 합니다. 애플리케이션 응답의 유효한 의미를 변경하지 않는 변경 사항만 만들어야 합니다. 애플리케이션 개발자는 항상 미들웨어 구성 요소를 추가하여 추가 기능을 제공할 수 있으므로, 서버/게이트웨이 개발자는 구현에 보수적이어야 합니다. 어떤 의미에서 서버는 자신을 HTTP “게이트웨이 서버”로 간주해야 하며, 애플리케이션은 HTTP “원본 서버”로 간주되어야 합니다. (이러한 용어의 정의는 RFC 2616, 섹션 1.3을 참조하십시오.)
그러나 Web3 서버와 애플리케이션은 HTTP를 통해 통신하지 않으므로 RFC 2616에서 “홉-바이-홉” 헤더라고 부르는 것은 Web3 내부 통신에 적용되지 않습니다. Web3 애플리케이션은 “홉-바이-홉” 헤더를 생성하거나, 그러한 헤더를 생성해야 하는 HTTP 기능을 사용하려고 시도하거나, environ
딕셔너리의 들어오는 “홉-바이-홉” 헤더 내용에 의존해서는 안 됩니다. Web3 서버는 지원되는 모든 들어오는 “홉-바이-홉” 헤더를 자체적으로 처리해야 합니다 (예: 적용 가능한 경우 청크 인코딩을 포함한 들어오는 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의 일부를 Web3 확장으로 노출하기를 원할 수 있습니다.
가장 간단한 경우, 이는 mod_python.some_api
와 같은 environ
변수를 정의하는 것 이상의 것을 요구하지 않습니다. 그러나 많은 경우 미들웨어의 존재로 인해 이것이 어려울 수 있습니다. 예를 들어, environ
변수에서 발견되는 동일한 HTTP 헤더에 대한 액세스를 제공하는 API는 environ
이 미들웨어에 의해 수정된 경우 다른 데이터를 반환할 수 있습니다.
일반적으로, Web3 기능의 일부를 복제, 대체 또는 우회하는 모든 확장 API는 미들웨어 구성 요소와 호환되지 않을 위험이 있습니다. 서버/게이트웨이 개발자는 아무도 미들웨어를 사용하지 않을 것이라고 가정해서는 안 됩니다. 일부 프레임워크 개발자는 프레임워크가 거의 전적으로 다양한 종류의 미들웨어로 기능하도록 특별히 구성하기 때문입니다.
따라서 최대 호환성을 제공하기 위해 Web3 기능을 대체하는 확장 API를 제공하는 서버 및 게이트웨이는 해당 API를 대체하는 API의 일부를 사용하여 호출되도록 해당 API를 설계해야 합니다. 예를 들어, HTTP 요청 헤더에 액세스하는 확장 API는 서버/게이트웨이가 API를 통해 액세스할 수 있는 HTTP 헤더가 미들웨어에 의해 변경되지 않았음을 확인할 수 있도록 애플리케이션에 현재 environ
을 전달하도록 요구해야 합니다. 확장 API가 HTTP 헤더 내용에 대해 environ
과 항상 일치할 것이라고 보장할 수 없는 경우, 서비스 제공을 거부해야 합니다 (예: 오류 발생, 헤더 컬렉션 대신 None
반환 또는 API에 적절한 다른 방법).
이러한 지침은 구문 분석된 쿠키, 폼 변수, 세션 등과 같은 정보를 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):
environ['the_app.configval1'] = b'something'
return application(environ)
그러나 대부분의 기존 애플리케이션 및 프레임워크는 애플리케이션 또는 프레임워크별 구성 파일의 위치를 나타내는 단일 구성 값만 environ
에서 필요할 것입니다. (물론, 애플리케이션은 각 호출 시 다시 읽을 필요가 없도록 이러한 구성을 캐시해야 합니다.)
URL 재구성 (URL Reconstruction)
애플리케이션이 요청의 완전한 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 environ:
url += url_quote(environ['web3.script_name']) # url_quote는 별도 정의 필요
else:
url += environ['SCRIPT_NAME']
if 'web3.path_info' in environ:
url += url_quote(environ['web3.path_info']) # url_quote는 별도 정의 필요
else:
url += environ['PATH_INFO']
if query:
url += b'?' + query
이러한 재구성된 URL이 클라이언트가 요청한 URI와 정확히 동일하지 않을 수 있다는 점에 유의하십시오. 예를 들어, 서버 재작성 규칙은 클라이언트가 원래 요청한 URL을 정식 형식으로 만들기 위해 수정했을 수 있습니다.
미해결 질문 (Open Questions)
file_wrapper
대체. 현재 여기에 지정된 것은 없지만, 응답이 파일 래퍼인지 프로세스 내에서 미들웨어로 알아내는 방법을 제공하지 않는다면 대역 내 신호(in-band signalling)의 기존 시스템이 깨진다는 것은 분명합니다.
논쟁점 (Points of Contention)
아래에는 이 사양에 대한 잠재적인 논쟁점이 요약되어 있습니다.
WSGI 1.0 호환성 (WSGI 1.0 Compatibility)
WSGI 1.0 사양을 사용하여 작성된 구성 요소는 이 사양을 사용하여 작성된 구성 요소와 투명하게 상호 운용되지 않습니다. 이는 이 제안의 목표와 WSGI 1.0의 목표가 직접적으로 일치하지 않기 때문입니다.
WSGI 1.0은 Python 2.2에서 2.7 사이의 버전과 사양 수준의 하위 호환성을 제공해야 합니다. 그러나 이 사양은 Python 2.5 및 이전 버전과의 호환성을 포기하고 비교적 최근 Python 2 버전(2.6 및 2.7)과 비교적 최근 Python 3 버전(3.1) 간의 호환성을 제공합니다.
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은 인코딩 및 디코딩에 대해 전혀 신경 쓰지 않았습니다. 허용 가능한 전송 값에 대한 설명과 다양한 값이 특정 인코딩으로 가장 잘 디코딩될 수 있음을 제안했지만, 서버가 어떤 디코딩도 수행하도록 요구하지는 않았습니다.
Python 3에는 바이트를 나타내는 데 사용할 수 있는 문자열과 유사한 유형이 없습니다. bytes
유형이 있습니다. bytes
유형은 Python 3.1 이상에서 Python 2 str
과 상당히 비슷하게 작동하지만, str.__mod__
및 해당 반복 프로토콜과 동등한 동작이 부족하고, 포함, 시퀀스 처리 및 동등성 비교가 다릅니다.
어느 경우든 Python 3에는 Python 2 str
유형과 동일하게 동작하는 유형이 없으며, 그러한 유형을 생성하는 방법도 존재하지 않습니다. 적절한 유형을 구축할 수 있는 “String ABC”와 같은 것이 없기 때문입니다. 이러한 설계 비호환성으로 인해 기존 WSGI 1.0 서버, 미들웨어 및 애플리케이션은 2to3
를 통해 실행된 후에도 Python 3에서 작동하지 않습니다.
WSGI 사양을 업데이트하여 Python 2와 Python 3 모두에서 실행되는 WSGI 애플리케이션을 작성할 수 있도록 하는 기존 Web-SIG 논의는 Python 2 str
유형(바이트 시퀀스를 나타냄)과 Python 3 str
유형(텍스트를 나타냄) 간의 사양 수준 동등성을 생성하는 경향이 있습니다. 이러한 동등성은 이러한 유형의 다른 역할을 감안할 때 여러 영역에서 긴장됩니다. Python 3 bytes
유형 API와 Python 2 str
유형 API의 하위 집합 사이에는 논리적으로 더 간단한 동등성이 존재합니다. 이 사양은 이 하위 집합 동등성을 활용합니다.
한편, Python 2 vs. Python 3 호환성 문제 외에도 Web-SIG의 다양한 논의에서 지적했듯이 WSGI 1.0 사양은 너무 일반적이며, 구현 복잡성을 희생하여 비동기 애플리케이션에 대한 지원( .write
를 통해)을 제공합니다. 이 사양은 WSGI 1.0과 Python 3 간의 근본적인 비호환성을 자연스러운 분기점으로 사용하여 비동기 애플리케이션에 대한 특수 지원을 변경하여 복잡성을 줄인 사양을 만듭니다.
이전 WSGI 1.0 애플리케이션의 하위 호환성을 제공하여 Web3 스택에서 실행될 수 있도록, 기존 WSGI 1.0 애플리케이션 “앞에” 사용할 수 있는 Web3 미들웨어가 생성될 것이라고 가정합니다. 이 미들웨어는 Python 3에서 Python 3 str
유형과 HTTP 요청에서 나타나는 바이트 값 및 모든 관련 인코딩 추측(또는 구성) 간의 동등성을 요구할 것입니다.
참고: 이러한 미들웨어는 미래에 Python 3 str
과 HTTP 바이트 값 간의 동등성을 그리는 대신, 아직 생성되지 않은 “ebytes” 유형 (일명 “bytes-with-benefits”)을 사용할 수 있습니다. 특히 String ABC 제안이 Python 코어에 수용되고 구현되는 경우 더욱 그렇습니다.
반대로, Python 2 플랫폼에서 Web3 애플리케이션이 WSGI 1.0 스택 뒤에서 실행될 수 있도록 WSGI 1.0 미들웨어가 생성될 것이라고 가정합니다.
environ
및 응답 값으로서의 바이트 (Environ and Response Values as Bytes)
일반적인 미들웨어 및 애플리케이션 작성자는 bytes
를 환경 값 및 응답 값으로 사용하는 것을 불편하게 여길 수 있습니다. 특히, ('%s' % bytes_val)
또는 bytes_val.format('123')
와 같은 일반적인 문자열 형식 지정 함수를 사용할 수 없습니다. Python 3와 같이 두 유형이 다른 플랫폼에서는 bytes
가 문자열과 동일한 API를 가지지 않기 때문입니다. 마찬가지로, 이러한 플랫폼에서는 bytes
를 텍스트와 상호 교환적으로 사용하는 stdlib
HTTP 관련 API 지원이 불완전할 수 있습니다. bytes
가 불편하거나 라이브러리 API와 호환되지 않는 곳에서는 미들웨어 및 애플리케이션 작성자가 이러한 bytes
를 명시적으로 텍스트로 디코딩해야 합니다. 이는 미들웨어 작성자에게 특히 불편합니다. 환경 값을 문자열로 작업하려면, 암시된 인코딩에서 디코딩해야 하며, environ
값을 변경해야 하는 경우 값을 environ
에 배치하기 전에 바이트 스트림으로 인코딩해야 합니다. 사양에서 bytes
를 environ
값으로 사용하는 것이 일반 개발자에게는 불편할 수 있지만, 몇 가지 이점을 제공합니다.
bytes
유형을 사용하여 HTTP 및 서버 값을 애플리케이션에 나타내는 것은 HTTP가 근본적으로 바이트 지향 프로토콜이기 때문에 현실과 가장 가깝게 일치합니다. environ
값이 문자열로 강제되는 경우, 각 서버는 HTTP 환경에서 제공되는 다양한 값의 인코딩을 추측하기 위해 휴리스틱을 사용해야 합니다. 모든 문자열을 사용하면 일반적인 미들웨어 작성자의 편의성을 높일 수 있지만, 값이 의미 있는 비-서러게이트(non-surrogate) 문자열로 디코딩될 수 없을 때 모호함과 혼란을 야기할 수도 있습니다.
bytes
를 environ
값으로 사용하면 사양이 참여 서버에 인코딩 구성 매개변수를 알려야 할 필요성을 피할 수 있습니다. environ
값이 문자열로 처리되어 바이트에서 디코딩되어야 하는 경우, 구성 매개변수는 결국 애플리케이션 배포자의 정책 단서로 필요하게 될 수 있습니다. 이러한 정책은 다양한 상황에서 적절한 디코딩 전략을 추측하는 데 사용되며, 특정 애플리케이션 인코딩 정책을 강제하는 부담을 서버에 효과적으로 전가합니다. 서버가 하나 이상의 애플리케이션을 서비스해야 하는 경우, 이러한 구성은 빠르게 복잡해질 것입니다. 많은 정책은 선언적으로 표현하는 것도 불가능할 것입니다.
실제로 HTTP는 복잡하고 오래된 프로토콜이며, 의미를 파악하기 위해 복잡한 휴리스틱 집합이 필요합니다. 이 프로토콜이 이러한 복잡성으로부터 우리를 보호할 수 있다면 좋겠지만, 현실에 상응하는 수준의 제어를 애플리케이션 작성자에게 제공하면서도 신뢰할 수 있게 그렇게 할 수는 없습니다. Python 애플리케이션은 종종 레거시 휴리스틱으로 구문 분석되어야 할 뿐만 아니라 기존 HTTP 사양에도 따르지 않는 환경에 포함된 데이터를 처리해야 합니다. 이러한 상황이 불쾌하지만, 규칙적으로 발생하며, HTTP 사양 위반이 감지될 때 적절한 조치를 결정할 수 있는 유일한 사람들은 애플리케이션 개발자이기 때문에 이를 애플리케이션 개발자로부터 숨기는 것은 불가능하고 바람직하지 않습니다.
일부에서는 bytes
와 문자열 값을 environ
값으로 혼합 사용하는 것을 주장했습니다. 이 제안은 그러한 전략을 피합니다. bytes
만을 environ
값으로 사용하면 이 사양을 완전히 머리 속에 넣을 수 있습니다. 어떤 값이 문자열이고 어떤 값이 bytes
인지 추측할 필요가 없습니다.
이 프로토콜은 모든 environ
값이 문자열이라면 개발자의 머리에도 들어갈 수 있었겠지만, 이 사양은 그러한 전략을 사용하지 않습니다. 이것이 bytes
사용과 관련하여 가장 큰 논쟁점이 될 가능성이 높습니다. bytes
를 옹호하는 입장: 개발자들은 계약 자체가 최적이 아니더라도 일관된 계약을 가진 프로토콜을 선호하는 경우가 많습니다. 서러게이트를 포함하는 값이 애플리케이션의 I/O 경계를 넘어선 후에 문제를 일으킬 때까지 인코딩 문제를 개발자로부터 숨긴다면, 애플리케이션에서 만들어진 가정을 수정하기 위해 “여기 바이트가 있으니 당신이 디코딩하세요”라는 방식으로 훨씬 일찍 문제를 제시하는 것보다 훨씬 더 많은 작업을 해야 할 것입니다. 이것은 또한 “바이트가 불편하다”는 가정에 대한 반론이기도 합니다. 예외 사례에 신경 쓰지 않는 일반적인 애플리케이션 개발자에게 bytes
를 제시하는 것이 불편할 수 있지만, 복잡하고 지저분한 상황을 처리해야 하는 애플리케이션 개발자에게는 극도로 편리합니다. bytes
를 사용하면 책임의 명확한 분리와 함께 적절한 수준의 제어를 허용하기 때문입니다.
프로토콜이 bytes
를 사용하는 경우, environ
및 반환 값 내에서 bytes
만으로 작업하는 것을 더 편리하게 만들 라이브러리가 생성될 것이라고 가정합니다. 예를 들어, WSGI 1.0 라이브러리인 “WebOb” 및 “Werkzeug”와 유사한 것들입니다. 이러한 라이브러리는 편의성과 제어 사이의 간극을 메워주어, 사양이 간단하고 규칙적이며 일반적인 작성자에게 Web3 미들웨어 및 애플리케이션 구성 요소를 생성하는 편리한 방법을 제공하도록 합니다. 이는 인코딩 정책을 프로토콜에 내장하는 합리적인 대안으로 보입니다. 많은 그러한 라이브러리가 프로토콜과 독립적으로 생성될 수 있고, 애플리케이션 개발자가 특정 작업에 대해 적절한 수준의 제어 및 편의성을 제공하는 라이브러리를 선택할 수 있기 때문입니다.
모든 bytes
를 사용하는 것에 대한 몇 가지 대안은 다음과 같습니다.
- 서버가 모든 CGI 및 서버
environ
값을 손실 없는latin-1
인코딩을 사용하여 문자열로 디코딩하도록 합니다. 결과 문자열 내에서 디코딩할 수 없는 바이트를 몰래 포함시킵니다. surrogateescape
오류 처리기를 사용하여utf-8
인코딩으로 모든 CGI 및 서버environ
값을 문자열로 인코딩합니다. 이는 기존 Python 2에서는 작동하지 않습니다.- 일부 값은
bytes
로, 다른 값은 일반적인 용도에 따라 문자열로 인코딩합니다.
애플리케이션은 CONTENT_LENGTH
를 넘어 web3.input
을 읽을 수 있어야 함 (Applications Should be Allowed to Read web3.input Past CONTENT_LENGTH)
에서 Graham Dumpleton은 wsgi.input
이 데이터 부족을 나타내는 표시기로 빈 문자열을 반환해야 하며, 애플리케이션은 CONTENT_LENGTH
에 지정된 바이트 수를 넘어 읽을 수 있어야 하며, EOF 마커로 빈 문자열에만 의존해야 한다고 주장합니다. WSGI는 애플리케이션이 “잘 작동하고 CONTENT_LENGTH
에 지정된 모든 데이터를 읽으면 데이터를 처리하고 응답을 반환한다. 그런 다음 동일한 소켓 연결을 후속 요청에 사용할 수 있다”고 가정합니다. Graham은 WSGI 어댑터가 원시 소켓 연결을 래핑하도록 요구하기를 원합니다. “이 래퍼 객체는 읽은 데이터의 양을 계산해야 하며, 데이터 양이 CONTENT_LENGTH
에 정의된 양에 도달하면 후속 읽기는 대신 빈 문자열을 반환해야 합니다.” 이는 청크 인코딩 및 입력 필터를 지원하는 데 유용할 수 있습니다.
web3.input
알 수 없는 길이 (web3.input Unknown Length)
environ['web3.input']
에 콘텐츠가 있지만 콘텐츠 길이를 알 수 없음을 나타내는 문서화된 방법이 없습니다.
web3.input
의 read()
는 크기 없는 호출 규칙을 지원해야 함 (read()
of web3.input Should Support No-Size Calling Convention)
에서 Graham Dumpleton은 wsgi.input
의 read()
메서드가 인수 없이 호출될 수 있어야 하며, 그 결과는 “사용 가능한 모든 요청 콘텐츠”여야 한다고 주장합니다. 논의가 필요합니다.
Armin의 의견: 구현에 이 요구 사항을 추가하도록 사양을 변경했습니다. 과거에 이로 인해 너무 많은 어려움을 겪었습니다. 그러나 논의는 열려 있습니다.
입력 필터는 environ CONTENT_LENGTH
를 -1로 설정해야 함 (Input Filters should set environ CONTENT_LENGTH to -1)
에서 Graham Dumpleton은 입력 필터가 입력이 변경되었음을 나타내기 위해 environ['CONTENT_LENGTH']
를 -1로 설정할 수 있다고 제안합니다.
두 개의 튜플 리터럴 리스트로서의 헤더 (headers as Literal List of Two-Tuples)
왜 애플리케이션이 두 개의 튜플 리터럴 리스트인 헤더 구조를 반환하도록 하는가? 헤더의 반복성은 스택 위로 이동하는 동안 유지되어야 한다고 생각하지만, 항상 그 자리에서 변경할 수 있어야 한다고 생각하지는 않습니다. 이 요구 사항을 완화할 수 있을까요?
Armin의 의견: 강력하게 예.
미들웨어가 차단하지 않아야 한다는 요구 사항 제거 (Removed Requirement that Middleware Not Block)
다음 요구 사항이 제거되었습니다: “미들웨어 구성 요소는 애플리케이션 이터러블에서 여러 값을 기다리면서 반복을 차단해서는 안 됩니다. 미들웨어가 출력을 생성하기 전에 애플리케이션에서 더 많은 데이터를 축적해야 하는 경우, 빈 문자열을 yield해야 합니다.” 이 요구 사항은 비동기 애플리케이션 및 서버를 지원하기 위해 존재했습니다 (PEP 333의 “미들웨어의 블록 경계 처리” 참조). 비동기 애플리케이션은 이제 web3.async
가능 프로토콜에 의해 명시적으로 서비스됩니다 (Web3 애플리케이션 호출 가능 객체 자체가 호출 가능 객체를 반환할 수 있습니다).
web3.script_name
및 web3.path_info
이러한 값은 이 사양에 따라 원본 서버가 환경에 배치해야 합니다. SCRIPT_NAME
및 PATH_INFO
와 달리, 이러한 값은 요청 URI에서 파생된 원래 URL 인코딩 변형이어야 합니다. 이러한 값이 원래 어떻게 계산되어야 하는지, 그리고 서버가 URL 재작성을 수행하는 경우 그 값이 무엇이어야 하는지 파악해야 할 것입니다.
긴 응답 헤더 (Long Response Headers)
Bob Brewer는 Web-SIG에서 다음과 같이 언급했습니다.
“각 header_value
는 내부에 또는 끝에 캐리지 리턴이나 라인 피드를 포함하여 제어 문자를 포함해서는 안 됩니다. (이러한 요구 사항은 응답 헤더를 검사하거나 수정해야 하는 서버, 게이트웨이 및 중간 응답 프로세서가 수행해야 하는 구문 분석의 복잡성을 최소화하기 위한 것입니다.) (PEP 333)”
이는 이해할 수 있지만, HTTP 헤더는 (대부분) *TEXT로 정의되며, “ *TEXT의 단어는 RFC 2047의 규칙에 따라 인코딩된 경우에만 ISO-8859-1 문자 집합 이외의 문자 집합의 문자를 포함할 수 있습니다.” 그리고 RFC 2047은 “인코딩된 단어는 75자보다 길 수 없습니다… 75자의 ‘인코딩된 단어’에 맞지 않는 더 많은 텍스트를 인코딩하려면 여러 ‘인코딩된 단어’(CRLF SPACE로 구분)를 사용할 수 있습니다.” 이는 HTTP 헤더 폴딩 규칙도 충족합니다. “헤더 필드는 각 추가 줄 앞에 최소 하나의 SP 또는 HT를 사용하여 여러 줄에 걸쳐 확장될 수 있습니다.” (PEP 333)
따라서 HTTP를 읽는 관점에서, 어딘가의 코드는 길이가 긴 인코딩된 응답 헤더 값에 새 줄을 삽입해야 합니다. 세 가지 옵션이 있습니다.
- 현재 상태를 유지하고, ISO-8859-1 문자 집합 외의 75자 이상 단어를 포함하는 응답 헤더 값을 허용하지 않습니다.
- WSGI 응답 헤더에 새 줄 문자를 허용합니다.
- WSGI 서버가 값을 HTTP를 통해 보내기 전에 인코딩 및 폴딩을 수행하도록 요구/강력히 제안합니다.
요청 트레일러 및 청크 전송 인코딩 (Request Trailers and Chunked Transfer Encoding)
요청 콘텐츠에 청크 전송 인코딩을 사용할 때, RFC는 요청 트레일러를 허용합니다. 이는 요청 헤더와 유사하지만 최종 널(null) 데이터 청크 뒤에 옵니다. 이러한 트레일러는 청크 데이터 스트림의 길이가 유한하고 모든 데이터가 읽혔을 때만 사용할 수 있습니다. WSGI와 Web3 모두 현재 이를 지원하지 않습니다.
참고 자료 (References)
- (1, 2) The Common Gateway Interface Specification, v 1.1, 3rd Draft (https://datatracker.ietf.org/doc/html/draft-coar-cgi-v11-03)
- “Chunked Transfer Coding” – HTTP/1.1, RFC 2616#section-3.6.1
- mod_ssl Reference, “Environment Variables” (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25)
- (1, 2, 3) Details on WSGI 1.0 amendments/clarifications. (http://blog.dscpl.com.au/2009/10/details-on-wsgi-10-amendmentsclarificat.html)
- [Web-SIG] WSGI and long response header values (https://mail.python.org/pipermail/web-sig/2006-September/002244.html)
저작권 (Copyright)
이 문서는 공개 도메인으로 지정되었습니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments