nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: prometheus/procfs
Pull request #204 (2019-08-30 05:49:35)

Add parsing of `Tgid:` from `/proc/<pid>/status`
Hi @discordianfish @pgier ! Thank you for GREAT library !

I added parsing of `Tgid:` from `/proc/<pid>/status`

This pull request contains 2 fixes.

- Fix fixtures.ttar ( /proc/26231/status `Pid:` should be 26231 )
- Add parsing of `Tgid:` from `/proc/<pid>/status`

## Use case

- I want to detect if thread reader process ( using `p.PID == p.TGID` )

Thank you !



Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index 8591c27..599e098 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -184,10 +184,10 @@ Lines: 53
 Name:	prometheus
 Umask:	0022
 State:	S (sleeping)
-Tgid:	1
+Tgid:	26231
 Ngid:	0
-Pid:	1
-PPid:	0
+Pid:	26231
+PPid:	1
 TracerPid:	0
 Uid:	0	0	0	0
 Gid:	0	0	0	0
diff --git a/proc_status.go b/proc_status.go
index 6b4b61f..b10cdc5 100644
--- a/proc_status.go
+++ b/proc_status.go
@@ -29,6 +29,9 @@ type ProcStatus struct {
 	// The process name.
 	Name string
 
+	// Thread group ID.
+	TGID int
+
 	// Peak virtual memory size.
 	VmPeak uint64
 	// Virtual memory size.
@@ -113,6 +116,8 @@ func (p Proc) NewStatus() (ProcStatus, error) {
 
 func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintBytes uint64) {
 	switch k {
+	case "Tgid":
+		s.TGID = int(vUint)
 	case "Name":
 		s.Name = vString
 	case "VmPeak":


Test patch:
diff --git a/proc_status_test.go b/proc_status_test.go
index 2a9fac6..494a34d 100644
--- a/proc_status_test.go
+++ b/proc_status_test.go
@@ -33,7 +33,8 @@ func TestProcStatus(t *testing.T) {
 		want int
 		have int
 	}{
-		{name: "pid", want: 26231, have: s.PID},
+		{name: "Pid", want: 26231, have: s.PID},
+		{name: "Tgid", want: 26231, have: s.TGID},
 		{name: "VmPeak", want: 58472 * 1024, have: int(s.VmPeak)},
 		{name: "VmSize", want: 58440 * 1024, have: int(s.VmSize)},
 		{name: "VmLck", want: 0 * 1024, have: int(s.VmLck)},
Repository: prometheus/procfs
Pull request #282 (2020-04-17 14:50:28)

Fix mountinfo parsing to handle entries with 10 fields
See https://github.com/prometheus/node_exporter/pull/1673 for the full context of how this is impacting node_exporter.


Patch:
diff --git a/mountinfo.go b/mountinfo.go
index c350cbe..59f4d50 100644
--- a/mountinfo.go
+++ b/mountinfo.go
@@ -77,7 +77,7 @@ func parseMountInfoString(mountString string) (*MountInfo, error) {
 
 	mountInfo := strings.Split(mountString, " ")
 	mountInfoLength := len(mountInfo)
-	if mountInfoLength < 11 {
+	if mountInfoLength < 10 {
 		return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString)
 	}
 


Test patch:
diff --git a/mountinfo_test.go b/mountinfo_test.go
index de0ecc1..fab4f44 100644
--- a/mountinfo_test.go
+++ b/mountinfo_test.go
@@ -131,6 +131,38 @@ func TestMountInfo(t *testing.T) {
 			},
 			invalid: false,
 		},
+		{
+			name: "Docker overlay with 10 fields (no optional fields)",
+			s:    "137 45 253:2 /lib/docker/overlay2 /var/lib/docker/overlay2 rw,relatime - ext4 /dev/mapper/vg0-lv_var rw,data=ordered",
+			mount: &MountInfo{
+				MountID:        137,
+				ParentID:       45,
+				MajorMinorVer:  "253:2",
+				Root:           "/lib/docker/overlay2",
+				MountPoint:     "/var/lib/docker/overlay2",
+				Options:        map[string]string{"rw": "", "relatime": ""},
+				OptionalFields: map[string]string{},
+				FSType:         "ext4",
+				Source:         "/dev/mapper/vg0-lv_var",
+				SuperOptions:   map[string]string{"rw": "", "data": "ordered"},
+			},
+		},
+		{
+			name: "bind chroot bind mount with 10 fields (no optional fields)",
+			s:    "157 47 253:2 /etc/named /var/named/chroot/etc/named rw,relatime - ext4 /dev/mapper/vg0-lv_root rw,data=ordered",
+			mount: &MountInfo{
+				MountID:        157,
+				ParentID:       47,
+				MajorMinorVer:  "253:2",
+				Root:           "/etc/named",
+				MountPoint:     "/var/named/chroot/etc/named",
+				Options:        map[string]string{"rw": "", "relatime": ""},
+				OptionalFields: map[string]string{},
+				FSType:         "ext4",
+				Source:         "/dev/mapper/vg0-lv_root",
+				SuperOptions:   map[string]string{"rw": "", "data": "ordered"},
+			},
+		},
 	}
 
 	for i, test := range tests {
Repository: prometheus/procfs
Pull request #327 (2020-09-14 16:42:12)

update to go 1.15
Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4224ea8..7dee320 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -42,9 +42,9 @@ workflows:
     jobs:
     - test:
         command: default
-        name: linux-1-13
+        name: linux-1-15
         os: linux
-        go_version: "1.13"
+        go_version: "1.15"
     - test:
         command: default
         name: linux-1-14
@@ -52,9 +52,9 @@ workflows:
         go_version: "1.14"
     - test:
         command: no-test
-        name: windows-1-13
+        name: windows-1-15
         os: windows
-        go_version: "1.13"
+        go_version: "1.15"
     - test:
         command: no-test
         name: windows-1-14
diff --git a/cpuinfo.go b/cpuinfo.go
index b9fb589..1711d73 100644
--- a/cpuinfo.go
+++ b/cpuinfo.go
@@ -313,6 +313,22 @@ func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
 				return nil, err
 			}
 			cpuinfo[i].CPUMHz = v
+		case "physical id":
+			cpuinfo[i].PhysicalID = field[1]
+		case "core id":
+			cpuinfo[i].CoreID = field[1]
+		case "cpu cores":
+			v, err := strconv.ParseUint(field[1], 0, 32)
+			if err != nil {
+				return nil, err
+			}
+			cpuinfo[i].CPUCores = uint(v)
+		case "siblings":
+			v, err := strconv.ParseUint(field[1], 0, 32)
+			if err != nil {
+				return nil, err
+			}
+			cpuinfo[i].Siblings = uint(v)
 		}
 	}
 


Test patch:
diff --git a/cpuinfo_test.go b/cpuinfo_test.go
index bfec15d..9a00d69 100644
--- a/cpuinfo_test.go
+++ b/cpuinfo_test.go
@@ -115,18 +115,34 @@ processor 2: version = FF,  identification = 2733E8,  machine = 2964
 processor 3: version = FF,  identification = 2733E8,  machine = 2964
 
 cpu number      : 0
+physical id     : 2
+core id         : 0
+siblings        : 8
+cpu cores       : 4
 cpu MHz dynamic : 5000
 cpu MHz static  : 5000
 
 cpu number      : 1
+physical id     : 2
+core id         : 0
+siblings        : 8
+cpu cores       : 4
 cpu MHz dynamic : 5000
 cpu MHz static  : 5000
 
 cpu number      : 2
+physical id     : 2
+core id         : 1
+siblings        : 8
+cpu cores       : 4
 cpu MHz dynamic : 5000
 cpu MHz static  : 5000
 
 cpu number      : 3
+physical id     : 2
+core id         : 1
+siblings        : 8
+cpu cores       : 4
 cpu MHz dynamic : 5000
 cpu MHz static  : 5000
 `
@@ -327,6 +343,18 @@ func TestCPUInfoParseS390X(t *testing.T) {
 	if want, have := 5000.0, cpuinfo[2].CPUMHz; want != have {
 		t.Errorf("want cpu MHz %v, have %v", want, have)
 	}
+	if want, have := uint(8), cpuinfo[3].Siblings; want != have {
+		t.Errorf("want siblings %v, have %v", want, have)
+	}
+	if want, have := "1", cpuinfo[3].CoreID; want != have {
+		t.Errorf("want core id %v, have %v", want, have)
+	}
+	if want, have := uint(4), cpuinfo[2].CPUCores; want != have {
+		t.Errorf("want cpu cores %v, have %v", want, have)
+	}
+	if want, have := "2", cpuinfo[2].PhysicalID; want != have {
+		t.Errorf("want physical id %v, have %v", want, have)
+	}
 }
 
 func TestCPUInfoParseMips(t *testing.T) {
Repository: tornadoweb/tornado
Pull request #1890 (2016-11-15 11:37:30)

Improve introspection of coroutines
This PR ensures the `__wrapped__` attribute of a coroutine points to the original function, and it also adds a utility function to know whether a function is a coroutine.

The initial motivation stems from the complications encountered in https://github.com/dask/distributed/pull/673.


Patch:
diff --git a/docs/gen.rst b/docs/gen.rst
index 8e867ed0..52c7f55d 100644
--- a/docs/gen.rst
+++ b/docs/gen.rst
@@ -50,6 +50,8 @@
 
    .. autofunction:: maybe_future
 
+   .. autofunction:: is_coroutine_function
+
    Legacy interface
    ----------------
 
diff --git a/tornado/gen.py b/tornado/gen.py
index 73f9ba10..d7df3b52 100644
--- a/tornado/gen.py
+++ b/tornado/gen.py
@@ -273,10 +273,11 @@ def _make_coroutine_wrapper(func, replace_callback):
     """
     # On Python 3.5, set the coroutine flag on our generator, to allow it
     # to be used with 'await'.
+    wrapped = func
     if hasattr(types, 'coroutine'):
         func = types.coroutine(func)
 
-    @functools.wraps(func)
+    @functools.wraps(wrapped)
     def wrapper(*args, **kwargs):
         future = TracebackFuture()
 
@@ -328,9 +329,19 @@ def _make_coroutine_wrapper(func, replace_callback):
                     future = None
         future.set_result(result)
         return future
+
+    wrapper.__wrapped__ = wrapped
+    wrapper.__tornado_coroutine__ = True
     return wrapper
 
 
+def is_coroutine_function(func):
+    """Return whether *func* is a coroutine function, i.e. a function
+    wrapped with `~.gen.coroutine`.
+    """
+    return getattr(func, '__tornado_coroutine__', False)
+
+
 class Return(Exception):
     """Special exception to return a value from a `coroutine`.
 


Test patch:
diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py
index bfaba567..8bbfc5fa 100644
--- a/tornado/test/gen_test.py
+++ b/tornado/test/gen_test.py
@@ -657,6 +657,28 @@ class GenCoroutineTest(AsyncTestCase):
         super(GenCoroutineTest, self).tearDown()
         assert self.finished
 
+    def test_attributes(self):
+        self.finished = True
+
+        def f():
+            yield gen.moment
+
+        coro = gen.coroutine(f)
+        self.assertEqual(coro.__name__, f.__name__)
+        self.assertEqual(coro.__module__, f.__module__)
+        self.assertIs(coro.__wrapped__, f)
+
+    def test_is_coroutine_function(self):
+        self.finished = True
+
+        def f():
+            yield gen.moment
+
+        coro = gen.coroutine(f)
+        self.assertFalse(gen.is_coroutine_function(f))
+        self.assertTrue(gen.is_coroutine_function(coro))
+        self.assertFalse(gen.is_coroutine_function(coro()))
+
     @gen_test
     def test_sync_gen_return(self):
         @gen.coroutine
Repository: tornadoweb/tornado
Pull request #1906 (2016-12-05 11:37:57)

tcpserver: handle_stream can be a native coroutine
Discovered here:

http://stackoverflow.com/questions/40239901/tornado-tcp-server-client-process-communication/40257248?noredirect=1#comment69144882_40257248

and here:

http://stackoverflow.com/questions/40968271/async-with-tornado-tcpclient-and-tcpserver/40973556


Patch:
diff --git a/tornado/tcpserver.py b/tornado/tcpserver.py
index 31f49359..ac666698 100644
--- a/tornado/tcpserver.py
+++ b/tornado/tcpserver.py
@@ -21,6 +21,7 @@ import errno
 import os
 import socket
 
+from tornado import gen
 from tornado.log import app_log
 from tornado.ioloop import IOLoop
 from tornado.iostream import IOStream, SSLIOStream
@@ -290,8 +291,10 @@ class TCPServer(object):
                 stream = IOStream(connection, io_loop=self.io_loop,
                                   max_buffer_size=self.max_buffer_size,
                                   read_chunk_size=self.read_chunk_size)
+
             future = self.handle_stream(stream, address)
             if future is not None:
-                self.io_loop.add_future(future, lambda f: f.result())
+                self.io_loop.add_future(gen.convert_yielded(future),
+                                        lambda f: f.result())
         except Exception:
             app_log.error("Error in connection callback", exc_info=True)


Test patch:
diff --git a/tornado/test/tcpserver_test.py b/tornado/test/tcpserver_test.py
index 5c51c71f..2d20b796 100644
--- a/tornado/test/tcpserver_test.py
+++ b/tornado/test/tcpserver_test.py
@@ -7,6 +7,7 @@ from tornado.iostream import IOStream
 from tornado.log import app_log
 from tornado.stack_context import NullContext
 from tornado.tcpserver import TCPServer
+from tornado.test.util import skipBefore35, exec_test
 from tornado.testing import AsyncTestCase, ExpectLog, bind_unused_port, gen_test
 
 
@@ -39,6 +40,28 @@ class TCPServerTest(AsyncTestCase):
             if client is not None:
                 client.close()
 
+    @skipBefore35
+    @gen_test
+    def test_handle_stream_native_coroutine(self):
+        # handle_stream may be a native coroutine.
+
+        namespace = exec_test(globals(), locals(), """
+        class TestServer(TCPServer):
+            async def handle_stream(self, stream, address):
+                stream.write(b'data')
+                stream.close()
+        """)
+
+        sock, port = bind_unused_port()
+        server = namespace['TestServer']()
+        server.add_socket(sock)
+        client = IOStream(socket.socket())
+        yield client.connect(('localhost', port))
+        result = yield client.read_until_close()
+        self.assertEqual(result, b'data')
+        server.stop()
+        client.close()
+
     def test_stop_twice(self):
         sock, port = bind_unused_port()
         server = TCPServer()
Repository: tornadoweb/tornado
Pull request #1977 (2017-03-21 16:33:10)

allow facebook responses to be in JSON or text/html when requesting a…
Since Graph API v2.3, Facebook "fixed" their access_token endpoint. It no longer returns plaintext query strings but JSON. This issue came up during an update to facebook's graph api v2.8.

I'm using 4.3 so I assume that's the branch I should merge into.

daynejones: @bdarnell thanks for the help. I updated the pull request. 

For posterity, here's the excerpt from Facebook's changelog:

> [Oauth Access Token] Format - The response format of https://www.facebook.com/v2.3/oauth/access_token returned when you exchange a code for an access_token now return valid JSON instead of being URL encoded. The new format of this response is {"access_token": {TOKEN}, "token_type":{TYPE}, "expires_in":{TIME}}. We made this update to be compliant with section 5.1 of RFC 6749.

The changelog can be found here: https://developers.facebook.com/docs/apps/changelog

And to echo what you already said, according to that changelog, v2.2 has reached it's end of life as of today, March 25, 2017.
bdarnell: Thanks! I'll make a 4.4.3 release with this patch and make a new 4.5 beta. 

Patch:
diff --git a/tornado/auth.py b/tornado/auth.py
index d71d56a6..554db592 100644
--- a/tornado/auth.py
+++ b/tornado/auth.py
@@ -978,9 +978,9 @@ class FacebookGraphMixin(OAuth2Mixin):
             future.set_exception(AuthError('Facebook auth error: %s' % str(response)))
             return
 
-        args = urlparse.parse_qs(escape.native_str(response.body))
+        args = escape.json_decode(response.body)
         session = {
-            "access_token": args["access_token"][-1],
+            "access_token": args.get("access_token"),
             "expires": args.get("expires")
         }
 


Test patch:
diff --git a/tornado/test/auth_test.py b/tornado/test/auth_test.py
index d18b7b97..14dd2f62 100644
--- a/tornado/test/auth_test.py
+++ b/tornado/test/auth_test.py
@@ -149,7 +149,7 @@ class FacebookClientLoginHandler(RequestHandler, FacebookGraphMixin):
 
 class FacebookServerAccessTokenHandler(RequestHandler):
     def get(self):
-        self.write('access_token=asdf')
+        self.write(dict(access_token="asdf"))
 
 
 class FacebookServerMeHandler(RequestHandler):
Repository: tornadoweb/tornado
Pull request #2018 (2017-04-19 16:03:43)

fix backwards compatibility of url_concat for args=None
A change made in 4.5 to the `url_concat` function broke some assumptions used in external code.

Notably, in SaltStack, this breaks parsing for some states people are using in production. See this issue: https://github.com/saltstack/salt/issues/40749

This patch fixes that external code to once again allow `url_concat("foo", None)` and adds a test to check this behavior.

My first time contributing - let me know if I missed something big. 

Thanks!


Patch:
diff --git a/tornado/httputil.py b/tornado/httputil.py
index dc206fc8..818ea914 100644
--- a/tornado/httputil.py
+++ b/tornado/httputil.py
@@ -603,6 +603,8 @@ def url_concat(url, args):
     >>> url_concat("http://example.com/foo?a=b", [("c", "d"), ("c", "d2")])
     'http://example.com/foo?a=b&c=d&c=d2'
     """
+    if args is None:
+        return url
     parsed_url = urlparse(url)
     if isinstance(args, dict):
         parsed_query = parse_qsl(parsed_url.query, keep_blank_values=True)


Test patch:
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
index 5249eadd..d1278567 100644
--- a/tornado/test/httputil_test.py
+++ b/tornado/test/httputil_test.py
@@ -66,6 +66,13 @@ class TestUrlConcat(unittest.TestCase):
         )
         self.assertEqual(url, "https://localhost/path?r=1&t=2")
 
+    def test_url_concat_none_params(self):
+        url = url_concat(
+            "https://localhost/path?r=1&t=2",
+            None,
+        )
+        self.assertEqual(url, "https://localhost/path?r=1&t=2")
+
     def test_url_concat_with_frag(self):
         url = url_concat(
             "https://localhost/path#tab",
Repository: tornadoweb/tornado
Pull request #2132 (2017-08-10 15:25:46)

Remove headers from HTTPServerRequest repr
In tornadoweb/tornado#1112 it was decided to stop including headers in the request repr, since they are needlessly verbose and risk leaking user secrets into the application log.


Patch:
diff --git a/tornado/httputil.py b/tornado/httputil.py
index a98273ae..f2b289b3 100755
--- a/tornado/httputil.py
+++ b/tornado/httputil.py
@@ -467,8 +467,7 @@ class HTTPServerRequest(object):
     def __repr__(self):
         attrs = ("protocol", "host", "method", "uri", "version", "remote_ip")
         args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
-        return "%s(%s, headers=%s)" % (
-            self.__class__.__name__, args, dict(self.headers))
+        return "%s(%s)" % (self.__class__.__name__, args)
 
 
 class HTTPInputError(Exception):


Test patch:
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
index 7ac79925..1521ca03 100644
--- a/tornado/test/httputil_test.py
+++ b/tornado/test/httputil_test.py
@@ -418,6 +418,10 @@ class HTTPServerRequestTest(unittest.TestCase):
         requets = HTTPServerRequest(uri='/')
         self.assertIsInstance(requets.body, bytes)
 
+    def test_repr_does_not_contain_headers(self):
+        request = HTTPServerRequest(uri='/', headers={'Canary': 'Coal Mine'})
+        self.assertTrue('Canary' not in repr(request))
+
 
 class ParseRequestStartLineTest(unittest.TestCase):
     METHOD = "GET"
Repository: tornadoweb/tornado
Pull request #2266 (2018-01-31 17:09:34)

fix gen.multi with gen.moment
fixes assertion in gen.multi when calling convert_yielded with more than one gen.moment or None

convert_yielded can be called with None or [None]. When it is called with [None], gen.multi gets [gen.moment], which fails assert(is_future) now that gen.moment is no longer a real Future.


Patch:
diff --git a/tornado/gen.py b/tornado/gen.py
index 183b28f1..0ceda1d5 100644
--- a/tornado/gen.py
+++ b/tornado/gen.py
@@ -814,7 +814,7 @@ def multi_future(children, quiet_exceptions=()):
     else:
         keys = None
     children = list(map(convert_yielded, children))
-    assert all(is_future(i) for i in children)
+    assert all(is_future(i) or isinstance(i, _NullFuture) for i in children)
     unfinished_children = set(children)
 
     future = _create_future()


Test patch:
diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py
index 6850af81..12621cec 100644
--- a/tornado/test/gen_test.py
+++ b/tornado/test/gen_test.py
@@ -1598,6 +1598,18 @@ class RunnerGCTest(AsyncTestCase):
             # coroutine finalizer was called (not on PyPy3 apparently)
             self.assertIs(result[-1], None)
 
+    def test_multi_moment(self):
+        # Test gen.multi with moment
+        # now that it's not a real Future
+        @gen.coroutine
+        def wait_a_moment():
+            result = yield gen.multi([gen.moment, gen.moment])
+            raise gen.Return(result)
+
+        loop = self.get_new_ioloop()
+        result = loop.run_sync(wait_a_moment)
+        self.assertEqual(result, [None, None])
+
 
 if __name__ == '__main__':
     unittest.main()
Repository: tornadoweb/tornado
Pull request #2741 (2019-09-08 03:22:41)

Fix extra data sending at HEAD response with Transfer-Encoding: Chunked
Hi, Tornado send extra data `0\r\n\r\n` at HEAD response with Transfer-Encoding: Chunked.

```zsh
➜  ~ telnet localhost 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.1

HTTP/1.1 200 OK
Server: TornadoServer/6.1.dev1
Content-Type: text/html; charset=UTF-8
Date: Sun, 08 Sep 2019 03:19:09 GMT
Transfer-Encoding: chunked

0

```


Patch:
diff --git a/tornado/http1connection.py b/tornado/http1connection.py
index 9b40fd65..9e71e594 100644
--- a/tornado/http1connection.py
+++ b/tornado/http1connection.py
@@ -406,6 +406,8 @@ class HTTP1Connection(httputil.HTTPConnection):
                 # self._request_start_line.version or
                 # start_line.version?
                 self._request_start_line.version == "HTTP/1.1"
+                # Omit payload header field for HEAD request.
+                and self._request_start_line.method != "HEAD"
                 # 1xx, 204 and 304 responses have no body (not even a zero-length
                 # body), and so should not have either Content-Length or
                 # Transfer-Encoding headers.


Test patch:
diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py
index 4ad3ce7e..7a552d19 100644
--- a/tornado/test/httpserver_test.py
+++ b/tornado/test/httpserver_test.py
@@ -770,6 +770,12 @@ class KeepAliveTest(AsyncHTTPTestCase):
                 # be written out in chunks.
                 self.write("".join(chr(i % 256) * 1024 for i in range(512)))
 
+        class TransferEncodingChunkedHandler(RequestHandler):
+            @gen.coroutine
+            def head(self):
+                self.write("Hello world")
+                yield self.flush()
+
         class FinishOnCloseHandler(RequestHandler):
             def initialize(self, cleanup_event):
                 self.cleanup_event = cleanup_event
@@ -790,6 +796,7 @@ class KeepAliveTest(AsyncHTTPTestCase):
             [
                 ("/", HelloHandler),
                 ("/large", LargeHandler),
+                ("/chunked", TransferEncodingChunkedHandler),
                 (
                     "/finish_on_close",
                     FinishOnCloseHandler,
@@ -943,6 +950,16 @@ class KeepAliveTest(AsyncHTTPTestCase):
         self.assertEqual(self.headers["Connection"], "Keep-Alive")
         self.close()
 
+    @gen_test
+    def test_keepalive_chunked_head_no_body(self):
+        yield self.connect()
+        self.stream.write(b"HEAD /chunked HTTP/1.1\r\n\r\n")
+        yield self.read_headers()
+
+        self.stream.write(b"HEAD /chunked HTTP/1.1\r\n\r\n")
+        yield self.read_headers()
+        self.close()
+
 
 class GzipBaseTest(object):
     def get_app(self):