~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/amqplib/client_0_8/connection.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
AMQP 0-8 Connections
 
3
 
 
4
"""
 
5
# Copyright (C) 2007-2008 Barry Pederson <bp@barryp.org>
 
6
#
 
7
# This library is free software; you can redistribute it and/or
 
8
# modify it under the terms of the GNU Lesser General Public
 
9
# License as published by the Free Software Foundation; either
 
10
# version 2.1 of the License, or (at your option) any later version.
 
11
#
 
12
# This library is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
# Lesser General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU Lesser General Public
 
18
# License along with this library; if not, write to the Free Software
 
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 
20
 
 
21
import logging
 
22
 
 
23
from abstract_channel import AbstractChannel
 
24
from channel import Channel
 
25
from exceptions import *
 
26
from method_framing import MethodReader, MethodWriter
 
27
from serialization import AMQPReader, AMQPWriter
 
28
from transport import create_transport
 
29
 
 
30
__all__ =  [
 
31
            'Connection',
 
32
           ]
 
33
 
 
34
#
 
35
# Client property info that gets sent to the server on connection startup
 
36
#
 
37
LIBRARY_PROPERTIES = {
 
38
    'library': 'Python amqplib',
 
39
    'library_version': '0.6.1',
 
40
    }
 
41
 
 
42
AMQP_LOGGER = logging.getLogger('amqplib')
 
43
 
 
44
 
 
45
class Connection(AbstractChannel):
 
46
    """
 
47
    The connection class provides methods for a client to establish a
 
48
    network connection to a server, and for both peers to operate the
 
49
    connection thereafter.
 
50
 
 
51
    GRAMMAR:
 
52
 
 
53
        connection          = open-connection *use-connection close-connection
 
54
        open-connection     = C:protocol-header
 
55
                              S:START C:START-OK
 
56
                              *challenge
 
57
                              S:TUNE C:TUNE-OK
 
58
                              C:OPEN S:OPEN-OK | S:REDIRECT
 
59
        challenge           = S:SECURE C:SECURE-OK
 
60
        use-connection      = *channel
 
61
        close-connection    = C:CLOSE S:CLOSE-OK
 
62
                            / S:CLOSE C:CLOSE-OK
 
63
 
 
64
    """
 
65
    def __init__(self,
 
66
        host='localhost',
 
67
        userid='guest',
 
68
        password='guest',
 
69
        login_method='AMQPLAIN',
 
70
        login_response=None,
 
71
        virtual_host='/',
 
72
        locale='en_US',
 
73
        client_properties=None,
 
74
        ssl=False,
 
75
        insist=False,
 
76
        connect_timeout=None,
 
77
        **kwargs):
 
78
        """
 
79
        Create a connection to the specified host, which should be
 
80
        a 'host[:port]', such as 'localhost', or '1.2.3.4:5672'
 
81
        (defaults to 'localhost', if a port is not specified then
 
82
        5672 is used)
 
83
 
 
84
        If login_response is not specified, one is built up for you from
 
85
        userid and password if they are present.
 
