{
    "file_diffs": [
        {
            "old_file_content": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Non-blocking HTTP client implementation using pycurl.\"\"\"\n\nfrom __future__ import absolute_import, division, print_function, with_statement\n\nimport collections\nimport logging\nimport pycurl\nimport threading\nimport time\n\nfrom tornado import httputil\nfrom tornado import ioloop\nfrom tornado.log import gen_log\nfrom tornado import stack_context\n\nfrom tornado.escape import utf8, native_str\nfrom tornado.httpclient import HTTPResponse, HTTPError, AsyncHTTPClient, main\nfrom tornado.util import bytes_type\n\ntry:\n    from io import BytesIO  # py3\nexcept ImportError:\n    from cStringIO import StringIO as BytesIO  # py2\n\n\nclass CurlAsyncHTTPClient(AsyncHTTPClient):\n    def initialize(self, io_loop, max_clients=10, defaults=None):\n        super(CurlAsyncHTTPClient, self).initialize(io_loop, defaults=defaults)\n        self._multi = pycurl.CurlMulti()\n        self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout)\n        self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket)\n        self._curls = [_curl_create() for i in range(max_clients)]\n        self._free_list = self._curls[:]\n        self._requests = collections.deque()\n        self._fds = {}\n        self._timeout = None\n\n        try:\n            self._socket_action = self._multi.socket_action\n        except AttributeError:\n            # socket_action is found in pycurl since 7.18.2 (it's been\n            # in libcurl longer than that but wasn't accessible to\n            # python).\n            gen_log.warning(\"socket_action method missing from pycurl; \"\n                            \"falling back to socket_all. Upgrading \"\n                            \"libcurl and pycurl will improve performance\")\n            self._socket_action = \\\n                lambda fd, action: self._multi.socket_all()\n\n        # libcurl has bugs that sometimes cause it to not report all\n        # relevant file descriptors and timeouts to TIMERFUNCTION/\n        # SOCKETFUNCTION.  Mitigate the effects of such bugs by\n        # forcing a periodic scan of all active requests.\n        self._force_timeout_callback = ioloop.PeriodicCallback(\n            self._handle_force_timeout, 1000, io_loop=io_loop)\n        self._force_timeout_callback.start()\n\n        # Work around a bug in libcurl 7.29.0: Some fields in the curl\n        # multi object are initialized lazily, and its destructor will\n        # segfault if it is destroyed without having been used.  Add\n        # and remove a dummy handle to make sure everything is\n        # initialized.\n        dummy_curl_handle = pycurl.Curl()\n        self._multi.add_handle(dummy_curl_handle)\n        self._multi.remove_handle(dummy_curl_handle)\n\n    def close(self):\n        self._force_timeout_callback.stop()\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        for curl in self._curls:\n            curl.close()\n        self._multi.close()\n        self._closed = True\n        super(CurlAsyncHTTPClient, self).close()\n\n    def fetch_impl(self, request, callback):\n        self._requests.append((request, callback))\n        self._process_queue()\n        self._set_timeout(0)\n\n    def _handle_socket(self, event, fd, multi, data):\n        \"\"\"Called by libcurl when it wants to change the file descriptors\n        it cares about.\n        \"\"\"\n        event_map = {\n            pycurl.POLL_NONE: ioloop.IOLoop.NONE,\n            pycurl.POLL_IN: ioloop.IOLoop.READ,\n            pycurl.POLL_OUT: ioloop.IOLoop.WRITE,\n            pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE\n        }\n        if event == pycurl.POLL_REMOVE:\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n                del self._fds[fd]\n        else:\n            ioloop_event = event_map[event]\n            # libcurl sometimes closes a socket and then opens a new\n            # one using the same FD without giving us a POLL_NONE in\n            # between.  This is a problem with the epoll IOLoop,\n            # because the kernel can tell when a socket is closed and\n            # removes it from the epoll automatically, causing future\n            # update_handler calls to fail.  Since we can't tell when\n            # this has happened, always use remove and re-add\n            # instead of update.\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n            self.io_loop.add_handler(fd, self._handle_events,\n                                     ioloop_event)\n            self._fds[fd] = ioloop_event\n\n    def _set_timeout(self, msecs):\n        \"\"\"Called by libcurl to schedule a timeout.\"\"\"\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        self._timeout = self.io_loop.add_timeout(\n            self.io_loop.time() + msecs / 1000.0, self._handle_timeout)\n\n    def _handle_events(self, fd, events):\n        \"\"\"Called by IOLoop when there is activity on one of our\n        file descriptors.\n        \"\"\"\n        action = 0\n        if events & ioloop.IOLoop.READ:\n            action |= pycurl.CSELECT_IN\n        if events & ioloop.IOLoop.WRITE:\n            action |= pycurl.CSELECT_OUT\n        while True:\n            try:\n                ret, num_handles = self._socket_action(fd, action)\n            except pycurl.error as e:\n                ret = e.args[0]\n            if ret != pycurl.E_CALL_MULTI_PERFORM:\n                break\n        self._finish_pending_requests()\n\n    def _handle_timeout(self):\n        \"\"\"Called by IOLoop when the requested timeout has passed.\"\"\"\n        with stack_context.NullContext():\n            self._timeout = None\n            while True:\n                try:\n                    ret, num_handles = self._socket_action(\n                        pycurl.SOCKET_TIMEOUT, 0)\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n        # In theory, we shouldn't have to do this because curl will\n        # call _set_timeout whenever the timeout changes.  However,\n        # sometimes after _handle_timeout we will need to reschedule\n        # immediately even though nothing has changed from curl's\n        # perspective.  This is because when socket_action is\n        # called with SOCKET_TIMEOUT, libcurl decides internally which\n        # timeouts need to be processed by using a monotonic clock\n        # (where available) while tornado uses python's time.time()\n        # to decide when timeouts have occurred.  When those clocks\n        # disagree on elapsed time (as they will whenever there is an\n        # NTP adjustment), tornado might call _handle_timeout before\n        # libcurl is ready.  After each timeout, resync the scheduled\n        # timeout with libcurl's current state.\n        new_timeout = self._multi.timeout()\n        if new_timeout >= 0:\n            self._set_timeout(new_timeout)\n\n    def _handle_force_timeout(self):\n        \"\"\"Called by IOLoop periodically to ask libcurl to process any\n        events it may have forgotten about.\n        \"\"\"\n        with stack_context.NullContext():\n            while True:\n                try:\n                    ret, num_handles = self._multi.socket_all()\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n    def _finish_pending_requests(self):\n        \"\"\"Process any requests that were completed by the last\n        call to multi.socket_action.\n        \"\"\"\n        while True:\n            num_q, ok_list, err_list = self._multi.info_read()\n            for curl in ok_list:\n                self._finish(curl)\n            for curl, errnum, errmsg in err_list:\n                self._finish(curl, errnum, errmsg)\n            if num_q == 0:\n                break\n        self._process_queue()\n\n    def _process_queue(self):\n        with stack_context.NullContext():\n            while True:\n                started = 0\n                while self._free_list and self._requests:\n                    started += 1\n                    curl = self._free_list.pop()\n                    (request, callback) = self._requests.popleft()\n                    curl.info = {\n                        \"headers\": httputil.HTTPHeaders(),\n                        \"buffer\": BytesIO(),\n                        \"request\": request,\n                        \"callback\": callback,\n                        \"curl_start_time\": time.time(),\n                    }\n                    # Disable IPv6 to mitigate the effects of this bug\n                    # on curl versions <= 7.21.0\n                    # http://sourceforge.net/tracker/?func=detail&aid=3017819&group_id=976&atid=100976\n                    if pycurl.version_info()[2] <= 0x71500:  # 7.21.0\n                        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n                    _curl_setup_request(curl, request, curl.info[\"buffer\"],\n                                        curl.info[\"headers\"])\n                    self._multi.add_handle(curl)\n\n                if not started:\n                    break\n\n    def _finish(self, curl, curl_error=None, curl_message=None):\n        info = curl.info\n        curl.info = None\n        self._multi.remove_handle(curl)\n        self._free_list.append(curl)\n        buffer = info[\"buffer\"]\n        if curl_error:\n            error = CurlError(curl_error, curl_message)\n            code = error.code\n            effective_url = None\n            buffer.close()\n            buffer = None\n        else:\n            error = None\n            code = curl.getinfo(pycurl.HTTP_CODE)\n            effective_url = curl.getinfo(pycurl.EFFECTIVE_URL)\n            buffer.seek(0)\n        # the various curl timings are documented at\n        # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html\n        time_info = dict(\n            queue=info[\"curl_start_time\"] - info[\"request\"].start_time,\n            namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME),\n            connect=curl.getinfo(pycurl.CONNECT_TIME),\n            pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME),\n            starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME),\n            total=curl.getinfo(pycurl.TOTAL_TIME),\n            redirect=curl.getinfo(pycurl.REDIRECT_TIME),\n        )\n        try:\n            info[\"callback\"](HTTPResponse(\n                request=info[\"request\"], code=code, headers=info[\"headers\"],\n                buffer=buffer, effective_url=effective_url, error=error,\n                request_time=time.time() - info[\"curl_start_time\"],\n                time_info=time_info))\n        except Exception:\n            self.handle_callback_exception(info[\"callback\"])\n\n    def handle_callback_exception(self, callback):\n        self.io_loop.handle_callback_exception(callback)\n\n\nclass CurlError(HTTPError):\n    def __init__(self, errno, message):\n        HTTPError.__init__(self, 599, message)\n        self.errno = errno\n\n\ndef _curl_create():\n    curl = pycurl.Curl()\n    if gen_log.isEnabledFor(logging.DEBUG):\n        curl.setopt(pycurl.VERBOSE, 1)\n        curl.setopt(pycurl.DEBUGFUNCTION, _curl_debug)\n    return curl\n\n\ndef _curl_setup_request(curl, request, buffer, headers):\n    curl.setopt(pycurl.URL, native_str(request.url))\n\n    # libcurl's magic \"Expect: 100-continue\" behavior causes delays\n    # with servers that don't support it (which include, among others,\n    # Google's OpenID endpoint).  Additionally, this behavior has\n    # a bug in conjunction with the curl_multi_socket_action API\n    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),\n    # which increases the delays.  It's more trouble than it's worth,\n    # so just turn off the feature (yes, setting Expect: to an empty\n    # value is the official way to disable this)\n    if \"Expect\" not in request.headers:\n        request.headers[\"Expect\"] = \"\"\n\n    # libcurl adds Pragma: no-cache by default; disable that too\n    if \"Pragma\" not in request.headers:\n        request.headers[\"Pragma\"] = \"\"\n\n    # Request headers may be either a regular dict or HTTPHeaders object\n    if isinstance(request.headers, httputil.HTTPHeaders):\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.get_all()])\n    else:\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.items()])\n\n    if request.header_callback:\n        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)\n    else:\n        curl.setopt(pycurl.HEADERFUNCTION,\n                    lambda line: _curl_header_callback(headers, line))\n    if request.streaming_callback:\n        write_function = request.streaming_callback\n    else:\n        write_function = buffer.write\n    if bytes_type is str:  # py2\n        curl.setopt(pycurl.WRITEFUNCTION, write_function)\n    else:  # py3\n        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes\n        # a fork/port.  That version has a bug in which it passes unicode\n        # strings instead of bytes to the WRITEFUNCTION.  This means that\n        # if you use a WRITEFUNCTION (which tornado always does), you cannot\n        # download arbitrary binary data.  This needs to be fixed in the\n        # ported pycurl package, but in the meantime this lambda will\n        # make it work for downloading (utf8) text.\n        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))\n    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)\n    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)\n    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))\n    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))\n    if request.user_agent:\n        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))\n    else:\n        curl.setopt(pycurl.USERAGENT, \"Mozilla/5.0 (compatible; pycurl)\")\n    if request.network_interface:\n        curl.setopt(pycurl.INTERFACE, request.network_interface)\n    if request.use_gzip:\n        curl.setopt(pycurl.ENCODING, \"gzip,deflate\")\n    else:\n        curl.setopt(pycurl.ENCODING, \"none\")\n    if request.proxy_host and request.proxy_port:\n        curl.setopt(pycurl.PROXY, request.proxy_host)\n        curl.setopt(pycurl.PROXYPORT, request.proxy_port)\n        if request.proxy_username:\n            credentials = '%s:%s' % (request.proxy_username,\n                                     request.proxy_password)\n            curl.setopt(pycurl.PROXYUSERPWD, credentials)\n    else:\n        curl.setopt(pycurl.PROXY, '')\n        curl.unsetopt(pycurl.PROXYUSERPWD)\n    if request.validate_cert:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 1)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 2)\n    else:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 0)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 0)\n    if request.ca_certs is not None:\n        curl.setopt(pycurl.CAINFO, request.ca_certs)\n    else:\n        # There is no way to restore pycurl.CAINFO to its default value\n        # (Using unsetopt makes it reject all certificates).\n        # I don't see any way to read the default value from python so it\n        # can be restored later.  We'll have to just leave CAINFO untouched\n        # if no ca_certs file was specified, and require that if any\n        # request uses a custom ca_certs file, they all must.\n        pass\n\n    if request.allow_ipv6 is False:\n        # Curl behaves reasonably when DNS resolution gives an ipv6 address\n        # that we can't reach, so allow ipv6 unless the user asks to disable.\n        # (but see version check in _process_queue above)\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n    else:\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)\n\n    # Set the request method through curl's irritating interface which makes\n    # up names for almost every single method\n    curl_options = {\n        \"GET\": pycurl.HTTPGET,\n        \"POST\": pycurl.POST,\n        \"PUT\": pycurl.UPLOAD,\n        \"HEAD\": pycurl.NOBODY,\n    }\n    custom_methods = set([\"DELETE\", \"OPTIONS\", \"PATCH\"])\n    for o in curl_options.values():\n        curl.setopt(o, False)\n    if request.method in curl_options:\n        curl.unsetopt(pycurl.CUSTOMREQUEST)\n        curl.setopt(curl_options[request.method], True)\n    elif request.allow_nonstandard_methods or request.method in custom_methods:\n        curl.setopt(pycurl.CUSTOMREQUEST, request.method)\n    else:\n        raise KeyError('unknown method ' + request.method)\n\n    # Handle curl's cryptic options for every individual HTTP method\n    if request.method in (\"POST\", \"PUT\"):\n        if request.body is None:\n            raise AssertionError(\n                'Body must not be empty for \"%s\" request'\n                % request.method)\n\n        request_buffer = BytesIO(utf8(request.body))\n        curl.setopt(pycurl.READFUNCTION, request_buffer.read)\n        if request.method == \"POST\":\n            def ioctl(cmd):\n                if cmd == curl.IOCMD_RESTARTREAD:\n                    request_buffer.seek(0)\n            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)\n            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))\n        else:\n            curl.setopt(pycurl.INFILESIZE, len(request.body))\n    elif request.method == \"GET\":\n        if request.body is not None:\n            raise AssertionError('Body must be empty for GET request')\n\n    if request.auth_username is not None:\n        userpwd = \"%s:%s\" % (request.auth_username, request.auth_password or '')\n\n        if request.auth_mode is None or request.auth_mode == \"basic\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)\n        elif request.auth_mode == \"digest\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)\n        else:\n            raise ValueError(\"Unsupported auth_mode %s\" % request.auth_mode)\n\n        curl.setopt(pycurl.USERPWD, native_str(userpwd))\n        gen_log.debug(\"%s %s (username: %r)\", request.method, request.url,\n                      request.auth_username)\n    else:\n        curl.unsetopt(pycurl.USERPWD)\n        gen_log.debug(\"%s %s\", request.method, request.url)\n\n    if request.client_cert is not None:\n        curl.setopt(pycurl.SSLCERT, request.client_cert)\n\n    if request.client_key is not None:\n        curl.setopt(pycurl.SSLKEY, request.client_key)\n\n    if threading.activeCount() > 1:\n        # libcurl/pycurl is not thread-safe by default.  When multiple threads\n        # are used, signals should be disabled.  This has the side effect\n        # of disabling DNS timeouts in some environments (when libcurl is\n        # not linked against ares), so we don't do it when there is only one\n        # thread.  Applications that use many short-lived threads may need\n        # to set NOSIGNAL manually in a prepare_curl_callback since\n        # there may not be any other threads running at the time we call\n        # threading.activeCount.\n        curl.setopt(pycurl.NOSIGNAL, 1)\n    if request.prepare_curl_callback is not None:\n        request.prepare_curl_callback(curl)\n\n\ndef _curl_header_callback(headers, header_line):\n    # header_line as returned by curl includes the end-of-line characters.\n    header_line = header_line.strip()\n    if header_line.startswith(\"HTTP/\"):\n        headers.clear()\n        return\n    if not header_line:\n        return\n    headers.parse_line(header_line)\n\n\ndef _curl_debug(debug_type, debug_msg):\n    debug_types = ('I', '<', '>', '<', '>')\n    if debug_type == 0:\n        gen_log.debug('%s', debug_msg.strip())\n    elif debug_type in (1, 2):\n        for line in debug_msg.splitlines():\n            gen_log.debug('%s %s', debug_types[debug_type], line)\n    elif debug_type == 4:\n        gen_log.debug('%s %r', debug_types[debug_type], debug_msg)\n\nif __name__ == \"__main__\":\n    AsyncHTTPClient.configure(CurlAsyncHTTPClient)\n    main()\n",
            "new_file_content": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Non-blocking HTTP client implementation using pycurl.\"\"\"\n\nfrom __future__ import absolute_import, division, print_function, with_statement\n\nimport collections\nimport logging\nimport pycurl\nimport threading\nimport time\n\nfrom tornado import httputil\nfrom tornado import ioloop\nfrom tornado.log import gen_log\nfrom tornado import stack_context\n\nfrom tornado.escape import utf8, native_str\nfrom tornado.httpclient import HTTPResponse, HTTPError, AsyncHTTPClient, main\nfrom tornado.util import bytes_type\n\ntry:\n    from io import BytesIO  # py3\nexcept ImportError:\n    from cStringIO import StringIO as BytesIO  # py2\n\n\nclass CurlAsyncHTTPClient(AsyncHTTPClient):\n    def initialize(self, io_loop, max_clients=10, defaults=None):\n        super(CurlAsyncHTTPClient, self).initialize(io_loop, defaults=defaults)\n        self._multi = pycurl.CurlMulti()\n        self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout)\n        self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket)\n        self._curls = [_curl_create() for i in range(max_clients)]\n        self._free_list = self._curls[:]\n        self._requests = collections.deque()\n        self._fds = {}\n        self._timeout = None\n\n        try:\n            self._socket_action = self._multi.socket_action\n        except AttributeError:\n            # socket_action is found in pycurl since 7.18.2 (it's been\n            # in libcurl longer than that but wasn't accessible to\n            # python).\n            gen_log.warning(\"socket_action method missing from pycurl; \"\n                            \"falling back to socket_all. Upgrading \"\n                            \"libcurl and pycurl will improve performance\")\n            self._socket_action = \\\n                lambda fd, action: self._multi.socket_all()\n\n        # libcurl has bugs that sometimes cause it to not report all\n        # relevant file descriptors and timeouts to TIMERFUNCTION/\n        # SOCKETFUNCTION.  Mitigate the effects of such bugs by\n        # forcing a periodic scan of all active requests.\n        self._force_timeout_callback = ioloop.PeriodicCallback(\n            self._handle_force_timeout, 1000, io_loop=io_loop)\n        self._force_timeout_callback.start()\n\n        # Work around a bug in libcurl 7.29.0: Some fields in the curl\n        # multi object are initialized lazily, and its destructor will\n        # segfault if it is destroyed without having been used.  Add\n        # and remove a dummy handle to make sure everything is\n        # initialized.\n        dummy_curl_handle = pycurl.Curl()\n        self._multi.add_handle(dummy_curl_handle)\n        self._multi.remove_handle(dummy_curl_handle)\n\n    def close(self):\n        self._force_timeout_callback.stop()\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        for curl in self._curls:\n            curl.close()\n        self._multi.close()\n        self._closed = True\n        super(CurlAsyncHTTPClient, self).close()\n\n    def fetch_impl(self, request, callback):\n        self._requests.append((request, callback))\n        self._process_queue()\n        self._set_timeout(0)\n\n    def _handle_socket(self, event, fd, multi, data):\n        \"\"\"Called by libcurl when it wants to change the file descriptors\n        it cares about.\n        \"\"\"\n        event_map = {\n            pycurl.POLL_NONE: ioloop.IOLoop.NONE,\n            pycurl.POLL_IN: ioloop.IOLoop.READ,\n            pycurl.POLL_OUT: ioloop.IOLoop.WRITE,\n            pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE\n        }\n        if event == pycurl.POLL_REMOVE:\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n                del self._fds[fd]\n        else:\n            ioloop_event = event_map[event]\n            # libcurl sometimes closes a socket and then opens a new\n            # one using the same FD without giving us a POLL_NONE in\n            # between.  This is a problem with the epoll IOLoop,\n            # because the kernel can tell when a socket is closed and\n            # removes it from the epoll automatically, causing future\n            # update_handler calls to fail.  Since we can't tell when\n            # this has happened, always use remove and re-add\n            # instead of update.\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n            self.io_loop.add_handler(fd, self._handle_events,\n                                     ioloop_event)\n            self._fds[fd] = ioloop_event\n\n    def _set_timeout(self, msecs):\n        \"\"\"Called by libcurl to schedule a timeout.\"\"\"\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        self._timeout = self.io_loop.add_timeout(\n            self.io_loop.time() + msecs / 1000.0, self._handle_timeout)\n\n    def _handle_events(self, fd, events):\n        \"\"\"Called by IOLoop when there is activity on one of our\n        file descriptors.\n        \"\"\"\n        action = 0\n        if events & ioloop.IOLoop.READ:\n            action |= pycurl.CSELECT_IN\n        if events & ioloop.IOLoop.WRITE:\n            action |= pycurl.CSELECT_OUT\n        while True:\n            try:\n                ret, num_handles = self._socket_action(fd, action)\n            except pycurl.error as e:\n                ret = e.args[0]\n            if ret != pycurl.E_CALL_MULTI_PERFORM:\n                break\n        self._finish_pending_requests()\n\n    def _handle_timeout(self):\n        \"\"\"Called by IOLoop when the requested timeout has passed.\"\"\"\n        with stack_context.NullContext():\n            self._timeout = None\n            while True:\n                try:\n                    ret, num_handles = self._socket_action(\n                        pycurl.SOCKET_TIMEOUT, 0)\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n        # In theory, we shouldn't have to do this because curl will\n        # call _set_timeout whenever the timeout changes.  However,\n        # sometimes after _handle_timeout we will need to reschedule\n        # immediately even though nothing has changed from curl's\n        # perspective.  This is because when socket_action is\n        # called with SOCKET_TIMEOUT, libcurl decides internally which\n        # timeouts need to be processed by using a monotonic clock\n        # (where available) while tornado uses python's time.time()\n        # to decide when timeouts have occurred.  When those clocks\n        # disagree on elapsed time (as they will whenever there is an\n        # NTP adjustment), tornado might call _handle_timeout before\n        # libcurl is ready.  After each timeout, resync the scheduled\n        # timeout with libcurl's current state.\n        new_timeout = self._multi.timeout()\n        if new_timeout >= 0:\n            self._set_timeout(new_timeout)\n\n    def _handle_force_timeout(self):\n        \"\"\"Called by IOLoop periodically to ask libcurl to process any\n        events it may have forgotten about.\n        \"\"\"\n        with stack_context.NullContext():\n            while True:\n                try:\n                    ret, num_handles = self._multi.socket_all()\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n    def _finish_pending_requests(self):\n        \"\"\"Process any requests that were completed by the last\n        call to multi.socket_action.\n        \"\"\"\n        while True:\n            num_q, ok_list, err_list = self._multi.info_read()\n            for curl in ok_list:\n                self._finish(curl)\n            for curl, errnum, errmsg in err_list:\n                self._finish(curl, errnum, errmsg)\n            if num_q == 0:\n                break\n        self._process_queue()\n\n    def _process_queue(self):\n        with stack_context.NullContext():\n            while True:\n                started = 0\n                while self._free_list and self._requests:\n                    started += 1\n                    curl = self._free_list.pop()\n                    (request, callback) = self._requests.popleft()\n                    curl.info = {\n                        \"headers\": httputil.HTTPHeaders(),\n                        \"buffer\": BytesIO(),\n                        \"request\": request,\n                        \"callback\": callback,\n                        \"curl_start_time\": time.time(),\n                    }\n                    # Disable IPv6 to mitigate the effects of this bug\n                    # on curl versions <= 7.21.0\n                    # http://sourceforge.net/tracker/?func=detail&aid=3017819&group_id=976&atid=100976\n                    if pycurl.version_info()[2] <= 0x71500:  # 7.21.0\n                        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n                    _curl_setup_request(curl, request, curl.info[\"buffer\"],\n                                        curl.info[\"headers\"])\n                    self._multi.add_handle(curl)\n\n                if not started:\n                    break\n\n    def _finish(self, curl, curl_error=None, curl_message=None):\n        info = curl.info\n        curl.info = None\n        self._multi.remove_handle(curl)\n        self._free_list.append(curl)\n        buffer = info[\"buffer\"]\n        if curl_error:\n            error = CurlError(curl_error, curl_message)\n            code = error.code\n            effective_url = None\n            buffer.close()\n            buffer = None\n        else:\n            error = None\n            code = curl.getinfo(pycurl.HTTP_CODE)\n            effective_url = curl.getinfo(pycurl.EFFECTIVE_URL)\n            buffer.seek(0)\n        # the various curl timings are documented at\n        # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html\n        time_info = dict(\n            queue=info[\"curl_start_time\"] - info[\"request\"].start_time,\n            namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME),\n            connect=curl.getinfo(pycurl.CONNECT_TIME),\n            pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME),\n            starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME),\n            total=curl.getinfo(pycurl.TOTAL_TIME),\n            redirect=curl.getinfo(pycurl.REDIRECT_TIME),\n        )\n        try:\n            info[\"callback\"](HTTPResponse(\n                request=info[\"request\"], code=code, headers=info[\"headers\"],\n                buffer=buffer, effective_url=effective_url, error=error,\n                request_time=time.time() - info[\"curl_start_time\"],\n                time_info=time_info))\n        except Exception:\n            self.handle_callback_exception(info[\"callback\"])\n\n    def handle_callback_exception(self, callback):\n        self.io_loop.handle_callback_exception(callback)\n\n\nclass CurlError(HTTPError):\n    def __init__(self, errno, message):\n        HTTPError.__init__(self, 599, message)\n        self.errno = errno\n\n\ndef _curl_create():\n    curl = pycurl.Curl()\n    if gen_log.isEnabledFor(logging.DEBUG):\n        curl.setopt(pycurl.VERBOSE, 1)\n        curl.setopt(pycurl.DEBUGFUNCTION, _curl_debug)\n    return curl\n\n\ndef _curl_setup_request(curl, request, buffer, headers):\n    curl.setopt(pycurl.URL, native_str(request.url))\n\n    # libcurl's magic \"Expect: 100-continue\" behavior causes delays\n    # with servers that don't support it (which include, among others,\n    # Google's OpenID endpoint).  Additionally, this behavior has\n    # a bug in conjunction with the curl_multi_socket_action API\n    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),\n    # which increases the delays.  It's more trouble than it's worth,\n    # so just turn off the feature (yes, setting Expect: to an empty\n    # value is the official way to disable this)\n    if \"Expect\" not in request.headers:\n        request.headers[\"Expect\"] = \"\"\n\n    # libcurl adds Pragma: no-cache by default; disable that too\n    if \"Pragma\" not in request.headers:\n        request.headers[\"Pragma\"] = \"\"\n\n    # Request headers may be either a regular dict or HTTPHeaders object\n    if isinstance(request.headers, httputil.HTTPHeaders):\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.get_all()])\n    else:\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.items()])\n\n    if request.header_callback:\n        curl.setopt(pycurl.HEADERFUNCTION,\n                    lambda line: request.header_callback(native_str(line)))\n    else:\n        curl.setopt(pycurl.HEADERFUNCTION,\n                    lambda line: _curl_header_callback(headers,\n                                                       native_str(line)))\n    if request.streaming_callback:\n        write_function = request.streaming_callback\n    else:\n        write_function = buffer.write\n    if bytes_type is str:  # py2\n        curl.setopt(pycurl.WRITEFUNCTION, write_function)\n    else:  # py3\n        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes\n        # a fork/port.  That version has a bug in which it passes unicode\n        # strings instead of bytes to the WRITEFUNCTION.  This means that\n        # if you use a WRITEFUNCTION (which tornado always does), you cannot\n        # download arbitrary binary data.  This needs to be fixed in the\n        # ported pycurl package, but in the meantime this lambda will\n        # make it work for downloading (utf8) text.\n        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))\n    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)\n    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)\n    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))\n    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))\n    if request.user_agent:\n        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))\n    else:\n        curl.setopt(pycurl.USERAGENT, \"Mozilla/5.0 (compatible; pycurl)\")\n    if request.network_interface:\n        curl.setopt(pycurl.INTERFACE, request.network_interface)\n    if request.use_gzip:\n        curl.setopt(pycurl.ENCODING, \"gzip,deflate\")\n    else:\n        curl.setopt(pycurl.ENCODING, \"none\")\n    if request.proxy_host and request.proxy_port:\n        curl.setopt(pycurl.PROXY, request.proxy_host)\n        curl.setopt(pycurl.PROXYPORT, request.proxy_port)\n        if request.proxy_username:\n            credentials = '%s:%s' % (request.proxy_username,\n                                     request.proxy_password)\n            curl.setopt(pycurl.PROXYUSERPWD, credentials)\n    else:\n        curl.setopt(pycurl.PROXY, '')\n        curl.unsetopt(pycurl.PROXYUSERPWD)\n    if request.validate_cert:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 1)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 2)\n    else:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 0)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 0)\n    if request.ca_certs is not None:\n        curl.setopt(pycurl.CAINFO, request.ca_certs)\n    else:\n        # There is no way to restore pycurl.CAINFO to its default value\n        # (Using unsetopt makes it reject all certificates).\n        # I don't see any way to read the default value from python so it\n        # can be restored later.  We'll have to just leave CAINFO untouched\n        # if no ca_certs file was specified, and require that if any\n        # request uses a custom ca_certs file, they all must.\n        pass\n\n    if request.allow_ipv6 is False:\n        # Curl behaves reasonably when DNS resolution gives an ipv6 address\n        # that we can't reach, so allow ipv6 unless the user asks to disable.\n        # (but see version check in _process_queue above)\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n    else:\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)\n\n    # Set the request method through curl's irritating interface which makes\n    # up names for almost every single method\n    curl_options = {\n        \"GET\": pycurl.HTTPGET,\n        \"POST\": pycurl.POST,\n        \"PUT\": pycurl.UPLOAD,\n        \"HEAD\": pycurl.NOBODY,\n    }\n    custom_methods = set([\"DELETE\", \"OPTIONS\", \"PATCH\"])\n    for o in curl_options.values():\n        curl.setopt(o, False)\n    if request.method in curl_options:\n        curl.unsetopt(pycurl.CUSTOMREQUEST)\n        curl.setopt(curl_options[request.method], True)\n    elif request.allow_nonstandard_methods or request.method in custom_methods:\n        curl.setopt(pycurl.CUSTOMREQUEST, request.method)\n    else:\n        raise KeyError('unknown method ' + request.method)\n\n    # Handle curl's cryptic options for every individual HTTP method\n    if request.method in (\"POST\", \"PUT\"):\n        if request.body is None:\n            raise AssertionError(\n                'Body must not be empty for \"%s\" request'\n                % request.method)\n\n        request_buffer = BytesIO(utf8(request.body))\n        curl.setopt(pycurl.READFUNCTION, request_buffer.read)\n        if request.method == \"POST\":\n            def ioctl(cmd):\n                if cmd == curl.IOCMD_RESTARTREAD:\n                    request_buffer.seek(0)\n            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)\n            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))\n        else:\n            curl.setopt(pycurl.INFILESIZE, len(request.body))\n    elif request.method == \"GET\":\n        if request.body is not None:\n            raise AssertionError('Body must be empty for GET request')\n\n    if request.auth_username is not None:\n        userpwd = \"%s:%s\" % (request.auth_username, request.auth_password or '')\n\n        if request.auth_mode is None or request.auth_mode == \"basic\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)\n        elif request.auth_mode == \"digest\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)\n        else:\n            raise ValueError(\"Unsupported auth_mode %s\" % request.auth_mode)\n\n        curl.setopt(pycurl.USERPWD, native_str(userpwd))\n        gen_log.debug(\"%s %s (username: %r)\", request.method, request.url,\n                      request.auth_username)\n    else:\n        curl.unsetopt(pycurl.USERPWD)\n        gen_log.debug(\"%s %s\", request.method, request.url)\n\n    if request.client_cert is not None:\n        curl.setopt(pycurl.SSLCERT, request.client_cert)\n\n    if request.client_key is not None:\n        curl.setopt(pycurl.SSLKEY, request.client_key)\n\n    if threading.activeCount() > 1:\n        # libcurl/pycurl is not thread-safe by default.  When multiple threads\n        # are used, signals should be disabled.  This has the side effect\n        # of disabling DNS timeouts in some environments (when libcurl is\n        # not linked against ares), so we don't do it when there is only one\n        # thread.  Applications that use many short-lived threads may need\n        # to set NOSIGNAL manually in a prepare_curl_callback since\n        # there may not be any other threads running at the time we call\n        # threading.activeCount.\n        curl.setopt(pycurl.NOSIGNAL, 1)\n    if request.prepare_curl_callback is not None:\n        request.prepare_curl_callback(curl)\n\n\ndef _curl_header_callback(headers, header_line):\n    # header_line as returned by curl includes the end-of-line characters.\n    header_line = header_line.strip()\n    if header_line.startswith(\"HTTP/\"):\n        headers.clear()\n        return\n    if not header_line:\n        return\n    headers.parse_line(header_line)\n\n\ndef _curl_debug(debug_type, debug_msg):\n    debug_types = ('I', '<', '>', '<', '>')\n    if debug_type == 0:\n        gen_log.debug('%s', debug_msg.strip())\n    elif debug_type in (1, 2):\n        for line in debug_msg.splitlines():\n            gen_log.debug('%s %s', debug_types[debug_type], line)\n    elif debug_type == 4:\n        gen_log.debug('%s %r', debug_types[debug_type], debug_msg)\n\nif __name__ == \"__main__\":\n    AsyncHTTPClient.configure(CurlAsyncHTTPClient)\n    main()\n",
            "header": {
                "file": {
                    "path": "tornado/curl_httpclient.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "cb97710a",
                "new_commit_hash": "0df7a7ee",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/curl_httpclient.py"
            },
            "plus_file": {
                "path": "b/tornado/curl_httpclient.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 318,
                            "length": 10
                        },
                        "new_range": {
                            "start": 318,
                            "length": 12
                        },
                        "section": "def _curl_setup_request(curl, request, buffer, headers):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                    [native_str(\"%s: %s\" % i) for i in request.headers.items()])",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    if request.header_callback:",
                                "type": "context"
                            },
                            {
                                "content": "        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)",
                                "type": "deleted"
                            },
                            {
                                "content": "        curl.setopt(pycurl.HEADERFUNCTION,",
                                "type": "added"
                            },
                            {
                                "content": "                    lambda line: request.header_callback(native_str(line)))",
                                "type": "added"
                            },
                            {
                                "content": "    else:",
                                "type": "context"
                            },
                            {
                                "content": "        curl.setopt(pycurl.HEADERFUNCTION,",
                                "type": "context"
                            },
                            {
                                "content": "                    lambda line: _curl_header_callback(headers, line))",
                                "type": "deleted"
                            },
                            {
                                "content": "                    lambda line: _curl_header_callback(headers,",
                                "type": "added"
                            },
                            {
                                "content": "                                                       native_str(line)))",
                                "type": "added"
                            },
                            {
                                "content": "    if request.streaming_callback:",
                                "type": "context"
                            },
                            {
                                "content": "        write_function = request.streaming_callback",
                                "type": "context"
                            },
                            {
                                "content": "    else:",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/curl_httpclient.py",
                            "type": "function",
                            "name": "_curl_setup_request",
                            "content": "def _curl_setup_request(curl, request, buffer, headers):\n    curl.setopt(pycurl.URL, native_str(request.url))\n\n    # libcurl's magic \"Expect: 100-continue\" behavior causes delays\n    # with servers that don't support it (which include, among others,\n    # Google's OpenID endpoint).  Additionally, this behavior has\n    # a bug in conjunction with the curl_multi_socket_action API\n    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),\n    # which increases the delays.  It's more trouble than it's worth,\n    # so just turn off the feature (yes, setting Expect: to an empty\n    # value is the official way to disable this)\n    if \"Expect\" not in request.headers:\n        request.headers[\"Expect\"] = \"\"\n\n    # libcurl adds Pragma: no-cache by default; disable that too\n    if \"Pragma\" not in request.headers:\n        request.headers[\"Pragma\"] = \"\"\n\n    # Request headers may be either a regular dict or HTTPHeaders object\n    if isinstance(request.headers, httputil.HTTPHeaders):\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.get_all()])\n    else:\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.items()])\n\n    if request.header_callback:\n        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)\n    else:\n        curl.setopt(pycurl.HEADERFUNCTION,\n                    lambda line: _curl_header_callback(headers, line))\n    if request.streaming_callback:\n        write_function = request.streaming_callback\n    else:\n        write_function = buffer.write\n    if bytes_type is str:  # py2\n        curl.setopt(pycurl.WRITEFUNCTION, write_function)\n    else:  # py3\n        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes\n        # a fork/port.  That version has a bug in which it passes unicode\n        # strings instead of bytes to the WRITEFUNCTION.  This means that\n        # if you use a WRITEFUNCTION (which tornado always does), you cannot\n        # download arbitrary binary data.  This needs to be fixed in the\n        # ported pycurl package, but in the meantime this lambda will\n        # make it work for downloading (utf8) text.\n        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))\n    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)\n    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)\n    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))\n    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))\n    if request.user_agent:\n        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))\n    else:\n        curl.setopt(pycurl.USERAGENT, \"Mozilla/5.0 (compatible; pycurl)\")\n    if request.network_interface:\n        curl.setopt(pycurl.INTERFACE, request.network_interface)\n    if request.use_gzip:\n        curl.setopt(pycurl.ENCODING, \"gzip,deflate\")\n    else:\n        curl.setopt(pycurl.ENCODING, \"none\")\n    if request.proxy_host and request.proxy_port:\n        curl.setopt(pycurl.PROXY, request.proxy_host)\n        curl.setopt(pycurl.PROXYPORT, request.proxy_port)\n        if request.proxy_username:\n            credentials = '%s:%s' % (request.proxy_username,\n                                     request.proxy_password)\n            curl.setopt(pycurl.PROXYUSERPWD, credentials)\n    else:\n        curl.setopt(pycurl.PROXY, '')\n        curl.unsetopt(pycurl.PROXYUSERPWD)\n    if request.validate_cert:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 1)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 2)\n    else:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 0)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 0)\n    if request.ca_certs is not None:\n        curl.setopt(pycurl.CAINFO, request.ca_certs)\n    else:\n        # There is no way to restore pycurl.CAINFO to its default value\n        # (Using unsetopt makes it reject all certificates).\n        # I don't see any way to read the default value from python so it\n        # can be restored later.  We'll have to just leave CAINFO untouched\n        # if no ca_certs file was specified, and require that if any\n        # request uses a custom ca_certs file, they all must.\n        pass\n\n    if request.allow_ipv6 is False:\n        # Curl behaves reasonably when DNS resolution gives an ipv6 address\n        # that we can't reach, so allow ipv6 unless the user asks to disable.\n        # (but see version check in _process_queue above)\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n    else:\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)\n\n    # Set the request method through curl's irritating interface which makes\n    # up names for almost every single method\n    curl_options = {\n        \"GET\": pycurl.HTTPGET,\n        \"POST\": pycurl.POST,\n        \"PUT\": pycurl.UPLOAD,\n        \"HEAD\": pycurl.NOBODY,\n    }\n    custom_methods = set([\"DELETE\", \"OPTIONS\", \"PATCH\"])\n    for o in curl_options.values():\n        curl.setopt(o, False)\n    if request.method in curl_options:\n        curl.unsetopt(pycurl.CUSTOMREQUEST)\n        curl.setopt(curl_options[request.method], True)\n    elif request.allow_nonstandard_methods or request.method in custom_methods:\n        curl.setopt(pycurl.CUSTOMREQUEST, request.method)\n    else:\n        raise KeyError('unknown method ' + request.method)\n\n    # Handle curl's cryptic options for every individual HTTP method\n    if request.method in (\"POST\", \"PUT\"):\n        if request.body is None:\n            raise AssertionError(\n                'Body must not be empty for \"%s\" request'\n                % request.method)\n\n        request_buffer = BytesIO(utf8(request.body))\n        curl.setopt(pycurl.READFUNCTION, request_buffer.read)\n        if request.method == \"POST\":\n            def ioctl(cmd):\n                if cmd == curl.IOCMD_RESTARTREAD:\n                    request_buffer.seek(0)\n            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)\n            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))\n        else:\n            curl.setopt(pycurl.INFILESIZE, len(request.body))\n    elif request.method == \"GET\":\n        if request.body is not None:\n            raise AssertionError('Body must be empty for GET request')\n\n    if request.auth_username is not None:\n        userpwd = \"%s:%s\" % (request.auth_username, request.auth_password or '')\n\n        if request.auth_mode is None or request.auth_mode == \"basic\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)\n        elif request.auth_mode == \"digest\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)\n        else:\n            raise ValueError(\"Unsupported auth_mode %s\" % request.auth_mode)\n\n        curl.setopt(pycurl.USERPWD, native_str(userpwd))\n        gen_log.debug(\"%s %s (username: %r)\", request.method, request.url,\n                      request.auth_username)\n    else:\n        curl.unsetopt(pycurl.USERPWD)\n        gen_log.debug(\"%s %s\", request.method, request.url)\n\n    if request.client_cert is not None:\n        curl.setopt(pycurl.SSLCERT, request.client_cert)\n\n    if request.client_key is not None:\n        curl.setopt(pycurl.SSLKEY, request.client_key)\n\n    if threading.activeCount() > 1:\n        # libcurl/pycurl is not thread-safe by default.  When multiple threads\n        # are used, signals should be disabled.  This has the side effect\n        # of disabling DNS timeouts in some environments (when libcurl is\n        # not linked against ares), so we don't do it when there is only one\n        # thread.  Applications that use many short-lived threads may need\n        # to set NOSIGNAL manually in a prepare_curl_callback since\n        # there may not be any other threads running at the time we call\n        # threading.activeCount.\n        curl.setopt(pycurl.NOSIGNAL, 1)\n    if request.prepare_curl_callback is not None:\n        request.prepare_curl_callback(curl)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 294,
                            "end_lineno": 463,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from __future__ import absolute_import, division, print_function, with_statement\n\nfrom hashlib import md5\n\nfrom tornado.httpclient import HTTPRequest\nfrom tornado.stack_context import ExceptionStackContext\nfrom tornado.testing import AsyncHTTPTestCase\nfrom tornado.test import httpclient_test\nfrom tornado.test.util import unittest\nfrom tornado.web import Application, RequestHandler\n\ntry:\n    import pycurl\nexcept ImportError:\n    pycurl = None\n\nif pycurl is not None:\n    from tornado.curl_httpclient import CurlAsyncHTTPClient\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientCommonTestCase(httpclient_test.HTTPClientCommonTestCase):\n    def get_http_client(self):\n        client = CurlAsyncHTTPClient(io_loop=self.io_loop)\n        # make sure AsyncHTTPClient magic doesn't give us the wrong class\n        self.assertTrue(isinstance(client, CurlAsyncHTTPClient))\n        return client\n\n\nclass DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5('%s:%s:%s' % (username, realm, password)).hexdigest()\n            h2 = md5('%s:%s' % (self.request.method,\n                                self.request.path)).hexdigest()\n            digest = md5('%s:%s:%s' % (h1, nonce, h2)).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientTestCase(AsyncHTTPTestCase):\n    def setUp(self):\n        super(CurlHTTPClientTestCase, self).setUp()\n        self.http_client = CurlAsyncHTTPClient(self.io_loop)\n\n    def get_app(self):\n        return Application([\n            ('/digest', DigestAuthHandler),\n        ])\n\n    def test_prepare_curl_callback_stack_context(self):\n        exc_info = []\n\n        def error_handler(typ, value, tb):\n            exc_info.append((typ, value, tb))\n            self.stop()\n            return True\n\n        with ExceptionStackContext(error_handler):\n            request = HTTPRequest(self.get_url('/'),\n                                  prepare_curl_callback=lambda curl: 1 / 0)\n        self.http_client.fetch(request, callback=self.stop)\n        self.wait()\n        self.assertEqual(1, len(exc_info))\n        self.assertIs(exc_info[0][0], ZeroDivisionError)\n\n    def test_digest_auth(self):\n        response = self.fetch('/digest', auth_mode='digest',\n                              auth_username='foo', auth_password='bar')\n        self.assertEqual(response.body, b'ok')\n",
            "new_file_content": "from __future__ import absolute_import, division, print_function, with_statement\n\nfrom hashlib import md5\n\nfrom tornado.escape import utf8\nfrom tornado.httpclient import HTTPRequest\nfrom tornado.stack_context import ExceptionStackContext\nfrom tornado.testing import AsyncHTTPTestCase\nfrom tornado.test import httpclient_test\nfrom tornado.test.util import unittest\nfrom tornado.web import Application, RequestHandler\n\ntry:\n    import pycurl\nexcept ImportError:\n    pycurl = None\n\nif pycurl is not None:\n    from tornado.curl_httpclient import CurlAsyncHTTPClient\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientCommonTestCase(httpclient_test.HTTPClientCommonTestCase):\n    def get_http_client(self):\n        client = CurlAsyncHTTPClient(io_loop=self.io_loop)\n        # make sure AsyncHTTPClient magic doesn't give us the wrong class\n        self.assertTrue(isinstance(client, CurlAsyncHTTPClient))\n        return client\n\n\nclass DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5(utf8('%s:%s:%s' % (username, realm, password))).hexdigest()\n            h2 = md5(utf8('%s:%s' % (self.request.method,\n                                     self.request.path))).hexdigest()\n            digest = md5(utf8('%s:%s:%s' % (h1, nonce, h2))).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientTestCase(AsyncHTTPTestCase):\n    def setUp(self):\n        super(CurlHTTPClientTestCase, self).setUp()\n        self.http_client = CurlAsyncHTTPClient(self.io_loop)\n\n    def get_app(self):\n        return Application([\n            ('/digest', DigestAuthHandler),\n        ])\n\n    def test_prepare_curl_callback_stack_context(self):\n        exc_info = []\n\n        def error_handler(typ, value, tb):\n            exc_info.append((typ, value, tb))\n            self.stop()\n            return True\n\n        with ExceptionStackContext(error_handler):\n            request = HTTPRequest(self.get_url('/'),\n                                  prepare_curl_callback=lambda curl: 1 / 0)\n        self.http_client.fetch(request, callback=self.stop)\n        self.wait()\n        self.assertEqual(1, len(exc_info))\n        self.assertIs(exc_info[0][0], ZeroDivisionError)\n\n    def test_digest_auth(self):\n        response = self.fetch('/digest', auth_mode='digest',\n                              auth_username='foo', auth_password='bar')\n        self.assertEqual(response.body, b'ok')\n",
            "header": {
                "file": {
                    "path": "tornado/test/curl_httpclient_test.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e9ba8a76",
                "new_commit_hash": "faf6cdc7",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/test/curl_httpclient_test.py"
            },
            "plus_file": {
                "path": "b/tornado/test/curl_httpclient_test.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 2,
                            "length": 6
                        },
                        "new_range": {
                            "start": 2,
                            "length": 7
                        },
                        "section": "from __future__ import absolute_import, division, print_function, with_statement"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from hashlib import md5",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from tornado.escape import utf8",
                                "type": "added"
                            },
                            {
                                "content": "from tornado.httpclient import HTTPRequest",
                                "type": "context"
                            },
                            {
                                "content": "from tornado.stack_context import ExceptionStackContext",
                                "type": "context"
                            },
                            {
                                "content": "from tornado.testing import AsyncHTTPTestCase",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tornado/test/curl_httpclient_test.py",
                            "type": "statement",
                            "name": "",
                            "content": "from tornado.escape import utf8",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 5,
                            "end_lineno": 5,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 51,
                            "length": 10
                        },
                        "new_range": {
                            "start": 52,
                            "length": 10
                        },
                        "section": "class DigestAuthHandler(RequestHandler):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            assert param_dict['nonce'] == nonce",
                                "type": "context"
                            },
                            {
                                "content": "            assert param_dict['username'] == username",
                                "type": "context"
                            },
                            {
                                "content": "            assert param_dict['uri'] == self.request.path",
                                "type": "context"
                            },
                            {
                                "content": "            h1 = md5('%s:%s:%s' % (username, realm, password)).hexdigest()",
                                "type": "deleted"
                            },
                            {
                                "content": "            h2 = md5('%s:%s' % (self.request.method,",
                                "type": "deleted"
                            },
                            {
                                "content": "                                self.request.path)).hexdigest()",
                                "type": "deleted"
                            },
                            {
                                "content": "            digest = md5('%s:%s:%s' % (h1, nonce, h2)).hexdigest()",
                                "type": "deleted"
                            },
                            {
                                "content": "            h1 = md5(utf8('%s:%s:%s' % (username, realm, password))).hexdigest()",
                                "type": "added"
                            },
                            {
                                "content": "            h2 = md5(utf8('%s:%s' % (self.request.method,",
                                "type": "added"
                            },
                            {
                                "content": "                                     self.request.path))).hexdigest()",
                                "type": "added"
                            },
                            {
                                "content": "            digest = md5(utf8('%s:%s:%s' % (h1, nonce, h2))).hexdigest()",
                                "type": "added"
                            },
                            {
                                "content": "            if digest == param_dict['response']:",
                                "type": "context"
                            },
                            {
                                "content": "                self.write('ok')",
                                "type": "context"
                            },
                            {
                                "content": "            else:",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/test/curl_httpclient_test.py",
                            "type": "method",
                            "name": "DigestAuthHandler.get",
                            "content": "class DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5('%s:%s:%s' % (username, realm, password)).hexdigest()\n            h2 = md5('%s:%s' % (self.request.method,\n                                self.request.path)).hexdigest()\n            digest = md5('%s:%s:%s' % (h1, nonce, h2)).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 31,
                            "end_lineno": 66,
                            "parent": {
                                "file_name": "tornado/test/curl_httpclient_test.py",
                                "type": "class",
                                "name": "DigestAuthHandler",
                                "content": "class DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5('%s:%s:%s' % (username, realm, password)).hexdigest()\n            h2 = md5('%s:%s' % (self.request.method,\n                                self.request.path)).hexdigest()\n            digest = md5('%s:%s:%s' % (h1, nonce, h2)).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 30,
                                "end_lineno": 66,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "tornado/test/curl_httpclient_test.py",
                            "type": "class",
                            "name": "DigestAuthHandler",
                            "content": "class DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5('%s:%s:%s' % (username, realm, password)).hexdigest()\n            h2 = md5('%s:%s' % (self.request.method,\n                                self.request.path)).hexdigest()\n            digest = md5('%s:%s:%s' % (h1, nonce, h2)).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 30,
                            "end_lineno": 66,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "f5ac210298c9006e08284d5845fe661fe6f9be44^",
    "new_commit_hash": "f5ac210298c9006e08284d5845fe661fe6f9be44",
    "commit_message": "Fixes for soon-to-be-released py3-compatible version of pycurl.",
    "commit_date": "2013-12-29T14:57:29-05:00",
    "metadata": {}
}
{
    "repo_name": "tornado",
    "new_commit_hash": "f5ac210298c9006e08284d5845fe661fe6f9be44",
    "test_file_codes": [
        "from __future__ import absolute_import, division, print_function, with_statement\n\nfrom hashlib import md5\n\nfrom tornado.escape import utf8\nfrom tornado.httpclient import HTTPRequest\nfrom tornado.stack_context import ExceptionStackContext\nfrom tornado.testing import AsyncHTTPTestCase\nfrom tornado.test import httpclient_test\nfrom tornado.test.util import unittest\nfrom tornado.web import Application, RequestHandler\n\ntry:\n    import pycurl\nexcept ImportError:\n    pycurl = None\n\nif pycurl is not None:\n    from tornado.curl_httpclient import CurlAsyncHTTPClient\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientCommonTestCase(httpclient_test.HTTPClientCommonTestCase):\n    def get_http_client(self):\n        client = CurlAsyncHTTPClient(io_loop=self.io_loop)\n        # make sure AsyncHTTPClient magic doesn't give us the wrong class\n        self.assertTrue(isinstance(client, CurlAsyncHTTPClient))\n        return client\n\n\nclass DigestAuthHandler(RequestHandler):\n    def get(self):\n        realm = 'test'\n        opaque = 'asdf'\n        # Real implementations would use a random nonce.\n        nonce = \"1234\"\n        username = 'foo'\n        password = 'bar'\n\n        auth_header = self.request.headers.get('Authorization', None)\n        if auth_header is not None:\n            auth_mode, params = auth_header.split(' ', 1)\n            assert auth_mode == 'Digest'\n            param_dict = {}\n            for pair in params.split(','):\n                k, v = pair.strip().split('=', 1)\n                if v[0] == '\"' and v[-1] == '\"':\n                    v = v[1:-1]\n                param_dict[k] = v\n            assert param_dict['realm'] == realm\n            assert param_dict['opaque'] == opaque\n            assert param_dict['nonce'] == nonce\n            assert param_dict['username'] == username\n            assert param_dict['uri'] == self.request.path\n            h1 = md5(utf8('%s:%s:%s' % (username, realm, password))).hexdigest()\n            h2 = md5(utf8('%s:%s' % (self.request.method,\n                                     self.request.path))).hexdigest()\n            digest = md5(utf8('%s:%s:%s' % (h1, nonce, h2))).hexdigest()\n            if digest == param_dict['response']:\n                self.write('ok')\n            else:\n                self.write('fail')\n        else:\n            self.set_status(401)\n            self.set_header('WWW-Authenticate',\n                            'Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"' %\n                            (realm, nonce, opaque))\n\n\n@unittest.skipIf(pycurl is None, \"pycurl module not present\")\nclass CurlHTTPClientTestCase(AsyncHTTPTestCase):\n    def setUp(self):\n        super(CurlHTTPClientTestCase, self).setUp()\n        self.http_client = CurlAsyncHTTPClient(self.io_loop)\n\n    def get_app(self):\n        return Application([\n            ('/digest', DigestAuthHandler),\n        ])\n\n    def test_prepare_curl_callback_stack_context(self):\n        exc_info = []\n\n        def error_handler(typ, value, tb):\n            exc_info.append((typ, value, tb))\n            self.stop()\n            return True\n\n        with ExceptionStackContext(error_handler):\n            request = HTTPRequest(self.get_url('/'),\n                                  prepare_curl_callback=lambda curl: 1 / 0)\n        self.http_client.fetch(request, callback=self.stop)\n        self.wait()\n        self.assertEqual(1, len(exc_info))\n        self.assertIs(exc_info[0][0], ZeroDivisionError)\n\n    def test_digest_auth(self):\n        response = self.fetch('/digest', auth_mode='digest',\n                              auth_username='foo', auth_password='bar')\n        self.assertEqual(response.body, b'ok')\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n    def tearDown(self):\n        # Reset signal handlers\n        from twisted.internet import reactor\n\n        reactor.removeAll()\n        reactor.stop()\n        super().tearDown()\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\".\", pattern=\"test_*.py\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "tornado_unittest_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 551ms\nPrepared 1 package in 940ms\nInstalled 1 package in 0.81ms\n + tornado==3.2.dev2 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44)\nResolved 29 packages in 16ms\nInstalled 29 packages in 101ms\n + attrs==24.3.0\n + automat==24.8.1\n + cachetools==5.5.0\n + cffi==1.17.1\n + chardet==5.2.0\n + colorama==0.4.6\n + constantly==23.10.4\n + distlib==0.3.9\n + exceptiongroup==1.2.2\n + filelock==3.16.1\n + hyperlink==21.0.0\n + idna==3.10\n + incremental==24.7.2\n + iniconfig==2.0.0\n + packaging==24.2\n + platformdirs==4.3.6\n + pluggy==1.5.0\n + pycares==4.5.0\n + pycparser==2.22\n + pycurl==7.45.4\n + pyproject-api==1.8.0\n + pytest==8.3.4\n + setuptools==75.6.0\n + tomli==2.2.1\n + tox==4.23.2\n + twisted==24.11.0\n + typing-extensions==4.12.2\n + virtualenv==20.28.0\n + zope-interface==7.2\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44\n.........................\ncollected 25 items\n\n==================== short test summary info ====================\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_304_with_content_length\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_all_methods\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_basic_auth\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_basic_auth_explicit_mode\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_body\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_body_encoding\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_chunked\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_chunked_close\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_configure_defaults\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_credentials_in_url\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_final_callback_stack_context\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_follow_redirect\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_future_http_error\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_future_interface\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_header_callback\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_header_callback_stack_context\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_hello_world\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_post\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_reuse_request_from_response\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_streaming_callback\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_streaming_stack_context\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_types\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_unsupported_auth_mode\nPASSED r2e_tests.test_1::CurlHTTPClientTestCase::test_digest_auth\nPASSED r2e_tests.test_1::CurlHTTPClientTestCase::test_prepare_curl_callback_stack_context\n=================== 25 passed in 0.04s ===================\n",
    "new_commit_res_stderr": "WARNING:tornado.access:405 HEAD /all_methods (127.0.0.1) 0.20ms\nWARNING:tornado.access:404 GET /notfound (127.0.0.1) 0.31ms\nWARNING:tornado.access:401 GET /digest (127.0.0.1) 0.17ms\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44\n.........................\ncollected 25 items\n\n=================================== FAILURES ===================================\n__________ CurlHTTPClientCommonTestCase.test_304_with_content_length __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 328, in test_304_with_content_length\n    self.assertEqual(response.code, 304)\nAssertionError: 599 != 304\n__________ CurlHTTPClientCommonTestCase.test_all_methods __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 381, in test_all_methods\n    self.assertEqual(response.body, utf8(method))\nAssertionError: None != b'GET'\n__________ CurlHTTPClientCommonTestCase.test_basic_auth __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 201, in test_basic_auth\n    self.assertEqual(self.fetch(\"/auth\", auth_username=\"Aladdin\",\nAssertionError: None != b'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='\n__________ CurlHTTPClientCommonTestCase.test_basic_auth_explicit_mode __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 206, in test_basic_auth_explicit_mode\n    self.assertEqual(self.fetch(\"/auth\", auth_username=\"Aladdin\",\nAssertionError: None != b'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='\n__________ CurlHTTPClientCommonTestCase.test_chunked __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 141, in test_chunked\n    self.assertEqual(response.body, b\"asdfqwer\")\nAssertionError: None != b'asdfqwer'\n__________ CurlHTTPClientCommonTestCase.test_configure_defaults __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 319, in test_configure_defaults\n    self.assertEqual(response.body, b'TestDefaultUserAgent')\nAssertionError: None != b'TestDefaultUserAgent'\n__________ CurlHTTPClientCommonTestCase.test_credentials_in_url __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 236, in test_credentials_in_url\n    self.assertEqual(b\"Basic \" + base64.b64encode(b\"me:secret\"),\nAssertionError: b'Basic bWU6c2VjcmV0' != None\n__________ CurlHTTPClientCommonTestCase.test_follow_redirect __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 224, in test_follow_redirect\n    self.assertEqual(302, response.code)\nAssertionError: 302 != 599\n__________ CurlHTTPClientCommonTestCase.test_future_http_error __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 360, in test_future_http_error\n    yield self.http_client.fetch(self.get_url('/notfound'))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 520, in run\n    next = self.yield_point.get_result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 410, in get_result\n    return self.runner.pop_result(self.key).result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 131, in result\n    return super(TracebackFuture, self).result(timeout=timeout)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 439, in result\n    return self.__get_result()\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 391, in __get_result\n    raise self._exception\ntornado.curl_httpclient.CurlError: HTTP 599: Failure writing output to destination, passed 24 returned 0\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 426, in wrapper\n    return self.io_loop.run_sync(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/ioloop.py\", line 373, in run_sync\n    return future_cell[0].result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 129, in result\n    raise_exc_info(self.__exc_info)\n  File \"<string>\", line 3, in raise_exc_info\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/stack_context.py\", line 302, in wrapped\n    ret = fn(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 574, in inner\n    self.set_result(key, result)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 500, in set_result\n    self.run()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 529, in run\n    yielded = self.gen.throw(*exc_info)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 362, in test_future_http_error\n    self.assertEqual(e.code, 404)\nAssertionError: 599 != 404\n__________ CurlHTTPClientCommonTestCase.test_header_callback_stack_context __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 309, in test_header_callback_stack_context\n    self.assertEqual(len(exc_info), 1)\nAssertionError: 8 != 1\n__________ CurlHTTPClientCommonTestCase.test_hello_world __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 116, in test_hello_world\n    self.assertEqual(response.code, 200)\nAssertionError: 599 != 200\n__________ CurlHTTPClientCommonTestCase.test_post __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 136, in test_post\n    self.assertEqual(response.code, 200)\nAssertionError: 599 != 200\n__________ CurlHTTPClientCommonTestCase.test_streaming_callback __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 130, in test_streaming_callback\n    self.assertEqual(chunks, [b\"Hello world!\"])\nAssertionError: Lists differ: [] != [b'Hello world!']\n\nSecond list contains 1 additional elements.\nFirst extra element 0:\nb'Hello world!'\n\n- []\n+ [b'Hello world!']\n__________ CurlHTTPClientCommonTestCase.test_streaming_stack_context __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 196, in test_streaming_stack_context\n    self.assertEqual(chunks, [b'asdf', b'qwer'])\nAssertionError: Lists differ: [] != [b'asdf', b'qwer']\n\nSecond list contains 2 additional elements.\nFirst extra element 0:\nb'asdf'\n\n- []\n+ [b'asdf', b'qwer']\n__________ CurlHTTPClientCommonTestCase.test_types __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 266, in test_types\n    self.assertEqual(type(response.body), bytes_type)\nAssertionError: <class 'NoneType'> != <class 'bytes'>\n__________ CurlHTTPClientTestCase.test_digest_auth __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/r2e_tests/test_1.py\", line 100, in test_digest_auth\n    self.assertEqual(response.body, b'ok')\nAssertionError: None != b'ok'\n\n=================================== ERRORS =====================================\n__________ CurlHTTPClientCommonTestCase.test_body_encoding __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 246, in test_body_encoding\n    self.assertEqual(response.headers[\"Content-Length\"], \"2\")\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/httputil.py\", line 191, in __getitem__\n    return dict.__getitem__(self, _normalized_headers[name])\nKeyError: 'Content-Length'\n__________ CurlHTTPClientCommonTestCase.test_chunked_close __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 176, in test_chunked_close\n    resp.rethrow()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/httpclient.py\", line 490, in rethrow\n    raise self.error\ntornado.curl_httpclient.CurlError: HTTP 599: Failure writing output to destination, passed 17 returned 0\n__________ CurlHTTPClientCommonTestCase.test_future_interface __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 426, in wrapper\n    return self.io_loop.run_sync(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/ioloop.py\", line 373, in run_sync\n    return future_cell[0].result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 129, in result\n    raise_exc_info(self.__exc_info)\n  File \"<string>\", line 3, in raise_exc_info\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/stack_context.py\", line 302, in wrapped\n    ret = fn(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 574, in inner\n    self.set_result(key, result)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 500, in set_result\n    self.run()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 529, in run\n    yielded = self.gen.throw(*exc_info)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 354, in test_future_interface\n    response = yield self.http_client.fetch(self.get_url('/hello'))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 520, in run\n    next = self.yield_point.get_result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 410, in get_result\n    return self.runner.pop_result(self.key).result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 131, in result\n    return super(TracebackFuture, self).result(timeout=timeout)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 439, in result\n    return self.__get_result()\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 391, in __get_result\n    raise self._exception\ntornado.curl_httpclient.CurlError: HTTP 599: Failure writing output to destination, passed 17 returned 0\n__________ CurlHTTPClientCommonTestCase.test_header_callback __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 290, in test_header_callback\n    self.fetch('/chunk', header_callback=header_callback,\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 330, in fetch\n    return self.wait()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 269, in wait\n    self.__rethrow()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 205, in __rethrow\n    raise_exc_info(failure)\n  File \"<string>\", line 3, in raise_exc_info\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/stack_context.py\", line 302, in wrapped\n    ret = fn(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 287, in streaming_callback\n    self.assertEqual(headers['Content-Type'], 'text/html; charset=UTF-8')\nKeyError: 'Content-Type'\n__________ CurlHTTPClientCommonTestCase.test_reuse_request_from_response __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/testing.py\", line 426, in wrapper\n    return self.io_loop.run_sync(\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/ioloop.py\", line 373, in run_sync\n    return future_cell[0].result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 129, in result\n    raise_exc_info(self.__exc_info)\n  File \"<string>\", line 3, in raise_exc_info\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/stack_context.py\", line 302, in wrapped\n    ret = fn(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 574, in inner\n    self.set_result(key, result)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 500, in set_result\n    self.run()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 529, in run\n    yielded = self.gen.throw(*exc_info)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/test/httpclient_test.py\", line 372, in test_reuse_request_from_response\n    response = yield self.http_client.fetch(url)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 520, in run\n    next = self.yield_point.get_result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/gen.py\", line 410, in get_result\n    return self.runner.pop_result(self.key).result()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/concurrent.py\", line 131, in result\n    return super(TracebackFuture, self).result(timeout=timeout)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 439, in result\n    return self.__get_result()\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/_base.py\", line 391, in __get_result\n    raise self._exception\ntornado.curl_httpclient.CurlError: HTTP 599: Failure writing output to destination, passed 17 returned 0\n\n==================== short test summary info ====================\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_304_with_content_length\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_all_methods\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_basic_auth\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_basic_auth_explicit_mode\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_body\nERROR r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_body_encoding\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_chunked\nERROR r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_chunked_close\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_configure_defaults\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_credentials_in_url\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_final_callback_stack_context\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_follow_redirect\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_future_http_error\nERROR r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_future_interface\nERROR r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_header_callback\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_header_callback_stack_context\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_hello_world\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_post\nERROR r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_reuse_request_from_response\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_streaming_callback\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_streaming_stack_context\nFAILED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_types\nPASSED r2e_tests.test_1::CurlHTTPClientCommonTestCase::test_unsupported_auth_mode\nFAILED r2e_tests.test_1::CurlHTTPClientTestCase::test_digest_auth\nPASSED r2e_tests.test_1::CurlHTTPClientTestCase::test_prepare_curl_callback_stack_context\n=================== 16 failed, 5 errors, 4 passed in 0.04s ===================\n",
    "old_commit_res_stderr": "Traceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nWARNING:tornado.access:404 GET /notfound (127.0.0.1) 0.22ms\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\nWARNING:tornado.access:401 GET /digest (127.0.0.1) 0.15ms\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 324, in <lambda>\n    lambda line: _curl_header_callback(headers, line))\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_f5ac210298c9006e08284d5845fe661fe6f9be44/tornado/curl_httpclient.py\", line 469, in _curl_header_callback\n    if header_line.startswith(\"HTTP/\"):\nTypeError: startswith first arg must be bytes or a tuple of bytes, not str\n"
}
tornado/curl_httpclient.py tornado/test/curl_httpclient_test.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Header callbacks receive bytes instead of strings, causing encoding issues

**Description:**
When making HTTP requests using the CurlHTTPClient, the header callbacks receive header lines as bytes rather than decoded strings. This leads to encoding mismatches, causing various functionality tests to fail unexpectedly.

**Example Buggy Code:**
```python
def make_request():
    headers = {}
    def header_callback(line):
        headers['Content-Type'] = line
    request = HTTPRequest(url="http://example.com/auth", header_callback=header_callback)
    client = CurlHTTPClient()
    client.fetch(request)
    # Further processing expects headers['Content-Type'] to be a string
```

**Expected Behavior:**
Header callbacks should receive header lines as decoded strings to ensure proper encoding and seamless processing of header information.

**Actual Behavior:**
Header callbacks receive header lines as bytes, leading to encoding issues and causing tests that rely on string-based header values to fail.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/curl_httpclient.py
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""Non-blocking HTTP client implementation using pycurl."""

from __future__ import absolute_import, division, print_function, with_statement

import collections
import logging
import pycurl
import threading
import time

from tornado import httputil
from tornado import ioloop
from tornado.log import gen_log
from tornado import stack_context

from tornado.escape import utf8, native_str
from tornado.httpclient import HTTPResponse, HTTPError, AsyncHTTPClient, main
from tornado.util import bytes_type

try:
    from io import BytesIO  # py3
except ImportError:
    from cStringIO import StringIO as BytesIO  # py2


class CurlAsyncHTTPClient(AsyncHTTPClient):
    def initialize(self, io_loop, max_clients=10, defaults=None):
        super(CurlAsyncHTTPClient, self).initialize(io_loop, defaults=defaults)
        self._multi = pycurl.CurlMulti()
        self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout)
        self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket)
        self._curls = [_curl_create() for i in range(max_clients)]
        self._free_list = self._curls[:]
        self._requests = collections.deque()
        self._fds = {}
        self._timeout = None

        try:
            self._socket_action = self._multi.socket_action
        except AttributeError:
            # socket_action is found in pycurl since 7.18.2 (it's been
            # in libcurl longer than that but wasn't accessible to
            # python).
            gen_log.warning("socket_action method missing from pycurl; "
                            "falling back to socket_all. Upgrading "
                            "libcurl and pycurl will improve performance")
            self._socket_action = \
                lambda fd, action: self._multi.socket_all()

        # libcurl has bugs that sometimes cause it to not report all
        # relevant file descriptors and timeouts to TIMERFUNCTION/
        # SOCKETFUNCTION.  Mitigate the effects of such bugs by
        # forcing a periodic scan of all active requests.
        self._force_timeout_callback = ioloop.PeriodicCallback(
            self._handle_force_timeout, 1000, io_loop=io_loop)
        self._force_timeout_callback.start()

        # Work around a bug in libcurl 7.29.0: Some fields in the curl
        # multi object are initialized lazily, and its destructor will
        # segfault if it is destroyed without having been used.  Add
        # and remove a dummy handle to make sure everything is
        # initialized.
        dummy_curl_handle = pycurl.Curl()
        self._multi.add_handle(dummy_curl_handle)
        self._multi.remove_handle(dummy_curl_handle)

    def close(self):
        self._force_timeout_callback.stop()
        if self._timeout is not None:
            self.io_loop.remove_timeout(self._timeout)
        for curl in self._curls:
            curl.close()
        self._multi.close()
        self._closed = True
        super(CurlAsyncHTTPClient, self).close()

    def fetch_impl(self, request, callback):
        self._requests.append((request, callback))
        self._process_queue()
        self._set_timeout(0)

    def _handle_socket(self, event, fd, multi, data):
        """Called by libcurl when it wants to change the file descriptors
        it cares about.
        """
        event_map = {
            pycurl.POLL_NONE: ioloop.IOLoop.NONE,
            pycurl.POLL_IN: ioloop.IOLoop.READ,
            pycurl.POLL_OUT: ioloop.IOLoop.WRITE,
            pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE
        }
        if event == pycurl.POLL_REMOVE:
            if fd in self._fds:
                self.io_loop.remove_handler(fd)
                del self._fds[fd]
        else:
            ioloop_event = event_map[event]
            # libcurl sometimes closes a socket and then opens a new
            # one using the same FD without giving us a POLL_NONE in
            # between.  This is a problem with the epoll IOLoop,
            # because the kernel can tell when a socket is closed and
            # removes it from the epoll automatically, causing future
            # update_handler calls to fail.  Since we can't tell when
            # this has happened, always use remove and re-add
            # instead of update.
            if fd in self._fds:
                self.io_loop.remove_handler(fd)
            self.io_loop.add_handler(fd, self._handle_events,
                                     ioloop_event)
            self._fds[fd] = ioloop_event

    def _set_timeout(self, msecs):
        """Called by libcurl to schedule a timeout."""
        if self._timeout is not None:
            self.io_loop.remove_timeout(self._timeout)
        self._timeout = self.io_loop.add_timeout(
            self.io_loop.time() + msecs / 1000.0, self._handle_timeout)

    def _handle_events(self, fd, events):
        """Called by IOLoop when there is activity on one of our
        file descriptors.
        """
        action = 0
        if events & ioloop.IOLoop.READ:
            action |= pycurl.CSELECT_IN
        if events & ioloop.IOLoop.WRITE:
            action |= pycurl.CSELECT_OUT
        while True:
            try:
                ret, num_handles = self._socket_action(fd, action)
            except pycurl.error as e:
                ret = e.args[0]
            if ret != pycurl.E_CALL_MULTI_PERFORM:
                break
        self._finish_pending_requests()

    def _handle_timeout(self):
        """Called by IOLoop when the requested timeout has passed."""
        with stack_context.NullContext():
            self._timeout = None
            while True:
                try:
                    ret, num_handles = self._socket_action(
                        pycurl.SOCKET_TIMEOUT, 0)
                except pycurl.error as e:
                    ret = e.args[0]
                if ret != pycurl.E_CALL_MULTI_PERFORM:
                    break
            self._finish_pending_requests()

        # In theory, we shouldn't have to do this because curl will
        # call _set_timeout whenever the timeout changes.  However,
        # sometimes after _handle_timeout we will need to reschedule
        # immediately even though nothing has changed from curl's
        # perspective.  This is because when socket_action is
        # called with SOCKET_TIMEOUT, libcurl decides internally which
        # timeouts need to be processed by using a monotonic clock
        # (where available) while tornado uses python's time.time()
        # to decide when timeouts have occurred.  When those clocks
        # disagree on elapsed time (as they will whenever there is an
        # NTP adjustment), tornado might call _handle_timeout before
        # libcurl is ready.  After each timeout, resync the scheduled
        # timeout with libcurl's current state.
        new_timeout = self._multi.timeout()
        if new_timeout >= 0:
            self._set_timeout(new_timeout)

    def _handle_force_timeout(self):
        """Called by IOLoop periodically to ask libcurl to process any
        events it may have forgotten about.
        """
        with stack_context.NullContext():
            while True:
                try:
                    ret, num_handles = self._multi.socket_all()
                except pycurl.error as e:
                    ret = e.args[0]
                if ret != pycurl.E_CALL_MULTI_PERFORM:
                    break
            self._finish_pending_requests()

    def _finish_pending_requests(self):
        """Process any requests that were completed by the last
        call to multi.socket_action.
        """
        while True:
            num_q, ok_list, err_list = self._multi.info_read()
            for curl in ok_list:
                self._finish(curl)
            for curl, errnum, errmsg in err_list:
                self._finish(curl, errnum, errmsg)
            if num_q == 0:
                break
        self._process_queue()

    def _process_queue(self):
        with stack_context.NullContext():
            while True:
                started = 0
                while self._free_list and self._requests:
                    started += 1
                    curl = self._free_list.pop()
                    (request, callback) = self._requests.popleft()
                    curl.info = {
                        "headers": httputil.HTTPHeaders(),
                        "buffer": BytesIO(),
                        "request": request,
                        "callback": callback,
                        "curl_start_time": time.time(),
                    }
                    # Disable IPv6 to mitigate the effects of this bug
                    # on curl versions <= 7.21.0
                    # http://sourceforge.net/tracker/?func=detail&aid=3017819&group_id=976&atid=100976
                    if pycurl.version_info()[2] <= 0x71500:  # 7.21.0
                        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
                    _curl_setup_request(curl, request, curl.info["buffer"],
                                        curl.info["headers"])
                    self._multi.add_handle(curl)

                if not started:
                    break

    def _finish(self, curl, curl_error=None, curl_message=None):
        info = curl.info
        curl.info = None
        self._multi.remove_handle(curl)
        self._free_list.append(curl)
        buffer = info["buffer"]
        if curl_error:
            error = CurlError(curl_error, curl_message)
            code = error.code
            effective_url = None
            buffer.close()
            buffer = None
        else:
            error = None
            code = curl.getinfo(pycurl.HTTP_CODE)
            effective_url = curl.getinfo(pycurl.EFFECTIVE_URL)
            buffer.seek(0)
        # the various curl timings are documented at
        # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
        time_info = dict(
            queue=info["curl_start_time"] - info["request"].start_time,
            namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME),
            connect=curl.getinfo(pycurl.CONNECT_TIME),
            pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME),
            starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME),
            total=curl.getinfo(pycurl.TOTAL_TIME),
            redirect=curl.getinfo(pycurl.REDIRECT_TIME),
        )
        try:
            info["callback"](HTTPResponse(
                request=info["request"], code=code, headers=info["headers"],
                buffer=buffer, effective_url=effective_url, error=error,
                request_time=time.time() - info["curl_start_time"],
                time_info=time_info))
        except Exception:
            self.handle_callback_exception(info["callback"])

    def handle_callback_exception(self, callback):
        self.io_loop.handle_callback_exception(callback)


class CurlError(HTTPError):
    def __init__(self, errno, message):
        HTTPError.__init__(self, 599, message)
        self.errno = errno


def _curl_create():
    curl = pycurl.Curl()
    if gen_log.isEnabledFor(logging.DEBUG):
        curl.setopt(pycurl.VERBOSE, 1)
        curl.setopt(pycurl.DEBUGFUNCTION, _curl_debug)
    return curl


def _curl_setup_request(curl, request, buffer, headers):
    curl.setopt(pycurl.URL, native_str(request.url))

    # libcurl's magic "Expect: 100-continue" behavior causes delays
    # with servers that don't support it (which include, among others,
    # Google's OpenID endpoint).  Additionally, this behavior has
    # a bug in conjunction with the curl_multi_socket_action API
    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),
    # which increases the delays.  It's more trouble than it's worth,
    # so just turn off the feature (yes, setting Expect: to an empty
    # value is the official way to disable this)
    if "Expect" not in request.headers:
        request.headers["Expect"] = ""

    # libcurl adds Pragma: no-cache by default; disable that too
    if "Pragma" not in request.headers:
        request.headers["Pragma"] = ""

    # Request headers may be either a regular dict or HTTPHeaders object
    if isinstance(request.headers, httputil.HTTPHeaders):
        curl.setopt(pycurl.HTTPHEADER,
                    [native_str("%s: %s" % i) for i in request.headers.get_all()])
    else:
        curl.setopt(pycurl.HTTPHEADER,
                    [native_str("%s: %s" % i) for i in request.headers.items()])

    if request.header_callback:
        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)
    else:
        curl.setopt(pycurl.HEADERFUNCTION,
                    lambda line: _curl_header_callback(headers, line))
    if request.streaming_callback:
        write_function = request.streaming_callback
    else:
        write_function = buffer.write
    if bytes_type is str:  # py2
        curl.setopt(pycurl.WRITEFUNCTION, write_function)
    else:  # py3
        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes
        # a fork/port.  That version has a bug in which it passes unicode
        # strings instead of bytes to the WRITEFUNCTION.  This means that
        # if you use a WRITEFUNCTION (which tornado always does), you cannot
        # download arbitrary binary data.  This needs to be fixed in the
        # ported pycurl package, but in the meantime this lambda will
        # make it work for downloading (utf8) text.
        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))
    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)
    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)
    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))
    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))
    if request.user_agent:
        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))
    else:
        curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)")
    if request.network_interface:
        curl.setopt(pycurl.INTERFACE, request.network_interface)
    if request.use_gzip:
        curl.setopt(pycurl.ENCODING, "gzip,deflate")
    else:
        curl.setopt(pycurl.ENCODING, "none")
    if request.proxy_host and request.proxy_port:
        curl.setopt(pycurl.PROXY, request.proxy_host)
        curl.setopt(pycurl.PROXYPORT, request.proxy_port)
        if request.proxy_username:
            credentials = '%s:%s' % (request.proxy_username,
                                     request.proxy_password)
            curl.setopt(pycurl.PROXYUSERPWD, credentials)
    else:
        curl.setopt(pycurl.PROXY, '')
        curl.unsetopt(pycurl.PROXYUSERPWD)
    if request.validate_cert:
        curl.setopt(pycurl.SSL_VERIFYPEER, 1)
        curl.setopt(pycurl.SSL_VERIFYHOST, 2)
    else:
        curl.setopt(pycurl.SSL_VERIFYPEER, 0)
        curl.setopt(pycurl.SSL_VERIFYHOST, 0)
    if request.ca_certs is not None:
        curl.setopt(pycurl.CAINFO, request.ca_certs)
    else:
        # There is no way to restore pycurl.CAINFO to its default value
        # (Using unsetopt makes it reject all certificates).
        # I don't see any way to read the default value from python so it
        # can be restored later.  We'll have to just leave CAINFO untouched
        # if no ca_certs file was specified, and require that if any
        # request uses a custom ca_certs file, they all must.
        pass

    if request.allow_ipv6 is False:
        # Curl behaves reasonably when DNS resolution gives an ipv6 address
        # that we can't reach, so allow ipv6 unless the user asks to disable.
        # (but see version check in _process_queue above)
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
    else:
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)

    # Set the request method through curl's irritating interface which makes
    # up names for almost every single method
    curl_options = {
        "GET": pycurl.HTTPGET,
        "POST": pycurl.POST,
        "PUT": pycurl.UPLOAD,
        "HEAD": pycurl.NOBODY,
    }
    custom_methods = set(["DELETE", "OPTIONS", "PATCH"])
    for o in curl_options.values():
        curl.setopt(o, False)
    if request.method in curl_options:
        curl.unsetopt(pycurl.CUSTOMREQUEST)
        curl.setopt(curl_options[request.method], True)
    elif request.allow_nonstandard_methods or request.method in custom_methods:
        curl.setopt(pycurl.CUSTOMREQUEST, request.method)
    else:
        raise KeyError('unknown method ' + request.method)

    # Handle curl's cryptic options for every individual HTTP method
    if request.method in ("POST", "PUT"):
        if request.body is None:
            raise AssertionError(
                'Body must not be empty for "%s" request'
                % request.method)

        request_buffer = BytesIO(utf8(request.body))
        curl.setopt(pycurl.READFUNCTION, request_buffer.read)
        if request.method == "POST":
            def ioctl(cmd):
                if cmd == curl.IOCMD_RESTARTREAD:
                    request_buffer.seek(0)
            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)
            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))
        else:
            curl.setopt(pycurl.INFILESIZE, len(request.body))
    elif request.method == "GET":
        if request.body is not None:
            raise AssertionError('Body must be empty for GET request')

    if request.auth_username is not None:
        userpwd = "%s:%s" % (request.auth_username, request.auth_password or '')

        if request.auth_mode is None or request.auth_mode == "basic":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
        elif request.auth_mode == "digest":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
        else:
            raise ValueError("Unsupported auth_mode %s" % request.auth_mode)

        curl.setopt(pycurl.USERPWD, native_str(userpwd))
        gen_log.debug("%s %s (username: %r)", request.method, request.url,
                      request.auth_username)
    else:
        curl.unsetopt(pycurl.USERPWD)
        gen_log.debug("%s %s", request.method, request.url)

    if request.client_cert is not None:
        curl.setopt(pycurl.SSLCERT, request.client_cert)

    if request.client_key is not None:
        curl.setopt(pycurl.SSLKEY, request.client_key)

    if threading.activeCount() > 1:
        # libcurl/pycurl is not thread-safe by default.  When multiple threads
        # are used, signals should be disabled.  This has the side effect
        # of disabling DNS timeouts in some environments (when libcurl is
        # not linked against ares), so we don't do it when there is only one
        # thread.  Applications that use many short-lived threads may need
        # to set NOSIGNAL manually in a prepare_curl_callback since
        # there may not be any other threads running at the time we call
        # threading.activeCount.
        curl.setopt(pycurl.NOSIGNAL, 1)
    if request.prepare_curl_callback is not None:
        request.prepare_curl_callback(curl)


def _curl_header_callback(headers, header_line):
    # header_line as returned by curl includes the end-of-line characters.
    header_line = header_line.strip()
    if header_line.startswith("HTTP/"):
        headers.clear()
        return
    if not header_line:
        return
    headers.parse_line(header_line)


def _curl_debug(debug_type, debug_msg):
    debug_types = ('I', '<', '>', '<', '>')
    if debug_type == 0:
        gen_log.debug('%s', debug_msg.strip())
    elif debug_type in (1, 2):
        for line in debug_msg.splitlines():
            gen_log.debug('%s %s', debug_types[debug_type], line)
    elif debug_type == 4:
        gen_log.debug('%s %r', debug_types[debug_type], debug_msg)

if __name__ == "__main__":
    AsyncHTTPClient.configure(CurlAsyncHTTPClient)
    main()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Header callbacks receive bytes instead of strings, causing encoding issues

**Description:**
When making HTTP requests using the CurlHTTPClient, the header callbacks receive header lines as bytes rather than decoded strings. This leads to encoding mismatches, causing various functionality tests to fail unexpectedly.

**Example Buggy Code:**
```python
def make_request():
    headers = {}
    def header_callback(line):
        headers['Content-Type'] = line
    request = HTTPRequest(url="http://example.com/auth", header_callback=header_callback)
    client = CurlHTTPClient()
    client.fetch(request)
    # Further processing expects headers['Content-Type'] to be a string
```

**Expected Behavior:**
Header callbacks should receive header lines as decoded strings to ensure proper encoding and seamless processing of header information.

**Actual Behavior:**
Header callbacks receive header lines as bytes, leading to encoding issues and causing tests that rely on string-based header values to fail.

[/ISSUE]
{
    "CurlHTTPClientCommonTestCase.test_304_with_content_length": "PASSED",
    "CurlHTTPClientCommonTestCase.test_all_methods": "PASSED",
    "CurlHTTPClientCommonTestCase.test_basic_auth": "PASSED",
    "CurlHTTPClientCommonTestCase.test_basic_auth_explicit_mode": "PASSED",
    "CurlHTTPClientCommonTestCase.test_body": "PASSED",
    "CurlHTTPClientCommonTestCase.test_body_encoding": "PASSED",
    "CurlHTTPClientCommonTestCase.test_chunked": "PASSED",
    "CurlHTTPClientCommonTestCase.test_chunked_close": "PASSED",
    "CurlHTTPClientCommonTestCase.test_configure_defaults": "PASSED",
    "CurlHTTPClientCommonTestCase.test_credentials_in_url": "PASSED",
    "CurlHTTPClientCommonTestCase.test_final_callback_stack_context": "PASSED",
    "CurlHTTPClientCommonTestCase.test_follow_redirect": "PASSED",
    "CurlHTTPClientCommonTestCase.test_future_http_error": "PASSED",
    "CurlHTTPClientCommonTestCase.test_future_interface": "PASSED",
    "CurlHTTPClientCommonTestCase.test_header_callback": "PASSED",
    "CurlHTTPClientCommonTestCase.test_header_callback_stack_context": "PASSED",
    "CurlHTTPClientCommonTestCase.test_hello_world": "PASSED",
    "CurlHTTPClientCommonTestCase.test_post": "PASSED",
    "CurlHTTPClientCommonTestCase.test_reuse_request_from_response": "PASSED",
    "CurlHTTPClientCommonTestCase.test_streaming_callback": "PASSED",
    "CurlHTTPClientCommonTestCase.test_streaming_stack_context": "PASSED",
    "CurlHTTPClientCommonTestCase.test_types": "PASSED",
    "CurlHTTPClientCommonTestCase.test_unsupported_auth_mode": "PASSED",
    "CurlHTTPClientTestCase.test_digest_auth": "PASSED",
    "CurlHTTPClientTestCase.test_prepare_curl_callback_stack_context": "PASSED"
}
diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py
index cb97710a..0df7a7ee 100644
--- a/tornado/curl_httpclient.py
+++ b/tornado/curl_httpclient.py
@@ -318,10 +318,12 @@ def _curl_setup_request(curl, request, buffer, headers):
                     [native_str("%s: %s" % i) for i in request.headers.items()])
 
     if request.header_callback:
-        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)
+        curl.setopt(pycurl.HEADERFUNCTION,
+                    lambda line: request.header_callback(native_str(line)))
     else:
         curl.setopt(pycurl.HEADERFUNCTION,
-                    lambda line: _curl_header_callback(headers, line))
+                    lambda line: _curl_header_callback(headers,
+                                                       native_str(line)))
     if request.streaming_callback:
         write_function = request.streaming_callback
     else:

{"tornado/curl_httpclient.py": "#!/usr/bin/env python\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Non-blocking HTTP client implementation using pycurl.\"\"\"\n\nfrom __future__ import absolute_import, division, print_function, with_statement\n\nimport collections\nimport logging\nimport pycurl\nimport threading\nimport time\n\nfrom tornado import httputil\nfrom tornado import ioloop\nfrom tornado.log import gen_log\nfrom tornado import stack_context\n\nfrom tornado.escape import utf8, native_str\nfrom tornado.httpclient import HTTPResponse, HTTPError, AsyncHTTPClient, main\nfrom tornado.util import bytes_type\n\ntry:\n    from io import BytesIO  # py3\nexcept ImportError:\n    from cStringIO import StringIO as BytesIO  # py2\n\n\nclass CurlAsyncHTTPClient(AsyncHTTPClient):\n    def initialize(self, io_loop, max_clients=10, defaults=None):\n        super(CurlAsyncHTTPClient, self).initialize(io_loop, defaults=defaults)\n        self._multi = pycurl.CurlMulti()\n        self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout)\n        self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket)\n        self._curls = [_curl_create() for i in range(max_clients)]\n        self._free_list = self._curls[:]\n        self._requests = collections.deque()\n        self._fds = {}\n        self._timeout = None\n\n        try:\n            self._socket_action = self._multi.socket_action\n        except AttributeError:\n            # socket_action is found in pycurl since 7.18.2 (it's been\n            # in libcurl longer than that but wasn't accessible to\n            # python).\n            gen_log.warning(\"socket_action method missing from pycurl; \"\n                            \"falling back to socket_all. Upgrading \"\n                            \"libcurl and pycurl will improve performance\")\n            self._socket_action = \\\n                lambda fd, action: self._multi.socket_all()\n\n        # libcurl has bugs that sometimes cause it to not report all\n        # relevant file descriptors and timeouts to TIMERFUNCTION/\n        # SOCKETFUNCTION.  Mitigate the effects of such bugs by\n        # forcing a periodic scan of all active requests.\n        self._force_timeout_callback = ioloop.PeriodicCallback(\n            self._handle_force_timeout, 1000, io_loop=io_loop)\n        self._force_timeout_callback.start()\n\n        # Work around a bug in libcurl 7.29.0: Some fields in the curl\n        # multi object are initialized lazily, and its destructor will\n        # segfault if it is destroyed without having been used.  Add\n        # and remove a dummy handle to make sure everything is\n        # initialized.\n        dummy_curl_handle = pycurl.Curl()\n        self._multi.add_handle(dummy_curl_handle)\n        self._multi.remove_handle(dummy_curl_handle)\n\n    def close(self):\n        self._force_timeout_callback.stop()\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        for curl in self._curls:\n            curl.close()\n        self._multi.close()\n        self._closed = True\n        super(CurlAsyncHTTPClient, self).close()\n\n    def fetch_impl(self, request, callback):\n        self._requests.append((request, callback))\n        self._process_queue()\n        self._set_timeout(0)\n\n    def _handle_socket(self, event, fd, multi, data):\n        \"\"\"Called by libcurl when it wants to change the file descriptors\n        it cares about.\n        \"\"\"\n        event_map = {\n            pycurl.POLL_NONE: ioloop.IOLoop.NONE,\n            pycurl.POLL_IN: ioloop.IOLoop.READ,\n            pycurl.POLL_OUT: ioloop.IOLoop.WRITE,\n            pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE\n        }\n        if event == pycurl.POLL_REMOVE:\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n                del self._fds[fd]\n        else:\n            ioloop_event = event_map[event]\n            # libcurl sometimes closes a socket and then opens a new\n            # one using the same FD without giving us a POLL_NONE in\n            # between.  This is a problem with the epoll IOLoop,\n            # because the kernel can tell when a socket is closed and\n            # removes it from the epoll automatically, causing future\n            # update_handler calls to fail.  Since we can't tell when\n            # this has happened, always use remove and re-add\n            # instead of update.\n            if fd in self._fds:\n                self.io_loop.remove_handler(fd)\n            self.io_loop.add_handler(fd, self._handle_events,\n                                     ioloop_event)\n            self._fds[fd] = ioloop_event\n\n    def _set_timeout(self, msecs):\n        \"\"\"Called by libcurl to schedule a timeout.\"\"\"\n        if self._timeout is not None:\n            self.io_loop.remove_timeout(self._timeout)\n        self._timeout = self.io_loop.add_timeout(\n            self.io_loop.time() + msecs / 1000.0, self._handle_timeout)\n\n    def _handle_events(self, fd, events):\n        \"\"\"Called by IOLoop when there is activity on one of our\n        file descriptors.\n        \"\"\"\n        action = 0\n        if events & ioloop.IOLoop.READ:\n            action |= pycurl.CSELECT_IN\n        if events & ioloop.IOLoop.WRITE:\n            action |= pycurl.CSELECT_OUT\n        while True:\n            try:\n                ret, num_handles = self._socket_action(fd, action)\n            except pycurl.error as e:\n                ret = e.args[0]\n            if ret != pycurl.E_CALL_MULTI_PERFORM:\n                break\n        self._finish_pending_requests()\n\n    def _handle_timeout(self):\n        \"\"\"Called by IOLoop when the requested timeout has passed.\"\"\"\n        with stack_context.NullContext():\n            self._timeout = None\n            while True:\n                try:\n                    ret, num_handles = self._socket_action(\n                        pycurl.SOCKET_TIMEOUT, 0)\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n        # In theory, we shouldn't have to do this because curl will\n        # call _set_timeout whenever the timeout changes.  However,\n        # sometimes after _handle_timeout we will need to reschedule\n        # immediately even though nothing has changed from curl's\n        # perspective.  This is because when socket_action is\n        # called with SOCKET_TIMEOUT, libcurl decides internally which\n        # timeouts need to be processed by using a monotonic clock\n        # (where available) while tornado uses python's time.time()\n        # to decide when timeouts have occurred.  When those clocks\n        # disagree on elapsed time (as they will whenever there is an\n        # NTP adjustment), tornado might call _handle_timeout before\n        # libcurl is ready.  After each timeout, resync the scheduled\n        # timeout with libcurl's current state.\n        new_timeout = self._multi.timeout()\n        if new_timeout >= 0:\n            self._set_timeout(new_timeout)\n\n    def _handle_force_timeout(self):\n        \"\"\"Called by IOLoop periodically to ask libcurl to process any\n        events it may have forgotten about.\n        \"\"\"\n        with stack_context.NullContext():\n            while True:\n                try:\n                    ret, num_handles = self._multi.socket_all()\n                except pycurl.error as e:\n                    ret = e.args[0]\n                if ret != pycurl.E_CALL_MULTI_PERFORM:\n                    break\n            self._finish_pending_requests()\n\n    def _finish_pending_requests(self):\n        \"\"\"Process any requests that were completed by the last\n        call to multi.socket_action.\n        \"\"\"\n        while True:\n            num_q, ok_list, err_list = self._multi.info_read()\n            for curl in ok_list:\n                self._finish(curl)\n            for curl, errnum, errmsg in err_list:\n                self._finish(curl, errnum, errmsg)\n            if num_q == 0:\n                break\n        self._process_queue()\n\n    def _process_queue(self):\n        with stack_context.NullContext():\n            while True:\n                started = 0\n                while self._free_list and self._requests:\n                    started += 1\n                    curl = self._free_list.pop()\n                    (request, callback) = self._requests.popleft()\n                    curl.info = {\n                        \"headers\": httputil.HTTPHeaders(),\n                        \"buffer\": BytesIO(),\n                        \"request\": request,\n                        \"callback\": callback,\n                        \"curl_start_time\": time.time(),\n                    }\n                    # Disable IPv6 to mitigate the effects of this bug\n                    # on curl versions <= 7.21.0\n                    # http://sourceforge.net/tracker/?func=detail&aid=3017819&group_id=976&atid=100976\n                    if pycurl.version_info()[2] <= 0x71500:  # 7.21.0\n                        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n                    _curl_setup_request(curl, request, curl.info[\"buffer\"],\n                                        curl.info[\"headers\"])\n                    self._multi.add_handle(curl)\n\n                if not started:\n                    break\n\n    def _finish(self, curl, curl_error=None, curl_message=None):\n        info = curl.info\n        curl.info = None\n        self._multi.remove_handle(curl)\n        self._free_list.append(curl)\n        buffer = info[\"buffer\"]\n        if curl_error:\n            error = CurlError(curl_error, curl_message)\n            code = error.code\n            effective_url = None\n            buffer.close()\n            buffer = None\n        else:\n            error = None\n            code = curl.getinfo(pycurl.HTTP_CODE)\n            effective_url = curl.getinfo(pycurl.EFFECTIVE_URL)\n            buffer.seek(0)\n        # the various curl timings are documented at\n        # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html\n        time_info = dict(\n            queue=info[\"curl_start_time\"] - info[\"request\"].start_time,\n            namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME),\n            connect=curl.getinfo(pycurl.CONNECT_TIME),\n            pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME),\n            starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME),\n            total=curl.getinfo(pycurl.TOTAL_TIME),\n            redirect=curl.getinfo(pycurl.REDIRECT_TIME),\n        )\n        try:\n            info[\"callback\"](HTTPResponse(\n                request=info[\"request\"], code=code, headers=info[\"headers\"],\n                buffer=buffer, effective_url=effective_url, error=error,\n                request_time=time.time() - info[\"curl_start_time\"],\n                time_info=time_info))\n        except Exception:\n            self.handle_callback_exception(info[\"callback\"])\n\n    def handle_callback_exception(self, callback):\n        self.io_loop.handle_callback_exception(callback)\n\n\nclass CurlError(HTTPError):\n    def __init__(self, errno, message):\n        HTTPError.__init__(self, 599, message)\n        self.errno = errno\n\n\ndef _curl_create():\n    curl = pycurl.Curl()\n    if gen_log.isEnabledFor(logging.DEBUG):\n        curl.setopt(pycurl.VERBOSE, 1)\n        curl.setopt(pycurl.DEBUGFUNCTION, _curl_debug)\n    return curl\n\n\ndef _curl_setup_request(curl, request, buffer, headers):\n    curl.setopt(pycurl.URL, native_str(request.url))\n\n    # libcurl's magic \"Expect: 100-continue\" behavior causes delays\n    # with servers that don't support it (which include, among others,\n    # Google's OpenID endpoint).  Additionally, this behavior has\n    # a bug in conjunction with the curl_multi_socket_action API\n    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),\n    # which increases the delays.  It's more trouble than it's worth,\n    # so just turn off the feature (yes, setting Expect: to an empty\n    # value is the official way to disable this)\n    if \"Expect\" not in request.headers:\n        request.headers[\"Expect\"] = \"\"\n\n    # libcurl adds Pragma: no-cache by default; disable that too\n    if \"Pragma\" not in request.headers:\n        request.headers[\"Pragma\"] = \"\"\n\n    # Request headers may be either a regular dict or HTTPHeaders object\n    if isinstance(request.headers, httputil.HTTPHeaders):\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.get_all()])\n    else:\n        curl.setopt(pycurl.HTTPHEADER,\n                    [native_str(\"%s: %s\" % i) for i in request.headers.items()])\n\n    if request.header_callback:\n        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)\n    else:\n        curl.setopt(pycurl.HEADERFUNCTION,\n                    lambda line: _curl_header_callback(headers, line))\n    if request.streaming_callback:\n        write_function = request.streaming_callback\n    else:\n        write_function = buffer.write\n    if bytes_type is str:  # py2\n        curl.setopt(pycurl.WRITEFUNCTION, write_function)\n    else:  # py3\n        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes\n        # a fork/port.  That version has a bug in which it passes unicode\n        # strings instead of bytes to the WRITEFUNCTION.  This means that\n        # if you use a WRITEFUNCTION (which tornado always does), you cannot\n        # download arbitrary binary data.  This needs to be fixed in the\n        # ported pycurl package, but in the meantime this lambda will\n        # make it work for downloading (utf8) text.\n        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))\n    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)\n    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)\n    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))\n    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))\n    if request.user_agent:\n        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))\n    else:\n        curl.setopt(pycurl.USERAGENT, \"Mozilla/5.0 (compatible; pycurl)\")\n    if request.network_interface:\n        curl.setopt(pycurl.INTERFACE, request.network_interface)\n    if request.use_gzip:\n        curl.setopt(pycurl.ENCODING, \"gzip,deflate\")\n    else:\n        curl.setopt(pycurl.ENCODING, \"none\")\n    if request.proxy_host and request.proxy_port:\n        curl.setopt(pycurl.PROXY, request.proxy_host)\n        curl.setopt(pycurl.PROXYPORT, request.proxy_port)\n        if request.proxy_username:\n            credentials = '%s:%s' % (request.proxy_username,\n                                     request.proxy_password)\n            curl.setopt(pycurl.PROXYUSERPWD, credentials)\n    else:\n        curl.setopt(pycurl.PROXY, '')\n        curl.unsetopt(pycurl.PROXYUSERPWD)\n    if request.validate_cert:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 1)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 2)\n    else:\n        curl.setopt(pycurl.SSL_VERIFYPEER, 0)\n        curl.setopt(pycurl.SSL_VERIFYHOST, 0)\n    if request.ca_certs is not None:\n        curl.setopt(pycurl.CAINFO, request.ca_certs)\n    else:\n        # There is no way to restore pycurl.CAINFO to its default value\n        # (Using unsetopt makes it reject all certificates).\n        # I don't see any way to read the default value from python so it\n        # can be restored later.  We'll have to just leave CAINFO untouched\n        # if no ca_certs file was specified, and require that if any\n        # request uses a custom ca_certs file, they all must.\n        pass\n\n    if request.allow_ipv6 is False:\n        # Curl behaves reasonably when DNS resolution gives an ipv6 address\n        # that we can't reach, so allow ipv6 unless the user asks to disable.\n        # (but see version check in _process_queue above)\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)\n    else:\n        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)\n\n    # Set the request method through curl's irritating interface which makes\n    # up names for almost every single method\n    curl_options = {\n        \"GET\": pycurl.HTTPGET,\n        \"POST\": pycurl.POST,\n        \"PUT\": pycurl.UPLOAD,\n        \"HEAD\": pycurl.NOBODY,\n    }\n    custom_methods = set([\"DELETE\", \"OPTIONS\", \"PATCH\"])\n    for o in curl_options.values():\n        curl.setopt(o, False)\n    if request.method in curl_options:\n        curl.unsetopt(pycurl.CUSTOMREQUEST)\n        curl.setopt(curl_options[request.method], True)\n    elif request.allow_nonstandard_methods or request.method in custom_methods:\n        curl.setopt(pycurl.CUSTOMREQUEST, request.method)\n    else:\n        raise KeyError('unknown method ' + request.method)\n\n    # Handle curl's cryptic options for every individual HTTP method\n    if request.method in (\"POST\", \"PUT\"):\n        if request.body is None:\n            raise AssertionError(\n                'Body must not be empty for \"%s\" request'\n                % request.method)\n\n        request_buffer = BytesIO(utf8(request.body))\n        curl.setopt(pycurl.READFUNCTION, request_buffer.read)\n        if request.method == \"POST\":\n            def ioctl(cmd):\n                if cmd == curl.IOCMD_RESTARTREAD:\n                    request_buffer.seek(0)\n            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)\n            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))\n        else:\n            curl.setopt(pycurl.INFILESIZE, len(request.body))\n    elif request.method == \"GET\":\n        if request.body is not None:\n            raise AssertionError('Body must be empty for GET request')\n\n    if request.auth_username is not None:\n        userpwd = \"%s:%s\" % (request.auth_username, request.auth_password or '')\n\n        if request.auth_mode is None or request.auth_mode == \"basic\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)\n        elif request.auth_mode == \"digest\":\n            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)\n        else:\n            raise ValueError(\"Unsupported auth_mode %s\" % request.auth_mode)\n\n        curl.setopt(pycurl.USERPWD, native_str(userpwd))\n        gen_log.debug(\"%s %s (username: %r)\", request.method, request.url,\n                      request.auth_username)\n    else:\n        curl.unsetopt(pycurl.USERPWD)\n        gen_log.debug(\"%s %s\", request.method, request.url)\n\n    if request.client_cert is not None:\n        curl.setopt(pycurl.SSLCERT, request.client_cert)\n\n    if request.client_key is not None:\n        curl.setopt(pycurl.SSLKEY, request.client_key)\n\n    if threading.activeCount() > 1:\n        # libcurl/pycurl is not thread-safe by default.  When multiple threads\n        # are used, signals should be disabled.  This has the side effect\n        # of disabling DNS timeouts in some environments (when libcurl is\n        # not linked against ares), so we don't do it when there is only one\n        # thread.  Applications that use many short-lived threads may need\n        # to set NOSIGNAL manually in a prepare_curl_callback since\n        # there may not be any other threads running at the time we call\n        # threading.activeCount.\n        curl.setopt(pycurl.NOSIGNAL, 1)\n    if request.prepare_curl_callback is not None:\n        request.prepare_curl_callback(curl)\n\n\ndef _curl_header_callback(headers, header_line):\n    # header_line as returned by curl includes the end-of-line characters.\n    header_line = header_line.strip()\n    if header_line.startswith(\"HTTP/\"):\n        headers.clear()\n        return\n    if not header_line:\n        return\n    headers.parse_line(header_line)\n\n\ndef _curl_debug(debug_type, debug_msg):\n    debug_types = ('I', '<', '>', '<', '>')\n    if debug_type == 0:\n        gen_log.debug('%s', debug_msg.strip())\n    elif debug_type in (1, 2):\n        for line in debug_msg.splitlines():\n            gen_log.debug('%s %s', debug_types[debug_type], line)\n    elif debug_type == 4:\n        gen_log.debug('%s %r', debug_types[debug_type], debug_msg)\n\nif __name__ == \"__main__\":\n    AsyncHTTPClient.configure(CurlAsyncHTTPClient)\n    main()\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Header callbacks receive bytes instead of strings, causing encoding issues

**Description:**
When making HTTP requests using the CurlHTTPClient, the header callbacks receive header lines as bytes rather than decoded strings. This leads to encoding mismatches, causing various functionality tests to fail unexpectedly.

**Example Buggy Code:**
```python
def make_request():
    headers = {}
    def header_callback(line):
        headers['Content-Type'] = line
    request = HTTPRequest(url="http://example.com/auth", header_callback=header_callback)
    client = CurlHTTPClient()
    client.fetch(request)
    # Further processing expects headers['Content-Type'] to be a string
```

**Expected Behavior:**
Header callbacks should receive header lines as decoded strings to ensure proper encoding and seamless processing of header information.

**Actual Behavior:**
Header callbacks receive header lines as bytes, leading to encoding issues and causing tests that rely on string-based header values to fail.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/curl_httpclient.py
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""Non-blocking HTTP client implementation using pycurl."""

from __future__ import absolute_import, division, print_function, with_statement

import collections
import logging
import pycurl
import threading
import time

from tornado import httputil
from tornado import ioloop
from tornado.log import gen_log
from tornado import stack_context

from tornado.escape import utf8, native_str
from tornado.httpclient import HTTPResponse, HTTPError, AsyncHTTPClient, main
from tornado.util import bytes_type

try:
    from io import BytesIO  # py3
except ImportError:
    from cStringIO import StringIO as BytesIO  # py2


class CurlAsyncHTTPClient(AsyncHTTPClient):
    def initialize(self, io_loop, max_clients=10, defaults=None):
        super(CurlAsyncHTTPClient, self).initialize(io_loop, defaults=defaults)
        self._multi = pycurl.CurlMulti()
        self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout)
        self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket)
        self._curls = [_curl_create() for i in range(max_clients)]
        self._free_list = self._curls[:]
        self._requests = collections.deque()
        self._fds = {}
        self._timeout = None

        try:
            self._socket_action = self._multi.socket_action
        except AttributeError:
            # socket_action is found in pycurl since 7.18.2 (it's been
            # in libcurl longer than that but wasn't accessible to
            # python).
            gen_log.warning("socket_action method missing from pycurl; "
                            "falling back to socket_all. Upgrading "
                            "libcurl and pycurl will improve performance")
            self._socket_action = \
                lambda fd, action: self._multi.socket_all()

        # libcurl has bugs that sometimes cause it to not report all
        # relevant file descriptors and timeouts to TIMERFUNCTION/
        # SOCKETFUNCTION.  Mitigate the effects of such bugs by
        # forcing a periodic scan of all active requests.
        self._force_timeout_callback = ioloop.PeriodicCallback(
            self._handle_force_timeout, 1000, io_loop=io_loop)
        self._force_timeout_callback.start()

        # Work around a bug in libcurl 7.29.0: Some fields in the curl
        # multi object are initialized lazily, and its destructor will
        # segfault if it is destroyed without having been used.  Add
        # and remove a dummy handle to make sure everything is
        # initialized.
        dummy_curl_handle = pycurl.Curl()
        self._multi.add_handle(dummy_curl_handle)
        self._multi.remove_handle(dummy_curl_handle)

    def close(self):
        self._force_timeout_callback.stop()
        if self._timeout is not None:
            self.io_loop.remove_timeout(self._timeout)
        for curl in self._curls:
            curl.close()
        self._multi.close()
        self._closed = True
        super(CurlAsyncHTTPClient, self).close()

    def fetch_impl(self, request, callback):
        self._requests.append((request, callback))
        self._process_queue()
        self._set_timeout(0)

    def _handle_socket(self, event, fd, multi, data):
        """Called by libcurl when it wants to change the file descriptors
        it cares about.
        """
        event_map = {
            pycurl.POLL_NONE: ioloop.IOLoop.NONE,
            pycurl.POLL_IN: ioloop.IOLoop.READ,
            pycurl.POLL_OUT: ioloop.IOLoop.WRITE,
            pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE
        }
        if event == pycurl.POLL_REMOVE:
            if fd in self._fds:
                self.io_loop.remove_handler(fd)
                del self._fds[fd]
        else:
            ioloop_event = event_map[event]
            # libcurl sometimes closes a socket and then opens a new
            # one using the same FD without giving us a POLL_NONE in
            # between.  This is a problem with the epoll IOLoop,
            # because the kernel can tell when a socket is closed and
            # removes it from the epoll automatically, causing future
            # update_handler calls to fail.  Since we can't tell when
            # this has happened, always use remove and re-add
            # instead of update.
            if fd in self._fds:
                self.io_loop.remove_handler(fd)
            self.io_loop.add_handler(fd, self._handle_events,
                                     ioloop_event)
            self._fds[fd] = ioloop_event

    def _set_timeout(self, msecs):
        """Called by libcurl to schedule a timeout."""
        if self._timeout is not None:
            self.io_loop.remove_timeout(self._timeout)
        self._timeout = self.io_loop.add_timeout(
            self.io_loop.time() + msecs / 1000.0, self._handle_timeout)

    def _handle_events(self, fd, events):
        """Called by IOLoop when there is activity on one of our
        file descriptors.
        """
        action = 0
        if events & ioloop.IOLoop.READ:
            action |= pycurl.CSELECT_IN
        if events & ioloop.IOLoop.WRITE:
            action |= pycurl.CSELECT_OUT
        while True:
            try:
                ret, num_handles = self._socket_action(fd, action)
            except pycurl.error as e:
                ret = e.args[0]
            if ret != pycurl.E_CALL_MULTI_PERFORM:
                break
        self._finish_pending_requests()

    def _handle_timeout(self):
        """Called by IOLoop when the requested timeout has passed."""
        with stack_context.NullContext():
            self._timeout = None
            while True:
                try:
                    ret, num_handles = self._socket_action(
                        pycurl.SOCKET_TIMEOUT, 0)
                except pycurl.error as e:
                    ret = e.args[0]
                if ret != pycurl.E_CALL_MULTI_PERFORM:
                    break
            self._finish_pending_requests()

        # In theory, we shouldn't have to do this because curl will
        # call _set_timeout whenever the timeout changes.  However,
        # sometimes after _handle_timeout we will need to reschedule
        # immediately even though nothing has changed from curl's
        # perspective.  This is because when socket_action is
        # called with SOCKET_TIMEOUT, libcurl decides internally which
        # timeouts need to be processed by using a monotonic clock
        # (where available) while tornado uses python's time.time()
        # to decide when timeouts have occurred.  When those clocks
        # disagree on elapsed time (as they will whenever there is an
        # NTP adjustment), tornado might call _handle_timeout before
        # libcurl is ready.  After each timeout, resync the scheduled
        # timeout with libcurl's current state.
        new_timeout = self._multi.timeout()
        if new_timeout >= 0:
            self._set_timeout(new_timeout)

    def _handle_force_timeout(self):
        """Called by IOLoop periodically to ask libcurl to process any
        events it may have forgotten about.
        """
        with stack_context.NullContext():
            while True:
                try:
                    ret, num_handles = self._multi.socket_all()
                except pycurl.error as e:
                    ret = e.args[0]
                if ret != pycurl.E_CALL_MULTI_PERFORM:
                    break
            self._finish_pending_requests()

    def _finish_pending_requests(self):
        """Process any requests that were completed by the last
        call to multi.socket_action.
        """
        while True:
            num_q, ok_list, err_list = self._multi.info_read()
            for curl in ok_list:
                self._finish(curl)
            for curl, errnum, errmsg in err_list:
                self._finish(curl, errnum, errmsg)
            if num_q == 0:
                break
        self._process_queue()

    def _process_queue(self):
        with stack_context.NullContext():
            while True:
                started = 0
                while self._free_list and self._requests:
                    started += 1
                    curl = self._free_list.pop()
                    (request, callback) = self._requests.popleft()
                    curl.info = {
                        "headers": httputil.HTTPHeaders(),
                        "buffer": BytesIO(),
                        "request": request,
                        "callback": callback,
                        "curl_start_time": time.time(),
                    }
                    # Disable IPv6 to mitigate the effects of this bug
                    # on curl versions <= 7.21.0
                    # http://sourceforge.net/tracker/?func=detail&aid=3017819&group_id=976&atid=100976
                    if pycurl.version_info()[2] <= 0x71500:  # 7.21.0
                        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
                    _curl_setup_request(curl, request, curl.info["buffer"],
                                        curl.info["headers"])
                    self._multi.add_handle(curl)

                if not started:
                    break

    def _finish(self, curl, curl_error=None, curl_message=None):
        info = curl.info
        curl.info = None
        self._multi.remove_handle(curl)
        self._free_list.append(curl)
        buffer = info["buffer"]
        if curl_error:
            error = CurlError(curl_error, curl_message)
            code = error.code
            effective_url = None
            buffer.close()
            buffer = None
        else:
            error = None
            code = curl.getinfo(pycurl.HTTP_CODE)
            effective_url = curl.getinfo(pycurl.EFFECTIVE_URL)
            buffer.seek(0)
        # the various curl timings are documented at
        # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
        time_info = dict(
            queue=info["curl_start_time"] - info["request"].start_time,
            namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME),
            connect=curl.getinfo(pycurl.CONNECT_TIME),
            pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME),
            starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME),
            total=curl.getinfo(pycurl.TOTAL_TIME),
            redirect=curl.getinfo(pycurl.REDIRECT_TIME),
        )
        try:
            info["callback"](HTTPResponse(
                request=info["request"], code=code, headers=info["headers"],
                buffer=buffer, effective_url=effective_url, error=error,
                request_time=time.time() - info["curl_start_time"],
                time_info=time_info))
        except Exception:
            self.handle_callback_exception(info["callback"])

    def handle_callback_exception(self, callback):
        self.io_loop.handle_callback_exception(callback)


