Edit File by line
/home/barbar84/www/wp-conte.../plugins/sujqvwi/AnonR/smanonr..../lib64/python3....
File: asynchat.py
# -*- Mode: Python; tab-width: 4 -*-
[0] Fix | Delete
# Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
[1] Fix | Delete
# Author: Sam Rushing <rushing@nightmare.com>
[2] Fix | Delete
[3] Fix | Delete
# ======================================================================
[4] Fix | Delete
# Copyright 1996 by Sam Rushing
[5] Fix | Delete
#
[6] Fix | Delete
# All Rights Reserved
[7] Fix | Delete
#
[8] Fix | Delete
# Permission to use, copy, modify, and distribute this software and
[9] Fix | Delete
# its documentation for any purpose and without fee is hereby
[10] Fix | Delete
# granted, provided that the above copyright notice appear in all
[11] Fix | Delete
# copies and that both that copyright notice and this permission
[12] Fix | Delete
# notice appear in supporting documentation, and that the name of Sam
[13] Fix | Delete
# Rushing not be used in advertising or publicity pertaining to
[14] Fix | Delete
# distribution of the software without specific, written prior
[15] Fix | Delete
# permission.
[16] Fix | Delete
#
[17] Fix | Delete
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
[18] Fix | Delete
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
[19] Fix | Delete
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
[20] Fix | Delete
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
[21] Fix | Delete
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
[22] Fix | Delete
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
[23] Fix | Delete
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
[24] Fix | Delete
# ======================================================================
[25] Fix | Delete
[26] Fix | Delete
r"""A class supporting chat-style (command/response) protocols.
[27] Fix | Delete
[28] Fix | Delete
This class adds support for 'chat' style protocols - where one side
[29] Fix | Delete
sends a 'command', and the other sends a response (examples would be
[30] Fix | Delete
the common internet protocols - smtp, nntp, ftp, etc..).
[31] Fix | Delete
[32] Fix | Delete
The handle_read() method looks at the input stream for the current
[33] Fix | Delete
'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n'
[34] Fix | Delete
for multi-line output), calling self.found_terminator() on its
[35] Fix | Delete
receipt.
[36] Fix | Delete
[37] Fix | Delete
for example:
[38] Fix | Delete
Say you build an async nntp client using this class. At the start
[39] Fix | Delete
of the connection, you'll have self.terminator set to '\r\n', in
[40] Fix | Delete
order to process the single-line greeting. Just before issuing a
[41] Fix | Delete
'LIST' command you'll set it to '\r\n.\r\n'. The output of the LIST
[42] Fix | Delete
command will be accumulated (using your own 'collect_incoming_data'
[43] Fix | Delete
method) up to the terminator, and then control will be returned to
[44] Fix | Delete
you - by calling your self.found_terminator() method.
[45] Fix | Delete
"""
[46] Fix | Delete
import asyncore
[47] Fix | Delete
from collections import deque
[48] Fix | Delete
[49] Fix | Delete
[50] Fix | Delete
class async_chat(asyncore.dispatcher):
[51] Fix | Delete
"""This is an abstract class. You must derive from this class, and add
[52] Fix | Delete
the two methods collect_incoming_data() and found_terminator()"""
[53] Fix | Delete
[54] Fix | Delete
# these are overridable defaults
[55] Fix | Delete
[56] Fix | Delete
ac_in_buffer_size = 65536
[57] Fix | Delete
ac_out_buffer_size = 65536
[58] Fix | Delete
[59] Fix | Delete
# we don't want to enable the use of encoding by default, because that is a
[60] Fix | Delete
# sign of an application bug that we don't want to pass silently
[61] Fix | Delete
[62] Fix | Delete
use_encoding = 0
[63] Fix | Delete
encoding = 'latin-1'
[64] Fix | Delete
[65] Fix | Delete
def __init__(self, sock=None, map=None):
[66] Fix | Delete
# for string terminator matching
[67] Fix | Delete
self.ac_in_buffer = b''
[68] Fix | Delete
[69] Fix | Delete
# we use a list here rather than io.BytesIO for a few reasons...
[70] Fix | Delete
# del lst[:] is faster than bio.truncate(0)
[71] Fix | Delete
# lst = [] is faster than bio.truncate(0)
[72] Fix | Delete
self.incoming = []
[73] Fix | Delete
[74] Fix | Delete
# we toss the use of the "simple producer" and replace it with
[75] Fix | Delete
# a pure deque, which the original fifo was a wrapping of
[76] Fix | Delete
self.producer_fifo = deque()
[77] Fix | Delete
asyncore.dispatcher.__init__(self, sock, map)
[78] Fix | Delete
[79] Fix | Delete
def collect_incoming_data(self, data):
[80] Fix | Delete
raise NotImplementedError("must be implemented in subclass")
[81] Fix | Delete
[82] Fix | Delete
def _collect_incoming_data(self, data):
[83] Fix | Delete
self.incoming.append(data)
[84] Fix | Delete
[85] Fix | Delete
def _get_data(self):
[86] Fix | Delete
d = b''.join(self.incoming)
[87] Fix | Delete
del self.incoming[:]
[88] Fix | Delete
return d
[89] Fix | Delete
[90] Fix | Delete
def found_terminator(self):
[91] Fix | Delete
raise NotImplementedError("must be implemented in subclass")
[92] Fix | Delete
[93] Fix | Delete
def set_terminator(self, term):
[94] Fix | Delete
"""Set the input delimiter.
[95] Fix | Delete
[96] Fix | Delete
Can be a fixed string of any length, an integer, or None.
[97] Fix | Delete
"""
[98] Fix | Delete
if isinstance(term, str) and self.use_encoding:
[99] Fix | Delete
term = bytes(term, self.encoding)
[100] Fix | Delete
elif isinstance(term, int) and term < 0:
[101] Fix | Delete
raise ValueError('the number of received bytes must be positive')
[102] Fix | Delete
self.terminator = term
[103] Fix | Delete
[104] Fix | Delete
def get_terminator(self):
[105] Fix | Delete
return self.terminator
[106] Fix | Delete
[107] Fix | Delete
# grab some more data from the socket,
[108] Fix | Delete
# throw it to the collector method,
[109] Fix | Delete
# check for the terminator,
[110] Fix | Delete
# if found, transition to the next state.
[111] Fix | Delete
[112] Fix | Delete
def handle_read(self):
[113] Fix | Delete
[114] Fix | Delete
try:
[115] Fix | Delete
data = self.recv(self.ac_in_buffer_size)
[116] Fix | Delete
except BlockingIOError:
[117] Fix | Delete
return
[118] Fix | Delete
except OSError as why:
[119] Fix | Delete
self.handle_error()
[120] Fix | Delete
return
[121] Fix | Delete
[122] Fix | Delete
if isinstance(data, str) and self.use_encoding:
[123] Fix | Delete
data = bytes(str, self.encoding)
[124] Fix | Delete
self.ac_in_buffer = self.ac_in_buffer + data
[125] Fix | Delete
[126] Fix | Delete
# Continue to search for self.terminator in self.ac_in_buffer,
[127] Fix | Delete
# while calling self.collect_incoming_data. The while loop
[128] Fix | Delete
# is necessary because we might read several data+terminator
[129] Fix | Delete
# combos with a single recv(4096).
[130] Fix | Delete
[131] Fix | Delete
while self.ac_in_buffer:
[132] Fix | Delete
lb = len(self.ac_in_buffer)
[133] Fix | Delete
terminator = self.get_terminator()
[134] Fix | Delete
if not terminator:
[135] Fix | Delete
# no terminator, collect it all
[136] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer)
[137] Fix | Delete
self.ac_in_buffer = b''
[138] Fix | Delete
elif isinstance(terminator, int):
[139] Fix | Delete
# numeric terminator
[140] Fix | Delete
n = terminator
[141] Fix | Delete
if lb < n:
[142] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer)
[143] Fix | Delete
self.ac_in_buffer = b''
[144] Fix | Delete
self.terminator = self.terminator - lb
[145] Fix | Delete
else:
[146] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer[:n])
[147] Fix | Delete
self.ac_in_buffer = self.ac_in_buffer[n:]
[148] Fix | Delete
self.terminator = 0
[149] Fix | Delete
self.found_terminator()
[150] Fix | Delete
else:
[151] Fix | Delete
# 3 cases:
[152] Fix | Delete
# 1) end of buffer matches terminator exactly:
[153] Fix | Delete
# collect data, transition
[154] Fix | Delete
# 2) end of buffer matches some prefix:
[155] Fix | Delete
# collect data to the prefix
[156] Fix | Delete
# 3) end of buffer does not match any prefix:
[157] Fix | Delete
# collect data
[158] Fix | Delete
terminator_len = len(terminator)
[159] Fix | Delete
index = self.ac_in_buffer.find(terminator)
[160] Fix | Delete
if index != -1:
[161] Fix | Delete
# we found the terminator
[162] Fix | Delete
if index > 0:
[163] Fix | Delete
# don't bother reporting the empty string
[164] Fix | Delete
# (source of subtle bugs)
[165] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer[:index])
[166] Fix | Delete
self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
[167] Fix | Delete
# This does the Right Thing if the terminator
[168] Fix | Delete
# is changed here.
[169] Fix | Delete
self.found_terminator()
[170] Fix | Delete
else:
[171] Fix | Delete
# check for a prefix of the terminator
[172] Fix | Delete
index = find_prefix_at_end(self.ac_in_buffer, terminator)
[173] Fix | Delete
if index:
[174] Fix | Delete
if index != lb:
[175] Fix | Delete
# we found a prefix, collect up to the prefix
[176] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer[:-index])
[177] Fix | Delete
self.ac_in_buffer = self.ac_in_buffer[-index:]
[178] Fix | Delete
break
[179] Fix | Delete
else:
[180] Fix | Delete
# no prefix, collect it all
[181] Fix | Delete
self.collect_incoming_data(self.ac_in_buffer)
[182] Fix | Delete
self.ac_in_buffer = b''
[183] Fix | Delete
[184] Fix | Delete
def handle_write(self):
[185] Fix | Delete
self.initiate_send()
[186] Fix | Delete
[187] Fix | Delete
def handle_close(self):
[188] Fix | Delete
self.close()
[189] Fix | Delete
[190] Fix | Delete
def push(self, data):
[191] Fix | Delete
if not isinstance(data, (bytes, bytearray, memoryview)):
[192] Fix | Delete
raise TypeError('data argument must be byte-ish (%r)',
[193] Fix | Delete
type(data))
[194] Fix | Delete
sabs = self.ac_out_buffer_size
[195] Fix | Delete
if len(data) > sabs:
[196] Fix | Delete
for i in range(0, len(data), sabs):
[197] Fix | Delete
self.producer_fifo.append(data[i:i+sabs])
[198] Fix | Delete
else:
[199] Fix | Delete
self.producer_fifo.append(data)
[200] Fix | Delete
self.initiate_send()
[201] Fix | Delete
[202] Fix | Delete
def push_with_producer(self, producer):
[203] Fix | Delete
self.producer_fifo.append(producer)
[204] Fix | Delete
self.initiate_send()
[205] Fix | Delete
[206] Fix | Delete
def readable(self):
[207] Fix | Delete
"predicate for inclusion in the readable for select()"
[208] Fix | Delete
# cannot use the old predicate, it violates the claim of the
[209] Fix | Delete
# set_terminator method.
[210] Fix | Delete
[211] Fix | Delete
# return (len(self.ac_in_buffer) <= self.ac_in_buffer_size)
[212] Fix | Delete
return 1
[213] Fix | Delete
[214] Fix | Delete
def writable(self):
[215] Fix | Delete
"predicate for inclusion in the writable for select()"
[216] Fix | Delete
return self.producer_fifo or (not self.connected)
[217] Fix | Delete
[218] Fix | Delete
def close_when_done(self):
[219] Fix | Delete
"automatically close this channel once the outgoing queue is empty"
[220] Fix | Delete
self.producer_fifo.append(None)
[221] Fix | Delete
[222] Fix | Delete
def initiate_send(self):
[223] Fix | Delete
while self.producer_fifo and self.connected:
[224] Fix | Delete
first = self.producer_fifo[0]
[225] Fix | Delete
# handle empty string/buffer or None entry
[226] Fix | Delete
if not first:
[227] Fix | Delete
del self.producer_fifo[0]
[228] Fix | Delete
if first is None:
[229] Fix | Delete
self.handle_close()
[230] Fix | Delete
return
[231] Fix | Delete
[232] Fix | Delete
# handle classic producer behavior
[233] Fix | Delete
obs = self.ac_out_buffer_size
[234] Fix | Delete
try:
[235] Fix | Delete
data = first[:obs]
[236] Fix | Delete
except TypeError:
[237] Fix | Delete
data = first.more()
[238] Fix | Delete
if data:
[239] Fix | Delete
self.producer_fifo.appendleft(data)
[240] Fix | Delete
else:
[241] Fix | Delete
del self.producer_fifo[0]
[242] Fix | Delete
continue
[243] Fix | Delete
[244] Fix | Delete
if isinstance(data, str) and self.use_encoding:
[245] Fix | Delete
data = bytes(data, self.encoding)
[246] Fix | Delete
[247] Fix | Delete
# send the data
[248] Fix | Delete
try:
[249] Fix | Delete
num_sent = self.send(data)
[250] Fix | Delete
except OSError:
[251] Fix | Delete
self.handle_error()
[252] Fix | Delete
return
[253] Fix | Delete
[254] Fix | Delete
if num_sent:
[255] Fix | Delete
if num_sent < len(data) or obs < len(first):
[256] Fix | Delete
self.producer_fifo[0] = first[num_sent:]
[257] Fix | Delete
else:
[258] Fix | Delete
del self.producer_fifo[0]
[259] Fix | Delete
# we tried to send some actual data
[260] Fix | Delete
return
[261] Fix | Delete
[262] Fix | Delete
def discard_buffers(self):
[263] Fix | Delete
# Emergencies only!
[264] Fix | Delete
self.ac_in_buffer = b''
[265] Fix | Delete
del self.incoming[:]
[266] Fix | Delete
self.producer_fifo.clear()
[267] Fix | Delete
[268] Fix | Delete
[269] Fix | Delete
class simple_producer:
[270] Fix | Delete
[271] Fix | Delete
def __init__(self, data, buffer_size=512):
[272] Fix | Delete
self.data = data
[273] Fix | Delete
self.buffer_size = buffer_size
[274] Fix | Delete
[275] Fix | Delete
def more(self):
[276] Fix | Delete
if len(self.data) > self.buffer_size:
[277] Fix | Delete
result = self.data[:self.buffer_size]
[278] Fix | Delete
self.data = self.data[self.buffer_size:]
[279] Fix | Delete
return result
[280] Fix | Delete
else:
[281] Fix | Delete
result = self.data
[282] Fix | Delete
self.data = b''
[283] Fix | Delete
return result
[284] Fix | Delete
[285] Fix | Delete
[286] Fix | Delete
# Given 'haystack', see if any prefix of 'needle' is at its end. This
[287] Fix | Delete
# assumes an exact match has already been checked. Return the number of
[288] Fix | Delete
# characters matched.
[289] Fix | Delete
# for example:
[290] Fix | Delete
# f_p_a_e("qwerty\r", "\r\n") => 1
[291] Fix | Delete
# f_p_a_e("qwertydkjf", "\r\n") => 0
[292] Fix | Delete
# f_p_a_e("qwerty\r\n", "\r\n") => <undefined>
[293] Fix | Delete
[294] Fix | Delete
# this could maybe be made faster with a computed regex?
[295] Fix | Delete
# [answer: no; circa Python-2.0, Jan 2001]
[296] Fix | Delete
# new python: 28961/s
[297] Fix | Delete
# old python: 18307/s
[298] Fix | Delete
# re: 12820/s
[299] Fix | Delete
# regex: 14035/s
[300] Fix | Delete
[301] Fix | Delete
def find_prefix_at_end(haystack, needle):
[302] Fix | Delete
l = len(needle) - 1
[303] Fix | Delete
while l and not haystack.endswith(needle[:l]):
[304] Fix | Delete
l -= 1
[305] Fix | Delete
return l
[306] Fix | Delete
[307] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function