86
 
 
87
        """
 
88
        if (login_response is None) \
 
89
        and (userid is not None) \
 
90
        and (password is not None):
 
91
            login_response = AMQPWriter()
 
92
            login_response.write_table({'LOGIN': userid, 'PASSWORD': password})
 
93
            login_response = login_response.getvalue()[4:]  #Skip the length
 
94
                                                            #at the beginning
 
95
 
 
96
        d = {}
 
97
        d.update(LIBRARY_PROPERTIES)
 
98
        if client_properties:
 
99
            d.update(client_properties)
 
100
 
 
101
        self.known_hosts = ''
 
102
 
 
103
        while True:
 
104
            self.channels = {}
 
105
            # The connection object itself is treated as channel 0
 
106
            super(Connection, self).__init__(self, 0)
 
107
 
 
108
            self.transport = None
 
109
 
 
110
            # Properties set in the Tune method
 
111
            self.channel_max = 65535
 
112
            self.frame_max = 131072
 
113
            self.heartbeat = 0
 
114
 
 
115
            # Properties set in the Start method
 
116
            self.version_major = 0
 
117
            self.version_minor = 0
 
118
            self.server_properties = {}
 
119
            self.mechanisms = []
 
120
            self.locales = []
 
121
 
 
122
            # Let the transport.py module setup the actual
 
123
            # socket connection to the broker.
 
124
            #
 
125
            self.transport = create_transport(host, connect_timeout, ssl)
 
126
 
 
127
            self.method_reader = MethodReader(self.transport)
 
128
            self.method_writer = MethodWriter(self.transport, self.frame_max)
 
129
 
 
130
            self.wait(allowed_methods=[
 
131
                    (10, 10), # start
 
132
                    ])
 
133
 
 
134
            self._x_start_ok(d, login_method, login_response, locale)
 
135
 
 
136
            self._wait_tune_ok = True
 
137
            while self._wait_tune_ok:
 
138
                self.wait(allowed_methods=[
 
139
                    (10, 20), # secure
 
140
                    (10, 30), # tune
 
141
                    ])
 
142
 
 
143
            host = self._x_open(virtual_host, insist=insist)
 
144
            if host is None:
 
145
                # we weren't redirected
 
146
                return
 
147
 
 
148
            # we were redirected, close the socket, loop and try again
 
149
            try:
 
150
                self.close()
 
151
            except Exception:
 
152
                pass
 
153
 
 
154
 
 
155
    def _do_close(self):
 
156
        self.transport.close()
 
157
        self.transport = None
 
158
 
 
159
        temp_list = [x for x in self.channels.values() if x is not self]
 
160
        for ch in temp_list:
 
161
            ch._do_close()
 
162
 
 
163
        self.connection = self.channels = None
 
164
 
 
165
 
 
166
    def _get_free_channel_id(self):
 
167
        for i in xrange(1, self.channel_max+1):
 
168
            if i not in self.channels:
 
169
                return i
 
170
        raise AMQPException('No free channel ids, current=%d, channel_max=%d'
 
171
            % (len(self.channels), self.channel_max))
 
172
 
 
173
 
 
174
    def _wait_method(self, channel_id, allowed_methods):
 
175
        """
 
176
        Wait for a method from the server destined for
 
177
        a particular channel.
 
178
 
 
179
        """
 
180
        #
 
181
        # Check the channel's deferred methods
 
182
        #
 
183
        method_queue = self.channels[channel_id].method_queue
 
184
 
 
185
        for queued_method in method_queue:
 
186
            method_sig = queued_method[0]
 
187
            if (allowed_methods is None) \
 
188
            or (method_sig in allowed_methods) \
 
189
            or (method_sig == (20, 40)):
 
190
                method_queue.remove(queued_method)
 
191
                return queued_method
 
192
 
 
193
        #
 
194
        # Nothing queued, need to wait for a method from the peer
 
195
        #
 
196
        while True:
 
197
            channel, method_sig, args, content = \
 
198
                self.method_reader.read_method()
 
199
 
 
200
            if (channel == channel_id) \
 
201
            and ((allowed_methods is None) \
 
202
                or (method_sig in allowed_methods) \
 
203
                or (method_sig == (20, 40))):
 
204
                return method_sig, args, content
 
205
 
 
206
            #
 
207
            # Not the channel and/or method we were looking for.  Queue
 
208
            # this method for later
 
209
            #
 
210
            self.channels[channel].method_queue.append((method_sig, args, content))
 
211
 
 
212
            #
 
213
            # If we just queued up a method for channel 0 (the Connection
 
214
            # itself) it's probably a close method in reaction to some
 
215
            # error, so deal with it right away.
 
216
            #
 
217
            if channel == 0:
 
218
                self.wait()
 
219
 
 
220
 
 
221
    def channel(self, channel_id=None):
 
222
        """
 
223
        Fetch a Channel object identified by the numeric channel_id, or
 
224
        create that object if it doesn't already exist.
 
