# Buggy code
def render_GET(self, request):
    """
    .. http:get:: /torrentinfo

    A GET request to this endpoint will return information from a torrent found at a provided URI.
    This URI can either represent a file location, a magnet link or a HTTP(S) url.
    - torrent: the URI of the torrent file that should be downloaded. This parameter is required.

        **Example request**:

            .. sourcecode:: none

                curl -X GET http://localhost:8085/torrentinfo?torrent=file:/home/me/test.torrent

        **Example response**:

            .. sourcecode:: javascript

                {"metainfo": <torrent metainfo dictionary>}
    """

    def on_got_metainfo(metainfo):
        if not metainfo:
            if not request.finished:
                request.setResponseCode(http.INTERNAL_SERVER_ERROR)
                request.write(json.twisted_dumps({"error": "metainfo error"}))
            # If the above request.write failed, the request will have already been finished
            if not request.finished:
                self.finish_request(request)
                return

        if not isinstance(metainfo, dict) or b"info" not in metainfo:
            self._logger.warning("Received metainfo is not a valid dictionary")
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            request.write(json.twisted_dumps({"error": "invalid response"}))
            self.finish_request(request)
            return

        # Add the torrent to GigaChannel as a free-for-all entry, so others can search it
        self.session.lm.mds.TorrentMetadata.add_ffa_from_dict(
            tdef_to_metadata_dict(TorrentDef.load_from_dict(metainfo))
        )

        # TODO(Martijn): store the stuff in a database!!!
        # TODO(Vadim): this means cache the downloaded torrent in a binary storage, like LevelDB
        infohash = hashlib.sha1(bencode(metainfo[b"info"])).digest()

        # Check if the torrent is already in the downloads
        encoded_metainfo = deepcopy(metainfo)
        encoded_metainfo["download_exists"] = infohash in self.session.lm.downloads
        # FIXME: json.dumps garbles binary data that is used by the 'pieces' field
        # However, this is fine as long as the GUI does not use this field.
        encoded_metainfo[b"info"][b"pieces"] = hexlify(
            encoded_metainfo[b"info"][b"pieces"]
        ).encode("utf-8")
        encoded_metainfo = hexlify(
            json.dumps(recursive_unicode(encoded_metainfo), ensure_ascii=False).encode(
                "utf-8"
            )
        )

        request.write(json.twisted_dumps({"metainfo": encoded_metainfo}))
        self.finish_request(request)

    def on_lookup_error(failure):
        failure.trap(ConnectError, DNSLookupError, HttpError, ConnectionLost)
        request.setResponseCode(http.INTERNAL_SERVER_ERROR)
        request.write(
            json.twisted_dumps({"error": unichar_string(failure.getErrorMessage())})
        )
        self.finish_request(request)

    def _on_loaded(response):
        if response.startswith(b"magnet"):
            _, infohash, _ = parse_magnetlink(response)
            if infohash:
                self.session.lm.ltmgr.get_metainfo(infohash, timeout=20).addCallback(
                    on_got_metainfo
                )
                return

        # Otherwise, we directly invoke the on_got_metainfo method
        on_got_metainfo(bdecode(response))

    def on_file():
        try:
            filename = url2pathname(uri[5:])
            tdef = TorrentDef.load(filename)
            on_got_metainfo(tdef.get_metainfo())
            return NOT_DONE_YET
        except (TypeError, RuntimeError):
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            return json.twisted_dumps({"error": "error while decoding torrent file"})

    def on_magnet(mlink=None):
        infohash = parse_magnetlink(mlink or uri)[1]
        if infohash is None:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "missing infohash"})

        self.session.lm.ltmgr.get_metainfo(infohash, timeout=20).addCallback(
            on_got_metainfo
        )
        return NOT_DONE_YET

    args = recursive_unicode(request.args)
    if "uri" not in args or not args["uri"]:
        request.setResponseCode(http.BAD_REQUEST)
        return json.twisted_dumps({"error": "uri parameter missing"})

    uri = args["uri"][0]
    if uri.startswith("file:"):
        return on_file()
    elif uri.startswith("http"):
        http_get(uri.encode("utf-8")).addCallback(_on_loaded).addErrback(
            on_lookup_error
        )
    elif uri.startswith("magnet"):
        return on_magnet()
    else:
        request.setResponseCode(http.BAD_REQUEST)
        return json.twisted_dumps({"error": "invalid uri"})

    return NOT_DONE_YET


# Traceback (exceptions.RuntimeError)
Unhandled error in Deferred:

Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/twisted/web/_newclient.py", line 1229, in _bodyDataFinished_CONNECTED
self._bodyProtocol.connectionLost(reason)
File "/Library/Python/2.7/site-packages/twisted/web/client.py", line 2170, in connectionLost
self.deferred.callback(b''.join(self.dataBuffer))
File "/Library/Python/2.7/site-packages/twisted/internet/defer.py", line 460, in callback
self._startRunCallbacks(result)
File "/Library/Python/2.7/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
self._runCallbacks()
--- <exception caught here> ---
File "/Library/Python/2.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/Users/martijndevos/Documents/tribler/Tribler/Core/Modules/restapi/torrentinfo_endpoint.py", line 97, in on_lookup_error
failure.trap(ConnectError, DNSLookupError, HttpError, ConnectionLost)
File "/Library/Python/2.7/site-packages/twisted/python/failure.py", line 441, in trap
raise self
File "/Library/Python/2.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/Users/martijndevos/Documents/tribler/Tribler/Core/Modules/restapi/torrentinfo_endpoint.py", line 110, in _on_loaded
on_got_metainfo(bdecode(response))
exceptions.RuntimeError: expected value (list, dict, int or string) in bencoded string