class CurlError(HTTPError):
    def __init__(self, errno, message):
        HTTPError.__init__(self, 599, message)
        self.errno = errno


def _curl_create():
    curl = pycurl.Curl()
    if gen_log.isEnabledFor(logging.DEBUG):
        curl.setopt(pycurl.VERBOSE, 1)
        curl.setopt(pycurl.DEBUGFUNCTION, _curl_debug)
    return curl


def _curl_setup_request(curl, request, buffer, headers):
    curl.setopt(pycurl.URL, native_str(request.url))

    # libcurl's magic "Expect: 100-continue" behavior causes delays
    # with servers that don't support it (which include, among others,
    # Google's OpenID endpoint).  Additionally, this behavior has
    # a bug in conjunction with the curl_multi_socket_action API
    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),
    # which increases the delays.  It's more trouble than it's worth,
    # so just turn off the feature (yes, setting Expect: to an empty
    # value is the official way to disable this)
    if "Expect" not in request.headers:
        request.headers["Expect"] = ""

    # libcurl adds Pragma: no-cache by default; disable that too
    if "Pragma" not in request.headers:
        request.headers["Pragma"] = ""

    # Request headers may be either a regular dict or HTTPHeaders object
    if isinstance(request.headers, httputil.HTTPHeaders):
        curl.setopt(pycurl.HTTPHEADER,
                    [native_str("%s: %s" % i) for i in request.headers.get_all()])
    else:
        curl.setopt(pycurl.HTTPHEADER,
                    [native_str("%s: %s" % i) for i in request.headers.items()])

    if request.header_callback:
        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)
    else:
        curl.setopt(pycurl.HEADERFUNCTION,
                    lambda line: _curl_header_callback(headers, line))
    if request.streaming_callback:
        write_function = request.streaming_callback
    else:
        write_function = buffer.write
    if bytes_type is str:  # py2
        curl.setopt(pycurl.WRITEFUNCTION, write_function)
    else:  # py3
        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes
        # a fork/port.  That version has a bug in which it passes unicode
        # strings instead of bytes to the WRITEFUNCTION.  This means that
        # if you use a WRITEFUNCTION (which tornado always does), you cannot
        # download arbitrary binary data.  This needs to be fixed in the
        # ported pycurl package, but in the meantime this lambda will
        # make it work for downloading (utf8) text.
        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))
    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)
    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)
    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))
    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))
    if request.user_agent:
        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))
    else:
        curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)")
    if request.network_interface:
        curl.setopt(pycurl.INTERFACE, request.network_interface)
    if request.use_gzip:
        curl.setopt(pycurl.ENCODING, "gzip,deflate")
    else:
        curl.setopt(pycurl.ENCODING, "none")
    if request.proxy_host and request.proxy_port:
        curl.setopt(pycurl.PROXY, request.proxy_host)
        curl.setopt(pycurl.PROXYPORT, request.proxy_port)
        if request.proxy_username:
            credentials = '%s:%s' % (request.proxy_username,
                                     request.proxy_password)
            curl.setopt(pycurl.PROXYUSERPWD, credentials)
    else:
        curl.setopt(pycurl.PROXY, '')
        curl.unsetopt(pycurl.PROXYUSERPWD)
    if request.validate_cert:
        curl.setopt(pycurl.SSL_VERIFYPEER, 1)
        curl.setopt(pycurl.SSL_VERIFYHOST, 2)
    else:
        curl.setopt(pycurl.SSL_VERIFYPEER, 0)
        curl.setopt(pycurl.SSL_VERIFYHOST, 0)
    if request.ca_certs is not None:
        curl.setopt(pycurl.CAINFO, request.ca_certs)
    else:
        # There is no way to restore pycurl.CAINFO to its default value
        # (Using unsetopt makes it reject all certificates).
        # I don't see any way to read the default value from python so it
        # can be restored later.  We'll have to just leave CAINFO untouched
        # if no ca_certs file was specified, and require that if any
        # request uses a custom ca_certs file, they all must.
        pass

    if request.allow_ipv6 is False:
        # Curl behaves reasonably when DNS resolution gives an ipv6 address
        # that we can't reach, so allow ipv6 unless the user asks to disable.
        # (but see version check in _process_queue above)
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
    else:
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)

    # Set the request method through curl's irritating interface which makes
    # up names for almost every single method
    curl_options = {
        "GET": pycurl.HTTPGET,
        "POST": pycurl.POST,
        "PUT": pycurl.UPLOAD,
        "HEAD": pycurl.NOBODY,
    }
    custom_methods = set(["DELETE", "OPTIONS", "PATCH"])
    for o in curl_options.values():
        curl.setopt(o, False)
    if request.method in curl_options:
        curl.unsetopt(pycurl.CUSTOMREQUEST)
        curl.setopt(curl_options[request.method], True)
    elif request.allow_nonstandard_methods or request.method in custom_methods:
        curl.setopt(pycurl.CUSTOMREQUEST, request.method)
    else:
        raise KeyError('unknown method ' + request.method)

    # Handle curl's cryptic options for every individual HTTP method
    if request.method in ("POST", "PUT"):
        if request.body is None:
            raise AssertionError(
                'Body must not be empty for "%s" request'
                % request.method)

        request_buffer = BytesIO(utf8(request.body))
        curl.setopt(pycurl.READFUNCTION, request_buffer.read)
        if request.method == "POST":
            def ioctl(cmd):
                if cmd == curl.IOCMD_RESTARTREAD:
                    request_buffer.seek(0)
            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)
            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))
        else:
            curl.setopt(pycurl.INFILESIZE, len(request.body))
    elif request.method == "GET":
        if request.body is not None:
            raise AssertionError('Body must be empty for GET request')

    if request.auth_username is not None:
        userpwd = "%s:%s" % (request.auth_username, request.auth_password or '')

        if request.auth_mode is None or request.auth_mode == "basic":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
        elif request.auth_mode == "digest":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
        else:
            raise ValueError("Unsupported auth_mode %s" % request.auth_mode)

        curl.setopt(pycurl.USERPWD, native_str(userpwd))
        gen_log.debug("%s %s (username: %r)", request.method, request.url,
                      request.auth_username)
    else:
        curl.unsetopt(pycurl.USERPWD)
        gen_log.debug("%s %s", request.method, request.url)

    if request.client_cert is not None:
        curl.setopt(pycurl.SSLCERT, request.client_cert)

    if request.client_key is not None:
        curl.setopt(pycurl.SSLKEY, request.client_key)

    if threading.activeCount() > 1:
        # libcurl/pycurl is not thread-safe by default.  When multiple threads
        # are used, signals should be disabled.  This has the side effect
        # of disabling DNS timeouts in some environments (when libcurl is
        # not linked against ares), so we don't do it when there is only one
        # thread.  Applications that use many short-lived threads may need
        # to set NOSIGNAL manually in a prepare_curl_callback since
        # there may not be any other threads running at the time we call
        # threading.activeCount.
        curl.setopt(pycurl.NOSIGNAL, 1)
    if request.prepare_curl_callback is not None:
        request.prepare_curl_callback(curl)


def _curl_header_callback(headers, header_line):
    # header_line as returned by curl includes the end-of-line characters.
    header_line = header_line.strip()
    if header_line.startswith("HTTP/"):
        headers.clear()
        return
    if not header_line:
        return
    headers.parse_line(header_line)


def _curl_debug(debug_type, debug_msg):
    debug_types = ('I', '<', '>', '<', '>')
    if debug_type == 0:
        gen_log.debug('%s', debug_msg.strip())
    elif debug_type in (1, 2):
        for line in debug_msg.splitlines():
            gen_log.debug('%s %s', debug_types[debug_type], line)
    elif debug_type == 4:
        gen_log.debug('%s %r', debug_types[debug_type], debug_msg)

if __name__ == "__main__":
    AsyncHTTPClient.configure(CurlAsyncHTTPClient)
    main()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user