225
 
 
226
        """
 
227
        if channel_id in self.channels:
 
228
            return self.channels[channel_id]
 
229
 
 
230
        return Channel(self, channel_id)
 
231
 
 
232
 
 
233
    #################
 
234
 
 
235
    def close(self, reply_code=0, reply_text='', method_sig=(0, 0)):
 
236
        """
 
237
        request a connection close
 
238
 
 
239
        This method indicates that the sender wants to close the
 
240
        connection. This may be due to internal conditions (e.g. a
 
241
        forced shut-down) or due to an error handling a specific
 
242
        method, i.e. an exception.  When a close is due to an
 
243
        exception, the sender provides the class and method id of the
 
244
        method which caused the exception.
 
245
 
 
246
        RULE:
 
247
 
 
248
            After sending this method any received method except the
 
249
            Close-OK method MUST be discarded.
 
250
 
 
251
        RULE:
 
252
 
 
253
            The peer sending this method MAY use a counter or timeout
 
254
            to detect failure of the other peer to respond correctly
 
255
            with the Close-OK method.
 
256
 
 
257
        RULE:
 
258
 
 
259
            When a server receives the Close method from a client it
 
260
            MUST delete all server-side resources associated with the
 
261
            client's context.  A client CANNOT reconnect to a context
 
262
            after sending or receiving a Close method.
 
263
 
 
264
        PARAMETERS:
 
265
            reply_code: short
 
266
 
 
267
                The reply code. The AMQ reply codes are defined in AMQ
 
268
                RFC 011.
 
269
 
 
270
            reply_text: shortstr
 
271
 
 
272
                The localised reply text.  This text can be logged as an
 
273
                aid to resolving issues.
 
274
 
 
275
            class_id: short
 
276
 
 
277
                failing method class
 
278
 
 
279
                When the close is provoked by a method exception, this
 
280
                is the class of the method.
 
281
 
 
282
            method_id: short
 
283
 
 
284
                failing method ID
 
285
 
 
286
                When the close is provoked by a method exception, this
 
287
                is the ID of the method.
 
288
 
 
289
        """
 
290
        if self.transport is None:
 
291
            # already closed
 
292
            return
 
293
 
 
294
        args = AMQPWriter()
 
295
        args.write_short(reply_code)
 
296
        args.write_shortstr(reply_text)
 
297
        args.write_short(method_sig[0]) # class_id
 
298
        args.write_short(method_sig[1]) # method_id
 
299
        self._send_method((10, 60), args)
 
300
        return self.wait(allowed_methods=[
 
301
                          (10, 61),    # Connection.close_ok
 
302
                        ])
 
303
 
 
304
 
 
305
    def _close(self, args):
 
306
        """
 
307
        request a connection close
 
308
 
 
309
        This method indicates that the sender wants to close the
 
310
        connection. This may be due to internal conditions (e.g. a
 
311
        forced shut-down) or due to an error handling a specific
 
312
        method, i.e. an exception.  When a close is due to an
 
313
        exception, the sender provides the class and method id of the
 
314
        method which caused the exception.
 
315
 
 
316
        RULE:
 
317
 
 
318
            After sending this method any received method except the
 
319
            Close-OK method MUST be discarded.
 
320
 
 
321
        RULE:
 
322
 
 
323
            The peer sending this method MAY use a counter or timeout
 
324
            to detect failure of the other peer to respond correctly
 
325
            with the Close-OK method.
 
326
 
 
327
        RULE:
 
328
 
 
329
            When a server receives the Close method from a client it
 
330
            MUST delete all server-side resources associated with the
 
331
            client's context.  A client CANNOT reconnect to a context
 
332
            after sending or receiving a Close method.
 
333
 
 
334
        PARAMETERS:
 
335
            reply_code: short
 
336
 
 
337
                The reply code. The AMQ reply codes are defined in AMQ
 
338
                RFC 011.
 
339
 
 
340
            reply_text: shortstr
 
341
 
 
342
                The localised reply text.  This text can be logged as an
 
343
                aid to resolving issues.
 
344
 
 
345
            class_id: short
 
346
 
 
347
                failing method class
 
