# `asynchat` — Gestor de comandos/respuestas en sockets asíncronos

Código fuente: Lib/asynchat.py

Obsoleto desde la versión 3.6: `asynchat` will be removed in Python 3.12 (see PEP 594 for details). Please use `asyncio` instead.

Nota

Este módulo existe únicamente por motivos de retrocompatibilidad. Para nuevo código, es recomendable usar `asyncio`.

Este módulo se construye en la infraestructura de `asyncore`, simplificando los clientes y servidores asíncronos y facilitando la gestión de protocolos cuyos elementos son terminados por cadenas de texto arbitrarias, o que son de longitud variable. `asynchat` define la clase abstracta `async_chat` de la que se debe heredar, implementado los métodos `collect_incoming_data()` y `found_terminator()`. Utiliza el mismo bucle asíncrono que `asyncore`, y los dos tipos de canal, `asyncore.dispatcher` y `asynchat.async_chat`, se pueden mezclar libremente en el mapa de canal. Normalmente un canal de servidor `asyncore.dispatcher` genera nuevos objetos de canal `asynchat.async_chat`, al recibir peticiones de conexión entrantes.

class `asynchat.``async_chat`

  Esta clase es una subclase abstracta de `asyncore.dispatcher`. Para el uso práctico del código se debe heredar `async_chat`, definiendo los métodos significativos `collect_incoming_data()` y `found_terminator()`. Los métodos de `asyncore.dispatcher` se pueden utilizar, aunque no todos tienen sentido en un contexto de mensaje/respuesta.

  Al igual que `asyncore.dispatcher`, `async_chat` define una serie de eventos generados por un análisis sobre las condiciones de _socket_, tras una llamada a `select()`. Una vez que el bucle de _polling_ haya sido iniciado, los métodos de los objetos `async_chat` son llamados por el _framework_ que procesa los eventos, sin que tengamos que programar ninguna acción a mayores.

  Se pueden modificar dos atributos de clase, para mejorar el rendimiento o incluso hasta para ahorrar memoria.

  `ac_in_buffer_size`

    El tamaño del _buffer_ de entrada asíncrona (por defecto `4096`).

  `ac_out_buffer_size`

    El tamaño del _buffer_ de salida asíncrona (por defecto `4096`).

  Al contrario que `asyncore.dispatcher`, `async_chat` permite definir una cola FIFO de productores (producers). Un productor necesita tener un solo método, `more()`, que debe devolver los datos que se vayan a transmitir en el canal. Cuando el método `more()` devuelve un objeto bytes vacío, significa que el productor ya se ha agotado (por ejemplo, que no contiene más datos). En este punto, el objeto `async_chat` elimina el productor de la cola y empieza a usar el siguiente productor, si existiese. Cuando la cola de productores está vacía, el método `handle_write()` no hace nada. El método `set_terminator()` de los objetos de canal se utiliza para describir cómo reconocer, en una transmisión entrante desde el punto remoto, el final de esta transmisión o un punto de ruptura importante en la misma.

  Para construir una subclase funcional de `async_chat`, los métodos de entrada `collect_incoming_data()` and `found_terminator()` deben tratar los datos que el canal recibe asíncronamente. Los métodos se describen a continuación.

`async_chat.``close_when_done`()

  Añade un `None` en la cola de productores. Cuando este productor se extrae de la cola, hace que el canal se cierre.

`async_chat.``collect_incoming_data`(data)

  Llamado con data, conteniendo una cantidad arbitraria de datos recibidos. El método por defecto, que debe ser reemplazado, lanza una excepción `NotImplementedError`.

`async_chat.``discard_buffers`()

  En situaciones de emergencia, este método descarta cualquier dato albergado en los búfers de entrada y/o salida y en la cola del productor.

`async_chat.``found_terminator`()

  Llamado cuando el flujo de datos de entrada coincide con la condición de finalización establecida por `set_terminator()`. El método por defecto, que debe ser reemplazado, lanza una excepción `NotImplementedError`. Los datos de entrada en búfer deberían estar disponibles a través de un atributo de instancia.

`async_chat.``get_terminator`()

  Retorna el terminador actual del canal.

`async_chat.``push`(data)

  Añade datos en la cola del canal para asegurarse de su transmisión. Esto es todo lo que se necesita hacer para que el canal envíe los datos a la red, aunque es posible usar productores personalizados en esquemas más complejos para implementar características como encriptación o fragmentación.

`async_chat.``push_with_producer`(producer)

  Obtiene un objeto productor y lo añade a la cola de productores asociada al canal. Cuando todos los productores añadidos actualmente han sido agotados, el canal consumirá los datos de este productor llamando al método `more()`, y enviando los datos al punto remoto.

`async_chat.``set_terminator`(term)

  Establece la condición de finalización que será reconocida en este canal. `term` puede ser uno de los tres tipos de valores posibles, correspondientes a tres formas diferentes de tratar los datos de protocolo entrantes.

  término | Descripción
  --- | ---
  string | Llamará a `found_terminator()` cuando la cadena de caracteres se encuentre en el flujo de datos de entrada
  integer | Llamará a `found_terminator()` cuando el número de caracteres indicado se haya recibido
  `None` | El canal continúa recopilando datos indefinidamente

  Téngase en cuenta que cualquier dato posterior al terminador estará disponible para ser leído por el canal después de llamar a `found_terminator()`.

## Ejemplo de asynchat

El siguiente ejemplo parcial muestra cómo se pueden leer peticiones HTTP con `async_chat`. Un servidor web podría crear un objeto `http_request_handler` para cada conexión de cliente entrante. Téngase en cuenta que, inicialmente, el terminador del canal está configurado para detectar la línea vacía presente al final de las cabeceras HTTP, y una bandera indica que las cabeceras se están leyendo.

Una vez que las cabeceras se hayan leído, si la petición es de tipo POST (lo cual indica que hay más datos disponibles en el flujo de entrada), la cabecera `Content-Length:` se utiliza para establecer un terminador numérico para leer la cantidad de datos correcta en el canal.

El método `handle_request()` se llama una vez todas las entradas relevantes han sido serializadas (marshalled), tras establecer el terminador del canal a `None` para asegurarse de que cualquier dato extraño enviado por el cliente web es ignorado.

    import asynchat

    class http_request_handler(asynchat.async_chat):

        def __init__(self, sock, addr, sessions, log):
            asynchat.async_chat.__init__(self, sock=sock)
            self.addr = addr
            self.sessions = sessions
            self.ibuffer = []
            self.obuffer = b""
            self.set_terminator(b"\r\n\r\n")
            self.reading_headers = True
            self.handling = False
            self.cgi_data = None
            self.log = log

        def collect_incoming_data(self, data):
            """Buffer the data"""
            self.ibuffer.append(data)

        def found_terminator(self):
            if self.reading_headers:
                self.reading_headers = False
                self.parse_headers(b"".join(self.ibuffer))
                self.ibuffer = []
                if self.op.upper() == b"POST":
                    clen = self.headers.getheader("content-length")
                    self.set_terminator(int(clen))
                else:
                    self.handling = True
                    self.set_terminator(None)
                    self.handle_request()
            elif not self.handling:
                self.set_terminator(None)  # browsers sometimes over-send
                self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
                self.handling = True
                self.ibuffer = []
                self.handle_request()