Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
镜像
tornadoweb
Tornado
提交
e4d69843
Tornado
项目概览
镜像
/
tornadoweb
/
Tornado
大约 1 年 前同步成功
通知
26
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Tornado
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
e4d69843
编写于
8月 11, 2023
作者:
B
Ben Darnell
提交者:
GitHub
8月 11, 2023
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #3307 from bdarnell/branch6.3
Version 6.3.3
上级
e3aa6c5e
6a9e6fba
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
134 addition
and
21 deletion
+134
-21
.github/workflows/test.yml
.github/workflows/test.yml
+3
-2
docs/releases.rst
docs/releases.rst
+1
-0
docs/releases/v6.3.3.rst
docs/releases/v6.3.3.rst
+12
-0
tornado/__init__.py
tornado/__init__.py
+2
-2
tornado/http1connection.py
tornado/http1connection.py
+24
-3
tornado/test/httpserver_test.py
tornado/test/httpserver_test.py
+92
-14
未找到文件。
.github/workflows/test.yml
浏览文件 @
e4d69843
...
...
@@ -51,8 +51,9 @@ jobs:
tox_env
:
py311-full
-
python
:
'
3.11.0'
tox_env
:
py311-full
-
python
:
'
3.12.0-alpha
-
3.12'
tox_env
:
py312-full
# py312 testing is disabled in branch6.3; full support is coming in tornado 6.4
#- python: '3.12.0-alpha - 3.12'
# tox_env: py312-full
-
python
:
'
pypy-3.8'
# Pypy is a lot slower due to jit warmup costs, so don't run the
# "full" test config there.
...
...
docs/releases.rst
浏览文件 @
e4d69843
...
...
@@ -4,6 +4,7 @@ Release notes
.. toctree::
:maxdepth: 2
releases/v6.3.3
releases/v6.3.2
releases/v6.3.1
releases/v6.3.0
...
...
docs/releases/v6.3.3.rst
0 → 100644
浏览文件 @
e4d69843
What's new in Tornado 6.3.3
===========================
Aug 11, 2023
------------
Security improvements
~~~~~~~~~~~~~~~~~~~~~
- The ``Content-Length`` header and ``chunked`` ``Transfer-Encoding`` sizes are now parsed
more strictly (according to the relevant RFCs) to avoid potential request-smuggling
vulnerabilities when deployed behind certain proxies.
tornado/__init__.py
浏览文件 @
e4d69843
...
...
@@ -22,8 +22,8 @@
# is zero for an official release, positive for a development branch,
# or negative for a release candidate or beta (after the base version
# number has been incremented)
version
=
"6.3.
2
"
version_info
=
(
6
,
3
,
2
,
0
)
version
=
"6.3.
3
"
version_info
=
(
6
,
3
,
3
,
0
)
import
importlib
import
typing
...
...
tornado/http1connection.py
浏览文件 @
e4d69843
...
...
@@ -442,7 +442,7 @@ class HTTP1Connection(httputil.HTTPConnection):
):
self
.
_expected_content_remaining
=
0
elif
"Content-Length"
in
headers
:
self
.
_expected_content_remaining
=
int
(
headers
[
"Content-Length"
])
self
.
_expected_content_remaining
=
parse_
int
(
headers
[
"Content-Length"
])
else
:
self
.
_expected_content_remaining
=
None
# TODO: headers are supposed to be of type str, but we still have some
...
...
@@ -618,7 +618,7 @@ class HTTP1Connection(httputil.HTTPConnection):
headers
[
"Content-Length"
]
=
pieces
[
0
]
try
:
content_length
=
int
(
headers
[
"Content-Length"
])
# type: Optional[int]
content_length
:
Optional
[
int
]
=
parse_int
(
headers
[
"Content-Length"
])
except
ValueError
:
# Handles non-integer Content-Length value.
raise
httputil
.
HTTPInputError
(
...
...
@@ -668,7 +668,10 @@ class HTTP1Connection(httputil.HTTPConnection):
total_size
=
0
while
True
:
chunk_len_str
=
await
self
.
stream
.
read_until
(
b
"
\r\n
"
,
max_bytes
=
64
)
chunk_len
=
int
(
chunk_len_str
.
strip
(),
16
)
try
:
chunk_len
=
parse_hex_int
(
native_str
(
chunk_len_str
[:
-
2
]))
except
ValueError
:
raise
httputil
.
HTTPInputError
(
"invalid chunk size"
)
if
chunk_len
==
0
:
crlf
=
await
self
.
stream
.
read_bytes
(
2
)
if
crlf
!=
b
"
\r\n
"
:
...
...
@@ -842,3 +845,21 @@ class HTTP1ServerConnection(object):
await
asyncio
.
sleep
(
0
)
finally
:
delegate
.
on_close
(
self
)
DIGITS
=
re
.
compile
(
r
"[0-9]+"
)
HEXDIGITS
=
re
.
compile
(
r
"[0-9a-fA-F]+"
)
def
parse_int
(
s
:
str
)
->
int
:
"""Parse a non-negative integer from a string."""
if
DIGITS
.
fullmatch
(
s
)
is
None
:
raise
ValueError
(
"not an integer: %r"
%
s
)
return
int
(
s
)
def
parse_hex_int
(
s
:
str
)
->
int
:
"""Parse a non-negative hexadecimal integer from a string."""
if
HEXDIGITS
.
fullmatch
(
s
)
is
None
:
raise
ValueError
(
"not a hexadecimal integer: %r"
%
s
)
return
int
(
s
,
16
)
tornado/test/httpserver_test.py
浏览文件 @
e4d69843
...
...
@@ -18,7 +18,7 @@ from tornado.httputil import (
)
from
tornado.iostream
import
IOStream
from
tornado.locks
import
Event
from
tornado.log
import
gen_log
from
tornado.log
import
gen_log
,
app_log
from
tornado.netutil
import
ssl_options_to_context
from
tornado.simple_httpclient
import
SimpleAsyncHTTPClient
from
tornado.testing
import
(
...
...
@@ -41,6 +41,7 @@ import socket
import
ssl
import
sys
import
tempfile
import
textwrap
import
unittest
import
urllib.parse
from
io
import
BytesIO
...
...
@@ -118,7 +119,7 @@ class SSLTestMixin(object):
def
get_ssl_options
(
self
):
return
dict
(
ssl_version
=
self
.
get_ssl_version
(),
**
AsyncHTTPSTestCase
.
default_ssl_options
()
**
AsyncHTTPSTestCase
.
default_ssl_options
()
,
)
def
get_ssl_version
(
self
):
...
...
@@ -558,23 +559,60 @@ bar
)
self
.
assertEqual
(
json_decode
(
response
),
{
"foo"
:
[
"bar"
]})
@
gen_test
def
test_invalid_content_length
(
self
):
with
ExpectLog
(
gen_log
,
".*Only integer Content-Length is allowed"
,
level
=
logging
.
INFO
):
self
.
stream
.
write
(
b
"""
\
def
test_chunked_request_body_invalid_size
(
self
):
# Only hex digits are allowed in chunk sizes. Python's int() function
# also accepts underscores, so make sure we reject them here.
self
.
stream
.
write
(
b
"""
\
POST /echo HTTP/1.1
Content-Length: foo
Transfer-Encoding: chunked
bar
1_a
1234567890abcdef1234567890
0
"""
.
replace
(
b
"
\n
"
,
b
"
\r\n
"
)
b
"
\n
"
,
b
"
\r\n
"
)
)
with
ExpectLog
(
gen_log
,
".*invalid chunk size"
,
level
=
logging
.
INFO
):
start_line
,
headers
,
response
=
self
.
io_loop
.
run_sync
(
lambda
:
read_stream_body
(
self
.
stream
)
)
yield
self
.
stream
.
read_until_close
()
self
.
assertEqual
(
400
,
start_line
.
code
)
@
gen_test
def
test_invalid_content_length
(
self
):
# HTTP only allows decimal digits in content-length. Make sure we don't
# accept anything else, with special attention to things accepted by the
# python int() function (leading plus signs and internal underscores).
test_cases
=
[
(
"alphabetic"
,
"foo"
),
(
"leading plus"
,
"+10"
),
(
"internal underscore"
,
"1_0"
),
]
for
name
,
value
in
test_cases
:
with
self
.
subTest
(
name
=
name
),
closing
(
IOStream
(
socket
.
socket
()))
as
stream
:
with
ExpectLog
(
gen_log
,
".*Only integer Content-Length is allowed"
,
level
=
logging
.
INFO
,
):
yield
stream
.
connect
((
"127.0.0.1"
,
self
.
get_http_port
()))
stream
.
write
(
utf8
(
textwrap
.
dedent
(
f
"""
\
POST /echo HTTP/1.1
Content-Length:
{
value
}
Connection: close
1234567890
"""
).
replace
(
"
\n
"
,
"
\r\n
"
)
)
)
yield
stream
.
read_until_close
()
class
XHeaderTest
(
HandlerBaseTestCase
):
...
...
@@ -1123,6 +1161,46 @@ class StreamingChunkSizeTest(AsyncHTTPTestCase):
)
class
InvalidOutputContentLengthTest
(
AsyncHTTPTestCase
):
class
MessageDelegate
(
HTTPMessageDelegate
):
def
__init__
(
self
,
connection
):
self
.
connection
=
connection
def
headers_received
(
self
,
start_line
,
headers
):
content_lengths
=
{
"normal"
:
"10"
,
"alphabetic"
:
"foo"
,
"leading plus"
:
"+10"
,
"underscore"
:
"1_0"
,
}
self
.
connection
.
write_headers
(
ResponseStartLine
(
"HTTP/1.1"
,
200
,
"OK"
),
HTTPHeaders
({
"Content-Length"
:
content_lengths
[
headers
[
"x-test"
]]}),
)
self
.
connection
.
write
(
b
"1234567890"
)
self
.
connection
.
finish
()
def
get_app
(
self
):
class
App
(
HTTPServerConnectionDelegate
):
def
start_request
(
self
,
server_conn
,
request_conn
):
return
InvalidOutputContentLengthTest
.
MessageDelegate
(
request_conn
)
return
App
()
def
test_invalid_output_content_length
(
self
):
with
self
.
subTest
(
"normal"
):
response
=
self
.
fetch
(
"/"
,
method
=
"GET"
,
headers
=
{
"x-test"
:
"normal"
})
response
.
rethrow
()
self
.
assertEqual
(
response
.
body
,
b
"1234567890"
)
for
test
in
[
"alphabetic"
,
"leading plus"
,
"underscore"
]:
with
self
.
subTest
(
test
):
# This log matching could be tighter but I think I'm already
# over-testing here.
with
ExpectLog
(
app_log
,
"Uncaught exception"
):
with
self
.
assertRaises
(
HTTPError
):
self
.
fetch
(
"/"
,
method
=
"GET"
,
headers
=
{
"x-test"
:
test
})
class
MaxHeaderSizeTest
(
AsyncHTTPTestCase
):
def
get_app
(
self
):
return
Application
([(
"/"
,
HelloWorldRequestHandler
)])
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录