348
 
 
349
                When the close is provoked by a method exception, this
 
350
                is the class of the method.
 
351
 
 
352
            method_id: short
 
353
 
 
354
                failing method ID
 
355
 
 
356
                When the close is provoked by a method exception, this
 
357
                is the ID of the method.
 
358
 
 
359
        """
 
360
        reply_code = args.read_short()
 
361
        reply_text = args.read_shortstr()
 
362
        class_id = args.read_short()
 
363
        method_id = args.read_short()
 
364
 
 
365
        self._x_close_ok()
 
366
 
 
367
        raise AMQPConnectionException(reply_code, reply_text, (class_id, method_id))
 
368
 
 
369
 
 
370
    def _x_close_ok(self):
 
371
        """
 
372
        confirm a connection close
 
373
 
 
374
        This method confirms a Connection.Close method and tells the
 
375
        recipient that it is safe to release resources for the
 
376
        connection and close the socket.
 
377
 
 
378
        RULE:
 
379
 
 
380
            A peer that detects a socket closure without having
 
381
            received a Close-Ok handshake method SHOULD log the error.
 
382
 
 
383
        """
 
384
        self._send_method((10, 61))
 
385
        self._do_close()
 
386
 
 
387
 
 
388
    def _close_ok(self, args):
 
389
        """
 
390
        confirm a connection close
 
391
 
 
392
        This method confirms a Connection.Close method and tells the
 
393
        recipient that it is safe to release resources for the
 
394
        connection and close the socket.
 
395
 
 
396
        RULE:
 
397
 
 
398
            A peer that detects a socket closure without having
 
399
            received a Close-Ok handshake method SHOULD log the error.
 
400
 
 
401
        """
 
402
        self._do_close()
 
403
 
 
404
 
 
405
    def _x_open(self, virtual_host, capabilities='', insist=False):
 
406
        """
 
407
        open connection to virtual host
 
408
 
 
409
        This method opens a connection to a virtual host, which is a
 
410
        collection of resources, and acts to separate multiple
 
411
        application domains within a server.
 
412
 
 
413
        RULE:
 
414
 
 
415
            The client MUST open the context before doing any work on
 
416
            the connection.
 
417
 
 
418
        PARAMETERS:
 
419
            virtual_host: shortstr
 
420
 
 
421
                virtual host name
 
422
 
 
423
                The name of the virtual host to work with.
 
424
 
 
425
                RULE:
 
426
 
 
427
                    If the server supports multiple virtual hosts, it
 
428
                    MUST enforce a full separation of exchanges,
 
429
                    queues, and all associated entities per virtual
 
430
                    host. An application, connected to a specific
 
431
                    virtual host, MUST NOT be able to access resources
 
432
                    of another virtual host.
 
433
 
 
434
                RULE:
 
435
 
 
436
                    The server SHOULD verify that the client has
 
437
                    permission to access the specified virtual host.
 
438
 
 
439
                RULE:
 
440
 
 
441
                    The server MAY configure arbitrary limits per
 
442
                    virtual host, such as the number of each type of
 
443
                    entity that may be used, per connection and/or in
 
444
                    total.
 
445
 
 
446
            capabilities: shortstr
 
447
 
 
448
                required capabilities
 
449
 
 
450
                The client may specify a number of capability names,
 
451
                delimited by spaces.  The server can use this string
 
452
                to how to process the client's connection request.
 
453
 
 
454
            insist: boolean
 
455
 
 
456
                insist on connecting to server
 
457
 
 
458
                In a configuration with multiple load-sharing servers,
 
459
                the server may respond to a Connection.Open method
 
460
                with a Connection.Redirect. The insist option tells
 
461
                the server that the client is insisting on a
 
462
                connection to the specified server.
 
463
 
 
464
                RULE:
 
465
 
 
466
                    When the client uses the insist option, the server
 
467
                    SHOULD accept the client connection unless it is
 
468
                    technically unable to do so.
 
