�
Ϫ�f�z � �� � d Z ddlmZ ddlZddlZddlZddlmZ ddlm Z ddl
mZ ddlm
Z
ddlmZ dd lmZmZmZmZmZmZmZmZmZmZ dd
lmZmZ ddlmZm Z m!Z! ddl"m#Z#m$Z$m%Z% dd
l&m'Z' ddl(m)Z) ddl*m+Z+ ddl,m-Z-m.Z. ddl/m0Z0m1Z1 ddl2m3Z4m5Z6 ddl7m8Z8 ddl9m:Z: ddl;m<Z< ddl=m>Z? e?j� r
ddlAmBZBmCZCmDZDmEZE ndZ>e?Z>g d�ZG ededeHf �� ZIdZJdZKdZLdZMd ZNd!ZOd"ZPd#ZQd$ZRd%ZS G d&� d'e� ZT G d(� d)e� ZU G d*� d+e� ZV G d,� d-e� ZW G d.� d/eX� ZY G d0� d1eX� ZZ G d2� d3eY� Z[ G d4� d5eY� Z\ G d6� d7eY� Z] G d8� d9eY� Z^ G d:� d;eY� Z_ G d<� d=eY� Z` G d>� d?e`� Za G d@� dAeY� Zb G dB� dCeY� Zc G dD� dEeY� ZdeQeciZe G dF� dGeefeff � ZgegZh G dH� dIeg� Zi G dJ� dKeg� Zj eeV� G dL� dM� � Zk G dN� dOel� Zm eeW� G dP� dQem�R� � Zn eeW� G dS� dT� � Zog dU�ZpdV� Zq eeT� G dW� dX� � Zr G dY� dZer� Zs G d[� d\er� Zt G d]� d^er� Zu G d_� d`er� Zv G da� dbet� Zw G dc� ddew� Zx G de� dfer� Zy G dg� dher� Zz G di� djes� Z{ edk� Z| G dl� dmel� Z} G dn� doe}�R� Z~ G dp� dq� Z G dr� dseg� Z� G dt� duet� Z� G dv� dwe~� Z� G dx� dye~� Z� ee'� G dz� d{� � Z� eeU� G d|� d}e.e-e�� � Z� G d~� de�ekeneo� Z� G d�� d�� Z�e��j Z�e��j Z�d�� Z�d�� Z� G d�� d�er� Z� G d�� d�er� Z�y# eF$ r dZ>Y ���w xY w)�a�
This module implements AMP, the Asynchronous Messaging Protocol.
AMP is a protocol for sending multiple asynchronous request/response pairs over
the same connection. Requests and responses are both collections of key/value
pairs.
AMP is a very simple protocol which is not an application. This module is a
"protocol construction kit" of sorts; it attempts to be the simplest wire-level
implementation of Deferreds. AMP provides the following base-level features:
- Asynchronous request/response handling (hence the name)
- Requests and responses are both key/value pairs
- Binary transfer of all data: all data is length-prefixed. Your
application will never need to worry about quoting.
- Command dispatching (like HTTP Verbs): the protocol is extensible, and
multiple AMP sub-protocols can be grouped together easily.
The protocol implementation also provides a few additional features which are
not part of the core wire protocol, but are nevertheless very useful:
- Tight TLS integration, with an included StartTLS command.
- Handshaking to other protocols: because AMP has well-defined message
boundaries and maintains all incoming and outgoing requests for you, you
can start a connection over AMP and then switch to another protocol.
This makes it ideal for firewall-traversal applications where you may
have only one forwarded port but multiple applications that want to use
it.
Using AMP with Twisted is simple. Each message is a command, with a response.
You begin by defining a command type. Commands specify their input and output
in terms of the types that they expect to see in the request and response
key-value pairs. Here's an example of a command that adds two integers, 'a'
and 'b'::
class Sum(amp.Command):
arguments = [('a', amp.Integer()),
('b', amp.Integer())]
response = [('total', amp.Integer())]
Once you have specified a command, you need to make it part of a protocol, and
define a responder for it. Here's a 'JustSum' protocol that includes a
responder for our 'Sum' command::
class JustSum(amp.AMP):
def sum(self, a, b):
total = a + b
print 'Did a sum: %d + %d = %d' % (a, b, total)
return {'total': total}
Sum.responder(sum)
Later, when you want to actually do a sum, the following expression will return
a L{Deferred} which will fire with the result::
ClientCreator(reactor, amp.AMP).connectTCP(...).addCallback(
lambda p: p.callRemote(Sum, a=13, b=81)).addCallback(
lambda result: result['total'])
Command responders may also return Deferreds, causing the response to be
sent only once the Deferred fires::
class DelayedSum(amp.AMP):
def slowSum(self, a, b):
total = a + b
result = defer.Deferred()
reactor.callLater(3, result.callback, {'total': total})
return result
Sum.responder(slowSum)
This is transparent to the caller.
You can also define the propagation of specific errors in AMP. For example,
for the slightly more complicated case of division, we might have to deal with
division by zero::
class Divide(amp.Command):
arguments = [('numerator', amp.Integer()),
('denominator', amp.Integer())]
response = [('result', amp.Float())]
errors = {ZeroDivisionError: 'ZERO_DIVISION'}
The 'errors' mapping here tells AMP that if a responder to Divide emits a
L{ZeroDivisionError}, then the other side should be informed that an error of
the type 'ZERO_DIVISION' has occurred. Writing a responder which takes
advantage of this is very simple - just raise your exception normally::
class JustDivide(amp.AMP):
def divide(self, numerator, denominator):
result = numerator / denominator
print 'Divided: %d / %d = %d' % (numerator, denominator, total)
return {'result': result}
Divide.responder(divide)
On the client side, the errors mapping will be used to determine what the
'ZERO_DIVISION' error means, and translated into an asynchronous exception,
which can be handled normally as any L{Deferred} would be::
def trapZero(result):
result.trap(ZeroDivisionError)
print "Divided by zero: returning INF"
return 1e1000
ClientCreator(reactor, amp.AMP).connectTCP(...).addCallback(
lambda p: p.callRemote(Divide, numerator=1234,
denominator=0)
).addErrback(trapZero)
For a complete, runnable example of both of these commands, see the files in
the Twisted repository::
doc/core/examples/ampserver.py
doc/core/examples/ampclient.py
On the wire, AMP is a protocol which uses 2-byte lengths to prefix keys and
values, and empty keys to separate messages::
<2-byte length><key><2-byte length><value>
<2-byte length><key><2-byte length><value>
...
<2-byte length><key><2-byte length><value>
<NUL><NUL> # Empty Key == End of Message
And so on. Because it's tedious to refer to lengths and NULs constantly, the
documentation will refer to packets as if they were newline delimited, like
so::
C: _command: sum
C: _ask: ef639e5c892ccb54
C: a: 13
C: b: 81
S: _answer: ef639e5c892ccb54
S: total: 94
Notes:
In general, the order of keys is arbitrary. Specific uses of AMP may impose an
ordering requirement, but unless this is specified explicitly, any ordering may
be generated and any ordering must be accepted. This applies to the
command-related keys I{_command} and I{_ask} as well as any other keys.
Values are limited to the maximum encodable size in a 16-bit length, 65535
bytes.
Keys are limited to the maximum encodable size in a 8-bit length, 255 bytes.
Note that we still use 2-byte lengths to encode keys. This small redundancy
has several features:
- If an implementation becomes confused and starts emitting corrupt data,
or gets keys confused with values, many common errors will be signalled
immediately instead of delivering obviously corrupt packets.
- A single NUL will separate every key, and a double NUL separates
messages. This provides some redundancy when debugging traffic dumps.
- NULs will be present at regular intervals along the protocol, providing
some padding for otherwise braindead C implementations of the protocol,
so that <stdio.h> string functions will see the NUL and stop.
- This makes it possible to run an AMP server on a port also used by a
plain-text protocol, and easily distinguish between non-AMP clients (like
web browsers) which issue non-NUL as the first byte, and AMP clients,
which always issue NUL as the first byte.
@var MAX_VALUE_LENGTH: The maximum length of a message.
@type MAX_VALUE_LENGTH: L{int}
@var ASK: Marker for an Ask packet.
@type ASK: L{bytes}
@var ANSWER: Marker for an Answer packet.
@type ANSWER: L{bytes}
@var COMMAND: Marker for a Command packet.
@type COMMAND: L{bytes}
@var ERROR: Marker for an AMP box of error type.
@type ERROR: L{bytes}
@var ERROR_CODE: Marker for an AMP box containing the code of an error.
@type ERROR_CODE: L{bytes}
@var ERROR_DESCRIPTION: Marker for an AMP box containing the description of the
error.
@type ERROR_DESCRIPTION: L{bytes}
� )�annotationsN)�partial)�BytesIO)�count)�pack)�
MethodType)
�Any�Callable�ClassVar�Dict�List�Optional�Tuple�Type�TypeVar�Union)� Interface�implementer)�Deferred�fail�
maybeDeferred)�ConnectionClosed�ConnectionLost�PeerVerifyError)�IFileDescriptorReceiver)�CONNECTION_LOST)�Protocol)�Int16StringReceiver�StatefulStringProtocol)�filepath�log)�UTC�FixedOffsetTimeZone)�nativeString)�Failure)�accumulateClassDict)�ssl)�DN�Certificate�CertificateOptions�KeyPair)5�AMP�ANSWER�ASK�AmpBox�AmpError�AmpList�Argument�BadLocalReturn�BinaryBoxProtocol�Boolean�Box�
BoxDispatcher�COMMAND�Command�CommandLocator�Decimal�
Descriptor�ERROR�
ERROR_CODE�ERROR_DESCRIPTION�Float�
IArgumentType�IBoxReceiver�
IBoxSender�IResponderLocator�IncompatibleVersions�Integer�InvalidSignature�ListOf�MAX_KEY_LENGTH�MAX_VALUE_LENGTH�MalformedAmpBox�NoEmptyBoxes�
OnlyOneTLS�PROTOCOL_ERRORS�PYTHON_KEYWORDS�Path�ProtocolSwitchCommand�ProtocolSwitched�QuitBox�RemoteAmpError�SimpleStringLocator�StartTLS�String�TooLong�UNHANDLED_ERROR_CODE�UNKNOWN_ERROR_CODE�UnhandledCommand�utc�Unicode�UnknownRemoteError�parse�parseString�_T_Callable.)�bounds _asks _answers _commands _errors _error_codes _error_descriptions UNKNOWNs UNHANDLED� � c � � e Zd ZdZd� Zd� Zy)rA z�
An L{IArgumentType} can serialize a Python object into an AMP box and
deserialize information from an AMP box back into a Python object.
@since: 9.0
c � � y)a
Given an argument name and an AMP box containing serialized values,
extract one or more Python objects and add them to the C{objects}
dictionary.
@param name: The name associated with this argument. Most commonly
this is the key which can be used to find a serialized value in
C{strings}.
@type name: C{bytes}
@param strings: The AMP box from which to extract one or more
values.
@type strings: C{dict}
@param objects: The output dictionary to populate with the value for
this argument. The key used will be derived from C{name}. It may
differ; in Python 3, for example, the key will be a Unicode/native
string. See L{_wireNameToPythonIdentifier}.
@type objects: C{dict}
@param proto: The protocol instance which received the AMP box being
interpreted. Most likely this is an instance of L{AMP}, but
this is not guaranteed.
@return: L{None}
N� ��name�strings�objects�protos �7/usr/lib/python3/dist-packages/twisted/protocols/amp.py�fromBoxzIArgumentType.fromBoxI � � � c � � y)a=
Given an argument name and a dictionary containing structured Python
objects, serialize values into one or more strings and add them to
the C{strings} dictionary.
@param name: The name associated with this argument. Most commonly
this is the key in C{strings} to associate with a C{bytes} giving
the serialized form of that object.
@type name: C{bytes}
@param strings: The AMP box into which to insert one or more strings.
@type strings: C{dict}
@param objects: The input dictionary from which to extract Python
objects to serialize. The key used will be derived from C{name}.
It may differ; in Python 3, for example, the key will be a
Unicode/native string. See L{_wireNameToPythonIdentifier}.
@type objects: C{dict}
@param proto: The protocol instance which will send the AMP box once
it is fully populated. Most likely this is an instance of
L{AMP}, but this is not guaranteed.
@return: L{None}
Nrg rh s rm �toBoxzIArgumentType.toBoxe ro rp N)�__name__�
__module__�__qualname__�__doc__rn rr rg rp rm rA rA A s � ���8rp rA c � � e Zd ZdZd� Zd� Zy)rC z7
A transport which can send L{AmpBox} objects.
c � � y)z�
Send an L{AmpBox}.
@raise ProtocolSwitched: if the underlying protocol has been
switched.
@raise ConnectionLost: if the underlying connection has already been
lost.
Nrg ��boxs rm �sendBoxzIBoxSender.sendBox� ro rp c � � y)z�
An unhandled error occurred in response to a box. Log it
appropriately.
@param failure: a L{Failure} describing the error that occurred.
Nrg )�failures rm �unhandledErrorzIBoxSender.unhandledError� ro rp N)rs rt ru rv r{ r~ rg rp rm rC rC � s � �� �rp rC c �" |