Не удается правильно подключиться через http.client - PullRequest
0 голосов
/ 16 декабря 2018

У меня есть бот Telegram, который постоянно запрашивает обновление API Telegram.

Иногда я получаю сообщение об ошибке, подобное этому:

18-12-16 12:12:37: error: Traceback (most recent call last):
  File "/home/pi/MuseBot/main.py", line 157, in <module>
    main()
  File "/home/pi/MuseBot/main.py", line 34, in main
    updates = HANDLER.makeRequest("getUpdates", {"timeout": REQUEST_DELAY, "offset": lastOffset})
  File "/home/pi/MuseBot/functions.py", line 42, in makeRequest
    response = self.con.getresponse()
  File "/usr/lib/python3.5/http/client.py", line 1198, in getresponse
    response.begin()
  File "/usr/lib/python3.5/http/client.py", line 297, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python3.5/http/client.py", line 258, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "/usr/lib/python3.5/socket.py", line 576, in readinto
    return self._sock.recv_into(b)
  File "/usr/lib/python3.5/ssl.py", line 937, in recv_into
    return self.read(nbytes, buffer)
  File "/usr/lib/python3.5/ssl.py", line 799, in read
    return self._sslobj.read(len, buffer)
  File "/usr/lib/python3.5/ssl.py", line 583, in read
    v = self._sslobj.read(len, buffer)
OSError: [Errno 113] No route to host

Получив это несколько раз, я реализовалавтоматический перезапуск для бота - я решил, что эту ошибку я не могу остановить, она на стороне сервера.

Однако у меня возникли проблемы с этим.

У меня есть APIкласс-обработчик, чтобы мне было проще управлять запросами API.Это создается только один раз, когда скрипт запускается, а не после каждого перезапуска.

Создает соединение примерно так:

class ApiHandler:
    def __init__(self):
        self.con = http.client.HTTPSConnection(URL, 443)

И когда что-то идет не так, я вызываю эту функцию наit:

    def reconnect(self):
        self.con.close()
        self.con = http.client.HTTPSConnection(URL, 443)

Затем я делаю запросы с этим обработчиком, используя:

    def makeRequest(self, cmd, data={}):
        jsonData = json.dumps(data)
        try:
            self.con.request("POST", REQUEST_URL+cmd, jsonData, HEADERS)
        except:
            debug("An error occurred while carrying out the API request", 1)

        response = self.con.getresponse()
        decodedResponse = json.loads(response.read().decode())
        if not decodedResponse["ok"]:
            debug("reponse: {}".format(decodedResponse), 3)
            raise ApiError(decodedResponse["error_code"])
            return False

        return decodedResponse["result"]

(ApiError - это просто класс, созданный из Exception, который я использую, чтобы отличать его от других ошибок при обработке сбоев.) Каждый раз, когда я получаю вышеуказанную ошибку, я автоматически reconnect.Но каждый последующий вызов makeRequest выдает эту ошибку:

18-12-16 12:13:02: notice: An error occurred while carrying out the API request
18-12-16 12:13:02: error: Traceback (most recent call last):
  File "/home/pi/MuseBot/main.py", line 157, in <module>
    main()
  File "/home/pi/MuseBot/main.py", line 23, in main
    metaData = HANDLER.makeRequest("getMe")
  File "/home/pi/MuseBot/functions.py", line 42, in makeRequest
    response = self.con.getresponse()
  File "/usr/lib/python3.5/http/client.py", line 1194, in getresponse
    response = self.response_class(self.sock, method=self._method)
  File "/usr/lib/python3.5/http/client.py", line 235, in __init__
    self.fp = sock.makefile("rb")
AttributeError: 'NoneType' object has no attribute 'makefile'

Затем я автоматически воссоединяюсь и пытаюсь снова, но ошибка повторяется, и бот входит в адский цикл ошибок, который остается непроверенным, создает большой файл журналаи падает мой малиновый пи.Единственное решение - перезапустить мой Raspberry Pi.

Я бы подумал, что создание нового соединения в reconnect с помощью:

self.con = http.client.HTTPSConnection(URL, 443)

это исправит, но, видимо, нет.

Я в растерянности из-за того, как автоматически перезапустить моего бота без создания цикла ошибок.Любая помощь очень очень ценится.

1 Ответ

0 голосов
/ 21 декабря 2018

Уважаемый @JThistle, не принимайте это на свой счет, но у вас есть несколько ошибок в makeRequest, пожалуйста, прочтите мои комментарии в вашем коде, а затем пример, который должен сделать то, что вы ожидаете.

Ваш код:

def makeRequest(self, cmd, data={}):
    jsonData = json.dumps(data)
    try:
        self.con.request("POST", REQUEST_URL+ cmd, jsonData, HEADERS)
    except:
        # this is you main bug you do not rerise the exception so you
        # never actually reconnect
        debug("An error occurred while carrying out the API request", 1)

    # so your program follows to this step and the socket is closed when you try
    # to read from it, hence the second exception
    response = self.con.getresponse()

    decodedResponse = json.loads(response.read().decode())
    if not decodedResponse["ok"]:
        debug("reponse: {}".format(decodedResponse), 3)
        raise ApiError(decodedResponse["error_code"])
        # this is wrong you can not reach this return, raise exception ends execution
        # and goes up the stack until it will be caught or will crash the program
        return False

    return decodedResponse["result"]

Это должно вызвать повторное исключение, чтобы ваше повторное соединение могло действительно произойти:

# python uses snake case not camel case, it might seem silly but pytonistas are
# easily annoyed bunch
def make_request(self, cmd, data=None):
    # it is risky to use a reference type as default argument
    # https://docs.python-guide.org/writing/gotchas/
    data = data or {}
    json_data = json.dumps(data)

    try:
        self.con.request("POST", REQUEST_URL + cmd, json_data, HEADERS)
    except:
        debug("An error occurred while carrying out the API request", 1)
        raise  # important

    response = self.con.getresponse()

    decoded_response = json.loads(response.read().decode())
    # if the 'ok' is not present in the response string you will get 'KeyError'
    # so use .get("ok") to get None instead
    if not decoded_response.get("ok"):
        debug("reponse: {}".format(decoded_response), 3)
        error = decoded_response.get("error_code")
        # what if the "error_code" not in the response, response could be empty ?
        raise ApiError(error)

    # again response could be malformed and you will get "KeyError" if
    # "result" is not present
    return decoded_response["result"]
...