469
 
 
470
        """
 
471
        args = AMQPWriter()
 
472
        args.write_shortstr(virtual_host)
 
473
        args.write_shortstr(capabilities)
 
474
        args.write_bit(insist)
 
475
        self._send_method((10, 40), args)
 
476
        return self.wait(allowed_methods=[
 
477
                          (10, 41),    # Connection.open_ok
 
478
                          (10, 50),    # Connection.redirect
 
479
                        ])
 
480
 
 
481
 
 
482
    def _open_ok(self, args):
 
483
        """
 
484
        signal that the connection is ready
 
485
 
 
486
        This method signals to the client that the connection is ready
 
487
        for use.
 
488
 
 
489
        PARAMETERS:
 
490
            known_hosts: shortstr
 
491
 
 
492
        """
 
493
        self.known_hosts = args.read_shortstr()
 
494
        AMQP_LOGGER.debug('Open OK! known_hosts [%s]' % self.known_hosts)
 
495
        return None
 
496
 
 
497
 
 
498
    def _redirect(self, args):
 
499
        """
 
500
        asks the client to use a different server
 
501
 
 
502
        This method redirects the client to another server, based on
 
503
        the requested virtual host and/or capabilities.
 
504
 
 
505
        RULE:
 
506
 
 
507
            When getting the Connection.Redirect method, the client
 
508
            SHOULD reconnect to the host specified, and if that host
 
509
            is not present, to any of the hosts specified in the
 
510
            known-hosts list.
 
511
 
 
512
        PARAMETERS:
 
513
            host: shortstr
 
514
 
 
515
                server to connect to
 
516
 
 
517
                Specifies the server to connect to.  This is an IP
 
518
                address or a DNS name, optionally followed by a colon
 
519
                and a port number. If no port number is specified, the
 
520
                client should use the default port number for the
 
521
                protocol.
 
522
 
 
523
            known_hosts: shortstr
 
524
 
 
525
        """
 
526
        host = args.read_shortstr()
 
527
        self.known_hosts = args.read_shortstr()
 
528
        AMQP_LOGGER.debug('Redirected to [%s], known_hosts [%s]' % (host, self.known_hosts))
 
529
        return host
 
530
 
 
531
 
 
532
    def _secure(self, args):
 
533
        """
 
534
        security mechanism challenge
 
535
 
 
536
        The SASL protocol works by exchanging challenges and responses
 
537
        until both peers have received sufficient information to
 
538
        authenticate each other.  This method challenges the client to
 
539
        provide more information.
 
540
 
 
541
        PARAMETERS:
 
542
            challenge: longstr
 
543
 
 
544
                security challenge data
 
545
 
 
546
                Challenge information, a block of opaque binary data
 
547
                passed to the security mechanism.
 
548
 
 
549
        """
 
550
        challenge = args.read_longstr()
 
551
 
 
552
 
 
553
    def _x_secure_ok(self, response):
 
554
        """
 
555
        security mechanism response
 
556
 
 
557
        This method attempts to authenticate, passing a block of SASL
 
558
        data for the security mechanism at the server side.
 
559
 
 
560
        PARAMETERS:
 
561
            response: longstr
 
562
 
 
563
                security response data
 
564
 
 
565
                A block of opaque data passed to the security
 
566
                mechanism.  The contents of this data are defined by
 
567
                the SASL security mechanism.
 
568
 
 
569
        """
 
570
        args = AMQPWriter()
 
571
        args.write_longstr(response)
 
572
        self._send_method((10, 21), args)
 
573
 
 
574
 
 
575
    def _start(self, args):
 
576
        """
 
577
        start connection negotiation
 
578
 
 
579
        This method starts the connection negotiation process by
 
580
        telling the client the protocol version that the server
 
581
        proposes, along with a list of security mechanisms which the
 
582
        client can use for authentication.
 
583
 
 
584
        RULE:
 
585
 
 
586
            If the client cannot handle the protocol version suggested
 
587
            by the server it MUST close the socket connection.
 
588
 
 
589
        RULE:
 
590
 
 
591
            The server MUST provide a protocol version that is lower
 
592
            than or equal to that requested by the client in the
 
