10039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# -*- Mode: Python; tab-width: 4 -*-
2658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters#       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
3146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters#       Author: Sam Rushing <rushing@nightmare.com>
40039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
50039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# ======================================================================
60039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# Copyright 1996 by Sam Rushing
7146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters#
80039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum#                         All Rights Reserved
9146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters#
100039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# Permission to use, copy, modify, and distribute this software and
110039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# its documentation for any purpose and without fee is hereby
120039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# granted, provided that the above copyright notice appear in all
130039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# copies and that both that copyright notice and this permission
140039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# notice appear in supporting documentation, and that the name of Sam
150039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# Rushing not be used in advertising or publicity pertaining to
160039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# distribution of the software without specific, written prior
170039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# permission.
18146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters#
190039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
200039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
210039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
220039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
230039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
240039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
250039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
260039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# ======================================================================
270039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
28e4a1b6d7c4e96dd2af0541dd12444932260e2a66Guido van Rossumr"""A class supporting chat-style (command/response) protocols.
294b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum
304b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van RossumThis class adds support for 'chat' style protocols - where one side
314b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumsends a 'command', and the other sends a response (examples would be
324b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumthe common internet protocols - smtp, nntp, ftp, etc..).
334b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum
344b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van RossumThe handle_read() method looks at the input stream for the current
354b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n'
364b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumfor multi-line output), calling self.found_terminator() on its
374b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumreceipt.
384b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum
394b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumfor example:
404b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van RossumSay you build an async nntp client using this class.  At the start
414b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumof the connection, you'll have self.terminator set to '\r\n', in
424b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumorder to process the single-line greeting.  Just before issuing a
434b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum'LIST' command you'll set it to '\r\n.\r\n'.  The output of the LIST
444b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumcommand will be accumulated (using your own 'collect_incoming_data'
454b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossummethod) up to the terminator, and then control will be returned to
464b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossumyou - by calling your self.found_terminator() method.
474b8c6eaf8b287a27e0054cf6c751448b2077e83bGuido van Rossum"""
480039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossumimport asyncore
49ac093c6af04237deceedaab55d299819cbede7f9Raymond Hettingerfrom collections import deque
500039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
51d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
52fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinnerclass async_chat(asyncore.dispatcher):
53146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    """This is an abstract class.  You must derive from this class, and add
54146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    the two methods collect_incoming_data() and found_terminator()"""
55146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
56146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    # these are overridable defaults
57146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
58fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    ac_in_buffer_size = 65536
59fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    ac_out_buffer_size = 65536
60146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
61d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson    # we don't want to enable the use of encoding by default, because that is a
62d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson    # sign of an application bug that we don't want to pass silently
63d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
64fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    use_encoding = 0
65fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    encoding = 'latin-1'
66d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
67fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def __init__(self, sock=None, map=None):
68d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # for string terminator matching
69076da0957b1af50cccf40ec5c60742212b4a1f90Guido van Rossum        self.ac_in_buffer = b''
70d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
7150254c57cd7a562441b7ff385d59c0255301f3ffSerhiy Storchaka        # we use a list here rather than io.BytesIO for a few reasons...
7250254c57cd7a562441b7ff385d59c0255301f3ffSerhiy Storchaka        # del lst[:] is faster than bio.truncate(0)
7350254c57cd7a562441b7ff385d59c0255301f3ffSerhiy Storchaka        # lst = [] is faster than bio.truncate(0)
74d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.incoming = []
75d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
76d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # we toss the use of the "simple producer" and replace it with
77d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # a pure deque, which the original fifo was a wrapping of
78d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.producer_fifo = deque()
79fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner        asyncore.dispatcher.__init__(self, sock, map)
80146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
817dd5f3cf698a7ffb7e532719639c85f81a65dbe8Andrew M. Kuchling    def collect_incoming_data(self, data):
82ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter        raise NotImplementedError("must be implemented in subclass")
83863ac44b74cd66f7d289748816d65c65808c149bTim Peters
84d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson    def _collect_incoming_data(self, data):
85d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.incoming.append(data)
86d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
87d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson    def _get_data(self):
88d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        d = b''.join(self.incoming)
89d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        del self.incoming[:]
90d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        return d
91d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
927dd5f3cf698a7ffb7e532719639c85f81a65dbe8Andrew M. Kuchling    def found_terminator(self):
93ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter        raise NotImplementedError("must be implemented in subclass")
94863ac44b74cd66f7d289748816d65c65808c149bTim Peters
95fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def set_terminator(self, term):
96fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner        """Set the input delimiter.
97fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner
98fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner        Can be a fixed string of any length, an integer, or None.
99fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner        """
100d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        if isinstance(term, str) and self.use_encoding:
101d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            term = bytes(term, self.encoding)
102630a4f63c539345a6432d6177931b5fcc2f18aa7Victor Stinner        elif isinstance(term, int) and term < 0:
103630a4f63c539345a6432d6177931b5fcc2f18aa7Victor Stinner            raise ValueError('the number of received bytes must be positive')
104146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.terminator = term
105146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
106fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def get_terminator(self):
107146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        return self.terminator
108146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
109146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    # grab some more data from the socket,
110146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    # throw it to the collector method,
111146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    # check for the terminator,
112146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters    # if found, transition to the next state.
113146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
114fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def handle_read(self):
115146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
116146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        try:
117fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner            data = self.recv(self.ac_in_buffer_size)
11845cff66cf63593695ff5324d3765d8a1a1125adfVictor Stinner        except BlockingIOError:
11945cff66cf63593695ff5324d3765d8a1a1125adfVictor Stinner            return
1200832af6628ca5ac02d0226899725297dd508470bAndrew Svetlov        except OSError as why:
121146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            self.handle_error()
122146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            return
123146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
124d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        if isinstance(data, str) and self.use_encoding:
125d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            data = bytes(str, self.encoding)
126d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.ac_in_buffer = self.ac_in_buffer + data
127146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
128146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        # Continue to search for self.terminator in self.ac_in_buffer,
129146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        # while calling self.collect_incoming_data.  The while loop
130146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        # is necessary because we might read several data+terminator
131d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # combos with a single recv(4096).
132146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
133146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        while self.ac_in_buffer:
134146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            lb = len(self.ac_in_buffer)
135146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            terminator = self.get_terminator()
136ca69f0248c94a08f2077f8e17cf6ad556a2d9d16Andrew M. Kuchling            if not terminator:
137146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # no terminator, collect it all
138fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                self.collect_incoming_data(self.ac_in_buffer)
139806c2469cb2e16c6cfd73de9a216933bd55c131fGuido van Rossum                self.ac_in_buffer = b''
140d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            elif isinstance(terminator, int):
141146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # numeric terminator
142146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                n = terminator
143146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                if lb < n:
144fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                    self.collect_incoming_data(self.ac_in_buffer)
145076da0957b1af50cccf40ec5c60742212b4a1f90Guido van Rossum                    self.ac_in_buffer = b''
146146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.terminator = self.terminator - lb
147146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                else:
148fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                    self.collect_incoming_data(self.ac_in_buffer[:n])
149146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.ac_in_buffer = self.ac_in_buffer[n:]
150146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.terminator = 0
151146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.found_terminator()
152146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            else:
153146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # 3 cases:
154146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # 1) end of buffer matches terminator exactly:
155146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                #    collect data, transition
156146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # 2) end of buffer matches some prefix:
157146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                #    collect data to the prefix
158146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                # 3) end of buffer does not match any prefix:
159146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                #    collect data
160146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                terminator_len = len(terminator)
161b5d1392d9236eae981796cb7ae15d8ecd5035ac6Tim Peters                index = self.ac_in_buffer.find(terminator)
162146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                if index != -1:
163146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    # we found the terminator
164146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    if index > 0:
165fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                        # don't bother reporting the empty string
166fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                        # (source of subtle bugs)
167fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                        self.collect_incoming_data(self.ac_in_buffer[:index])
168146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
169fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                    # This does the Right Thing if the terminator
170fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                    # is changed here.
171146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    self.found_terminator()
172146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                else:
173146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    # check for a prefix of the terminator
174fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                    index = find_prefix_at_end(self.ac_in_buffer, terminator)
175146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    if index:
176146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                        if index != lb:
177146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                            # we found a prefix, collect up to the prefix
178fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                            self.collect_incoming_data(self.ac_in_buffer[:-index])
179146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                            self.ac_in_buffer = self.ac_in_buffer[-index:]
180146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                        break
181146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    else:
182146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                        # no prefix, collect it all
183fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner                        self.collect_incoming_data(self.ac_in_buffer)
184076da0957b1af50cccf40ec5c60742212b4a1f90Guido van Rossum                        self.ac_in_buffer = b''
185146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
186fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def handle_write(self):
187d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.initiate_send()
188146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
189fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def handle_close(self):
190146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.close()
191146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
192fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def push(self, data):
193d9e810a8701b92371232eece5896a799c33de505Victor Stinner        if not isinstance(data, (bytes, bytearray, memoryview)):
194d9e810a8701b92371232eece5896a799c33de505Victor Stinner            raise TypeError('data argument must be byte-ish (%r)',
195d9e810a8701b92371232eece5896a799c33de505Victor Stinner                            type(data))
196d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        sabs = self.ac_out_buffer_size
197d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        if len(data) > sabs:
198d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            for i in range(0, len(data), sabs):
199d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                self.producer_fifo.append(data[i:i+sabs])
200d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        else:
201d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            self.producer_fifo.append(data)
202146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.initiate_send()
203146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
204fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def push_with_producer(self, producer):
205d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.producer_fifo.append(producer)
206146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.initiate_send()
207146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
208fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def readable(self):
209146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        "predicate for inclusion in the readable for select()"
210d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # cannot use the old predicate, it violates the claim of the
211d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # set_terminator method.
212d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
213d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        # return (len(self.ac_in_buffer) <= self.ac_in_buffer_size)
214d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        return 1
215146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
216fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def writable(self):
217146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        "predicate for inclusion in the writable for select()"
218d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        return self.producer_fifo or (not self.connected)
219146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
220fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def close_when_done(self):
221146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        "automatically close this channel once the outgoing queue is empty"
222d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.producer_fifo.append(None)
223d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
224d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson    def initiate_send(self):
225d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        while self.producer_fifo and self.connected:
226d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            first = self.producer_fifo[0]
227d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            # handle empty string/buffer or None entry
228d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            if not first:
229d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                del self.producer_fifo[0]
230d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                if first is None:
231d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                    self.handle_close()
232146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                    return
233d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
234d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            # handle classic producer behavior
235d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            obs = self.ac_out_buffer_size
236d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            try:
237d9f38bc7043c6d94ea7d76249d48d18322b46e92Giampaolo Rodola'                data = first[:obs]
238d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            except TypeError:
239d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                data = first.more()
240146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                if data:
241d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                    self.producer_fifo.appendleft(data)
242146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                else:
243d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                    del self.producer_fifo[0]
244d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                continue
245146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
246d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            if isinstance(data, str) and self.use_encoding:
247d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                data = bytes(data, self.encoding)
248146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
249d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            # send the data
250146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            try:
251d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                num_sent = self.send(data)
2520832af6628ca5ac02d0226899725297dd508470bAndrew Svetlov            except OSError:
253146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                self.handle_error()
254146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters                return
255146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters
256d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            if num_sent:
257d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                if num_sent < len(data) or obs < len(first):
258d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                    self.producer_fifo[0] = first[num_sent:]
259d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                else:
260d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson                    del self.producer_fifo[0]
261d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            # we tried to send some actual data
262d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson            return
263d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson
264fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def discard_buffers(self):
265146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        # Emergencies only!
266076da0957b1af50cccf40ec5c60742212b4a1f90Guido van Rossum        self.ac_in_buffer = b''
267d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        del self.incoming[:]
268d74900ebb5a22b387b49684990da1925e1d6bdc9Josiah Carlson        self.producer_fifo.clear()
269da85a272a6216cf3583db8e25155cb3a7168b081Andrew M. Kuchling
270fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner
2710039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossumclass simple_producer:
272a8d0f4fd2d10a1f5e05d31e048e52a1192d84321Guido van Rossum
273fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def __init__(self, data, buffer_size=512):
274146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.data = data
275146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        self.buffer_size = buffer_size
2760039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
277fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner    def more(self):
278fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner        if len(self.data) > self.buffer_size:
279146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            result = self.data[:self.buffer_size]
280146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            self.data = self.data[self.buffer_size:]
281146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            return result
282146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters        else:
283146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            result = self.data
284076da0957b1af50cccf40ec5c60742212b4a1f90Guido van Rossum            self.data = b''
285146965abf2378cdb248cead43a613fb81aa7d1a4Tim Peters            return result
2860039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
287fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner
2880039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# Given 'haystack', see if any prefix of 'needle' is at its end.  This
2890039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# assumes an exact match has already been checked.  Return the number of
2900039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# characters matched.
2910039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# for example:
292fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner# f_p_a_e("qwerty\r", "\r\n") => 1
293fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner# f_p_a_e("qwertydkjf", "\r\n") => 0
294fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinner# f_p_a_e("qwerty\r\n", "\r\n") => <undefined>
2950039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
2960039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum# this could maybe be made faster with a computed regex?
297d305f515f9c4e3cedb3e47ec2711f1dd2a99ceb5Andrew M. Kuchling# [answer: no; circa Python-2.0, Jan 2001]
298c63a396c5fc55beeaf3c973f137e160fc844d5a6Andrew M. Kuchling# new python:   28961/s
299c63a396c5fc55beeaf3c973f137e160fc844d5a6Andrew M. Kuchling# old python:   18307/s
300d305f515f9c4e3cedb3e47ec2711f1dd2a99ceb5Andrew M. Kuchling# re:        12820/s
301d305f515f9c4e3cedb3e47ec2711f1dd2a99ceb5Andrew M. Kuchling# regex:     14035/s
3020039d7b4e6f07411f788dbcb52cd05d26fc7fec5Guido van Rossum
303fd5d1b51d64280d938b7cdc9d78c632b21b45dffVictor Stinnerdef find_prefix_at_end(haystack, needle):
304863ac44b74cd66f7d289748816d65c65808c149bTim Peters    l = len(needle) - 1
305863ac44b74cd66f7d289748816d65c65808c149bTim Peters    while l and not haystack.endswith(needle[:l]):
306863ac44b74cd66f7d289748816d65c65808c149bTim Peters        l -= 1
307863ac44b74cd66f7d289748816d65c65808c149bTim Peters    return l
308