ERROR   1565616789.85            tribler_window:73   (root)  Traceback (most recent call last):
File "/Users/martijndevos/Documents/tribler/TriblerGUI/event_request_manager.py", line 114, in on_read_data
raise RuntimeError(json_dict["event"]["text"])
RuntimeError: [Failure instance: Traceback: <type 'exceptions.RuntimeError'>: expected value (list, dict, int or string) in bencoded string
/Library/Python/2.7/site-packages/twisted/web/_newclient.py:1229:_bodyDataFinished_CONNECTED
/Library/Python/2.7/site-packages/twisted/web/client.py:2170:connectionLost
/Library/Python/2.7/site-packages/twisted/internet/defer.py:460:callback
/Library/Python/2.7/site-packages/twisted/internet/defer.py:568:_startRunCallbacks
--- <exception caught here> ---
/Library/Python/2.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
/Users/martijndevos/Documents/tribler/Tribler/Core/Modules/restapi/torrentinfo_endpoint.py:97:on_lookup_error
/Library/Python/2.7/site-packages/twisted/python/failure.py:441:trap
/Library/Python/2.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
/Users/martijndevos/Documents/tribler/Tribler/Core/Modules/restapi/torrentinfo_endpoint.py:110:_on_loaded

# Fixed code
def render_GET(self, request):
    """
    .. http:get:: /torrentinfo

    A GET request to this endpoint will return information from a torrent found at a provided URI.
    This URI can either represent a file location, a magnet link or a HTTP(S) url.
    - torrent: the URI of the torrent file that should be downloaded. This parameter is required.

        **Example request**:

            .. sourcecode:: none

                curl -X GET http://localhost:8085/torrentinfo?torrent=file:/home/me/test.torrent

        **Example response**:

            .. sourcecode:: javascript

                {"metainfo": <torrent metainfo dictionary>}
    """

    def on_got_metainfo(metainfo):
        if not metainfo:
            if not request.finished:
                request.setResponseCode(http.INTERNAL_SERVER_ERROR)
                request.write(json.twisted_dumps({"error": "metainfo error"}))
            # If the above request.write failed, the request will have already been finished
            if not request.finished:
                self.finish_request(request)
                return

        if not isinstance(metainfo, dict) or b"info" not in metainfo:
            self._logger.warning("Received metainfo is not a valid dictionary")
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            request.write(json.twisted_dumps({"error": "invalid response"}))
            self.finish_request(request)
            return

        # Add the torrent to GigaChannel as a free-for-all entry, so others can search it
        self.session.lm.mds.TorrentMetadata.add_ffa_from_dict(
            tdef_to_metadata_dict(TorrentDef.load_from_dict(metainfo))
        )

        # TODO(Martijn): store the stuff in a database!!!
        # TODO(Vadim): this means cache the downloaded torrent in a binary storage, like LevelDB
        infohash = hashlib.sha1(bencode(metainfo[b"info"])).digest()

        # Check if the torrent is already in the downloads
        encoded_metainfo = deepcopy(metainfo)
        encoded_metainfo["download_exists"] = infohash in self.session.lm.downloads
        # FIXME: json.dumps garbles binary data that is used by the 'pieces' field
        # However, this is fine as long as the GUI does not use this field.
        encoded_metainfo[b"info"][b"pieces"] = hexlify(
            encoded_metainfo[b"info"][b"pieces"]
        ).encode("utf-8")
        encoded_metainfo = hexlify(
            json.dumps(recursive_unicode(encoded_metainfo), ensure_ascii=False).encode(
                "utf-8"
            )
        )

        request.write(json.twisted_dumps({"metainfo": encoded_metainfo}))
        self.finish_request(request)

    def on_lookup_error(failure):
        failure.trap(ConnectError, DNSLookupError, HttpError, ConnectionLost)
        request.setResponseCode(http.INTERNAL_SERVER_ERROR)
        request.write(
            json.twisted_dumps({"error": unichar_string(failure.getErrorMessage())})
        )
        self.finish_request(request)

    def _on_loaded(response):
        if response.startswith(b"magnet"):
            _, infohash, _ = parse_magnetlink(response)
            if infohash:
                self.session.lm.ltmgr.get_metainfo(infohash, timeout=20).addCallback(
                    on_got_metainfo
                )
                return

        # Otherwise, we directly invoke the on_got_metainfo method
        try:
            decoded_response = bdecode(response)
            on_got_metainfo(decoded_response)
        except RuntimeError:
            # The decoding failed - handle it like a None metainfo
            on_got_metainfo(None)

    def on_file():
        try:
            filename = url2pathname(uri[5:])
            tdef = TorrentDef.load(filename)
            on_got_metainfo(tdef.get_metainfo())
            return NOT_DONE_YET
        except (TypeError, RuntimeError):
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            return json.twisted_dumps({"error": "error while decoding torrent file"})

    def on_magnet(mlink=None):
        infohash = parse_magnetlink(mlink or uri)[1]
        if infohash is None:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "missing infohash"})

        self.session.lm.ltmgr.get_metainfo(infohash, timeout=20).addCallback(
            on_got_metainfo
        )
        return NOT_DONE_YET

    args = recursive_unicode(request.args)
    if "uri" not in args or not args["uri"]:
        request.setResponseCode(http.BAD_REQUEST)
        return json.twisted_dumps({"error": "uri parameter missing"})

    uri = args["uri"][0]
    if uri.startswith("file:"):
        return on_file()
    elif uri.startswith("http"):
        http_get(uri.encode("utf-8")).addCallback(_on_loaded).addErrback(
            on_lookup_error
        )
    elif uri.startswith("magnet"):
        return on_magnet()
    else:
        request.setResponseCode(http.BAD_REQUEST)
        return json.twisted_dumps({"error": "invalid uri"})

    return NOT_DONE_YET