593
            protocol header. If the server cannot support the
 
594
            specified protocol it MUST NOT send this method, but MUST
 
595
            close the socket connection.
 
596
 
 
597
        PARAMETERS:
 
598
            version_major: octet
 
599
 
 
600
                protocol major version
 
601
 
 
602
                The protocol major version that the server agrees to
 
603
                use, which cannot be higher than the client's major
 
604
                version.
 
605
 
 
606
            version_minor: octet
 
607
 
 
608
                protocol major version
 
609
 
 
610
                The protocol minor version that the server agrees to
 
611
                use, which cannot be higher than the client's minor
 
612
                version.
 
613
 
 
614
            server_properties: table
 
615
 
 
616
                server properties
 
617
 
 
618
            mechanisms: longstr
 
619
 
 
620
                available security mechanisms
 
621
 
 
622
                A list of the security mechanisms that the server
 
623
                supports, delimited by spaces.  Currently ASL supports
 
624
                these mechanisms: PLAIN.
 
625
 
 
626
            locales: longstr
 
627
 
 
628
                available message locales
 
629
 
 
630
                A list of the message locales that the server
 
631
                supports, delimited by spaces.  The locale defines the
 
632
                language in which the server will send reply texts.
 
633
 
 
634
                RULE:
 
635
 
 
636
                    All servers MUST support at least the en_US
 
637
                    locale.
 
638
 
 
639
        """
 
640
        self.version_major = args.read_octet()
 
641
        self.version_minor = args.read_octet()
 
642
        self.server_properties = args.read_table()
 
643
        self.mechanisms = args.read_longstr().split(' ')
 
644
        self.locales = args.read_longstr().split(' ')
 
645
 
 
646
        AMQP_LOGGER.debug('Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s'
 
647
                % (self.version_major, self.version_minor,
 
648
                   str(self.server_properties), self.mechanisms, self.locales))
 
649
 
 
650
 
 
651
    def _x_start_ok(self, client_properties, mechanism, response, locale):
 
652
        """
 
653
        select security mechanism and locale
 
654
 
 
655
        This method selects a SASL security mechanism. ASL uses SASL
 
656
        (RFC2222) to negotiate authentication and encryption.
 
657
 
 
658
        PARAMETERS:
 
659
            client_properties: table
 
660
 
 
661
                client properties
 
662
 
 
663
            mechanism: shortstr
 
664
 
 
665
                selected security mechanism
 
666
 
 
667
                A single security mechanisms selected by the client,
 
668
                which must be one of those specified by the server.
 
669
 
 
670
                RULE:
 
671
 
 
672
                    The client SHOULD authenticate using the highest-
 
673
                    level security profile it can handle from the list
 
674
                    provided by the server.
 
675
 
 
676
                RULE:
 
677
 
 
678
                    The mechanism field MUST contain one of the
 
679
                    security mechanisms proposed by the server in the
 
680
                    Start method. If it doesn't, the server MUST close
 
681
                    the socket.
 
682
 
 
683
            response: longstr
 
684
 
 
685
                security response data
 
686
 
 
687
                A block of opaque data passed to the security
 
688
                mechanism. The contents of this data are defined by
 
689
                the SASL security mechanism.  For the PLAIN security
 
690
                mechanism this is defined as a field table holding two
 
691
                fields, LOGIN and PASSWORD.
 
692
 
 
693
            locale: shortstr
 
694
 
 
695
                selected message locale
 
696
 
 
697
                A single message local selected by the client, which
 
698
                must be one of those specified by the server.
 
699
 
 
700
        """
 
701
        args = AMQPWriter()
 
702
        args.write_table(client_properties)
 
703
        args.write_shortstr(mechanism)
 
704
        args.write_longstr(response)
 
705
        args.write_shortstr(locale)
 
706
        self._send_method((10, 11), args)
 
707
 
 
708
 
 
709
    def _tune(self, args):
 
710
        """
 
711
        propose connection tuning parameters
 
712
 
 
713
        This method proposes a set of connection configuration values
 
714
        to the client.  The client can accept and/or adjust these.
 
715
 
 
716
        PARAMETERS:
 
717
            channel_max: short
 
718
 
 
719
                proposed maximum channels
 
720
 
 
721
                The maximum total number of channels that the server
 
722
                allows per connection. Zero means that the server does
 
723
                not impose a fixed limit, but the number of allowed
 
724
                channels may be limited by available server resources.
 
725
 
 
726
            frame_max: long
 
727
 
 
728
                proposed maximum frame size
 
729
 
 
730
                The largest frame size that the server proposes for
 
731
                the connection. The client can negotiate a lower
 
732
                value.  Zero means that the server does not impose any
 
733
                specific limit but may reject very large frames if it
 
734
                cannot allocate resources for them.
 
735
 
 
736
                RULE:
 
737
 
 
738
                    Until the frame-max has been negotiated, both
 
739
                    peers MUST accept frames of up to 4096 octets
 
740
                    large. The minimum non-zero value for the frame-
 
741
                    max field is 4096.
 
742
 
 
743
            heartbeat: short
 
744
 
 
745
                desired heartbeat delay
 
746
 
 
747
                The delay, in seconds, of the connection heartbeat
 
748
                that the server wants.  Zero means the server does not
 
749
                want a heartbeat.
 
750
 
 
751
        """
 
752
        self.channel_max = args.read_short() or self.channel_max
 
753
        self.frame_max = args.read_long() or self.frame_max
 
754
        self.method_writer.frame_max = self.frame_max
 
755
        self.heartbeat = args.read_short()
 
756
 
 
757
        self._x_tune_ok(self.channel_max, self.frame_max, 0)
 
758
 
 
759
 
 
760
    def _x_tune_ok(self, channel_max, frame_max, heartbeat):
 
761
        """
 
762
        negotiate connection tuning parameters
 
763
 
 
764
        This method sends the client's connection tuning parameters to
 
765
        the server. Certain fields are negotiated, others provide
 
766
        capability information.
 
767
 
 
768
        PARAMETERS:
 
769
            channel_max: short
 
770
 
 
771
                negotiated maximum channels
 
772
 
 
773
                The maximum total number of channels that the client
 
774
                will use per connection.  May not be higher than the
 
775
                value specified by the server.
 
776
 
 
777
                RULE:
 
778
 
 
779
                    The server MAY ignore the channel-max value or MAY
 
780
                    use it for tuning its resource allocation.
 
781
 
 
782
            frame_max: long
 
783
 
 
784
                negotiated maximum frame size
 
785
 
 
786
                The largest frame size that the client and server will
 
787
                use for the connection.  Zero means that the client
 
788
                does not impose any specific limit but may reject very
 
789
                large frames if it cannot allocate resources for them.
 
790
                Note that the frame-max limit applies principally to
 
791
                content frames, where large contents can be broken
 
792
                into frames of arbitrary size.
 
793
 
 
794
                RULE:
 
795
 
 
796
                    Until the frame-max has been negotiated, both
 
797
                    peers must accept frames of up to 4096 octets
 
798
                    large. The minimum non-zero value for the frame-
 
799
                    max field is 4096.
 
800
 
 
801
            heartbeat: short
 
802
 
 
803
                desired heartbeat delay
 
804
 
 
805
                The delay, in seconds, of the connection heartbeat
 
806
                that the client wants. Zero means the client does not
 
807
                want a heartbeat.
 
808
 
 
809
        """
 
810
        args = AMQPWriter()
 
811
        args.write_short(channel_max)
 
812
        args.write_long(frame_max)
 
813
        args.write_short(heartbeat)
 
814
        self._send_method((10, 31), args)
 
815
        self._wait_tune_ok = False
 
816
 
 
817
 
 
818
    _METHOD_MAP = {
 
819
        (10, 10): _start,
 
820
        (10, 20): _secure,
 
821
        (10, 30): _tune,
 
822
        (10, 41): _open_ok,
 
823
        (10, 50): _redirect,
 
824
        (10, 60): _close,
 
825
        (10, 61): _close_ok,
 
826
        }