From: Aleksandr Mogylchenko Date: Thu, 23 Jul 2015 05:44:10 +0000 (+0300) Subject: Rabbitmq 3.5.4 for MOS 7.0 X-Git-Tag: mos-9.0~8 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=9c07a575db01ddf90809fab48ce84b905d8e55e3;p=packages%2Ftrusty%2Frabbitmq-server.git Rabbitmq 3.5.4 for MOS 7.0 Source picked up from Debian Sid: http://http.debian.net/debian/pool/main/r/rabbitmq-server/rabbitmq-server_3.5.4-1.dsc http://http.debian.net/debian/pool/main/r/rabbitmq-server/rabbitmq-server_3.5.4.orig.tar.gz http://http.debian.net/debian/pool/main/r/rabbitmq-server/rabbitmq-server_3.5.4-1.debian.tar.xz Closes-Bug: #1463000 Change-Id: Ie1c58830295f6feb5d45e97a42129ac3f8d821e9 --- diff --git a/debian/LICENSE.head b/debian/LICENSE.head deleted file mode 100644 index 2b5a17e..0000000 --- a/debian/LICENSE.head +++ /dev/null @@ -1,5 +0,0 @@ -This package, the RabbitMQ server is licensed under the MPL. - -If you have any questions regarding licensing, please contact us at -info@rabbitmq.com. - diff --git a/debian/LICENSE.tail b/debian/LICENSE.tail deleted file mode 100644 index 7858a04..0000000 --- a/debian/LICENSE.tail +++ /dev/null @@ -1,516 +0,0 @@ - -The MIT license is as follows: - - "Permission is hereby granted, free of charge, to any person - obtaining a copy of this file (the Software), to deal in the - Software without restriction, including without limitation the - rights to use, copy, modify, merge, publish, distribute, - sublicense, and/or sell copies of the Software, and to permit - persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE." - - -The BSD 2-Clause license is as follows: - - "Redistribution and use in source and binary forms, with or - without modification, are permitted provided that the - following conditions are met: - - 1. Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - - -The rest of this package is licensed under the Mozilla Public License 1.1 -Authors and Copyright are as described below: - - The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. - - - MOZILLA PUBLIC LICENSE - Version 1.1 - - --------------- - -1. Definitions. - - 1.0.1. "Commercial Use" means distribution or otherwise making the - Covered Code available to a third party. - - 1.1. "Contributor" means each entity that creates or contributes to - the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the Original - Code, prior Modifications used by a Contributor, and the Modifications - made by that particular Contributor. - - 1.3. "Covered Code" means the Original Code or Modifications or the - combination of the Original Code and Modifications, in each case - including portions thereof. - - 1.4. "Electronic Distribution Mechanism" means a mechanism generally - accepted in the software development community for the electronic - transfer of data. - - 1.5. "Executable" means Covered Code in any form other than Source - Code. - - 1.6. "Initial Developer" means the individual or entity identified - as the Initial Developer in the Source Code notice required by Exhibit - A. - - 1.7. "Larger Work" means a work which combines Covered Code or - portions thereof with code not governed by the terms of this License. - - 1.8. "License" means this document. - - 1.8.1. "Licensable" means having the right to grant, to the maximum - extent possible, whether at the time of the initial grant or - subsequently acquired, any and all of the rights conveyed herein. - - 1.9. "Modifications" means any addition to or deletion from the - substance or structure of either the Original Code or any previous - Modifications. When Covered Code is released as a series of files, a - Modification is: - A. Any addition to or deletion from the contents of a file - containing Original Code or previous Modifications. - - B. Any new file that contains any part of the Original Code or - previous Modifications. - - 1.10. "Original Code" means Source Code of computer software code - which is described in the Source Code notice required by Exhibit A as - Original Code, and which, at the time of its release under this - License is not already Covered Code governed by this License. - - 1.10.1. "Patent Claims" means any patent claim(s), now owned or - hereafter acquired, including without limitation, method, process, - and apparatus claims, in any patent Licensable by grantor. - - 1.11. "Source Code" means the preferred form of the Covered Code for - making modifications to it, including all modules it contains, plus - any associated interface definition files, scripts used to control - compilation and installation of an Executable, or source code - differential comparisons against either the Original Code or another - well known, available Covered Code of the Contributor's choice. The - Source Code can be in a compressed or archival form, provided the - appropriate decompression or de-archiving software is widely available - for no charge. - - 1.12. "You" (or "Your") means an individual or a legal entity - exercising rights under, and complying with all of the terms of, this - License or a future version of this License issued under Section 6.1. - For legal entities, "You" includes any entity which controls, is - controlled by, or is under common control with You. For purposes of - this definition, "control" means (a) the power, direct or indirect, - to cause the direction or management of such entity, whether by - contract or otherwise, or (b) ownership of more than fifty percent - (50%) of the outstanding shares or beneficial ownership of such - entity. - -2. Source Code License. - - 2.1. The Initial Developer Grant. - The Initial Developer hereby grants You a world-wide, royalty-free, - non-exclusive license, subject to third party intellectual property - claims: - (a) under intellectual property rights (other than patent or - trademark) Licensable by Initial Developer to use, reproduce, - modify, display, perform, sublicense and distribute the Original - Code (or portions thereof) with or without Modifications, and/or - as part of a Larger Work; and - - (b) under Patents Claims infringed by the making, using or - selling of Original Code, to make, have made, use, practice, - sell, and offer for sale, and/or otherwise dispose of the - Original Code (or portions thereof). - - (c) the licenses granted in this Section 2.1(a) and (b) are - effective on the date Initial Developer first distributes - Original Code under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is - granted: 1) for code that You delete from the Original Code; 2) - separate from the Original Code; or 3) for infringements caused - by: i) the modification of the Original Code or ii) the - combination of the Original Code with other software or devices. - - 2.2. Contributor Grant. - Subject to third party intellectual property claims, each Contributor - hereby grants You a world-wide, royalty-free, non-exclusive license - - (a) under intellectual property rights (other than patent or - trademark) Licensable by Contributor, to use, reproduce, modify, - display, perform, sublicense and distribute the Modifications - created by such Contributor (or portions thereof) either on an - unmodified basis, with other Modifications, as Covered Code - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or - selling of Modifications made by that Contributor either alone - and/or in combination with its Contributor Version (or portions - of such combination), to make, use, sell, offer for sale, have - made, and/or otherwise dispose of: 1) Modifications made by that - Contributor (or portions thereof); and 2) the combination of - Modifications made by that Contributor with its Contributor - Version (or portions of such combination). - - (c) the licenses granted in Sections 2.2(a) and 2.2(b) are - effective on the date Contributor first makes Commercial Use of - the Covered Code. - - (d) Notwithstanding Section 2.2(b) above, no patent license is - granted: 1) for any code that Contributor has deleted from the - Contributor Version; 2) separate from the Contributor Version; - 3) for infringements caused by: i) third party modifications of - Contributor Version or ii) the combination of Modifications made - by that Contributor with other software (except as part of the - Contributor Version) or other devices; or 4) under Patent Claims - infringed by Covered Code in the absence of Modifications made by - that Contributor. - -3. Distribution Obligations. - - 3.1. Application of License. - The Modifications which You create or to which You contribute are - governed by the terms of this License, including without limitation - Section 2.2. The Source Code version of Covered Code may be - distributed only under the terms of this License or a future version - of this License released under Section 6.1, and You must include a - copy of this License with every copy of the Source Code You - distribute. You may not offer or impose any terms on any Source Code - version that alters or restricts the applicable version of this - License or the recipients' rights hereunder. However, You may include - an additional document offering the additional rights described in - Section 3.5. - - 3.2. Availability of Source Code. - Any Modification which You create or to which You contribute must be - made available in Source Code form under the terms of this License - either on the same media as an Executable version or via an accepted - Electronic Distribution Mechanism to anyone to whom you made an - Executable version available; and if made available via Electronic - Distribution Mechanism, must remain available for at least twelve (12) - months after the date it initially became available, or at least six - (6) months after a subsequent version of that particular Modification - has been made available to such recipients. You are responsible for - ensuring that the Source Code version remains available even if the - Electronic Distribution Mechanism is maintained by a third party. - - 3.3. Description of Modifications. - You must cause all Covered Code to which You contribute to contain a - file documenting the changes You made to create that Covered Code and - the date of any change. You must include a prominent statement that - the Modification is derived, directly or indirectly, from Original - Code provided by the Initial Developer and including the name of the - Initial Developer in (a) the Source Code, and (b) in any notice in an - Executable version or related documentation in which You describe the - origin or ownership of the Covered Code. - - 3.4. Intellectual Property Matters - (a) Third Party Claims. - If Contributor has knowledge that a license under a third party's - intellectual property rights is required to exercise the rights - granted by such Contributor under Sections 2.1 or 2.2, - Contributor must include a text file with the Source Code - distribution titled "LEGAL" which describes the claim and the - party making the claim in sufficient detail that a recipient will - know whom to contact. If Contributor obtains such knowledge after - the Modification is made available as described in Section 3.2, - Contributor shall promptly modify the LEGAL file in all copies - Contributor makes available thereafter and shall take other steps - (such as notifying appropriate mailing lists or newsgroups) - reasonably calculated to inform those who received the Covered - Code that new knowledge has been obtained. - - (b) Contributor APIs. - If Contributor's Modifications include an application programming - interface and Contributor has knowledge of patent licenses which - are reasonably necessary to implement that API, Contributor must - also include this information in the LEGAL file. - - (c) Representations. - Contributor represents that, except as disclosed pursuant to - Section 3.4(a) above, Contributor believes that Contributor's - Modifications are Contributor's original creation(s) and/or - Contributor has sufficient rights to grant the rights conveyed by - this License. - - 3.5. Required Notices. - You must duplicate the notice in Exhibit A in each file of the Source - Code. If it is not possible to put such notice in a particular Source - Code file due to its structure, then You must include such notice in a - location (such as a relevant directory) where a user would be likely - to look for such a notice. If You created one or more Modification(s) - You may add your name as a Contributor to the notice described in - Exhibit A. You must also duplicate this License in any documentation - for the Source Code where You describe recipients' rights or ownership - rights relating to Covered Code. You may choose to offer, and to - charge a fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Code. However, You - may do so only on Your own behalf, and not on behalf of the Initial - Developer or any Contributor. You must make it absolutely clear than - any such warranty, support, indemnity or liability obligation is - offered by You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred by the - Initial Developer or such Contributor as a result of warranty, - support, indemnity or liability terms You offer. - - 3.6. Distribution of Executable Versions. - You may distribute Covered Code in Executable form only if the - requirements of Section 3.1-3.5 have been met for that Covered Code, - and if You include a notice stating that the Source Code version of - the Covered Code is available under the terms of this License, - including a description of how and where You have fulfilled the - obligations of Section 3.2. The notice must be conspicuously included - in any notice in an Executable version, related documentation or - collateral in which You describe recipients' rights relating to the - Covered Code. You may distribute the Executable version of Covered - Code or ownership rights under a license of Your choice, which may - contain terms different from this License, provided that You are in - compliance with the terms of this License and that the license for the - Executable version does not attempt to limit or alter the recipient's - rights in the Source Code version from the rights set forth in this - License. If You distribute the Executable version under a different - license You must make it absolutely clear that any terms which differ - from this License are offered by You alone, not by the Initial - Developer or any Contributor. You hereby agree to indemnify the - Initial Developer and every Contributor for any liability incurred by - the Initial Developer or such Contributor as a result of any such - terms You offer. - - 3.7. Larger Works. - You may create a Larger Work by combining Covered Code with other code - not governed by the terms of this License and distribute the Larger - Work as a single product. In such a case, You must make sure the - requirements of this License are fulfilled for the Covered Code. - -4. Inability to Comply Due to Statute or Regulation. - - If it is impossible for You to comply with any of the terms of this - License with respect to some or all of the Covered Code due to - statute, judicial order, or regulation then You must: (a) comply with - the terms of this License to the maximum extent possible; and (b) - describe the limitations and the code they affect. Such description - must be included in the LEGAL file described in Section 3.4 and must - be included with all distributions of the Source Code. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Application of this License. - - This License applies to code to which the Initial Developer has - attached the notice in Exhibit A and to related Covered Code. - -6. Versions of the License. - - 6.1. New Versions. - Netscape Communications Corporation ("Netscape") may publish revised - and/or new versions of the License from time to time. Each version - will be given a distinguishing version number. - - 6.2. Effect of New Versions. - Once Covered Code has been published under a particular version of the - License, You may always continue to use it under the terms of that - version. You may also choose to use such Covered Code under the terms - of any subsequent version of the License published by Netscape. No one - other than Netscape has the right to modify the terms applicable to - Covered Code created under this License. - - 6.3. Derivative Works. - If You create or use a modified version of this License (which you may - only do in order to apply it to code which is not already Covered Code - governed by this License), You must (a) rename Your license so that - the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", - "MPL", "NPL" or any confusingly similar phrase do not appear in your - license (except to note that your license differs from this License) - and (b) otherwise make it clear that Your version of the license - contains terms which differ from the Mozilla Public License and - Netscape Public License. (Filling in the name of the Initial - Developer, Original Code or Contributor in the notice described in - Exhibit A shall not of themselves be deemed to be modifications of - this License.) - -7. DISCLAIMER OF WARRANTY. - - COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, - WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF - DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. - THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE - IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, - YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE - COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER - OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -8. TERMINATION. - - 8.1. This License and the rights granted hereunder will terminate - automatically if You fail to comply with terms herein and fail to cure - such breach within 30 days of becoming aware of the breach. All - sublicenses to the Covered Code which are properly granted shall - survive any termination of this License. Provisions which, by their - nature, must remain in effect beyond the termination of this License - shall survive. - - 8.2. If You initiate litigation by asserting a patent infringement - claim (excluding declatory judgment actions) against Initial Developer - or a Contributor (the Initial Developer or Contributor against whom - You file such action is referred to as "Participant") alleging that: - - (a) such Participant's Contributor Version directly or indirectly - infringes any patent, then any and all rights granted by such - Participant to You under Sections 2.1 and/or 2.2 of this License - shall, upon 60 days notice from Participant terminate prospectively, - unless if within 60 days after receipt of notice You either: (i) - agree in writing to pay Participant a mutually agreeable reasonable - royalty for Your past and future use of Modifications made by such - Participant, or (ii) withdraw Your litigation claim with respect to - the Contributor Version against such Participant. If within 60 days - of notice, a reasonable royalty and payment arrangement are not - mutually agreed upon in writing by the parties or the litigation claim - is not withdrawn, the rights granted by Participant to You under - Sections 2.1 and/or 2.2 automatically terminate at the expiration of - the 60 day notice period specified above. - - (b) any software, hardware, or device, other than such Participant's - Contributor Version, directly or indirectly infringes any patent, then - any rights granted to You by such Participant under Sections 2.1(b) - and 2.2(b) are revoked effective as of the date You first made, used, - sold, distributed, or had made, Modifications made by that - Participant. - - 8.3. If You assert a patent infringement claim against Participant - alleging that such Participant's Contributor Version directly or - indirectly infringes any patent where such claim is resolved (such as - by license or settlement) prior to the initiation of patent - infringement litigation, then the reasonable value of the licenses - granted by such Participant under Sections 2.1 or 2.2 shall be taken - into account in determining the amount or value of any payment or - license. - - 8.4. In the event of termination under Sections 8.1 or 8.2 above, - all end user license agreements (excluding distributors and resellers) - which have been validly granted by You or any distributor hereunder - prior to termination shall survive termination. - -9. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL - DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, - OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR - ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY - CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, - WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY - RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW - PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE - EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO - THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -10. U.S. GOVERNMENT END USERS. - - The Covered Code is a "commercial item," as that term is defined in - 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer - software" and "commercial computer software documentation," as such - terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 - C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), - all U.S. Government End Users acquire Covered Code with only those - rights set forth herein. - -11. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. This License shall be governed by - California law provisions (except to the extent applicable law, if - any, provides otherwise), excluding its conflict-of-law provisions. - With respect to disputes in which at least one party is a citizen of, - or an entity chartered or registered to do business in the United - States of America, any litigation relating to this License shall be - subject to the jurisdiction of the Federal Courts of the Northern - District of California, with venue lying in Santa Clara County, - California, with the losing party responsible for costs, including - without limitation, court costs and reasonable attorneys' fees and - expenses. The application of the United Nations Convention on - Contracts for the International Sale of Goods is expressly excluded. - Any law or regulation which provides that the language of a contract - shall be construed against the drafter shall not apply to this - License. - -12. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or indirectly, - out of its utilization of rights under this License and You agree to - work with Initial Developer and Contributors to distribute such - responsibility on an equitable basis. Nothing herein is intended or - shall be deemed to constitute any admission of liability. - -13. MULTIPLE-LICENSED CODE. - - Initial Developer may designate portions of the Covered Code as - "Multiple-Licensed". "Multiple-Licensed" means that the Initial - Developer permits you to utilize portions of the Covered Code under - Your choice of the NPL or the alternative licenses, if any, specified - by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. - - ``The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is RabbitMQ. - - The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.'' - - [NOTE: The text of this Exhibit A may differ slightly from the text of - the notices in the Source Code files of the Original Code. You should - use the text of this Exhibit A rather than the text found in the - Original Code Source Code for Your Modifications.] diff --git a/debian/README b/debian/README deleted file mode 100644 index 0a29ee2..0000000 --- a/debian/README +++ /dev/null @@ -1,20 +0,0 @@ -This is rabbitmq-server, a message broker implementing AMQP, STOMP and MQTT. - -Most of the documentation for RabbitMQ is provided on the RabbitMQ web -site. You can see documentation for the current version at: - -http://www.rabbitmq.com/documentation.html - -and for previous versions at: - -http://www.rabbitmq.com/previous.html - -Man pages are installed with this package. Of particular interest are -rabbitmqctl(1), to interact with a running RabbitMQ server, and -rabbitmq-plugins(1), to enable and disable plugins. These should be -run as the superuser. - -An example configuration file is provided in the same directory as -this README. Copy it to /etc/rabbitmq/rabbitmq.config to use it. The -RabbitMQ server must be restarted after changing the configuration -file or enabling or disabling plugins. diff --git a/debian/changelog b/debian/changelog index 0bf370e..99269f9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +rabbitmq-server (3.5.4-1~u14.04+mos1) mos7.0; urgency=medium + + * Pick the source from Debian Sid, changes: + - build-depend on default version of dh-systemd; + - changelog was taken from original package; + + -- Aleksandr Mogylchenko Thu, 06 Aug 2015 23:00:41 +0200 + rabbitmq-server (3.3.5-1~u14.04+mos2) mos7.0; urgency=medium * Repackaged for 7.0 @@ -343,4 +351,3 @@ rabbitmq-server (1.0.0-alpha-1) unstable; urgency=low * Initial release -- Tony Garnock-Jones Wed, 31 Jan 2007 19:06:33 +0000 - diff --git a/debian/compat b/debian/compat index 45a4fb7..ec63514 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -8 +9 diff --git a/debian/control b/debian/control index 944fae4..81e6f6a 100644 --- a/debian/control +++ b/debian/control @@ -1,16 +1,31 @@ Source: rabbitmq-server Section: net Priority: extra -Maintainer: MOS Linux team -XSBC-Orig-Maintainer: RabbitMQ Team -Build-Depends: cdbs, debhelper (>= 9), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:13.b.3), erlang-src (>= 1:13.b.3), unzip, zip -Standards-Version: 3.9.2 +Maintainer: MOS Linux team +XSBC-Orig-Maintainer: PKG OpenStack +Uploaders: James Page , Thomas Goirand +Build-Depends: debhelper (>= 9~), + dh-systemd (>= 1.14~), + erlang-dev, + erlang-nox (>= 1:13.b.3), + erlang-src (>= 1:13.b.3), + python-simplejson, + unzip, + xmlto, + xsltproc, + zip +Standards-Version: 3.9.6 +Vcs-Browser: http://anonscm.debian.org/gitweb/?p=openstack/rabbitmq-server.git +Vcs-Git: git://anonscm.debian.org/openstack/rabbitmq-server.git +Homepage: http://www.rabbitmq.com/ Package: rabbitmq-server Architecture: all -Depends: erlang-nox (>= 1:13.b.3) | esl-erlang, adduser, logrotate, ${misc:Depends} +Depends: adduser, + erlang-nox (>= 1:13.b.3) | esl-erlang, + logrotate, + ${misc:Depends} Description: AMQP server written in Erlang RabbitMQ is an implementation of AMQP, the emerging standard for high performance enterprise messaging. The RabbitMQ server is a robust and scalable implementation of an AMQP broker. -Homepage: http://www.rabbitmq.com/ diff --git a/debian/copyright b/debian/copyright index e384a7c..3ce58ff 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,567 +1,564 @@ -This package was debianized by Tony Garnock-Jones on -Wed, 3 Jan 2007 15:43:44 +0000. - -It was downloaded from http://www.rabbitmq.com/ - - -This package, the RabbitMQ server is licensed under the MPL. - -If you have any questions regarding licensing, please contact us at -info@rabbitmq.com. - -The files amqp-rabbitmq-0.8.json and amqp-rabbitmq-0.9.1.json are -"Copyright (C) 2008-2013 GoPivotal", Inc. and are covered by the MIT -license. - -jQuery is "Copyright (c) 2010 John Resig" and is covered by the MIT -license. It was downloaded from http://jquery.com/ - -EJS is "Copyright (c) 2007 Edward Benson" and is covered by the MIT -license. It was downloaded from http://embeddedjs.com/ - -Sammy is "Copyright (c) 2008 Aaron Quint, Quirkey NYC, LLC" and is -covered by the MIT license. It was downloaded from -http://code.quirkey.com/sammy/ - -ExplorerCanvas is "Copyright 2006 Google Inc" and is covered by the -Apache License version 2.0. It was downloaded from -http://code.google.com/p/explorercanvas/ - -Flot is "Copyright (c) 2007-2013 IOLA and Ole Laursen" and is covered -by the MIT license. It was downloaded from -http://www.flotcharts.org/ -Webmachine is Copyright (c) Basho Technologies and is covered by the -Apache License 2.0. It was downloaded from http://webmachine.basho.com/ - -Eldap is "Copyright (c) 2010, Torbjorn Tornkvist" and is covered by -the MIT license. It was downloaded from https://github.com/etnt/eldap - -Mochiweb is "Copyright (c) 2007 Mochi Media, Inc." and is covered by -the MIT license. It was downloaded from -http://github.com/mochi/mochiweb/ - -glMatrix is "Copyright (c) 2011, Brandon Jones" and is covered by the -BSD 2-Clause license. It was downloaded from -http://code.google.com/p/glmatrix/ - - -The MIT license is as follows: - - "Permission is hereby granted, free of charge, to any person - obtaining a copy of this file (the Software), to deal in the - Software without restriction, including without limitation the - rights to use, copy, modify, merge, publish, distribute, - sublicense, and/or sell copies of the Software, and to permit - persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE." - - -The BSD 2-Clause license is as follows: - - "Redistribution and use in source and binary forms, with or - without modification, are permitted provided that the - following conditions are met: - - 1. Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - - -The rest of this package is licensed under the Mozilla Public License 1.1 -Authors and Copyright are as described below: - - The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. - - - MOZILLA PUBLIC LICENSE - Version 1.1 - - --------------- - -1. Definitions. - - 1.0.1. "Commercial Use" means distribution or otherwise making the - Covered Code available to a third party. - - 1.1. "Contributor" means each entity that creates or contributes to - the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the Original - Code, prior Modifications used by a Contributor, and the Modifications - made by that particular Contributor. - - 1.3. "Covered Code" means the Original Code or Modifications or the - combination of the Original Code and Modifications, in each case - including portions thereof. - - 1.4. "Electronic Distribution Mechanism" means a mechanism generally - accepted in the software development community for the electronic - transfer of data. - - 1.5. "Executable" means Covered Code in any form other than Source - Code. - - 1.6. "Initial Developer" means the individual or entity identified - as the Initial Developer in the Source Code notice required by Exhibit - A. - - 1.7. "Larger Work" means a work which combines Covered Code or - portions thereof with code not governed by the terms of this License. - - 1.8. "License" means this document. - - 1.8.1. "Licensable" means having the right to grant, to the maximum - extent possible, whether at the time of the initial grant or - subsequently acquired, any and all of the rights conveyed herein. - - 1.9. "Modifications" means any addition to or deletion from the - substance or structure of either the Original Code or any previous - Modifications. When Covered Code is released as a series of files, a - Modification is: - A. Any addition to or deletion from the contents of a file - containing Original Code or previous Modifications. - - B. Any new file that contains any part of the Original Code or - previous Modifications. - - 1.10. "Original Code" means Source Code of computer software code - which is described in the Source Code notice required by Exhibit A as - Original Code, and which, at the time of its release under this - License is not already Covered Code governed by this License. - - 1.10.1. "Patent Claims" means any patent claim(s), now owned or - hereafter acquired, including without limitation, method, process, - and apparatus claims, in any patent Licensable by grantor. - - 1.11. "Source Code" means the preferred form of the Covered Code for - making modifications to it, including all modules it contains, plus - any associated interface definition files, scripts used to control - compilation and installation of an Executable, or source code - differential comparisons against either the Original Code or another - well known, available Covered Code of the Contributor's choice. The - Source Code can be in a compressed or archival form, provided the - appropriate decompression or de-archiving software is widely available - for no charge. - - 1.12. "You" (or "Your") means an individual or a legal entity - exercising rights under, and complying with all of the terms of, this - License or a future version of this License issued under Section 6.1. - For legal entities, "You" includes any entity which controls, is - controlled by, or is under common control with You. For purposes of - this definition, "control" means (a) the power, direct or indirect, - to cause the direction or management of such entity, whether by - contract or otherwise, or (b) ownership of more than fifty percent - (50%) of the outstanding shares or beneficial ownership of such - entity. - -2. Source Code License. - - 2.1. The Initial Developer Grant. - The Initial Developer hereby grants You a world-wide, royalty-free, - non-exclusive license, subject to third party intellectual property - claims: - (a) under intellectual property rights (other than patent or - trademark) Licensable by Initial Developer to use, reproduce, - modify, display, perform, sublicense and distribute the Original - Code (or portions thereof) with or without Modifications, and/or - as part of a Larger Work; and - - (b) under Patents Claims infringed by the making, using or - selling of Original Code, to make, have made, use, practice, - sell, and offer for sale, and/or otherwise dispose of the - Original Code (or portions thereof). - - (c) the licenses granted in this Section 2.1(a) and (b) are - effective on the date Initial Developer first distributes - Original Code under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is - granted: 1) for code that You delete from the Original Code; 2) - separate from the Original Code; or 3) for infringements caused - by: i) the modification of the Original Code or ii) the - combination of the Original Code with other software or devices. - - 2.2. Contributor Grant. - Subject to third party intellectual property claims, each Contributor - hereby grants You a world-wide, royalty-free, non-exclusive license - - (a) under intellectual property rights (other than patent or - trademark) Licensable by Contributor, to use, reproduce, modify, - display, perform, sublicense and distribute the Modifications - created by such Contributor (or portions thereof) either on an - unmodified basis, with other Modifications, as Covered Code - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or - selling of Modifications made by that Contributor either alone - and/or in combination with its Contributor Version (or portions - of such combination), to make, use, sell, offer for sale, have - made, and/or otherwise dispose of: 1) Modifications made by that - Contributor (or portions thereof); and 2) the combination of - Modifications made by that Contributor with its Contributor - Version (or portions of such combination). - - (c) the licenses granted in Sections 2.2(a) and 2.2(b) are - effective on the date Contributor first makes Commercial Use of - the Covered Code. - - (d) Notwithstanding Section 2.2(b) above, no patent license is - granted: 1) for any code that Contributor has deleted from the - Contributor Version; 2) separate from the Contributor Version; - 3) for infringements caused by: i) third party modifications of - Contributor Version or ii) the combination of Modifications made - by that Contributor with other software (except as part of the - Contributor Version) or other devices; or 4) under Patent Claims - infringed by Covered Code in the absence of Modifications made by - that Contributor. - -3. Distribution Obligations. - - 3.1. Application of License. - The Modifications which You create or to which You contribute are - governed by the terms of this License, including without limitation - Section 2.2. The Source Code version of Covered Code may be - distributed only under the terms of this License or a future version - of this License released under Section 6.1, and You must include a - copy of this License with every copy of the Source Code You - distribute. You may not offer or impose any terms on any Source Code - version that alters or restricts the applicable version of this - License or the recipients' rights hereunder. However, You may include - an additional document offering the additional rights described in - Section 3.5. - - 3.2. Availability of Source Code. - Any Modification which You create or to which You contribute must be - made available in Source Code form under the terms of this License - either on the same media as an Executable version or via an accepted - Electronic Distribution Mechanism to anyone to whom you made an - Executable version available; and if made available via Electronic - Distribution Mechanism, must remain available for at least twelve (12) - months after the date it initially became available, or at least six - (6) months after a subsequent version of that particular Modification - has been made available to such recipients. You are responsible for - ensuring that the Source Code version remains available even if the - Electronic Distribution Mechanism is maintained by a third party. - - 3.3. Description of Modifications. - You must cause all Covered Code to which You contribute to contain a - file documenting the changes You made to create that Covered Code and - the date of any change. You must include a prominent statement that - the Modification is derived, directly or indirectly, from Original - Code provided by the Initial Developer and including the name of the - Initial Developer in (a) the Source Code, and (b) in any notice in an - Executable version or related documentation in which You describe the - origin or ownership of the Covered Code. - - 3.4. Intellectual Property Matters - (a) Third Party Claims. - If Contributor has knowledge that a license under a third party's - intellectual property rights is required to exercise the rights - granted by such Contributor under Sections 2.1 or 2.2, - Contributor must include a text file with the Source Code - distribution titled "LEGAL" which describes the claim and the - party making the claim in sufficient detail that a recipient will - know whom to contact. If Contributor obtains such knowledge after - the Modification is made available as described in Section 3.2, - Contributor shall promptly modify the LEGAL file in all copies - Contributor makes available thereafter and shall take other steps - (such as notifying appropriate mailing lists or newsgroups) - reasonably calculated to inform those who received the Covered - Code that new knowledge has been obtained. - - (b) Contributor APIs. - If Contributor's Modifications include an application programming - interface and Contributor has knowledge of patent licenses which - are reasonably necessary to implement that API, Contributor must - also include this information in the LEGAL file. - - (c) Representations. - Contributor represents that, except as disclosed pursuant to - Section 3.4(a) above, Contributor believes that Contributor's - Modifications are Contributor's original creation(s) and/or - Contributor has sufficient rights to grant the rights conveyed by - this License. - - 3.5. Required Notices. - You must duplicate the notice in Exhibit A in each file of the Source - Code. If it is not possible to put such notice in a particular Source - Code file due to its structure, then You must include such notice in a - location (such as a relevant directory) where a user would be likely - to look for such a notice. If You created one or more Modification(s) - You may add your name as a Contributor to the notice described in - Exhibit A. You must also duplicate this License in any documentation - for the Source Code where You describe recipients' rights or ownership - rights relating to Covered Code. You may choose to offer, and to - charge a fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Code. However, You - may do so only on Your own behalf, and not on behalf of the Initial - Developer or any Contributor. You must make it absolutely clear than - any such warranty, support, indemnity or liability obligation is - offered by You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred by the - Initial Developer or such Contributor as a result of warranty, - support, indemnity or liability terms You offer. - - 3.6. Distribution of Executable Versions. - You may distribute Covered Code in Executable form only if the - requirements of Section 3.1-3.5 have been met for that Covered Code, - and if You include a notice stating that the Source Code version of - the Covered Code is available under the terms of this License, - including a description of how and where You have fulfilled the - obligations of Section 3.2. The notice must be conspicuously included - in any notice in an Executable version, related documentation or - collateral in which You describe recipients' rights relating to the - Covered Code. You may distribute the Executable version of Covered - Code or ownership rights under a license of Your choice, which may - contain terms different from this License, provided that You are in - compliance with the terms of this License and that the license for the - Executable version does not attempt to limit or alter the recipient's - rights in the Source Code version from the rights set forth in this - License. If You distribute the Executable version under a different - license You must make it absolutely clear that any terms which differ - from this License are offered by You alone, not by the Initial - Developer or any Contributor. You hereby agree to indemnify the - Initial Developer and every Contributor for any liability incurred by - the Initial Developer or such Contributor as a result of any such - terms You offer. - - 3.7. Larger Works. - You may create a Larger Work by combining Covered Code with other code - not governed by the terms of this License and distribute the Larger - Work as a single product. In such a case, You must make sure the - requirements of this License are fulfilled for the Covered Code. - -4. Inability to Comply Due to Statute or Regulation. - - If it is impossible for You to comply with any of the terms of this - License with respect to some or all of the Covered Code due to - statute, judicial order, or regulation then You must: (a) comply with - the terms of this License to the maximum extent possible; and (b) - describe the limitations and the code they affect. Such description - must be included in the LEGAL file described in Section 3.4 and must - be included with all distributions of the Source Code. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Application of this License. - - This License applies to code to which the Initial Developer has - attached the notice in Exhibit A and to related Covered Code. - -6. Versions of the License. - - 6.1. New Versions. - Netscape Communications Corporation ("Netscape") may publish revised - and/or new versions of the License from time to time. Each version - will be given a distinguishing version number. - - 6.2. Effect of New Versions. - Once Covered Code has been published under a particular version of the - License, You may always continue to use it under the terms of that - version. You may also choose to use such Covered Code under the terms - of any subsequent version of the License published by Netscape. No one - other than Netscape has the right to modify the terms applicable to - Covered Code created under this License. - - 6.3. Derivative Works. - If You create or use a modified version of this License (which you may - only do in order to apply it to code which is not already Covered Code - governed by this License), You must (a) rename Your license so that - the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", - "MPL", "NPL" or any confusingly similar phrase do not appear in your - license (except to note that your license differs from this License) - and (b) otherwise make it clear that Your version of the license - contains terms which differ from the Mozilla Public License and - Netscape Public License. (Filling in the name of the Initial - Developer, Original Code or Contributor in the notice described in - Exhibit A shall not of themselves be deemed to be modifications of - this License.) - -7. DISCLAIMER OF WARRANTY. - - COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, - WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF - DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. - THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE - IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, - YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE - COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER - OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -8. TERMINATION. - - 8.1. This License and the rights granted hereunder will terminate - automatically if You fail to comply with terms herein and fail to cure - such breach within 30 days of becoming aware of the breach. All - sublicenses to the Covered Code which are properly granted shall - survive any termination of this License. Provisions which, by their - nature, must remain in effect beyond the termination of this License - shall survive. - - 8.2. If You initiate litigation by asserting a patent infringement - claim (excluding declatory judgment actions) against Initial Developer - or a Contributor (the Initial Developer or Contributor against whom - You file such action is referred to as "Participant") alleging that: - - (a) such Participant's Contributor Version directly or indirectly - infringes any patent, then any and all rights granted by such - Participant to You under Sections 2.1 and/or 2.2 of this License - shall, upon 60 days notice from Participant terminate prospectively, - unless if within 60 days after receipt of notice You either: (i) - agree in writing to pay Participant a mutually agreeable reasonable - royalty for Your past and future use of Modifications made by such - Participant, or (ii) withdraw Your litigation claim with respect to - the Contributor Version against such Participant. If within 60 days - of notice, a reasonable royalty and payment arrangement are not - mutually agreed upon in writing by the parties or the litigation claim - is not withdrawn, the rights granted by Participant to You under - Sections 2.1 and/or 2.2 automatically terminate at the expiration of - the 60 day notice period specified above. - - (b) any software, hardware, or device, other than such Participant's - Contributor Version, directly or indirectly infringes any patent, then - any rights granted to You by such Participant under Sections 2.1(b) - and 2.2(b) are revoked effective as of the date You first made, used, - sold, distributed, or had made, Modifications made by that - Participant. - - 8.3. If You assert a patent infringement claim against Participant - alleging that such Participant's Contributor Version directly or - indirectly infringes any patent where such claim is resolved (such as - by license or settlement) prior to the initiation of patent - infringement litigation, then the reasonable value of the licenses - granted by such Participant under Sections 2.1 or 2.2 shall be taken - into account in determining the amount or value of any payment or - license. - - 8.4. In the event of termination under Sections 8.1 or 8.2 above, - all end user license agreements (excluding distributors and resellers) - which have been validly granted by You or any distributor hereunder - prior to termination shall survive termination. - -9. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL - DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, - OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR - ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY - CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, - WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY - RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW - PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE - EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO - THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -10. U.S. GOVERNMENT END USERS. - - The Covered Code is a "commercial item," as that term is defined in - 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer - software" and "commercial computer software documentation," as such - terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 - C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), - all U.S. Government End Users acquire Covered Code with only those - rights set forth herein. - -11. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. This License shall be governed by - California law provisions (except to the extent applicable law, if - any, provides otherwise), excluding its conflict-of-law provisions. - With respect to disputes in which at least one party is a citizen of, - or an entity chartered or registered to do business in the United - States of America, any litigation relating to this License shall be - subject to the jurisdiction of the Federal Courts of the Northern - District of California, with venue lying in Santa Clara County, - California, with the losing party responsible for costs, including - without limitation, court costs and reasonable attorneys' fees and - expenses. The application of the United Nations Convention on - Contracts for the International Sale of Goods is expressly excluded. - Any law or regulation which provides that the language of a contract - shall be construed against the drafter shall not apply to this - License. - -12. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or indirectly, - out of its utilization of rights under this License and You agree to - work with Initial Developer and Contributors to distribute such - responsibility on an equitable basis. Nothing herein is intended or - shall be deemed to constitute any admission of liability. - -13. MULTIPLE-LICENSED CODE. - - Initial Developer may designate portions of the Covered Code as - "Multiple-Licensed". "Multiple-Licensed" means that the Initial - Developer permits you to utilize portions of the Covered Code under - Your choice of the NPL or the alternative licenses, if any, specified - by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. - - ``The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is RabbitMQ. - - The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.'' - - [NOTE: The text of this Exhibit A may differ slightly from the text of - the notices in the Source Code files of the Original Code. You should - use the text of this Exhibit A rather than the text found in the - Original Code Source Code for Your Modifications.] - - -The Debian packaging is (C) 2007-2013, GoPivotal, Inc. and is licensed -under the MPL 1.1, see above. - +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: RabbitMQ +Source: http://www.rabbitmq.com/ + +Files: debian/* +Copyright: (c) 2007-2013, GoPivotal, Inc. + (c) 2007, Tony Garnock-Jones + (c) 2014, Blair Hester + (c) 2012-2014, Emile Joubert + (c) 2008-2012, John Leuner + (c) 2014, James Page + (c) 2014, Thomas Goirand +License: MPL-1.1 + +Files: codegen/amqp-rabbitmq-*.json +Copyright: (c) 2008-2013, GoPivotal Inc. +License: Expat + +Files: plugins-src/rabbitmq-management/priv/www/js/jquery*.js +Copyright: (c) 2010 John Resig +License: Expat +Comments: Downloaded from http://jquery.com/ + +Files: plugins-src/rabbitmq-management/priv/www/js/ejs* + plugins-src/rabbitmq-management/priv/www/js/tmpl +Copyright: (c) 2007, Edward Benson +License: Expat +Comments: downloaded from http://embeddedjs.com/ + +Files: plugins-src/rabbitmq-management/priv/www/js/sammy*.js +Copyright: (c) 2008 Aaron Quint, Quirkey NYC, LLC +License: Expat +Comments: Downloaded from http://code.quirkey.com/sammy/ + +Files: plugins-src/rabbitmq-management/priv/www/js/excanvas*.js +Copyright: (c) 2006, Google Inc +License: Apache-2.0 +Comments: Downloaded from http://code.google.com/p/explorercanvas/ + +Files: plugins-src/rabbitmq-management/priv/www/js/jquery.flot*.js +Copyright: (c) 2007-2013, IOLA and Ole Laursen +License: Expat +Comments: Downloaded from http://www.flotcharts.org/ + +Files: plugins-src/webmachine-wrapper/* +Copyright: (c) Basho Technologies +License: Apache-2.0 +Comments: Downloaded from http://webmachine.basho.com/ + +Files: plugins-src/eldap-wrapper/* +Copyright: (c) 2010, Torbjorn Tornkvist +License: Expat +Comments: Downloaded from https://github.com/etnt/eldap + +Files: plugins-src/mochiweb-wrapper/mochiweb-git/* +Copyright: (c) 2007, Mochi Media, Inc. +License: Expat +Comments: Downloaded from http://github.com/mochi/mochiweb/ + +Files: + plugins-src/rabbitmq-management-visualiser/priv/www/visualiser/js/glMatrix*.js +Copyright: (c) 2011, Brandon Jones +License: BSD-2-Clause +Comments: Downloaded from http://code.google.com/p/glmatrix/ + +Files: * +Copyright: (c) 2007-2014 GoPivotal, Inc. +License: MPL-1.1 + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this file (the Software), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the following + conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE." + +License: BSD-2-Clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + . + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE." + +License: MPL-1.1 + MOZILLA PUBLIC LICENSE Version 1.1 + 1. Definitions. + . + 1.0.1. "Commercial Use" means distribution or otherwise making the Covered + Code available to a third party. + . + 1.1. "Contributor" means each entity that creates or contributes to the + creation of Modifications. + . + 1.2. "Contributor Version" means the combination of the Original Code, prior + Modifications used by a Contributor, and the Modifications made by that + particular Contributor. + . + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case including + portions thereof. + . + 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted + in the software development community for the electronic transfer of data. + . + 1.5. "Executable" means Covered Code in any form other than Source Code. + . + 1.6. "Initial Developer" means the individual or entity identified as the + Initial Developer in the Source Code notice required by Exhibit A. + . + 1.7. "Larger Work" means a work which combines Covered Code or portions + thereof with code not governed by the terms of this License. + . + 1.8. "License" means this document. + . + 1.8.1. "Licensable" means having the right to grant, to the maximum extent + possible, whether at the time of the initial grant or subsequently acquired, + any and all of the rights conveyed herein. + . + 1.9. "Modifications" means any addition to or deletion from the substance or + structure of either the Original Code or any previous Modifications. When + Covered Code is released as a series of files, a Modification is: + . + A. Any addition to or deletion from the contents of a file containing + Original Code or previous Modifications. + . + B. Any new file that contains any part of the Original Code or previous + Modifications. + . + 1.10. "Original Code" means Source Code of computer software code which is + described in the Source Code notice required by Exhibit A as Original Code, + and which, at the time of its release under this License is not already + Covered Code governed by this License. + . + 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter + acquired, including without limitation, method, process, and apparatus + claims, in any patent Licensable by grantor. + . + 1.11. "Source Code" means the preferred form of the Covered Code for making + modifications to it, including all modules it contains, plus any associated + interface definition files, scripts used to control compilation and + installation of an Executable, or source code differential comparisons + against either the Original Code or another well known, available Covered + Code of the Contributor's choice. The Source Code can be in a compressed or + archival form, provided the appropriate decompression or de-archiving + software is widely available for no charge. + . + 1.12. "You" (or "Your") means an individual or a legal entity exercising + rights under, and complying with all of the terms of, this License or a + future version of this License issued under Section 6.1. For legal entities, + "You" includes any entity which controls, is controlled by, or is under + common control with You. For purposes of this definition, "control" means (a) + the power, direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial ownership of such + entity. + . + 2. Source Code License. + . + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property claims: + . + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + . + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + . + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + . + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + . + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license + . + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + . + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + . + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + . + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + . + 3. Distribution Obligations. + . + 3.1. Application of License. + . + The Modifications which You create or to which You contribute are governed by + the terms of this License, including without limitation Section 2.2. The + Source Code version of Covered Code may be distributed only under the terms of + this License or a future version of this License released under Section 6.1, + and You must include a copy of this License with every copy of the Source Code + You distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this License or the + recipients' rights hereunder. However, You may include an additional document + offering the additional rights described in Section 3.5. + . + 3.2. Availability of Source Code. + . + Any Modification which You create or to which You contribute must be made + available in Source Code form under the terms of this License either on the + same media as an Executable version or via an accepted Electronic Distribution + Mechanism to anyone to whom you made an Executable version available; and if + made available via Electronic Distribution Mechanism, must remain available + for at least twelve (12) months after the date it initially became available, + or at least six (6) months after a subsequent version of that particular + Modification has been made available to such recipients. You are responsible + for ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + . + 3.3. Description of Modifications. + . + You must cause all Covered Code to which You contribute to contain a file + documenting the changes You made to create that Covered Code and the date of + any change. You must include a prominent statement that the Modification is + derived, directly or indirectly, from Original Code provided by the Initial + Developer and including the name of the Initial Developer in (a) the Source + Code, and (b) in any notice in an Executable version or related documentation + in which You describe the origin or ownership of the Covered Code. + . + 3.4. Intellectual Property Matters + . + (a) Third Party Claims. + . + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + . + (b) Contributor APIs. + . + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + . + (c) Representations. + . + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + . + 3.5. Required Notices. + . + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + . + 3.6. Distribution of Executable Versions. + . + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + . + 3.7. Larger Works. + . + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + . + 4. Inability to Comply Due to Statute or Regulation. + . + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + . + 5. Application of this License. + . + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + . + 6. Versions of the License. + . + 6.1. New Versions. + . + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + . + 6.2. Effect of New Versions. + . + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + . + 6.3. Derivative Works. + . + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + . + 7. DISCLAIMER OF WARRANTY. + . + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + . + 8. TERMINATION. + . + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + . + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + . + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + . + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + . + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + . + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + . + 9. LIMITATION OF LIABILITY. + . + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + . + 10. U.S. GOVERNMENT END USERS. + . + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + . + 11. MISCELLANEOUS. + . + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + . + 12. RESPONSIBILITY FOR CLAIMS. + . + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + . + 13. MULTIPLE-LICENSED CODE. + . + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + . + EXHIBIT A -Mozilla Public License. + . + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ + . + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + the specific language governing rights and limitations under the License. + . + The Original Code is RabbitMQ. + . + The Initial Developer of the Original Code is GoPivotal, Inc. Copyright (c) + 2007-2014 GoPivotal, Inc. All rights reserved. + +License: Apache-2.0 + On Debian GNU/Linux system you can find the complete text of the + Apache-2.0 license in '/usr/share/common-licenses/Apache-2.0' diff --git a/debian/dirs b/debian/dirs deleted file mode 100644 index 625b7d4..0000000 --- a/debian/dirs +++ /dev/null @@ -1,9 +0,0 @@ -usr/lib/rabbitmq/bin -usr/lib/erlang/lib -usr/sbin -usr/share/man -var/lib/rabbitmq/mnesia -var/log/rabbitmq -etc/logrotate.d -etc/rabbitmq - diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..ee339ed --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,6 @@ +[DEFAULT] +debian-branch = master +pristine-tar = True + +[buildpackage] +export-dir = ../build-area/ diff --git a/debian/rabbitmq-server.ocf b/debian/ocf/rabbitmq-server old mode 100644 new mode 100755 similarity index 100% rename from debian/rabbitmq-server.ocf rename to debian/ocf/rabbitmq-server diff --git a/debian/rabbitmq-env.conf b/debian/rabbitmq-env.conf new file mode 100644 index 0000000..bebe2ab --- /dev/null +++ b/debian/rabbitmq-env.conf @@ -0,0 +1,13 @@ +# Defaults to rabbit. This can be useful if you want to run more than one node +# per machine - RABBITMQ_NODENAME should be unique per erlang-node-and-machine +# combination. See the clustering on a single machine guide for details: +# http://www.rabbitmq.com/clustering.html#single-machine +#NODENAME=rabbit + +# By default RabbitMQ will bind to all interfaces, on IPv4 and IPv6 if +# available. Set this if you only want to bind to one network interface or# +# address family. +#NODE_IP_ADDRESS=127.0.0.1 + +# Defaults to 5672. +#NODE_PORT=5672 diff --git a/debian/rabbitmq-script-wrapper b/debian/rabbitmq-script-wrapper old mode 100644 new mode 100755 index 4fecc2e..a622ae2 --- a/debian/rabbitmq-script-wrapper +++ b/debian/rabbitmq-script-wrapper @@ -32,6 +32,9 @@ SCRIPT=`basename $0` if [ `id -u` = `id -u rabbitmq` -a "$SCRIPT" = "rabbitmq-server" ] ; then /usr/lib/rabbitmq/bin/rabbitmq-server "$@" > "/var/log/rabbitmq/startup_log" 2> "/var/log/rabbitmq/startup_err" elif [ `id -u` = `id -u rabbitmq` -o "$SCRIPT" = "rabbitmq-plugins" ] ; then + if [ -f $PWD/.erlang.cookie ] ; then + export HOME=. + fi /usr/lib/rabbitmq/bin/${SCRIPT} "$@" elif [ `id -u` = 0 ] ; then su rabbitmq -s /bin/sh -c "/usr/lib/rabbitmq/bin/${SCRIPT} ${CMDLINE}" diff --git a/debian/rabbitmq-server-wait b/debian/rabbitmq-server-wait new file mode 100755 index 0000000..cdf53e5 --- /dev/null +++ b/debian/rabbitmq-server-wait @@ -0,0 +1,22 @@ +#!/bin/sh -e +## The contents of this file are subject to the Mozilla Public License +## Version 1.1 (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License +## at http://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +## + +# Get default settings with user overrides for (RABBITMQ_) +# Non-empty defaults should be set in rabbitmq-env +. `dirname $0`/rabbitmq-env + +/usr/lib/rabbitmq/bin/rabbitmqctl wait $RABBITMQ_PID_FILE diff --git a/debian/rabbitmq-server.default b/debian/rabbitmq-server.default index 1efb356..bde5e30 100644 --- a/debian/rabbitmq-server.default +++ b/debian/rabbitmq-server.default @@ -6,6 +6,4 @@ # to handle many simultaneous connections. Refer to the system # documentation for ulimit (in man bash) for more information. # -ulimit -H -n 105472 -ulimit -S -n 102400 - +#ulimit -n 1024 diff --git a/debian/rabbitmq-server.dirs b/debian/rabbitmq-server.dirs new file mode 100644 index 0000000..e6127a0 --- /dev/null +++ b/debian/rabbitmq-server.dirs @@ -0,0 +1,3 @@ +usr/lib/erlang/lib +var/lib/rabbitmq/mnesia +var/log/rabbitmq diff --git a/debian/rabbitmq-server.install b/debian/rabbitmq-server.install new file mode 100644 index 0000000..902f3dd --- /dev/null +++ b/debian/rabbitmq-server.install @@ -0,0 +1,4 @@ +debian/ocf/rabbitmq-server /usr/lib/ocf/resource.d/rabbitmq/ +debian/rabbitmq-server-wait /usr/lib/rabbitmq/bin +debian/rabbitmq-script-wrapper /usr/lib/rabbitmq/bin +debian/rabbitmq-env.conf /etc/rabbitmq diff --git a/debian/rabbitmq-server.links b/debian/rabbitmq-server.links new file mode 100644 index 0000000..0bfa1c5 --- /dev/null +++ b/debian/rabbitmq-server.links @@ -0,0 +1,3 @@ +/usr/lib/rabbitmq/bin/rabbitmq-script-wrapper /usr/sbin/rabbitmqctl +/usr/lib/rabbitmq/bin/rabbitmq-script-wrapper /usr/sbin/rabbitmq-server +/usr/lib/rabbitmq/bin/rabbitmq-script-wrapper /usr/sbin/rabbitmq-plugins diff --git a/debian/postinst b/debian/rabbitmq-server.postinst similarity index 100% rename from debian/postinst rename to debian/rabbitmq-server.postinst diff --git a/debian/postrm.in b/debian/rabbitmq-server.postrm similarity index 100% rename from debian/postrm.in rename to debian/rabbitmq-server.postrm diff --git a/debian/rabbitmq-server.service b/debian/rabbitmq-server.service new file mode 100644 index 0000000..faa73c1 --- /dev/null +++ b/debian/rabbitmq-server.service @@ -0,0 +1,15 @@ +[Unit] +Description=RabbitMQ Messaging Server +After=network.target + +[Service] +Type=simple +User=rabbitmq +SyslogIdentifier=rabbitmq +LimitNOFILE=65536 +ExecStart=/usr/sbin/rabbitmq-server +ExecStartPost=/usr/lib/rabbitmq/bin/rabbitmq-server-wait +ExecStop=/usr/sbin/rabbitmqctl stop + +[Install] +WantedBy=multi-user.target diff --git a/debian/rules b/debian/rules old mode 100644 new mode 100755 index cac29c8..489d7df --- a/debian/rules +++ b/debian/rules @@ -1,27 +1,21 @@ #!/usr/bin/make -f +# -*- makefile -*- +#export DH_VERBOSE=1 -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/cdbs/1/class/makefile.mk +%: + dh $@ --parallel --with systemd -RABBIT_LIB=$(DEB_DESTDIR)usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION)/ -RABBIT_BIN=$(DEB_DESTDIR)usr/lib/rabbitmq/bin/ +DEB_UPSTREAM_VERSION=$(shell dpkg-parsechangelog | sed -rne 's,^Version: ([^+]+)-.*,\1,p') +DEB_DESTDIR=debian/rabbitmq-server +RABBIT_LIB=$(DEB_DESTDIR)/usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION) +RABBIT_BIN=$(DEB_DESTDIR)/usr/lib/rabbitmq/bin +DOCDIR=$(DEB_DESTDIR)/usr/share/doc/rabbitmq-server -DOCDIR=$(DEB_DESTDIR)usr/share/doc/rabbitmq-server/ -DEB_MAKE_INSTALL_TARGET := install TARGET_DIR=$(RABBIT_LIB) SBIN_DIR=$(RABBIT_BIN) DOC_INSTALL_DIR=$(DOCDIR) MAN_DIR=$(DEB_DESTDIR)usr/share/man/ -DEB_MAKE_CLEAN_TARGET:= distclean -DEB_INSTALL_DOCS_ALL=debian/README +override_dh_auto_install: + dh_auto_install -- TARGET_DIR=$(RABBIT_LIB) SBIN_DIR=$(RABBIT_BIN) \ + DOC_INSTALL_DIR=$(DOCDIR) MAN_DIR=$(DEB_DESTDIR)/usr/share/man + rm -f $(RABBIT_LIB)/LICENSE* $(RABBIT_LIB)/INSTALL* -DEB_DH_INSTALLINIT_ARGS="--no-start" - -install/rabbitmq-server:: - mkdir -p $(DOCDIR) - rm $(RABBIT_LIB)LICENSE* $(RABBIT_LIB)INSTALL* - for script in rabbitmqctl rabbitmq-server rabbitmq-plugins; do \ - install -p -D -m 0755 debian/rabbitmq-script-wrapper $(DEB_DESTDIR)usr/sbin/$$script; \ - done - sed -e 's|@RABBIT_LIB@|/usr/lib/rabbitmq/lib/rabbitmq_server-$(DEB_UPSTREAM_VERSION)|g' debian/postrm - install -p -D -m 0755 debian/rabbitmq-server.ocf $(DEB_DESTDIR)usr/lib/ocf/resource.d/rabbitmq/rabbitmq-server - install -p -D -m 0644 debian/rabbitmq-server.default $(DEB_DESTDIR)etc/default/rabbitmq-server - -clean:: - rm -f plugins-src/rabbitmq-server debian/postrm plugins/README +override_dh_auto_clean: + rm -f plugins-src/rabbitmq-server plugins/README + dh_auto_clean diff --git a/debian/watch b/debian/watch index b41aff9..e41153d 100644 --- a/debian/watch +++ b/debian/watch @@ -1,4 +1,2 @@ version=3 - -http://www.rabbitmq.com/releases/rabbitmq-server/v(.*)/rabbitmq-server-(\d.*)\.tar\.gz \ - debian uupdate +http://www.rabbitmq.com/releases/rabbitmq-server/v(.*)/rabbitmq-server-(\d.*)\.tar\.gz diff --git a/rabbitmq-server/LICENSE b/rabbitmq-server/LICENSE index 58c43e7..9deeb23 100644 --- a/rabbitmq-server/LICENSE +++ b/rabbitmq-server/LICENSE @@ -96,7 +96,7 @@ The rest of this package is licensed under the Mozilla Public License 1.1 Authors and Copyright are as described below: The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. + Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. MOZILLA PUBLIC LICENSE @@ -548,7 +548,7 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ. The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.'' + Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/rabbitmq-server/Makefile b/rabbitmq-server/Makefile index c54b44e..1c98001 100644 --- a/rabbitmq-server/Makefile +++ b/rabbitmq-server/Makefile @@ -8,24 +8,30 @@ RABBITMQ_LOG_BASE ?= $(TMPDIR) DEPS_FILE=deps.mk SOURCE_DIR=src +TEST_DIR=test/src EBIN_DIR=ebin +TEST_EBIN_DIR=test/ebin INCLUDE_DIR=include DOCS_DIR=docs INCLUDES=$(wildcard $(INCLUDE_DIR)/*.hrl) $(INCLUDE_DIR)/rabbit_framing.hrl SOURCES=$(wildcard $(SOURCE_DIR)/*.erl) $(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl $(USAGES_ERL) +TEST_SOURCES=$(wildcard $(TEST_DIR)/*.erl) BEAM_TARGETS=$(patsubst $(SOURCE_DIR)/%.erl, $(EBIN_DIR)/%.beam, $(SOURCES)) +TEST_BEAM_TARGETS=$(patsubst $(TEST_DIR)/%.erl, $(TEST_EBIN_DIR)/%.beam, $(TEST_SOURCES)) TARGETS=$(EBIN_DIR)/rabbit.app $(INCLUDE_DIR)/rabbit_framing.hrl $(BEAM_TARGETS) plugins +TEST_TARGETS=$(TEST_BEAM_TARGETS) WEB_URL=http://www.rabbitmq.com/ MANPAGES=$(patsubst %.xml, %.gz, $(wildcard $(DOCS_DIR)/*.[0-9].xml)) WEB_MANPAGES=$(patsubst %.xml, %.man.xml, $(wildcard $(DOCS_DIR)/*.[0-9].xml) $(DOCS_DIR)/rabbitmq-service.xml $(DOCS_DIR)/rabbitmq-echopid.xml) USAGES_XML=$(DOCS_DIR)/rabbitmqctl.1.xml $(DOCS_DIR)/rabbitmq-plugins.1.xml USAGES_ERL=$(foreach XML, $(USAGES_XML), $(call usage_xml_to_erl, $(XML))) -QC_MODULES := rabbit_backing_queue_qc -QC_TRIALS ?= 100 ifeq ($(shell python -c 'import simplejson' 2>/dev/null && echo yes),yes) PYTHON=python else +ifeq ($(shell python2.7 -c 'import json' 2>/dev/null && echo yes),yes) +PYTHON=python2.7 +else ifeq ($(shell python2.6 -c 'import simplejson' 2>/dev/null && echo yes),yes) PYTHON=python2.6 else @@ -37,14 +43,15 @@ PYTHON=python endif endif endif +endif BASIC_PLT=basic.plt RABBIT_PLT=rabbit.plt ifndef USE_SPECS -# our type specs rely on callback specs, which are available in R15B +# our type specs rely on dict:dict/0 etc, which are only available in 17.0 # upwards. -USE_SPECS:=$(shell erl -noshell -eval 'io:format([list_to_integer(X) || X <- string:tokens(erlang:system_info(version), ".")] >= [5,9]), halt().') +USE_SPECS:=$(shell erl -noshell -eval 'io:format([list_to_integer(X) || X <- string:tokens(erlang:system_info(version), ".")] >= [5,11]), halt().') endif ifndef USE_PROPER_QC @@ -54,7 +61,13 @@ USE_PROPER_QC:=$(shell erl -noshell -eval 'io:format({module, proper} =:= code:e endif #other args: +native +"{hipe,[o3,verbose]}" -Ddebug=true +debug_info +no_strict_record_tests -ERLC_OPTS=-I $(INCLUDE_DIR) -o $(EBIN_DIR) -Wall -v +debug_info $(call boolean_macro,$(USE_SPECS),use_specs) $(call boolean_macro,$(USE_PROPER_QC),use_proper_qc) +ERLC_OPTS=-I $(INCLUDE_DIR) -Wall -v +debug_info $(call boolean_macro,$(USE_SPECS),use_specs) $(call boolean_macro,$(USE_PROPER_QC),use_proper_qc) + +ifdef INSTRUMENT_FOR_QC +ERLC_OPTS += -DINSTR_MOD=gm_qc +else +ERLC_OPTS += -DINSTR_MOD=gm +endif include version.mk @@ -102,7 +115,7 @@ ifneq "$(DEFAULT_GOAL_MAKE)" "$(firstword $(sort $(DEFAULT_GOAL_MAKE) $(MAKE_VER .DEFAULT_GOAL=all endif -all: $(TARGETS) +all: $(TARGETS) $(TEST_TARGETS) .PHONY: plugins check-xref ifneq "$(PLUGINS_SRC_DIR)" "" @@ -123,7 +136,7 @@ plugins: # Not building plugins check-xref: - $(info xref checks are disabled) + $(info xref checks are disabled as there is no plugins-src directory) endif @@ -135,7 +148,13 @@ $(EBIN_DIR)/rabbit.app: $(EBIN_DIR)/rabbit_app.in $(SOURCES) generate_app escript generate_app $< $@ $(SOURCE_DIR) $(EBIN_DIR)/%.beam: $(SOURCE_DIR)/%.erl | $(DEPS_FILE) - erlc $(ERLC_OPTS) -pa $(EBIN_DIR) $< + erlc -o $(EBIN_DIR) $(ERLC_OPTS) -pa $(EBIN_DIR) $< + +$(TEST_EBIN_DIR)/%.beam: $(TEST_DIR)/%.erl | $(TEST_EBIN_DIR) + erlc -o $(TEST_EBIN_DIR) $(ERLC_OPTS) -pa $(EBIN_DIR) -pa $(TEST_EBIN_DIR) $< + +$(TEST_EBIN_DIR): + mkdir -p $(TEST_EBIN_DIR) $(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $(PYTHON) codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ @@ -169,6 +188,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) clean: rm -f $(EBIN_DIR)/*.beam rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel + rm -rf $(TEST_EBIN_DIR) rm -f $(PLUGINS_DIR)/*.ez [ -d "$(PLUGINS_SRC_DIR)" ] && PLUGINS_SRC_DIR="" PRESERVE_CLONE_DIR=1 make -C $(PLUGINS_SRC_DIR) clean || true rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc @@ -210,19 +230,28 @@ run-background-node: all $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ RABBITMQ_NODE_ONLY=true \ RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ - ./scripts/rabbitmq-server + ./scripts/rabbitmq-server -detached run-tests: all + echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) + echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) -n hare || true OUT=$$(echo "rabbit_tests:all_tests()." | $(ERL_CALL)) ; \ echo $$OUT ; echo $$OUT | grep '^{ok, passed}$$' > /dev/null run-qc: all - $(foreach MOD,$(QC_MODULES),./quickcheck $(RABBITMQ_NODENAME) $(MOD) $(QC_TRIALS)) + echo 'code:add_path("$(TEST_EBIN_DIR)").' | $(ERL_CALL) + ./quickcheck $(RABBITMQ_NODENAME) rabbit_backing_queue_qc 100 40 + ./quickcheck $(RABBITMQ_NODENAME) gm_qc 1000 200 start-background-node: all -rm -f $(RABBITMQ_MNESIA_DIR).pid mkdir -p $(RABBITMQ_MNESIA_DIR) - nohup sh -c "$(MAKE) run-background-node > $(RABBITMQ_MNESIA_DIR)/startup_log 2> $(RABBITMQ_MNESIA_DIR)/startup_err" > /dev/null & + $(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \ + RABBITMQ_NODE_ONLY=true \ + RABBITMQ_SERVER_START_ARGS="$(RABBITMQ_SERVER_START_ARGS)" \ + ./scripts/rabbitmq-server \ + > $(RABBITMQ_MNESIA_DIR)/startup_log \ + 2> $(RABBITMQ_MNESIA_DIR)/startup_err & ./scripts/rabbitmqctl -n $(RABBITMQ_NODENAME) wait $(RABBITMQ_MNESIA_DIR).pid kernel start-rabbit-on-node: all @@ -346,19 +375,22 @@ install_docs: docs_all install_dirs cp $$manpage $(MAN_DIR)/man$$section; \ done; \ done - cp $(DOCS_DIR)/rabbitmq.config.example $(DOC_INSTALL_DIR)/rabbitmq.config.example + if test "$(DOC_INSTALL_DIR)"; then \ + cp $(DOCS_DIR)/rabbitmq.config.example $(DOC_INSTALL_DIR)/rabbitmq.config.example; \ + fi install_dirs: @ OK=true && \ { [ -n "$(TARGET_DIR)" ] || { echo "Please set TARGET_DIR."; OK=false; }; } && \ { [ -n "$(SBIN_DIR)" ] || { echo "Please set SBIN_DIR."; OK=false; }; } && \ - { [ -n "$(MAN_DIR)" ] || { echo "Please set MAN_DIR."; OK=false; }; } && \ - { [ -n "$(DOC_INSTALL_DIR)" ] || { echo "Please set DOC_INSTALL_DIR."; OK=false; }; } && $$OK + { [ -n "$(MAN_DIR)" ] || { echo "Please set MAN_DIR."; OK=false; }; } && $$OK mkdir -p $(TARGET_DIR)/sbin mkdir -p $(SBIN_DIR) mkdir -p $(MAN_DIR) - mkdir -p $(DOC_INSTALL_DIR) + if test "$(DOC_INSTALL_DIR)"; then \ + mkdir -p $(DOC_INSTALL_DIR); \ + fi $(foreach XML,$(USAGES_XML),$(eval $(call usage_dep, $(XML)))) diff --git a/rabbitmq-server/README b/rabbitmq-server/README index 90e99e6..67e3a66 100644 --- a/rabbitmq-server/README +++ b/rabbitmq-server/README @@ -1 +1 @@ -Please see http://www.rabbitmq.com/build-server.html for build instructions. \ No newline at end of file +Please see http://www.rabbitmq.com/build-server.html for build instructions. diff --git a/rabbitmq-server/codegen.py b/rabbitmq-server/codegen.py index b2356bb..9f16b32 100644 --- a/rabbitmq-server/codegen.py +++ b/rabbitmq-server/codegen.py @@ -11,7 +11,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is GoPivotal, Inc. -## Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +## Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. ## from __future__ import nested_scopes @@ -106,7 +106,7 @@ def printFileHeader(): %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %%""" def genErl(spec): diff --git a/rabbitmq-server/codegen/CONTRIBUTING.md b/rabbitmq-server/codegen/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/codegen/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/docs/rabbitmq-plugins.1.xml b/rabbitmq-server/docs/rabbitmq-plugins.1.xml index 8ecb4fc..f7be2d2 100644 --- a/rabbitmq-server/docs/rabbitmq-plugins.1.xml +++ b/rabbitmq-server/docs/rabbitmq-plugins.1.xml @@ -40,6 +40,7 @@ rabbitmq-plugins + -n node command command options @@ -62,6 +63,16 @@ enabled. Implicitly enabled plugins are automatically disabled again when they are no longer required. + + + The enable, disable and + set commands will update the plugins file and + then attempt to connect to the broker and ensure it is running + all enabled plugins. By default if it is not possible to connect + to the running broker (for example if it is stopped) then a + warning is displayed. Specify --online or + --offline to change this behaviour. + @@ -97,12 +108,14 @@ Lists all plugins, their versions, dependencies and - descriptions. Each plugin is prefixed with a status - indicator - [ ] to indicate that the plugin is not - enabled, [E] to indicate that it is explicitly enabled, - [e] to indicate that it is implicitly enabled, and [!] to - indicate that it is enabled but missing and thus not - operational. + descriptions. Each plugin is prefixed with two status + indicator characters inside [ ]. The first indicator can + be " " to indicate that the plugin is not enabled, "E" to + indicate that it is explicitly enabled, "e" to indicate + that it is implicitly enabled, or "!" to indicate that it + is enabled but missing and thus not operational. The + second indicator can be " " to show that the plugin is not + running, or "*" to show that it is. If the optional pattern is given, only plugins whose @@ -130,17 +143,24 @@ - enable plugin ... + enable --offline --online plugin ... + + --offline + Just modify the enabled plugins file. + + + --online + Treat failure to connect to the running broker as fatal. + plugin One or more plugins to enable. - Enables the specified plugins and all their - dependencies. + Enables the specified plugins and all their dependencies. For example: @@ -154,17 +174,24 @@ - disable plugin ... + disable --offline --online plugin ... + + --offline + Just modify the enabled plugins file. + + + --online + Treat failure to connect to the running broker as fatal. + plugin One or more plugins to disable. - Disables the specified plugins and all plugins that - depend on them. + Disables the specified plugins and all their dependencies. For example: @@ -175,6 +202,42 @@ + + + set --offline --online plugin ... + + + + --offline + Just modify the enabled plugins file. + + + --online + Treat failure to connect to the running broker as fatal. + + + plugin + Zero or more plugins to enable. + + + + Enables the specified plugins and all their + dependencies. Unlike rabbitmq-plugins + enable this command ignores and overwrites any + existing enabled plugins. rabbitmq-plugins + set with no plugin arguments is a legal command + meaning "disable all plugins". + + + For example: + rabbitmq-plugins set rabbitmq_management + + This command enables the management + plugin and its dependencies and disables everything else. + + + + diff --git a/rabbitmq-server/docs/rabbitmq-service.xml b/rabbitmq-server/docs/rabbitmq-service.xml index a4bd158..3368960 100644 --- a/rabbitmq-server/docs/rabbitmq-service.xml +++ b/rabbitmq-server/docs/rabbitmq-service.xml @@ -66,8 +66,7 @@ Display usage information. Install the service. The service will not be started. Subsequent invocations will update the service parameters if -relevant environment variables were modified or if the active -plugins were changed. +relevant environment variables were modified. diff --git a/rabbitmq-server/docs/rabbitmq.config.example b/rabbitmq-server/docs/rabbitmq.config.example index a128bfb..9b3855c 100644 --- a/rabbitmq-server/docs/rabbitmq.config.example +++ b/rabbitmq-server/docs/rabbitmq.config.example @@ -27,11 +27,16 @@ %% %% {ssl_listeners, [5671]}, + %% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection + %% and SSL handshake), in milliseconds. + %% + %% {handshake_timeout, 10000}, + %% Log levels (currently just used for connection logging). - %% One of 'info', 'warning', 'error' or 'none', in decreasing order - %% of verbosity. Defaults to 'info'. + %% One of 'debug', 'info', 'warning', 'error' or 'none', in decreasing + %% order of verbosity. Defaults to 'info'. %% - %% {log_levels, [{connection, info}]}, + %% {log_levels, [{connection, info}, {channel, info}]}, %% Set to 'true' to perform reverse DNS lookups when accepting a %% connection. Hostnames will then be shown instead of IP addresses @@ -103,7 +108,7 @@ %% This pertains to both the rabbitmq_auth_mechanism_ssl plugin and %% STOMP ssl_cert_login configurations. See the rabbitmq_stomp - %% configuration section later in this fail and the README in + %% configuration section later in this file and the README in %% https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further %% details. %% @@ -111,6 +116,10 @@ %% %% {ssl_cert_login_from, common_name}, + %% SSL handshake timeout, in milliseconds. + %% + %% {ssl_handshake_timeout, 5000}, + %% %% Default User / VHost %% ==================== @@ -211,6 +220,13 @@ %% %% {cluster_nodes, {['rabbit@my.host.com'], disc}}, + %% Interval (in milliseconds) at which we send keepalive messages + %% to other cluster members. Note that this is not the same thing + %% as net_ticktime; missed keepalive messages will not cause nodes + %% to be considered down. + %% + %% {cluster_keepalive_interval, 10000}, + %% Set (internal) statistics collection granularity. %% %% {collect_statistics, none}, @@ -221,7 +237,17 @@ %% Explicitly enable/disable hipe compilation. %% - %% {hipe_compile, true} + %% {hipe_compile, true}, + + %% Timeout used when waiting for Mnesia tables in a cluster to + %% become available. + %% + %% {mnesia_table_loading_timeout, 30000}, + + %% Size in bytes below which to embed messages in the queue index. See + %% http://www.rabbitmq.com/persistence-conf.html + %% + %% {queue_index_embed_msgs_below, 4096} ]}, @@ -265,9 +291,13 @@ %% {certfile, "/path/to/cert.pem"}, %% {keyfile, "/path/to/key.pem"}]}]}, + %% One of 'basic', 'detailed' or 'none'. See + %% http://www.rabbitmq.com/management.html#fine-stats for more details. + %% {rates_mode, basic}, + %% Configure how long aggregated data (such as message rates and queue %% lengths) is retained. Please read the plugin's documentation in - %% https://www.rabbitmq.com/management.html#configuration for more + %% http://www.rabbitmq.com/management.html#configuration for more %% details. %% %% {sample_retention_policies, @@ -276,14 +306,6 @@ %% {detailed, [{10, 5}]}]} ]}, - {rabbitmq_management_agent, - [%% Misc/Advanced Options - %% - %% NB: Change these only if you understand what you are doing! - %% - %% {force_fine_statistics, true} - ]}, - %% ---------------------------------------------------------------------------- %% RabbitMQ Shovel Plugin %% @@ -396,7 +418,8 @@ %% ---------------------------------------------------------------------------- %% RabbitMQ MQTT Adapter %% - %% See http://hg.rabbitmq.com/rabbitmq-mqtt/file/stable/README.md for details + %% See https://github.com/rabbitmq/rabbitmq-mqtt/blob/stable/README.md + %% for details %% ---------------------------------------------------------------------------- {rabbitmq_mqtt, @@ -450,7 +473,7 @@ %% ---------------------------------------------------------------------------- %% RabbitMQ AMQP 1.0 Support %% - %% See http://hg.rabbitmq.com/rabbitmq-amqp1.0/file/default/README.md + %% See https://github.com/rabbitmq/rabbitmq-amqp1.0/blob/stable/README.md %% for details %% ---------------------------------------------------------------------------- diff --git a/rabbitmq-server/docs/rabbitmqctl.1.xml b/rabbitmq-server/docs/rabbitmqctl.1.xml index 01b024a..92d4846 100644 --- a/rabbitmq-server/docs/rabbitmqctl.1.xml +++ b/rabbitmq-server/docs/rabbitmqctl.1.xml @@ -41,6 +41,7 @@ rabbitmqctl -n node + -t timeout -q command command options @@ -92,6 +93,15 @@ + + -t timeout + + + Operation timeout in seconds. Only applicable to "list" commands. + Default is "infinity". + + + @@ -406,11 +416,15 @@ online, except when using the --offline flag. - When using the --offline flag the node you - connect to will become the canonical source for cluster metadata - (e.g. which queues exist), even if it was not before. Therefore - you should use this command on the latest node to shut down if - at all possible. + When using the --offline flag + rabbitmqctl will not attempt to connect to a node as + normal; instead it will temporarily become the node in + order to make the change. This is useful if the node + cannot be started normally. In this case the node will + become the canonical source for cluster metadata + (e.g. which queues exist), even if it was not + before. Therefore you should use this command on the + latest node to shut down if at all possible. For example: rabbitmqctl -n hare@mcnulty forget_cluster_node rabbit@stringer @@ -421,6 +435,41 @@ + + rename_cluster_node oldnode1 newnode1 oldnode2 newnode2 ... + + + Supports renaming of cluster nodes in the local database. + + + This subcommand causes rabbitmqctl to temporarily become + the node in order to make the change. The local cluster + node must therefore be completely stopped; other nodes + can be online or offline. + + + This subcommand takes an even number of arguments, in + pairs representing the old and new names for nodes. You + must specify the old and new names for this node and for + any other nodes that are stopped and being renamed at + the same time. + + + It is possible to stop all nodes and rename them all + simultaneously (in which case old and new names for all + nodes must be given to every node) or stop and rename + nodes one at a time (in which case each node only needs + to be told how its own name is changing). + + For example: + rabbitmqctl rename_cluster_node rabbit@misshelpful rabbit@cordelia + + This command will rename the node + rabbit@misshelpful to the node + rabbit@cordelia. + + + update_cluster_nodes clusternode @@ -453,6 +502,44 @@ + + force_boot + + + Ensure that the node will start next time, even if it + was not the last to shut down. + + + Normally when you shut down a RabbitMQ cluster + altogether, the first node you restart should be the + last one to go down, since it may have seen things + happen that other nodes did not. But sometimes + that's not possible: for instance if the entire cluster + loses power then all nodes may think they were not the + last to shut down. + + + In such a case you can invoke rabbitmqctl + force_boot while the node is down. This will + tell the node to unconditionally start next time you ask + it to. If any changes happened to the cluster after this + node shut down, they will be lost. + + + If the last node to go down is permanently lost then you + should use rabbitmqctl forget_cluster_node + --offline in preference to this command, as it + will ensure that mirrored queues which were mastered on + the lost node get promoted. + + For example: + rabbitmqctl force_boot + + This will force the node not to wait for other nodes + next time it is started. + + + sync_queue queue @@ -502,6 +589,25 @@ + + purge_queue queue + + + + + queue + + + The name of the queue to purge. + + + + + + Purges a queue (removes all messages in it). + + + set_cluster_name name @@ -1147,6 +1253,50 @@ Sum of ready and unacknowledged messages (queue depth). + + messages_ready_ram + Number of messages from messages_ready which are resident in ram. + + + messages_unacknowledged_ram + Number of messages from messages_unacknowledged which are resident in ram. + + + messages_ram + Total number of messages which are resident in ram. + + + messages_persistent + Total number of persistent messages in the queue (will always be 0 for transient queues). + + + message_bytes + Sum of the size of all message bodies in the queue. This does not include the message properties (including headers) or any overhead. + + + message_bytes_ready + Like message_bytes but counting only those messages ready to be delivered to clients. + + + message_bytes_unacknowledged + Like message_bytes but counting only those messages delivered to clients but not yet acknowledged. + + + message_bytes_ram + Like message_bytes but counting only those messages which are in RAM. + + + message_bytes_persistent + Like message_bytes but counting only those messages which are persistent. + + + disk_reads + Total number of times messages have been read from disk by this queue since it started. + + + disk_writes + Total number of times messages have been written to disk by this queue since it started. + consumers Number of consumers. @@ -1175,10 +1325,14 @@ message loss. - status - The status of the queue. Normally - 'running', but may be "{syncing, MsgCount}" if the queue is - synchronising. + state + The state of the queue. Normally + 'running', but may be "{syncing, MsgCount}" if the + queue is synchronising. Queues which are located on + cluster nodes that are currently down will be shown + with a status of 'down' (and most other + queueinfoitems will be + unavailable). @@ -1475,6 +1629,10 @@ send_pend Send queue size. + + connected_at + Date and time this connection was established, as timestamp. + If no connectioninfoitems are @@ -1640,7 +1798,7 @@ Display the name and value of each variable in the - application environment. + application environment for each running application. @@ -1727,7 +1885,9 @@ - Starts tracing. + Starts tracing. Note that the trace state is not + persistent; it will revert to being off if the server is + restarted. diff --git a/rabbitmq-server/ebin/rabbit_app.in b/rabbitmq-server/ebin/rabbit_app.in index aff1472..df58ba3 100644 --- a/rabbitmq-server/ebin/rabbit_app.in +++ b/rabbitmq-server/ebin/rabbit_app.in @@ -1,7 +1,7 @@ {application, rabbit, %% -*- erlang -*- [{description, "RabbitMQ"}, {id, "RabbitMQ"}, - {vsn, "3.3.5"}, + {vsn, "3.5.4"}, {modules, []}, {registered, [rabbit_amqqueue_sup, rabbit_log, @@ -29,6 +29,7 @@ {heartbeat, 580}, {msg_store_file_size_limit, 16777216}, {queue_index_max_journal_entries, 65536}, + {queue_index_embed_msgs_below, 4096}, {default_user, <<"guest">>}, {default_pass, <<"guest">>}, {default_user_tags, [administrator]}, @@ -39,14 +40,19 @@ {server_properties, []}, {collect_statistics, none}, {collect_statistics_interval, 5000}, + {mnesia_table_loading_timeout, 30000}, {auth_mechanisms, ['PLAIN', 'AMQPLAIN']}, {auth_backends, [rabbit_auth_backend_internal]}, {delegate_count, 16}, {trace_vhosts, []}, {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, + {ssl_handshake_timeout, 5000}, + {ssl_allow_poodle_attack, false}, + {handshake_timeout, 10000}, {reverse_dns_lookups, false}, {cluster_partition_handling, ignore}, + {cluster_keepalive_interval, 10000}, {tcp_listen_options, [binary, {packet, raw}, {reuseaddr, true}, @@ -73,5 +79,7 @@ mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, ssl_connection, tls_connection, ssl_record, tls_record, gen_fsm, ssl]}, - {ssl_apps, [asn1, crypto, public_key, ssl]} + {ssl_apps, [asn1, crypto, public_key, ssl]}, + %% see rabbitmq-server#114 + {mirroring_flow_control, true} ]}]}. diff --git a/rabbitmq-server/include/gm_specs.hrl b/rabbitmq-server/include/gm_specs.hrl index 245c23b..5a98e70 100644 --- a/rabbitmq-server/include/gm_specs.hrl +++ b/rabbitmq-server/include/gm_specs.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -ifdef(use_specs). @@ -20,9 +20,9 @@ -type(args() :: any()). -type(members() :: [pid()]). --spec(joined/2 :: (args(), members()) -> callback_result()). --spec(members_changed/3 :: (args(), members(), members()) -> callback_result()). --spec(handle_msg/3 :: (args(), pid(), any()) -> callback_result()). --spec(terminate/2 :: (args(), term()) -> any()). +-spec(joined/2 :: (args(), members()) -> callback_result()). +-spec(members_changed/3 :: (args(), members(),members()) -> callback_result()). +-spec(handle_msg/3 :: (args(), pid(), any()) -> callback_result()). +-spec(handle_terminate/2 :: (args(), term()) -> any()). -endif. diff --git a/rabbitmq-server/include/rabbit.hrl b/rabbitmq-server/include/rabbit.hrl index 5ac3197..ddcfd6a 100644 --- a/rabbitmq-server/include/rabbit.hrl +++ b/rabbitmq-server/include/rabbit.hrl @@ -11,15 +11,20 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% +%% Passed around most places -record(user, {username, tags, - auth_backend, %% Module this user came from - impl %% Scratch space for that module - }). + authz_backends}). %% List of {Module, AuthUserImpl} pairs +%% Passed to auth backends +-record(auth_user, {username, + tags, + impl}). + +%% Implementation for the internal auth backend -record(internal_user, {username, password_hash, tags}). -record(permission, {configure, write, read}). -record(user_vhost, {username, virtual_host}). @@ -39,13 +44,26 @@ -record(resource, {virtual_host, kind, name}). --record(exchange, {name, type, durable, auto_delete, internal, arguments, - scratches, policy, decorators}). --record(exchange_serial, {name, next}). +%% fields described as 'transient' here are cleared when writing to +%% rabbit_durable_ +-record(exchange, { + name, type, durable, auto_delete, internal, arguments, %% immutable + scratches, %% durable, explicitly updated via update_scratch/3 + policy, %% durable, implicitly updated when policy changes + decorators}). %% transient, recalculated in store/1 (i.e. recovery) + +-record(amqqueue, { + name, durable, auto_delete, exclusive_owner = none, %% immutable + arguments, %% immutable + pid, %% durable (just so we know home node) + slave_pids, sync_slave_pids, %% transient + recoverable_slaves, %% durable + policy, %% durable, implicit update as above + gm_pids, %% transient + decorators, %% transient, recalculated as above + state}). %% durable (have we crashed?) --record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, sync_slave_pids, policy, - gm_pids, decorators}). +-record(exchange_serial, {name, next}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). @@ -70,12 +88,12 @@ is_persistent}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, confirm, sender, message, msg_seq_no}). +-record(delivery, {mandatory, confirm, sender, message, msg_seq_no, flow}). -record(amqp_error, {name, explanation = "", method = none}). -record(event, {type, props, reference = undefined, timestamp}). --record(message_properties, {expiry, needs_confirming = false}). +-record(message_properties, {expiry, needs_confirming = false, size}). -record(plugin, {name, %% atom() version, %% string() @@ -86,7 +104,7 @@ %%---------------------------------------------------------------------------- --define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2014 GoPivotal, Inc."). +-define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2015 Pivotal Software, Inc."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). -define(ERTS_MINIMUM, "5.6.3"). @@ -105,9 +123,6 @@ -define(DESIRED_HIBERNATE, 10000). -define(CREDIT_DISC_BOUND, {2000, 500}). -%% This is dictated by `erlang:send_after' on which we depend to implement TTL. --define(MAX_EXPIRY_TIMER, 4294967295). - -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). -define(DELETED_HEADER, <<"BCC">>). diff --git a/rabbitmq-server/include/rabbit_cli.hrl b/rabbitmq-server/include/rabbit_cli.hrl new file mode 100644 index 0000000..1bffc9a --- /dev/null +++ b/rabbitmq-server/include/rabbit_cli.hrl @@ -0,0 +1,50 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. +%% + +-define(NODE_OPT, "-n"). +-define(QUIET_OPT, "-q"). +-define(VHOST_OPT, "-p"). +-define(TIMEOUT_OPT, "-t"). + +-define(VERBOSE_OPT, "-v"). +-define(MINIMAL_OPT, "-m"). +-define(ENABLED_OPT, "-E"). +-define(ENABLED_ALL_OPT, "-e"). + +-define(PRIORITY_OPT, "--priority"). +-define(APPLY_TO_OPT, "--apply-to"). +-define(RAM_OPT, "--ram"). +-define(OFFLINE_OPT, "--offline"). +-define(ONLINE_OPT, "--online"). + + +-define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). +-define(QUIET_DEF, {?QUIET_OPT, flag}). +-define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}). +-define(TIMEOUT_DEF, {?TIMEOUT_OPT, {option, "infinity"}}). + +-define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). +-define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). +-define(ENABLED_DEF, {?ENABLED_OPT, flag}). +-define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). + +-define(PRIORITY_DEF, {?PRIORITY_OPT, {option, "0"}}). +-define(APPLY_TO_DEF, {?APPLY_TO_OPT, {option, "all"}}). +-define(RAM_DEF, {?RAM_OPT, flag}). +-define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). +-define(ONLINE_DEF, {?ONLINE_OPT, flag}). + +-define(RPC_TIMEOUT, infinity). diff --git a/rabbitmq-server/include/rabbit_msg_store.hrl b/rabbitmq-server/include/rabbit_msg_store.hrl index 4e726b0..803ed6b 100644 --- a/rabbitmq-server/include/rabbit_msg_store.hrl +++ b/rabbitmq-server/include/rabbit_msg_store.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -include("rabbit.hrl"). diff --git a/rabbitmq-server/plugins-src/Makefile b/rabbitmq-server/plugins-src/Makefile index 9b9f5e8..4ab8c86 100644 --- a/rabbitmq-server/plugins-src/Makefile +++ b/rabbitmq-server/plugins-src/Makefile @@ -35,15 +35,32 @@ REPOS:= \ toke \ webmachine-wrapper -BRANCH:=default +BRANCH:=master + +UMBRELLA_REPO_FETCH:=$(shell git remote -v 2>/dev/null | awk '/^origin\t.+ \(fetch\)$$/ { print $$2; }') +ifdef UMBRELLA_REPO_FETCH +GIT_CORE_REPOBASE_FETCH:=$(shell dirname $(UMBRELLA_REPO_FETCH)) +GIT_CORE_SUFFIX_FETCH:=$(suffix $(UMBRELLA_REPO_FETCH)) +else +GIT_CORE_REPOBASE_FETCH:=https://github.com/rabbitmq +GIT_CORE_SUFFIX_FETCH:=.git +endif -HG_CORE_REPOBASE:=$(shell dirname `hg paths default 2>/dev/null` 2>/dev/null) -ifndef HG_CORE_REPOBASE -HG_CORE_REPOBASE:=http://hg.rabbitmq.com/ +UMBRELLA_REPO_PUSH:=$(shell git remote -v 2>/dev/null | awk '/^origin\t.+ \(push\)$$/ { print $$2; }') +ifdef UMBRELLA_REPO_PUSH +GIT_CORE_REPOBASE_PUSH:=$(shell dirname $(UMBRELLA_REPO_PUSH)) +GIT_CORE_SUFFIX_PUSH:=$(suffix $(UMBRELLA_REPO_PUSH)) +else +GIT_CORE_REPOBASE_PUSH:=git@github.com:rabbitmq +GIT_CORE_SUFFIX_PUSH:=.git endif VERSION:=0.0.0 +ifndef VERBOSE +QUIET:=@ +endif + #---------------------------------- all: @@ -70,18 +87,18 @@ plugins-srcdist: rm -rf $(PLUGINS_SRC_DIST_DIR) mkdir -p $(PLUGINS_SRC_DIST_DIR)/licensing - rsync -a --exclude '.hg*' rabbitmq-erlang-client $(PLUGINS_SRC_DIST_DIR)/ + rsync -a --exclude '.git*' rabbitmq-erlang-client $(PLUGINS_SRC_DIST_DIR)/ touch $(PLUGINS_SRC_DIST_DIR)/rabbitmq-erlang-client/.srcdist_done - rsync -a --exclude '.hg*' rabbitmq-server $(PLUGINS_SRC_DIST_DIR)/ + rsync -a --exclude '.git*' rabbitmq-server $(PLUGINS_SRC_DIST_DIR)/ touch $(PLUGINS_SRC_DIST_DIR)/rabbitmq-server/.srcdist_done $(MAKE) -f all-packages.mk copy-srcdist VERSION=$(VERSION) PLUGINS_SRC_DIST_DIR=$(PLUGINS_SRC_DIST_DIR) cp Makefile *.mk generate* $(PLUGINS_SRC_DIST_DIR)/ echo "This is the released version of rabbitmq-public-umbrella. \ -You can clone the full version with: hg clone http://hg.rabbitmq.com/rabbitmq-public-umbrella" > $(PLUGINS_SRC_DIST_DIR)/README +You can clone the full version with: git clone https://github.com/rabbitmq/rabbitmq-public-umbrella.git" > $(PLUGINS_SRC_DIST_DIR)/README - PRESERVE_CLONE_DIR=1 make -C $(PLUGINS_SRC_DIST_DIR) clean + PRESERVE_CLONE_DIR=1 $(MAKE) -C $(PLUGINS_SRC_DIST_DIR) clean rm -rf $(PLUGINS_SRC_DIST_DIR)/rabbitmq-server #---------------------------------- @@ -105,11 +122,56 @@ up_c: named_update #---------------------------------- $(REPOS): - hg clone $(HG_CORE_REPOBASE)/$@ + $(QUIET)retries=5; \ + umbrella_branch="$$(git branch | awk '/^\* / { print $$2; }')"; \ + if test "$$umbrella_branch" = "stable"; then \ + branch_arg="-b $$umbrella_branch"; \ + fi; \ + while ! git clone $$branch_arg $(GIT_CORE_REPOBASE_FETCH)/$@$(GIT_CORE_SUFFIX_FETCH); do \ + retries=$$((retries - 1)); \ + if test "$$retries" = 0; then break; fi; \ + sleep 1; \ + done + $(QUIET)test -d $@ + $(QUIET)global_user_name="$$(git config --global user.name)"; \ + global_user_email="$$(git config --global user.email)"; \ + user_name="$$(git config user.name)"; \ + user_email="$$(git config user.email)"; \ + cd $@ && \ + git remote set-url --push origin $(GIT_CORE_REPOBASE_PUSH)/$@$(GIT_CORE_SUFFIX_PUSH) && \ + if test "$$global_user_name" != "$$user_name"; then git config user.name "$$user_name"; fi && \ + if test "$$global_user_email" != "$$user_email"; then git config user.email "$$user_email"; fi + .PHONY: checkout checkout: $(REPOS) +.PHONY: list-repos +list-repos: + @for repo in $(REPOS); do echo $$repo; done + +.PHONY: sync-gituser +sync-gituser: + @global_user_name="$$(git config --global user.name)"; \ + global_user_email="$$(git config --global user.email)"; \ + user_name="$$(git config user.name)"; \ + user_email="$$(git config user.email)"; \ + for repo in $(REPOS); do \ + cd $$repo && \ + git config --unset user.name && \ + git config --unset user.email && \ + if test "$$global_user_name" != "$$user_name"; then git config user.name "$$user_name"; fi && \ + if test "$$global_user_email" != "$$user_email"; then git config user.email "$$user_email"; fi && \ + cd ..; done + +.PHONY: sync-gitremote +sync-gitremote: + @for repo in $(REPOS); do \ + cd $$repo && \ + git remote set-url origin $(GIT_CORE_REPOBASE_FETCH)/$$repo$(GIT_CORE_SUFFIX_FETCH) && \ + git remote set-url --push origin $(GIT_CORE_REPOBASE_PUSH)/$$repo$(GIT_CORE_SUFFIX_PUSH) && \ + cd ..; done + #---------------------------------- # Subrepository management @@ -137,38 +199,42 @@ endef # Do not allow status to fork with -j otherwise output will be garbled .PHONY: status status: checkout - $(foreach DIR,. $(REPOS), \ - (cd $(DIR); OUT=$$(hg st -mad); \ - if \[ ! -z "$$OUT" \]; then echo "\n$(DIR):\n$$OUT"; fi) &&) true + @for repo in . $(REPOS); do \ + echo "$$repo:"; \ + cd "$$repo" && git status -s && cd - >/dev/null; \ + done .PHONY: pull pull: $(foreach DIR,. $(REPOS),$(DIR)+pull) -$(eval $(call repo_targets,. $(REPOS),pull,| %,(cd % && hg pull))) +$(eval $(call repo_targets,. $(REPOS),pull,| %,\ + (cd % && git fetch -p && \ + (! git symbolic-ref -q HEAD || git pull --ff-only)))) .PHONY: update -update: $(foreach DIR,. $(REPOS),$(DIR)+update) - -$(eval $(call repo_targets,. $(REPOS),update,%+pull,(cd % && hg up))) +update: pull .PHONY: named_update named_update: $(foreach DIR,. $(REPOS),$(DIR)+named_update) -$(eval $(call repo_targets,. $(REPOS),named_update,%+pull,\ - (cd % && hg up -C $(BRANCH)))) +$(eval $(call repo_targets,. $(REPOS),named_update,| %,\ + (cd % && git fetch -p && git checkout $(BRANCH) && \ + (! git symbolic-ref -q HEAD || git pull --ff-only)))) .PHONY: tag tag: $(foreach DIR,. $(REPOS),$(DIR)+tag) -$(eval $(call repo_targets,. $(REPOS),tag,| %,(cd % && hg tag $(TAG)))) +$(eval $(call repo_targets,. $(REPOS),tag,| %,\ + (cd % && git tag $(TAG)))) .PHONY: push push: $(foreach DIR,. $(REPOS),$(DIR)+push) -# "|| true" sicne hg push fails if there are no changes -$(eval $(call repo_targets,. $(REPOS),push,| %,(cd % && hg push -f || true))) +$(eval $(call repo_targets,. $(REPOS),push,| %,\ + (cd % && git push && git push --tags))) .PHONY: checkin checkin: $(foreach DIR,. $(REPOS),$(DIR)+checkin) -$(eval $(call repo_targets,. $(REPOS),checkin,| %,(cd % && hg ci))) +$(eval $(call repo_targets,. $(REPOS),checkin,| %,\ + (cd % && (test -z "$$$$(git status -s -uno)" || git commit -a)))) diff --git a/rabbitmq-server/plugins-src/README b/rabbitmq-server/plugins-src/README index ae655c6..58177d4 100644 --- a/rabbitmq-server/plugins-src/README +++ b/rabbitmq-server/plugins-src/README @@ -1 +1 @@ -This is the released version of rabbitmq-public-umbrella. You can clone the full version with: hg clone http://hg.rabbitmq.com/rabbitmq-public-umbrella +This is the released version of rabbitmq-public-umbrella. You can clone the full version with: git clone https://github.com/rabbitmq/rabbitmq-public-umbrella.git diff --git a/rabbitmq-server/plugins-src/cowboy-wrapper/CONTRIBUTING.md b/rabbitmq-server/plugins-src/cowboy-wrapper/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/cowboy-wrapper/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/cowboy-wrapper/cowboy-git/src/cowboy_http_req.erl.orig b/rabbitmq-server/plugins-src/cowboy-wrapper/cowboy-git/src/cowboy_http_req.erl.orig deleted file mode 100644 index bf4ac7a..0000000 --- a/rabbitmq-server/plugins-src/cowboy-wrapper/cowboy-git/src/cowboy_http_req.erl.orig +++ /dev/null @@ -1,815 +0,0 @@ -%% Copyright (c) 2011, Loïc Hoguin -%% Copyright (c) 2011, Anthony Ramine -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -%% @doc HTTP request manipulation API. -%% -%% Almost all functions in this module return a new Req variable. -%% It should always be used instead of the one used in your function call -%% because it keeps the state of the request. It also allows Cowboy to do -%% some lazy evaluation and cache results where possible. --module(cowboy_http_req). - --export([ - method/1, version/1, peer/1, peer_addr/1, - host/1, host_info/1, raw_host/1, port/1, - path/1, path_info/1, raw_path/1, - qs_val/2, qs_val/3, qs_vals/1, raw_qs/1, - binding/2, binding/3, bindings/1, - header/2, header/3, headers/1, - parse_header/2, parse_header/3, - cookie/2, cookie/3, cookies/1, - meta/2, meta/3 -]). %% Request API. - --export([ - body/1, body/2, body_qs/1, - multipart_data/1, multipart_skip/1 -]). %% Request Body API. - --export([ - set_resp_cookie/4, set_resp_header/3, set_resp_body/2, - set_resp_body_fun/3, has_resp_header/2, has_resp_body/1, - reply/2, reply/3, reply/4, - chunked_reply/2, chunked_reply/3, chunk/2, - upgrade_reply/3 -]). %% Response API. - --export([ - compact/1, transport/1 -]). %% Misc API. - --include("include/http.hrl"). --include_lib("eunit/include/eunit.hrl"). - -%% Request API. - -%% @doc Return the HTTP method of the request. --spec method(#http_req{}) -> {cowboy_http:method(), #http_req{}}. -method(Req) -> - {Req#http_req.method, Req}. - -%% @doc Return the HTTP version used for the request. --spec version(#http_req{}) -> {cowboy_http:version(), #http_req{}}. -version(Req) -> - {Req#http_req.version, Req}. - -%% @doc Return the peer address and port number of the remote host. --spec peer(#http_req{}) -> {{inet:ip_address(), inet:ip_port()}, #http_req{}}. -peer(Req=#http_req{socket=Socket, transport=Transport, peer=undefined}) -> - {ok, Peer} = Transport:peername(Socket), - {Peer, Req#http_req{peer=Peer}}; -peer(Req) -> - {Req#http_req.peer, Req}. - -%% @doc Returns the peer address calculated from headers. --spec peer_addr(#http_req{}) -> {inet:ip_address(), #http_req{}}. -peer_addr(Req = #http_req{}) -> - {RealIp, Req1} = header(<<"X-Real-Ip">>, Req), - {ForwardedForRaw, Req2} = header(<<"X-Forwarded-For">>, Req1), - {{PeerIp, _PeerPort}, Req3} = peer(Req2), - ForwardedFor = case ForwardedForRaw of - undefined -> - undefined; - ForwardedForRaw -> - case re:run(ForwardedForRaw, "^(?[^\\,]+)", - [{capture, [first_ip], binary}]) of - {match, [FirstIp]} -> FirstIp; - _Any -> undefined - end - end, - {ok, PeerAddr} = if - is_binary(RealIp) -> inet_parse:address(binary_to_list(RealIp)); - is_binary(ForwardedFor) -> inet_parse:address(binary_to_list(ForwardedFor)); - true -> {ok, PeerIp} - end, - {PeerAddr, Req3}. - -%% @doc Return the tokens for the hostname requested. --spec host(#http_req{}) -> {cowboy_dispatcher:tokens(), #http_req{}}. -host(Req) -> - {Req#http_req.host, Req}. - -%% @doc Return the extra host information obtained from partially matching -%% the hostname using '...'. --spec host_info(#http_req{}) - -> {cowboy_dispatcher:tokens() | undefined, #http_req{}}. -host_info(Req) -> - {Req#http_req.host_info, Req}. - -%% @doc Return the raw host directly taken from the request. --spec raw_host(#http_req{}) -> {binary(), #http_req{}}. -raw_host(Req) -> - {Req#http_req.raw_host, Req}. - -%% @doc Return the port used for this request. --spec port(#http_req{}) -> {inet:ip_port(), #http_req{}}. -port(Req) -> - {Req#http_req.port, Req}. - -%% @doc Return the path segments for the path requested. -%% -%% Following RFC2396, this function may return path segments containing any -%% character, including / if, and only if, a / was escaped -%% and part of a path segment in the path requested. --spec path(#http_req{}) -> {cowboy_dispatcher:tokens(), #http_req{}}. -path(Req) -> - {Req#http_req.path, Req}. - -%% @doc Return the extra path information obtained from partially matching -%% the patch using '...'. --spec path_info(#http_req{}) - -> {cowboy_dispatcher:tokens() | undefined, #http_req{}}. -path_info(Req) -> - {Req#http_req.path_info, Req}. - -%% @doc Return the raw path directly taken from the request. --spec raw_path(#http_req{}) -> {binary(), #http_req{}}. -raw_path(Req) -> - {Req#http_req.raw_path, Req}. - -%% @equiv qs_val(Name, Req, undefined) --spec qs_val(binary(), #http_req{}) - -> {binary() | true | undefined, #http_req{}}. -qs_val(Name, Req) when is_binary(Name) -> - qs_val(Name, Req, undefined). - -%% @doc Return the query string value for the given key, or a default if -%% missing. -qs_val(Name, Req=#http_req{raw_qs=RawQs, qs_vals=undefined, - urldecode={URLDecFun, URLDecArg}}, Default) when is_binary(Name) -> - QsVals = parse_qs(RawQs, fun(Bin) -> URLDecFun(Bin, URLDecArg) end), - qs_val(Name, Req#http_req{qs_vals=QsVals}, Default); -qs_val(Name, Req, Default) -> - case lists:keyfind(Name, 1, Req#http_req.qs_vals) of - {Name, Value} -> {Value, Req}; - false -> {Default, Req} - end. - -%% @doc Return the full list of query string values. --spec qs_vals(#http_req{}) -> {list({binary(), binary() | true}), #http_req{}}. -qs_vals(Req=#http_req{raw_qs=RawQs, qs_vals=undefined, - urldecode={URLDecFun, URLDecArg}}) -> - QsVals = parse_qs(RawQs, fun(Bin) -> URLDecFun(Bin, URLDecArg) end), - qs_vals(Req#http_req{qs_vals=QsVals}); -qs_vals(Req=#http_req{qs_vals=QsVals}) -> - {QsVals, Req}. - -%% @doc Return the raw query string directly taken from the request. --spec raw_qs(#http_req{}) -> {binary(), #http_req{}}. -raw_qs(Req) -> - {Req#http_req.raw_qs, Req}. - -%% @equiv binding(Name, Req, undefined) --spec binding(atom(), #http_req{}) -> {binary() | undefined, #http_req{}}. -binding(Name, Req) when is_atom(Name) -> - binding(Name, Req, undefined). - -%% @doc Return the binding value for the given key obtained when matching -%% the host and path against the dispatch list, or a default if missing. -binding(Name, Req, Default) when is_atom(Name) -> - case lists:keyfind(Name, 1, Req#http_req.bindings) of - {Name, Value} -> {Value, Req}; - false -> {Default, Req} - end. - -%% @doc Return the full list of binding values. --spec bindings(#http_req{}) -> {list({atom(), binary()}), #http_req{}}. -bindings(Req) -> - {Req#http_req.bindings, Req}. - -%% @equiv header(Name, Req, undefined) --spec header(atom() | binary(), #http_req{}) - -> {binary() | undefined, #http_req{}}. -header(Name, Req) when is_atom(Name) orelse is_binary(Name) -> - header(Name, Req, undefined). - -%% @doc Return the header value for the given key, or a default if missing. -header(Name, Req, Default) when is_atom(Name) orelse is_binary(Name) -> - case lists:keyfind(Name, 1, Req#http_req.headers) of - {Name, Value} -> {Value, Req}; - false -> {Default, Req} - end. - -%% @doc Return the full list of headers. --spec headers(#http_req{}) -> {cowboy_http:headers(), #http_req{}}. -headers(Req) -> - {Req#http_req.headers, Req}. - -%% @doc Semantically parse headers. -%% -%% When the value isn't found, a proper default value for the type -%% returned is used as a return value. -%% @see parse_header/3 --spec parse_header(cowboy_http:header(), #http_req{}) - -> {any(), #http_req{}} | {error, badarg}. -parse_header(Name, Req=#http_req{p_headers=PHeaders}) -> - case lists:keyfind(Name, 1, PHeaders) of - false -> parse_header(Name, Req, parse_header_default(Name)); - {Name, Value} -> {Value, Req} - end. - -%% @doc Default values for semantic header parsing. --spec parse_header_default(cowboy_http:header()) -> any(). -parse_header_default('Connection') -> []; -parse_header_default(_Name) -> undefined. - -%% @doc Semantically parse headers. -%% -%% When the header is unknown, the value is returned directly without parsing. --spec parse_header(cowboy_http:header(), #http_req{}, any()) - -> {any(), #http_req{}} | {error, badarg}. -parse_header(Name, Req, Default) when Name =:= 'Accept' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:list(Value, fun cowboy_http:media_range/2) - end); -parse_header(Name, Req, Default) when Name =:= 'Accept-Charset' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:nonempty_list(Value, fun cowboy_http:conneg/2) - end); -parse_header(Name, Req, Default) when Name =:= 'Accept-Encoding' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:list(Value, fun cowboy_http:conneg/2) - end); -parse_header(Name, Req, Default) when Name =:= 'Accept-Language' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:nonempty_list(Value, fun cowboy_http:language_range/2) - end); -parse_header(Name, Req, Default) when Name =:= 'Connection' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) - end); -parse_header(Name, Req, Default) when Name =:= 'Content-Length' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:digits(Value) - end); -parse_header(Name, Req, Default) when Name =:= 'Content-Type' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:content_type(Value) - end); -parse_header(Name, Req, Default) - when Name =:= 'If-Match'; Name =:= 'If-None-Match' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:entity_tag_match(Value) - end); -parse_header(Name, Req, Default) - when Name =:= 'If-Modified-Since'; Name =:= 'If-Unmodified-Since' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:http_date(Value) - end); -parse_header(Name, Req, Default) when Name =:= 'Upgrade' -> - parse_header(Name, Req, Default, - fun (Value) -> - cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) - end); -parse_header(Name, Req, Default) -> - {Value, Req2} = header(Name, Req, Default), - {undefined, Value, Req2}. - -parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) -> - case header(Name, Req) of - {undefined, Req2} -> - {Default, Req2#http_req{p_headers=[{Name, Default}|PHeaders]}}; - {Value, Req2} -> - case Fun(Value) of - {error, badarg} -> - {error, badarg}; - P -> - {P, Req2#http_req{p_headers=[{Name, P}|PHeaders]}} - end - end. - -%% @equiv cookie(Name, Req, undefined) --spec cookie(binary(), #http_req{}) - -> {binary() | true | undefined, #http_req{}}. -cookie(Name, Req) when is_binary(Name) -> - cookie(Name, Req, undefined). - -%% @doc Return the cookie value for the given key, or a default if -%% missing. -cookie(Name, Req=#http_req{cookies=undefined}, Default) when is_binary(Name) -> - case header('Cookie', Req) of - {undefined, Req2} -> - {Default, Req2#http_req{cookies=[]}}; - {RawCookie, Req2} -> - Cookies = cowboy_cookies:parse_cookie(RawCookie), - cookie(Name, Req2#http_req{cookies=Cookies}, Default) - end; -cookie(Name, Req, Default) -> - case lists:keyfind(Name, 1, Req#http_req.cookies) of - {Name, Value} -> {Value, Req}; - false -> {Default, Req} - end. - -%% @doc Return the full list of cookie values. --spec cookies(#http_req{}) -> {list({binary(), binary() | true}), #http_req{}}. -cookies(Req=#http_req{cookies=undefined}) -> - case header('Cookie', Req) of - {undefined, Req2} -> - {[], Req2#http_req{cookies=[]}}; - {RawCookie, Req2} -> - Cookies = cowboy_cookies:parse_cookie(RawCookie), - cookies(Req2#http_req{cookies=Cookies}) - end; -cookies(Req=#http_req{cookies=Cookies}) -> - {Cookies, Req}. - -%% @equiv meta(Name, Req, undefined) --spec meta(atom(), #http_req{}) -> {any() | undefined, #http_req{}}. -meta(Name, Req) -> - meta(Name, Req, undefined). - -%% @doc Return metadata information about the request. -%% -%% Metadata information varies from one protocol to another. Websockets -%% would define the protocol version here, while REST would use it to -%% indicate which media type, language and charset were retained. --spec meta(atom(), #http_req{}, any()) -> {any(), #http_req{}}. -meta(Name, Req, Default) -> - case lists:keyfind(Name, 1, Req#http_req.meta) of - {Name, Value} -> {Value, Req}; - false -> {Default, Req} - end. - -%% Request Body API. - -%% @doc Return the full body sent with the request, or {error, badarg} -%% if no Content-Length is available. -%% @todo We probably want to allow a max length. -%% @todo Add multipart support to this function. --spec body(#http_req{}) -> {ok, binary(), #http_req{}} | {error, atom()}. -body(Req) -> - {Length, Req2} = cowboy_http_req:parse_header('Content-Length', Req), - case Length of - undefined -> {error, badarg}; - {error, badarg} -> {error, badarg}; - _Any -> - body(Length, Req2) - end. - -%% @doc Return Length bytes of the request body. -%% -%% You probably shouldn't be calling this function directly, as it expects the -%% Length argument to be the full size of the body, and will consider -%% the body to be fully read from the socket. -%% @todo We probably want to configure the timeout. --spec body(non_neg_integer(), #http_req{}) - -> {ok, binary(), #http_req{}} | {error, atom()}. -body(Length, Req=#http_req{body_state=waiting, buffer=Buffer}) - when is_integer(Length) andalso Length =< byte_size(Buffer) -> - << Body:Length/binary, Rest/bits >> = Buffer, - {ok, Body, Req#http_req{body_state=done, buffer=Rest}}; -body(Length, Req=#http_req{socket=Socket, transport=Transport, - body_state=waiting, buffer=Buffer}) -> - case Transport:recv(Socket, Length - byte_size(Buffer), 5000) of - {ok, Body} -> {ok, << Buffer/binary, Body/binary >>, - Req#http_req{body_state=done, buffer= <<>>}}; - {error, Reason} -> {error, Reason} - end. - -%% @doc Return the full body sent with the reqest, parsed as an -%% application/x-www-form-urlencoded string. Essentially a POST query string. --spec body_qs(#http_req{}) -> {list({binary(), binary() | true}), #http_req{}}. -body_qs(Req=#http_req{urldecode={URLDecFun, URLDecArg}}) -> - {ok, Body, Req2} = body(Req), - {parse_qs(Body, fun(Bin) -> URLDecFun(Bin, URLDecArg) end), Req2}. - -%% Multipart Request API. - -%% @doc Return data from the multipart parser. -%% -%% Use this function for multipart streaming. For each part in the request, -%% this function returns {headers, Headers} followed by a sequence of -%% {data, Data} tuples and finally end_of_part. When there -%% is no part to parse anymore, eof is returned. -%% -%% If the request Content-Type is not a multipart one, {error, badarg} -%% is returned. --spec multipart_data(#http_req{}) - -> {{headers, cowboy_http:headers()} - | {data, binary()} | end_of_part | eof, - #http_req{}}. -multipart_data(Req=#http_req{body_state=waiting}) -> - {{<<"multipart">>, _SubType, Params}, Req2} = - parse_header('Content-Type', Req), - {_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params), - {Length, Req3=#http_req{buffer=Buffer}} = - parse_header('Content-Length', Req2), - multipart_data(Req3, Length, cowboy_multipart:parser(Boundary), Buffer); -multipart_data(Req=#http_req{body_state={multipart, Length, Cont}}) -> - multipart_data(Req, Length, Cont()); -multipart_data(Req=#http_req{body_state=done}) -> - {eof, Req}. - -multipart_data(Req, Length, Parser, Buffer) when byte_size(Buffer) >= Length -> - << Data:Length/binary, Rest/binary >> = Buffer, - multipart_data(Req#http_req{buffer=Rest}, 0, Parser(Data)); -multipart_data(Req, Length, Parser, Buffer) -> - NewLength = Length - byte_size(Buffer), - multipart_data(Req#http_req{buffer= <<>>}, NewLength, Parser(Buffer)). - -multipart_data(Req, Length, {headers, Headers, Cont}) -> - {{headers, Headers}, Req#http_req{body_state={multipart, Length, Cont}}}; -multipart_data(Req, Length, {body, Data, Cont}) -> - {{body, Data}, Req#http_req{body_state={multipart, Length, Cont}}}; -multipart_data(Req, Length, {end_of_part, Cont}) -> - {end_of_part, Req#http_req{body_state={multipart, Length, Cont}}}; -multipart_data(Req, 0, eof) -> - {eof, Req#http_req{body_state=done}}; -multipart_data(Req=#http_req{socket=Socket, transport=Transport}, - Length, eof) -> - {ok, _Data} = Transport:recv(Socket, Length, 5000), - {eof, Req#http_req{body_state=done}}; -multipart_data(Req=#http_req{socket=Socket, transport=Transport}, - Length, {more, Parser}) when Length > 0 -> - case Transport:recv(Socket, 0, 5000) of - {ok, << Data:Length/binary, Buffer/binary >>} -> - multipart_data(Req#http_req{buffer=Buffer}, 0, Parser(Data)); - {ok, Data} -> - multipart_data(Req, Length - byte_size(Data), Parser(Data)) - end. - -%% @doc Skip a part returned by the multipart parser. -%% -%% This function repeatedly calls multipart_data/1 until -%% end_of_part or eof is parsed. -multipart_skip(Req) -> - case multipart_data(Req) of - {end_of_part, Req2} -> {ok, Req2}; - {eof, Req2} -> {ok, Req2}; - {_Other, Req2} -> multipart_skip(Req2) - end. - -%% Response API. - -%% @doc Add a cookie header to the response. --spec set_resp_cookie(binary(), binary(), [cowboy_cookies:cookie_option()], - #http_req{}) -> {ok, #http_req{}}. -set_resp_cookie(Name, Value, Options, Req) -> - {HeaderName, HeaderValue} = cowboy_cookies:cookie(Name, Value, Options), - set_resp_header(HeaderName, HeaderValue, Req). - -%% @doc Add a header to the response. -set_resp_header(Name, Value, Req=#http_req{resp_headers=RespHeaders}) -> - NameBin = header_to_binary(Name), - {ok, Req#http_req{resp_headers=[{NameBin, Value}|RespHeaders]}}. - -%% @doc Add a body to the response. -%% -%% The body set here is ignored if the response is later sent using -%% anything other than reply/2 or reply/3. The response body is expected -%% to be a binary or an iolist. -set_resp_body(Body, Req) -> - {ok, Req#http_req{resp_body=Body}}. - - -%% @doc Add a body function to the response. -%% -%% The response body may also be set to a content-length - stream-function pair. -%% If the response body is of this type normal response headers will be sent. -%% After the response headers has been sent the body function is applied. -%% The body function is expected to write the response body directly to the -%% socket using the transport module. -%% -%% If the body function crashes while writing the response body or writes fewer -%% bytes than declared the behaviour is undefined. The body set here is ignored -%% if the response is later sent using anything other than `reply/2' or -%% `reply/3'. -%% -%% @see cowboy_http_req:transport/1. --spec set_resp_body_fun(non_neg_integer(), fun(() -> {sent, non_neg_integer()}), - #http_req{}) -> {ok, #http_req{}}. -set_resp_body_fun(StreamLen, StreamFun, Req) -> - {ok, Req#http_req{resp_body={StreamLen, StreamFun}}}. - - -%% @doc Return whether the given header has been set for the response. -has_resp_header(Name, #http_req{resp_headers=RespHeaders}) -> - NameBin = header_to_binary(Name), - lists:keymember(NameBin, 1, RespHeaders). - -%% @doc Return whether a body has been set for the response. -has_resp_body(#http_req{resp_body={Length, _}}) -> - Length > 0; -has_resp_body(#http_req{resp_body=RespBody}) -> - iolist_size(RespBody) > 0. - -%% @equiv reply(Status, [], [], Req) --spec reply(cowboy_http:status(), #http_req{}) -> {ok, #http_req{}}. -reply(Status, Req=#http_req{resp_body=Body}) -> - reply(Status, [], Body, Req). - -%% @equiv reply(Status, Headers, [], Req) --spec reply(cowboy_http:status(), cowboy_http:headers(), #http_req{}) - -> {ok, #http_req{}}. -reply(Status, Headers, Req=#http_req{resp_body=Body}) -> - reply(Status, Headers, Body, Req). - -%% @doc Send a reply to the client. -reply(Status, Headers, Body, Req=#http_req{socket=Socket, - transport=Transport, connection=Connection, pid=ReqPid, - method=Method, resp_state=waiting, resp_headers=RespHeaders}) -> - RespConn = response_connection(Headers, Connection), - ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end, - Head = response_head(Status, Headers, RespHeaders, [ - {<<"Connection">>, atom_to_connection(Connection)}, - {<<"Content-Length">>, integer_to_list(ContentLen)}, - {<<"Date">>, cowboy_clock:rfc1123()}, - {<<"Server">>, <<"Cowboy">>} - ]), - case {Method, Body} of - {'HEAD', _} -> Transport:send(Socket, Head); - {_, {_, StreamFun}} -> Transport:send(Socket, Head), StreamFun(); - {_, _} -> Transport:send(Socket, [Head, Body]) - end, - ReqPid ! {?MODULE, resp_sent}, - {ok, Req#http_req{connection=RespConn, resp_state=done, - resp_headers=[], resp_body= <<>>}}. - -%% @equiv chunked_reply(Status, [], Req) --spec chunked_reply(cowboy_http:status(), #http_req{}) -> {ok, #http_req{}}. -chunked_reply(Status, Req) -> - chunked_reply(Status, [], Req). - -%% @doc Initiate the sending of a chunked reply to the client. -%% @see cowboy_http_req:chunk/2 --spec chunked_reply(cowboy_http:status(), cowboy_http:headers(), #http_req{}) - -> {ok, #http_req{}}. -chunked_reply(Status, Headers, Req=#http_req{socket=Socket, - transport=Transport, connection=Connection, pid=ReqPid, - resp_state=waiting, resp_headers=RespHeaders}) -> - RespConn = response_connection(Headers, Connection), - Head = response_head(Status, Headers, RespHeaders, [ - {<<"Connection">>, atom_to_connection(Connection)}, - {<<"Transfer-Encoding">>, <<"chunked">>}, - {<<"Date">>, cowboy_clock:rfc1123()}, - {<<"Server">>, <<"Cowboy">>} - ]), - Transport:send(Socket, Head), - ReqPid ! {?MODULE, resp_sent}, - {ok, Req#http_req{connection=RespConn, resp_state=chunks, - resp_headers=[], resp_body= <<>>}}. - -%% @doc Send a chunk of data. -%% -%% A chunked reply must have been initiated before calling this function. -chunk(_Data, #http_req{socket=_Socket, transport=_Transport, method='HEAD'}) -> - ok; -chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) -> - Transport:send(Socket, [erlang:integer_to_list(iolist_size(Data), 16), - <<"\r\n">>, Data, <<"\r\n">>]). - -%% @doc Send an upgrade reply. -%% @private --spec upgrade_reply(cowboy_http:status(), cowboy_http:headers(), #http_req{}) - -> {ok, #http_req{}}. -upgrade_reply(Status, Headers, Req=#http_req{socket=Socket, transport=Transport, - pid=ReqPid, resp_state=waiting, resp_headers=RespHeaders}) -> - Head = response_head(Status, Headers, RespHeaders, [ - {<<"Connection">>, <<"Upgrade">>} - ]), - Transport:send(Socket, Head), - ReqPid ! {?MODULE, resp_sent}, - {ok, Req#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}. - -%% Misc API. - -%% @doc Compact the request data by removing all non-system information. -%% -%% This essentially removes the host, path, query string, bindings and headers. -%% Use it when you really need to save up memory, for example when having -%% many concurrent long-running connections. --spec compact(#http_req{}) -> #http_req{}. -compact(Req) -> - Req#http_req{host=undefined, host_info=undefined, path=undefined, - path_info=undefined, qs_vals=undefined, - bindings=undefined, headers=[], - p_headers=[], cookies=[]}. - -%% @doc Return the transport module and socket associated with a request. -%% -%% This exposes the same socket interface used internally by the HTTP protocol -%% implementation to developers that needs low level access to the socket. -%% -%% It is preferred to use this in conjuction with the stream function support -%% in `set_resp_body_fun/3' if this is used to write a response body directly -%% to the socket. This ensures that the response headers are set correctly. --spec transport(#http_req{}) -> {ok, module(), inet:socket()}. -transport(#http_req{transport=Transport, socket=Socket}) -> - {ok, Transport, Socket}. - -%% Internal. - --spec parse_qs(binary(), fun((binary()) -> binary())) -> - list({binary(), binary() | true}). -parse_qs(<<>>, _URLDecode) -> - []; -parse_qs(Qs, URLDecode) -> - Tokens = binary:split(Qs, <<"&">>, [global, trim]), - [case binary:split(Token, <<"=">>) of - [Token] -> {URLDecode(Token), true}; - [Name, Value] -> {URLDecode(Name), URLDecode(Value)} - end || Token <- Tokens]. - --spec response_connection(cowboy_http:headers(), keepalive | close) - -> keepalive | close. -response_connection([], Connection) -> - Connection; -response_connection([{Name, Value}|Tail], Connection) -> - case Name of - 'Connection' -> response_connection_parse(Value); - Name when is_atom(Name) -> response_connection(Tail, Connection); - Name -> - Name2 = cowboy_bstr:to_lower(Name), - case Name2 of - <<"connection">> -> response_connection_parse(Value); - _Any -> response_connection(Tail, Connection) - end - end. - --spec response_connection_parse(binary()) -> keepalive | close. -response_connection_parse(ReplyConn) -> - Tokens = cowboy_http:nonempty_list(ReplyConn, fun cowboy_http:token/2), - cowboy_http:connection_to_atom(Tokens). - --spec response_head(cowboy_http:status(), cowboy_http:headers(), - cowboy_http:headers(), cowboy_http:headers()) -> iolist(). -response_head(Status, Headers, RespHeaders, DefaultHeaders) -> - StatusLine = <<"HTTP/1.1 ", (status(Status))/binary, "\r\n">>, - Headers2 = [{header_to_binary(Key), Value} || {Key, Value} <- Headers], - Headers3 = merge_headers( - merge_headers(Headers2, RespHeaders), - DefaultHeaders), - Headers4 = [[Key, <<": ">>, Value, <<"\r\n">>] - || {Key, Value} <- Headers3], - [StatusLine, Headers4, <<"\r\n">>]. - --spec merge_headers(cowboy_http:headers(), cowboy_http:headers()) - -> cowboy_http:headers(). -merge_headers(Headers, []) -> - Headers; -merge_headers(Headers, [{Name, Value}|Tail]) -> - Headers2 = case lists:keymember(Name, 1, Headers) of - true -> Headers; - false -> Headers ++ [{Name, Value}] - end, - merge_headers(Headers2, Tail). - --spec atom_to_connection(keepalive) -> <<_:80>>; - (close) -> <<_:40>>. -atom_to_connection(keepalive) -> - <<"keep-alive">>; -atom_to_connection(close) -> - <<"close">>. - --spec status(cowboy_http:status()) -> binary(). -status(100) -> <<"100 Continue">>; -status(101) -> <<"101 Switching Protocols">>; -status(102) -> <<"102 Processing">>; -status(200) -> <<"200 OK">>; -status(201) -> <<"201 Created">>; -status(202) -> <<"202 Accepted">>; -status(203) -> <<"203 Non-Authoritative Information">>; -status(204) -> <<"204 No Content">>; -status(205) -> <<"205 Reset Content">>; -status(206) -> <<"206 Partial Content">>; -status(207) -> <<"207 Multi-Status">>; -status(226) -> <<"226 IM Used">>; -status(300) -> <<"300 Multiple Choices">>; -status(301) -> <<"301 Moved Permanently">>; -status(302) -> <<"302 Found">>; -status(303) -> <<"303 See Other">>; -status(304) -> <<"304 Not Modified">>; -status(305) -> <<"305 Use Proxy">>; -status(306) -> <<"306 Switch Proxy">>; -status(307) -> <<"307 Temporary Redirect">>; -status(400) -> <<"400 Bad Request">>; -status(401) -> <<"401 Unauthorized">>; -status(402) -> <<"402 Payment Required">>; -status(403) -> <<"403 Forbidden">>; -status(404) -> <<"404 Not Found">>; -status(405) -> <<"405 Method Not Allowed">>; -status(406) -> <<"406 Not Acceptable">>; -status(407) -> <<"407 Proxy Authentication Required">>; -status(408) -> <<"408 Request Timeout">>; -status(409) -> <<"409 Conflict">>; -status(410) -> <<"410 Gone">>; -status(411) -> <<"411 Length Required">>; -status(412) -> <<"412 Precondition Failed">>; -status(413) -> <<"413 Request Entity Too Large">>; -status(414) -> <<"414 Request-URI Too Long">>; -status(415) -> <<"415 Unsupported Media Type">>; -status(416) -> <<"416 Requested Range Not Satisfiable">>; -status(417) -> <<"417 Expectation Failed">>; -status(418) -> <<"418 I'm a teapot">>; -status(422) -> <<"422 Unprocessable Entity">>; -status(423) -> <<"423 Locked">>; -status(424) -> <<"424 Failed Dependency">>; -status(425) -> <<"425 Unordered Collection">>; -status(426) -> <<"426 Upgrade Required">>; -status(500) -> <<"500 Internal Server Error">>; -status(501) -> <<"501 Not Implemented">>; -status(502) -> <<"502 Bad Gateway">>; -status(503) -> <<"503 Service Unavailable">>; -status(504) -> <<"504 Gateway Timeout">>; -status(505) -> <<"505 HTTP Version Not Supported">>; -status(506) -> <<"506 Variant Also Negotiates">>; -status(507) -> <<"507 Insufficient Storage">>; -status(510) -> <<"510 Not Extended">>; -status(B) when is_binary(B) -> B. - --spec header_to_binary(cowboy_http:header()) -> binary(). -header_to_binary('Cache-Control') -> <<"Cache-Control">>; -header_to_binary('Connection') -> <<"Connection">>; -header_to_binary('Date') -> <<"Date">>; -header_to_binary('Pragma') -> <<"Pragma">>; -header_to_binary('Transfer-Encoding') -> <<"Transfer-Encoding">>; -header_to_binary('Upgrade') -> <<"Upgrade">>; -header_to_binary('Via') -> <<"Via">>; -header_to_binary('Accept') -> <<"Accept">>; -header_to_binary('Accept-Charset') -> <<"Accept-Charset">>; -header_to_binary('Accept-Encoding') -> <<"Accept-Encoding">>; -header_to_binary('Accept-Language') -> <<"Accept-Language">>; -header_to_binary('Authorization') -> <<"Authorization">>; -header_to_binary('From') -> <<"From">>; -header_to_binary('Host') -> <<"Host">>; -header_to_binary('If-Modified-Since') -> <<"If-Modified-Since">>; -header_to_binary('If-Match') -> <<"If-Match">>; -header_to_binary('If-None-Match') -> <<"If-None-Match">>; -header_to_binary('If-Range') -> <<"If-Range">>; -header_to_binary('If-Unmodified-Since') -> <<"If-Unmodified-Since">>; -header_to_binary('Max-Forwards') -> <<"Max-Forwards">>; -header_to_binary('Proxy-Authorization') -> <<"Proxy-Authorization">>; -header_to_binary('Range') -> <<"Range">>; -header_to_binary('Referer') -> <<"Referer">>; -header_to_binary('User-Agent') -> <<"User-Agent">>; -header_to_binary('Age') -> <<"Age">>; -header_to_binary('Location') -> <<"Location">>; -header_to_binary('Proxy-Authenticate') -> <<"Proxy-Authenticate">>; -header_to_binary('Public') -> <<"Public">>; -header_to_binary('Retry-After') -> <<"Retry-After">>; -header_to_binary('Server') -> <<"Server">>; -header_to_binary('Vary') -> <<"Vary">>; -header_to_binary('Warning') -> <<"Warning">>; -header_to_binary('Www-Authenticate') -> <<"Www-Authenticate">>; -header_to_binary('Allow') -> <<"Allow">>; -header_to_binary('Content-Base') -> <<"Content-Base">>; -header_to_binary('Content-Encoding') -> <<"Content-Encoding">>; -header_to_binary('Content-Language') -> <<"Content-Language">>; -header_to_binary('Content-Length') -> <<"Content-Length">>; -header_to_binary('Content-Location') -> <<"Content-Location">>; -header_to_binary('Content-Md5') -> <<"Content-Md5">>; -header_to_binary('Content-Range') -> <<"Content-Range">>; -header_to_binary('Content-Type') -> <<"Content-Type">>; -header_to_binary('Etag') -> <<"Etag">>; -header_to_binary('Expires') -> <<"Expires">>; -header_to_binary('Last-Modified') -> <<"Last-Modified">>; -header_to_binary('Accept-Ranges') -> <<"Accept-Ranges">>; -header_to_binary('Set-Cookie') -> <<"Set-Cookie">>; -header_to_binary('Set-Cookie2') -> <<"Set-Cookie2">>; -header_to_binary('X-Forwarded-For') -> <<"X-Forwarded-For">>; -header_to_binary('Cookie') -> <<"Cookie">>; -header_to_binary('Keep-Alive') -> <<"Keep-Alive">>; -header_to_binary('Proxy-Connection') -> <<"Proxy-Connection">>; -header_to_binary(B) when is_binary(B) -> B. - -%% Tests. - --ifdef(TEST). - -parse_qs_test_() -> - %% {Qs, Result} - Tests = [ - {<<"">>, []}, - {<<"a=b">>, [{<<"a">>, <<"b">>}]}, - {<<"aaa=bbb">>, [{<<"aaa">>, <<"bbb">>}]}, - {<<"a&b">>, [{<<"a">>, true}, {<<"b">>, true}]}, - {<<"a=b&c&d=e">>, [{<<"a">>, <<"b">>}, - {<<"c">>, true}, {<<"d">>, <<"e">>}]}, - {<<"a=b=c=d=e&f=g">>, [{<<"a">>, <<"b=c=d=e">>}, {<<"f">>, <<"g">>}]}, - {<<"a+b=c+d">>, [{<<"a b">>, <<"c d">>}]} - ], - URLDecode = fun cowboy_http:urldecode/1, - [{Qs, fun() -> R = parse_qs(Qs, URLDecode) end} || {Qs, R} <- Tests]. - --endif. diff --git a/rabbitmq-server/plugins-src/do-package.mk b/rabbitmq-server/plugins-src/do-package.mk index a26d446..b76c9a5 100644 --- a/rabbitmq-server/plugins-src/do-package.mk +++ b/rabbitmq-server/plugins-src/do-package.mk @@ -227,6 +227,9 @@ INCLUDE_DIRS+=$(UPSTREAM_INCLUDE_DIRS) define package_rules +# We use --no-backup-if-mismatch to prevent .orig files ending up in +# source builds and causing warnings on Debian if the patches have +# fuzz. ifdef UPSTREAM_GIT $(CLONE_DIR)/.done: rm -rf $(CLONE_DIR) @@ -234,7 +237,8 @@ $(CLONE_DIR)/.done: # Work around weird github breakage (bug 25264) cd $(CLONE_DIR) && git pull $(if $(UPSTREAM_REVISION),cd $(CLONE_DIR) && git checkout $(UPSTREAM_REVISION)) - $(if $(WRAPPER_PATCHES),$(foreach F,$(WRAPPER_PATCHES),patch -d $(CLONE_DIR) -p1 <$(PACKAGE_DIR)/$(F) &&) :) + $(if $(WRAPPER_PATCHES),$(foreach F,$(WRAPPER_PATCHES),patch -E -z .umbrella-orig -d $(CLONE_DIR) -p1 <$(PACKAGE_DIR)/$(F) &&) :) + find $(CLONE_DIR) -name "*.umbrella-orig" -delete touch $$@ endif # UPSTREAM_GIT @@ -242,7 +246,8 @@ ifdef UPSTREAM_HG $(CLONE_DIR)/.done: rm -rf $(CLONE_DIR) hg clone -r $(or $(UPSTREAM_REVISION),default) $(UPSTREAM_HG) $(CLONE_DIR) - $(if $(WRAPPER_PATCHES),$(foreach F,$(WRAPPER_PATCHES),patch -d $(CLONE_DIR) -p1 <$(PACKAGE_DIR)/$(F) &&) :) + $(if $(WRAPPER_PATCHES),$(foreach F,$(WRAPPER_PATCHES),patch -E -z .umbrella-orig -d $(CLONE_DIR) -p1 <$(PACKAGE_DIR)/$(F) &&) :) + find $(CLONE_DIR) -name "*.umbrella-orig" -delete touch $$@ endif # UPSTREAM_HG @@ -299,16 +304,14 @@ define run_broker cp -p $(PACKAGE_DIR)/dist/*.ez $(TEST_TMPDIR)/plugins $(call copy,$(3),$(TEST_TMPDIR)/plugins) rm -f $(TEST_TMPDIR)/plugins/rabbit_common*.ez - for plugin in \ - $$$$(RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ - RABBITMQ_ENABLED_PLUGINS_FILE=$(TEST_TMPDIR)/enabled_plugins \ - $(UMBRELLA_BASE_DIR)/rabbitmq-server/scripts/rabbitmq-plugins list -m); do \ - RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ + RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ RABBITMQ_ENABLED_PLUGINS_FILE=$(TEST_TMPDIR)/enabled_plugins \ $(UMBRELLA_BASE_DIR)/rabbitmq-server/scripts/rabbitmq-plugins \ - enable $$$$plugin; \ - done - RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ + set --offline $$$$(RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ + RABBITMQ_ENABLED_PLUGINS_FILE=$(TEST_TMPDIR)/enabled_plugins \ + $(UMBRELLA_BASE_DIR)/rabbitmq-server/scripts/rabbitmq-plugins list -m | tr '\n' ' ') + MAKE="$(MAKE)" \ + RABBITMQ_PLUGINS_DIR=$(TEST_TMPDIR)/plugins \ RABBITMQ_ENABLED_PLUGINS_FILE=$(TEST_TMPDIR)/enabled_plugins \ RABBITMQ_LOG_BASE=$(TEST_TMPDIR)/log \ RABBITMQ_MNESIA_BASE=$(TEST_TMPDIR)/$(NODENAME) \ @@ -337,13 +340,14 @@ define run_with_broker_tests_aux | $(ERL_CALL) $(ERL_CALL_OPTS) \ | tee -a $(TEST_TMPDIR)/rabbit-test-output \ | egrep "{ok, (ok|passed)}" >/dev/null &&) \ - $(foreach SCRIPT,$(WITH_BROKER_TEST_SCRIPTS),$(SCRIPT) &&) : ; \ + MAKE="$(MAKE)" RABBITMQ_NODENAME="$(NODENAME)" \ + $(foreach SCRIPT,$(WITH_BROKER_TEST_SCRIPTS),$(SCRIPT) &&) : ; \ then \ touch $(TEST_TMPDIR)/.passed ; \ - echo "\nPASSED\n" ; \ + printf "\nPASSED\n" ; \ else \ cat $(TEST_TMPDIR)/rabbit-test-output ; \ - echo "\n\nFAILED\n" ; \ + printf "\n\nFAILED\n" ; \ fi sleep 1 echo "rabbit_misc:report_cover(), init:stop()." | $(ERL_CALL) $(ERL_CALL_OPTS) diff --git a/rabbitmq-server/plugins-src/eldap-wrapper/CONTRIBUTING.md b/rabbitmq-server/plugins-src/eldap-wrapper/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/eldap-wrapper/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/mochiweb-wrapper/CONTRIBUTING.md b/rabbitmq-server/plugins-src/mochiweb-wrapper/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/mochiweb-wrapper/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/mochiweb-wrapper/mochiweb-git/src/mochiweb_request.erl.orig b/rabbitmq-server/plugins-src/mochiweb-wrapper/mochiweb-git/src/mochiweb_request.erl.orig deleted file mode 100644 index 0fea1eb..0000000 --- a/rabbitmq-server/plugins-src/mochiweb-wrapper/mochiweb-git/src/mochiweb_request.erl.orig +++ /dev/null @@ -1,857 +0,0 @@ -%% @author Bob Ippolito -%% @copyright 2007 Mochi Media, Inc. - -%% @doc MochiWeb HTTP Request abstraction. - --module(mochiweb_request). --author('bob@mochimedia.com'). - --include_lib("kernel/include/file.hrl"). --include("internal.hrl"). - --define(QUIP, "Any of you quaids got a smint?"). - --export([new/5]). --export([get_header_value/2, get_primary_header_value/2, get_combined_header_value/2, get/2, dump/1]). --export([send/2, recv/2, recv/3, recv_body/1, recv_body/2, stream_body/4]). --export([start_response/2, start_response_length/2, start_raw_response/2]). --export([respond/2, ok/2]). --export([not_found/1, not_found/2]). --export([parse_post/1, parse_qs/1]). --export([should_close/1, cleanup/1]). --export([parse_cookie/1, get_cookie_value/2]). --export([serve_file/3, serve_file/4]). --export([accepted_encodings/2]). --export([accepts_content_type/2, accepted_content_types/2]). - --define(SAVE_QS, mochiweb_request_qs). --define(SAVE_PATH, mochiweb_request_path). --define(SAVE_RECV, mochiweb_request_recv). --define(SAVE_BODY, mochiweb_request_body). --define(SAVE_BODY_LENGTH, mochiweb_request_body_length). --define(SAVE_POST, mochiweb_request_post). --define(SAVE_COOKIE, mochiweb_request_cookie). --define(SAVE_FORCE_CLOSE, mochiweb_request_force_close). - -%% @type key() = atom() | string() | binary() -%% @type value() = atom() | string() | binary() | integer() -%% @type headers(). A mochiweb_headers structure. -%% @type request(). A mochiweb_request parameterized module instance. -%% @type response(). A mochiweb_response parameterized module instance. -%% @type ioheaders() = headers() | [{key(), value()}]. - -% 5 minute default idle timeout --define(IDLE_TIMEOUT, 300000). - -% Maximum recv_body() length of 1MB --define(MAX_RECV_BODY, (1024*1024)). - -%% @spec new(Socket, Method, RawPath, Version, headers()) -> request() -%% @doc Create a new request instance. -new(Socket, Method, RawPath, Version, Headers) -> - {?MODULE, [Socket, Method, RawPath, Version, Headers]}. - -%% @spec get_header_value(K, request()) -> undefined | Value -%% @doc Get the value of a given request header. -get_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> - mochiweb_headers:get_value(K, Headers). - -get_primary_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> - mochiweb_headers:get_primary_value(K, Headers). - -get_combined_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> - mochiweb_headers:get_combined_value(K, Headers). - -%% @type field() = socket | scheme | method | raw_path | version | headers | peer | path | body_length | range - -%% @spec get(field(), request()) -> term() -%% @doc Return the internal representation of the given field. If -%% socket is requested on a HTTPS connection, then -%% an ssl socket will be returned as {ssl, SslSocket}. -%% You can use SslSocket with the ssl -%% application, eg: ssl:peercert(SslSocket). -get(socket, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - Socket; -get(scheme, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - case mochiweb_socket:type(Socket) of - plain -> - http; - ssl -> - https - end; -get(method, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}) -> - Method; -get(raw_path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> - RawPath; -get(version, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}) -> - Version; -get(headers, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> - Headers; -get(peer, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case mochiweb_socket:peername(Socket) of - {ok, {Addr={10, _, _, _}, _Port}} -> - case get_header_value("x-forwarded-for", THIS) of - undefined -> - inet_parse:ntoa(Addr); - Hosts -> - string:strip(lists:last(string:tokens(Hosts, ","))) - end; - {ok, {{127, 0, 0, 1}, _Port}} -> - case get_header_value("x-forwarded-for", THIS) of - undefined -> - "127.0.0.1"; - Hosts -> - string:strip(lists:last(string:tokens(Hosts, ","))) - end; - {ok, {Addr, _Port}} -> - inet_parse:ntoa(Addr); - {error, enotconn} -> - exit(normal) - end; -get(path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> - case erlang:get(?SAVE_PATH) of - undefined -> - {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath), - Path = mochiweb_util:unquote(Path0), - put(?SAVE_PATH, Path), - Path; - Cached -> - Cached - end; -get(body_length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case erlang:get(?SAVE_BODY_LENGTH) of - undefined -> - BodyLength = body_length(THIS), - put(?SAVE_BODY_LENGTH, {cached, BodyLength}), - BodyLength; - {cached, Cached} -> - Cached - end; -get(range, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case get_header_value(range, THIS) of - undefined -> - undefined; - RawRange -> - mochiweb_http:parse_range_request(RawRange) - end. - -%% @spec dump(request()) -> {mochiweb_request, [{atom(), term()}]} -%% @doc Dump the internal representation to a "human readable" set of terms -%% for debugging/inspection purposes. -dump({?MODULE, [_Socket, Method, RawPath, Version, Headers]}) -> - {?MODULE, [{method, Method}, - {version, Version}, - {raw_path, RawPath}, - {headers, mochiweb_headers:to_list(Headers)}]}. - -%% @spec send(iodata(), request()) -> ok -%% @doc Send data over the socket. -send(Data, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - case mochiweb_socket:send(Socket, Data) of - ok -> - ok; - _ -> - exit(normal) - end. - -%% @spec recv(integer(), request()) -> binary() -%% @doc Receive Length bytes from the client as a binary, with the default -%% idle timeout. -recv(Length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - recv(Length, ?IDLE_TIMEOUT, THIS). - -%% @spec recv(integer(), integer(), request()) -> binary() -%% @doc Receive Length bytes from the client as a binary, with the given -%% Timeout in msec. -recv(Length, Timeout, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - case mochiweb_socket:recv(Socket, Length, Timeout) of - {ok, Data} -> - put(?SAVE_RECV, true), - Data; - _ -> - exit(normal) - end. - -%% @spec body_length(request()) -> undefined | chunked | unknown_transfer_encoding | integer() -%% @doc Infer body length from transfer-encoding and content-length headers. -body_length({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case get_header_value("transfer-encoding", THIS) of - undefined -> - case get_combined_header_value("content-length", THIS) of - undefined -> - undefined; - Length -> - list_to_integer(Length) - end; - "chunked" -> - chunked; - Unknown -> - {unknown_transfer_encoding, Unknown} - end. - - -%% @spec recv_body(request()) -> binary() -%% @doc Receive the body of the HTTP request (defined by Content-Length). -%% Will only receive up to the default max-body length of 1MB. -recv_body({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - recv_body(?MAX_RECV_BODY, THIS). - -%% @spec recv_body(integer(), request()) -> binary() -%% @doc Receive the body of the HTTP request (defined by Content-Length). -%% Will receive up to MaxBody bytes. -recv_body(MaxBody, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case erlang:get(?SAVE_BODY) of - undefined -> - % we could use a sane constant for max chunk size - Body = stream_body(?MAX_RECV_BODY, fun - ({0, _ChunkedFooter}, {_LengthAcc, BinAcc}) -> - iolist_to_binary(lists:reverse(BinAcc)); - ({Length, Bin}, {LengthAcc, BinAcc}) -> - NewLength = Length + LengthAcc, - if NewLength > MaxBody -> - exit({body_too_large, chunked}); - true -> - {NewLength, [Bin | BinAcc]} - end - end, {0, []}, MaxBody, THIS), - put(?SAVE_BODY, Body), - Body; - Cached -> Cached - end. - -stream_body(MaxChunkSize, ChunkFun, FunState, {?MODULE,[_Socket,_Method,_RawPath,_Version,_Headers]}=THIS) -> - stream_body(MaxChunkSize, ChunkFun, FunState, undefined, THIS). - -stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - Expect = case get_header_value("expect", THIS) of - undefined -> - undefined; - Value when is_list(Value) -> - string:to_lower(Value) - end, - case Expect of - "100-continue" -> - _ = start_raw_response({100, gb_trees:empty()}, THIS), - ok; - _Else -> - ok - end, - case body_length(THIS) of - undefined -> - undefined; - {unknown_transfer_encoding, Unknown} -> - exit({unknown_transfer_encoding, Unknown}); - chunked -> - % In this case the MaxBody is actually used to - % determine the maximum allowed size of a single - % chunk. - stream_chunked_body(MaxChunkSize, ChunkFun, FunState, THIS); - 0 -> - <<>>; - Length when is_integer(Length) -> - case MaxBodyLength of - MaxBodyLength when is_integer(MaxBodyLength), MaxBodyLength < Length -> - exit({body_too_large, content_length}); - _ -> - stream_unchunked_body(Length, ChunkFun, FunState, THIS) - end - end. - - -%% @spec start_response({integer(), ioheaders()}, request()) -> response() -%% @doc Start the HTTP response by sending the Code HTTP response and -%% ResponseHeaders. The server will set header defaults such as Server -%% and Date if not present in ResponseHeaders. -start_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - HResponse = mochiweb_headers:make(ResponseHeaders), - HResponse1 = mochiweb_headers:default_from_list(server_headers(), - HResponse), - start_raw_response({Code, HResponse1}, THIS). - -%% @spec start_raw_response({integer(), headers()}, request()) -> response() -%% @doc Start the HTTP response by sending the Code HTTP response and -%% ResponseHeaders. -start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) -> - F = fun ({K, V}, Acc) -> - [mochiweb_util:make_io(K), <<": ">>, V, <<"\r\n">> | Acc] - end, - End = lists:foldl(F, [<<"\r\n">>], - mochiweb_headers:to_list(ResponseHeaders)), - send([make_version(Version), make_code(Code), <<"\r\n">> | End], THIS), - mochiweb:new_response({THIS, Code, ResponseHeaders}). - - -%% @spec start_response_length({integer(), ioheaders(), integer()}, request()) -> response() -%% @doc Start the HTTP response by sending the Code HTTP response and -%% ResponseHeaders including a Content-Length of Length. The server -%% will set header defaults such as Server -%% and Date if not present in ResponseHeaders. -start_response_length({Code, ResponseHeaders, Length}, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - HResponse = mochiweb_headers:make(ResponseHeaders), - HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse), - start_response({Code, HResponse1}, THIS). - -%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}, request()) -> response() -%% @doc Start the HTTP response with start_response, and send Body to the -%% client (if the get(method) /= 'HEAD'). The Content-Length header -%% will be set by the Body length, and the server will insert header -%% defaults. -respond({Code, ResponseHeaders, {file, IoDevice}}, - {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) -> - Length = mochiweb_io:iodevice_size(IoDevice), - Response = start_response_length({Code, ResponseHeaders, Length}, THIS), - case Method of - 'HEAD' -> - ok; - _ -> - mochiweb_io:iodevice_stream( - fun (Body) -> send(Body, THIS) end, - IoDevice) - end, - Response; -respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, Method, _RawPath, Version, _Headers]}=THIS) -> - HResponse = mochiweb_headers:make(ResponseHeaders), - HResponse1 = case Method of - 'HEAD' -> - %% This is what Google does, http://www.google.com/ - %% is chunked but HEAD gets Content-Length: 0. - %% The RFC is ambiguous so emulating Google is smart. - mochiweb_headers:enter("Content-Length", "0", - HResponse); - _ when Version >= {1, 1} -> - %% Only use chunked encoding for HTTP/1.1 - mochiweb_headers:enter("Transfer-Encoding", "chunked", - HResponse); - _ -> - %% For pre-1.1 clients we send the data as-is - %% without a Content-Length header and without - %% chunk delimiters. Since the end of the document - %% is now ambiguous we must force a close. - put(?SAVE_FORCE_CLOSE, true), - HResponse - end, - start_response({Code, HResponse1}, THIS); -respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) -> - Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}, THIS), - case Method of - 'HEAD' -> - ok; - _ -> - send(Body, THIS) - end, - Response. - -%% @spec not_found(request()) -> response() -%% @doc Alias for not_found([]). -not_found({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - not_found([], THIS). - -%% @spec not_found(ExtraHeaders, request()) -> response() -%% @doc Alias for respond({404, [{"Content-Type", "text/plain"} -%% | ExtraHeaders], <<"Not found.">>}). -not_found(ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], - <<"Not found.">>}, THIS). - -%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}, request()) -> -%% response() -%% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}). -ok({ContentType, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - ok({ContentType, [], Body}, THIS); -ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - HResponse = mochiweb_headers:make(ResponseHeaders), - case THIS:get(range) of - X when (X =:= undefined orelse X =:= fail) orelse Body =:= chunked -> - %% http://code.google.com/p/mochiweb/issues/detail?id=54 - %% Range header not supported when chunked, return 200 and provide - %% full response. - HResponse1 = mochiweb_headers:enter("Content-Type", ContentType, - HResponse), - respond({200, HResponse1, Body}, THIS); - Ranges -> - {PartList, Size} = range_parts(Body, Ranges), - case PartList of - [] -> %% no valid ranges - HResponse1 = mochiweb_headers:enter("Content-Type", - ContentType, - HResponse), - %% could be 416, for now we'll just return 200 - respond({200, HResponse1, Body}, THIS); - PartList -> - {RangeHeaders, RangeBody} = - mochiweb_multipart:parts_to_body(PartList, ContentType, Size), - HResponse1 = mochiweb_headers:enter_from_list( - [{"Accept-Ranges", "bytes"} | - RangeHeaders], - HResponse), - respond({206, HResponse1, RangeBody}, THIS) - end - end. - -%% @spec should_close(request()) -> bool() -%% @doc Return true if the connection must be closed. If false, using -%% Keep-Alive should be safe. -should_close({?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) -> - ForceClose = erlang:get(?SAVE_FORCE_CLOSE) =/= undefined, - DidNotRecv = erlang:get(?SAVE_RECV) =:= undefined, - ForceClose orelse Version < {1, 0} - %% Connection: close - orelse is_close(get_header_value("connection", THIS)) - %% HTTP 1.0 requires Connection: Keep-Alive - orelse (Version =:= {1, 0} - andalso get_header_value("connection", THIS) =/= "Keep-Alive") - %% unread data left on the socket, can't safely continue - orelse (DidNotRecv - andalso get_combined_header_value("content-length", THIS) =/= undefined - andalso list_to_integer(get_combined_header_value("content-length", THIS)) > 0) - orelse (DidNotRecv - andalso get_header_value("transfer-encoding", THIS) =:= "chunked"). - -is_close("close") -> - true; -is_close(S=[_C, _L, _O, _S, _E]) -> - string:to_lower(S) =:= "close"; -is_close(_) -> - false. - -%% @spec cleanup(request()) -> ok -%% @doc Clean up any junk in the process dictionary, required before continuing -%% a Keep-Alive request. -cleanup({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) -> - L = [?SAVE_QS, ?SAVE_PATH, ?SAVE_RECV, ?SAVE_BODY, ?SAVE_BODY_LENGTH, - ?SAVE_POST, ?SAVE_COOKIE, ?SAVE_FORCE_CLOSE], - lists:foreach(fun(K) -> - erase(K) - end, L), - ok. - -%% @spec parse_qs(request()) -> [{Key::string(), Value::string()}] -%% @doc Parse the query string of the URL. -parse_qs({?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> - case erlang:get(?SAVE_QS) of - undefined -> - {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath), - Parsed = mochiweb_util:parse_qs(QueryString), - put(?SAVE_QS, Parsed), - Parsed; - Cached -> - Cached - end. - -%% @spec get_cookie_value(Key::string, request()) -> string() | undefined -%% @doc Get the value of the given cookie. -get_cookie_value(Key, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - proplists:get_value(Key, parse_cookie(THIS)). - -%% @spec parse_cookie(request()) -> [{Key::string(), Value::string()}] -%% @doc Parse the cookie header. -parse_cookie({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case erlang:get(?SAVE_COOKIE) of - undefined -> - Cookies = case get_header_value("cookie", THIS) of - undefined -> - []; - Value -> - mochiweb_cookies:parse_cookie(Value) - end, - put(?SAVE_COOKIE, Cookies), - Cookies; - Cached -> - Cached - end. - -%% @spec parse_post(request()) -> [{Key::string(), Value::string()}] -%% @doc Parse an application/x-www-form-urlencoded form POST. This -%% has the side-effect of calling recv_body(). -parse_post({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case erlang:get(?SAVE_POST) of - undefined -> - Parsed = case recv_body(THIS) of - undefined -> - []; - Binary -> - case get_primary_header_value("content-type",THIS) of - "application/x-www-form-urlencoded" ++ _ -> - mochiweb_util:parse_qs(Binary); - _ -> - [] - end - end, - put(?SAVE_POST, Parsed), - Parsed; - Cached -> - Cached - end. - -%% @spec stream_chunked_body(integer(), fun(), term(), request()) -> term() -%% @doc The function is called for each chunk. -%% Used internally by read_chunked_body. -stream_chunked_body(MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case read_chunk_length(THIS) of - 0 -> - Fun({0, read_chunk(0, THIS)}, FunState); - Length when Length > MaxChunkSize -> - NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState, THIS), - stream_chunked_body(MaxChunkSize, Fun, NewState, THIS); - Length -> - NewState = Fun({Length, read_chunk(Length, THIS)}, FunState), - stream_chunked_body(MaxChunkSize, Fun, NewState, THIS) - end. - -stream_unchunked_body(0, Fun, FunState, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) -> - Fun({0, <<>>}, FunState); -stream_unchunked_body(Length, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > 0 -> - PktSize = case Length > ?RECBUF_SIZE of - true -> - ?RECBUF_SIZE; - false -> - Length - end, - Bin = recv(PktSize, THIS), - NewState = Fun({PktSize, Bin}, FunState), - stream_unchunked_body(Length - PktSize, Fun, NewState, THIS). - -%% @spec read_chunk_length(request()) -> integer() -%% @doc Read the length of the next HTTP chunk. -read_chunk_length({?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - ok = mochiweb_socket:setopts(Socket, [{packet, line}]), - case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of - {ok, Header} -> - ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), - Splitter = fun (C) -> - C =/= $\r andalso C =/= $\n andalso C =/= $ - end, - {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)), - mochihex:to_int(Hex); - _ -> - exit(normal) - end. - -%% @spec read_chunk(integer(), request()) -> Chunk::binary() | [Footer::binary()] -%% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the -%% HTTP footers (as a list of binaries, since they're nominal). -read_chunk(0, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - ok = mochiweb_socket:setopts(Socket, [{packet, line}]), - F = fun (F1, Acc) -> - case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of - {ok, <<"\r\n">>} -> - Acc; - {ok, Footer} -> - F1(F1, [Footer | Acc]); - _ -> - exit(normal) - end - end, - Footers = F(F, []), - ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), - put(?SAVE_RECV, true), - Footers; -read_chunk(Length, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> - case mochiweb_socket:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of - {ok, <>} -> - Chunk; - _ -> - exit(normal) - end. - -read_sub_chunks(Length, MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > MaxChunkSize -> - Bin = recv(MaxChunkSize, THIS), - NewState = Fun({size(Bin), Bin}, FunState), - read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState, THIS); - -read_sub_chunks(Length, _MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - Fun({Length, read_chunk(Length, THIS)}, FunState). - -%% @spec serve_file(Path, DocRoot, request()) -> Response -%% @doc Serve a file relative to DocRoot. -serve_file(Path, DocRoot, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - serve_file(Path, DocRoot, [], THIS). - -%% @spec serve_file(Path, DocRoot, ExtraHeaders, request()) -> Response -%% @doc Serve a file relative to DocRoot. -serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case mochiweb_util:safe_relative_path(Path) of - undefined -> - not_found(ExtraHeaders, THIS); - RelPath -> - FullPath = filename:join([DocRoot, RelPath]), - case filelib:is_dir(FullPath) of - true -> - maybe_redirect(RelPath, FullPath, ExtraHeaders, THIS); - false -> - maybe_serve_file(FullPath, ExtraHeaders, THIS) - end - end. - -%% Internal API - -%% This has the same effect as the DirectoryIndex directive in httpd -directory_index(FullPath) -> - filename:join([FullPath, "index.html"]). - -maybe_redirect([], FullPath, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS); - -maybe_redirect(RelPath, FullPath, ExtraHeaders, - {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}=THIS) -> - case string:right(RelPath, 1) of - "/" -> - maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS); - _ -> - Host = mochiweb_headers:get_value("host", Headers), - Location = "http://" ++ Host ++ "/" ++ RelPath ++ "/", - LocationBin = list_to_binary(Location), - MoreHeaders = [{"Location", Location}, - {"Content-Type", "text/html"} | ExtraHeaders], - Top = <<"" - "" - "301 Moved Permanently" - "" - "

Moved Permanently

" - "

The document has moved >, - Bottom = <<">here.

\n">>, - Body = <>, - respond({301, MoreHeaders, Body}, THIS) - end. - -maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case file:read_file_info(File) of - {ok, FileInfo} -> - LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), - case get_header_value("if-modified-since", THIS) of - LastModified -> - respond({304, ExtraHeaders, ""}, THIS); - _ -> - case file:open(File, [raw, binary]) of - {ok, IoDevice} -> - ContentType = mochiweb_util:guess_mime(File), - Res = ok({ContentType, - [{"last-modified", LastModified} - | ExtraHeaders], - {file, IoDevice}}, THIS), - ok = file:close(IoDevice), - Res; - _ -> - not_found(ExtraHeaders, THIS) - end - end; - {error, _} -> - not_found(ExtraHeaders, THIS) - end. - -server_headers() -> - [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"}, - {"Date", httpd_util:rfc1123_date()}]. - -make_code(X) when is_integer(X) -> - [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]]; -make_code(Io) when is_list(Io); is_binary(Io) -> - Io. - -make_version({1, 0}) -> - <<"HTTP/1.0 ">>; -make_version(_) -> - <<"HTTP/1.1 ">>. - -range_parts({file, IoDevice}, Ranges) -> - Size = mochiweb_io:iodevice_size(IoDevice), - F = fun (Spec, Acc) -> - case mochiweb_http:range_skip_length(Spec, Size) of - invalid_range -> - Acc; - V -> - [V | Acc] - end - end, - LocNums = lists:foldr(F, [], Ranges), - {ok, Data} = file:pread(IoDevice, LocNums), - Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) -> - {Skip, Skip + Length - 1, PartialBody} - end, - LocNums, Data), - {Bodies, Size}; -range_parts(Body0, Ranges) -> - Body = iolist_to_binary(Body0), - Size = size(Body), - F = fun(Spec, Acc) -> - case mochiweb_http:range_skip_length(Spec, Size) of - invalid_range -> - Acc; - {Skip, Length} -> - <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body, - [{Skip, Skip + Length - 1, PartialBody} | Acc] - end - end, - {lists:foldr(F, [], Ranges), Size}. - -%% @spec accepted_encodings([encoding()], request()) -> [encoding()] | bad_accept_encoding_value -%% @type encoding() = string(). -%% -%% @doc Returns a list of encodings accepted by a request. Encodings that are -%% not supported by the server will not be included in the return list. -%% This list is computed from the "Accept-Encoding" header and -%% its elements are ordered, descendingly, according to their Q values. -%% -%% Section 14.3 of the RFC 2616 (HTTP 1.1) describes the "Accept-Encoding" -%% header and the process of determining which server supported encodings -%% can be used for encoding the body for the request's response. -%% -%% Examples -%% -%% 1) For a missing "Accept-Encoding" header: -%% accepted_encodings(["gzip", "identity"]) -> ["identity"] -%% -%% 2) For an "Accept-Encoding" header with value "gzip, deflate": -%% accepted_encodings(["gzip", "identity"]) -> ["gzip", "identity"] -%% -%% 3) For an "Accept-Encoding" header with value "gzip;q=0.5, deflate": -%% accepted_encodings(["gzip", "deflate", "identity"]) -> -%% ["deflate", "gzip", "identity"] -%% -accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - AcceptEncodingHeader = case get_header_value("Accept-Encoding", THIS) of - undefined -> - ""; - Value -> - Value - end, - case mochiweb_util:parse_qvalues(AcceptEncodingHeader) of - invalid_qvalue_string -> - bad_accept_encoding_value; - QList -> - mochiweb_util:pick_accepted_encodings( - QList, SupportedEncodings, "identity" - ) - end. - -%% @spec accepts_content_type(string() | binary(), request()) -> boolean() | bad_accept_header -%% -%% @doc Determines whether a request accepts a given media type by analyzing its -%% "Accept" header. -%% -%% Examples -%% -%% 1) For a missing "Accept" header: -%% accepts_content_type("application/json") -> true -%% -%% 2) For an "Accept" header with value "text/plain, application/*": -%% accepts_content_type("application/json") -> true -%% -%% 3) For an "Accept" header with value "text/plain, */*; q=0.0": -%% accepts_content_type("application/json") -> false -%% -%% 4) For an "Accept" header with value "text/plain; q=0.5, */*; q=0.1": -%% accepts_content_type("application/json") -> true -%% -%% 5) For an "Accept" header with value "text/*; q=0.0, */*": -%% accepts_content_type("text/plain") -> false -%% -accepts_content_type(ContentType1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]), - AcceptHeader = accept_header(THIS), - case mochiweb_util:parse_qvalues(AcceptHeader) of - invalid_qvalue_string -> - bad_accept_header; - QList -> - [MainType, _SubType] = string:tokens(ContentType, "/"), - SuperType = MainType ++ "/*", - lists:any( - fun({"*/*", Q}) when Q > 0.0 -> - true; - ({Type, Q}) when Q > 0.0 -> - Type =:= ContentType orelse Type =:= SuperType; - (_) -> - false - end, - QList - ) andalso - (not lists:member({ContentType, 0.0}, QList)) andalso - (not lists:member({SuperType, 0.0}, QList)) - end. - -%% @spec accepted_content_types([string() | binary()], request()) -> [string()] | bad_accept_header -%% -%% @doc Filters which of the given media types this request accepts. This filtering -%% is performed by analyzing the "Accept" header. The returned list is sorted -%% according to the preferences specified in the "Accept" header (higher Q values -%% first). If two or more types have the same preference (Q value), they're order -%% in the returned list is the same as they're order in the input list. -%% -%% Examples -%% -%% 1) For a missing "Accept" header: -%% accepted_content_types(["text/html", "application/json"]) -> -%% ["text/html", "application/json"] -%% -%% 2) For an "Accept" header with value "text/html, application/*": -%% accepted_content_types(["application/json", "text/html"]) -> -%% ["application/json", "text/html"] -%% -%% 3) For an "Accept" header with value "text/html, */*; q=0.0": -%% accepted_content_types(["text/html", "application/json"]) -> -%% ["text/html"] -%% -%% 4) For an "Accept" header with value "text/html; q=0.5, */*; q=0.1": -%% accepts_content_types(["application/json", "text/html"]) -> -%% ["text/html", "application/json"] -%% -accepted_content_types(Types1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - Types = lists:map( - fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end, - Types1), - AcceptHeader = accept_header(THIS), - case mochiweb_util:parse_qvalues(AcceptHeader) of - invalid_qvalue_string -> - bad_accept_header; - QList -> - TypesQ = lists:foldr( - fun(T, Acc) -> - case proplists:get_value(T, QList) of - undefined -> - [MainType, _SubType] = string:tokens(T, "/"), - case proplists:get_value(MainType ++ "/*", QList) of - undefined -> - case proplists:get_value("*/*", QList) of - Q when is_float(Q), Q > 0.0 -> - [{Q, T} | Acc]; - _ -> - Acc - end; - Q when Q > 0.0 -> - [{Q, T} | Acc]; - _ -> - Acc - end; - Q when Q > 0.0 -> - [{Q, T} | Acc]; - _ -> - Acc - end - end, - [], Types), - % Note: Stable sort. If 2 types have the same Q value we leave them in the - % same order as in the input list. - SortFun = fun({Q1, _}, {Q2, _}) -> Q1 >= Q2 end, - [Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)] - end. - -accept_header({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> - case get_header_value("Accept", THIS) of - undefined -> - "*/*"; - Value -> - Value - end. - -%% -%% Tests -%% --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/README.md b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/README.md index a4dca1c..22bac87 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/README.md +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/README.md @@ -4,9 +4,13 @@ This plugin adds AMQP 1.0 support to RabbitMQ. # Status -This is a prototype. You can send and receive messages between 0-9-1 -or 0-8 clients and 1.0 clients with broadly the same semantics as you -would get with 0-9-1. +This is mostly a prototype, but it is supported. We describe it as a +prototype since the amount of real world use and thus battle-testing +it has received is not so large as that of the STOMP or MQTT +plugins. Howver, bugs do get fixed as they are reported. + +You can send and receive messages between 0-9-1 or 0-8 clients and 1.0 +clients with broadly the same semantics as you would get with 0-9-1. # Building and configuring @@ -157,6 +161,7 @@ For targets, addresses are: | "/topic/" RK Publish to amq.topic with routing key RK | "/amq/queue/" Q Publish to default exchange with routing key Q | "/queue/" Q Publish to default exchange with routing key Q + | Q (no leading slash) Publish to default exchange with routing key Q | "/queue" Publish to default exchange with message subj as routing key For sources, addresses are: @@ -165,6 +170,11 @@ For sources, addresses are: | "/topic/" RK Consume from temp queue bound to amq.topic with routing key RK | "/amq/queue/" Q Consume from Q | "/queue/" Q Consume from Q + | Q (no leading slash) Consume from Q + +The intent is that the source and destination address formats should be +mostly the same as those supported by the STOMP plugin, to the extent +permitted by AMQP 1.0 semantics. ## Virtual Hosts diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/codegen.py b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/codegen.py index 8dd63b9..145cfe5 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/codegen.py +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/codegen.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import sys import os import re @@ -87,7 +87,7 @@ def print_hrl(types, defines): def print_define(opt, source): (name, value) = opt if source == 'symbol': - quoted = '"%s"' % value + quoted = '<<"%s">>' % value else: quoted = value print """-define(V_1_0_%s, {%s, %s}).""" % (name, source, quoted) diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_binary_generator.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_binary_generator.erl index 21d7809..2277cb3 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_binary_generator.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_binary_generator.erl @@ -124,4 +124,4 @@ constructor(symbol) -> <<16#a3>>. generate(symbol, Value) -> - [<<(length(Value)):8>>, list_to_binary(Value)]. + [<<(size(Value)):8>>, Value]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_framing.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_framing.erl index c5bde6b..a4b1b60 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_framing.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_framing.erl @@ -64,7 +64,7 @@ keys(Record) -> [{symbol, symbolify(K)} || K <- rabbit_amqp1_0_framing0:fields(Record)]. symbolify(FieldName) when is_atom(FieldName) -> - re:replace(atom_to_list(FieldName), "_", "-", [{return,list}, global]). + re:replace(atom_to_list(FieldName), "_", "-", [{return,binary}, global]). %% TODO: in fields of composite types with multiple=true, "a null %% value and a zero-length array (with a correct type for its @@ -105,23 +105,19 @@ decode(Other) -> decode_map(Fields) -> [{decode(K), decode(V)} || {K, V} <- Fields]. -encode_described(list, ListOrNumber, Frame) -> - Desc = descriptor(ListOrNumber), - {described, Desc, +encode_described(list, CodeNumber, Frame) -> + {described, {ulong, CodeNumber}, {list, lists:map(fun encode/1, tl(tuple_to_list(Frame)))}}; -encode_described(map, ListOrNumber, Frame) -> - Desc = descriptor(ListOrNumber), - {described, Desc, +encode_described(map, CodeNumber, Frame) -> + {described, {ulong, CodeNumber}, {map, lists:zip(keys(Frame), lists:map(fun encode/1, tl(tuple_to_list(Frame))))}}; -encode_described(binary, ListOrNumber, #'v1_0.data'{content = Content}) -> - Desc = descriptor(ListOrNumber), - {described, Desc, {binary, Content}}; -encode_described('*', ListOrNumber, #'v1_0.amqp_value'{content = Content}) -> - Desc = descriptor(ListOrNumber), - {described, Desc, Content}; -encode_described(annotations, ListOrNumber, Frame) -> - encode_described(map, ListOrNumber, Frame). +encode_described(binary, CodeNumber, #'v1_0.data'{content = Content}) -> + {described, {ulong, CodeNumber}, {binary, Content}}; +encode_described('*', CodeNumber, #'v1_0.amqp_value'{content = Content}) -> + {described, {ulong, CodeNumber}, Content}; +encode_described(annotations, CodeNumber, Frame) -> + encode_described(map, CodeNumber, Frame). encode(X) -> rabbit_amqp1_0_framing0:encode(X). @@ -140,12 +136,6 @@ symbol_for(X) -> number_for(X) -> rabbit_amqp1_0_framing0:number_for(X). -descriptor(Symbol) when is_list(Symbol) -> - {symbol, Symbol}; -descriptor(Number) when is_number(Number) -> - {ulong, Number}. - - pprint(Thing) when is_tuple(Thing) -> case rabbit_amqp1_0_framing0:fields(Thing) of unknown -> Thing; diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_incoming_link.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_incoming_link.erl index 245581e..ab5d331 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_incoming_link.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_incoming_link.erl @@ -47,10 +47,10 @@ attach(#'v1_0.attach'{name = Name, case ensure_target(Target, #incoming_link{ name = Name, - route_state = rabbit_routing_util:init_state() }, + route_state = rabbit_routing_util:init_state(), + delivery_count = InitTransfer }, DCh) of - {ok, ServerTarget, - IncomingLink = #incoming_link{ delivery_count = InitTransfer }} -> + {ok, ServerTarget, IncomingLink} -> {_, _Outcomes} = rabbit_amqp1_0_link_util:outcomes(Source), %% Default is mixed Confirm = @@ -81,7 +81,6 @@ attach(#'v1_0.attach'{name = Name, IncomingLink#incoming_link{recv_settle_mode = RcvSettleMode}, {ok, [Attach, Flow], IncomingLink1, Confirm}; {error, Reason} -> - rabbit_log:warning("AMQP 1.0 attach rejected ~p~n", [Reason]), %% TODO proper link establishment protocol here? protocol_error(?V_1_0_AMQP_ERROR_INVALID_FIELD, "Attach rejected: ~p", [Reason]) @@ -194,7 +193,8 @@ ensure_target(Target = #'v1_0.target'{address = Address, timeout = _Timeout}, Link = #incoming_link{ route_state = RouteState }, DCh) -> DeclareParams = [{durable, rabbit_amqp1_0_link_util:durable(Durable)}, - {check_exchange, true}], + {check_exchange, true}, + {nowait, false}], case Dynamic of true -> protocol_error(?V_1_0_AMQP_ERROR_NOT_IMPLEMENTED, @@ -226,7 +226,7 @@ ensure_target(Target = #'v1_0.target'{address = Address, E end; _Else -> - {error, {unknown_address, Address}} + {error, {address_not_utf8_string, Address}} end. incoming_flow(#incoming_link{ delivery_count = Count }, Handle) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_outgoing_link.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_outgoing_link.erl index 2499085..c418ba4 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_outgoing_link.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_outgoing_link.erl @@ -91,9 +91,10 @@ attach(#'v1_0.attach'{name = Name, protocol_error(?V_1_0_AMQP_ERROR_INTERNAL_ERROR, "Consume failed: ~p", [Fail]) end; - {error, _Reason} -> - %% TODO Deal with this properly -- detach and what have you - {ok, [#'v1_0.attach'{source = undefined}]} + {error, Reason} -> + %% TODO proper link establishment protocol here? + protocol_error(?V_1_0_AMQP_ERROR_INVALID_FIELD, + "Attach rejected: ~p", [Reason]) end. credit_drained(#'basic.credit_drained'{credit_drained = CreditDrained}, @@ -156,7 +157,8 @@ ensure_source(Source = #'v1_0.source'{address = Address, timeout = _Timeout}, Link = #outgoing_link{ route_state = RouteState }, DCh) -> DeclareParams = [{durable, rabbit_amqp1_0_link_util:durable(Durable)}, - {check_exchange, true}], + {check_exchange, true}, + {nowait, false}], case Dynamic of true -> protocol_error(?V_1_0_AMQP_ERROR_NOT_IMPLEMENTED, "Dynamic sources not supported", []); @@ -176,10 +178,12 @@ ensure_source(Source = #'v1_0.source'{address = Address, ER = rabbit_routing_util:parse_routing(Dest), ok = rabbit_routing_util:ensure_binding(Queue, ER, DCh), {ok, Source, Link#outgoing_link{route_state = RouteState1, - queue = Queue}} + queue = Queue}}; + {error, _} = E -> + E end; _ -> - {error, {unknown_address, Address}} + {error, {address_not_utf8_string, Address}} end. delivery(Deliver = #'basic.deliver'{delivery_tag = DeliveryTag, diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_reader.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_reader.erl index add5b1a..fbff350 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_reader.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_reader.erl @@ -518,7 +518,7 @@ handle_input({frame_header_1_0, Mode}, end, case Size of 8 -> % length inclusive - {State, {frame_header_1_0, Mode}, 8}; %% heartbeat + State; %% heartbeat _ -> switch_callback(State, {frame_payload_1_0, Mode, DOff, Channel}, Size - 8) end; @@ -545,8 +545,9 @@ start_1_0_connection(sasl, State = #v1{sock = Sock}) -> Ms = {array, symbol, case application:get_env(rabbitmq_amqp1_0, default_user) of {ok, none} -> []; - {ok, _} -> ["ANONYMOUS"] - end ++ [ atom_to_list(M) || M <- auth_mechanisms(Sock)]}, + {ok, _} -> [<<"ANONYMOUS">>] + end ++ + [list_to_binary(atom_to_list(M)) || M <- auth_mechanisms(Sock)]}, Mechanisms = #'v1_0.sasl_mechanisms'{sasl_server_mechanisms = Ms}, ok = send_on_channel0(Sock, Mechanisms, rabbit_amqp1_0_sasl), start_1_0_connection0(sasl, State); diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_session_process.erl b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_session_process.erl index 0955dd9..2f9be46 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_session_process.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/src/rabbit_amqp1_0_session_process.erl @@ -147,7 +147,7 @@ handle_cast({frame, Frame, FlowPid}, catch exit:Reason = #'v1_0.error'{} -> %% TODO shut down nicely like rabbit_channel End = #'v1_0.end'{ error = Reason }, - rabbit_log:warning("Closing session for connection ~p: ~p~n", + rabbit_log:warning("Closing session for connection ~p:~n~p~n", [ReaderPid, Reason]), ok = rabbit_amqp1_0_writer:send_command_sync(Sock, End), {stop, normal, State}; @@ -242,7 +242,12 @@ handle_control(#'v1_0.disposition'{state = Outcome, requeue = false}; #'v1_0.released'{} -> #'basic.reject'{delivery_tag = DeliveryTag, - requeue = true} + requeue = true}; + _ -> + protocol_error( + ?V_1_0_AMQP_ERROR_INVALID_FIELD, + "Unrecognised state: ~p~n" + "Disposition was: ~p~n", [Outcome, Disp]) end) end, case rabbit_amqp1_0_session:settle(Disp, session(State), AckFun) of diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/Makefile b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/Makefile index 4775fcf..3a1c639 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/Makefile @@ -1,5 +1,5 @@ -CLIENT_DIR=swiftmq_9_2_5_client -CLIENT_PKG=$(CLIENT_DIR).zip +CLIENT_DIR=swiftmq_9_7_1_client +CLIENT_PKG=$(CLIENT_DIR).tar.gz .PHONY: test @@ -8,11 +8,13 @@ test: build/lib build/lib: $(CLIENT_PKG) mkdir -p build/tmp - unzip -d build/tmp $(CLIENT_PKG) + tar -zx -f $(CLIENT_PKG) -C build/tmp mkdir -p build/lib mv build/tmp/$(CLIENT_DIR)/jars/*.jar build/lib rm -rf build/tmp cp ../lib-java/*.jar build/lib + (cd ../../../rabbitmq-java-client && ant dist) + cp ../../../rabbitmq-java-client/build/dist/rabbitmq-client.jar build/lib $(CLIENT_PKG): @echo diff --git a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/test/com/rabbitmq/amqp1_0/tests/swiftmq/SwiftMQTests.java b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/test/com/rabbitmq/amqp1_0/tests/swiftmq/SwiftMQTests.java index b58e2a5..2db131f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/test/com/rabbitmq/amqp1_0/tests/swiftmq/SwiftMQTests.java +++ b/rabbitmq-server/plugins-src/rabbitmq-amqp1.0/test/swiftmq/test/com/rabbitmq/amqp1_0/tests/swiftmq/SwiftMQTests.java @@ -1,7 +1,10 @@ package com.rabbitmq.amqp1_0.tests.swiftmq; +import com.rabbitmq.client.*; import com.swiftmq.amqp.AMQPContext; import com.swiftmq.amqp.v100.client.*; +import com.swiftmq.amqp.v100.client.Connection; +import com.swiftmq.amqp.v100.client.Consumer; import com.swiftmq.amqp.v100.generated.messaging.message_format.*; import com.swiftmq.amqp.v100.generated.messaging.message_format.Properties; import com.swiftmq.amqp.v100.messaging.AMQPMessage; @@ -213,16 +216,6 @@ public class SwiftMQTests extends TestCase { route(QUEUE, "test", "", true); route("test", "test", "", true); - try { - route(QUEUE, "/exchange/missing", "", false); - fail("Missing exchange should fail"); - } catch (Exception e) { } - - try { - route("/exchange/missing/", QUEUE, "", false); - fail("Missing exchange should fail"); - } catch (Exception e) { } - route("/topic/#.c.*", "/topic/a.b.c.d", "", true); route("/topic/#.c.*", "/exchange/amq.topic", "a.b.c.d", true); route("/exchange/amq.topic/#.y.*", "/topic/w.x.y.z", "", true); @@ -242,6 +235,19 @@ public class SwiftMQTests extends TestCase { emptyQueue(QUEUE); } + public void testRoutingInvalidRoutes() throws Exception { + ConnectionFactory factory = new ConnectionFactory(); + com.rabbitmq.client.Connection connection = factory.newConnection(); + Channel channel = connection.createChannel(); + channel.queueDeclare("transient", false, false, false, null); + connection.close(); + + for (String dest : Arrays.asList("/exchange/missing", "/queue/transient", "/fruit/orange")) { + routeInvalidSource(dest); + routeInvalidTarget(dest); + } + } + private void emptyQueue(String q) throws Exception { AMQPContext ctx = new AMQPContext(AMQPContext.CLIENT); Connection conn = new Connection(ctx, host, port, false); @@ -291,6 +297,42 @@ public class SwiftMQTests extends TestCase { conn.close(); } + private void routeInvalidSource(String consumerSource) throws Exception { + AMQPContext ctx = new AMQPContext(AMQPContext.CLIENT); + Connection conn = new Connection(ctx, host, port, false); + conn.connect(); + Session s = conn.createSession(INBOUND_WINDOW, OUTBOUND_WINDOW); + try { + Consumer c = s.createConsumer(consumerSource, CONSUMER_LINK_CREDIT, QoS.AT_LEAST_ONCE, false, null); + c.close(); + fail("Source '" + consumerSource + "' should fail"); + } + catch (Exception e) { + // no-op + } + finally { + conn.close(); + } + } + + private void routeInvalidTarget(String producerTarget) throws Exception { + AMQPContext ctx = new AMQPContext(AMQPContext.CLIENT); + Connection conn = new Connection(ctx, host, port, false); + conn.connect(); + Session s = conn.createSession(INBOUND_WINDOW, OUTBOUND_WINDOW); + try { + Producer p = s.createProducer(producerTarget, QoS.AT_LEAST_ONCE); + p.close(); + fail("Target '" + producerTarget + "' should fail"); + } + catch (Exception e) { + // no-op + } + finally { + conn.close(); + } + } + // TODO: generalise to a comparison of all immutable parts of messages private boolean compareMessageData(AMQPMessage m1, AMQPMessage m2) throws IOException { ByteArrayOutputStream b1 = new ByteArrayOutputStream(); diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/example/setup.sh b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/example/setup.sh index 0f519d8..bca4dcb 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/example/setup.sh +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/example/setup.sh @@ -6,7 +6,7 @@ sudo apt-get --yes purge slapd sudo rm -rf /var/lib/ldap -sudo apt-get --yes install slapd +sudo apt-get --yes install slapd ldap-utils sleep 1 DIR=$(dirname $0) diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/package.mk b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/package.mk index 25abf44..02c22ee 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/package.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/package.mk @@ -2,6 +2,8 @@ RELEASABLE:=true DEPS:=rabbitmq-server rabbitmq-erlang-client eldap-wrapper ifeq ($(shell nc -z localhost 389 && echo true),true) -WITH_BROKER_TEST_COMMANDS:=eunit:test(rabbit_auth_backend_ldap_test,[verbose]) +WITH_BROKER_TEST_COMMANDS:=eunit:test([rabbit_auth_backend_ldap_unit_test,rabbit_auth_backend_ldap_test],[verbose]) WITH_BROKER_TEST_CONFIG:=$(PACKAGE_DIR)/etc/rabbit-test +else +$(warning Not running LDAP tests; no LDAP server found on localhost) endif diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap.erl index 8fe976a..943ac55 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_auth_backend_ldap). @@ -21,10 +21,11 @@ -include_lib("eldap/include/eldap.hrl"). -include_lib("rabbit_common/include/rabbit.hrl"). --behaviour(rabbit_auth_backend). +-behaviour(rabbit_authn_backend). +-behaviour(rabbit_authz_backend). --export([description/0]). --export([check_user_login/2, check_vhost_access/2, check_resource_access/3]). +-export([user_login_authentication/2, user_login_authorization/1, + check_vhost_access/3, check_resource_access/3]). -define(L(F, A), log("LDAP " ++ F, A)). -define(L1(F, A), log(" LDAP " ++ F, A)). @@ -36,13 +37,7 @@ %%-------------------------------------------------------------------- -description() -> - [{name, <<"LDAP">>}, - {description, <<"LDAP authentication / authorisation">>}]. - -%%-------------------------------------------------------------------- - -check_user_login(Username, []) -> +user_login_authentication(Username, []) -> %% Without password, e.g. EXTERNAL ?L("CHECK: passwordless login for ~s", [Username]), R = with_ldap(creds(none), @@ -51,14 +46,14 @@ check_user_login(Username, []) -> [Username, log_result(R)]), R; -check_user_login(Username, [{password, <<>>}]) -> +user_login_authentication(Username, [{password, <<>>}]) -> %% Password "" is special in LDAP, see %% https://tools.ietf.org/html/rfc4513#section-5.1.2 ?L("CHECK: unauthenticated login for ~s", [Username]), ?L("DECISION: unauthenticated login for ~s: denied", [Username]), {refused, "user '~s' - unauthenticated bind not allowed", [Username]}; -check_user_login(User, [{password, PW}]) -> +user_login_authentication(User, [{password, PW}]) -> ?L("CHECK: login for ~s", [User]), R = case dn_lookup_when() of prebind -> UserDN = username_to_dn_prebind(User), @@ -70,11 +65,18 @@ check_user_login(User, [{password, PW}]) -> ?L("DECISION: login for ~s: ~p", [User, log_result(R)]), R; -check_user_login(Username, AuthProps) -> +user_login_authentication(Username, AuthProps) -> exit({unknown_auth_props, Username, AuthProps}). -check_vhost_access(User = #user{username = Username, - impl = #impl{user_dn = UserDN}}, VHost) -> +user_login_authorization(Username) -> + case user_login_authentication(Username, []) of + {ok, #auth_user{impl = Impl}} -> {ok, Impl}; + Else -> Else + end. + +check_vhost_access(User = #auth_user{username = Username, + impl = #impl{user_dn = UserDN}}, + VHost, _Sock) -> Args = [{username, Username}, {user_dn, UserDN}, {vhost, VHost}], @@ -84,8 +86,8 @@ check_vhost_access(User = #user{username = Username, [log_vhost(Args), log_user(User), log_result(R)]), R. -check_resource_access(User = #user{username = Username, - impl = #impl{user_dn = UserDN}}, +check_resource_access(User = #auth_user{username = Username, + impl = #impl{user_dn = UserDN}}, #resource{virtual_host = VHost, kind = Type, name = Name}, Permission) -> Args = [{username, Username}, @@ -133,7 +135,7 @@ evaluate0({in_group, DNPattern}, Args, User, LDAP) -> evaluate({in_group, DNPattern, "member"}, Args, User, LDAP); evaluate0({in_group, DNPattern, Desc}, Args, - #user{impl = #impl{user_dn = UserDN}}, LDAP) -> + #auth_user{impl = #impl{user_dn = UserDN}}, LDAP) -> Filter = eldap:equalityMatch(Desc, UserDN), DN = fill(DNPattern, Args), R = object_exists(DN, Filter, LDAP), @@ -247,33 +249,24 @@ with_ldap({error, _} = E, _Fun, _State) -> %% TODO - ATM we create and destroy a new LDAP connection on every %% call. This could almost certainly be more efficient. with_ldap({ok, Creds}, Fun, Servers) -> - Opts0 = [{ssl, env(use_ssl)}, {port, env(port)}], - SSLOpts = env(ssl_options), - %% We can't just pass through [] as sslopts in the old case, eldap - %% exit()s when you do that. - Opts1 = case {SSLOpts, rabbit_misc:version_compare( - erlang:system_info(version), "5.10")} of %% R16A - {[], _} -> Opts0; - {_, lt} -> exit({ssl_options_requires_min_r16a}); - {_, _} -> [{sslopts, SSLOpts} | Opts0] - end, - Opts2 = case env(log) of + Opts0 = [{port, env(port)}], + Opts1 = case env(log) of network -> Pre = " LDAP network traffic: ", rabbit_log:info( " LDAP connecting to servers: ~p~n", [Servers]), [{log, fun(1, S, A) -> rabbit_log:warning(Pre ++ S, A); (2, S, A) -> rabbit_log:info (Pre ++ S, A) - end} | Opts1]; + end} | Opts0]; _ -> - Opts1 + Opts0 end, %% eldap defaults to 'infinity' but doesn't allow you to set that. Harrumph. Opts = case env(timeout) of - infinity -> Opts2; - MS -> [{timeout, MS} | Opts2] + infinity -> Opts1; + MS -> [{timeout, MS} | Opts1] end, - case eldap:open(Servers, Opts) of + case eldap_open(Servers, Opts) of {ok, LDAP} -> try Creds of anon -> @@ -300,6 +293,43 @@ with_ldap({ok, Creds}, Fun, Servers) -> Error end. +eldap_open(Servers, Opts) -> + case eldap:open(Servers, ssl_conf() ++ Opts) of + {ok, LDAP} -> + TLS = env(use_starttls), + case {TLS, at_least("5.10.4")} of %%R16B03 + {false, _} -> {ok, LDAP}; + {true, false} -> exit({starttls_requires_min_r16b3}); + {true, _} -> TLSOpts = ssl_options(), + ELDAP = eldap, %% Fool xref + case ELDAP:start_tls(LDAP, TLSOpts) of + ok -> {ok, LDAP}; + Error -> Error + end + end; + Error -> + Error + end. + +ssl_conf() -> + %% We must make sure not to add SSL options unless a) we have at least R16A + %% b) we have SSL turned on (or it breaks StartTLS...) + case env(use_ssl) of + false -> [{ssl, false}]; + true -> %% Only the unfixed version can be [] + case {env(ssl_options), at_least("5.10")} of %% R16A + {_, true} -> [{ssl, true}, {sslopts, ssl_options()}]; + {[], _} -> [{ssl, true}]; + {_, false} -> exit({ssl_options_requires_min_r16a}) + end + end. + +ssl_options() -> + rabbit_networking:fix_ssl_options(env(ssl_options)). + +at_least(Ver) -> + rabbit_misc:version_compare(erlang:system_info(version), Ver) =/= lt. + env(F) -> {ok, V} = application:get_env(rabbitmq_auth_backend_ldap, F), V. @@ -309,23 +339,33 @@ do_login(Username, PrebindUserDN, Password, LDAP) -> unknown -> username_to_dn(Username, LDAP, dn_lookup_when()); _ -> PrebindUserDN end, - User = #user{username = Username, - auth_backend = ?MODULE, - impl = #impl{user_dn = UserDN, - password = Password}}, - TagRes = [begin - ?L1("CHECK: does ~s have tag ~s?", [Username, Tag]), - R = evaluate(Q, [{username, Username}, - {user_dn, UserDN}], User, LDAP), - ?L1("DECISION: does ~s have tag ~s? ~p", - [Username, Tag, R]), - {Tag, R} - end || {Tag, Q} <- env(tag_queries)], - case [E || {_, E = {error, _}} <- TagRes] of - [] -> {ok, User#user{tags = [Tag || {Tag, true} <- TagRes]}}; - [E | _] -> E + User = #auth_user{username = Username, + impl = #impl{user_dn = UserDN, + password = Password}}, + DTQ = fun (LDAPn) -> do_tag_queries(Username, UserDN, User, LDAPn) end, + TagRes = case env(other_bind) of + as_user -> DTQ(LDAP); + _ -> with_ldap(creds(User), DTQ) + end, + case TagRes of + {ok, L} -> case [E || {_, E = {error, _}} <- L] of + [] -> Tags = [Tag || {Tag, true} <- L], + {ok, User#auth_user{tags = Tags}}; + [E | _] -> E + end; + E -> E end. +do_tag_queries(Username, UserDN, User, LDAP) -> + {ok, [begin + ?L1("CHECK: does ~s have tag ~s?", [Username, Tag]), + R = evaluate(Q, [{username, Username}, + {user_dn, UserDN}], User, LDAP), + ?L1("DECISION: does ~s have tag ~s? ~p", + [Username, Tag, R]), + {Tag, R} + end || {Tag, Q} <- env(tag_queries)]}. + dn_lookup_when() -> case {env(dn_lookup_attribute), env(dn_lookup_bind)} of {none, _} -> never; {_, as_user} -> postbind; @@ -364,8 +404,8 @@ creds(User) -> creds(User, env(other_bind)). creds(none, as_user) -> {error, "'other_bind' set to 'as_user' but no password supplied"}; -creds(#user{impl = #impl{user_dn = UserDN, password = Password}}, as_user) -> - {ok, {UserDN, Password}}; +creds(#auth_user{impl = #impl{user_dn = UserDN, password = PW}}, as_user) -> + {ok, {UserDN, PW}}; creds(_, Creds) -> {ok, Creds}. @@ -380,13 +420,13 @@ fill(Fmt, Args) -> ?L2("template result: \"~s\"", [R]), R. -log_result({ok, #user{}}) -> ok; -log_result(true) -> ok; -log_result(false) -> denied; -log_result({refused, _, _}) -> denied; -log_result(E) -> E. +log_result({ok, #auth_user{}}) -> ok; +log_result(true) -> ok; +log_result(false) -> denied; +log_result({refused, _, _}) -> denied; +log_result(E) -> E. -log_user(#user{username = U}) -> rabbit_misc:format("\"~s\"", [U]). +log_user(#auth_user{username = U}) -> rabbit_misc:format("\"~s\"", [U]). log_vhost(Args) -> rabbit_misc:format("access to vhost \"~s\"", [pget(vhost, Args)]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_app.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_app.erl index de13c3b..e0cd7aa 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_app.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_auth_backend_ldap_app). @@ -32,7 +32,8 @@ start(_Type, _StartArgs) -> "in the list of auth_backends. LDAP auth will not work.~n") end, {ok, SSL} = application:get_env(rabbitmq_auth_backend_ldap, use_ssl), - case SSL of + {ok, TLS} = application:get_env(rabbitmq_auth_backend_ldap, use_starttls), + case SSL orelse TLS of true -> rabbit_networking:ensure_ssl(); false -> ok end, diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_util.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_util.erl index c7cf113..47c3d66 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbit_auth_backend_ldap_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_auth_backend_ldap_util). @@ -25,7 +25,10 @@ fill(Fmt, [{K, V} | T]) -> Var = [[$\\, $$, ${] ++ atom_to_list(K) ++ [$}]], fill(re:replace(Fmt, Var, [to_repl(V)], [global]), T). -to_repl(V) when is_atom(V) -> - atom_to_list(V); -to_repl(V) -> - V. +to_repl(V) when is_atom(V) -> to_repl(atom_to_list(V)); +to_repl(V) when is_binary(V) -> to_repl(binary_to_list(V)); +to_repl([]) -> []; +to_repl([$\\ | T]) -> [$\\, $\\ | to_repl(T)]; +to_repl([$& | T]) -> [$\\, $& | to_repl(T)]; +to_repl([H | T]) -> [H | to_repl(T)]. + diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbitmq_auth_backend_ldap.app.src b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbitmq_auth_backend_ldap.app.src index e1b7628..b2139d6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbitmq_auth_backend_ldap.app.src +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/src/rabbitmq_auth_backend_ldap.app.src @@ -15,6 +15,7 @@ {resource_access_query, {constant, true}}, {tag_queries, [{administrator, {constant, false}}]}, {use_ssl, false}, + {use_starttls, false}, {ssl_options, []}, {port, 389}, {timeout, infinity}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_test.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_test.erl index 72fbc84..2b92632 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_auth_backend_ldap_test). diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_unit_test.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_unit_test.erl new file mode 100644 index 0000000..47223f9 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-backend-ldap/test/src/rabbit_auth_backend_ldap_unit_test.erl @@ -0,0 +1,33 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. +%% + +-module(rabbit_auth_backend_ldap_unit_test). + +-include_lib("eunit/include/eunit.hrl"). + +fill_test() -> + F = fun(Fmt, Args, Res) -> + ?assertEqual(Res, rabbit_auth_backend_ldap_util:fill(Fmt, Args)) + end, + F("x${username}x", [{username, "ab"}], "xabx"), + F("x${username}x", [{username, ab}], "xabx"), + F("x${username}x", [{username, <<"ab">>}], "xabx"), + F("x${username}x", [{username, ""}], "xx"), + F("x${username}x", [{fusername, "ab"}], "x${username}x"), + F("x${usernamex", [{username, "ab"}], "x${usernamex"), + F("x${username}x", [{username, "a\\b"}], "xa\\bx"), + F("x${username}x", [{username, "a&b"}], "xa&bx"), + ok. diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl.erl index 883a9dc..47cfcab 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% @@ -29,7 +29,9 @@ {mfa, {rabbit_registry, register, [auth_mechanism, <<"EXTERNAL">>, ?MODULE]}}, {requires, rabbit_registry}, - {enables, kernel_ready}]}). + {enables, kernel_ready}, + {cleanup, {rabbit_registry, unregister, + [auth_mechanism, <<"EXTERNAL">>]}}]}). -record(state, {username = undefined}). @@ -55,20 +57,21 @@ init(Sock) -> Username = case rabbit_net:peercert(Sock) of {ok, C} -> case rabbit_ssl:peer_cert_auth_name(C) of - unsafe -> {refused, "configuration unsafe", []}; - not_found -> {refused, "no name found", []}; + unsafe -> {refused, none, + "configuration unsafe", []}; + not_found -> {refused, none, "no name found", []}; Name -> Name end; {error, no_peercert} -> - {refused, "no peer certificate", []}; + {refused, none, "no peer certificate", []}; nossl -> - {refused, "not SSL connection", []} + {refused, none, "not SSL connection", []} end, #state{username = Username}. handle_response(_Response, #state{username = Username}) -> case Username of - {refused, _, _} = E -> + {refused, _, _, _} = E -> E; _ -> rabbit_access_control:check_user_login(Username, []) diff --git a/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl_app.erl b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl_app.erl index 1867d3a..7f6eff9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-auth-mechanism-ssl/src/rabbit_auth_mechanism_ssl_app.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_ssl_app). diff --git a/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/src/rabbit_exchange_type_consistent_hash.erl b/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/src/rabbit_exchange_type_consistent_hash.erl index 381d054..68e3253 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/src/rabbit_exchange_type_consistent_hash.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-consistent-hash-exchange/src/rabbit_exchange_type_consistent_hash.erl @@ -33,7 +33,9 @@ {mfa, {rabbit_registry, register, [exchange, <<"x-consistent-hash">>, ?MODULE]}}, {requires, rabbit_registry}, - {enables, kernel_ready}]}). + {enables, kernel_ready}, + {cleanup, {rabbit_registry, unregister, + [exchange, <<"x-consistent-hash">>]}}]}). -rabbit_boot_step( {rabbit_exchange_type_consistent_hash_mnesia, diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile index 71c3927..a42c666 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile @@ -11,7 +11,7 @@ # The Original Code is RabbitMQ. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. # VERSION=0.0.0 diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile.in b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile.in index 42ead35..0b46f9f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile.in +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/Makefile.in @@ -11,7 +11,7 @@ # The Original Code is RabbitMQ. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. # VERSION=%%VSN%% diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/common.mk b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/common.mk index 994d474..9de9221 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/common.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/common.mk @@ -11,7 +11,7 @@ # The Original Code is RabbitMQ. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. # # The client library can either be built from source control or by downloading diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/ebin/amqp_client.app.in b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/ebin/amqp_client.app.in index f4627b3..c9c96ad 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/ebin/amqp_client.app.in +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/ebin/amqp_client.app.in @@ -3,6 +3,7 @@ {vsn, "%%VSN%%"}, {modules, []}, {registered, [amqp_sup]}, - {env, [{prefer_ipv6, false}]}, + {env, [{prefer_ipv6, false}, + {ssl_options, []}]}, {mod, {amqp_client, []}}, {applications, [kernel, stdlib, xmerl]}]}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client.hrl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client.hrl index cc1f485..bc74dd7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -ifndef(AMQP_CLIENT_HRL). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client_internal.hrl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client_internal.hrl index bdaf9c2..f990f04 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client_internal.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_client_internal.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -include("amqp_client.hrl"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_gen_consumer_spec.hrl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_gen_consumer_spec.hrl index dda4993..fbaa28c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_gen_consumer_spec.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/amqp_gen_consumer_spec.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -include("amqp_client.hrl"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/rabbit_routing_prefixes.hrl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/rabbit_routing_prefixes.hrl index 79e41ca..34618c5 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/rabbit_routing_prefixes.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/include/rabbit_routing_prefixes.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -define(QUEUE_PREFIX, "/queue"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/rabbit_common.app.in b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/rabbit_common.app.in index dc80817..46b2a4d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/rabbit_common.app.in +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/rabbit_common.app.in @@ -19,7 +19,8 @@ rabbit_command_assembler, rabbit_exchange_type, rabbit_exchange_decorator, - rabbit_auth_backend, + rabbit_authn_backend, + rabbit_authz_backend, rabbit_auth_mechanism, rabbit_framing_amqp_0_8, rabbit_framing_amqp_0_9_1, @@ -27,6 +28,7 @@ rabbit_misc, rabbit_msg_store_index, rabbit_net, + rabbit_networking, rabbit_nodes, rabbit_policy_validator, rabbit_reader, diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_auth_mechanisms.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_auth_mechanisms.erl index 576cdc1..9192cad 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_auth_mechanisms.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_auth_mechanisms.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel.erl index abff0e4..1121795 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @type close_reason(Type) = {shutdown, amqp_reason(Type)}. @@ -74,7 +74,8 @@ -export([call_consumer/2, subscribe/3]). -export([next_publish_seqno/1, wait_for_confirms/1, wait_for_confirms/2, wait_for_confirms_or_die/1, wait_for_confirms_or_die/2]). --export([start_link/5, set_writer/2, connection_closing/3, open/1]). +-export([start_link/5, set_writer/2, connection_closing/3, open/1, + enable_delivery_flow_control/1, notify_received/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). @@ -97,7 +98,16 @@ flow_handler = none, unconfirmed_set = gb_sets:new(), waiting_set = gb_trees:empty(), - only_acks_received = true + only_acks_received = true, + + %% true | false, only relevant in the direct + %% client case. + %% when true, consumers will manually notify + %% queue pids using rabbit_amqqueue:notify_sent/2 + %% to prevent the queue from overwhelming slow + %% consumers that use automatic acknowledgement + %% mode. + delivery_flow_control = false }). %%--------------------------------------------------------------------------- @@ -342,6 +352,12 @@ start_link(Driver, Connection, ChannelNumber, Consumer, Identity) -> set_writer(Pid, Writer) -> gen_server:cast(Pid, {set_writer, Writer}). +enable_delivery_flow_control(Pid) -> + gen_server:cast(Pid, enable_delivery_flow_control). + +notify_received({Pid, QPid, ServerChPid}) -> + gen_server:cast(Pid, {send_notify, {QPid, ServerChPid}}). + %% @private connection_closing(Pid, ChannelCloseType, Reason) -> gen_server:cast(Pid, {connection_closing, ChannelCloseType, Reason}). @@ -399,9 +415,19 @@ handle_call({subscribe, BasicConsume, Subscriber}, From, State) -> State). %% @private +handle_cast({set_writer, Writer}, State = #state{driver = direct}) -> + link(Writer), + {noreply, State#state{writer = Writer}}; handle_cast({set_writer, Writer}, State) -> {noreply, State#state{writer = Writer}}; %% @private +handle_cast(enable_delivery_flow_control, State) -> + {noreply, State#state{delivery_flow_control = true}}; +%% @private +handle_cast({send_notify, {QPid, ChPid}}, State) -> + rabbit_amqqueue:notify_sent(QPid, ChPid), + {noreply, State}; +%% @private handle_cast({cast, Method, AmqpMsg, Sender, noflow}, State) -> handle_method_to_server(Method, AmqpMsg, none, Sender, noflow, State); handle_cast({cast, Method, AmqpMsg, Sender, flow}, State) -> @@ -458,9 +484,15 @@ handle_info({send_command, Method, Content}, State) -> handle_method_from_server(Method, Content, State); %% Received from rabbit_channel in the direct case %% @private -handle_info({send_command_and_notify, Q, ChPid, Method, Content}, State) -> - handle_method_from_server(Method, Content, State), - rabbit_amqqueue:notify_sent(Q, ChPid), +handle_info({send_command_and_notify, QPid, ChPid, + Method = #'basic.deliver'{}, Content}, + State = #state{delivery_flow_control = MFC}) -> + case MFC of + false -> handle_method_from_server(Method, Content, State), + rabbit_amqqueue:notify_sent(QPid, ChPid); + true -> handle_method_from_server(Method, Content, + {self(), QPid, ChPid}, State) + end, {noreply, State}; %% This comes from the writer or rabbit_channel %% @private @@ -633,7 +665,9 @@ pre_do(_, _, _, State) -> %% Handling of methods from the server %%--------------------------------------------------------------------------- -handle_method_from_server(Method, Content, State = #state{closing = Closing}) -> +safely_handle_method_from_server(Method, Content, + Continuation, + State = #state{closing = Closing}) -> case is_connection_method(Method) of true -> server_misbehaved( #amqp_error{name = command_invalid, @@ -651,11 +685,28 @@ handle_method_from_server(Method, Content, State = #state{closing = Closing}) -> "server because channel is closing~n", [self(), {Method, Content}]), {noreply, State}; - true -> handle_method_from_server1(Method, - amqp_msg(Content), State) + true -> + Continuation() end end. +handle_method_from_server(Method, Content, State) -> + Fun = fun () -> + handle_method_from_server1(Method, + amqp_msg(Content), State) + end, + safely_handle_method_from_server(Method, Content, Fun, State). + +handle_method_from_server(Method = #'basic.deliver'{}, + Content, DeliveryCtx, State) -> + Fun = fun () -> + handle_method_from_server1(Method, + amqp_msg(Content), + DeliveryCtx, + State) + end, + safely_handle_method_from_server(Method, Content, Fun, State). + handle_method_from_server1(#'channel.open_ok'{}, none, State) -> {noreply, rpc_bottom_half(ok, State)}; handle_method_from_server1(#'channel.close'{reply_code = Code, @@ -739,6 +790,12 @@ handle_method_from_server1(Method, none, State) -> handle_method_from_server1(Method, Content, State) -> {noreply, rpc_bottom_half({Method, Content}, State)}. +%% only used with manual consumer-to-queue flow control +handle_method_from_server1(#'basic.deliver'{} = Deliver, AmqpMsg, + DeliveryCtx, State) -> + ok = call_to_consumer(Deliver, AmqpMsg, DeliveryCtx, State), + {noreply, State}. + %%--------------------------------------------------------------------------- %% Other handle_* functions %%--------------------------------------------------------------------------- @@ -920,5 +977,8 @@ handle_wait_for_confirms(From, Timeout, call_to_consumer(Method, Args, #state{consumer = Consumer}) -> amqp_gen_consumer:call_consumer(Consumer, Method, Args). +call_to_consumer(Method, Args, DeliveryCtx, #state{consumer = Consumer}) -> + amqp_gen_consumer:call_consumer(Consumer, Method, Args, DeliveryCtx). + safe_cancel_timer(undefined) -> ok; safe_cancel_timer(TRef) -> erlang:cancel_timer(TRef). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup.erl index fbd2d0c..8fc4d8f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private @@ -53,7 +53,6 @@ start_writer(_Sup, direct, [ConnPid, Node, User, VHost, Collector], rpc:call(Node, rabbit_direct, start_channel, [ChNumber, ChPid, ConnPid, ConnName, ?PROTOCOL, User, VHost, ?CLIENT_CAPABILITIES, Collector]), - link(RabbitCh), RabbitCh; start_writer(Sup, network, [Sock, FrameMax], ConnName, ChNumber, ChPid) -> {ok, Writer} = supervisor2:start_child( diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup_sup.erl index 2108b6a..6d461e1 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channel_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channels_manager.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channels_manager.erl index e805ea1..b260cd2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channels_manager.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_channels_manager.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_client.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_client.erl index f5fa1ae..83905d0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_client.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_client.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection.erl index d6d044f..371b225 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @type close_reason(Type) = {shutdown, amqp_reason(Type)}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_sup.erl index 5268ded..7bc8a2d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_type_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_type_sup.erl index 41717c2..5802375 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_type_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_connection_type_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_connection.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_connection.erl index e0ed0b3..5cd7df7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_connection.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_connection.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private @@ -34,14 +34,16 @@ params, adapter_info, collector, - closing_reason %% undefined | Reason + closing_reason, %% undefined | Reason + connected_at }). -define(INFO_KEYS, [type]). -define(CREATION_EVENT_KEYS, [pid, protocol, host, port, name, peer_host, peer_port, - user, vhost, client_properties, type]). + user, vhost, client_properties, type, + connected_at]). %%--------------------------------------------------------------------------- @@ -72,6 +74,10 @@ handle_message({force_event_refresh, Ref}, State = #state{node = Node}) -> {ok, State}; handle_message(closing_timeout, State = #state{closing_reason = Reason}) -> {stop, {closing_timeout, Reason}, State}; +handle_message({'DOWN', _MRef, process, _ConnSup, shutdown}, State) -> + {stop, {shutdown, node_down}, State}; +handle_message({'DOWN', _MRef, process, _ConnSup, Reason}, State) -> + {stop, {remote_node_down, Reason}, State}; handle_message(Msg, State) -> {stop, {unexpected_msg, Msg}, State}. @@ -94,6 +100,7 @@ i(user, #state{params = P}) -> P#amqp_params_direct.username; i(vhost, #state{params = P}) -> P#amqp_params_direct.virtual_host; i(client_properties, #state{params = P}) -> P#amqp_params_direct.client_properties; +i(connected_at, #state{connected_at = T}) -> T; %% Optional adapter info i(protocol, #state{adapter_info = I}) -> I#amqp_adapter_info.protocol; i(host, #state{adapter_info = I}) -> I#amqp_adapter_info.host; @@ -122,7 +129,8 @@ connect(Params = #amqp_params_direct{username = Username, State1 = State#state{node = Node, vhost = VHost, params = Params, - adapter_info = ensure_adapter_info(Info)}, + adapter_info = ensure_adapter_info(Info), + connected_at = rabbit_misc:now_to_ms(os:timestamp())}, case rpc:call(Node, rabbit_direct, connect, [{Username, Password}, VHost, ?PROTOCOL, self(), connection_info(State1)]) of @@ -130,6 +138,13 @@ connect(Params = #amqp_params_direct{username = Username, {ok, ChMgr, Collector} = SIF(i(name, State1)), State2 = State1#state{user = User, collector = Collector}, + %% There's no real connection-level process on the remote + %% node for us to monitor or link to, but we want to + %% detect connection death if the remote node goes down + %% when there are no channels. So we monitor the + %% supervisor; that way we find out if the node goes down + %% or the rabbit app stops. + erlang:monitor(process, {rabbit_direct_client_sup, Node}), {ok, {ServerProperties, 0, ChMgr, State2}}; {error, _} = E -> E; diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_consumer.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_consumer.erl index 517a2b3..34b1423 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_consumer.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_direct_consumer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% %% @doc This module is an implementation of the amqp_gen_consumer @@ -45,7 +45,8 @@ -behaviour(amqp_gen_consumer). -export([init/1, handle_consume_ok/3, handle_consume/3, handle_cancel_ok/3, - handle_cancel/2, handle_server_cancel/2, handle_deliver/3, + handle_cancel/2, handle_server_cancel/2, + handle_deliver/3, handle_deliver/4, handle_info/2, handle_call/3, terminate/2]). %%--------------------------------------------------------------------------- @@ -86,6 +87,10 @@ handle_server_cancel(M, C) -> handle_deliver(M, A, C) -> C ! {M, A}, {ok, C}. +handle_deliver(M, A, DeliveryCtx, C) -> + C ! {M, A, DeliveryCtx}, + {ok, C}. + %% @private handle_info({'DOWN', _MRef, process, C, Info}, C) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_connection.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_connection.erl index 3cc64b7..55618ac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_connection.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_connection.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_consumer.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_consumer.erl index d94194c..68637cc 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_consumer.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_gen_consumer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% %% @doc A behaviour module for implementing consumers for @@ -31,7 +31,7 @@ -behaviour(gen_server2). --export([start_link/3, call_consumer/2, call_consumer/3]). +-export([start_link/3, call_consumer/2, call_consumer/3, call_consumer/4]). -export([behaviour_info/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_info/3]). @@ -71,6 +71,9 @@ call_consumer(Pid, Msg) -> call_consumer(Pid, Method, Args) -> gen_server2:call(Pid, {consumer_call, Method, Args}, infinity). +call_consumer(Pid, Method, Args, DeliveryCtx) -> + gen_server2:call(Pid, {consumer_call, Method, Args, DeliveryCtx}, infinity). + %%--------------------------------------------------------------------------- %% Behaviour %%--------------------------------------------------------------------------- @@ -149,6 +152,19 @@ behaviour_info(callbacks) -> %% is received from the server. {handle_deliver, 3}, + %% handle_deliver(Deliver, Message, + %% DeliveryCtx, State) -> ok_error() + %% where + %% Deliver = #'basic.deliver'{} + %% Message = #amqp_msg{} + %% DeliveryCtx = {pid(), pid(), pid()} + %% State = state() + %% + %% This callback is invoked by the channel every time a basic.deliver + %% is received from the server. Only relevant for channels that use + %% direct client connection and manual flow control. + {handle_deliver, 4}, + %% handle_info(Info, State) -> ok_error() %% where %% Info = any() @@ -207,6 +223,15 @@ init([ConsumerModule, ExtraParams, Identity]) -> prioritise_info({'DOWN', _MRef, process, _Pid, _Info}, _Len, _State) -> 1; prioritise_info(_, _Len, _State) -> 0. +consumer_call_reply(Return, State) -> + case Return of + {ok, NewMState} -> + {reply, ok, State#state{module_state = NewMState}}; + {error, Reason, NewMState} -> + {stop, {error, Reason}, {error, Reason}, + State#state{module_state = NewMState}} + end. + handle_call({consumer_call, Msg}, From, State = #state{module = ConsumerModule, module_state = MState}) -> @@ -240,13 +265,14 @@ handle_call({consumer_call, Method, Args}, _From, #'basic.deliver'{} -> ConsumerModule:handle_deliver(Method, Args, MState) end, - case Return of - {ok, NewMState} -> - {reply, ok, State#state{module_state = NewMState}}; - {error, Reason, NewMState} -> - {stop, {error, Reason}, {error, Reason}, - State#state{module_state = NewMState}} - end. + consumer_call_reply(Return, State); + +%% only supposed to be used with basic.deliver +handle_call({consumer_call, Method = #'basic.deliver'{}, Args, DeliveryCtx}, _From, + State = #state{module = ConsumerModule, + module_state = MState}) -> + Return = ConsumerModule:handle_deliver(Method, Args, DeliveryCtx, MState), + consumer_call_reply(Return, State). handle_cast(_What, State) -> {noreply, State}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_main_reader.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_main_reader.erl index 258f73c..b8e4ff9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_main_reader.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_main_reader.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_network_connection.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_network_connection.erl index e18841b..5edb44b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_network_connection.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_network_connection.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private @@ -132,17 +132,23 @@ do_connect({Addr, Family}, {error, _} = E -> E end; do_connect({Addr, Family}, - AmqpParams = #amqp_params_network{ssl_options = SslOpts, + AmqpParams = #amqp_params_network{ssl_options = SslOpts0, port = Port, connection_timeout = Timeout, socket_options = ExtraOpts}, SIF, State) -> + {ok, GlobalSslOpts} = application:get_env(amqp_client, ssl_options), app_utils:start_applications([asn1, crypto, public_key, ssl]), obtain(), case gen_tcp:connect(Addr, Port, [Family | ?RABBIT_TCP_OPTS] ++ ExtraOpts, Timeout) of {ok, Sock} -> + SslOpts = rabbit_networking:fix_ssl_options( + orddict:to_list( + orddict:merge(fun (_, _A, B) -> B end, + orddict:from_list(GlobalSslOpts), + orddict:from_list(SslOpts0)))), case ssl:connect(Sock, SslOpts) of {ok, SslSock} -> RabbitSslSock = #ssl_socket{ssl = SslSock, tcp = Sock}, @@ -298,7 +304,7 @@ client_properties(UserProperties) -> {<<"version">>, longstr, list_to_binary(Vsn)}, {<<"platform">>, longstr, <<"Erlang">>}, {<<"copyright">>, longstr, - <<"Copyright (c) 2007-2014 GoPivotal, Inc.">>}, + <<"Copyright (c) 2007-2015 Pivotal Software, Inc.">>}, {<<"information">>, longstr, <<"Licensed under the MPL. " "See http://www.rabbitmq.com/">>}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_client.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_client.erl index a192b6b..c5bed0d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_client.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_client.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @doc This module allows the simple execution of an asynchronous RPC over @@ -88,7 +88,8 @@ call(RpcClient, Payload) -> %% Sets up a reply queue for this client to listen on setup_reply_queue(State = #state{channel = Channel}) -> #'queue.declare_ok'{queue = Q} = - amqp_channel:call(Channel, #'queue.declare'{}), + amqp_channel:call(Channel, #'queue.declare'{exclusive = true, + auto_delete = true}), State#state{reply_queue = Q}. %% Registers this RPC client instance as a consumer to handle rpc responses diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_server.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_server.erl index 35e28a9..1452536 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_server.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_rpc_server.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @doc This is a utility module that is used to expose an arbitrary function diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_selective_consumer.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_selective_consumer.erl index 76b9de4..dc916ac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_selective_consumer.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_selective_consumer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% %% @doc This module is an implementation of the amqp_gen_consumer @@ -45,7 +45,8 @@ -export([register_default_consumer/2]). -export([init/1, handle_consume_ok/3, handle_consume/3, handle_cancel_ok/3, - handle_cancel/2, handle_server_cancel/2, handle_deliver/3, + handle_cancel/2, handle_server_cancel/2, + handle_deliver/3, handle_deliver/4, handle_info/2, handle_call/3, terminate/2]). -record(state, {consumers = dict:new(), %% Tag -> ConsumerPid @@ -154,8 +155,13 @@ handle_server_cancel(Cancel = #'basic.cancel'{nowait = true}, State) -> {ok, State1}. %% @private -handle_deliver(Deliver, Message, State) -> - deliver(Deliver, Message, State), +handle_deliver(Method, Message, State) -> + deliver(Method, Message, State), + {ok, State}. + +%% @private +handle_deliver(Method, Message, DeliveryCtx, State) -> + deliver(Method, Message, DeliveryCtx, State), {ok, State}. %% @private @@ -201,18 +207,26 @@ terminate(_Reason, State) -> %% Internal plumbing %%--------------------------------------------------------------------------- -deliver(Msg, State) -> - deliver(Msg, undefined, State). -deliver(Msg, Message, State) -> - Combined = if Message =:= undefined -> Msg; - true -> {Msg, Message} - end, - case resolve_consumer(tag(Msg), State) of - {consumer, Pid} -> Pid ! Combined; - {default, Pid} -> Pid ! Combined; +deliver_to_consumer_or_die(Method, Msg, State) -> + case resolve_consumer(tag(Method), State) of + {consumer, Pid} -> Pid ! Msg; + {default, Pid} -> Pid ! Msg; error -> exit(unexpected_delivery_and_no_default_consumer) end. +deliver(Method, State) -> + deliver(Method, undefined, State). +deliver(Method, Message, State) -> + Combined = if Message =:= undefined -> Method; + true -> {Method, Message} + end, + deliver_to_consumer_or_die(Method, Combined, State). +deliver(Method, Message, DeliveryCtx, State) -> + Combined = if Message =:= undefined -> Method; + true -> {Method, Message, DeliveryCtx} + end, + deliver_to_consumer_or_die(Method, Combined, State). + do_cancel(Cancel, State = #state{consumers = Consumers, monitors = Monitors}) -> Tag = tag(Cancel), diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_sup.erl index 984e9bb..9c928d5 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% @private diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_uri.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_uri.erl index 63609d9..04446b0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_uri.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/amqp_uri.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(amqp_uri). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/rabbit_routing_util.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/rabbit_routing_util.erl index 55af2d6..7daa728 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/rabbit_routing_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/src/rabbit_routing_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2013-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2013-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_routing_util). @@ -79,8 +79,8 @@ parse_endpoint0(Type, Rest, _) -> %% -------------------------------------------------------------------------- -ensure_endpoint(Dir, Channel, EndPoint, State) -> - ensure_endpoint(Dir, Channel, EndPoint, [], State). +ensure_endpoint(Dir, Channel, Endpoint, State) -> + ensure_endpoint(Dir, Channel, Endpoint, [], State). ensure_endpoint(source, Channel, {exchange, {Name, _}}, Params, State) -> check_exchange(Name, Channel, @@ -106,7 +106,10 @@ ensure_endpoint(_, Channel, {queue, Name}, Params, State) -> #'queue.declare'{queue = Queue, nowait = true}, queue, Params1), - amqp_channel:cast(Channel, Method), + case Method#'queue.declare'.nowait of + true -> amqp_channel:cast(Channel, Method); + false -> amqp_channel:call(Channel, Method) + end, sets:add_element(Queue, State) end, {ok, Queue, State1}; @@ -119,9 +122,11 @@ ensure_endpoint(dest, Channel, {exchange, {Name, _}}, Params, State) -> ensure_endpoint(dest, _Ch, {topic, _}, _Params, State) -> {ok, undefined, State}; -ensure_endpoint(_, _Ch, {Type, Name}, _Params, State) - when Type =:= reply_queue orelse Type =:= amqqueue -> - {ok, list_to_binary(Name), State}; +ensure_endpoint(_, _Ch, {amqqueue, Name}, _Params, State) -> + {ok, list_to_binary(Name), State}; + +ensure_endpoint(_, _Ch, {reply_queue, Name}, _Params, State) -> + {ok, list_to_binary(Name), State}; ensure_endpoint(_Direction, _Ch, _Endpoint, _Params, _State) -> {error, invalid_endpoint}. @@ -167,17 +172,50 @@ check_exchange(ExchangeName, Channel, true) -> #'exchange.declare_ok'{} = amqp_channel:call(Channel, XDecl), ok. +update_queue_declare_arguments(Method, Params) -> + Method#'queue.declare'{arguments = + proplists:get_value(arguments, Params, [])}. + +update_queue_declare_exclusive(Method, Params) -> + case proplists:get_value(exclusive, Params) of + undefined -> Method; + Val -> Method#'queue.declare'{exclusive = Val} + end. + +update_queue_declare_auto_delete(Method, Params) -> + case proplists:get_value(auto_delete, Params) of + undefined -> Method; + Val -> Method#'queue.declare'{auto_delete = Val} + end. + +update_queue_declare_nowait(Method, Params) -> + case proplists:get_value(nowait, Params) of + undefined -> Method; + Val -> Method#'queue.declare'{nowait = Val} + end. + queue_declare_method(#'queue.declare'{} = Method, Type, Params) -> + %% defaults Method1 = case proplists:get_value(durable, Params, false) of true -> Method#'queue.declare'{durable = true}; false -> Method#'queue.declare'{auto_delete = true, exclusive = true} end, + %% set the rest of queue.declare fields from Params + Method2 = lists:foldl(fun (F, Acc) -> F(Acc, Params) end, + Method1, [fun update_queue_declare_arguments/2, + fun update_queue_declare_exclusive/2, + fun update_queue_declare_auto_delete/2, + fun update_queue_declare_nowait/2]), case {Type, proplists:get_value(subscription_queue_name_gen, Params)} of {topic, SQNG} when is_function(SQNG) -> - Method1#'queue.declare'{queue = SQNG()}; + Method2#'queue.declare'{queue = SQNG()}; + {exchange, SQNG} when is_function(SQNG) -> + Method2#'queue.declare'{queue = SQNG()}; + {'reply-queue', SQNG} when is_function(SQNG) -> + Method2#'queue.declare'{queue = SQNG()}; _ -> - Method1 + Method2 end. %% -------------------------------------------------------------------------- @@ -193,4 +231,3 @@ unescape(Str) -> unescape(Str, []). unescape("%2F" ++ Str, Acc) -> unescape(Str, [$/ | Acc]); unescape([C | Str], Acc) -> unescape(Str, [C | Acc]); unescape([], Acc) -> lists:reverse(Acc). - diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test.mk b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test.mk index e36d21a..ae3057f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test.mk @@ -11,7 +11,7 @@ # The Original Code is RabbitMQ. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. # IS_SUCCESS:=egrep "(All .+ tests (successful|passed).|Test passed.)" diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/Makefile b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/Makefile index 5b55b51..beef64c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/Makefile @@ -11,7 +11,7 @@ # The Original Code is RabbitMQ. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. # TEST_SOURCES=$(wildcard *.erl) diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_client_SUITE.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_client_SUITE.erl index 13958bb..e0bef04 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_client_SUITE.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_client_SUITE.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(amqp_client_SUITE). @@ -76,13 +76,14 @@ subscribe_nowait_test_() -> ?RUN([]). connection_blocked_network_test_() -> ?RUN([]). non_existent_exchange_test_() -> ?RUN([negative]). -bogus_rpc_test_() -> ?RUN([negative, repeat]). -hard_error_test_() -> ?RUN([negative, repeat]). +bogus_rpc_test_() -> ?RUN([negative, repeat]). +hard_error_test_() -> ?RUN([negative, repeat]). non_existent_user_test_() -> ?RUN([negative]). invalid_password_test_() -> ?RUN([negative]). non_existent_vhost_test_() -> ?RUN([negative]). no_permission_test_() -> ?RUN([negative]). channel_writer_death_test_() -> ?RUN([negative]). +connection_failure_test_() -> ?RUN([negative]). channel_death_test_() -> ?RUN([negative]). shortstr_overflow_property_test_() -> ?RUN([negative]). shortstr_overflow_field_test_() -> ?RUN([negative]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_dbg.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_dbg.erl index f6b5cb0..cb20555 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_dbg.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/amqp_dbg.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(amqp_dbg). diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/negative_test_util.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/negative_test_util.erl index cd5910d..a4f962c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/negative_test_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/negative_test_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(negative_test_util). @@ -80,11 +80,33 @@ hard_error_test() -> test_util:wait_for_death(Channel), test_util:wait_for_death(Connection). +%% The connection should die if the underlying connection is prematurely +%% closed. For a network connection, this means that the TCP socket is +%% closed. For a direct connection (remotely only, of course), this means that +%% the RabbitMQ node appears as down. +connection_failure_test() -> + {ok, Connection} = test_util:new_connection(), + case amqp_connection:info(Connection, [type, amqp_params]) of + [{type, direct}, {amqp_params, Params}] -> + case Params#amqp_params_direct.node of + N when N == node() -> + amqp_connection:close(Connection); + N -> + true = erlang:disconnect_node(N), + net_adm:ping(N) + end; + [{type, network}, {amqp_params, _}] -> + [{sock, Sock}] = amqp_connection:info(Connection, [sock]), + ok = gen_tcp:close(Sock) + end, + test_util:wait_for_death(Connection), + ok. + %% An error in a channel should result in the death of the entire connection. %% The death of the channel is caused by an error in generating the frames -%% (writer dies) - only in the network case +%% (writer dies) channel_writer_death_test() -> - {ok, Connection} = test_util:new_connection(just_network), + {ok, Connection} = test_util:new_connection(), {ok, Channel} = amqp_connection:open_channel(Connection), Publish = #'basic.publish'{routing_key = <<>>, exchange = <<>>}, QoS = #'basic.qos'{prefetch_count = 0}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/test_util.erl b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/test_util.erl index 121a400..b674423 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/test_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-erlang-client/test/test_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(test_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation-management/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-federation-management/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-federation-management/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/federation.js b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/federation.js index 5d3e225..7110ea2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/federation.js +++ b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/federation.js @@ -66,5 +66,5 @@ HELP['federation-trust-user-id'] = 'Set "Yes" to preserve the "user-id" field across a federation link, even if the user-id does not match that used to republish the message. Set to "No" to clear the "user-id" field when messages are federated. Only set this to "Yes" if you trust the upstream broker not to forge user-ids.'; function link_fed_conn(vhost, name) { - return _link_to(fmt_escape_html(name), '#/federation-upstreams/' + esc(vhost) + '/' + esc(name)); + return _link_to(name, '#/federation-upstreams/' + esc(vhost) + '/' + esc(name)); } diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation-upstream.ejs b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation-upstream.ejs index 4271aad..6e66b4e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation-upstream.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation-upstream.ejs @@ -1,13 +1,9 @@ -

Federation Upstream: <%= fmt_string(upstream.name) %>

+

Federation Upstream: <%= fmt_string(upstream.name) %><%= fmt_maybe_vhost(upstream.vhost) %>

Overview

- - - - diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation.ejs b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation.ejs index 6fe3990..15910c9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-federation-management/priv/www/js/tmpl/federation.ejs @@ -74,7 +74,7 @@ diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation-management/src/rabbit_federation_mgmt.erl b/rabbitmq-server/plugins-src/rabbitmq-federation-management/src/rabbit_federation_mgmt.erl index 818f3a9..10dde01 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation-management/src/rabbit_federation_mgmt.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation-management/src/rabbit_federation_mgmt.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_mgmt). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-federation/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/include/rabbit_federation.hrl b/rabbitmq-server/plugins-src/rabbitmq-federation/include/rabbit_federation.hrl index 66c8a8a..0995cfd 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/include/rabbit_federation.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/include/rabbit_federation.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -record(upstream, {uris, @@ -25,7 +25,8 @@ trust_user_id, ack_mode, ha_policy, - name}). + name, + bind_nowait}). -record(upstream_params, {uri, diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_app.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_app.erl index d4bdc29..119ef60 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_app.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_app). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_db.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_db.erl index 7faf3eb..d00f991 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_db.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_db.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_db). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_event.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_event.erl index 856bd1b..677d5f2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_event.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_event.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_event). @@ -19,7 +19,7 @@ -include_lib("rabbit_common/include/rabbit.hrl"). --export([add_handler/0]). +-export([add_handler/0, remove_handler/0]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). @@ -31,6 +31,9 @@ add_handler() -> gen_event:add_handler(rabbit_event, ?MODULE, []). +remove_handler() -> + gen_event:delete_handler(rabbit_event, ?MODULE, []). + init([]) -> {ok, []}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange.erl index 69bce12..fa6102c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% TODO rename this @@ -19,9 +19,11 @@ -rabbit_boot_step({?MODULE, [{description, "federation exchange decorator"}, - {mfa, {rabbit_registry, register, - [exchange_decorator, <<"federation">>, ?MODULE]}}, + {mfa, {rabbit_exchange_decorator, register, + [<<"federation">>, ?MODULE]}}, {requires, rabbit_registry}, + {cleanup, {rabbit_exchange_decorator, unregister, + [<<"federation">>]}}, {enables, recovery}]}). -include_lib("amqp_client/include/amqp_client.hrl"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link.erl index 2730045..12f5316 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_exchange_link). @@ -274,24 +274,28 @@ binding_op(UpdateFun, Cmd, B = #binding{args = Args}, bind_cmd(Type, #binding{key = Key, args = Args}, State = #state{internal_exchange = IntXNameBin, - upstream_params = UpstreamParams}) -> + upstream_params = UpstreamParams, + upstream = Upstream}) -> #upstream_params{x_or_q = X} = UpstreamParams, + #upstream{bind_nowait = Nowait} = Upstream, case update_binding(Args, State) of ignore -> ignore; - NewArgs -> bind_cmd0(Type, name(X), IntXNameBin, Key, NewArgs) + NewArgs -> bind_cmd0(Type, name(X), IntXNameBin, Key, NewArgs, Nowait) end. -bind_cmd0(bind, Source, Destination, RoutingKey, Arguments) -> +bind_cmd0(bind, Source, Destination, RoutingKey, Arguments, Nowait) -> #'exchange.bind'{source = Source, destination = Destination, routing_key = RoutingKey, - arguments = Arguments}; + arguments = Arguments, + nowait = Nowait}; -bind_cmd0(unbind, Source, Destination, RoutingKey, Arguments) -> +bind_cmd0(unbind, Source, Destination, RoutingKey, Arguments, Nowait) -> #'exchange.unbind'{source = Source, destination = Destination, routing_key = RoutingKey, - arguments = Arguments}. + arguments = Arguments, + nowait = Nowait}. %% This function adds information about the current node to the %% binding arguments, or returns 'ignore' if it determines the binding diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link_sup_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link_sup_sup.erl index 239fcbf..529edea 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link_sup_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_exchange_link_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_exchange_link_sup_sup). @@ -62,4 +62,5 @@ init([]) -> {ok, {{one_for_one, 3, 10}, []}}. %% See comment in rabbit_federation_queue_link_sup_sup:id/1 -id(X = #exchange{}) -> X#exchange{scratches = none}. +id(X = #exchange{policy = Policy}) -> X1 = rabbit_exchange:immutable(X), + X1#exchange{policy = Policy}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_sup.erl index dd5f9ce..2999a18 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_link_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_util.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_util.erl index d88f09e..757331f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_link_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_link_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_parameters.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_parameters.erl index 2385bc0..c05f4c0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_parameters.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_parameters.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_parameters). @@ -21,21 +21,30 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -export([validate/5, notify/4, notify_clear/3]). --export([register/0, validate_policy/1, adjust/1]). +-export([register/0, unregister/0, validate_policy/1, adjust/1]). + +-define(RUNTIME_PARAMETERS, + [{runtime_parameter, <<"federation">>}, + {runtime_parameter, <<"federation-upstream">>}, + {runtime_parameter, <<"federation-upstream-set">>}, + {policy_validator, <<"federation-upstream">>}, + {policy_validator, <<"federation-upstream-set">>}]). -rabbit_boot_step({?MODULE, [{description, "federation parameters"}, {mfa, {rabbit_federation_parameters, register, []}}, {requires, rabbit_registry}, + {cleanup, {rabbit_federation_parameters, unregister, []}}, {enables, recovery}]}). register() -> [rabbit_registry:register(Class, Name, ?MODULE) || - {Class, Name} <- [{runtime_parameter, <<"federation">>}, - {runtime_parameter, <<"federation-upstream">>}, - {runtime_parameter, <<"federation-upstream-set">>}, - {policy_validator, <<"federation-upstream">>}, - {policy_validator, <<"federation-upstream-set">>}]], + {Class, Name} <- ?RUNTIME_PARAMETERS], + ok. + +unregister() -> + [rabbit_registry:unregister(Class, Name) || + {Class, Name} <- ?RUNTIME_PARAMETERS], ok. validate(_VHost, <<"federation-upstream-set">>, Name, Term, _User) -> @@ -82,7 +91,8 @@ shared_validation() -> {<<"trust-user-id">>, fun rabbit_parameter_validation:boolean/2, optional}, {<<"ack-mode">>, rabbit_parameter_validation:enum( ['no-ack', 'on-publish', 'on-confirm']), optional}, - {<<"ha-policy">>, fun rabbit_parameter_validation:binary/2, optional}]. + {<<"ha-policy">>, fun rabbit_parameter_validation:binary/2, optional}, + {<<"bind-nowait">>, fun rabbit_parameter_validation:boolean/2, optional}]. validate_uri(Name, Term) when is_binary(Term) -> case rabbit_parameter_validation:binary(Name, Term) of diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue.erl index 7e70e0f..49c4f40 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue.erl @@ -11,16 +11,18 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_queue). -rabbit_boot_step({?MODULE, [{description, "federation queue decorator"}, - {mfa, {rabbit_registry, register, - [queue_decorator, <<"federation">>, ?MODULE]}}, + {mfa, {rabbit_queue_decorator, register, + [<<"federation">>, ?MODULE]}}, {requires, rabbit_registry}, + {cleanup, {rabbit_queue_decorator, unregister, + [<<"federation">>]}}, {enables, recovery}]}). -include_lib("amqp_client/include/amqp_client.hrl"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link.erl index 2daaee2..4dd7810 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_queue_link). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link_sup_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link_sup_sup.erl index ee1a8fe..9c6a703 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link_sup_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_queue_link_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_queue_link_sup_sup). @@ -59,16 +59,14 @@ stop_child(Q) -> init([]) -> {ok, {{one_for_one, 3, 10}, []}}. -%% Clean out all transient aspects of the queue. We need to keep the -%% entire queue around rather than just take its name since we will -%% want to know its policy to determine how to federate it, and its -%% immutable properties in case we want to redeclare it upstream. We -%% don't just take its name and look it up again since that would -%% introduce race conditions when policies change frequently. Note -%% that since we take down all the links and start again when policies -%% change, the policy will always be correct, so we don't clear it out -%% here and can trust it. -id(Q = #amqqueue{}) -> Q#amqqueue{pid = none, - slave_pids = none, - sync_slave_pids = none, - gm_pids = none}. +%% Clean out all mutable aspects of the queue except policy. We need +%% to keep the entire queue around rather than just take its name +%% since we will want to know its policy to determine how to federate +%% it, and its immutable properties in case we want to redeclare it +%% upstream. We don't just take its name and look it up again since +%% that would introduce race conditions when policies change +%% frequently. Note that since we take down all the links and start +%% again when policies change, the policy will always be correct, so +%% we don't clear it out here and can trust it. +id(Q = #amqqueue{policy = Policy}) -> Q1 = rabbit_amqqueue:immutable(Q), + Q1#amqqueue{policy = Policy}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_status.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_status.erl index 7fd7e7e..59dc79e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_status.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_status.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_status). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_sup.erl index 390b115..52a837d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_sup). @@ -23,7 +23,7 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -define(SUPERVISOR, rabbit_federation_sup). --export([start_link/0]). +-export([start_link/0, stop/0]). -export([init/1]). @@ -35,7 +35,9 @@ [{description, "federation"}, {mfa, {rabbit_sup, start_child, [?MODULE]}}, {requires, kernel_ready}, - {enables, rabbit_federation_exchange}]}). + {cleanup, {?MODULE, stop, []}}, + {enables, rabbit_federation_exchange}, + {enables, rabbit_federation_queue}]}). %%---------------------------------------------------------------------------- @@ -44,6 +46,11 @@ start_link() -> rabbit_federation_event:add_handler(), R. +stop() -> + rabbit_federation_event:remove_handler(), + ok = supervisor:terminate_child(rabbit_sup, ?MODULE), + ok = supervisor:delete_child(rabbit_sup, ?MODULE). + %%---------------------------------------------------------------------------- init([]) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream.erl index 168fce0..398dbcf 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_upstream). @@ -131,7 +131,8 @@ from_upstream_or_set(US, Name, U, XorQ) -> binary_to_list( bget('ack-mode', US, U, <<"on-confirm">>))), ha_policy = bget('ha-policy', US, U, none), - name = Name}. + name = Name, + bind_nowait = bget('bind-nowait', US, U, false)}. %%---------------------------------------------------------------------------- diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream_exchange.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream_exchange.erl index e84cb29..920bc9f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream_exchange.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_upstream_exchange.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_upstream_exchange). @@ -21,6 +21,8 @@ {mfa, {rabbit_registry, register, [exchange, <<"x-federation-upstream">>, ?MODULE]}}, {requires, rabbit_registry}, + {cleanup, {rabbit_registry, unregister, + [exchange, <<"x-federation-upstream">>]}}, {enables, recovery}]}). -include_lib("rabbit_common/include/rabbit.hrl"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_util.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_util.erl index b6a1cfd..33e903e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/src/rabbit_federation_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_exchange_test.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_exchange_test.erl index 96913df..cce16f8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_exchange_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_exchange_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_exchange_test). @@ -23,6 +23,7 @@ -import(rabbit_misc, [pget/2]). -import(rabbit_federation_util, [name/1]). +-import(rabbit_test_util, [enable_plugin/2, disable_plugin/2]). -import(rabbit_federation_test_util, [expect/3, expect_empty/2, @@ -566,6 +567,45 @@ federate_unfederate_test() -> assert_connections(Xs, []) end, [x(<<"dyn.exch1">>), x(<<"dyn.exch2">>)]). +dynamic_plugin_stop_start_test() -> + Cfg = single_cfg(), + X1 = <<"dyn.exch1">>, + X2 = <<"dyn.exch2">>, + with_ch( + fun (Ch) -> + set_policy(Cfg, <<"dyn">>, <<"^dyn\\.">>, <<"localhost">>), + + %% Declare federated exchange - get link + assert_connections([X1], [<<"localhost">>]), + + %% Disable plugin, link goes + ok = disable_plugin(Cfg, "rabbitmq_federation"), + %% We can't check with status for obvious reasons... + undefined = whereis(rabbit_federation_sup), + {error, not_found} = rabbit_registry:lookup_module( + exchange, 'x-federation-upstream'), + + %% Create exchange then re-enable plugin, links appear + declare_exchange(Ch, x(X2)), + ok = enable_plugin(Cfg, "rabbitmq_federation"), + assert_connections([X1, X2], [<<"localhost">>]), + {ok, _} = rabbit_registry:lookup_module( + exchange, 'x-federation-upstream'), + + %% Test both exchanges work. They are just federated to + %% themselves so should duplicate messages. + [begin + Q = bind_queue(Ch, X, <<"key">>), + await_binding(Cfg, X, <<"key">>, 2), + publish(Ch, X, <<"key">>, <<"HELLO">>), + expect(Ch, Q, [<<"HELLO">>, <<"HELLO">>]), + delete_queue(Ch, Q) + end || X <- [X1, X2]], + + clear_policy(Cfg, <<"dyn">>), + assert_connections([X1, X2], []) + end, [x(X1)]). + %%---------------------------------------------------------------------------- with_ch(Fun, Xs) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_queue_test.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_queue_test.erl index 5d521d2..d58c0d6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_queue_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_queue_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_queue_test). @@ -23,6 +23,7 @@ -import(rabbit_misc, [pget/2]). -import(rabbit_federation_util, [name/1]). +-import(rabbit_test_util, [enable_plugin/2, disable_plugin/2]). -import(rabbit_federation_test_util, [expect/3, @@ -119,6 +120,32 @@ federate_unfederate_test() -> q(<<"upstream2">>), q(<<"downstream">>)]). +dynamic_plugin_stop_start_test() -> + Cfg = single_cfg(), + Q1 = <<"dyn.q1">>, + Q2 = <<"dyn.q2">>, + U = <<"upstream">>, + with_ch( + fun (Ch) -> + set_policy(Cfg, <<"dyn">>, <<"^dyn\\.">>, U), + %% Declare federated queue - get link + expect_federation(Ch, U, Q1), + + %% Disable plugin, link goes + ok = disable_plugin(Cfg, "rabbitmq_federation"), + expect_no_federation(Ch, U, Q1), + + %% Create exchange then re-enable plugin, links appear + declare_queue(Ch, q(Q2)), + ok = enable_plugin(Cfg, "rabbitmq_federation"), + expect_federation(Ch, U, Q1), + expect_federation(Ch, U, Q2), + + clear_policy(Cfg, <<"dyn">>), + expect_no_federation(Ch, U, Q1), + expect_no_federation(Ch, U, Q2), + delete_queue(Ch, Q2) + end, [q(Q1), q(U)]). %% Downstream: rabbit-test, port 5672 %% Upstream: hare, port 5673 diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_test_util.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_test_util.erl index 87a584e..d70042e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_test_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_test_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_test_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_unit_test.erl b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_unit_test.erl index 3a1cf8b..76d23b8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_unit_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-federation/test/src/rabbit_federation_unit_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_federation_unit_test). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-management-agent/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_app.erl b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_app.erl index 2b47cfd..6220ac6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_app.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_agent_app). @@ -19,16 +19,6 @@ -behaviour(application). -export([start/2, stop/1]). -%% Make sure our database is hooked in *before* listening on the network or -%% recovering queues (i.e. so there can't be any events fired before it starts). --rabbit_boot_step({rabbit_mgmt_db_handler, - [{description, "management agent"}, - {mfa, {rabbit_mgmt_db_handler, add_handler, - []}}, - {requires, rabbit_event}, - {enables, recovery}]}). - - start(_Type, _StartArgs) -> rabbit_mgmt_agent_sup:start_link(). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_sup.erl index 647b99f..cd0635f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_agent_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_agent_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_db_handler.erl b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_db_handler.erl index a3012bb..5bd9bc0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_db_handler.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_db_handler.erl @@ -11,14 +11,24 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_db_handler). +%% Make sure our database is hooked in *before* listening on the network or +%% recovering queues (i.e. so there can't be any events fired before it starts). +-rabbit_boot_step({rabbit_mgmt_db_handler, + [{description, "management agent"}, + {mfa, {?MODULE, add_handler, []}}, + {cleanup, {gen_event, delete_handler, + [rabbit_event, ?MODULE, []]}}, + {requires, rabbit_event}, + {enables, recovery}]}). + -behaviour(gen_event). --export([add_handler/0, gc/0]). +-export([add_handler/0, gc/0, rates_mode/0]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). @@ -27,17 +37,43 @@ add_handler() -> ensure_statistics_enabled(), - gen_event:add_sup_handler(rabbit_event, ?MODULE, []). + gen_event:add_handler(rabbit_event, ?MODULE, []). gc() -> erlang:garbage_collect(whereis(rabbit_event)). +%% some people have reasons to only run with the agent enabled: +%% make it possible for them to configure key management app +%% settings such as rates_mode. +get_management_env(Key) -> + rabbit_misc:get_env( + rabbitmq_management, Key, + rabbit_misc:get_env(rabbitmq_management_agent, Key, undefined)). + +rates_mode() -> + case get_management_env(rates_mode) of + undefined -> basic; + Mode -> Mode + end. + +handle_force_fine_statistics() -> + case get_management_env(force_fine_statistics) of + undefined -> + ok; + X -> + rabbit_log:warning( + "force_fine_statistics set to ~p; ignored.~n" + "Replaced by {rates_mode, none} in the rabbitmq_management " + "application.~n", [X]) + end. + %%---------------------------------------------------------------------------- ensure_statistics_enabled() -> - {ok, ForceStats} = application:get_env(rabbitmq_management_agent, - force_fine_statistics), + ForceStats = rates_mode() =/= none, + handle_force_fine_statistics(), {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), + rabbit_log:info("Management plugin: using rates mode '~p'~n", [rates_mode()]), case {ForceStats, StatsLevel} of {true, fine} -> ok; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_external_stats.erl b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_external_stats.erl index 805e44e..62f783b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_external_stats.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbit_mgmt_external_stats.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_external_stats). @@ -24,19 +24,23 @@ -export([list_registry_plugins/1]). +-import(rabbit_misc, [pget/2]). + -include_lib("rabbit_common/include/rabbit.hrl"). -define(REFRESH_RATIO, 5000). -define(KEYS, [name, partitions, os_pid, fd_used, fd_total, sockets_used, sockets_total, mem_used, mem_limit, mem_alarm, disk_free_limit, disk_free, disk_free_alarm, - proc_used, proc_total, statistics_level, + proc_used, proc_total, rates_mode, uptime, run_queue, processors, exchange_types, - auth_mechanisms, applications, contexts]). + auth_mechanisms, applications, contexts, + log_file, sasl_log_file, db_dir, config_files, net_ticktime, + enabled_plugins, persister_stats]). %%-------------------------------------------------------------------- --record(state, {fd_total}). +-record(state, {fd_total, fhc_stats, fhc_stats_derived, node_owners}). %%-------------------------------------------------------------------- @@ -172,23 +176,34 @@ i(disk_free_limit, _State) -> get_disk_free_limit(); i(disk_free, _State) -> get_disk_free(); i(disk_free_alarm, _State) -> resource_alarm_set(disk); i(contexts, _State) -> rabbit_web_dispatch_contexts(); -i(uptime, _State) -> - {Total, _} = erlang:statistics(wall_clock), - Total; -i(statistics_level, _State) -> - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), - StatsLevel; -i(exchange_types, _State) -> - list_registry_plugins(exchange); +i(uptime, _State) -> {Total, _} = erlang:statistics(wall_clock), + Total; +i(rates_mode, _State) -> rabbit_mgmt_db_handler:rates_mode(); +i(exchange_types, _State) -> list_registry_plugins(exchange); +i(log_file, _State) -> log_location(kernel); +i(sasl_log_file, _State) -> log_location(sasl); +i(db_dir, _State) -> list_to_binary(rabbit_mnesia:dir()); +i(config_files, _State) -> [list_to_binary(F) || F <- rabbit:config_files()]; +i(net_ticktime, _State) -> net_kernel:get_net_ticktime(); +i(persister_stats, State) -> persister_stats(State); +i(enabled_plugins, _State) -> {ok, Dir} = application:get_env( + rabbit, enabled_plugins_file), + rabbit_plugins:read_enabled(Dir); i(auth_mechanisms, _State) -> {ok, Mechanisms} = application:get_env(rabbit, auth_mechanisms), list_registry_plugins( auth_mechanism, fun (N) -> lists:member(list_to_atom(binary_to_list(N)), Mechanisms) end); -i(applications, _State) -> +i(applications, _State) -> [format_application(A) || A <- lists:keysort(1, rabbit_misc:which_applications())]. +log_location(Type) -> + case rabbit:log_location(Type) of + tty -> <<"tty">>; + File -> list_to_binary(File) + end. + resource_alarm_set(Source) -> lists:member({{resource_limit, Source, node()},[]}, rabbit_alarm:get_alarms()). @@ -212,6 +227,51 @@ set_plugin_name(Name, Module) -> [{name, list_to_binary(atom_to_list(Name))} | proplists:delete(name, Module:description())]. +persister_stats(#state{fhc_stats = FHC, + fhc_stats_derived = FHCD}) -> + [{flatten_key(K), V} || {{_Op, Type} = K, V} <- FHC, + Type =/= time] ++ + [{flatten_key(K), V} || {K, V} <- FHCD]. + +flatten_key({A, B}) -> + list_to_atom(atom_to_list(A) ++ "_" ++ atom_to_list(B)). + +cluster_links() -> + {ok, Items} = net_kernel:nodes_info(), + [Link || Item <- Items, + Link <- [format_nodes_info(Item)], Link =/= undefined]. + +format_nodes_info({Node, Info}) -> + Owner = proplists:get_value(owner, Info), + case catch process_info(Owner, links) of + {links, Links} -> + case [Link || Link <- Links, is_port(Link)] of + [Port] -> + {Node, Owner, format_nodes_info1(Port)}; + _ -> + undefined + end; + _ -> + undefined + end. + +format_nodes_info1(Port) -> + case {rabbit_net:socket_ends(Port, inbound), + rabbit_net:getstat(Port, [recv_oct, send_oct])} of + {{ok, {PeerAddr, PeerPort, SockAddr, SockPort}}, {ok, Stats}} -> + [{peer_addr, maybe_ntoab(PeerAddr)}, + {peer_port, PeerPort}, + {sock_addr, maybe_ntoab(SockAddr)}, + {sock_port, SockPort}, + {recv_bytes, pget(recv_oct, Stats)}, + {send_bytes, pget(send_oct, Stats)}]; + _ -> + [] + end. + +maybe_ntoab(A) when is_tuple(A) -> list_to_binary(rabbit_misc:ntoab(A)); +maybe_ntoab(H) -> H. + %%-------------------------------------------------------------------- %% This is slightly icky in that we introduce knowledge of @@ -244,17 +304,18 @@ format_mochiweb_option_list(C) -> format_mochiweb_option(ssl_opts, V) -> format_mochiweb_option_list(V); -format_mochiweb_option(ciphers, V) -> - list_to_binary(rabbit_misc:format("~w", [V])); -format_mochiweb_option(_K, V) when is_list(V) -> - list_to_binary(V); format_mochiweb_option(_K, V) -> - V. + case io_lib:printable_list(V) of + true -> list_to_binary(V); + false -> list_to_binary(rabbit_misc:format("~w", [V])) + end. %%-------------------------------------------------------------------- init([]) -> - State = #state{fd_total = file_handle_cache:ulimit()}, + State = #state{fd_total = file_handle_cache:ulimit(), + fhc_stats = file_handle_cache_stats:get(), + node_owners = sets:new()}, %% If we emit an update straight away we will do so just before %% the mgmt db starts up - and then have to wait ?REFRESH_RATIO %% until we send another. So let's have a shorter wait in the hope @@ -282,7 +343,39 @@ code_change(_, State, _) -> {ok, State}. %%-------------------------------------------------------------------- -emit_update(State) -> +emit_update(State0) -> + State = update_state(State0), rabbit_event:notify(node_stats, infos(?KEYS, State)), erlang:send_after(?REFRESH_RATIO, self(), emit_update), - State. + emit_node_node_stats(State). + +emit_node_node_stats(State = #state{node_owners = Owners}) -> + Links = cluster_links(), + NewOwners = sets:from_list([{Node, Owner} || {Node, Owner, _} <- Links]), + Dead = sets:to_list(sets:subtract(Owners, NewOwners)), + [rabbit_event:notify( + node_node_deleted, [{route, Route}]) || {Node, _Owner} <- Dead, + Route <- [{node(), Node}, + {Node, node()}]], + [rabbit_event:notify( + node_node_stats, [{route, {node(), Node}} | Stats]) || + {Node, _Owner, Stats} <- Links], + State#state{node_owners = NewOwners}. + +update_state(State0 = #state{fhc_stats = FHC0}) -> + FHC = file_handle_cache_stats:get(), + Avgs = [{{Op, avg_time}, avg_op_time(Op, V, FHC, FHC0)} + || {{Op, time}, V} <- FHC], + State0#state{fhc_stats = FHC, + fhc_stats_derived = Avgs}. + +-define(MICRO_TO_MILLI, 1000). + +avg_op_time(Op, Time, FHC, FHC0) -> + Time0 = pget({Op, time}, FHC0), + TimeDelta = Time - Time0, + OpDelta = pget({Op, count}, FHC) - pget({Op, count}, FHC0), + case OpDelta of + 0 -> 0; + _ -> (TimeDelta / OpDelta) / ?MICRO_TO_MILLI + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbitmq_management_agent.app.src b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbitmq_management_agent.app.src index e94c97b..bd4b826 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbitmq_management_agent.app.src +++ b/rabbitmq-server/plugins-src/rabbitmq-management-agent/src/rabbitmq_management_agent.app.src @@ -4,5 +4,5 @@ {modules, []}, {registered, []}, {mod, {rabbit_mgmt_agent_app, []}}, - {env, [{force_fine_statistics, true}]}, + {env, []}, {applications, [kernel, stdlib, rabbit]}]}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/priv/www/visualiser/js/glMatrix-min.js b/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/priv/www/visualiser/js/glMatrix-min.js index 4e4a830..8411d43 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/priv/www/visualiser/js/glMatrix-min.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management-visualiser/priv/www/visualiser/js/glMatrix-min.js @@ -1,32 +1,32 @@ -// glMatrix v0.9.5 -glMatrixArrayType=typeof Float32Array!="undefined"?Float32Array:typeof WebGLFloatArray!="undefined"?WebGLFloatArray:Array;var vec3={};vec3.create=function(a){var b=new glMatrixArrayType(3);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2]}return b};vec3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];return b};vec3.add=function(a,b,c){if(!c||a==c){a[0]+=b[0];a[1]+=b[1];a[2]+=b[2];return a}c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];return c}; -vec3.subtract=function(a,b,c){if(!c||a==c){a[0]-=b[0];a[1]-=b[1];a[2]-=b[2];return a}c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];return c};vec3.negate=function(a,b){b||(b=a);b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];return b};vec3.scale=function(a,b,c){if(!c||a==c){a[0]*=b;a[1]*=b;a[2]*=b;return a}c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;return c}; -vec3.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=Math.sqrt(c*c+d*d+e*e);if(g){if(g==1){b[0]=c;b[1]=d;b[2]=e;return b}}else{b[0]=0;b[1]=0;b[2]=0;return b}g=1/g;b[0]=c*g;b[1]=d*g;b[2]=e*g;return b};vec3.cross=function(a,b,c){c||(c=a);var d=a[0],e=a[1];a=a[2];var g=b[0],f=b[1];b=b[2];c[0]=e*b-a*f;c[1]=a*g-d*b;c[2]=d*f-e*g;return c};vec3.length=function(a){var b=a[0],c=a[1];a=a[2];return Math.sqrt(b*b+c*c+a*a)};vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]}; -vec3.direction=function(a,b,c){c||(c=a);var d=a[0]-b[0],e=a[1]-b[1];a=a[2]-b[2];b=Math.sqrt(d*d+e*e+a*a);if(!b){c[0]=0;c[1]=0;c[2]=0;return c}b=1/b;c[0]=d*b;c[1]=e*b;c[2]=a*b;return c};vec3.lerp=function(a,b,c,d){d||(d=a);d[0]=a[0]+c*(b[0]-a[0]);d[1]=a[1]+c*(b[1]-a[1]);d[2]=a[2]+c*(b[2]-a[2]);return d};vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"};var mat3={}; -mat3.create=function(a){var b=new glMatrixArrayType(9);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9]}return b};mat3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return b};mat3.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=1;a[5]=0;a[6]=0;a[7]=0;a[8]=1;return a}; -mat3.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[5];a[1]=a[3];a[2]=a[6];a[3]=c;a[5]=a[7];a[6]=d;a[7]=e;return a}b[0]=a[0];b[1]=a[3];b[2]=a[6];b[3]=a[1];b[4]=a[4];b[5]=a[7];b[6]=a[2];b[7]=a[5];b[8]=a[8];return b};mat3.toMat4=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=0;b[4]=a[3];b[5]=a[4];b[6]=a[5];b[7]=0;b[8]=a[6];b[9]=a[7];b[10]=a[8];b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; -mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};var mat4={};mat4.create=function(a){var b=new glMatrixArrayType(16);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15]}return b}; -mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a}; -mat4.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[3],g=a[6],f=a[7],h=a[11];a[1]=a[4];a[2]=a[8];a[3]=a[12];a[4]=c;a[6]=a[9];a[7]=a[13];a[8]=d;a[9]=g;a[11]=a[14];a[12]=e;a[13]=f;a[14]=h;return a}b[0]=a[0];b[1]=a[4];b[2]=a[8];b[3]=a[12];b[4]=a[1];b[5]=a[5];b[6]=a[9];b[7]=a[13];b[8]=a[2];b[9]=a[6];b[10]=a[10];b[11]=a[14];b[12]=a[3];b[13]=a[7];b[14]=a[11];b[15]=a[15];return b}; -mat4.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],g=a[4],f=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],o=a[11],m=a[12],n=a[13],p=a[14];a=a[15];return m*k*h*e-j*n*h*e-m*f*l*e+g*n*l*e+j*f*p*e-g*k*p*e-m*k*d*i+j*n*d*i+m*c*l*i-b*n*l*i-j*c*p*i+b*k*p*i+m*f*d*o-g*n*d*o-m*c*h*o+b*n*h*o+g*c*p*o-b*f*p*o-j*f*d*a+g*k*d*a+j*c*h*a-b*k*h*a-g*c*l*a+b*f*l*a}; -mat4.inverse=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],o=a[10],m=a[11],n=a[12],p=a[13],r=a[14],s=a[15],A=c*h-d*f,B=c*i-e*f,t=c*j-g*f,u=d*i-e*h,v=d*j-g*h,w=e*j-g*i,x=k*p-l*n,y=k*r-o*n,z=k*s-m*n,C=l*r-o*p,D=l*s-m*p,E=o*s-m*r,q=1/(A*E-B*D+t*C+u*z-v*y+w*x);b[0]=(h*E-i*D+j*C)*q;b[1]=(-d*E+e*D-g*C)*q;b[2]=(p*w-r*v+s*u)*q;b[3]=(-l*w+o*v-m*u)*q;b[4]=(-f*E+i*z-j*y)*q;b[5]=(c*E-e*z+g*y)*q;b[6]=(-n*w+r*t-s*B)*q;b[7]=(k*w-o*t+m*B)*q;b[8]=(f*D-h*z+j*x)*q; -b[9]=(-c*D+d*z-g*x)*q;b[10]=(n*v-p*t+s*A)*q;b[11]=(-k*v+l*t-m*A)*q;b[12]=(-f*C+h*y-i*x)*q;b[13]=(c*C-d*y+e*x)*q;b[14]=(-n*u+p*B-r*A)*q;b[15]=(k*u-l*B+o*A)*q;return b};mat4.toRotationMat=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; -mat4.toMat3=function(a,b){b||(b=mat3.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[4];b[4]=a[5];b[5]=a[6];b[6]=a[8];b[7]=a[9];b[8]=a[10];return b};mat4.toInverseMat3=function(a,b){var c=a[0],d=a[1],e=a[2],g=a[4],f=a[5],h=a[6],i=a[8],j=a[9],k=a[10],l=k*f-h*j,o=-k*g+h*i,m=j*g-f*i,n=c*l+d*o+e*m;if(!n)return null;n=1/n;b||(b=mat3.create());b[0]=l*n;b[1]=(-k*d+e*j)*n;b[2]=(h*d-e*f)*n;b[3]=o*n;b[4]=(k*c-e*i)*n;b[5]=(-h*c+e*g)*n;b[6]=m*n;b[7]=(-j*c+d*i)*n;b[8]=(f*c-d*g)*n;return b}; -mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],o=a[9],m=a[10],n=a[11],p=a[12],r=a[13],s=a[14];a=a[15];var A=b[0],B=b[1],t=b[2],u=b[3],v=b[4],w=b[5],x=b[6],y=b[7],z=b[8],C=b[9],D=b[10],E=b[11],q=b[12],F=b[13],G=b[14];b=b[15];c[0]=A*d+B*h+t*l+u*p;c[1]=A*e+B*i+t*o+u*r;c[2]=A*g+B*j+t*m+u*s;c[3]=A*f+B*k+t*n+u*a;c[4]=v*d+w*h+x*l+y*p;c[5]=v*e+w*i+x*o+y*r;c[6]=v*g+w*j+x*m+y*s;c[7]=v*f+w*k+x*n+y*a;c[8]=z*d+C*h+D*l+E*p;c[9]=z*e+C*i+D*o+E*r;c[10]=z* -g+C*j+D*m+E*s;c[11]=z*f+C*k+D*n+E*a;c[12]=q*d+F*h+G*l+b*p;c[13]=q*e+F*i+G*o+b*r;c[14]=q*g+F*j+G*m+b*s;c[15]=q*f+F*k+G*n+b*a;return c};mat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1];b=b[2];c[0]=a[0]*d+a[4]*e+a[8]*b+a[12];c[1]=a[1]*d+a[5]*e+a[9]*b+a[13];c[2]=a[2]*d+a[6]*e+a[10]*b+a[14];return c}; -mat4.multiplyVec4=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=b[3];c[0]=a[0]*d+a[4]*e+a[8]*g+a[12]*b;c[1]=a[1]*d+a[5]*e+a[9]*g+a[13]*b;c[2]=a[2]*d+a[6]*e+a[10]*g+a[14]*b;c[3]=a[3]*d+a[7]*e+a[11]*g+a[15]*b;return c}; -mat4.translate=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[12]=a[0]*d+a[4]*e+a[8]*b+a[12];a[13]=a[1]*d+a[5]*e+a[9]*b+a[13];a[14]=a[2]*d+a[6]*e+a[10]*b+a[14];a[15]=a[3]*d+a[7]*e+a[11]*b+a[15];return a}var g=a[0],f=a[1],h=a[2],i=a[3],j=a[4],k=a[5],l=a[6],o=a[7],m=a[8],n=a[9],p=a[10],r=a[11];c[0]=g;c[1]=f;c[2]=h;c[3]=i;c[4]=j;c[5]=k;c[6]=l;c[7]=o;c[8]=m;c[9]=n;c[10]=p;c[11]=r;c[12]=g*d+j*e+m*b+a[12];c[13]=f*d+k*e+n*b+a[13];c[14]=h*d+l*e+p*b+a[14];c[15]=i*d+o*e+r*b+a[15];return c}; -mat4.scale=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[0]*=d;a[1]*=d;a[2]*=d;a[3]*=d;a[4]*=e;a[5]*=e;a[6]*=e;a[7]*=e;a[8]*=b;a[9]*=b;a[10]*=b;a[11]*=b;return a}c[0]=a[0]*d;c[1]=a[1]*d;c[2]=a[2]*d;c[3]=a[3]*d;c[4]=a[4]*e;c[5]=a[5]*e;c[6]=a[6]*e;c[7]=a[7]*e;c[8]=a[8]*b;c[9]=a[9]*b;c[10]=a[10]*b;c[11]=a[11]*b;c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15];return c}; -mat4.rotate=function(a,b,c,d){var e=c[0],g=c[1];c=c[2];var f=Math.sqrt(e*e+g*g+c*c);if(!f)return null;if(f!=1){f=1/f;e*=f;g*=f;c*=f}var h=Math.sin(b),i=Math.cos(b),j=1-i;b=a[0];f=a[1];var k=a[2],l=a[3],o=a[4],m=a[5],n=a[6],p=a[7],r=a[8],s=a[9],A=a[10],B=a[11],t=e*e*j+i,u=g*e*j+c*h,v=c*e*j-g*h,w=e*g*j-c*h,x=g*g*j+i,y=c*g*j+e*h,z=e*c*j+g*h;e=g*c*j-e*h;g=c*c*j+i;if(d){if(a!=d){d[12]=a[12];d[13]=a[13];d[14]=a[14];d[15]=a[15]}}else d=a;d[0]=b*t+o*u+r*v;d[1]=f*t+m*u+s*v;d[2]=k*t+n*u+A*v;d[3]=l*t+p*u+B* -v;d[4]=b*w+o*x+r*y;d[5]=f*w+m*x+s*y;d[6]=k*w+n*x+A*y;d[7]=l*w+p*x+B*y;d[8]=b*z+o*e+r*g;d[9]=f*z+m*e+s*g;d[10]=k*z+n*e+A*g;d[11]=l*z+p*e+B*g;return d};mat4.rotateX=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[4],g=a[5],f=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[0]=a[0];c[1]=a[1];c[2]=a[2];c[3]=a[3];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[4]=e*b+i*d;c[5]=g*b+j*d;c[6]=f*b+k*d;c[7]=h*b+l*d;c[8]=e*-d+i*b;c[9]=g*-d+j*b;c[10]=f*-d+k*b;c[11]=h*-d+l*b;return c}; -mat4.rotateY=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[4]=a[4];c[5]=a[5];c[6]=a[6];c[7]=a[7];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*-d;c[1]=g*b+j*-d;c[2]=f*b+k*-d;c[3]=h*b+l*-d;c[8]=e*d+i*b;c[9]=g*d+j*b;c[10]=f*d+k*b;c[11]=h*d+l*b;return c}; -mat4.rotateZ=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[4],j=a[5],k=a[6],l=a[7];if(c){if(a!=c){c[8]=a[8];c[9]=a[9];c[10]=a[10];c[11]=a[11];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*d;c[1]=g*b+j*d;c[2]=f*b+k*d;c[3]=h*b+l*d;c[4]=e*-d+i*b;c[5]=g*-d+j*b;c[6]=f*-d+k*b;c[7]=h*-d+l*b;return c}; -mat4.frustum=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=e*2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=e*2/i;f[6]=0;f[7]=0;f[8]=(b+a)/h;f[9]=(d+c)/i;f[10]=-(g+e)/j;f[11]=-1;f[12]=0;f[13]=0;f[14]=-(g*e*2)/j;f[15]=0;return f};mat4.perspective=function(a,b,c,d,e){a=c*Math.tan(a*Math.PI/360);b=a*b;return mat4.frustum(-b,b,-a,a,c,d,e)}; -mat4.ortho=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=2/i;f[6]=0;f[7]=0;f[8]=0;f[9]=0;f[10]=-2/j;f[11]=0;f[12]=-(a+b)/h;f[13]=-(d+c)/i;f[14]=-(g+e)/j;f[15]=1;return f}; -mat4.lookAt=function(a,b,c,d){d||(d=mat4.create());var e=a[0],g=a[1];a=a[2];var f=c[0],h=c[1],i=c[2];c=b[1];var j=b[2];if(e==b[0]&&g==c&&a==j)return mat4.identity(d);var k,l,o,m;c=e-b[0];j=g-b[1];b=a-b[2];m=1/Math.sqrt(c*c+j*j+b*b);c*=m;j*=m;b*=m;k=h*b-i*j;i=i*c-f*b;f=f*j-h*c;if(m=Math.sqrt(k*k+i*i+f*f)){m=1/m;k*=m;i*=m;f*=m}else f=i=k=0;h=j*f-b*i;l=b*k-c*f;o=c*i-j*k;if(m=Math.sqrt(h*h+l*l+o*o)){m=1/m;h*=m;l*=m;o*=m}else o=l=h=0;d[0]=k;d[1]=h;d[2]=c;d[3]=0;d[4]=i;d[5]=l;d[6]=j;d[7]=0;d[8]=f;d[9]= -o;d[10]=b;d[11]=0;d[12]=-(k*e+i*g+f*a);d[13]=-(h*e+l*g+o*a);d[14]=-(c*e+j*g+b*a);d[15]=1;return d};mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"};quat4={};quat4.create=function(a){var b=new glMatrixArrayType(4);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3]}return b};quat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];return b}; -quat4.calculateW=function(a,b){var c=a[0],d=a[1],e=a[2];if(!b||a==b){a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return a}b[0]=c;b[1]=d;b[2]=e;b[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return b};quat4.inverse=function(a,b){if(!b||a==b){a[0]*=1;a[1]*=1;a[2]*=1;return a}b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];b[3]=a[3];return b};quat4.length=function(a){var b=a[0],c=a[1],d=a[2];a=a[3];return Math.sqrt(b*b+c*c+d*d+a*a)}; -quat4.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=Math.sqrt(c*c+d*d+e*e+g*g);if(f==0){b[0]=0;b[1]=0;b[2]=0;b[3]=0;return b}f=1/f;b[0]=c*f;b[1]=d*f;b[2]=e*f;b[3]=g*f;return b};quat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2];a=a[3];var f=b[0],h=b[1],i=b[2];b=b[3];c[0]=d*b+a*f+e*i-g*h;c[1]=e*b+a*h+g*f-d*i;c[2]=g*b+a*i+d*h-e*f;c[3]=a*b-d*f-e*h-g*i;return c}; -quat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=a[0];var f=a[1],h=a[2];a=a[3];var i=a*d+f*g-h*e,j=a*e+h*d-b*g,k=a*g+b*e-f*d;d=-b*d-f*e-h*g;c[0]=i*a+d*-b+j*-h-k*-f;c[1]=j*a+d*-f+k*-b-i*-h;c[2]=k*a+d*-h+i*-f-j*-b;return c};quat4.toMat3=function(a,b){b||(b=mat3.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=k+g;b[4]=1-(j+e);b[5]=d-f;b[6]=c-h;b[7]=d+f;b[8]=1-(j+l);return b}; -quat4.toMat4=function(a,b){b||(b=mat4.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=0;b[4]=k+g;b[5]=1-(j+e);b[6]=d-f;b[7]=0;b[8]=c-h;b[9]=d+f;b[10]=1-(j+l);b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b};quat4.slerp=function(a,b,c,d){d||(d=a);var e=c;if(a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]<0)e=-1*c;d[0]=1-c*a[0]+e*b[0];d[1]=1-c*a[1]+e*b[1];d[2]=1-c*a[2]+e*b[2];d[3]=1-c*a[3]+e*b[3];return d}; -quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"}; +// glMatrix v0.9.5 +glMatrixArrayType=typeof Float32Array!="undefined"?Float32Array:typeof WebGLFloatArray!="undefined"?WebGLFloatArray:Array;var vec3={};vec3.create=function(a){var b=new glMatrixArrayType(3);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2]}return b};vec3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];return b};vec3.add=function(a,b,c){if(!c||a==c){a[0]+=b[0];a[1]+=b[1];a[2]+=b[2];return a}c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];return c}; +vec3.subtract=function(a,b,c){if(!c||a==c){a[0]-=b[0];a[1]-=b[1];a[2]-=b[2];return a}c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];return c};vec3.negate=function(a,b){b||(b=a);b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];return b};vec3.scale=function(a,b,c){if(!c||a==c){a[0]*=b;a[1]*=b;a[2]*=b;return a}c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;return c}; +vec3.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=Math.sqrt(c*c+d*d+e*e);if(g){if(g==1){b[0]=c;b[1]=d;b[2]=e;return b}}else{b[0]=0;b[1]=0;b[2]=0;return b}g=1/g;b[0]=c*g;b[1]=d*g;b[2]=e*g;return b};vec3.cross=function(a,b,c){c||(c=a);var d=a[0],e=a[1];a=a[2];var g=b[0],f=b[1];b=b[2];c[0]=e*b-a*f;c[1]=a*g-d*b;c[2]=d*f-e*g;return c};vec3.length=function(a){var b=a[0],c=a[1];a=a[2];return Math.sqrt(b*b+c*c+a*a)};vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]}; +vec3.direction=function(a,b,c){c||(c=a);var d=a[0]-b[0],e=a[1]-b[1];a=a[2]-b[2];b=Math.sqrt(d*d+e*e+a*a);if(!b){c[0]=0;c[1]=0;c[2]=0;return c}b=1/b;c[0]=d*b;c[1]=e*b;c[2]=a*b;return c};vec3.lerp=function(a,b,c,d){d||(d=a);d[0]=a[0]+c*(b[0]-a[0]);d[1]=a[1]+c*(b[1]-a[1]);d[2]=a[2]+c*(b[2]-a[2]);return d};vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"};var mat3={}; +mat3.create=function(a){var b=new glMatrixArrayType(9);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9]}return b};mat3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return b};mat3.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=1;a[5]=0;a[6]=0;a[7]=0;a[8]=1;return a}; +mat3.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[5];a[1]=a[3];a[2]=a[6];a[3]=c;a[5]=a[7];a[6]=d;a[7]=e;return a}b[0]=a[0];b[1]=a[3];b[2]=a[6];b[3]=a[1];b[4]=a[4];b[5]=a[7];b[6]=a[2];b[7]=a[5];b[8]=a[8];return b};mat3.toMat4=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=0;b[4]=a[3];b[5]=a[4];b[6]=a[5];b[7]=0;b[8]=a[6];b[9]=a[7];b[10]=a[8];b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; +mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};var mat4={};mat4.create=function(a){var b=new glMatrixArrayType(16);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15]}return b}; +mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a}; +mat4.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[3],g=a[6],f=a[7],h=a[11];a[1]=a[4];a[2]=a[8];a[3]=a[12];a[4]=c;a[6]=a[9];a[7]=a[13];a[8]=d;a[9]=g;a[11]=a[14];a[12]=e;a[13]=f;a[14]=h;return a}b[0]=a[0];b[1]=a[4];b[2]=a[8];b[3]=a[12];b[4]=a[1];b[5]=a[5];b[6]=a[9];b[7]=a[13];b[8]=a[2];b[9]=a[6];b[10]=a[10];b[11]=a[14];b[12]=a[3];b[13]=a[7];b[14]=a[11];b[15]=a[15];return b}; +mat4.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],g=a[4],f=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],o=a[11],m=a[12],n=a[13],p=a[14];a=a[15];return m*k*h*e-j*n*h*e-m*f*l*e+g*n*l*e+j*f*p*e-g*k*p*e-m*k*d*i+j*n*d*i+m*c*l*i-b*n*l*i-j*c*p*i+b*k*p*i+m*f*d*o-g*n*d*o-m*c*h*o+b*n*h*o+g*c*p*o-b*f*p*o-j*f*d*a+g*k*d*a+j*c*h*a-b*k*h*a-g*c*l*a+b*f*l*a}; +mat4.inverse=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],o=a[10],m=a[11],n=a[12],p=a[13],r=a[14],s=a[15],A=c*h-d*f,B=c*i-e*f,t=c*j-g*f,u=d*i-e*h,v=d*j-g*h,w=e*j-g*i,x=k*p-l*n,y=k*r-o*n,z=k*s-m*n,C=l*r-o*p,D=l*s-m*p,E=o*s-m*r,q=1/(A*E-B*D+t*C+u*z-v*y+w*x);b[0]=(h*E-i*D+j*C)*q;b[1]=(-d*E+e*D-g*C)*q;b[2]=(p*w-r*v+s*u)*q;b[3]=(-l*w+o*v-m*u)*q;b[4]=(-f*E+i*z-j*y)*q;b[5]=(c*E-e*z+g*y)*q;b[6]=(-n*w+r*t-s*B)*q;b[7]=(k*w-o*t+m*B)*q;b[8]=(f*D-h*z+j*x)*q; +b[9]=(-c*D+d*z-g*x)*q;b[10]=(n*v-p*t+s*A)*q;b[11]=(-k*v+l*t-m*A)*q;b[12]=(-f*C+h*y-i*x)*q;b[13]=(c*C-d*y+e*x)*q;b[14]=(-n*u+p*B-r*A)*q;b[15]=(k*u-l*B+o*A)*q;return b};mat4.toRotationMat=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; +mat4.toMat3=function(a,b){b||(b=mat3.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[4];b[4]=a[5];b[5]=a[6];b[6]=a[8];b[7]=a[9];b[8]=a[10];return b};mat4.toInverseMat3=function(a,b){var c=a[0],d=a[1],e=a[2],g=a[4],f=a[5],h=a[6],i=a[8],j=a[9],k=a[10],l=k*f-h*j,o=-k*g+h*i,m=j*g-f*i,n=c*l+d*o+e*m;if(!n)return null;n=1/n;b||(b=mat3.create());b[0]=l*n;b[1]=(-k*d+e*j)*n;b[2]=(h*d-e*f)*n;b[3]=o*n;b[4]=(k*c-e*i)*n;b[5]=(-h*c+e*g)*n;b[6]=m*n;b[7]=(-j*c+d*i)*n;b[8]=(f*c-d*g)*n;return b}; +mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],o=a[9],m=a[10],n=a[11],p=a[12],r=a[13],s=a[14];a=a[15];var A=b[0],B=b[1],t=b[2],u=b[3],v=b[4],w=b[5],x=b[6],y=b[7],z=b[8],C=b[9],D=b[10],E=b[11],q=b[12],F=b[13],G=b[14];b=b[15];c[0]=A*d+B*h+t*l+u*p;c[1]=A*e+B*i+t*o+u*r;c[2]=A*g+B*j+t*m+u*s;c[3]=A*f+B*k+t*n+u*a;c[4]=v*d+w*h+x*l+y*p;c[5]=v*e+w*i+x*o+y*r;c[6]=v*g+w*j+x*m+y*s;c[7]=v*f+w*k+x*n+y*a;c[8]=z*d+C*h+D*l+E*p;c[9]=z*e+C*i+D*o+E*r;c[10]=z* +g+C*j+D*m+E*s;c[11]=z*f+C*k+D*n+E*a;c[12]=q*d+F*h+G*l+b*p;c[13]=q*e+F*i+G*o+b*r;c[14]=q*g+F*j+G*m+b*s;c[15]=q*f+F*k+G*n+b*a;return c};mat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1];b=b[2];c[0]=a[0]*d+a[4]*e+a[8]*b+a[12];c[1]=a[1]*d+a[5]*e+a[9]*b+a[13];c[2]=a[2]*d+a[6]*e+a[10]*b+a[14];return c}; +mat4.multiplyVec4=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=b[3];c[0]=a[0]*d+a[4]*e+a[8]*g+a[12]*b;c[1]=a[1]*d+a[5]*e+a[9]*g+a[13]*b;c[2]=a[2]*d+a[6]*e+a[10]*g+a[14]*b;c[3]=a[3]*d+a[7]*e+a[11]*g+a[15]*b;return c}; +mat4.translate=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[12]=a[0]*d+a[4]*e+a[8]*b+a[12];a[13]=a[1]*d+a[5]*e+a[9]*b+a[13];a[14]=a[2]*d+a[6]*e+a[10]*b+a[14];a[15]=a[3]*d+a[7]*e+a[11]*b+a[15];return a}var g=a[0],f=a[1],h=a[2],i=a[3],j=a[4],k=a[5],l=a[6],o=a[7],m=a[8],n=a[9],p=a[10],r=a[11];c[0]=g;c[1]=f;c[2]=h;c[3]=i;c[4]=j;c[5]=k;c[6]=l;c[7]=o;c[8]=m;c[9]=n;c[10]=p;c[11]=r;c[12]=g*d+j*e+m*b+a[12];c[13]=f*d+k*e+n*b+a[13];c[14]=h*d+l*e+p*b+a[14];c[15]=i*d+o*e+r*b+a[15];return c}; +mat4.scale=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[0]*=d;a[1]*=d;a[2]*=d;a[3]*=d;a[4]*=e;a[5]*=e;a[6]*=e;a[7]*=e;a[8]*=b;a[9]*=b;a[10]*=b;a[11]*=b;return a}c[0]=a[0]*d;c[1]=a[1]*d;c[2]=a[2]*d;c[3]=a[3]*d;c[4]=a[4]*e;c[5]=a[5]*e;c[6]=a[6]*e;c[7]=a[7]*e;c[8]=a[8]*b;c[9]=a[9]*b;c[10]=a[10]*b;c[11]=a[11]*b;c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15];return c}; +mat4.rotate=function(a,b,c,d){var e=c[0],g=c[1];c=c[2];var f=Math.sqrt(e*e+g*g+c*c);if(!f)return null;if(f!=1){f=1/f;e*=f;g*=f;c*=f}var h=Math.sin(b),i=Math.cos(b),j=1-i;b=a[0];f=a[1];var k=a[2],l=a[3],o=a[4],m=a[5],n=a[6],p=a[7],r=a[8],s=a[9],A=a[10],B=a[11],t=e*e*j+i,u=g*e*j+c*h,v=c*e*j-g*h,w=e*g*j-c*h,x=g*g*j+i,y=c*g*j+e*h,z=e*c*j+g*h;e=g*c*j-e*h;g=c*c*j+i;if(d){if(a!=d){d[12]=a[12];d[13]=a[13];d[14]=a[14];d[15]=a[15]}}else d=a;d[0]=b*t+o*u+r*v;d[1]=f*t+m*u+s*v;d[2]=k*t+n*u+A*v;d[3]=l*t+p*u+B* +v;d[4]=b*w+o*x+r*y;d[5]=f*w+m*x+s*y;d[6]=k*w+n*x+A*y;d[7]=l*w+p*x+B*y;d[8]=b*z+o*e+r*g;d[9]=f*z+m*e+s*g;d[10]=k*z+n*e+A*g;d[11]=l*z+p*e+B*g;return d};mat4.rotateX=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[4],g=a[5],f=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[0]=a[0];c[1]=a[1];c[2]=a[2];c[3]=a[3];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[4]=e*b+i*d;c[5]=g*b+j*d;c[6]=f*b+k*d;c[7]=h*b+l*d;c[8]=e*-d+i*b;c[9]=g*-d+j*b;c[10]=f*-d+k*b;c[11]=h*-d+l*b;return c}; +mat4.rotateY=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[4]=a[4];c[5]=a[5];c[6]=a[6];c[7]=a[7];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*-d;c[1]=g*b+j*-d;c[2]=f*b+k*-d;c[3]=h*b+l*-d;c[8]=e*d+i*b;c[9]=g*d+j*b;c[10]=f*d+k*b;c[11]=h*d+l*b;return c}; +mat4.rotateZ=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[4],j=a[5],k=a[6],l=a[7];if(c){if(a!=c){c[8]=a[8];c[9]=a[9];c[10]=a[10];c[11]=a[11];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*d;c[1]=g*b+j*d;c[2]=f*b+k*d;c[3]=h*b+l*d;c[4]=e*-d+i*b;c[5]=g*-d+j*b;c[6]=f*-d+k*b;c[7]=h*-d+l*b;return c}; +mat4.frustum=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=e*2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=e*2/i;f[6]=0;f[7]=0;f[8]=(b+a)/h;f[9]=(d+c)/i;f[10]=-(g+e)/j;f[11]=-1;f[12]=0;f[13]=0;f[14]=-(g*e*2)/j;f[15]=0;return f};mat4.perspective=function(a,b,c,d,e){a=c*Math.tan(a*Math.PI/360);b=a*b;return mat4.frustum(-b,b,-a,a,c,d,e)}; +mat4.ortho=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=2/i;f[6]=0;f[7]=0;f[8]=0;f[9]=0;f[10]=-2/j;f[11]=0;f[12]=-(a+b)/h;f[13]=-(d+c)/i;f[14]=-(g+e)/j;f[15]=1;return f}; +mat4.lookAt=function(a,b,c,d){d||(d=mat4.create());var e=a[0],g=a[1];a=a[2];var f=c[0],h=c[1],i=c[2];c=b[1];var j=b[2];if(e==b[0]&&g==c&&a==j)return mat4.identity(d);var k,l,o,m;c=e-b[0];j=g-b[1];b=a-b[2];m=1/Math.sqrt(c*c+j*j+b*b);c*=m;j*=m;b*=m;k=h*b-i*j;i=i*c-f*b;f=f*j-h*c;if(m=Math.sqrt(k*k+i*i+f*f)){m=1/m;k*=m;i*=m;f*=m}else f=i=k=0;h=j*f-b*i;l=b*k-c*f;o=c*i-j*k;if(m=Math.sqrt(h*h+l*l+o*o)){m=1/m;h*=m;l*=m;o*=m}else o=l=h=0;d[0]=k;d[1]=h;d[2]=c;d[3]=0;d[4]=i;d[5]=l;d[6]=j;d[7]=0;d[8]=f;d[9]= +o;d[10]=b;d[11]=0;d[12]=-(k*e+i*g+f*a);d[13]=-(h*e+l*g+o*a);d[14]=-(c*e+j*g+b*a);d[15]=1;return d};mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"};quat4={};quat4.create=function(a){var b=new glMatrixArrayType(4);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3]}return b};quat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];return b}; +quat4.calculateW=function(a,b){var c=a[0],d=a[1],e=a[2];if(!b||a==b){a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return a}b[0]=c;b[1]=d;b[2]=e;b[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return b};quat4.inverse=function(a,b){if(!b||a==b){a[0]*=1;a[1]*=1;a[2]*=1;return a}b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];b[3]=a[3];return b};quat4.length=function(a){var b=a[0],c=a[1],d=a[2];a=a[3];return Math.sqrt(b*b+c*c+d*d+a*a)}; +quat4.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=Math.sqrt(c*c+d*d+e*e+g*g);if(f==0){b[0]=0;b[1]=0;b[2]=0;b[3]=0;return b}f=1/f;b[0]=c*f;b[1]=d*f;b[2]=e*f;b[3]=g*f;return b};quat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2];a=a[3];var f=b[0],h=b[1],i=b[2];b=b[3];c[0]=d*b+a*f+e*i-g*h;c[1]=e*b+a*h+g*f-d*i;c[2]=g*b+a*i+d*h-e*f;c[3]=a*b-d*f-e*h-g*i;return c}; +quat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=a[0];var f=a[1],h=a[2];a=a[3];var i=a*d+f*g-h*e,j=a*e+h*d-b*g,k=a*g+b*e-f*d;d=-b*d-f*e-h*g;c[0]=i*a+d*-b+j*-h-k*-f;c[1]=j*a+d*-f+k*-b-i*-h;c[2]=k*a+d*-h+i*-f-j*-b;return c};quat4.toMat3=function(a,b){b||(b=mat3.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=k+g;b[4]=1-(j+e);b[5]=d-f;b[6]=c-h;b[7]=d+f;b[8]=1-(j+l);return b}; +quat4.toMat4=function(a,b){b||(b=mat4.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=0;b[4]=k+g;b[5]=1-(j+e);b[6]=d-f;b[7]=0;b[8]=c-h;b[9]=d+f;b[10]=1-(j+l);b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b};quat4.slerp=function(a,b,c,d){d||(d=a);var e=c;if(a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]<0)e=-1*c;d[0]=1-c*a[0]+e*b[0];d[1]=1-c*a[1]+e*b[1];d[2]=1-c*a[2]+e*b[2];d[3]=1-c*a[3]+e*b[3];return d}; +quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"}; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-management/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/LICENSE-MPL-RabbitMQ b/rabbitmq-server/plugins-src/rabbitmq-management/LICENSE-MPL-RabbitMQ index b412190..0339c53 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/LICENSE-MPL-RabbitMQ +++ b/rabbitmq-server/plugins-src/rabbitmq-management/LICENSE-MPL-RabbitMQ @@ -447,7 +447,7 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ Management Plugin. The Initial Developer of the Original Code is GoPivotal, Inc. - Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved.'' + Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/bin/rabbitmqadmin b/rabbitmq-server/plugins-src/rabbitmq-management/bin/rabbitmqadmin index 6f4cb6c..f8f2da5 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-management/bin/rabbitmqadmin +++ b/rabbitmq-server/plugins-src/rabbitmq-management/bin/rabbitmqadmin @@ -13,40 +13,55 @@ # The Original Code is RabbitMQ Management Plugin. # # The Initial Developer of the Original Code is GoPivotal, Inc. -# Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +# Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. import sys -if sys.version_info[0] < 2 or sys.version_info[1] < 6: - print "Sorry, rabbitmqadmin requires at least Python 2.6." +if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 6): + print("Sorry, rabbitmqadmin requires at least Python 2.6.") sys.exit(1) -from ConfigParser import ConfigParser, NoSectionError from optparse import OptionParser, TitledHelpFormatter -import httplib import urllib -import urlparse import base64 import json import os import socket +if sys.version_info[0] == 2: + from ConfigParser import ConfigParser, NoSectionError + import httplib + import urlparse + from urllib import quote_plus + def b64(s): + return base64.b64encode(s) +else: + from configparser import ConfigParser, NoSectionError + import http.client as httplib + import urllib.parse as urlparse + from urllib.parse import quote_plus + def b64(s): + return base64.b64encode(s.encode('utf-8')).decode('utf-8') + VERSION = '%%VSN%%' -LISTABLE = {'connections': {'vhost': False}, - 'channels': {'vhost': False}, - 'exchanges': {'vhost': True}, - 'queues': {'vhost': True}, - 'bindings': {'vhost': True}, +LISTABLE = {'connections': {'vhost': False, 'cols': ['name','user','channels']}, + 'channels': {'vhost': False, 'cols': ['name', 'user']}, + 'consumers': {'vhost': True}, + 'exchanges': {'vhost': True, 'cols': ['name', 'type']}, + 'queues': {'vhost': True, 'cols': ['name', 'messages']}, + 'bindings': {'vhost': True, 'cols': ['source', 'destination', + 'routing_key']}, 'users': {'vhost': False}, - 'vhosts': {'vhost': False}, + 'vhosts': {'vhost': False, 'cols': ['name', 'messages']}, 'permissions': {'vhost': False}, - 'nodes': {'vhost': False}, - 'parameters': {'vhost': False, - 'json': ['value']}, - 'policies': {'vhost': False, - 'json': ['definition']}} + 'nodes': {'vhost': False, 'cols': ['name','type','mem_used']}, + 'parameters': {'vhost': False, 'json': ['value']}, + 'policies': {'vhost': False, 'json': ['definition']}} -SHOWABLE = {'overview': {'vhost': False}} +SHOWABLE = {'overview': {'vhost': False, 'cols': ['rabbitmq_version', + 'cluster_name', + 'queue_totals.messages', + 'object_totals.queues']}} PROMOTE_COLUMNS = ['vhost', 'name', 'type', 'source', 'destination', 'destination_type', 'routing_key'] @@ -118,8 +133,10 @@ PURGABLE = { EXTRA_VERBS = { 'publish': {'mandatory': ['routing_key'], 'optional': {'payload': None, + 'properties': {}, 'exchange': 'amq.default', 'payload_encoding': 'string'}, + 'json': ['properties'], 'uri': '/exchanges/{vhost}/{exchange}/publish'}, 'get': {'mandatory': ['queue'], 'optional': {'count': '1', 'requeue': 'true', @@ -341,7 +358,7 @@ def make_configuration(): try: config.read(options.config) new_conf = dict(config.items(options.node)) - except NoSectionError, error: + except NoSectionError as error: if options.node == "default": pass else: @@ -384,14 +401,14 @@ def main(): method() def output(s): - print maybe_utf8(s, sys.stdout) + print(maybe_utf8(s, sys.stdout)) def die(s): sys.stderr.write(maybe_utf8("*** {0}\n".format(s), sys.stderr)) exit(1) def maybe_utf8(s, stream): - if stream.isatty(): + if sys.version_info[0] == 3 or stream.isatty(): # It will have an encoding, which Python will respect return s else: @@ -424,14 +441,14 @@ class Management: else: conn = httplib.HTTPConnection(self.options.hostname, self.options.port) - headers = {"Authorization": - "Basic " + base64.b64encode(self.options.username + ":" + - self.options.password)} + auth = (self.options.username + ":" + self.options.password) + + headers = {"Authorization": "Basic " + b64(auth)} if body != "": headers["Content-Type"] = "application/json" try: conn.request(method, path, body, headers) - except socket.error, e: + except socket.error as e: die("Could not connect: {0}".format(e)) resp = conn.getresponse() if resp.status == 400: @@ -449,7 +466,7 @@ class Management: if resp.status < 200 or resp.status > 400: raise Exception("Received %d %s for path %s\n%s" % (resp.status, resp.reason, path, resp.read())) - return resp.read() + return resp.read().decode('utf-8') def verbose(self, string): if self.options.verbose: @@ -459,6 +476,11 @@ class Management: assert_usage(len(self.args) == 1, 'Exactly one argument required') return self.args[0] + def use_cols(self): + # Deliberately do not cast to int here; we only care about the + # default, not explicit setting. + return self.options.depth == 1 and not 'json' in self.options.format + def invoke_help(self): if len(self.args) == 0: parser.print_help() @@ -472,15 +494,14 @@ class Management: assert_usage(False, """help topic must be one of: subcommands config""") - print usage + print(usage) exit(0) def invoke_publish(self): (uri, upload) = self.parse_args(self.args, EXTRA_VERBS['publish']) - upload['properties'] = {} # TODO do we care here? if not 'payload' in upload: data = sys.stdin.read() - upload['payload'] = base64.b64encode(data) + upload['payload'] = b64(data) upload['payload_encoding'] = 'base64' resp = json.loads(self.post(uri, json.dumps(upload))) if resp['routed']: @@ -521,16 +542,14 @@ class Management: % (self.options.hostname, path)) def invoke_list(self): - cols = self.args[1:] - (uri, obj_info) = self.list_show_uri(LISTABLE, 'list', cols) + (uri, obj_info, cols) = self.list_show_uri(LISTABLE, 'list') format_list(self.get(uri), cols, obj_info, self.options) def invoke_show(self): - cols = self.args[1:] - (uri, obj_info) = self.list_show_uri(SHOWABLE, 'show', cols) + (uri, obj_info, cols) = self.list_show_uri(SHOWABLE, 'show') format_list('[{0}]'.format(self.get(uri)), cols, obj_info, self.options) - def list_show_uri(self, obj_types, verb, cols): + def list_show_uri(self, obj_types, verb): obj_type = self.args[0] assert_usage(obj_type in obj_types, "Don't know how to {0} {1}".format(verb, obj_type)) @@ -538,7 +557,10 @@ class Management: uri = "/%s" % obj_type query = [] if obj_info['vhost'] and self.options.vhost: - uri += "/%s" % urllib.quote_plus(self.options.vhost) + uri += "/%s" % quote_plus(self.options.vhost) + cols = self.args[1:] + if cols == [] and 'cols' in obj_info and self.use_cols(): + cols = obj_info['cols'] if cols != []: query.append("columns=" + ",".join(cols)) sort = self.options.sort @@ -549,7 +571,7 @@ class Management: query = "&".join(query) if query != "": uri += "?" + query - return (uri, obj_info) + return (uri, obj_info, cols) def invoke_declare(self): (obj_type, uri, upload) = self.declare_delete_parse(DECLARABLE) @@ -589,7 +611,7 @@ class Management: uri_template = obj['uri'] upload = {} for k in optional.keys(): - if optional[k]: + if optional[k] is not None: upload[k] = optional[k] for arg in args: assert_usage("=" in arg, @@ -609,8 +631,8 @@ class Management: uri_args = {} for k in upload: v = upload[k] - if v and isinstance(v, basestring): - uri_args[k] = urllib.quote_plus(v) + if v and isinstance(v, (str, bytes)): + uri_args[k] = quote_plus(v) if k == 'destination_type': uri_args['destination_char'] = v[0] uri = uri_template.format(**uri_args) @@ -620,7 +642,7 @@ class Management: try: return json.loads(text) except ValueError: - print "Could not parse JSON:\n {0}".format(text) + print("Could not parse JSON:\n {0}".format(text)) sys.exit(1) def format_list(json_list, columns, args, options): @@ -646,7 +668,7 @@ class Lister: output(string) def display(self, json_list): - depth = sys.maxint + depth = sys.maxsize if len(self.columns) == 0: depth = int(self.options.depth) (columns, table) = self.list_to_table(json.loads(json_list), depth) @@ -666,7 +688,7 @@ class Lister: column = prefix == '' and key or (prefix + '.' + key) subitem = item[key] if type(subitem) == dict: - if self.obj_info.has_key('json') and key in self.obj_info['json']: + if 'json' in self.obj_info and key in self.obj_info['json']: fun(column, json.dumps(subitem)) else: if depth < max_depth: @@ -676,7 +698,7 @@ class Lister: # mind (which come out looking decent); the second # one has applications in nodes (which look less # so, but what would look good?). - if [x for x in subitem if type(x) != unicode] == []: + if [x for x in subitem if type(x) != str] == []: serialised = " ".join(subitem) else: serialised = json.dumps(subitem) @@ -689,17 +711,17 @@ class Lister: def add_to_row(col, val): if col in column_ix: - row[column_ix[col]] = unicode(val) + row[column_ix[col]] = str(val) if len(self.columns) == 0: for item in items: add('', 1, item, add_to_columns) - columns = columns.keys() + columns = list(columns.keys()) columns.sort(key=column_sort_key) else: columns = self.columns - for i in xrange(0, len(columns)): + for i in range(0, len(columns)): column_ix[columns[i]] = i for item in items: row = len(columns) * [''] @@ -733,10 +755,10 @@ class LongList(Lister): max_width = 0 for col in columns: max_width = max(max_width, len(col)) - fmt = "{0:>" + unicode(max_width) + "}: {1}" + fmt = "{0:>" + str(max_width) + "}: {1}" output(sep) - for i in xrange(0, len(table)): - for j in xrange(0, len(columns)): + for i in range(0, len(table)): + for j in range(0, len(columns)): output(fmt.format(columns[j], table[i][j])) output(sep) @@ -754,8 +776,8 @@ class TableList(Lister): def ascii_table(self, rows): table = "" col_widths = [0] * len(rows[0]) - for i in xrange(0, len(rows[0])): - for j in xrange(0, len(rows)): + for i in range(0, len(rows[0])): + for j in range(0, len(rows)): col_widths[i] = max(col_widths[i], len(rows[j][i])) self.ascii_bar(col_widths) self.ascii_row(col_widths, rows[0], "^") @@ -766,8 +788,8 @@ class TableList(Lister): def ascii_row(self, col_widths, row, align): txt = "|" - for i in xrange(0, len(col_widths)): - fmt = " {0:" + align + unicode(col_widths[i]) + "} " + for i in range(0, len(col_widths)): + fmt = " {0:" + align + str(col_widths[i]) + "} " txt += fmt.format(row[i]) + "|" output(txt) @@ -784,9 +806,9 @@ class KeyValueList(Lister): self.options = options def display_list(self, columns, table): - for i in xrange(0, len(table)): + for i in range(0, len(table)): row = [] - for j in xrange(0, len(columns)): + for j in range(0, len(columns)): row.append("{0}=\"{1}\"".format(columns[j], table[i][j])) output(" ".join(row)) @@ -799,7 +821,7 @@ class BashList(Lister): def display_list(self, columns, table): ix = None - for i in xrange(0, len(columns)): + for i in range(0, len(columns)): if columns[i] == 'name': ix = i if ix is not None: diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/etc/rabbit-test.config b/rabbitmq-server/plugins-src/rabbitmq-management/etc/rabbit-test.config index f1ed238..6b9bbe2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/etc/rabbit-test.config +++ b/rabbitmq-server/plugins-src/rabbitmq-management/etc/rabbit-test.config @@ -5,6 +5,8 @@ %% List of {MaxAgeSecs, IfTimestampDivisibleBySecs} [{global, [{10000000000000, 1}]}, {basic, [{10000000000000, 1}]}, - {detailed, [{10000000000000, 1}]}]} + {detailed, [{10000000000000, 1}]}]}, + %% We're going to test this, so enable it! + {rates_mode, detailed} ]} ]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/include/rabbit_mgmt.hrl b/rabbitmq-server/plugins-src/rabbitmq-management/include/rabbit_mgmt.hrl index d9f342e..43cc67d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/include/rabbit_mgmt.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/include/rabbit_mgmt.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -record(context, {user, password = none}). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/package.mk b/rabbitmq-server/plugins-src/rabbitmq-management/package.mk index 16e5c58..3d0817a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/package.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-management/package.mk @@ -5,7 +5,7 @@ COVER:=false WITH_BROKER_TEST_COMMANDS:=rabbit_test_runner:run_in_broker(\"$(PACKAGE_DIR)/test/ebin\",\"$(FILTER)\") WITH_BROKER_TEST_CONFIG:=$(PACKAGE_DIR)/etc/rabbit-test STANDALONE_TEST_COMMANDS:=rabbit_test_runner:run_multi(\"$(UMBRELLA_BASE_DIR)/rabbitmq-server\",\"$(PACKAGE_DIR)/test/ebin\",\"$(FILTER)\",$(COVER),\"/tmp/rabbitmq-multi-node/plugins\") -WITH_BROKER_TEST_SCRIPTS:=$(PACKAGE_DIR)/test/src/rabbitmqadmin-test.py +WITH_BROKER_TEST_SCRIPTS:=$(PACKAGE_DIR)/test/src/rabbitmqadmin-test-wrapper.sh CONSTRUCT_APP_PREREQS:=$(shell find $(PACKAGE_DIR)/priv -type f) $(PACKAGE_DIR)/bin/rabbitmqadmin define construct_app_commands diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/api/index.html b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/api/index.html index 4a547e5..a509a69 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/api/index.html +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/api/index.html @@ -202,7 +202,9 @@ Content-Length: 0 @@ -227,11 +229,24 @@ Content-Length: 0 messages. POST to upload an existing set of definitions. Note that:
    -
  • The definitions are merged. Anything already existing is - untouched.
  • -
  • Conflicts will cause an error.
  • -
  • In the event of an error you will be left with a - part-applied set of definitions.
  • +
  • + The definitions are merged. Anything already existing on + the server but not in the uploaded definitions is + untouched. +
  • +
  • + Conflicting definitions on immutable objects (exchanges, + queues and bindings) will cause an error. +
  • +
  • + Conflicting definitions on mutable objects will cause + the object in the server to be overwritten with the + object from the definitions. +
  • +
  • + In the event of an error you will be left with a + part-applied set of definitions. +
For convenience you may upload a file from a browser to this URI (i.e. you can use multipart/form-data as @@ -286,6 +301,22 @@ Content-Length: 0 + + + + + + + + + + + + + + + + @@ -308,9 +339,17 @@ Content-Length: 0 - + @@ -348,10 +387,13 @@ Content-Length: 0
{"routed": true}
routed will be true if the message was sent to at least one queue. -

Please note that the publish / get paths in the HTTP API are - intended for injecting test messages, diagnostics etc - they do not - implement reliable delivery and so should be treated as a sysadmin's - tool rather than a general API for messaging.

+

+ Please note that the HTTP API is not ideal for high + performance publishing; the need to create a new TCP + connection for each message published can limit message + throughput compared to AMQP or other protocols using + long-lived connections. +

@@ -376,9 +418,18 @@ Content-Length: 0 - + @@ -435,10 +486,12 @@ Content-Length: 0 message payload if it is larger than the size given (in bytes).

truncate is optional; all other keys are mandatory.

-

Please note that the publish / get paths in the HTTP API are - intended for injecting test messages, diagnostics etc - they do not - implement reliable delivery and so should be treated as a sysadmin's - tool rather than a general API for messaging.

+

+ Please note that the get path in the HTTP API is intended + for diagnostics etc - it does not implement reliable + delivery and so should be treated as a sysadmin's tool + rather than a general API for messaging. +

@@ -467,7 +520,7 @@ Content-Length: 0 queue. Remember, an exchange and a queue can be bound together many times! To create a new binding, POST to this URI. You will need a body looking something like this: -
{"routing_key":"my_routing_key","arguments":[]}
+
{"routing_key":"my_routing_key","arguments":{}}
All keys are optional. The response will contain a Location header telling you the URI of your new binding. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/css/main.css b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/css/main.css index 43e68c5..74a321d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/css/main.css +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/css/main.css @@ -40,11 +40,24 @@ div.box, div.section, div.section-hidden { overflow: auto; width: 100%; } .right { float: right; } .clear { clear: both; } -.help, .rate-options { color: #888; cursor: pointer; } -.help:hover, .rate-options:hover { color: #444; } +.help, .popup-options-link { color: #888; cursor: pointer; } +.help:hover, .popup-options-link:hover { color: #444; } -.tag-link { color: #444; cursor: pointer; } -.tag-link:hover { color: #888; } +.rate-visibility-option { cursor: pointer; padding: 4px; background: #fafafa; border: 1px solid #f0f0f0; border-radius: 3px; display:block; } +.rate-visibility-option:hover { background: #ddf; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddf),color-stop(1, #bbf)); + border: 1px solid #88d; + border-radius: 3px; } + +.rate-visibility-option-hidden { text-decoration: line-through; color: #888; } + + +table.legend { float: left; } +table.legend th { padding: 4px 10px 4px 0; width: 80px; } +table.legend td { padding: 4px 0 4px 10px; width: 130px; } + +.tag-link, .argument-link { color: #444; cursor: pointer; } +.tag-link:hover, .argument-link:hover { color: #888; } .filter { overflow: auto; width: 100%; margin-bottom: 10px; } .filter table { float: left; } @@ -59,18 +72,23 @@ input#truncate { width: 50px; text-align: right; } table { border-collapse: collapse; } table th { font-weight: normal; color: black; } -table th, table td { font: 12px/17px Verdana,sans-serif; padding: 4px; } +table th, table td { font: 12px Verdana,sans-serif; padding: 5px 4px; } table.list th, table.list td { vertical-align: top; min-width: 5em; width: auto; } -table.list { border-width: 1px; border-bottom: 1px solid #ccc; margin-bottom: 1em; } -table.list th, table.list td { border-left: 1px solid #ccc; border-right: 1px solid #ccc; } -table.list th { text-align: center; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } +table.list { border-width: 1px; margin-bottom: 1em; } +table.list th, table.list td { border: 1px solid #ccc; } +table.list th { text-align: center; } +table.list th.plus-minus { border: none; min-width: 2em; } table.list td a { display: block; width: 100%; } table.list th a.sort { display: block; width: 100%; cursor: pointer; } table.list th a.sort .arrow { color: #888; } table.list td p { margin: 0; padding: 1px 0 0 0; } table.list td p.warning { margin: 0; padding: 5px; } +table.list td.plain, table.list td.plain td, table.list td.plain th { border: none; background: none; } +table.list th.plain { border-left: none; border-top: none; border-right: none; background: none; } +table.list th.plain h3 { margin: 0; border: 0; } + #main .internal-purpose, #main .internal-purpose * { color: #aaa; } div.section table.list, div.section-hidden table.list { margin-bottom: 0; } @@ -81,33 +99,37 @@ div.colour-key { float: left; width: 10px; height: 10px; margin: 3px 5px 0 0;} div.memory-info { float: left; padding: 10px 10px 0 0; } button.memory-button { margin-top: 10px; } -div.memory_connection_procs { background: #955300; } -div.memory_queue_procs { background: #da7900; } -div.memory_plugins { background: #ffc884; } -div.memory_other_proc { background: #fff4e7; } -div.memory_mnesia { background: #005395; } -div.memory_msg_index { background: #0079da; } -div.memory_mgmt_db { background: #84c8ff; } -div.memory_other_ets { background: #e7f4ff; } -div.memory_binary { background: #666; } -div.memory_code { background: #999; } -div.memory_atom { background: #bbb; } -div.memory_other_system { background: #ddd; } +div.memory_queue { background: #bd4688; } +div.memory_binary { background: url(../img/bg-binary.png); } +div.memory_conn { background: #dada66; } +div.memory_proc { background: #6abf59; } +div.memory_table { background: #6679da; } +div.memory_system { background: #999; } + +div.memory-bar div.memory_queue { border-right: solid 1px #eb50a6; } +div.memory-bar div.memory_binary { border-right: solid 1px #eb50a6; } +div.memory-bar div.memory_conn { border-right: solid 1px #ebeb8d; } +div.memory-bar div.memory_proc { border-right: solid 1px #79da66; } +div.memory-bar div.memory_table { border-right: solid 1px #8d9ceb; } +div.memory-bar div.memory_system { border-right: solid 1px #bbb; } sub { display: block; font-size: 0.8em; color: #888; } small { font-size: 0.8em; color: #888; } #main sub a { color: #888; } #main sub a:hover { color: #444; } +table.argument-links { color: #888; } +table.argument-links td { font-size: 0.64em; vertical-align: top; } .unknown { color: #888; } -table.facts { float: left; margin-right: 50px; } -table.facts th { color: black; text-align: right; border-right: 1px solid #ccc; } +table.facts { float: left; } +table.facts th, table.legend th { color: black; text-align: right; border-right: 1px solid #ccc; } table.facts th, table.facts td { vertical-align: top; padding: 0 10px 10px 10px; } +table.facts th.horizontal { border-right: none; padding: 0 10px 5px 10px; } table.facts-long th { text-align: right; font-weight: bold; } table.facts-long th, table.facts-long td { vertical-align: top; } -table.facts-fixed-width th, table.facts-fixed-width td { width: 130px; } +table.facts-l { margin-right: 50px; } table.mini th { border: none; padding: 0 2px 2px 2px; text-align: right; } table.mini td { border: none; padding: 0 2px 2px 2px; } @@ -144,9 +166,9 @@ p.warning, div.form-popup-warn { background: #ff8; } div.form-popup-info { background: #8f8; } div.form-popup-help { text-align: left !important; background: #f8f8f8; border: 1px solid #ccc; } div.form-popup-warn, div.form-popup-info, div.form-popup-help { margin: 20px; padding: 15px; border-radius: 10px; -moz-border-radius: 10px; text-align: center; max-width: 600px; z-index: 1; display: none; position: fixed; min-width: 500px; } -div.form-popup-warn span, div.form-popup-info span, div.form-popup-help span, div.form-popup-rate-options span { color: black; font-weight: bold; cursor: pointer; } +div.form-popup-warn span, div.form-popup-info span, div.form-popup-help span, div.form-popup-options span { color: black; font-weight: bold; cursor: pointer; } -div.form-popup-rate-options { +div.form-popup-options { z-index: 1; position: absolute; right: 35px; padding: 15px; background: white; border-left: 1px solid #ccc; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; border-radius: 10px 0 0 10px; -moz-border-radius: 10px 0 0 10px; } @@ -163,8 +185,6 @@ p.status-error, p.warning { margin: 20px; padding: 15px; border-radius: 10px; -m .chart-medium { width: 600px; height: 200px; } .chart-large { width: 800px; height: 300px; } -.chart-legend { float: left; } - .micro-highlight { min-width: 120px; font-size: 100%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 0 0; color: #888; border-radius: 10px; -moz-border-radius: 10px; } .micro-highlight a { font-weight: normal !important; color: #888 !important; } .micro-highlight strong { font-size: 120%; color: #444; font-weight: normal; } @@ -205,7 +225,7 @@ table.form table.subform th, table.form table.subform td { padding: 0; } .multifield-sub { border: 1px solid #ddd; background: #f8f8f8; padding: 10px; border-radius: 10px; -moz-border-radius: 10px; float: left; margin-bottom: 10px; } -label.radio { padding: 5px; border: 1px solid #eee; cursor: pointer; border-radius: 5px; -moz-border-radius: 5px; } +label.radio, label.checkbox { padding: 5px; border: 1px solid #eee; cursor: pointer; border-radius: 5px; -moz-border-radius: 5px; } table.two-col-layout { width: 100%; } table.two-col-layout > tbody > tr > td { width: 50%; vertical-align: top; } @@ -232,6 +252,9 @@ h3 { padding: 0 0 2px 0; margin: 1em 0 1em 0; font-size: 1em; border-bottom: 1px acronym { background: #add; color: #222; padding: 2px 4px; border-radius: 2px; -moz-border-radius: 2px; border: none; cursor: default; } +acronym.policy { background: none; border: 2px solid #add; padding: 0 2px; } +table.list td acronym a { display: inline; width: auto; } + acronym.warning { background: #daa; } .status-red acronym, .status-yellow acronym, .status-green acronym, .status-grey acronym, small acronym, acronym.normal { background: none; color: inherit; padding: 0; border-bottom: 1px dotted; cursor: default; } diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/doc/stats.html b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/doc/stats.html index f95f528..fd977f7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/doc/stats.html +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/doc/stats.html @@ -110,14 +110,37 @@ set an age and an increment for the samples you want. The end of the range returned will always correspond to the present.

+ +

+ Different types of data take different query parameters to + return samples, as in the following table. You can specify more + than one set of parameters if the resource you are requesting + can generate more than one type of sample (for example, queues + can return message rates and queue lengths). +

+ +
Virtual Host<%= fmt_string(upstream.vhost) %>
URI <%= fmt_string(upstream.value.uri) %> <% if (link.local_channel) { %> - <%= fmt_rate(link.local_channel.message_stats, 'confirm') %> + <%= fmt_detail_rate(link.local_channel.message_stats, 'confirm') %> <% } %> <%= link.timestamp %> /api/nodes/name An individual node in the RabbitMQ cluster. Add - "?memory=true" to get memory statistics. + "?memory=true" to get memory statistics, and "?binary=true" + to get a breakdown of binary memory use (may be expensive if + there are many small binaries in the system).
/api/channels/channel Details about an individual channel.
X/api/consumersA list of all consumers.
X/api/consumers/vhostA list of all consumers in a given virtual host.
X X /api/exchanges/vhost/nameAn individual exchange. To PUT an exchange, you will need a body looking something like this: -
{"type":"direct","auto_delete":false,"durable":true,"internal":false,"arguments":[]}
- The type key is mandatory; other keys are optional.
+ An individual exchange. To PUT an exchange, you will need a body looking something like this: +
{"type":"direct","auto_delete":false,"durable":true,"internal":false,"arguments":{}}
+ The type key is mandatory; other keys are optional. +

+ When DELETEing an exchange you can add the query string + parameter if-unused=true. This prevents the + delete from succeeding if the exchange is bound to a queue + or as a source to another exchange. +

+
X
X /api/queues/vhost/nameAn individual queue. To PUT a queue, you will need a body looking something like this: -
{"auto_delete":false,"durable":true,"arguments":[],"node":"rabbit@smacmullen"}
- All keys are optional.
+ An individual queue. To PUT a queue, you will need a body looking something like this: +
{"auto_delete":false,"durable":true,"arguments":{},"node":"rabbit@smacmullen"}
+ All keys are optional. +

+ When DELETEing a queue you can add the query string + parameters if-empty=true and / + or if-unused=true. These prevent the delete + from succeeding if the queue contains messages, or has + consumers, respectively. +

+
X
+ + + + + + + + + + + + + + + + +
Messages sent and receivedmsg_rates_age / msg_rates_incr
Bytes sent and receiveddata_rates_age / data_rates_incr +
Queue lengthslengths_age / lengths_incr
Node statistics (e.g. file descriptors, disk space free)node_stats_age / node_stats_incr
+

- Use msg_rates_age - and msg_rates_incr to return samples for messages - sent and received, data_rates_age - and data_rates_incr to return samples for bytes - sent and received, and lengths_age - and lengths_incr to return samples for queue - lengths. For example, + For example, appending ?lengths_age=3600&lengths_incr=60 will return the last hour's data on queue lengths, with a sample for every minute. @@ -215,13 +238,15 @@

Detailed message stats objects

- In addition, queues, exchanges and channels will return a + In addition, queues, exchanges and channels can return a breakdown of message stats for each of their neighbours (i.e. adjacent objects in the chain: channel -> exchange -> - queue -> channel). + queue -> channel). This will only happen if + the rates_mode configuration item has been switched + to detailed from its default of basic.

- As this possibly constitutes a large quantity of data, it is + As this possibly constitutes a large quantity of data, it is also only returned when querying a single channel, queue or exchange rather than a list. Note also that the default sample retention policy means that these detailed message stats do not retain @@ -283,6 +308,12 @@ set_cluster_name. + + contexts + + A list of web application contexts in the cluster. + + erlang_full_version @@ -360,15 +391,22 @@ - statistics_db_node + rates_mode - Name of the cluster node hosting the management statistics database. + 'none', 'basic' or 'detailed'. + + + + statistics_db_event_queue + + Number of outstanding statistics events yet to be processed + by the database. - statistics_level + statistics_db_node - Whether the node is running fine or coarse statistics. + Name of the cluster node hosting the management statistics database. @@ -392,12 +430,32 @@ List of all SASL authentication mechanisms installed on the node. + + cluster_links + + A list of the other nodes in the cluster. For each node, + there are details of the TCP connection used to connect to + it and statistics on data that has been transferred. + + + + config_files + + List of config files read by the node. + + contexts List of all HTTP listeners on the node. + + db_dir + + Location of the persistent storage used by the node. + + disk_free @@ -416,6 +474,12 @@ Point at which the disk alarm will go off. + + enabled_plugins + + List of plugins which are both explicitly enabled and running. + + exchange_types @@ -434,6 +498,88 @@ Used file descriptors. + + io_read_avg_time + + Average wall time (milliseconds) for each disk read operation in + the last statistics interval. + + + + io_read_bytes + + Total number of bytes read from disk by the persister. + + + + io_read_count + + Total number of read operations by the persister. + + + + io_reopen_count + + Total number of times the persister has needed to recycle + file handles between queues. In an ideal world this number + will be zero; if the number is large, performance might be + improved by increasing the number of file handles available + to RabbitMQ. + + + + io_seek_avg_time + + Average wall time (milliseconds) for each seek operation in + the last statistics interval. + + + + + io_seek_count + + Total number of seek operations by the persister. + + + + io_sync_avg_time + + Average wall time (milliseconds) for each fsync() operation in + the last statistics interval. + + + + + io_sync_count + + Total number of fsync() operations by the persister. + + + + io_write_avg_time + + Average wall time (milliseconds) for each disk write operation in + the last statistics interval. + + + + io_write_bytes + + Total number of bytes written to disk by the persister. + + + + io_write_count + + Total number of write operations by the persister. + + + + log_file + + Location of main log file. + + mem_used @@ -452,12 +598,48 @@ Point at which the memory alarm will go off. + + mnesia_disk_tx_count + + Number of Mnesia transactions which have been performed that + required writes to disk. (e.g. creating a durable + queue). Only transactions which originated on this node are + included. + + + + mnesia_ram_tx_count + + Number of Mnesia transactions which have been performed that + did not require writes to disk. (e.g. creating a transient + queue). Only transactions which originated on this node are + included. + + + + msg_store_read_count + + Number of messages which have been read from the message store. + + + + msg_store_write_count + + Number of messages which have been written to the message store. + + name Node name. + + net_ticktime + + Current kernel net_ticktime setting for the node. + + os_pid @@ -489,6 +671,33 @@ Number of cores detected and usable by Erlang. + + queue_index_journal_write_count + + Number of records written to the queue index journal. Each + record represents a message being published to a queue, + being delivered from a queue, and being acknowledged in a + queue. + + + + queue_index_read_count + + Number of records read from the queue index. + + + + queue_index_write_count + + Number of records written to the queue index. + + + + rates_mode + + 'none', 'basic' or 'detailed'. + + run_queue @@ -503,21 +712,21 @@ - sockets_total + sasl_log_file - File descriptors available for use as sockets. + Location of sasl log file. - sockets_used + sockets_total - File descriptors used as sockets. + File descriptors available for use as sockets. - statistics_level + sockets_used - 'fine' or 'coarse'. + File descriptors used as sockets. @@ -548,6 +757,15 @@ if ?memory=true is appended to the URL. + + binary + + Detailed breakdown of the owners of binary memory. Only + appears if ?binary=true is appended to the + URL. Note that this can be an expensive query if there are + many small binaries in the system. + +

/api/connections

diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/img/bg-binary.png b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/img/bg-binary.png new file mode 100644 index 0000000..dc136bf Binary files /dev/null and b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/img/bg-binary.png differ diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/charts.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/charts.js index ed28295..0ec370f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/charts.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/charts.js @@ -1,10 +1,250 @@ +// +// Formatting side +// + +function message_rates(id, stats) { + var items = [['Publish', 'publish'], ['Confirm', 'confirm'], + ['Publish (In)', 'publish_in'], + ['Publish (Out)', 'publish_out'], + ['Deliver', 'deliver'], + ['Redelivered', 'redeliver'], + ['Acknowledge', 'ack'], + ['Get', 'get'], ['Deliver (noack)', 'deliver_no_ack'], + ['Get (noack)', 'get_no_ack'], + ['Return', 'return_unroutable'], + ['Disk read', 'disk_reads'], + ['Disk write', 'disk_writes']]; + return rates_chart_or_text(id, stats, items, fmt_rate, fmt_rate_axis, true, 'Message rates', 'message-rates'); +} + +function queue_lengths(id, stats) { + var items = [['Ready', 'messages_ready'], + ['Unacked', 'messages_unacknowledged'], + ['Total', 'messages']]; + return rates_chart_or_text(id, stats, items, fmt_num_thousands, fmt_plain_axis, false, 'Queued messages', 'queued-messages'); +} + +function data_rates(id, stats) { + var items = [['From client', 'recv_oct'], ['To client', 'send_oct']]; + return rates_chart_or_text(id, stats, items, fmt_rate_bytes, fmt_rate_bytes_axis, true, 'Data rates'); +} + +function rates_chart_or_text(id, stats, items, fmt, axis_fmt, chart_rates, + heading, heading_help) { + var prefix = chart_h3(id, heading, heading_help); + + return prefix + rates_chart_or_text_no_heading( + id, id, stats, items, fmt, axis_fmt, chart_rates); +} + +function rates_chart_or_text_no_heading(type_id, id, stats, items, + fmt, axis_fmt, chart_rates) { + var mode = get_pref('rate-mode-' + type_id); + var range = get_pref('chart-range'); + var res; + if (keys(stats).length > 0) { + if (mode == 'chart') { + res = rates_chart( + type_id, id, items, stats, fmt, axis_fmt, 'full', chart_rates); + } + else { + res = rates_text(items, stats, mode, fmt, chart_rates); + } + if (res == "") res = '

Waiting for data...

'; + } + else { + res = '

Currently idle

'; + } + return res; +} + +function chart_h3(id, heading, heading_help) { + var mode = get_pref('rate-mode-' + id); + var range = get_pref('chart-range'); + return '

' + heading + + ' (' + prefix_title(mode, range) + + ')' + (heading_help == undefined ? '' : + ' ') + + '

'; +} + +function prefix_title(mode, range) { + var desc = CHART_PERIODS[range]; + if (mode == 'chart') { + return 'chart: ' + desc.toLowerCase(); + } + else if (mode == 'curr') { + return 'current value'; + } + else { + return 'moving average: ' + desc.toLowerCase(); + } +} + +function node_stat_count(used_key, limit_key, stats, thresholds) { + var used = stats[used_key]; + var limit = stats[limit_key]; + if (typeof used == 'number') { + return node_stat(used_key, 'Used', limit_key, 'available', stats, + fmt_plain, fmt_plain_axis, + fmt_color(used / limit, thresholds)); + } else { + return used; + } +} + +function node_stat_count_bar(used_key, limit_key, stats, thresholds) { + var used = stats[used_key]; + var limit = stats[limit_key]; + if (typeof used == 'number') { + return node_stat_bar(used_key, limit_key, 'available', stats, + fmt_plain_axis, + fmt_color(used / limit, thresholds)); + } else { + return used; + } +} + +function node_stat(used_key, used_name, limit_key, suffix, stats, fmt, + axis_fmt, colour, help, invert) { + if (get_pref('rate-mode-node-stats') == 'chart') { + var items = [[used_name, used_key], ['Limit', limit_key]]; + add_fake_limit_details(used_key, limit_key, stats); + return rates_chart('node-stats', 'node-stats-' + used_key, items, stats, + fmt, axis_fmt, 'node', false); + } else { + return node_stat_bar(used_key, limit_key, suffix, stats, axis_fmt, + colour, help, invert); + } +} + +function add_fake_limit_details(used_key, limit_key, stats) { + var source = stats[used_key + '_details'].samples; + var limit = stats[limit_key]; + var dest = []; + for (var i in source) { + dest[i] = {sample: limit, timestamp: source[i].timestamp}; + } + stats[limit_key + '_details'] = {samples: dest}; +} + +function node_stat_bar(used_key, limit_key, suffix, stats, fmt, colour, + help, invert) { + var used = stats[used_key]; + var limit = stats[limit_key]; + var width = 120; + + var res = ''; + var other_colour = colour; + var ratio = invert ? (limit / used) : (used / limit); + if (ratio > 1) { + ratio = 1 / ratio; + inverted = true; + colour += '-dark'; + } + else { + other_colour += '-dark'; + } + var offset = Math.round(width * (1 - ratio)); + + res += '
'; + res += '
'; + res += fmt(used); + if (help != null) { + res += ' '; + } + res += '
'; // status-bar-main + res += '' + fmt(limit) + ' ' + suffix + ''; + res += '
'; // status-bar + + return res; +} + +function node_stats_prefs() { + return chart_h3('node-stats', 'Node statistics'); +} + +function rates_chart(type_id, id, items, stats, fmt, axis_fmt, type, + chart_rates) { + function show(key) { + return get_pref('chart-line-' + id + key) === 'true'; + } + + var size = get_pref('chart-size-' + type_id); + var legend = []; + chart_data[id] = {}; + chart_data[id]['data'] = {}; + chart_data[id]['fmt'] = axis_fmt; + var ix = 0; + for (var i in items) { + var name = items[i][0]; + var key = items[i][1]; + var key_details = key + '_details'; + if (key_details in stats) { + if (show(key)) { + chart_data[id]['data'][name] = stats[key_details]; + chart_data[id]['data'][name].ix = ix; + } + var value = chart_rates ? pick_rate(fmt, stats, key) : + pick_abs(fmt, stats, key); + legend.push({name: name, + key: key, + value: value, + show: show(key)}); + ix++; + } + } + var html = '
'; + html += ''; + for (var i = 0; i < legend.length; i++) { + if (i % 3 == 0 && i < legend.length - 1) { + html += '
'; + } + + html += '' + } + html += '
'; + html += legend[i].name + ''; + html += '
' + legend[i].value + '
'; + return legend.length > 0 ? html : ''; +} + +function rates_text(items, stats, mode, fmt, chart_rates) { + var res = ''; + for (var i in items) { + var name = items[i][0]; + var key = items[i][1]; + var key_details = key + '_details'; + if (key_details in stats) { + var details = stats[key_details]; + res += '
' + name + ''; + res += chart_rates ? pick_rate(fmt, stats, key, mode) : + pick_abs(fmt, stats, key, mode); + res += '
'; + } + } + return res == '' ? '' : '
' + res + '
'; +} + +// +// Rendering side +// + function render_charts() { $('.chart').map(function() { render_chart($(this)); }); } -var chart_colors = ['#edc240', '#afd8f8', '#cb4b4b', '#4da74d', '#9440ed', '#666666', '#aaaaaa']; +var chart_colors = {full: ['#edc240', '#afd8f8', '#cb4b4b', '#4da74d', '#9440ed', '#666666', '#aaaaaa'], + node: ['#6ae26a', '#e24545']}; var chart_chrome = { series: { lines: { show: true } }, @@ -14,17 +254,24 @@ var chart_chrome = { legend: { show: false } }; +function chart_fill(mode, i) { + return mode =='node' && i == 0; +} + function render_chart(div) { var id = div.attr('id').substring('chart-'.length); var rate_mode = div.hasClass('chart-rates'); - var out_data = []; - var i = 0; var data = chart_data[id]['data']; var fmt = chart_data[id]['fmt']; + + var mode = div.hasClass('chart-full') ? 'full': 'node'; + var colors = chart_colors[mode]; + for (var name in data) { var series = data[name]; var samples = series.samples; + var i = series.ix; var d = []; for (var j = 1; j < samples.length; j++) { var x = samples[j].timestamp; @@ -43,8 +290,8 @@ function render_chart(div) { } d.push([x, y]); } - out_data.push({data: d, color: chart_colors[i], shadowSize: 0}); - i++; + out_data.push({data: d, color: colors[i], shadowSize: 0, + lines: {show: true, fill: chart_fill(mode, i)}}); } chart_data[id] = {}; @@ -67,6 +314,6 @@ function update_rate_options(sammy) { var id = sammy.params['id']; store_pref('rate-mode-' + id, sammy.params['mode']); store_pref('chart-size-' + id, sammy.params['size']); - store_pref('chart-range-' + id, sammy.params['range']); + store_pref('chart-range', sammy.params['range']); partial_update(); } diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/dispatcher.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/dispatcher.js index db47881..4c2d670 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/dispatcher.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/dispatcher.js @@ -26,9 +26,10 @@ dispatcher_add(function(sammy) { sammy.get('#/nodes/:name', function() { var name = esc(this.params['name']); - render({'node': '/nodes/' + name}, + render({'node': {path: '/nodes/' + name, + options: {ranges: ['node-stats']}}}, 'node', ''); - }); + }); path('#/connections', {'connections': {path: '/connections', options: {sort:true}}}, @@ -214,7 +215,7 @@ dispatcher_add(function(sammy) { }); sammy.put('#/logout', function() { - document.cookie = 'auth=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + clear_pref('auth'); location.reload(); }); @@ -224,4 +225,7 @@ dispatcher_add(function(sammy) { sammy.put('#/rate-options', function() { update_rate_options(this); }); + sammy.put('#/column-options', function() { + update_column_options(this); + }); }); diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/formatters.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/formatters.js index e4e3923..8f7f813 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/formatters.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/formatters.js @@ -12,11 +12,6 @@ function fmt_string(str, unknown) { return fmt_escape_html("" + str); } -function fmt_bytes(bytes) { - if (bytes == undefined) return UNKNOWN_REPR; - return fmt_si_prefix(bytes, bytes, 1024, false) + 'B'; -} - function fmt_si_prefix(num0, max0, thousand, allow_fractions) { if (num == 0) return 0; @@ -34,25 +29,39 @@ function fmt_si_prefix(num0, max0, thousand, allow_fractions) { num.toFixed(0)) + powers[power]; } -function fmt_memory(memory, key) { - return '
' + - fmt_bytes(memory[key]); -} - -function fmt_boolean(b) { - if (b == undefined) return UNKNOWN_REPR; +function fmt_boolean(b, unknown) { + if (unknown == undefined) unknown = UNKNOWN_REPR; + if (b == undefined) return unknown; return b ? "●" : "○"; } function fmt_date(d) { + var res = fmt_date0(d); + return res[0] + ' ' + res[1]; +} + +function fmt_date_mini(d) { + var res = fmt_date0(d); + return res[1] + '' + res[0] + ''; +} + +function fmt_date0(d) { function f(i) { return i < 10 ? "0" + i : i; } - return d.getFullYear() + "-" + f(d.getMonth() + 1) + "-" + - f(d.getDate()) + " " + f(d.getHours()) + ":" + f(d.getMinutes()) + - ":" + f(d.getSeconds()); + return [d.getFullYear() + "-" + f(d.getMonth() + 1) + "-" + + f(d.getDate()), f(d.getHours()) + ":" + f(d.getMinutes()) + + ":" + f(d.getSeconds())]; +} + +function fmt_timestamp(ts) { + return fmt_date(new Date(ts)); +} + +function fmt_timestamp_mini(ts) { + return fmt_date_mini(new Date(ts)); } function fmt_time(t, suffix) { @@ -64,24 +73,39 @@ function fmt_millis(millis) { return Math.round(millis / 1000) + "s"; } -function fmt_parameters(obj) { - return fmt_table_short(args_to_params(obj)); +function fmt_features(obj) { + return fmt_table_short(args_to_features(obj)); } -function fmt_parameters_short(obj) { +function fmt_policy_short(obj) { + if (obj.policy != undefined && obj.policy != '') { + return '' + + fmt_escape_html(obj.policy) + ' '; + } else { + return ''; + } +} + +function fmt_features_short(obj) { var res = ''; - var params = args_to_params(obj); + var features = args_to_features(obj); + + if (obj.owner_pid_details != undefined) { + res += '' + + link_conn(obj.owner_pid_details.name, "Excl") + ' '; + } for (var k in ALL_ARGS) { - if (params[k] != undefined) { - res += '' + ALL_ARGS[k].short + ' '; } } - if (params.arguments) { - res += 'Args'; + if (features.arguments) { + res += 'Args '; } return res; } @@ -98,7 +122,7 @@ function short_chan(name) { return (match != null && match.length == 3) ? match[1] + match[2] : name; } -function args_to_params(obj) { +function args_to_features(obj) { var res = {}; for (var k in obj.arguments) { if (k in KNOWN_ARGS) { @@ -177,14 +201,6 @@ function fmt_color(r, thresholds) { return 'green'; } -function fmt_deliver_rate(obj, show_redeliver) { - var res = fmt_rate(obj, 'deliver_get'); - if (show_redeliver) { - res += '' + fmt_rate(obj, 'redeliver') + ''; - } - return res; -} - function fmt_rate_num(num) { if (num == undefined) return UNKNOWN_REPR; else if (num < 1) return num.toFixed(2); @@ -207,87 +223,76 @@ function fmt_percent(num) { } } -function fmt_rate(obj, name, mode) { - var raw = fmt_rate0(obj, name, mode, fmt_rate_num); - return raw == '' ? '' : (raw + '/s'); +function pick_rate(fmt, obj, name, mode) { + if (obj == undefined || obj[name] == undefined || + obj[name + '_details'] == undefined) return ''; + var details = obj[name + '_details']; + return fmt(mode == 'avg' ? details.avg_rate : details.rate); } -function fmt_rate_bytes(obj, name, mode) { - var raw = fmt_rate0(obj, name, mode, fmt_bytes); - return raw == '' ? '' : (raw + '/s' + - '(' + fmt_bytes(obj[name]) + ' total)'); +function pick_abs(fmt, obj, name, mode) { + if (obj == undefined || obj[name] == undefined || + obj[name + '_details'] == undefined) return ''; + var details = obj[name + '_details']; + return fmt(mode == 'avg' ? details.avg : obj[name]); } -function fmt_rate_large(obj, name, mode) { - return '' + fmt_rate0(obj, name, mode, fmt_rate_num) + - 'msg/s'; +function fmt_detail_rate(obj, name, mode) { + return pick_rate(fmt_rate, obj, name, mode); } -function fmt_rate_bytes_large(obj, name, mode) { - return '' + fmt_rate0(obj, name, mode, fmt_bytes) + '/s' + - '(' + fmt_bytes(obj[name]) + ' total)'; +function fmt_detail_rate_bytes(obj, name, mode) { + return pick_rate(fmt_rate_bytes, obj, name, mode); } -function fmt_rate0(obj, name, mode, fmt) { - if (obj == undefined || obj[name] == undefined || - obj[name + '_details'] == undefined) return ''; - var details = obj[name + '_details']; - return fmt(mode == 'avg' ? details.avg_rate : details.rate); +// --------------------------------------------------------------------- + +// These are pluggable for charts etc + +function fmt_plain(num) { + return num; } -function fmt_msgs(obj, name, mode) { - return fmt_msgs0(obj, name, mode) + ' msg'; +function fmt_plain_axis(num, max) { + return fmt_si_prefix(num, max, 1000, true); } -function fmt_msgs_large(obj, name, mode) { - return '' + fmt_msgs0(obj, name, mode) + '' + - fmt_rate0(obj, name, mode, fmt_msgs_rate); +function fmt_rate(num) { + return fmt_rate_num(num) + '/s'; } -function fmt_msgs0(obj, name, mode) { - if (obj == undefined || obj[name] == undefined || - obj[name + '_details'] == undefined) return ''; - var details = obj[name + '_details']; - return mode == 'avg' ? fmt_rate_num(details.avg) : - fmt_num_thousands(obj[name]); +function fmt_rate_axis(num, max) { + return fmt_plain_axis(num, max) + '/s'; } -function fmt_msgs_rate(num) { - if (num > 0) return '+' + fmt_rate_num(num) + ' msg/s'; - else if (num < 0) return '-' + fmt_rate_num(-num) + ' msg/s'; - else return ' '; +function fmt_bytes(bytes) { + if (bytes == undefined) return UNKNOWN_REPR; + return fmt_si_prefix(bytes, bytes, 1024, false) + 'B'; } -function fmt_rate_axis(num, max) { - return fmt_si_prefix(num, max, 1000, true) + '/s'; +function fmt_bytes_axis(num, max) { + num = parseInt(num); + return fmt_bytes(isNaN(num) ? 0 : num); } -function fmt_msgs_axis(num, max) { - return fmt_si_prefix(num, max, 1000, true); +function fmt_rate_bytes(num) { + return fmt_bytes(num) + '/s'; } function fmt_rate_bytes_axis(num, max) { - num = parseInt(num); - return fmt_bytes(isNaN(num) ? 0 : num) + '/s'; + return fmt_bytes_axis(num, max) + '/s'; } -function is_stat_empty(obj, name) { - if (obj == undefined - || obj[name] == undefined - || obj[name + '_details'] == undefined - || obj[name + '_details'].rate < 0.00001) return true; - return false; +function fmt_ms(num) { + return fmt_rate_num(num) + 'ms'; } -function is_col_empty(objects, name, accessor) { - if (accessor == undefined) accessor = function(o) {return o.message_stats;}; - for (var i = 0; i < objects.length; i++) { - var object = objects[i]; - if (!is_stat_empty(accessor(object), name)) { - return false; - } - } - return true; +// --------------------------------------------------------------------- + +function fmt_maybe_vhost(name) { + return vhosts_interesting ? + ' in virtual host ' + fmt_escape_html(name) + '' + : ''; } function fmt_exchange(name) { @@ -319,15 +324,6 @@ function fmt_download_filename(host) { (now.getMonth() + 1) + "-" + now.getDate() + ".json"; } -function fmt_fd_used(used, total) { - if (used == 'install_handle_from_sysinternals') { - return '

handle.exe missing ' + total + ' available

'; - } - else { - return used; - } -} - function fmt_table_short(table) { return '' + fmt_table_body(table, ':') + '
'; } @@ -340,8 +336,8 @@ function fmt_table_long(table) { function fmt_table_body(table, x) { var res = ''; for (k in table) { - res += '' + k + x + '' + fmt_amqp_value(table[k]) + - ''; + res += '' + fmt_escape_html(k) + x + '' + + '' + fmt_amqp_value(table[k]) + ''; } return res; } @@ -369,7 +365,7 @@ function fmt_amqp_value(val) { function fmt_table_flat(table) { var res = []; for (k in table) { - res.push(k + ': ' + fmt_amqp_value_flat(table[k])); + res.push(fmt_escape_html(k) + ': ' + fmt_amqp_value_flat(table[k])); } return res.join(', '); } @@ -405,6 +401,30 @@ function fmt_uptime(u) { return min + 'm ' + sec + 's'; } +function fmt_plugins_small(node) { + if (node.applications === undefined) return ''; + var plugins = []; + for (var i = 0; i < node.applications.length; i++) { + var application = node.applications[i]; + if (jQuery.inArray(application.name, node.enabled_plugins) != -1 ) { + plugins.push(application.name); + } + } + return '' + + plugins.length + ''; +} + +function get_plugins_list(node) { + var result = []; + for (var i = 0; i < node.applications.length; i++) { + var application = node.applications[i]; + if (jQuery.inArray(application.name, node.enabled_plugins) != -1 ) { + result.push(application); + } + } + return result; +} + function fmt_rabbit_version(applications) { for (var i in applications) { if (applications[i].name == 'rabbit') { @@ -453,7 +473,7 @@ function fmt_node(node_host) { var both = node_host.split('@'); var node = both.slice(0, 1); var host = both.slice(1); - return '' + node + '@' + host; + return node == 'rabbit' ? host : (node + '@' + host); } function fmt_object_state(obj) { @@ -481,6 +501,16 @@ function fmt_object_state(obj) { colour = 'yellow'; explanation = 'Publishing rate recently restricted by server.'; } + else if (obj.state == 'down') { + colour = 'red'; + explanation = 'The queue is located on a cluster node or nodes that ' + + 'are down.'; + } + else if (obj.state == 'crashed') { + colour = 'red'; + explanation = 'The queue has crashed repeatedly and been unable to ' + + 'restart.'; + } return fmt_state(colour, text, explanation); } @@ -498,44 +528,6 @@ function fmt_state(colour, text, explanation) { return '
' + key; } -function fmt_resource_bar(used_label, limit_label, ratio, colour, help) { - var width = 120; - - var res = ''; - var other_colour = colour; - if (ratio > 1) { - ratio = 1 / ratio; - inverted = true; - colour += '-dark'; - } - else { - other_colour += '-dark'; - } - var offset = Math.round(width * (1 - ratio)); - - res += '
'; - res += '
'; - res += used_label; - if (help != null) { - res += ' '; - } - res += '
'; // status-bar-main - if (limit_label != null) { - res += '' + limit_label + ''; - } - res += '
'; // status-bar - return res; -} - -function fmt_resource_bar_count(used, total, thresholds) { - if (typeof used == 'number') { - return fmt_resource_bar(used, total + ' available', used / total, - fmt_color(used / total, thresholds)); - } else { - return used; - } -} - function fmt_shortened_uri(uri) { if (typeof uri == 'object') { var res = ''; @@ -572,8 +564,9 @@ function fmt_client_name(properties) { function fmt_trunc(str, max_length) { return str.length > max_length ? - ('' + - str.substring(0, max_length) + '...') : str; + ('' + + fmt_escape_html(str.substring(0, max_length)) + '...') : + fmt_escape_html(str); } function alt_rows(i, args) { @@ -662,115 +655,6 @@ function fmt_highlight_filter(text) { } } -function message_rates(id, stats) { - var items = [['Publish', 'publish'], ['Confirm', 'confirm'], - ['Publish (In)', 'publish_in'], - ['Publish (Out)', 'publish_out'], - ['Deliver', 'deliver'], - ['Redelivered', 'redeliver'], - ['Acknowledge', 'ack'], - ['Get', 'get'], ['Deliver (noack)', 'deliver_no_ack'], - ['Get (noack)', 'get_no_ack'], - ['Return', 'return_unroutable']]; - return rates_chart_or_text(id, stats, items, fmt_rate, fmt_rate_large, fmt_rate_axis, true, 'Message rates', 'message-rates'); -} - -function queue_lengths(id, stats) { - var items = [['Ready', 'messages_ready'], - ['Unacknowledged', 'messages_unacknowledged'], - ['Total', 'messages']]; - return rates_chart_or_text(id, stats, items, fmt_msgs, fmt_msgs_large, fmt_msgs_axis, false, 'Queued messages', 'queued-messages'); -} - -function data_rates(id, stats) { - var items = [['From client', 'recv_oct'], ['To client', 'send_oct']]; - return rates_chart_or_text(id, stats, items, fmt_rate_bytes, fmt_rate_bytes_large, fmt_rate_bytes_axis, true, 'Data rates'); -} - -function rates_chart_or_text(id, stats, items, chart_fmt, text_fmt, axis_fmt, chart_rates, - heading, heading_help) { - var mode = get_pref('rate-mode-' + id); - var range = get_pref('chart-range-' + id); - var prefix = '

' + heading + - ' (' + prefix_title(mode, range) + ')' + - (heading_help == undefined ? '' : - ' ') + - '

'; - var res; - - if (keys(stats).length > 0) { - if (mode == 'chart') { - res = rates_chart(id, items, stats, chart_fmt, axis_fmt, chart_rates); - } - else { - res = rates_text(items, stats, mode, text_fmt); - } - if (res == "") res = '

Waiting for data...

'; - } - else { - res = '

Currently idle

'; - } - return prefix + '
' + res + '
'; -} - -function prefix_title(mode, range) { - var desc = CHART_PERIODS[range]; - if (mode == 'chart') { - return 'chart: ' + desc.toLowerCase(); - } - else if (mode == 'curr') { - return 'current value'; - } - else { - return 'moving average: ' + desc.toLowerCase(); - } -} - -function rates_chart(id, items, stats, rate_fmt, axis_fmt, chart_rates) { - var size = get_pref('chart-size-' + id); - var show = []; - chart_data[id] = {}; - chart_data[id]['data'] = {}; - chart_data[id]['fmt'] = axis_fmt; - for (var i in items) { - var name = items[i][0]; - var key = items[i][1]; - var key_details = key + '_details'; - if (key_details in stats) { - chart_data[id]['data'][name] = stats[key_details]; - show.push([name, rate_fmt(stats, key)]); - } - } - var html = '
'; - html += ''; - for (var i = 0; i < show.length; i++) { - html += '' - } - html += '
' + show[i][0] + ''; - html += '
' + show[i][1] + '
'; - return show.length > 0 ? html : ''; -} - -function rates_text(items, stats, mode, rate_fmt) { - var res = ''; - for (var i in items) { - var name = items[i][0]; - var key = items[i][1]; - var key_details = key + '_details'; - if (key_details in stats) { - var details = stats[key_details]; - res += '
' + name; - res += rate_fmt(stats, key, mode); - res += '
'; - } - } - return res == '' ? '' : '
' + res + '
'; -} - function filter_ui(items) { current_truncate = (current_truncate == null) ? parseInt(get_pref('truncate')) : current_truncate; @@ -850,12 +734,36 @@ function fmt_sort(display, sort) { return '' + prefix + display + ''; } +function group_count(mode, group, bools) { + var count = 0; + for (var i = 0; i < bools.length; i++) { + if (bools[i]) count++; + } + + var options = COLUMNS[mode][group]; + for (var i = 0; i < options.length; i++) { + var column = options[i][0]; + if (show_column(mode, column)) count++; + } + return count; +} + +function group_heading(mode, group, bools) { + var count = group_count(mode, group, bools); + if (count == 0) { + return ''; + } + else { + return '' + group + ''; + } +} + function fmt_permissions(obj, permissions, lookup, show, warning) { var res = []; for (var i in permissions) { var permission = permissions[i]; if (permission[lookup] == obj.name) { - res.push(permission[show]); + res.push(fmt_escape_html(permission[show])); } } return res.length == 0 ? warning : res.join(', '); @@ -866,12 +774,18 @@ var radio_id = 0; function fmt_radio(name, text, value, current) { radio_id++; return ''; } +function fmt_checkbox(name, text, current) { + return ''; +} + function properties_size(obj) { var count = 0; for (k in obj) { diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/global.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/global.js index 0111aed..f2de0d9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/global.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/global.js @@ -18,8 +18,10 @@ var KNOWN_ARGS = {'alternate-exchange': {'short': 'AE', 'type': 'string' 'x-message-ttl': {'short': 'TTL', 'type': 'int'}, 'x-expires': {'short': 'Exp', 'type': 'int'}, 'x-max-length': {'short': 'Lim', 'type': 'int'}, + 'x-max-length-bytes': {'short': 'Lim B', 'type': 'int'}, 'x-dead-letter-exchange': {'short': 'DLX', 'type': 'string'}, - 'x-dead-letter-routing-key': {'short': 'DLK', 'type': 'string'}}; + 'x-dead-letter-routing-key': {'short': 'DLK', 'type': 'string'}, + 'x-max-priority': {'short': 'Pri', 'type': 'int'}}; // Things that are like arguments that we format the same way in listings. var IMPLICIT_ARGS = {'durable': {'short': 'D', 'type': 'boolean'}, @@ -28,8 +30,8 @@ var IMPLICIT_ARGS = {'durable': {'short': 'D', 'type': 'boolean'}, // Both the above var ALL_ARGS = {}; -for (var k in KNOWN_ARGS) ALL_ARGS[k] = KNOWN_ARGS[k]; for (var k in IMPLICIT_ARGS) ALL_ARGS[k] = IMPLICIT_ARGS[k]; +for (var k in KNOWN_ARGS) ALL_ARGS[k] = KNOWN_ARGS[k]; var NAVIGATION = {'Overview': ['#/', "management"], 'Connections': ['#/connections', "management"], @@ -49,6 +51,84 @@ var CHART_PERIODS = {'60|5': 'Last minute', '28800|600': 'Last eight hours', '86400|1800': 'Last day'}; +var COLUMNS = + {'exchanges' : + {'Overview': [['type', 'Type', true], + ['features', 'Features (with policy)', true], + ['features_no_policy', 'Features (no policy)', false], + ['policy', 'Policy', false]], + 'Message rates': [['rate-in', 'rate in', true], + ['rate-out', 'rate out', true]]}, + 'queues' : + {'Overview': [['features', 'Features (with policy)', true], + ['features_no_policy', 'Features (no policy)', false], + ['policy', 'Policy', false], + ['consumers', 'Consumer count', false], + ['consumer_utilisation', 'Consumer utilisation', false], + ['state', 'State', true]], + 'Messages': [['msgs-ready', 'Ready', true], + ['msgs-unacked', 'Unacknowledged', true], + ['msgs-ram', 'In memory', false], + ['msgs-persistent', 'Persistent', false], + ['msgs-total', 'Total', true]], + 'Message bytes': [['msg-bytes-ready', 'Ready', false], + ['msg-bytes-unacked', 'Unacknowledged', false], + ['msg-bytes-ram', 'In memory', false], + ['msg-bytes-persistent', 'Persistent', false], + ['msg-bytes-total', 'Total', false]], + 'Message rates': [['rate-incoming', 'incoming', true], + ['rate-deliver', 'deliver / get', true], + ['rate-redeliver', 'redelivered', false], + ['rate-ack', 'ack', true]]}, + 'channels' : + {'Overview': [['user', 'User name', true], + ['mode', 'Mode', true], + ['state', 'State', true]], + 'Details': [['msgs-unconfirmed', 'Unconfirmed', true], + ['prefetch', 'Prefetch', true], + ['msgs-unacked', 'Unacked', true]], + 'Transactions': [['msgs-uncommitted', 'Msgs uncommitted', false], + ['acks-uncommitted', 'Acks uncommitted', false]], + 'Message rates': [['rate-publish', 'publish', true], + ['rate-confirm', 'confirm', true], + ['rate-return', 'return (mandatory)', false], + ['rate-deliver', 'deliver / get', true], + ['rate-redeliver', 'redelivered', false], + ['rate-ack', 'ack', true]]}, + 'connections': + {'Overview': [['user', 'User name', true], + ['state', 'State', true]], + 'Details': [['ssl', 'SSL / TLS', true], + ['ssl_info', 'SSL Details', false], + ['protocol', 'Protocol', true], + ['channels', 'Channels', true], + ['channel_max', 'Channel max', false], + ['frame_max', 'Frame max', false], + ['auth_mechanism', 'Auth mechanism', false], + ['client', 'Client', false]], + 'Network': [['from_client', 'From client', true], + ['to_client', 'To client', true], + ['heartbeat', 'Heartbeat', false], + ['connected_at', 'Connected at', false]]}, + + 'vhosts': + {'Messages': [['msgs-ready', 'Ready', true], + ['msgs-unacked', 'Unacknowledged', true], + ['msgs-total', 'Total', true]], + 'Network': [['from_client', 'From client', true], + ['to_client', 'To client', true]], + 'Message rates': [['rate-publish', 'publish', true], + ['rate-deliver', 'deliver / get', true]]}, + 'overview': + {'Statistics': [['file_descriptors', 'File descriptors', true], + ['socket_descriptors', 'Socket descriptors', true], + ['erlang_processes', 'Erlang processes', true], + ['memory', 'Memory', true], + ['disk_space', 'Disk space', true]], + 'General': [['uptime', 'Uptime', false], + ['rates_mode', 'Rates mode', false], + ['info', 'Info', true]]}}; + /////////////////////////////////////////////////////////////////////////// // // // Mostly constant, typically get set once at startup (or rarely anyway) // @@ -56,8 +136,9 @@ var CHART_PERIODS = {'60|5': 'Last minute', /////////////////////////////////////////////////////////////////////////// // All these are to do with hiding UI elements if -var statistics_level; // ...there are no fine stats +var rates_mode; // ...there are no fine stats var user_administrator; // ...user is not an admin +var user_policymaker; // ...user is not a policymaker var user_monitor; // ...user cannot monitor var nodes_interesting; // ...we are not in a cluster var vhosts_interesting; // ...there is only one vhost @@ -82,19 +163,20 @@ var user; // Set up the above vars function setup_global_vars() { var overview = JSON.parse(sync_get('/overview')); - statistics_level = overview.statistics_level; + rates_mode = overview.rates_mode; user_tags = expand_user_tags(user.tags.split(",")); user_administrator = jQuery.inArray("administrator", user_tags) != -1; + user_policymaker = jQuery.inArray("policymaker", user_tags) != -1; user_monitor = jQuery.inArray("monitoring", user_tags) != -1; replace_content('login-details', - '

User: ' + user.name + '

' + - '

Cluster: ' + overview.cluster_name + ' ' + + '

User: ' + fmt_escape_html(user.name) + '

' + + '

Cluster: ' + fmt_escape_html(overview.cluster_name) + ' ' + (user_administrator ? '(change)' : '') + '

' + - '

RabbitMQ ' + overview.rabbitmq_version + + '

RabbitMQ ' + fmt_escape_html(overview.rabbitmq_version) + ', Erlang ' + - overview.erlang_version + '

'); + fmt_escape_html(overview.erlang_full_version) + '">Erlang ' + + fmt_escape_html(overview.erlang_version) + '

'); nodes_interesting = false; rabbit_versions_interesting = false; if (user_monitor) { @@ -145,6 +227,10 @@ var current_template; // Which JSON requests do we need to populate it var current_reqs; +// And which of those have yet to return (so we can cancel them when +// changing current_template). +var outstanding_reqs = []; + // Which tab is highlighted var current_highlight; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/help.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/help.js index b2e3b13..f50c19a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/help.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/help.js @@ -17,6 +17,9 @@ HELP = { 'queue-max-length': 'How many (ready) messages a queue can contain before it starts to drop them from its head.
(Sets the "x-max-length" argument.)', + 'queue-max-length-bytes': + 'Total body size for ready messages a queue can contain before it starts to drop them from its head.
(Sets the "x-max-length-bytes" argument.)', + 'queue-auto-delete': 'If yes, the queue will delete itself after at least one consumer has connected, and then all consumers have disconnected.', @@ -26,11 +29,17 @@ HELP = { 'queue-dead-letter-routing-key': 'Optional replacement routing key to use when a message is dead-lettered. If this is not set, the message\'s original routing key will be used.
(Sets the "x-dead-letter-routing-key" argument.)', - 'queue-memory-resident': - '

Number of messages in the queue which are held in memory. These messages may also be on disc (if they are persistent).

There may be a limit imposed in order to manage total memory use. If the number of memory-resident messages in the queue exceeds the limit some messages will be paged out.

', + 'queue-max-priority': + 'Maximum number of priority levels for the queue to support; if not set, the queue will not support message priorities.
(Sets the "x-max-priority" argument.)', + + 'queue-messages': + '

Message counts.

Note that "in memory" and "persistent" are not mutually exclusive; persistent messages can be in memory as well as on disc, and transient messages can be paged out if memory is tight. Non-durable queues will consider all messages to be transient.

', - 'queue-persistent': - 'Number of messages in the queue which are persistent. These messages will be on disc but may also be available in memory. Note that if a message is published as persistent but routed to a transient queue it is not considered persistent by that queue, so transient queues will always report 0 persistent messages.', + 'queue-message-body-bytes': + '

The sum total of the sizes of the message bodies in this queue. This only counts message bodies; it does not include message properties (including headers) or metadata used by the queue.

Note that "in memory" and "persistent" are not mutually exclusive; persistent messages can be in memory as well as on disc, and transient messages can be paged out if memory is tight. Non-durable queues will consider all messages to be transient.

If a message is routed to multiple queues on publication, its body will be stored only once (in memory and on disk) and shared between queues. The value shown here does not take account of this effect.

', + + 'queue-process-memory': + 'Total memory used by this queue process. This does not include in-memory message bodies (which may be shared between queues and will appear in the global "binaries" memory) but does include everything else.', 'queue-consumer-utilisation': 'Fraction of the time that the queue is able to immediately deliver messages to consumers. If this number is less than 100% you may be able to deliver messages faster if: \ @@ -188,9 +197,7 @@ HELP = {
Number of messages for which the server is waiting for acknowledgement.
\
Total
\
The total of these two numbers.
\ - \ - Note that the rate of change of total queued messages does \ - not include messages removed due to queue deletion.', + ', 'message-rates': 'Only rates for which some activity is taking place will be shown.\ @@ -213,7 +220,16 @@ HELP = {
Rate at which messages with the \'redelivered\' flag set are being delivered. Note that these messages will also be counted in one of the delivery rates above.
\
Return
\
Rate at which basic.return is sent to publishers for unroutable messages published with the \'mandatory\' flag set.
\ - ', +
Disk read
\ +
Rate at which queues read messages from disk.
\ +
Disk write
\ +
Rate at which queues write messages to disk.
\ + \ +

\ + Note that the last two items are originate in queues rather than \ + channels; they may therefore be slightly out of sync with other \ + statistics.\ +

', 'disk-monitoring-no-watermark' : 'There is no disk space low watermark set. RabbitMQ will not take any action to avoid running out of disk space.', @@ -221,51 +237,21 @@ HELP = { 'memory-use' : '

Note that the memory details shown here are only updated on request - they could be too expensive to calculate every few seconds on a busy server.

Read more on memory use.

', - 'policy-definitions' : '
\ -
ha-mode
\ -
\ - One of all, exactly\ - or nodes.\ -
\ -
ha-params
\ -
\ - Absent if ha-mode is all, a number\ + 'binary-use' : '

Binary accounting is not exact; binaries are shared between processes (and thus the same binary might be counted in more than one section), and the VM does not allow us to track binaries that are not associated with processes (so some binary use might not appear at all).

', + + 'policy-ha-mode' : 'One of all (mirror to all nodes in the cluster), exactly (mirror to a set number of nodes) or nodes (mirror to an explicit list of nodes). If you choose one of the latter two, you must also set ha-params.', + + 'policy-ha-params' : 'Absent if ha-mode is all, a number\ if ha-mode is exactly, or a list\ - of strings if ha-mode is nodes.\ -
\ -
ha-sync-mode
\ -
\ - One of manual or automatic.\ -
\ -
alternate-exchange
\ -
\ - The name of an alternate exchange.\ -
\ -
dead-letter-exchange
\ -
\ - The name of a dead letter exchange.\ -
\ -
dead-letter-routing-key
\ -
\ - Key to use when dead-lettering.\ -
\ -
message-ttl
\ -
\ - Per-queue message TTL, in milliseconds.\ -
\ -
expires
\ -
\ - Queue TTL, in milliseconds.\ -
\ -
max-length
\ -
\ - Maximum queue length, in messages.\ -
\ -
federation-upstream-set
\ -
\ - A string; only if the federation plugin is enabled.\ -
\ -
', + of strings if ha-mode is nodes.', + + 'policy-ha-sync-mode' : 'One of manual or automatic.', + + 'policy-federation-upstream-set' : + 'A string; only if the federation plugin is enabled. Chooses the name of a set of upstreams to use with federation, or "all" to use all upstreams. Incompatible with federation-upstream.', + + 'policy-federation-upstream' : + 'A string; only if the federation plugin is enabled. Chooses a specific upstream set to use for federation. Incompatible with federation-upstream-set.', 'handle-exe' : 'In order to monitor the number of file descriptors in use on Windows, RabbitMQ needs the handle.exe command line tool from Microsoft. Download it and place it in the path (e.g. in C:\Windows).', @@ -274,6 +260,68 @@ HELP = { and regular expressions are matched in a case-insensitive manner.

\ (Regular expression reference)', + 'plugins' : + 'Note that only plugins which are both explicitly enabled and running are shown here.', + + 'io-operations': + 'Rate of I/O operations. Only operations performed by the message \ + persister are shown here (e.g. metadata changes in Mnesia or writes \ + to the log files are not shown).\ +
\ +
Read
\ +
Rate at which data is read from the disk.
\ +
Write
\ +
Rate at which data is written to the disk.
\ +
Seek
\ +
Rate at which the broker switches position while reading or \ + writing to disk.
\ +
Sync
\ +
Rate at which the broker invokes fsync() to ensure \ + data is flushed to disk.
\ +
Reopen
\ +
Rate at which the broker recycles file handles in order to support \ + more queues than it has file handles. If this operation is occurring \ + frequently you may get a performance boost from increasing the number \ + of file handles available.
\ +
', + + 'mnesia-transactions': + 'Rate at which Mnesia transactions are initiated on this node (this node \ + will also take part in Mnesia transactions initiated on other nodes).\ +
\ +
RAM only
\ +
Rate at which RAM-only transactions take place (e.g. creation / \ + deletion of transient queues).
\ +
Disk
\ +
Rate at which disk (and RAM) transactions take place (.e.g \ + creation / deletion of durable queues).
\ +
', + + 'persister-operations-msg': + 'Rate at which per-message persister operations take place on this node. See \ + here \ + for more information on the persister. \ +
\ +
QI Journal
\ +
Rate at which message information (publishes, deliveries and \ + acknowledgements) is written to queue index journals.
\ +
Store Read
\ +
Rate at which messages are read from the message store.
\ +
Store Write
\ +
Rate at which messages are written to the message store.
\ +
', + + 'persister-operations-bulk': + 'Rate at which whole-file persister operations take place on this node. See \ + here \ + for more information on the persister. \ +
\ +
QI Read
\ +
Rate at which queue index segment files are read.
\ +
QI Write
\ +
Rate at which queue index segment files are written.
\ +
', + 'foo': 'foo' // No comma. }; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/main.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/main.js index 3091d90..8118f62 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/main.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/main.js @@ -16,16 +16,16 @@ function dispatcher() { } } -function set_auth_cookie(userinfo) { +function set_auth_pref(userinfo) { var b64 = b64_encode_utf8(userinfo); - document.cookie = 'auth=' + encodeURIComponent(b64); + store_pref('auth', encodeURIComponent(b64)); } function login_route () { var userpass = '' + this.params['username'] + ':' + this.params['password'], location = window.location.href, hash = window.location.hash; - set_auth_cookie(decodeURIComponent(userpass)); + set_auth_pref(decodeURIComponent(userpass)); location = location.substr(0, location.length - hash.length); window.location.replace(location); // because we change url, we don't need to hit check_login as @@ -38,13 +38,13 @@ function start_app_login() { this.put('#/login', function() { username = this.params['username']; password = this.params['password']; - set_auth_cookie(username + ':' + password); + set_auth_pref(username + ':' + password); check_login(); }); this.get('#/login/:username/:password', login_route) }); app.run(); - if (get_cookie('auth') != '') { + if (get_pref('auth') != null) { check_login(); } } @@ -52,7 +52,7 @@ function start_app_login() { function check_login() { user = JSON.parse(sync_get('/whoami')); if (user == false) { - document.cookie = 'auth=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + clear_pref('auth'); replace_content('login-status', '

Login failed

'); } else { @@ -189,9 +189,9 @@ function reset_timer() { function update_manual(div, query) { var path; var template; - if (query == 'memory') { - path = current_reqs['node'] + '?memory=true'; - template = 'memory'; + if (query == 'memory' || query == 'binary') { + path = current_reqs['node']['path'] + '?' + query + '=true'; + template = query; } var data = JSON.parse(sync_get(path)); @@ -202,6 +202,10 @@ function update_manual(div, query) { function render(reqs, template, highlight) { current_template = template; current_reqs = reqs; + for (var i in outstanding_reqs) { + outstanding_reqs[i].abort(); + } + outstanding_reqs = []; current_highlight = highlight; update(); } @@ -368,7 +372,6 @@ function y_position() { function with_update(fun) { with_reqs(apply_state(current_reqs), [], function(json) { - json.statistics_level = statistics_level; var html = format(current_template, json); fun(html); update_status('ok'); @@ -400,7 +403,7 @@ function apply_state(reqs) { if (options['ranges'] != undefined) { for (i in options['ranges']) { var type = options['ranges'][i]; - var range = get_pref('chart-range-' + type).split('|'); + var range = get_pref('chart-range').split('|'); var prefix; if (type.substring(0, 8) == 'lengths-') { prefix = 'lengths'; @@ -411,6 +414,9 @@ function apply_state(reqs) { else if (type.substring(0, 11) == 'data-rates-') { prefix = 'data_rates'; } + else if (type == 'node-stats') { + prefix = 'node_stats'; + } qs.push(prefix + '_age=' + parseInt(range[0])); qs.push(prefix + '_incr=' + parseInt(range[1])); } @@ -457,7 +463,7 @@ function postprocess() { return confirm("Are you sure? This object cannot be recovered " + "after deletion."); }); - $('div.section h2, div.section-hidden h2').click(function() { + $('div.section h2, div.section-hidden h2').die().live('click', function() { toggle_visibility($(this)); }); $('label').map(function() { @@ -473,7 +479,7 @@ function postprocess() { $('#download-definitions').click(function() { var path = 'api/definitions?download=' + esc($('#download-filename').val()) + - '&auth=' + get_cookie('auth'); + '&auth=' + get_pref('auth'); window.location = path; setTimeout('app.run()'); return false; @@ -501,31 +507,46 @@ function postprocess() { } } }); - setup_visibility(); $('.help').die().live('click', function() { help($(this).attr('id')) }); - $('.rate-options').die().live('click', function() { + $('.popup-options-link').die().live('click', function() { var remove = $('.popup-owner').length == 1 && $('.popup-owner').get(0) == $(this).get(0); $('.popup-owner').removeClass('popup-owner'); if (remove) { - $('.form-popup-rate-options').fadeOut(200, function() { + $('.form-popup-options').fadeOut(200, function() { $(this).remove(); }); } else { $(this).addClass('popup-owner'); - show_popup('rate-options', format('rate-options', {span: $(this)}), + var template = $(this).attr('type') + '-options'; + show_popup('options', format(template, {span: $(this)}), 'fade'); } }); + $('.rate-visibility-option').die().live('click', function() { + var k = $(this).attr('data-pref'); + var show = get_pref(k) !== 'true'; + store_pref(k, '' + show); + partial_update(); + }); $('input, select').live('focus', function() { update_counter = 0; // If there's interaction, reset the counter. }); $('.tag-link').click(function() { $('#tags').val($(this).attr('tag')); }); + $('.argument-link').click(function() { + var field = $(this).attr('field'); + var row = $('#' + field).find('.mf').last(); + var key = row.find('input').first(); + var type = row.find('select').last(); + key.val($(this).attr('key')); + type.val($(this).attr('type')); + update_multifields(); + }); $('form.auto-submit select, form.auto-submit input').live('click', function(){ $(this).parents('form').submit(); }); @@ -539,6 +560,7 @@ function postprocess() { } function postprocess_partial() { + setup_visibility(); $('.sort').click(function() { var sort = $(this).attr('sort'); if (current_sort == sort) { @@ -570,7 +592,8 @@ function update_multifield(multifield, dict) { var largest_id = 0; var empty_found = false; var name = multifield.attr('id'); - $('#' + name + ' *[name$="_mftype"]').each(function(index) { + var type_inputs = $('#' + name + ' *[name$="_mftype"]'); + type_inputs.each(function(index) { var re = new RegExp(name + '_([0-9]*)_mftype'); var match = $(this).attr('name').match(re); if (!match) return; @@ -591,10 +614,12 @@ function update_multifield(multifield, dict) { var key = dict ? $('#' + prefix + '_mfkey').val() : ''; var value = input.val(); if (key == '' && value == '') { - if (empty_found) { - $(this).parent().remove(); + if (index == type_inputs.length - 1) { + empty_found = true; + } + else { + $(this).parents('.mf').first().remove(); } - empty_found = true; } } else { @@ -610,13 +635,13 @@ function update_multifield(multifield, dict) { multifield_input(prefix, 'type', t); if (dict) { - multifield.append(' <% } %> +<% if (show_column('exchanges', 'type')) { %> - - -<% if (statistics_level == 'fine') { %> +<% } %> +<% if (show_column('exchanges', 'features')) { %> + +<% } %> +<% if (show_column('exchanges', 'features_no_policy')) { %> + +<% } %> +<% if (show_column('exchanges', 'policy')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> +<% if (show_column('exchanges', 'rate-in')) { %> +<% } %> +<% if (show_column('exchanges', 'rate-out')) { %> <% } %> +<% } %> + @@ -32,11 +46,29 @@ <% } %> - - -<% if (statistics_level == 'fine') { %> - - +<% if (show_column('exchanges', 'features')) { %> + +<% } %> +<% if (show_column('exchanges', 'features_no_policy')) { %> + +<% } %> +<% if (show_column('exchanges', 'policy')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> +<% if (show_column('exchanges', 'rate-in')) { %> + +<% } %> +<% if (show_column('exchanges', 'rate-out')) { %> + +<% } %> <% } %> <% } %> @@ -112,14 +144,18 @@ - - - -
' + + multifield.append('
' + multifield_input(prefix, 'key', 'text') + ' = ' + val_type + '
'); } else { - multifield.append('
' + val_type + '
'); + multifield.append('
' + val_type + '
'); } } } @@ -696,6 +721,11 @@ function setup_visibility() { } if (show) { $(this).addClass('section-visible'); + // Workaround for... something. Although div.hider is + // display:block anyway, not explicitly setting this + // prevents the first slideToggle() from animating + // successfully; instead the element just vanishes. + $(this).find('.hider').attr('style', 'display:block;'); } else { $(this).addClass('section-invisible'); @@ -835,7 +865,7 @@ function update_status(status) { } function auth_header() { - return "Basic " + decodeURIComponent(get_cookie('auth')); + return "Basic " + decodeURIComponent(get_pref('auth')); } function with_req(method, path, body, fun) { @@ -845,12 +875,17 @@ function with_req(method, path, body, fun) { req.setRequestHeader('authorization', auth_header()); req.onreadystatechange = function () { if (req.readyState == 4) { + var ix = jQuery.inArray(req, outstanding_reqs); + if (ix != -1) { + outstanding_reqs.splice(ix, 1); + } if (check_bad_response(req, true)) { last_successful_connect = new Date(); fun(req); } } }; + outstanding_reqs.push(req); req.send(body); } @@ -966,10 +1001,7 @@ function fill_path_template(template, params) { } function params_magic(params) { - return check_password( - add_known_arguments( - maybe_remove_fields( - collapse_multifields(params)))); + return check_password(maybe_remove_fields(collapse_multifields(params))); } function collapse_multifields(params0) { @@ -1043,25 +1075,6 @@ function collapse_multifields(params0) { return params; } -function add_known_arguments(params) { - for (var k in KNOWN_ARGS) { - var v = params[k]; - if (v != undefined && v != '') { - var type = KNOWN_ARGS[k].type; - if (type == 'int') { - v = parseInt(v); - if (isNaN(v)) { - throw(k + " must be an integer."); - } - } - params.arguments[k] = v; - } - delete params[k]; - } - - return params; -} - function check_password(params) { if (params['password'] != undefined) { if (params['password'] == '') { @@ -1139,6 +1152,20 @@ function put_policy(sammy, mandatory_keys, num_keys, bool_keys) { if (sync_put(sammy, '/policies/:vhost/:name')) update(); } +function update_column_options(sammy) { + var mode = sammy.params['mode']; + for (var group in COLUMNS[mode]) { + var options = COLUMNS[mode][group]; + for (var i = 0; i < options.length; i++) { + var key = options[i][0]; + var value = sammy.params[mode + '-' + key] != undefined; + store_pref('column-' + mode + '-' + key, value); + } + } + + partial_update(); +} + function debug(str) { $('

' + str + '

').appendTo('#debug'); } diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/prefs.js b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/prefs.js index c0f6edc..d0cccb2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/prefs.js +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/prefs.js @@ -1,19 +1,45 @@ -// TODO It would be nice to use DOM storage. When that's available. +// TODO strip out all this cookie nonsense when we drop support for MSIE 7. + +function local_storage_available() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } +} function store_pref(k, v) { - var d = parse_cookie(); - d[short_key(k)] = v; - store_cookie(d); + if (local_storage_available()) { + window.localStorage['rabbitmq.' + k] = v; + } + else { + var d = parse_cookie(); + d[short_key(k)] = v; + store_cookie(d); + } } function clear_pref(k) { - var d = parse_cookie(); - delete d[short_key(k)]; - store_cookie(d); + if (local_storage_available()) { + window.localStorage.removeItem('rabbitmq.' + k); + } + else { + var d = parse_cookie(); + delete d[short_key(k)]; + store_cookie(d); + } + } function get_pref(k) { - var r = parse_cookie()[short_key(k)]; + var r; + if (local_storage_available()) { + r = window.localStorage['rabbitmq.' + k]; + } + else { + r = parse_cookie()[short_key(k)]; + + } return r == undefined ? default_pref(k) : r; } @@ -21,16 +47,38 @@ function section_pref(template, name) { return 'visible|' + template + '|' + name; } +function show_column(mode, column) { + return get_pref('column-' + mode + '-' + column) == 'true'; +} + // --------------------------------------------------------------------------- function default_pref(k) { - if (k.substring(0, 12) == 'chart-range-') return '60|5'; if (k.substring(0, 11) == 'chart-size-') return 'small'; if (k.substring(0, 10) == 'rate-mode-') return 'chart'; + if (k.substring(0, 11) == 'chart-line-') return 'true'; if (k == 'truncate') return '100'; + if (k == 'chart-range') return '60|5'; + if (k.substring(0, 7) == 'column-') + return default_column_pref(k.substring(7)); return null; } +function default_column_pref(key0) { + var ix = key0.indexOf('-'); + var mode = key0.substring(0, ix); + var key = key0.substring(ix + 1); + for (var group in COLUMNS[mode]) { + var options = COLUMNS[mode][group]; + for (var i = 0; i < options.length; i++) { + if (options[i][0] == key) { + return '' + options[i][2]; + } + } + } + return 'false'; +} + // --------------------------------------------------------------------------- function parse_cookie() { diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/binary.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/binary.ejs new file mode 100644 index 0000000..c3e13c7 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/binary.ejs @@ -0,0 +1,53 @@ +<% + if (binary == "not_available") { +%> +

+ Binary statistics not available. +

+<% } else { %> +<% + var sections = {'queue_procs' : ['queue', 'Queues'], + 'queue_slave_procs' : ['queue', 'Queues (slaves)'], + 'connection_readers' : ['conn', 'Connection readers'], + 'connection_writers' : ['conn', 'Connection writers'], + 'connection_channels' : ['conn', 'Connection channels'], + 'connection_other' : ['conn', 'Connections (other)'], + 'msg_index' : ['table', 'Message store index'], + 'mgmt_db' : ['table', 'Management database'], + 'plugins' : ['proc', 'Plugins'], + 'other' : ['system', 'Other binary references']}; + var total_out = []; +%> +<%= format('memory-bar', {sections: sections, memory: binary, total_out: total_out}) %> +  +
+<% +var key = [[{name: 'Queues', colour: 'queue', + keys: [['queue_procs', 'queues'], + ['queue_slave_procs', 'slaves']]}], + + [{name: 'Connections', colour: 'conn', + keys: [['connection_readers', 'readers'], + ['connection_writers', 'writers'], + ['connection_channels', 'channels'], + ['connection_other', 'other']]}], + + [{name: 'Tables', colour: 'table', + keys: [['msg_index', 'message store index'], + ['mgmt_db', 'management database']]}], + + [{name: 'Processes', colour: 'proc', + keys: [['plugins', 'plugins']]}, + {name: 'System', colour: 'system', + keys: [['other', 'other']]}]]; +%> +<%= format('memory-table', {key: key, memory: binary}) %> +
+ +
+ Last updated: <%= fmt_date(new Date()) %>.
+ Total referenced binaries at last update: <%= fmt_bytes(total_out[0]) %> + +
+ +<% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channel.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channel.ejs index e7843d1..1e9d18e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channel.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channel.ejs @@ -1,15 +1,14 @@ -

Channel: <%= fmt_escape_html(channel.name) %>

+

Channel: <%= fmt_escape_html(channel.name) %><%= fmt_maybe_vhost(channel.vhost) %>

Overview

-
-<% if (statistics_level == 'fine') { %> +
+<% if (rates_mode != 'none') { %> <%= message_rates('msg-rates-ch', channel.message_stats) %> <% } %> -

Details

- +
@@ -19,12 +18,6 @@ -<% } %> -<% if (vhosts_interesting) { %> - - - - <% } %> @@ -36,7 +29,7 @@
Connection <%= link_conn(channel.connection_details.name) %>Node <%= fmt_node(channel.node) %>
Virtual host<%= fmt_string(channel.vhost) %>
Username
- +
@@ -69,7 +62,6 @@
State <%= fmt_object_state(channel) %><%= channel.acks_uncommitted %>
-
@@ -81,7 +73,7 @@
-<% if (statistics_level == 'fine') { %> +<% if (rates_mode == 'detailed') { %>

Message rates breakdown

diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channels-list.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channels-list.ejs index fa944a8..591a7c0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channels-list.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/channels-list.ejs @@ -1,22 +1,18 @@ <% if (channels.length > 0) { %> -<% - var col_return_unroutable = !is_col_empty(channels, 'return_unroutable'); - var col_redeliver = !is_col_empty(channels, 'redeliver'); - var ratesWidth = col_return_unroutable ? 5 : 4; -%> <% if (mode == 'standalone') { %> - - + <%= group_heading('channels', 'Overview', [true, vhosts_interesting, nodes_interesting]) %> <% } else { %> - - + <%= group_heading('channels', 'Overview', [true]) %> <% } %> -<% if (statistics_level == 'fine') { %> - + <%= group_heading('channels', 'Details', []) %> + <%= group_heading('channels', 'Transactions', []) %> +<% if (rates_mode != 'none') { %> + <%= group_heading('channels', 'Message rates', []) %> <% } %> + <% if (mode == 'standalone') { %> @@ -27,47 +23,96 @@ <% if (vhosts_interesting) { %> <% } %> +<% if (show_column('channels', 'user')) { %> +<% } %> +<% if (show_column('channels', 'mode')) { %> +<% } %> +<% if (show_column('channels', 'state')) { %> + +<% } %> +<% if (show_column('channels', 'msgs-unconfirmed')) { %> + +<% } %> +<% if (show_column('channels', 'prefetch')) { %> +<% } %> +<% if (show_column('channels', 'msgs-unacked')) { %> - - -<% if (statistics_level == 'fine') { %> +<% } %> +<% if (show_column('channels', 'msgs-uncommitted')) { %> + +<% } %> +<% if (show_column('channels', 'acks-uncommitted')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> +<% if (show_column('channels', 'rate-publish')) { %> +<% } %> +<% if (show_column('channels', 'rate-confirm')) { %> - +<% } %> +<% if (show_column('channels', 'rate-return')) { %> + +<% } %> +<% if (show_column('channels', 'rate-deliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-redeliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-ack')) { %> - <% if (col_return_unroutable) { %> - - <% } %> +<% } %> <% } %> <% } else { %> +<% if (show_column('channels', 'user')) { %> + +<% } %> +<% if (show_column('channels', 'mode')) { %> +<% } %> +<% if (show_column('channels', 'state')) { %> + +<% } %> +<% if (show_column('channels', 'msgs-unconfirmed')) { %> + +<% } %> +<% if (show_column('channels', 'prefetch')) { %> +<% } %> +<% if (show_column('channels', 'msgs-unacked')) { %> - - -<% if (statistics_level == 'fine') { %> +<% } %> +<% if (show_column('channels', 'msgs-uncommitted')) { %> + +<% } %> +<% if (show_column('channels', 'acks-uncommitted')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> +<% if (show_column('channels', 'rate-publish')) { %> +<% } %> +<% if (show_column('channels', 'rate-confirm')) { %> - +<% } %> +<% if (show_column('channels', 'rate-return')) { %> + +<% } %> +<% if (show_column('channels', 'rate-deliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-redeliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-ack')) { %> - <% if (col_return_unroutable) { %> - - <% } %> +<% } %> <% } %> <% } %> @@ -84,18 +129,24 @@ <% if (mode == 'standalone' && nodes_interesting) { %> <% } %> -<% if (mode == 'standalone') { %> -<% if (vhosts_interesting) { %> +<% if (mode == 'standalone' && vhosts_interesting) { %> <% } %> +<% if (show_column('channels', 'user')) { %> <% } %> - +<% } %> +<% if (show_column('channels', 'state')) { %> + +<% } %> +<% if (show_column('channels', 'msgs-unconfirmed')) { %> + +<% } %> +<% if (show_column('channels', 'prefetch')) { %> +<% } %> +<% if (show_column('channels', 'msgs-unacked')) { %> - - -<% if (statistics_level == 'fine') { %> - - - - - <% if (col_return_unroutable) { %> - - <% } %> +<% } %> +<% if (show_column('channels', 'msgs-uncommitted')) { %> + +<% } %> +<% if (show_column('channels', 'acks-uncommitted')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> +<% if (show_column('channels', 'rate-publish')) { %> + +<% } %> +<% if (show_column('channels', 'rate-confirm')) { %> + +<% } %> +<% if (show_column('channels', 'rate-return')) { %> + +<% } %> +<% if (show_column('channels', 'rate-deliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-redeliver')) { %> + +<% } %> +<% if (show_column('channels', 'rate-ack')) { %> + +<% } %> <% } %> <% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/columns-options.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/columns-options.ejs new file mode 100644 index 0000000..2059460 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/columns-options.ejs @@ -0,0 +1,25 @@ +<% + var mode = span.attr('for'); +%> + + + +
DetailsDetailsMessage rates+/-
<%= fmt_sort('Virtual host', 'vhost') %><%= fmt_sort('User name', 'user') %>Mode <%= fmt_sort('State', 'state') %><%= fmt_sort('Unconfirmed', 'messages_unconfirmed') %>Prefetch <%= fmt_sort('Unacked', 'messages_unacknowledged') %><%= fmt_sort('Unconfirmed', 'messages_unconfirmed') %><%= fmt_sort('State', 'state') %><%= fmt_sort('Uncommitted msgs', 'messages_uncommitted') %><%= fmt_sort('Uncommitted acks', 'acks_uncommitted') %><%= fmt_sort('publish', 'message_stats.publish_details.rate') %><%= fmt_sort('confirm', 'message_stats.confirm_details.rate') %> - <%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %> - <% if (col_redeliver) { %> - <%= fmt_sort('of which redelivered', 'message_stats.redeliver_details.rate') %> - <% } %> - <%= fmt_sort('return (mandatory)', 'message_stats.return_unroutable_details.rate') %><%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %><%= fmt_sort('redelivered', 'message_stats.redeliver_details.rate') %><%= fmt_sort('ack', 'message_stats.ack_details.rate') %><%= fmt_sort('return (mandatory)', 'message_stats.return_unroutable_details.rate') %> ChannelUser nameMode StateUnconfirmedPrefetch UnackedUnconfirmedStateUncommitted msgsUncommitted ackspublishconfirm - deliver / get - <% if (col_redeliver) { %> - of which redelivered - <% } %> - return (mandatory)deliver / getredeliveredackreturn (mandatory)
<%= fmt_node(channel.node) %><%= fmt_string(channel.vhost) %><%= fmt_string(channel.user) %> +<% if (show_column('channels', 'mode')) { %> + <%= fmt_channel_mode(channel) %> - <% if (channel.transactional) { %> - <%= channel.messages_uncommitted %>m/<%= channel.acks_uncommitted %>a - <% } %> <%= fmt_object_state(channel) %><%= channel.messages_unconfirmed %> <% if (channel.prefetch_count != 0) { %> <%= channel.prefetch_count %>
@@ -104,17 +155,35 @@ <%= channel.global_prefetch_count %> (global) <% } %>
<%= channel.messages_unacknowledged %><%= channel.messages_unconfirmed %><%= fmt_object_state(channel) %><%= fmt_rate(channel.message_stats, 'publish') %><%= fmt_rate(channel.message_stats, 'confirm') %><%= fmt_deliver_rate(channel.message_stats, col_redeliver) %><%= fmt_rate(channel.message_stats, 'ack') %><%= fmt_rate(channel.message_stats, 'return_unroutable') %><%= channel.messages_uncommitted %><%= channel.acks_uncommitted %><%= fmt_detail_rate(channel.message_stats, 'publish') %><%= fmt_detail_rate(channel.message_stats, 'confirm') %><%= fmt_detail_rate(channel.message_stats, 'return_unroutable') %><%= fmt_detail_rate(channel.message_stats, 'deliver_get') %><%= fmt_detail_rate(channel.message_stats, 'redeliver') %><%= fmt_detail_rate(channel.message_stats, 'ack') %>
+ + + +<% for (var group in COLUMNS[mode]) { + var options = COLUMNS[mode][group]; %> + + + +<% } %> + +
+

Columns for this table

+
+ <% for (var i = 0; i < options.length; i++) { %> + <%= fmt_checkbox(mode + '-' + options[i][0], options[i][1], get_pref('column-' + mode + '-' + options[i][0]) == 'true') %> + <% } %> +
+ diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connection.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connection.ejs index 0e01e81..ae1ce9b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connection.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connection.ejs @@ -1,25 +1,18 @@ -

Connection <%= fmt_string(connection.name) %>

+

Connection <%= fmt_string(connection.name) %><%= fmt_maybe_vhost(connection.vhost) %>

Overview

-
+
<%= data_rates('data-rates-conn', connection, 'Data rates') %> -

Details

- +
<% if (nodes_interesting) { %> <% } %> -<% if (vhosts_interesting) { %> - - - - -<% } %> @@ -28,6 +21,10 @@ + + + + <% if (connection.ssl) { %> @@ -51,7 +48,7 @@ - + @@ -64,7 +61,6 @@
Node <%= fmt_node(connection.node) %>
Virtual host<%= fmt_string(connection.vhost) %>
Username <%= fmt_string(connection.user) %>Protocol <%= connection.protocol %>
Connected at<%= fmt_timestamp(connection.connected_at) %>
<%= fmt_object_state(connection) %>
TimeoutHeartbeat <%= fmt_time(connection.timeout, 's') %>
<% } %> -
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connections.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connections.ejs index f60c94c..3173281 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connections.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/connections.ejs @@ -5,25 +5,61 @@ - - + <%= group_heading('connections', 'Overview', [vhosts_interesting, nodes_interesting, true]) %> + <%= group_heading('connections', 'Details', []) %> + <%= group_heading('connections', 'Network', []) %> + - - - -<% if (nodes_interesting) { %> - -<% } %> - - - - <% if (vhosts_interesting) { %> <% } %> - - + +<% if (nodes_interesting) { %> + +<% } %> +<% if (show_column('connections', 'user')) { %> + +<% } %> +<% if (show_column('connections', 'state')) { %> + +<% } %> +<% if (show_column('connections', 'ssl')) { %> + +<% } %> +<% if (show_column('connections', 'ssl_info')) { %> + +<% } %> +<% if (show_column('connections', 'protocol')) { %> + +<% } %> +<% if (show_column('connections', 'channels')) { %> + +<% } %> +<% if (show_column('connections', 'channel_max')) { %> + +<% } %> +<% if (show_column('connections', 'frame_max')) { %> + +<% } %> +<% if (show_column('connections', 'auth_mechanism')) { %> + +<% } %> +<% if (show_column('connections', 'client')) { %> + +<% } %> +<% if (show_column('connections', 'from_client')) { %> + +<% } %> +<% if (show_column('connections', 'to_client')) { %> + +<% } %> +<% if (show_column('connections', 'heartbeat')) { %> + +<% } %> +<% if (show_column('connections', 'connected_at')) { %> + +<% } %> @@ -32,26 +68,64 @@ var connection = connections[i]; %> > +<% if (vhosts_interesting) { %> + +<% } %> - - <% if (nodes_interesting) { %> <% } %> - - - - -<% if (vhosts_interesting) { %> - +<% if (show_column('connections', 'user')) { %> + <% } %> - +<% if (show_column('connections', 'state')) { %> +<% } %> +<% if (show_column('connections', 'ssl')) { %> + +<% } %> +<% if (show_column('connections', 'ssl_info')) { %> + +<% } %> +<% if (show_column('connections', 'protocol')) { %> + +<% } %> +<% if (show_column('connections', 'channels')) { %> + +<% } %> +<% if (show_column('connections', 'channel_max')) { %> + +<% } %> +<% if (show_column('connections', 'frame_max')) { %> + +<% } %> +<% if (show_column('connections', 'auth_mechanism')) { %> + +<% } %> +<% if (show_column('connections', 'client')) { %> + +<% } %> +<% if (show_column('connections', 'from_client')) { %> + +<% } %> +<% if (show_column('connections', 'to_client')) { %> + +<% } %> +<% if (show_column('connections', 'heartbeat')) { %> + +<% } %> +<% if (show_column('connections', 'connected_at')) { %> + +<% } %> <% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchange.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchange.ejs index d8d54f2..4eb2496 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchange.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchange.ejs @@ -1,13 +1,11 @@ -

Exchange: <%= fmt_exchange(exchange.name) %>

+

Exchange: <%= fmt_exchange(exchange.name) %><%= fmt_maybe_vhost(exchange.vhost) %>

Overview

-
-<% if (statistics_level == 'fine') { %> +
+<% if (rates_mode != 'none') { %> <%= message_rates('msg-rates-x', exchange.message_stats) %> <% } %> - -

Details

NetworkOverview+/-
<%= fmt_sort('Name', 'name') %><%= fmt_sort('Protocol', 'protocol') %><%= fmt_sort('Client', 'properties') %><%= fmt_sort('Node', 'node') %><%= fmt_sort('From client', 'recv_oct_details.rate') %><%= fmt_sort('To client', 'send_oct_details.rate') %><%= fmt_sort('Timeout', 'timeout') %><%= fmt_sort('Channels', 'channels') %><%= fmt_sort('Virtual host', 'vhost') %><%= fmt_sort('User name', 'user') %><%= fmt_sort('State', 'state') %><%= fmt_sort('Name', 'name') %><%= fmt_sort('Node', 'node') %><%= fmt_sort('User name', 'user') %><%= fmt_sort('State', 'state') %><%= fmt_sort('SSL / TLS', 'ssl') %>SSL Details<%= fmt_sort('Protocol', 'protocol') %><%= fmt_sort('Channels', 'channels') %><%= fmt_sort('Channel max', 'channel_max') %><%= fmt_sort('Frame max', 'frame_max') %><%= fmt_sort('Auth mechanism', 'auth_mechanism') %><%= fmt_sort('Client', 'properties') %><%= fmt_sort('From client', 'recv_oct_details.rate') %><%= fmt_sort('To client', 'send_oct_details.rate') %><%= fmt_sort('Heartbeat', 'timeout') %><%= fmt_sort('Connected at', 'connected_at') %>
<%= fmt_string(connection.vhost) %><%= link_conn(connection.name) %> - <%= connection.protocol %> - <% if (connection.ssl) { %> - SSL - <% } %> - <%= fmt_client_name(connection.client_properties) %><%= fmt_node(connection.node) %><%= fmt_rate_bytes(connection, 'recv_oct') %><%= fmt_rate_bytes(connection, 'send_oct') %><%= fmt_time(connection.timeout, 's') %><%= connection.channels %><%= fmt_string(connection.vhost) %><%= fmt_string(connection.user) %><%= fmt_string(connection.user) %><%= fmt_object_state(connection) %><%= fmt_boolean(connection.ssl, '') %> + <% if (connection.ssl) { %> + <%= connection.ssl_protocol %> + + <%= connection.ssl_key_exchange %> + <%= connection.ssl_cipher %> + <%= connection.ssl_hash %> + + <% } %> + <%= connection.protocol %><%= fmt_string(connection.channels, '') %><%= fmt_string(connection.channel_max, '') %><%= fmt_string(connection.frame_max, '') %><%= fmt_string(connection.auth_mechanism, '') %><%= fmt_client_name(connection.client_properties) %><%= fmt_detail_rate_bytes(connection, 'recv_oct') %><%= fmt_detail_rate_bytes(connection, 'send_oct') %><%= fmt_time(connection.timeout, 's') %><%= fmt_timestamp_mini(connection.connected_at) %>
@@ -15,25 +13,18 @@ - - + + -<% if (vhosts_interesting) { %> - - - - -<% } %>
<%= fmt_exchange_type(exchange.type) %>
Parameters<%= fmt_parameters(exchange) %>Features<%= fmt_features(exchange) %>
Policy <%= fmt_string(exchange.policy, '') %>
Virtual host<%= fmt_string(exchange.vhost) %>
-
-<% if (statistics_level == 'fine') { %> +<% if (rates_mode == 'detailed') { %>

Message rates breakdown

diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchanges.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchanges.ejs index 7154289..58589d5 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchanges.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/exchanges.ejs @@ -12,13 +12,27 @@
<%= fmt_sort('Virtual host', 'vhost') %><%= fmt_sort('Name', 'name') %><%= fmt_sort('Type', 'type') %><%= fmt_sort('Policy', 'policy') %>ParametersFeaturesFeatures<%= fmt_sort('Policy','policy') %><%= fmt_sort('Message rate in', 'message_stats.publish_in_details.rate') %><%= fmt_sort('Message rate out', 'message_stats.publish_out_details.rate') %>+/-
<%= link_exchange(exchange.vhost, exchange.name, exchange.arguments) %> <%= fmt_exchange_type(exchange.type) %><%= fmt_string(exchange.policy, '') %><%= fmt_parameters_short(exchange) %><%= fmt_rate(exchange.message_stats, 'publish_in') %><%= fmt_rate(exchange.message_stats, 'publish_out') %> + <%= fmt_features_short(exchange) %> + <%= fmt_policy_short(exchange) %> + + <%= fmt_features_short(exchange) %> + + <%= fmt_string(exchange.policy) %> + <%= fmt_detail_rate(exchange.message_stats, 'publish_in') %><%= fmt_detail_rate(exchange.message_stats, 'publish_out') %>
+ + + + + +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-bar.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-bar.ejs new file mode 100644 index 0000000..a6d6ab6 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-bar.ejs @@ -0,0 +1,24 @@ +
+<% + var width = 800; + + var pseudo_total = 0 + for (var section in sections) { + pseudo_total += memory[section]; + } + + total_out[0] = pseudo_total; + + for (var section in sections) { + if (memory[section] > 0) { + var section_width = Math.round(width * memory[section] / pseudo_total); +%> +
+
+<% + } + } +%> +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-table.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-table.ejs new file mode 100644 index 0000000..7f90ab8 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory-table.ejs @@ -0,0 +1,28 @@ +<% + for (var i in key) { +%> + +<% + for (var j in key[i]) { + var group = key[i][j]; +%> + + + + +<% } %> +
<%= group.name %>
+ +<% + for (var k in group.keys) { + var name = group.keys[k][0]; + var label = group.keys[k][1]; +%> + + + + +<% } %> +
<%= fmt_bytes(memory[name]) %><%= label %>
+
+<% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory.ejs index 90b2df4..1fb6aff 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/memory.ejs @@ -1,90 +1,59 @@ <% - var width = 800; if (memory == "not_available") { %>

Memory statistics not available.

<% } else { %> -
<% - var sections = {'connection_procs' : 'Connections', - 'queue_procs' : 'Queues', - 'plugins' : 'Plugins', - 'other_proc' : 'Other process memory', - 'mnesia' : 'Mnesia', - 'msg_index' : 'Message store index', - 'mgmt_db' : 'Management database', - 'other_ets' : 'Other ETS tables', - 'binary' : 'Binaries', - 'code' : 'Code', - 'atom' : 'Atoms', - 'other_system' : 'Other system'}; - for (var section in sections) { - var section_width = Math.round(width * memory[section] / memory.total); + var sections = {'queue_procs' : ['queue', 'Queues'], + 'queue_slave_procs' : ['queue', 'Queues (slaves)'], + 'binary' : ['binary', 'Binaries'], + 'connection_readers' : ['conn', 'Connection readers'], + 'connection_writers' : ['conn', 'Connection writers'], + 'connection_channels' : ['conn', 'Connection channels'], + 'connection_other' : ['conn', 'Connections (other)'], + 'mnesia' : ['table', 'Mnesia'], + 'msg_index' : ['table', 'Message store index'], + 'mgmt_db' : ['table', 'Management database'], + 'other_ets' : ['table', 'Other ETS tables'], + 'plugins' : ['proc', 'Plugins'], + 'other_proc' : ['proc', 'Other process memory'], + 'code' : ['system', 'Code'], + 'atom' : ['system', 'Atoms'], + 'other_system' : ['system', 'Other system']}; %> -
-
-<% } %> -
+<%= format('memory-bar', {sections: sections, memory: memory, total_out: []}) %>  
- - - - - - - - - - - - - - - - - -
Connections<%= fmt_memory(memory, 'connection_procs') %>
Queues<%= fmt_memory(memory, 'queue_procs') %>
Plugins<%= fmt_memory(memory, 'plugins') %>
Other process memory<%= fmt_memory(memory, 'other_proc') %>
- - - - - - - - - - - - - - - - - -
Mnesia<%= fmt_memory(memory, 'mnesia') %>
Message store index<%= fmt_memory(memory, 'msg_index') %>
Management database<%= fmt_memory(memory, 'mgmt_db') %>
Other ETS tables<%= fmt_memory(memory, 'other_ets') %>
- - - - - - - - - - - - - - - - - -
Binaries<%= fmt_memory(memory, 'binary') %>
Code<%= fmt_memory(memory, 'code') %>
Atoms<%= fmt_memory(memory, 'atom') %>
Other system<%= fmt_memory(memory, 'other_system') %>
+<% +var key = [[{name: 'Queues', colour: 'queue', + keys: [['queue_procs', 'queues'], + ['queue_slave_procs', 'slaves']]}, + {name: 'Binaries', colour: 'binary', + keys: [['binary', '']]}], + + [{name: 'Connections', colour: 'conn', + keys: [['connection_readers', 'readers'], + ['connection_writers', 'writers'], + ['connection_channels', 'channels'], + ['connection_other', 'other']]}], + + [{name: 'Tables', colour: 'table', + keys: [['mnesia', 'mnesia'], + ['msg_index', 'message store index'], + ['mgmt_db', 'management database'], + ['other_ets', 'other']]}], + + [{name: 'Processes', colour: 'proc', + keys: [['plugins', 'plugins'], + ['other_proc', 'other']]}, + {name: 'System', colour: 'system', + keys: [['code', 'code'], + ['atom', 'atoms'], + ['other_system', 'other']]}]]; +%> +<%= format('memory-table', {key: key, memory: memory}) %>
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-deliveries.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-deliveries.ejs index 448adf7..ee9e6f6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-deliveries.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-deliveries.ejs @@ -1,8 +1,5 @@

Deliveries

<% if (object && object.length > 0) { %> -<% - var col_redeliver = !is_col_empty(object, 'redeliver', function(o) {return o.stats;}); -%> <% if (mode == 'queue') { %> @@ -10,12 +7,7 @@ <% } else { %> <% } %> - + <% @@ -28,8 +20,8 @@ <% } else { %> <% } %> - - + + <% } %>
Queue - deliver / get - <% if (col_redeliver) { %> - of which redelivered - <% } %> - deliver / get ack
<%= link_queue(del.queue.vhost, del.queue.name) %><%= fmt_deliver_rate(del.stats, col_redeliver) %><%= fmt_rate(del.stats, 'ack') %><%= fmt_detail_rate(del.stats, 'deliver_get') %><%= fmt_detail_rate(del.stats, 'ack') %>
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-publishes.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-publishes.ejs index 84f9343..4ea959a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-publishes.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/msg-detail-publishes.ejs @@ -1,7 +1,6 @@

<%= label %>

<% if (object && object.length > 0) { %> <% - var col_return_unroutable = !is_col_empty(object, 'return_unroutable', function(o) {return o.stats;}); var col_confirm = mode != 'exchange-outgoing'; %> @@ -18,9 +17,6 @@ <% if (col_confirm) { %> -<% } %> -<% if (col_return_unroutable) { %> - <% } %> <% @@ -38,12 +34,9 @@ <% } else { %> <% } %> - + <% if (col_confirm) { %> - -<% } %> -<% if (col_return_unroutable) { %> - + <% } %> <% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/node.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/node.ejs index 00d7489..adde0cf 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/node.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/node.ejs @@ -1,20 +1,80 @@

Node <%= node.name %>

+
-
-

Overview

-
<% if (!node.running) { %>

Node not running

<% } else if (node.os_pid == undefined) { %>

Node statistics not available

<% } else { %> + +
+

Overview

+
+
+
publishconfirmreturn (mandatory)
<%= link_exchange(pub.exchange.vhost, pub.exchange.name) %><%= fmt_rate(pub.stats, 'publish') %><%= fmt_detail_rate(pub.stats, 'publish') %><%= fmt_rate(pub.stats, 'confirm') %><%= fmt_rate(pub.stats, 'return_unroutable') %><%= fmt_detail_rate(pub.stats, 'confirm') %>
+ + + + +<% if (rabbit_versions_interesting) { %> + + + + +<% } %> + + + + +
Uptime<%= fmt_uptime(node.uptime) %>
RabbitMQ Version<%= fmt_rabbit_version(node.applications) %>
Type + <% if (node.type == 'disc') { %> + Disc + <% } else { %> + RAM + <% } %> +
+ + <%= format('paths', {node: node}) %> +
+ +

Plugins

+ + + + + + + <% + var plugins = get_plugins_list(node); + for (var j = 0; j < plugins.length; j++) { + var application = plugins[j]; + %> + > + + + + + <% } %> +
NameVersionDescription
<%= application.name %><%= application.version %><%= application.description %>
+
+
+ +
+

Process statistics

+
+ <%= node_stats_prefs() %> @@ -22,7 +82,7 @@ Socket descriptors @@ -30,22 +90,19 @@ Erlang processes -
File descriptors -<%= fmt_resource_bar_count(fmt_fd_used(node.fd_used, node.fd_total), node.fd_total, FD_THRESHOLDS) %> +<% if (node.fd_used != 'install_handle_from_sysinternals') { %> + <%= node_stat_count('fd_used', 'fd_total', node, FD_THRESHOLDS) %> +<% } else { %> +

handle.exe missing <%= node.fd_total %> available

+ +<% } %>
-<%= fmt_resource_bar_count(node.sockets_used, node.sockets_total, FD_THRESHOLDS) %> + <%= node_stat_count('sockets_used', 'sockets_total', node, FD_THRESHOLDS) %>
-<%= fmt_resource_bar_count(node.proc_used, node.proc_total, PROCESS_THRESHOLDS) %> + <%= node_stat_count('proc_used', 'proc_total', node, PROCESS_THRESHOLDS) %>
-
Memory <% if (node.mem_limit != 'memory_monitoring_disabled') { %> - <%= fmt_resource_bar(fmt_bytes(node.mem_used), - fmt_bytes(node.mem_limit) + ' high watermark', - node.mem_used / node.mem_limit, - node.mem_alarm ? 'red' : 'green', - node.mem_alarm ? 'memory-alarm' : null) %> + <%= node_stat('mem_used', 'Used', 'mem_limit', 'high watermark', node, + fmt_bytes, fmt_bytes_axis, + node.mem_alarm ? 'red' : 'green', + node.mem_alarm ? 'memory-alarm' : null) %> <% } else { %> <%= fmt_bytes(node.mem_used) %> <% } %> @@ -57,44 +114,119 @@ <% if (node.disk_free_limit != 'disk_free_monitoring_disabled') { %> - <%= fmt_resource_bar(fmt_bytes(node.disk_free), - fmt_bytes(node.disk_free_limit) + ' low watermark', - node.disk_free_limit / node.disk_free, - node.disk_free_alarm ? 'red' : 'green', - node.disk_free_alarm ? 'disk_free-alarm' : null) %> + <%= node_stat('disk_free', 'Free', 'disk_free_limit', 'low watermark', node, + fmt_bytes, fmt_bytes_axis, + node.disk_free_alarm ? 'red' : 'green', + node.disk_free_alarm ? 'disk_free-alarm' : null, + true) %> <% } else { %> (not available) <% } %>
+
+
- - - - - -<% if (rabbit_versions_interesting) { %> - - - - +
+

Persistence statistics

+
+ <%= rates_chart_or_text('mnesia-stats-count', node, + [['RAM only', 'mnesia_ram_tx_count'], + ['Disk', 'mnesia_disk_tx_count']], + fmt_rate, fmt_rate_axis, true, 'Mnesia transactions', 'mnesia-transactions') %> + + <%= rates_chart_or_text('persister-msg-stats-count', node, + [['QI Journal', 'queue_index_journal_write_count'], + ['Store Read', 'msg_store_read_count'], + ['Store Write', 'msg_store_write_count']], + fmt_rate, fmt_rate_axis, true, 'Persistence operations (messages)', 'persister-operations-msg') %> + + <%= rates_chart_or_text('persister-bulk-stats-count', node, + [['QI Read', 'queue_index_read_count'], + ['QI Write', 'queue_index_write_count']], + fmt_rate, fmt_rate_axis, true, 'Persistence operations (bulk)', 'persister-operations-bulk') %> +
+
+ +
+

I/O statistics

+
+ <%= rates_chart_or_text('persister-io-stats-count', node, + [['Read', 'io_read_count'], + ['Write', 'io_write_count'], + ['Seek', 'io_seek_count'], + ['Sync', 'io_sync_count'], + ['Reopen', 'io_reopen_count']], + fmt_rate, fmt_rate_axis, true, 'I/O operations', 'io-operations') %> + + <%= rates_chart_or_text('persister-io-stats-bytes', node, + [['Read', 'io_read_bytes'], + ['Write', 'io_write_bytes']], + fmt_rate_bytes, fmt_rate_bytes_axis, true, 'I/O data rates') %> + + <%= rates_chart_or_text('persister-io-stats-time', node, + [['Read', 'io_read_avg_time'], + ['Write', 'io_write_avg_time'], + ['Seek', 'io_seek_avg_time'], + ['Sync', 'io_sync_avg_time']], + fmt_ms, fmt_ms, false, 'I/O average time per operation') %> +
+
+ +
+

Cluster links

+
+<% if (node.cluster_links.length > 0) { %> +
Uptime<%= fmt_uptime(node.uptime) %>
RabbitMQ Version<%= fmt_rabbit_version(node.applications) %>
+ + + + + + + + + <% + for (var i = 0; i < node.cluster_links.length; i++) { + var link = node.cluster_links[i]; + %> + > + + + + + + + <% } %> - - - - -
Remote nodeLocal addressLocal portRemote addressRemote port + <%= chart_h3('cluster-link-data-rates', 'Data rates') %> +
<%= link_node(link.name) %><%= fmt_string(link.sock_addr) %><%= fmt_string(link.sock_port) %><%= fmt_string(link.peer_addr) %><%= fmt_string(link.peer_port) %> + <%= rates_chart_or_text_no_heading( + 'cluster-link-data-rates', 'cluster-link-data-rates' + link.name, + link.stats, + [['Recv', 'recv_bytes'], + ['Send', 'send_bytes']], + fmt_rate_bytes, fmt_rate_bytes_axis, true) %> +
Type - <% if (node.type == 'disc') { %> - Disc - <% } else { %> - RAM - <% } %> -
+ +<% } else { %> +

... no cluster links ...

<% } %> +<% } %> + + + + + +<% if (node.running && node.os_pid != undefined) { %> +

Memory details

@@ -104,59 +236,25 @@
-

Applications

-
-<% if (!node.running) { %> -

Node not running

-<% } else if (node.os_pid == undefined) { %> -

Node statistics not available

-<% } else { %> - - - - - - <% - for (var j = 0; j < node.applications.length; j++) { - var application = node.applications[j]; - %> - > - - - - <% } %> -
NameVersion
- <%= application.name %> - <%= application.description %> - <%= application.version %>
-<% } %> +

Binary references

+
+

+ Warning: Calculating binary memory use can be expensive if + there are many small binaries in the system. +

+
+
-
-

Registry

-
-<% if (!node.running) { %> -

Node not running

-<% } else if (node.os_pid == undefined) { %> -

Node statistics not available

-<% } else { %> -

Exchange types

-<%= format('registry', {'list': node.exchange_types, 'node': node, 'show_enabled': false} ) %> -

Authentication mechanisms

-<%= format('registry', {'list': node.auth_mechanisms, 'node': node, 'show_enabled': true} ) %> <% } %> -
-
+ +
+<% if (node.running && node.os_pid != undefined) { %>

Advanced

-
-<% if (!node.running) { %> -

Node not running

-<% } else if (node.os_pid == undefined) { %> -

Node statistics not available

-<% } else { %> +

VM

@@ -165,8 +263,12 @@ - - + + + + + +
<%= node.os_pid %>
Statistics<%= node.statistics_level %>Rates mode<%= node.rates_mode %>
Net ticktime<%= node.net_ticktime %>s
@@ -180,6 +282,35 @@ <%= node.processors %> -<% } %> +
+ +

All applications

+ + + + + + + <% + for (var j = 0; j < node.applications.length; j++) { + var application = node.applications[j]; + %> + > + + + + + <% } %> +
NameVersionDescription
<%= application.name %><%= application.version %><%= application.description %>
+ +

Exchange types

+<%= format('registry', {'list': node.exchange_types, 'node': node, 'show_enabled': false} ) %> +

Authentication mechanisms

+<%= format('registry', {'list': node.auth_mechanisms, 'node': node, 'show_enabled': true} ) %> + +
+ +<% } %> +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/overview.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/overview.ejs index 7a886ea..86ff6ac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/overview.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/overview.ejs @@ -2,19 +2,36 @@ <% if (user_monitor) { %> <%= format('partition', {'nodes': nodes}) %> <% } %> + +
+<% if (overview.statistics_db_event_queue > 1000) { %> +

+ The management statistics database currently has a queue + of <%= overview.statistics_db_event_queue %> events to + process. If this number keeps increasing, so will the memory used by + the management plugin. + + <% if (overview.rates_mode != 'none') { %> + You may find it useful to set the rates_mode config item + to none. + <% } %> +

+<% } %> +
+

Totals

-
+
<% if (overview.statistics_db_node != 'not_running') { %> <%= queue_lengths('lengths-over', overview.queue_totals) %> -<% if (statistics_level == 'fine') { %> +<% if (rates_mode != 'none') { %> <%= message_rates('msg-rates-over', overview.message_stats) %> <% } %> <% } else { %> Totals not available <% } %> -
+<% if (overview.object_totals) { %>

Global counts

@@ -44,109 +61,150 @@
<% } %>
-
+<% } %>
<% if (user_monitor) { %>
+<% if (nodes.length == 1) { %> +

Node

+<% } else { %>

Nodes

+<% } %> +
+<% if (nodes.length == 1) { %> +

Node: <%= nodes[0].name %> (More about this node)

+<% } %> + +<% if (nodes.length > 1) { %> - - - - - +<% } %> + + <% if (show_column('overview', 'file_descriptors')) { %> + + <% } %> + <% if (show_column('overview', 'socket_descriptors')) { %> + + <% } %> + <% if (show_column('overview', 'erlang_processes')) { %> + + <% } %> + <% if (show_column('overview', 'memory')) { %> + + <% } %> + <% if (show_column('overview', 'disk_space')) { %> + + <% } %> + <% if (show_column('overview', 'uptime')) { %> - + <% } %> + <% if (show_column('overview', 'rates_mode')) { %> + + <% } %> + <% if (show_column('overview', 'info')) { %> + + <% } %> + <% for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; + var colspan = group_count('overview', 'Statistics', []) + + group_count('overview', 'General', []); %> > +<% if (nodes.length > 1) { %> +<% } %> <% if (!node.running) { %> - <% } else if (node.os_pid == undefined) { %> - <% } else { %> + <% if (show_column('overview', 'file_descriptors')) { %> + <% } %> + <% if (show_column('overview', 'socket_descriptors')) { %> + <% } %> + <% if (show_column('overview', 'erlang_processes')) { %> + <% } %> + <% if (show_column('overview', 'memory')) { %> + <% } %> + <% if (show_column('overview', 'disk_space')) { %> - -<% } %> + <% } %> + <% if (show_column('overview', 'uptime')) { %> + + <% } %> + <% if (show_column('overview', 'rates_mode')) { %> + + <% } %> + <% if (show_column('overview', 'info')) { %> + <% } %> +<% } %> <% } %>
Name - File descriptors - - Socket descriptors - - Erlang processes - - Memory - - Disk space - File descriptors Socket descriptors Erlang processesMemoryDisk spaceUptimeTypeRates modeInfo+/-
<%= link_node(node.name) %> <% if (rabbit_versions_interesting) { %> RabbitMQ <%= fmt_rabbit_version(node.applications) %> <% } %> +
Node not running
+
Node statistics not available
-<%= fmt_resource_bar_count(fmt_fd_used(node.fd_used, node.fd_total), node.fd_total, FD_THRESHOLDS) %> + <% if (node.fd_used != 'install_handle_from_sysinternals') { %> + <%= node_stat_count_bar('fd_used', 'fd_total', node, FD_THRESHOLDS) %> + <% } else { %> +

handle.exe missing <%= node.fd_total %> available

+ + <% } %>
-<%= fmt_resource_bar_count(node.sockets_used, node.sockets_total, FD_THRESHOLDS) %> + <%= node_stat_count_bar('sockets_used', 'sockets_total', node, FD_THRESHOLDS) %> -<%= fmt_resource_bar_count(node.proc_used, node.proc_total, PROCESS_THRESHOLDS) %> + + <%= node_stat_count_bar('proc_used', 'proc_total', node, PROCESS_THRESHOLDS) %> -<% if (node.mem_limit != 'memory_monitoring_disabled') { %> - <%= fmt_resource_bar(fmt_bytes(node.mem_used), - fmt_bytes(node.mem_limit) + ' high watermark', - node.mem_used / node.mem_limit, - node.mem_alarm ? 'red' : 'green', - node.mem_alarm ? 'memory-alarm' : null) %> -<% } else { %> + + <% if (node.mem_limit != 'memory_monitoring_disabled') { %> + <%= node_stat_bar('mem_used', 'mem_limit', 'high watermark', node, fmt_bytes_axis, + node.mem_alarm ? 'red' : 'green', + node.mem_alarm ? 'memory-alarm' : null) %> + <% } else { %> <%= fmt_bytes(node.mem_used) %> -<% } %> + <% } %> -<% if (node.disk_free_limit != 'disk_free_monitoring_disabled') { %> - <%= fmt_resource_bar(fmt_bytes(node.disk_free), - fmt_bytes(node.disk_free_limit) + ' low watermark', - node.disk_free_limit / node.disk_free, - node.disk_free_alarm ? 'red' : 'green', - node.disk_free_alarm ? 'disk-free-alarm' : null) %> -<% } else { %> + + <% if (node.disk_free_limit != 'disk_free_monitoring_disabled') { %> + <%= node_stat_bar('disk_free', 'disk_free_limit', 'low watermark', node, fmt_bytes_axis, + node.disk_free_alarm ? 'red' : 'green', + node.disk_free_alarm ? 'disk_free-alarm' : null, true) %> + <% } else { %> (not available) -<% } %> + <% } %> - <%= fmt_uptime(node.uptime) %> - <%= fmt_uptime(node.uptime) %><%= fmt_string(node.rates_mode) %> <% if (node.type == 'disc') { %> Disc <% } else { %> RAM <% } %> + <%= fmt_plugins_small(node) %> <% if (overview.statistics_db_node == node.name) { %> Stats <% } %> - <% if (overview.node == node.name) { %> - * - <% } %>
@@ -154,10 +212,16 @@ <% if (overview.statistics_db_node == 'not_running') { %>

Statistics database could not be contacted. Message rates and queue lengths will not be shown.

<% } %> + +<% if (nodes.length == 1 && nodes[0].os_pid != undefined) { %> +

Paths

+ <%= format('paths', {node: nodes[0]}) %> +<% } %> +
-
+

Ports and contexts

Listening ports

@@ -256,21 +320,18 @@
-<% if (overview.statistics_level != 'fine') { %> +<% if (overview.rates_mode == 'none') { %>
-

Message Rates Disabled

+

Message rates disabled

- The statistics level in this RabbitMQ server is currently set to - <%= overview.statistics_level %>. Message rates are therefore - disabled. + Message rates are currently disabled.

- To re-enable message rates, edit your configuration file and either - set collect_statistics to fine in - the rabbit application, or - set force_fine_statistics to true in - the rabbitmq_management_agent application + To re-enable message rates, edit your configuration file and + set rates_mode to basic + or detailed in the rabbitmq_management + application

diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/partition.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/partition.ejs index baaa0e2..bc22fe2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/partition.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/partition.ejs @@ -12,7 +12,10 @@ %>

Network partition detected

- Mnesia reports that this RabbitMQ cluster has experienced a network partition. This is a dangerous situation. RabbitMQ clusters should not be installed on networks which can experience partitions. + Mnesia reports that this RabbitMQ cluster has experienced a + network partition. There is a risk of losing data. Please read + RabbitMQ + documentation about network partitions and the possible solutions.

The nature of the partition is as follows: @@ -51,4 +54,52 @@ network partitions.

<% } %> +<% + var ticktime = null; + var ticktimes_unequal = false; + for (var i = 0; i < nodes.length; i++) { + var node_ticktime = nodes[i].net_ticktime; + if (node_ticktime != undefined) { + + if (ticktime != null && node_ticktime != ticktime) { + ticktimes_unequal = true; + } + ticktime = nodes[i].net_ticktime; + } + } + if (ticktimes_unequal) { +%> +

+ The kernel net_ticktime values are set + differently for different nodes in this cluster. +

+

+ The values are: +

+ + +<% + for (var i = 0; i < nodes.length; i++) { +%> + > + + + +<% + } +%> +
Nodenet_ticktime
<%= nodes[i].name %><%= nodes[i].net_ticktime %>
+

+ This is a dangerous configuration; use of substantially + unequal net_timetime values can lead to partitions + being falsely detected. +

+

+ More information on + net_ticktime. +

+<% + } +%>
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/paths.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/paths.ejs new file mode 100644 index 0000000..c854657 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/paths.ejs @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + +
+<% if (node.config_files.length == 1) { %> + Config file +<% } else { %> + Config files +<% } %> + + +<% + for (var i = 0; i < node.config_files.length; i++) { + var config = node.config_files[i]; +%> + <%= config %> +<% } %> +
Database directory + <%= node.db_dir %> +
Log file + <%= node.log_file %> +
SASL log file + <%= node.sasl_log_file %> +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policies.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policies.ejs index 37fbc3e..9e4e3c2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policies.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policies.ejs @@ -80,15 +80,51 @@ - - -
- * - + + + +
+ + + + + + + + + + + + + + + + + + + + * + diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policy.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policy.ejs index 5fc6720..c4a58c4 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policy.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/policy.ejs @@ -1,13 +1,9 @@ -

Policy: <%= fmt_string(policy.name) %>

+

Policy: <%= fmt_string(policy.name) %><%= fmt_maybe_vhost(policy.vhost) %>

Overview

- - - - diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queue.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queue.ejs index 6ac46e9..46fcd44 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queue.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queue.ejs @@ -1,92 +1,27 @@ -

Queue <%= fmt_string(queue.name) %>

+

Queue <%= fmt_string(queue.name) %><%= fmt_maybe_vhost(queue.vhost) %>

Overview

-
+
<%= queue_lengths('lengths-q', queue) %> -<% if (statistics_level == 'fine') { %> +<% if (rates_mode != 'none') { %> <%= message_rates('msg-rates-q', queue.message_stats) %> <% } %> -

Details

-
Virtual Host<%= fmt_string(policy.vhost) %>
Pattern <%= fmt_string(policy.pattern) %>
+
- - + + + <% if (queue.owner_pid_details != undefined) { %> - - -
Parameters<%= fmt_parameters(queue) %>Features<%= fmt_features(queue) %>
Policy <%= fmt_string(queue.policy, '') %>
Exclusive owner - <% if (queue.owner_pid_details == undefined) { %> - None - <% } else { %> - <%= link_conn(queue.owner_pid_details.name) %> - <% } %> -
- - - - - - - - - - - - - - - - - - -
State<%= fmt_object_state(queue) %>
Consumers<%= fmt_string(queue.consumers) %>
Consumer utilisation <%= fmt_percent(queue.consumer_utilisation) %>
Memory<%= fmt_bytes(queue.memory) %>
- - - - - - - - - - -
- Paging - - <% var messages_ram = queue.backing_queue_status.ram_msg_count + queue.backing_queue_status.ram_ack_count; %> - <% if (messages_ram == queue.messages) { %> - No paging - <% } else { %> - <%= fmt_num_thousands(messages_ram) %> / - <%= fmt_num_thousands(queue.messages) %> msg (in RAM / total) - <% } %> - - <% if (queue.backing_queue_status.target_ram_count == 'infinity') { %> - No limit - <% } else { %> - RAM target: <%= fmt_num_thousands(queue.backing_queue_status.target_ram_count) %> msg - <% } %> - -
- Persistent - - <%= fmt_num_thousands(queue.backing_queue_status.persistent_count) %> msg -
- - -<% if (vhosts_interesting) { %> - - - + <% } %> <% if (nodes_interesting) { %> @@ -94,6 +29,7 @@ + <% if (queue.owner_pid_details == undefined) { %> + <% } %> <% } %>
Virtual host<%= fmt_string(queue.vhost) %><%= link_conn(queue.owner_pid_details.name) %>
Node <%= fmt_node(queue.node) %>
Slaves @@ -138,13 +74,88 @@ <% } %>
-
+ + + + + + + + + + + + + + +
State<%= fmt_object_state(queue) %>
Consumers<%= fmt_string(queue.consumers) %>
Consumer utilisation <%= fmt_percent(queue.consumer_utilisation) %>
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TotalReadyUnackedIn memoryPersistent
+ Messages + + + <%= fmt_num_thousands(queue.messages) %> + + <%= fmt_num_thousands(queue.messages_ready) %> + + <%= fmt_num_thousands(queue.messages_unacknowledged) %> + + <%= fmt_num_thousands(queue.messages_ram) %> + + <%= fmt_num_thousands(queue.messages_persistent) %> +
+ Message body bytes + + + <%= fmt_bytes(queue.message_bytes) %> + + <%= fmt_bytes(queue.message_bytes_ready) %> + + <%= fmt_bytes(queue.message_bytes_unacknowledged) %> + + <%= fmt_bytes(queue.message_bytes_ram) %> + + <%= fmt_bytes(queue.message_bytes_persistent) %> +
+ Process memory + + <%= fmt_bytes(queue.memory) %>
-<% if (statistics_level == 'fine') { %> +<% if (rates_mode == 'detailed') { %>

Message rates breakdown

@@ -232,6 +243,51 @@
+<% if (user_policymaker) { %> +
+

Move messages

+
+ <% if (NAVIGATION['Admin'][0]['Shovel Management'] == undefined) { %> +

To move messages, the shovel plugin must be enabled, try:

+
$ rabbitmq-plugins enable rabbitmq_shovel rabbitmq_shovel_management
+ <% } else { %> +

+ The shovel plugin can be used to move messages from this queue + to another one. The form below will create a temporary shovel to + move messages to another queue on the same virtual host, with + default settings. +

+

+ For more options see the shovel + interface. +

+
+ + + + + + + + + + + + + + + + + + +
Destination queue:
+ +
+ <% } %> +
+
+<% } %> +

Delete / purge

diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queues.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queues.ejs index a579ee9..0c3fed8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queues.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/queues.ejs @@ -5,17 +5,16 @@ <%= filter_ui(queues) %>
<% if (queues.length > 0) { %> -<% - var col_redeliver = !is_col_empty(queues, 'redeliver'); -%> - - -<% if (statistics_level == 'fine') { %> - + <%= group_heading('queues', 'Overview', [vhosts_interesting, nodes_interesting, true]) %> + <%= group_heading('queues', 'Messages', []) %> + <%= group_heading('queues', 'Message bytes', []) %> +<% if (rates_mode != 'none') { %> + <%= group_heading('queues', 'Message rates', []) %> <% } %> + <% if (vhosts_interesting) { %> @@ -25,21 +24,67 @@ <% if (nodes_interesting) { %> <% } %> - - - +<% if (show_column('queues', 'features')) { %> + +<% } %> +<% if (show_column('queues', 'features_no_policy')) { %> + +<% } %> +<% if (show_column('queues', 'policy')) { %> + +<% } %> +<% if (show_column('queues', 'consumers')) { %> + +<% } %> +<% if (show_column('queues', 'consumer_utilisation')) { %> + +<% } %> +<% if (show_column('queues', 'state')) { %> +<% } %> +<% if (show_column('queues', 'msgs-ready')) { %> +<% } %> +<% if (show_column('queues', 'msgs-unacked')) { %> +<% } %> +<% if (show_column('queues', 'msgs-ram')) { %> + +<% } %> +<% if (show_column('queues', 'msgs-persistent')) { %> + +<% } %> +<% if (show_column('queues', 'msgs-total')) { %> -<% if (statistics_level == 'fine') { %> +<% } %> +<% if (show_column('queues', 'msg-bytes-ready')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-unacked')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-ram')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-persistent')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-total')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> + <% if (show_column('queues', 'rate-incoming')) { %> - + <% if (show_column('queues', 'rate-deliver')) { %> + + <% } %> + <% if (show_column('queues', 'rate-redeliver')) { %> + + <% } %> + <% if (show_column('queues', 'rate-ack')) { %> + <% } %> <% } %> @@ -62,21 +107,70 @@ <% } %> <% } %> +<% if (show_column('queues', 'features')) { %> - - +<% } %> +<% if (show_column('queues', 'features_no_policy')) { %> + +<% } %> +<% if (show_column('queues', 'policy')) { %> + +<% } %> +<% if (show_column('queues', 'consumers')) { %> + +<% } %> +<% if (show_column('queues', 'consumer_utilisation')) { %> + +<% } %> +<% if (show_column('queues', 'state')) { %> +<% } %> +<% if (show_column('queues', 'msgs-ready')) { %> +<% } %> +<% if (show_column('queues', 'msgs-unacked')) { %> +<% } %> +<% if (show_column('queues', 'msgs-ram')) { %> + +<% } %> +<% if (show_column('queues', 'msgs-persistent')) { %> + +<% } %> +<% if (show_column('queues', 'msgs-total')) { %> -<% if (statistics_level == 'fine') { %> - - - +<% } %> +<% if (show_column('queues', 'msg-bytes-ready')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-unacked')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-ram')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-persistent')) { %> + +<% } %> +<% if (show_column('queues', 'msg-bytes-total')) { %> + +<% } %> +<% if (rates_mode != 'none') { %> + <% if (show_column('queues', 'rate-incoming')) { %> + + <% } %> + <% if (show_column('queues', 'rate-deliver')) { %> + + <% } %> + <% if (show_column('queues', 'rate-redeliver')) { %> + + <% } %> + <% if (show_column('queues', 'rate-ack')) { %> + + <% } %> <% } %> <% } %> @@ -145,29 +239,25 @@ - - - - - - - - - - - - - - - - - - - - - +
OverviewMessagesMessage rates+/-
<%= fmt_sort('Node', 'node') %><%= fmt_sort('Exclusive', 'owner_pid_details.name') %>Parameters<%= fmt_sort('Policy', 'policy') %>FeaturesFeatures<%= fmt_sort('Policy','policy') %><%= fmt_sort('Consumers', 'consumers') %><%= fmt_sort('Consumer utilisation', 'consumer_utilisation') %><%= fmt_sort('State', 'state') %><%= fmt_sort('Ready', 'messages_ready') %><%= fmt_sort('Unacked', 'messages_unacknowledged') %><%= fmt_sort('In Memory', 'messages_ram') %><%= fmt_sort('Persistent', 'messages_persistent') %><%= fmt_sort('Total', 'messages') %><%= fmt_sort('Ready', 'message_bytes_ready') %><%= fmt_sort('Unacked', 'message_bytes_unacknowledged') %><%= fmt_sort('In Memory', 'message_bytes_ram') %><%= fmt_sort('Persistent', 'message_bytes_persistent') %><%= fmt_sort('Total', 'message_bytes') %><%= fmt_sort('incoming', 'message_stats.publish_details.rate') %><%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %> - <% if (col_redeliver) { %> - <%= fmt_sort('of which redelivered', 'message_stats.redeliver_details.rate') %> <% } %> -<%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %><%= fmt_sort('redelivered', 'message_stats.redeliver_details.rate') %><%= fmt_sort('ack', 'message_stats.ack_details.rate') %>
- <% if (queue.owner_pid_details != undefined) { %> - <%= link_conn(queue.owner_pid_details.name, "Owner") %> - <% } %> + <%= fmt_features_short(queue) %> + <%= fmt_policy_short(queue) %> <%= fmt_parameters_short(queue) %><%= fmt_string(queue.policy, '') %><%= fmt_features_short(queue) %><%= fmt_string(queue.policy) %><%= fmt_string(queue.consumers) %><%= fmt_percent(queue.consumer_utilisation) %><%= fmt_object_state(queue) %><%= fmt_num_thousands(queue.messages_ready) %><%= fmt_num_thousands(queue.messages_unacknowledged) %><%= fmt_num_thousands(queue.messages_ram) %><%= fmt_num_thousands(queue.messages_persistent) %><%= fmt_num_thousands(queue.messages) %><%= fmt_rate(queue.message_stats, 'publish') %><%= fmt_deliver_rate(queue.message_stats, col_redeliver) %><%= fmt_rate(queue.message_stats, 'ack') %><%= fmt_bytes(queue.message_bytes_ready) %><%= fmt_bytes(queue.message_bytes_unacknowledged) %><%= fmt_bytes(queue.message_bytes_ram) %><%= fmt_bytes(queue.message_bytes_persistent) %><%= fmt_bytes(queue.message_bytes) %><%= fmt_detail_rate(queue.message_stats, 'publish') %><%= fmt_detail_rate(queue.message_stats, 'deliver_get') %><%= fmt_detail_rate(queue.message_stats, 'redeliver') %><%= fmt_detail_rate(queue.message_stats, 'ack') %>
ms
ms
+
+ + + + + + +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/rate-options.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/rate-options.ejs index 904eb8e..da15b0e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/rate-options.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/rate-options.ejs @@ -2,7 +2,7 @@ var id = span.attr('for'); var mode = get_pref('rate-mode-' + id); var size = get_pref('chart-size-' + id); - var range = get_pref('chart-range-' + id); + var range = get_pref('chart-range'); %>
@@ -18,7 +18,9 @@ <%= fmt_radio('mode', 'Chart', 'chart', mode) %> <%= fmt_radio('mode', 'Current value', 'curr', mode) %> - <%= fmt_radio('mode', 'Moving average', 'avg', mode) %> + <% if (id != 'node-stats') { %> + <%= fmt_radio('mode', 'Moving average', 'avg', mode) %> + <% } %> @@ -29,6 +31,11 @@ <%= fmt_radio('size', 'Large', 'large', size) %> + + +

All time series

+ + diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/users.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/users.ejs index 1f4ba28..f0dfc4d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/users.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/users.ejs @@ -76,13 +76,18 @@ - - [Admin] - [Monitoring] - [Policymaker] - [Management] - [None] - + + + + + + diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhost.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhost.ejs index 3a92f4a..fe45177 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhost.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhost.ejs @@ -9,13 +9,12 @@

Overview

-
+
<%= queue_lengths('lengths-vhost', vhost) %> -<% if (statistics_level == 'fine') { %> +<% if (rates_mode != 'none') { %> <%= message_rates('msg-rates-vhost', vhost.message_stats) %> <% } %> <%= data_rates('data-rates-vhost', vhost, 'Data rates') %> -

Details

@@ -23,7 +22,6 @@
<%= fmt_boolean(vhost.tracing) %>
-
diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhosts.ejs b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhosts.ejs index cf9b637..a6cbe9f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhosts.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-management/priv/www/js/tmpl/vhosts.ejs @@ -10,23 +10,38 @@ Overview - Messages - Data rates -<% if (statistics_level == 'fine') { %> - Message rates + <%= group_heading('vhosts', 'Messages', []) %> + <%= group_heading('vhosts', 'Network', []) %> +<% if (rates_mode != 'none') { %> + <%= group_heading('vhosts', 'Message rates', []) %> <% } %> + +/- <%= fmt_sort('Name', 'name') %> Users +<% if (show_column('vhosts', 'msgs-ready')) { %> <%= fmt_sort('Ready', 'messages_ready') %> +<% } %> +<% if (show_column('vhosts', 'msgs-unacked')) { %> <%= fmt_sort('Unacked', 'messages_unacknowledged') %> +<% } %> +<% if (show_column('vhosts', 'msgs-total')) { %> <%= fmt_sort('Total', 'messages') %> - <%= fmt_sort('From clients', 'recv_oct_details.rate') %> - <%= fmt_sort('To clients', 'send_oct_details.rate') %> -<% if (statistics_level == 'fine') { %> - <%= fmt_sort('publish', 'message_stats.publish_details.rate') %> - <%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %> +<% } %> +<% if (show_column('vhosts', 'from_client')) { %> + <%= fmt_sort('From client', 'recv_oct_details.rate') %> +<% } %> +<% if (show_column('vhosts', 'to_client')) { %> + <%= fmt_sort('To client', 'send_oct_details.rate') %> +<% } %> +<% if (rates_mode != 'none') { %> + <% if (show_column('vhosts', 'rate-publish')) { %> + <%= fmt_sort('publish', 'message_stats.publish_details.rate') %> + <% } %> + <% if (show_column('vhosts', 'rate-deliver')) { %> + <%= fmt_sort('deliver / get','message_stats.deliver_get_details.rate') %> + <% } %> <% } %> @@ -39,14 +54,28 @@ <%= link_vhost(vhost.name) %> <%= fmt_permissions(vhost, permissions, 'vhost', 'user', '

No users

') %> - <%= fmt_string(vhost.messages_ready) %> - <%= fmt_string(vhost.messages_unacknowledged) %> - <%= fmt_string(vhost.messages) %> - <%= fmt_rate_bytes(vhost, 'recv_oct') %> - <%= fmt_rate_bytes(vhost, 'send_oct') %> -<% if (statistics_level == 'fine') { %> - <%= fmt_rate(vhost.message_stats, 'publish') %> - <%= fmt_deliver_rate(vhost.message_stats, false) %> +<% if (show_column('vhosts', 'msgs-ready')) { %> + <%= fmt_num_thousands(vhost.messages_ready) %> +<% } %> +<% if (show_column('vhosts', 'msgs-unacked')) { %> + <%= fmt_num_thousands(vhost.messages_unacknowledged) %> +<% } %> +<% if (show_column('vhosts', 'msgs-total')) { %> + <%= fmt_num_thousands(vhost.messages) %> +<% } %> +<% if (show_column('vhosts', 'from_client')) { %> + <%= fmt_detail_rate_bytes(vhost, 'recv_oct') %> +<% } %> +<% if (show_column('vhosts', 'to_client')) { %> + <%= fmt_detail_rate_bytes(vhost, 'send_oct') %> +<% } %> +<% if (rates_mode != 'none') { %> + <% if (show_column('vhosts', 'rate-publish')) { %> + <%= fmt_detail_rate(vhost.message_stats, 'publish') %> + <% } %> + <% if (show_column('vhosts', 'rate-deliver')) { %> + <%= fmt_detail_rate(vhost.message_stats, 'deliver_get') %> + <% } %> <% } %> <% } %> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_app.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_app.erl index a8b0894..b5f4b6b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_app.erl @@ -11,13 +11,13 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_app). -behaviour(application). --export([start/2, stop/1]). +-export([start/2, stop/1, reset_dispatcher/1]). -include("rabbit_mgmt.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). @@ -28,7 +28,7 @@ start(_Type, _StartArgs) -> {ok, Listener} = application:get_env(rabbitmq_management, listener), setup_wm_logging(), - register_context(Listener), + register_context(Listener, []), log_startup(Listener), rabbit_mgmt_sup_sup:start_link(). @@ -36,18 +36,28 @@ stop(_State) -> unregister_context(), ok. -register_context(Listener) -> +%% At the point at which this is invoked we have both newly enabled +%% apps and about-to-disable apps running (so that +%% rabbit_mgmt_reset_handler can look at all of them to find +%% extensions). Therefore we have to explicitly exclude +%% about-to-disable apps from our new dispatcher. +reset_dispatcher(IgnoreApps) -> + unregister_context(), + {ok, Listener} = application:get_env(rabbitmq_management, listener), + register_context(Listener, IgnoreApps). + +register_context(Listener, IgnoreApps) -> rabbit_web_dispatch:register_context_handler( - ?CONTEXT, Listener, "", make_loop(), "RabbitMQ Management"). + ?CONTEXT, Listener, "", make_loop(IgnoreApps), "RabbitMQ Management"). unregister_context() -> rabbit_web_dispatch:unregister_context(?CONTEXT). -make_loop() -> - Dispatch = rabbit_mgmt_dispatcher:build_dispatcher(), +make_loop(IgnoreApps) -> + Dispatch = rabbit_mgmt_dispatcher:build_dispatcher(IgnoreApps), WMLoop = rabbit_webmachine:makeloop(Dispatch), LocalPaths = [filename:join(module_path(M), ?STATIC_PATH) || - M <- rabbit_mgmt_dispatcher:modules()], + M <- rabbit_mgmt_dispatcher:modules(IgnoreApps)], fun(Req) -> respond(Req, LocalPaths, WMLoop) end. module_path(Module) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_db.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_db.erl index eda933f..e7cb753 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_db.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_db.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_db). @@ -24,14 +24,15 @@ -export([start_link/0]). -export([augment_exchanges/3, augment_queues/3, - augment_nodes/1, augment_vhosts/2, + augment_nodes/2, augment_vhosts/2, get_channel/2, get_connection/2, get_all_channels/1, get_all_connections/1, + get_all_consumers/0, get_all_consumers/1, get_overview/2, get_overview/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3, handle_pre_hibernate/1, prioritise_cast/3, - format_message_queue/2]). + code_change/3, handle_pre_hibernate/1, + prioritise_cast/3, prioritise_call/4, format_message_queue/2]). %% For testing -export([override_lookups/1, reset_lookups/0]). @@ -140,21 +141,46 @@ gc_next_key, lookups, interval, - event_refresh_ref}). + event_refresh_ref, + rates_mode}). -define(FINE_STATS_TYPES, [channel_queue_stats, channel_exchange_stats, channel_queue_exchange_stats]). -define(TABLES, [queue_stats, connection_stats, channel_stats, consumers_by_queue, consumers_by_channel, - node_stats]). + node_stats, node_node_stats]). -define(DELIVER_GET, [deliver, deliver_no_ack, get, get_no_ack]). -define(FINE_STATS, [publish, publish_in, publish_out, ack, deliver_get, confirm, return_unroutable, redeliver] ++ ?DELIVER_GET). --define(COARSE_QUEUE_STATS, - [messages, messages_ready, messages_unacknowledged]). +%% Most come from channels as fine stats, but queues emit these directly. +-define(QUEUE_MSG_RATES, [disk_reads, disk_writes]). + +-define(MSG_RATES, ?FINE_STATS ++ ?QUEUE_MSG_RATES). + +-define(QUEUE_MSG_COUNTS, [messages, messages_ready, messages_unacknowledged]). + +-define(COARSE_NODE_STATS, + [mem_used, fd_used, sockets_used, proc_used, disk_free, + io_read_count, io_read_bytes, io_read_avg_time, + io_write_count, io_write_bytes, io_write_avg_time, + io_sync_count, io_sync_avg_time, + io_seek_count, io_seek_avg_time, + io_reopen_count, mnesia_ram_tx_count, mnesia_disk_tx_count, + msg_store_read_count, msg_store_write_count, + queue_index_journal_write_count, + queue_index_write_count, queue_index_read_count]). + +-define(COARSE_NODE_NODE_STATS, [send_bytes, recv_bytes]). + +%% Normally 0 and no history means "has never happened, don't +%% report". But for these things we do want to report even at 0 with +%% no history. +-define(ALWAYS_REPORT_STATS, + [io_read_avg_time, io_write_avg_time, + io_sync_avg_time | ?QUEUE_MSG_COUNTS]). -define(COARSE_CONN_STATS, [recv_oct, send_oct]). @@ -175,6 +201,14 @@ prioritise_cast({event, #event{type = Type, prioritise_cast(_Msg, _Len, _State) -> 0. +%% We want timely replies to queries even when overloaded, so return 5 +%% as priority. Also we only have access to the queue length here, not +%% in handle_call/3, so stash it in the dictionary. This is a bit ugly +%% but better than fiddling with gen_server2 even more. +prioritise_call(_Msg, _From, Len, _State) -> + put(last_queue_length, Len), + 5. + %%---------------------------------------------------------------------------- %% API %%---------------------------------------------------------------------------- @@ -194,7 +228,7 @@ start_link() -> augment_exchanges(Xs, R, M) -> safe_call({augment_exchanges, Xs, R, M}, Xs). augment_queues(Qs, R, M) -> safe_call({augment_queues, Qs, R, M}, Qs). augment_vhosts(VHosts, R) -> safe_call({augment_vhosts, VHosts, R}, VHosts). -augment_nodes(Nodes) -> safe_call({augment_nodes, Nodes}, Nodes). +augment_nodes(Nodes, R) -> safe_call({augment_nodes, Nodes, R}, Nodes). get_channel(Name, R) -> safe_call({get_channel, Name, R}, not_found). get_connection(Name, R) -> safe_call({get_connection, Name, R}, not_found). @@ -202,6 +236,9 @@ get_connection(Name, R) -> safe_call({get_connection, Name, R}, not_found). get_all_channels(R) -> safe_call({get_all_channels, R}). get_all_connections(R) -> safe_call({get_all_connections, R}). +get_all_consumers() -> safe_call({get_all_consumers, all}). +get_all_consumers(V) -> safe_call({get_all_consumers, V}). + get_overview(User, R) -> safe_call({get_overview, User, R}). get_overview(R) -> safe_call({get_overview, all, R}). @@ -213,15 +250,15 @@ safe_call(Term, Default) -> safe_call(Term, Default, 1). %% See rabbit_mgmt_sup_sup for a discussion of the retry logic. safe_call(Term, Default, Retries) -> - try - gen_server2:call({global, ?MODULE}, Term, infinity) - catch exit:{noproc, _} -> - case Retries of - 0 -> Default; - _ -> rabbit_mgmt_sup_sup:start_child(), - safe_call(Term, Default, Retries - 1) - end - end. + rabbit_misc:with_exit_handler( + fun () -> + case Retries of + 0 -> Default; + _ -> rabbit_mgmt_sup_sup:start_child(), + safe_call(Term, Default, Retries - 1) + end + end, + fun () -> gen_server2:call({global, ?MODULE}, Term, infinity) end). %%---------------------------------------------------------------------------- %% Internal, gen_server2 callbacks @@ -232,6 +269,7 @@ init([Ref]) -> %% that the management plugin work. process_flag(priority, high), {ok, Interval} = application:get_env(rabbit, collect_statistics_interval), + {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode), rabbit_node_monitor:subscribe(self()), rabbit_log:info("Statistics database started.~n"), Table = fun () -> ets:new(rabbit_mgmt_db, [ordered_set]) end, @@ -243,7 +281,8 @@ init([Ref]) -> old_stats = Table(), aggregated_stats = Table(), aggregated_stats_index = Table(), - event_refresh_ref = Ref})), hibernate, + event_refresh_ref = Ref, + rates_mode = RatesMode})), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. handle_call({augment_exchanges, Xs, Ranges, basic}, _From, State) -> @@ -261,8 +300,8 @@ handle_call({augment_queues, Qs, Ranges, full}, _From, State) -> handle_call({augment_vhosts, VHosts, Ranges}, _From, State) -> reply(vhost_stats(Ranges, VHosts, State), State); -handle_call({augment_nodes, Nodes}, _From, State) -> - {reply, node_stats(Nodes, State), State}; +handle_call({augment_nodes, Nodes, Ranges}, _From, State) -> + {reply, node_stats(Ranges, Nodes, State), State}; handle_call({get_channel, Name, Ranges}, _From, State = #state{tables = Tables}) -> @@ -290,6 +329,14 @@ handle_call({get_all_connections, Ranges}, _From, Conns = created_events(connection_stats, Tables), reply(connection_stats(Ranges, Conns, State), State); +handle_call({get_all_consumers, VHost}, + _From, State = #state{tables = Tables}) -> + All = ets:tab2list(orddict:fetch(consumers_by_queue, Tables)), + {reply, [augment_msg_stats( + augment_consumer(Obj), State) || + {{#resource{virtual_host = VHostC}, _Ch, _CTag}, Obj} <- All, + VHost =:= all orelse VHost =:= VHostC], State}; + handle_call({get_overview, User, Ranges}, _From, State = #state{tables = Tables}) -> VHosts = case User of @@ -300,8 +347,8 @@ handle_call({get_overview, User, Ranges}, _From, %% recv_oct now! VStats = [read_simple_stats(vhost_stats, VHost, State) || VHost <- VHosts], - MessageStats = [overview_sum(Type, VStats) || Type <- ?FINE_STATS], - QueueStats = [overview_sum(Type, VStats) || Type <- ?COARSE_QUEUE_STATS], + MessageStats = [overview_sum(Type, VStats) || Type <- ?MSG_RATES], + QueueStats = [overview_sum(Type, VStats) || Type <- ?QUEUE_MSG_COUNTS], F = case User of all -> fun (L) -> length(L) end; _ -> fun (L) -> length(rabbit_mgmt_util:filter_user(L, User)) end @@ -322,7 +369,8 @@ handle_call({get_overview, User, Ranges}, _From, {channels, F(created_events(channel_stats, Tables))}], reply([{message_stats, format_samples(Ranges, MessageStats, State)}, {queue_totals, format_samples(Ranges, QueueStats, State)}, - {object_totals, ObjectTotals}], State); + {object_totals, ObjectTotals}, + {statistics_db_event_queue, get(last_queue_length)}], State); handle_call({override_lookups, Lookups}, _From, State) -> reply(ok, State#state{lookups = Lookups}); @@ -330,6 +378,12 @@ handle_call({override_lookups, Lookups}, _From, State) -> handle_call(reset_lookups, _From, State) -> reply(ok, reset_lookups(State)); +%% Used in rabbit_mgmt_test_db where we need guarantees events have +%% been handled before querying +handle_call({event, Event = #event{reference = none}}, _From, State) -> + handle_event(Event, State), + reply(ok, State); + handle_call(_Request, _From, State) -> reply(not_understood, State). @@ -407,6 +461,7 @@ pget(Key, List) -> pget(Key, List, unknown). %% passed a queue proplist that will already have been formatted - %% i.e. it will have name and vhost keys. id_name(node_stats) -> name; +id_name(node_node_stats) -> route; id_name(vhost_stats) -> name; id_name(queue_stats) -> name; id_name(exchange_stats) -> name; @@ -433,9 +488,9 @@ fine_stats_id(ChPid, {Q, X}) -> {ChPid, Q, X}; fine_stats_id(ChPid, QorX) -> {ChPid, QorX}. floor(TS, #state{interval = Interval}) -> - rabbit_mgmt_util:floor(rabbit_mgmt_format:timestamp_ms(TS), Interval). + rabbit_mgmt_util:floor(rabbit_mgmt_format:now_to_ms(TS), Interval). ceil(TS, #state{interval = Interval}) -> - rabbit_mgmt_util:ceil (rabbit_mgmt_format:timestamp_ms(TS), Interval). + rabbit_mgmt_util:ceil (rabbit_mgmt_format:now_to_ms(TS), Interval). details_key(Key) -> list_to_atom(atom_to_list(Key) ++ "_details"). @@ -447,9 +502,9 @@ handle_event(#event{type = queue_stats, props = Stats, timestamp = Timestamp}, State) -> handle_stats(queue_stats, Stats, Timestamp, [{fun rabbit_mgmt_format:properties/1,[backing_queue_status]}, - {fun rabbit_mgmt_format:timestamp/1, [idle_since]}, + {fun rabbit_mgmt_format:now_to_str/1, [idle_since]}, {fun rabbit_mgmt_format:queue_state/1, [state]}], - [messages, messages_ready, messages_unacknowledged], State); + ?QUEUE_MSG_COUNTS, ?QUEUE_MSG_RATES, State); handle_event(Event = #event{type = queue_deleted, props = [{name, Name}], @@ -464,8 +519,8 @@ handle_event(Event = #event{type = queue_deleted, %% This ceil must correspond to the ceil in append_samples/5 TS = ceil(Timestamp, State), OldStats = lookup_element(OldTable, Id), - [record_sample(Id, {Key, -pget(Key, OldStats, 0), TS, State}, State) - || Key <- ?COARSE_QUEUE_STATS], + [record_sample(Id, {Key, -pget(Key, OldStats, 0), TS, State}, true, State) + || Key <- ?QUEUE_MSG_COUNTS], delete_samples(channel_queue_stats, {'_', Name}, State), delete_samples(queue_exchange_stats, {Name, '_'}, State), delete_samples(queue_stats, Name, State), @@ -480,8 +535,7 @@ handle_event(Event = #event{type = exchange_deleted, handle_event(#event{type = vhost_deleted, props = [{name, Name}]}, State) -> - delete_samples(vhost_stats, Name, State), - {ok, State}; + delete_samples(vhost_stats, Name, State); handle_event(#event{type = connection_created, props = Stats}, State) -> handle_created( @@ -508,7 +562,7 @@ handle_event(#event{type = channel_created, props = Stats}, State) -> handle_event(#event{type = channel_stats, props = Stats, timestamp = Timestamp}, State = #state{old_stats = OldTable}) -> handle_stats(channel_stats, Stats, Timestamp, - [{fun rabbit_mgmt_format:timestamp/1, [idle_since]}], + [{fun rabbit_mgmt_format:now_to_str/1, [idle_since]}], [], State), ChPid = id(channel_stats, Stats), AllStats = [old_fine_stats(Type, Stats, State) @@ -516,8 +570,7 @@ handle_event(#event{type = channel_stats, props = Stats, timestamp = Timestamp}, ets:match_delete(OldTable, {{fine, {ChPid, '_'}}, '_'}), ets:match_delete(OldTable, {{fine, {ChPid, '_', '_'}}, '_'}), [handle_fine_stats(Timestamp, AllStatsElem, State) - || AllStatsElem <- AllStats], - {ok, State}; + || AllStatsElem <- AllStats]; handle_event(Event = #event{type = channel_closed, props = [{pid, Pid}]}, @@ -545,15 +598,24 @@ handle_event(#event{type = consumer_deleted, props = Props}, State) -> %% TODO: we don't clear up after dead nodes here - this is a very tiny %% leak every time a node is permanently removed from the cluster. Do %% we care? -handle_event(#event{type = node_stats, props = Stats, timestamp = Timestamp}, - State = #state{tables = Tables}) -> - Table = orddict:fetch(node_stats, Tables), - ets:insert(Table, {{pget(name, Stats), stats}, - proplists:delete(name, Stats), Timestamp}), - {ok, State}; - -handle_event(_Event, State) -> - {ok, State}. +handle_event(#event{type = node_stats, props = Stats0, timestamp = Timestamp}, + State) -> + Stats = proplists:delete(persister_stats, Stats0) ++ + pget(persister_stats, Stats0), + handle_stats(node_stats, Stats, Timestamp, [], ?COARSE_NODE_STATS, State); + +handle_event(#event{type = node_node_stats, props = Stats, + timestamp = Timestamp}, State) -> + handle_stats(node_node_stats, Stats, Timestamp, [], ?COARSE_NODE_NODE_STATS, + State); + +handle_event(Event = #event{type = node_node_deleted, + props = [{route, Route}]}, State) -> + delete_samples(node_node_stats, Route, State), + handle_deleted(node_node_stats, Event, State); + +handle_event(_Event, _State) -> + ok. handle_created(TName, Stats, Funs, State = #state{tables = Tables}) -> Formatted = rabbit_mgmt_format:format(Stats, Funs), @@ -562,12 +624,18 @@ handle_created(TName, Stats, Funs, State = #state{tables = Tables}) -> pget(name, Stats)}), {ok, State}. -handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, +handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, State) -> + handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, [], State). + +handle_stats(TName, Stats, Timestamp, Funs, RatesKeys, NoAggRatesKeys, State = #state{tables = Tables, old_stats = OldTable}) -> Id = id(TName, Stats), IdSamples = {coarse, {TName, Id}}, OldStats = lookup_element(OldTable, IdSamples), - append_samples(Stats, Timestamp, OldStats, IdSamples, RatesKeys, State), + append_samples( + Stats, Timestamp, OldStats, IdSamples, RatesKeys, true, State), + append_samples( + Stats, Timestamp, OldStats, IdSamples, NoAggRatesKeys, false, State), StripKeys = [id_name(TName)] ++ RatesKeys ++ ?FINE_STATS_TYPES, Stats1 = [{K, V} || {K, V} <- Stats, not lists:member(K, StripKeys)], Stats2 = rabbit_mgmt_format:format(Stats1, Funs), @@ -631,7 +699,7 @@ handle_fine_stat(Id, Stats, Timestamp, OldStats, State) -> 0 -> Stats; _ -> [{deliver_get, Total}|Stats] end, - append_samples(Stats1, Timestamp, OldStats, {fine, Id}, all, State). + append_samples(Stats1, Timestamp, OldStats, {fine, Id}, all, true, State). delete_samples(Type, {Id, '_'}, State) -> delete_samples_with_index(Type, Id, fun forward/2, State); @@ -655,7 +723,7 @@ reverse(A, B) -> {B, A}. delete_match(Type, Id) -> {{{Type, Id}, '_'}, '_'}. -append_samples(Stats, TS, OldStats, Id, Keys, +append_samples(Stats, TS, OldStats, Id, Keys, Agg, State = #state{old_stats = OldTable}) -> case ignore_coarse_sample(Id, State) of false -> @@ -663,22 +731,26 @@ append_samples(Stats, TS, OldStats, Id, Keys, %% queue_deleted NewMS = ceil(TS, State), case Keys of - all -> [append_sample(Key, Value, NewMS, OldStats, Id, State) - || {Key, Value} <- Stats]; - _ -> [append_sample( - Key, pget(Key, Stats), NewMS, OldStats, Id, State) - || Key <- Keys] + all -> [append_sample(K, V, NewMS, OldStats, Id, Agg, State) + || {K, V} <- Stats]; + _ -> [append_sample(K, V, NewMS, OldStats, Id, Agg, State) + || K <- Keys, + V <- [pget(K, Stats)], + V =/= 0 orelse lists:member(K, ?ALWAYS_REPORT_STATS)] end, ets:insert(OldTable, {Id, Stats}); true -> ok end. -append_sample(Key, Value, NewMS, OldStats, Id, State) when is_number(Value) -> - record_sample( - Id, {Key, Value - pget(Key, OldStats, 0), NewMS, State}, State); - -append_sample(_Key, _Value, _NewMS, _OldStats, _Id, _State) -> +append_sample(Key, Val, NewMS, OldStats, Id, Agg, State) when is_number(Val) -> + OldVal = case pget(Key, OldStats, 0) of + N when is_number(N) -> N; + _ -> 0 + end, + record_sample(Id, {Key, Val - OldVal, NewMS, State}, Agg, State), + ok; +append_sample(_Key, _Value, _NewMS, _OldStats, _Id, _Agg, _State) -> ok. ignore_coarse_sample({coarse, {queue_stats, Q}}, State) -> @@ -686,12 +758,22 @@ ignore_coarse_sample({coarse, {queue_stats, Q}}, State) -> ignore_coarse_sample(_, _) -> false. -record_sample({coarse, Id}, Args, State) -> +%% Node stats do not have a vhost of course +record_sample({coarse, {node_stats, _Node} = Id}, Args, true, _State) -> + record_sample0(Id, Args); + +record_sample({coarse, {node_node_stats, _Names} = Id}, Args, true, _State) -> + record_sample0(Id, Args); + +record_sample({coarse, Id}, Args, false, _State) -> + record_sample0(Id, Args); + +record_sample({coarse, Id}, Args, true, State) -> record_sample0(Id, Args), record_sample0({vhost_stats, vhost(Id, State)}, Args); %% Deliveries / acks (Q -> Ch) -record_sample({fine, {Ch, Q = #resource{kind = queue}}}, Args, State) -> +record_sample({fine, {Ch, Q = #resource{kind = queue}}}, Args, true, State) -> case object_exists(Q, State) of true -> record_sample0({channel_queue_stats, {Ch, Q}}, Args), record_sample0({queue_stats, Q}, Args); @@ -701,7 +783,7 @@ record_sample({fine, {Ch, Q = #resource{kind = queue}}}, Args, State) -> record_sample0({vhost_stats, vhost(Q)}, Args); %% Publishes / confirms (Ch -> X) -record_sample({fine, {Ch, X = #resource{kind = exchange}}}, Args, State) -> +record_sample({fine, {Ch, X = #resource{kind = exchange}}}, Args, true,State) -> case object_exists(X, State) of true -> record_sample0({channel_exchange_stats, {Ch, X}}, Args), record_sampleX(publish_in, X, Args); @@ -713,7 +795,7 @@ record_sample({fine, {Ch, X = #resource{kind = exchange}}}, Args, State) -> %% Publishes (but not confirms) (Ch -> X -> Q) record_sample({fine, {_Ch, Q = #resource{kind = queue}, - X = #resource{kind = exchange}}}, Args, State) -> + X = #resource{kind = exchange}}}, Args, true, State) -> %% TODO This one logically feels like it should be here. It would %% correspond to "publishing channel message rates to queue" - %% which would be nice to handle - except we don't. And just @@ -770,6 +852,11 @@ record_sampleX(RenamePublishTo, X, {publish, Diff, TS, State}) -> record_sampleX(_RenamePublishTo, X, {Type, Diff, TS, State}) -> record_sample0({exchange_stats, X}, {Type, Diff, TS, State}). +%% Ignore case where ID1 and ID2 are in a tuple, i.e. detailed stats, +%% when in basic mode +record_sample0({Type, {_ID1, _ID2}}, {_, _, _, #state{rates_mode = basic}}) + when Type =/= node_node_stats -> + ok; record_sample0(Id0, {Key, Diff, TS, #state{aggregated_stats = ETS, aggregated_stats_index = ETSi}}) -> Id = {Id0, Key}, @@ -802,6 +889,9 @@ record_sample0(Id0, {Key, Diff, TS, #state{aggregated_stats = ETS, {channel_stats, [{publishes, channel_exchange_stats, fun first/1}, {deliveries, channel_queue_stats, fun first/1}]}). +-define(NODE_DETAILS, + {node_stats, [{cluster_links, node_node_stats, fun first/1}]}). + first(Id) -> {Id, '$1'}. second(Id) -> {'$1', Id}. @@ -853,13 +943,23 @@ detail_channel_stats(Ranges, Objs, State) -> vhost_stats(Ranges, Objs, State) -> merge_stats(Objs, [simple_stats_fun(Ranges, vhost_stats, State)]). -node_stats(Objs, State) -> - merge_stats(Objs, [basic_stats_fun(node_stats, State)]). +node_stats(Ranges, Objs, State) -> + merge_stats(Objs, [basic_stats_fun(node_stats, State), + simple_stats_fun(Ranges, node_stats, State), + detail_and_basic_stats_fun( + node_node_stats, Ranges, ?NODE_DETAILS, State)]). merge_stats(Objs, Funs) -> - [lists:foldl(fun (Fun, Props) -> Fun(Props) ++ Props end, Obj, Funs) + [lists:foldl(fun (Fun, Props) -> combine(Fun(Props), Props) end, Obj, Funs) || Obj <- Objs]. +combine(New, Old) -> + case pget(state, Old) of + unknown -> New ++ Old; + live -> New ++ proplists:delete(state, Old); + _ -> proplists:delete(state, New) ++ Old + end. + %% i.e. the non-calculated stats basic_stats_fun(Type, #state{tables = Tables}) -> Table = orddict:fetch(Type, Tables), @@ -884,6 +984,28 @@ detail_stats_fun(Ranges, {IdType, FineSpecs}, State) -> || {Name, AggregatedStatsType, IdFun} <- FineSpecs] end. +%% This does not quite do the same as detail_stats_fun + +%% basic_stats_fun; the basic part here assumes compound keys (like +%% detail stats) but non-calculated (like basic stats). Currently the +%% only user of that is node-node stats. +%% +%% We also assume that FineSpecs is single length here (at [1]). +detail_and_basic_stats_fun(Type, Ranges, {IdType, FineSpecs}, + State = #state{tables = Tables}) -> + Table = orddict:fetch(Type, Tables), + F = detail_stats_fun(Ranges, {IdType, FineSpecs}, State), + fun (Props) -> + Id = id_lookup(IdType, Props), + BasicStatsRaw = ets:match(Table, {{{Id, '$1'}, stats}, '$2', '_'}), + BasicStatsDict = dict:from_list([{K, V} || [K,V] <- BasicStatsRaw]), + [{K, Items}] = F(Props), %% [1] + Items2 = [case dict:find(id_lookup(IdType, Item), BasicStatsDict) of + {ok, BasicStats} -> BasicStats ++ Item; + error -> Item + end || Item <- Items], + [{K, Items2}] + end. + read_simple_stats(Type, Id, #state{aggregated_stats = ETS}) -> FromETS = ets:match(ETS, {{{Type, Id}, '$1'}, '$2'}), [{K, V} || [K, V] <- FromETS]. @@ -903,7 +1025,7 @@ read_detail_stats(Type, Id, #state{aggregated_stats = ETS}) -> end, [], FromETS). extract_msg_stats(Stats) -> - FineStats = lists:append([[K, details_key(K)] || K <- ?FINE_STATS]), + FineStats = lists:append([[K, details_key(K)] || K <- ?MSG_RATES]), {MsgStats, Other} = lists:partition(fun({K, _}) -> lists:member(K, FineStats) end, Stats), case MsgStats of @@ -920,12 +1042,14 @@ format_detail_id(ChPid, State) when is_pid(ChPid) -> augment_msg_stats([{channel, ChPid}], State); format_detail_id(#resource{name = Name, virtual_host = Vhost, kind = Kind}, _State) -> - [{Kind, [{name, Name}, {vhost, Vhost}]}]. + [{Kind, [{name, Name}, {vhost, Vhost}]}]; +format_detail_id(Node, _State) when is_atom(Node) -> + [{name, Node}]. format_samples(Ranges, ManyStats, #state{interval = Interval}) -> lists:append( [case rabbit_mgmt_stats:is_blank(Stats) andalso - not lists:member(K, ?COARSE_QUEUE_STATS) of + not lists:member(K, ?ALWAYS_REPORT_STATS) of true -> []; false -> {Details, Counter} = rabbit_mgmt_stats:format( pick_range(K, Ranges), @@ -934,13 +1058,16 @@ format_samples(Ranges, ManyStats, #state{interval = Interval}) -> {details_key(K), Details}] end || {K, Stats} <- ManyStats]). -pick_range(K, {RangeL, RangeM, RangeD}) -> - case {lists:member(K, ?COARSE_QUEUE_STATS), - lists:member(K, ?FINE_STATS), - lists:member(K, ?COARSE_CONN_STATS)} of - {true, false, false} -> RangeL; - {false, true, false} -> RangeM; - {false, false, true} -> RangeD +pick_range(K, {RangeL, RangeM, RangeD, RangeN}) -> + case {lists:member(K, ?QUEUE_MSG_COUNTS), + lists:member(K, ?MSG_RATES), + lists:member(K, ?COARSE_CONN_STATS), + lists:member(K, ?COARSE_NODE_STATS) + orelse lists:member(K, ?COARSE_NODE_NODE_STATS)} of + {true, false, false, false} -> RangeL; + {false, true, false, false} -> RangeM; + {false, false, true, false} -> RangeD; + {false, false, false, true} -> RangeN end. %% We do this when retrieving the queue record rather than when @@ -1027,6 +1154,7 @@ augment_channel_pid(Pid, #state{tables = Tables}) -> {pget(connection, Ch), create}), [{name, pget(name, Ch)}, {number, pget(number, Ch)}, + {user, pget(user, Ch)}, {connection_name, pget(name, Conn)}, {peer_port, pget(peer_port, Conn)}, {peer_host, pget(peer_host, Conn)}]. @@ -1052,14 +1180,14 @@ gc_batch(State = #state{aggregated_stats = ETS}) -> gc_batch(0, _Policies, State) -> State; gc_batch(Rows, Policies, State = #state{aggregated_stats = ETS, - gc_next_key = Key0}) -> + gc_next_key = Key0}) -> Key = case Key0 of undefined -> ets:first(ETS); _ -> ets:next(ETS, Key0) end, Key1 = case Key of '$end_of_table' -> undefined; - _ -> Now = floor(erlang:now(), State), + _ -> Now = floor(os:timestamp(), State), Stats = ets:lookup_element(ETS, Key, 2), gc(Key, Stats, Policies, Now, ETS), Key @@ -1073,6 +1201,8 @@ gc({{Type, Id}, Key}, Stats, Policies, Now, ETS) -> Stats2 -> ets:insert(ETS, {{{Type, Id}, Key}, Stats2}) end. +retention_policy(node_stats) -> global; +retention_policy(node_node_stats) -> global; retention_policy(vhost_stats) -> global; retention_policy(queue_stats) -> basic; retention_policy(exchange_stats) -> basic; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_dispatcher.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_dispatcher.erl index bcfcfc6..e42635e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_dispatcher.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_dispatcher.erl @@ -11,24 +11,25 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_dispatcher). --export([modules/0, build_dispatcher/0]). +-export([modules/1, build_dispatcher/1]). -behaviour(rabbit_mgmt_extension). -export([dispatcher/0, web_ui/0]). -build_dispatcher() -> +build_dispatcher(Ignore) -> [{["api" | Path], Mod, Args} || {Path, Mod, Args} <- - lists:append([Module:dispatcher() || Module <- modules()])]. + lists:append([Module:dispatcher() || Module <- modules(Ignore)])]. -modules() -> - [Module || {Module, Behaviours} <- +modules(IgnoreApps) -> + [Module || {App, Module, Behaviours} <- rabbit_misc:all_module_attributes(behaviour), + not lists:member(App, IgnoreApps), lists:member(rabbit_mgmt_extension, Behaviours)]. %%---------------------------------------------------------------------------- @@ -55,6 +56,8 @@ dispatcher() -> {["connections", connection, "channels"], rabbit_mgmt_wm_connection_channels, []}, {["channels"], rabbit_mgmt_wm_channels, []}, {["channels", channel], rabbit_mgmt_wm_channel, []}, + {["consumers"], rabbit_mgmt_wm_consumers, []}, + {["consumers", vhost], rabbit_mgmt_wm_consumers, []}, {["exchanges"], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost, exchange], rabbit_mgmt_wm_exchange, []}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_extension.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_extension.erl index 5ecc38d..b558c40 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_extension.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_extension.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_extension). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_format.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_format.erl index 487ff86..b16d131 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_format.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_format.erl @@ -11,13 +11,13 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_format). -export([format/2, print/2, remove/1, ip/1, ipb/1, amqp_table/1, tuple/1]). --export([parameter/1, timestamp/1, timestamp_ms/1, strip_pids/1]). +-export([parameter/1, now_to_str/1, now_to_str_ms/1, now_to_ms/1, strip_pids/1]). -export([node_from_pid/1, protocol/1, resource/1, queue/1, queue_state/1]). -export([exchange/1, user/1, internal_user/1, binding/1, url/2]). -export([pack_binding_props/2, tokenise/1]). @@ -124,17 +124,22 @@ protocol_version({Major, Minor, 0}) -> protocol_version({Major, Minor}); protocol_version({Major, Minor, Revision}) -> io_lib:format("~B-~B-~B", [Major, Minor, Revision]). -timestamp_ms(unknown) -> +now_to_ms(unknown) -> unknown; -timestamp_ms(Timestamp) -> - timer:now_diff(Timestamp, {0,0,0}) div 1000. +now_to_ms(Now) -> + timer:now_diff(Now, {0,0,0}) div 1000. -timestamp(unknown) -> +now_to_str(unknown) -> unknown; -timestamp(Timestamp) -> - {{Y, M, D}, {H, Min, S}} = calendar:now_to_local_time(Timestamp), +now_to_str(Now) -> + {{Y, M, D}, {H, Min, S}} = calendar:now_to_local_time(Now), print("~w-~2.2.0w-~2.2.0w ~w:~2.2.0w:~2.2.0w", [Y, M, D, H, Min, S]). +now_to_str_ms(unknown) -> + unknown; +now_to_str_ms(Now = {_, _, Micro}) -> + print("~s:~3.3.0w", [now_to_str(Now), Micro div 1000]). + resource(unknown) -> unknown; resource(Res) -> resource(name, Res). @@ -152,9 +157,8 @@ internal_user(User) -> {tags, tags(User#internal_user.tags)}]. user(User) -> - [{name, User#user.username}, - {tags, tags(User#user.tags)}, - {auth_backend, User#user.auth_backend}]. + [{name, User#user.username}, + {tags, tags(User#user.tags)}]. tags(Tags) -> list_to_binary(string:join([atom_to_list(T) || T <- Tags], ",")). @@ -227,14 +231,16 @@ queue(#amqqueue{name = Name, auto_delete = AutoDelete, exclusive_owner = ExclusiveOwner, arguments = Arguments, - pid = Pid}) -> + pid = Pid, + state = State}) -> format( [{name, Name}, {durable, Durable}, {auto_delete, AutoDelete}, {owner_pid, ExclusiveOwner}, {arguments, Arguments}, - {pid, Pid}], + {pid, Pid}, + {state, State}], [{fun resource/1, [name]}, {fun amqp_table/1, [arguments]}, {fun policy/1, [policy]}]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_load_definitions.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_load_definitions.erl index 95fadfe..d12f545 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_load_definitions.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_load_definitions.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_load_definitions). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_reset_handler.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_reset_handler.erl new file mode 100644 index 0000000..e16351f --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_reset_handler.erl @@ -0,0 +1,86 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License at +%% http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +%% License for the specific language governing rights and limitations +%% under the License. +%% +%% The Original Code is RabbitMQ Management Console. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. +%% + +%% When management extensions are enabled and/or disabled at runtime, the +%% management web dispatch mechanism needs to be reset. This event handler +%% deals with responding to 'plugins_changed' events for management +%% extensions, forcing a reset when necessary. + +-module(rabbit_mgmt_reset_handler). + +-include_lib("rabbit_common/include/rabbit.hrl"). + +-behaviour(gen_event). + +-export([init/1, handle_call/2, handle_event/2, handle_info/2, + terminate/2, code_change/3]). + +-rabbit_boot_step({?MODULE, + [{description, "management extension handling"}, + {mfa, {gen_event, add_handler, + [rabbit_event, ?MODULE, []]}}, + {cleanup, {gen_event, delete_handler, + [rabbit_event, ?MODULE, []]}}, + {requires, rabbit_event}, + {enables, recovery}]}). + +-import(rabbit_misc, [pget/2, pget/3]). + +%%---------------------------------------------------------------------------- + +init([]) -> + {ok, []}. + +handle_call(_Request, State) -> + {ok, not_understood, State}. + +handle_event(#event{type = plugins_changed, props = Details}, State) -> + Enabled = pget(enabled, Details), + Disabled = pget(disabled, Details), + case extensions_changed(Enabled ++ Disabled) of + true -> rabbit_mgmt_app:reset_dispatcher(Disabled); + false -> ok + end, + {ok, State}; + +handle_event(_Event, State) -> + {ok, State}. + +handle_info(_Info, State) -> + {ok, State}. + +terminate(_Arg, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%---------------------------------------------------------------------------- + +%% We explicitly ignore the case where management has been +%% started/stopped since the dispatcher is either freshly created or +%% about to vanish. +extensions_changed(Apps) -> + not lists:member(rabbitmq_management, Apps) andalso + lists:any(fun is_extension/1, [Mod || App <- Apps, Mod <- mods(App)]). + +is_extension(Mod) -> + lists:member(rabbit_mgmt_extension, + pget(behaviour, Mod:module_info(attributes), [])). + +mods(App) -> + {ok, Modules} = application:get_key(App, modules), + Modules. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_stats.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_stats.erl index 74a4cc9..3e0c8a2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_stats.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_stats.erl @@ -45,7 +45,7 @@ record(TS, Diff, Stats = #stats{diffs = Diffs}) -> %%---------------------------------------------------------------------------- format(no_range, #stats{diffs = Diffs, base = Base}, Interval) -> - Now = rabbit_mgmt_format:timestamp_ms(erlang:now()), + Now = rabbit_mgmt_format:now_to_ms(os:timestamp()), RangePoint = ((Now div Interval) * Interval) - Interval, Count = sum_entire_tree(gb_trees:iterator(Diffs), Base), {[{rate, format_rate( diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup.erl index 008dd58..992ff72 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup_sup.erl index d83fb3b..6758a5d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_sup_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_util.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_util.erl index ad45092..2a15101 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_util). @@ -28,8 +28,9 @@ -export([with_channel/4, with_channel/5]). -export([props_to_method/2, props_to_method/4]). -export([all_or_one_vhost/2, http_to_amqp/5, reply/3, filter_vhost/3]). --export([filter_conn_ch_list/3, filter_user/2, list_login_vhosts/1]). --export([with_decode/5, decode/1, decode/2, redirect/2, args/1]). +-export([filter_conn_ch_list/3, filter_user/2, list_login_vhosts/2]). +-export([with_decode/5, decode/1, decode/2, redirect/2, set_resp_header/3, + args/1]). -export([reply_list/3, reply_list/4, sort_list/2, destination_type/1]). -export([post_respond/1, columns/1, is_monitor/1]). -export([list_visible_vhosts/1, b64decode_or_throw/1, no_range/0, range/1, @@ -40,6 +41,9 @@ -include("rabbit_mgmt.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). +-include_lib("webmachine/include/wm_reqdata.hrl"). +-include_lib("webmachine/include/wm_reqstate.hrl"). + -define(FRAMING, rabbit_framing_amqp_0_9_1). %%-------------------------------------------------------------------- @@ -73,7 +77,7 @@ user_matches_vhost(ReqData, User) -> case vhost(ReqData) of not_found -> true; none -> true; - V -> lists:member(V, list_login_vhosts(User)) + V -> lists:member(V, list_login_vhosts(User, peersock(ReqData))) end. %% Used for connections / channels. A normal user can only see / delete @@ -116,11 +120,7 @@ is_authorized(ReqData, Context, Username, Password, ErrorMsg, Fun) -> end, case rabbit_access_control:check_user_pass_login(Username, Password) of {ok, User = #user{tags = Tags}} -> - IPStr = wrq:peer(ReqData), - %% inet_parse:address/1 is an undocumented function but - %% exists in old versions of Erlang. inet:parse_address/1 - %% is a documented wrapper round it but introduced in R16B. - {ok, IP} = inet_parse:address(IPStr), + IP = peer(ReqData), case rabbit_access_control:check_user_loopback(Username, IP) of ok -> case is_mgmt_user(Tags) of @@ -137,12 +137,26 @@ is_authorized(ReqData, Context, Username, Password, ErrorMsg, Fun) -> not_allowed -> ErrFun(<<"User can only log in via localhost">>) end; - {refused, Msg, Args} -> + {refused, _Username, Msg, Args} -> rabbit_log:warning("HTTP access denied: ~s~n", [rabbit_misc:format(Msg, Args)]), not_authorised(<<"Login failed">>, ReqData, Context) end. +peer(ReqData) -> + {ok, {IP,_Port}} = peername(peersock(ReqData)), + IP. + +%% We can't use wrq:peer/1 because that trusts X-Forwarded-For. +peersock(ReqData) -> + WMState = ReqData#wm_reqdata.wm_state, + WMState#wm_reqstate.socket. + +%% Like the one in rabbit_net, but we and webmachine have a different +%% way of wrapping +peername(Sock) when is_port(Sock) -> inet:peername(Sock); +peername({ssl, SSL}) -> ssl:peername(SSL). + vhost(ReqData) -> case id(vhost, ReqData) of none -> none; @@ -162,7 +176,7 @@ reply(Facts, ReqData, Context) -> reply0(extract_columns(Facts, ReqData), ReqData, Context). reply0(Facts, ReqData, Context) -> - ReqData1 = wrq:set_resp_header("Cache-Control", "no-cache", ReqData), + ReqData1 = set_resp_header("Cache-Control", "no-cache", ReqData), try {mochijson2:encode(Facts), ReqData1, Context} catch exit:{json_encode, E} -> @@ -441,6 +455,8 @@ with_channel(VHost, ReqData, end; {error, {auth_failure, Msg}} -> not_authorised(Msg, ReqData, Context); + {error, access_refused} -> + not_authorised(<<"Access refused.">>, ReqData, Context); {error, {nodedown, N}} -> bad_request( list_to_binary( @@ -459,8 +475,8 @@ all_or_one_vhost(ReqData, Fun) -> VHost -> Fun(VHost) end. -filter_vhost(List, _ReqData, Context) -> - VHosts = list_login_vhosts(Context#context.user), +filter_vhost(List, ReqData, Context) -> + VHosts = list_login_vhosts(Context#context.user, peersock(ReqData)), [I || I <- List, lists:member(pget(vhost, I), VHosts)]. filter_user(List, _ReqData, #context{user = User}) -> @@ -482,8 +498,13 @@ filter_conn_ch_list(List, ReqData, Context) -> redirect(Location, ReqData) -> wrq:do_redirect(true, - wrq:set_resp_header("Location", - binary_to_list(Location), ReqData)). + set_resp_header("Location", + binary_to_list(Location), ReqData)). + +set_resp_header(K, V, ReqData) -> + wrq:set_resp_header(K, strip_crlf(V), ReqData). + +strip_crlf(Str) -> lists:append(string:tokens(Str, "\r\n")). args({struct, L}) -> args(L); args(L) -> rabbit_mgmt_format:to_amqp_table(L). @@ -494,8 +515,8 @@ post_respond({true, ReqData, Context}) -> post_respond({{halt, Code}, ReqData, Context}) -> {{halt, Code}, ReqData, Context}; post_respond({JSON, ReqData, Context}) -> - {true, wrq:set_resp_header( - "content-type", "application/json", + {true, set_resp_header( + "Content-Type", "application/json", wrq:append_to_response_body(JSON, ReqData)), Context}. is_admin(T) -> intersects(T, [administrator]). @@ -517,12 +538,12 @@ intersects(A, B) -> lists:any(fun(I) -> lists:member(I, B) end, A). list_visible_vhosts(User = #user{tags = Tags}) -> case is_monitor(Tags) of true -> rabbit_vhost:list(); - false -> list_login_vhosts(User) + false -> list_login_vhosts(User, undefined) end. -list_login_vhosts(User) -> +list_login_vhosts(User, Sock) -> [V || V <- rabbit_vhost:list(), - case catch rabbit_access_control:check_vhost_access(User, V) of + case catch rabbit_access_control:check_vhost_access(User, V, Sock) of ok -> true; _ -> false end]. @@ -536,25 +557,33 @@ b64decode_or_throw(B64) -> throw({error, {not_base64, B64}}) end. -no_range() -> {no_range, no_range, no_range}. +no_range() -> {no_range, no_range, no_range, no_range}. %% Take floor on queries so we make sure we only return samples %% for which we've finished receiving events. Fixes the "drop at %% the end" problem. range(ReqData) -> {range("lengths", fun floor/2, ReqData), range("msg_rates", fun floor/2, ReqData), - range("data_rates", fun floor/2, ReqData)}. + range("data_rates", fun floor/2, ReqData), + range("node_stats", fun floor/2, ReqData)}. %% ...but if we know only one event could have contributed towards %% what we are interested in, then let's take the ceiling instead and -%% get slightly fresher data. +%% get slightly fresher data that will match up with any +%% non-historical data we have (e.g. queue length vs queue messages in +%% RAM, they should both come from the same snapshot or we might +%% report more messages in RAM than total). %% -%% Why does msg_rates still use floor/2? Because in the cases where we -%% call this function (for connections and queues) the msg_rates are still -%% aggregated even though the lengths and data rates aren't. +%% However, we only do this for queue lengths since a) it's the only +%% thing where this ends up being really glaring and b) for other +%% numbers we care more about the rate than the absolute value, and if +%% we use ceil() we stand a 50:50 chance of looking up the last sample +%% in the range before we get it, and thus deriving an instantaneous +%% rate of 0.0. range_ceil(ReqData) -> {range("lengths", fun ceil/2, ReqData), range("msg_rates", fun floor/2, ReqData), - range("data_rates", fun ceil/2, ReqData)}. + range("data_rates", fun floor/2, ReqData), + range("node_stats", fun floor/2, ReqData)}. range(Prefix, Round, ReqData) -> Age0 = int(Prefix ++ "_age", ReqData), @@ -563,7 +592,7 @@ range(Prefix, Round, ReqData) -> is_integer(Age0) andalso is_integer(Incr0) -> Age = Age0 * 1000, Incr = Incr0 * 1000, - Now = rabbit_mgmt_format:timestamp_ms(erlang:now()), + Now = rabbit_mgmt_format:now_to_ms(os:timestamp()), Last = Round(Now, Incr), #range{first = (Last - Age), last = Last, diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_aliveness_test.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_aliveness_test.erl index 445eee5..fec6196 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_aliveness_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_aliveness_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_aliveness_test). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_binding.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_binding.erl index d794558..16e42ae 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_binding.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_binding.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_binding). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_bindings.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_bindings.erl index c452038..4f80778 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_bindings.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_bindings.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_bindings). @@ -81,8 +81,8 @@ accept_content(ReqData, {_Mode, Context}) -> "/api/bindings/~s/e/~s/~s/~s/~s", [VHost, Source, DestType, Dest, rabbit_mgmt_format:pack_binding_props(Key, Args)]))), - ReqData2 = wrq:set_resp_header("Location", Loc, ReqData), - {true, ReqData2, Context2} + {true, rabbit_mgmt_util:set_resp_header("Location", Loc, ReqData), + Context2} end. is_authorized(ReqData, {Mode, Context}) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channel.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channel.erl index 67e584f..6410433 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channel.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channel.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_channel). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channels.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channels.erl index 0274c8d..6675fa2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channels.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_channels.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_channels). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_cluster_name.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_cluster_name.erl index e3d2c43..f741821 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_cluster_name.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_cluster_name.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_cluster_name). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection.erl index 7f918b1..523bec2 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_connection). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection_channels.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection_channels.erl index c15b977..9becc91 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection_channels.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connection_channels.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_connection_channels). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connections.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connections.erl index 85689eb..1263d91 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connections.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_connections.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_connections). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_consumers.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_consumers.erl new file mode 100644 index 0000000..3d791d0 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_consumers.erl @@ -0,0 +1,56 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License at +%% http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +%% License for the specific language governing rights and limitations +%% under the License. +%% +%% The Original Code is RabbitMQ Management Plugin. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. + +-module(rabbit_mgmt_wm_consumers). + +-export([init/1, to_json/2, content_types_provided/2, resource_exists/2, + is_authorized/2]). + +-import(rabbit_misc, [pget/2]). + +-include("rabbit_mgmt.hrl"). +-include_lib("webmachine/include/webmachine.hrl"). +-include_lib("rabbit_common/include/rabbit.hrl"). + +%%-------------------------------------------------------------------- + +init(_Config) -> {ok, #context{}}. + +content_types_provided(ReqData, Context) -> + {[{"application/json", to_json}], ReqData, Context}. + +resource_exists(ReqData, Context) -> + {case rabbit_mgmt_util:vhost(ReqData) of + vhost_not_found -> false; + _ -> true + end, ReqData, Context}. + +to_json(ReqData, Context = #context{user = User}) -> + Consumers = case rabbit_mgmt_util:vhost(ReqData) of + none -> rabbit_mgmt_db:get_all_consumers(); + VHost -> rabbit_mgmt_db:get_all_consumers(VHost) + end, + rabbit_mgmt_util:reply_list( + filter_user(Consumers, User), ReqData, Context). + +is_authorized(ReqData, Context) -> + rabbit_mgmt_util:is_authorized(ReqData, Context). + +filter_user(List, #user{username = Username, tags = Tags}) -> + case rabbit_mgmt_util:is_monitor(Tags) of + true -> List; + false -> [I || I <- List, + pget(user, pget(channel_details, I)) == Username] + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_definitions.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_definitions.erl index d62f7c8..a33d6a1 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_definitions.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_definitions.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_definitions). @@ -69,7 +69,7 @@ to_json(ReqData, Context) -> {bindings, Bs}]), case wrq:get_qs_value("download", ReqData) of undefined -> ReqData; - Filename -> wrq:set_resp_header( + Filename -> rabbit_mgmt_util:set_resp_header( "Content-Disposition", "attachment; filename=" ++ mochiweb_util:unquote(Filename), ReqData) diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange.erl index 711756e..374280a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_exchange). @@ -55,9 +55,11 @@ accept_content(ReqData, Context) -> [{exchange, rabbit_mgmt_util:id(exchange, ReqData)}]). delete_resource(ReqData, Context) -> + IfUnused = "true" =:= wrq:get_qs_value("if-unused", ReqData), rabbit_mgmt_util:amqp_request( rabbit_mgmt_util:vhost(ReqData), ReqData, Context, - #'exchange.delete'{ exchange = id(ReqData) }). + #'exchange.delete'{exchange = id(ReqData), + if_unused = IfUnused}). is_authorized(ReqData, Context) -> rabbit_mgmt_util:is_authorized_vhost(ReqData, Context). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange_publish.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange_publish.erl index e1cc29e..467c055 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange_publish.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchange_publish.erl @@ -11,13 +11,13 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_exchange_publish). -export([init/1, resource_exists/2, post_is_create/2, is_authorized/2, - allowed_methods/2, process_post/2]). + allowed_methods/2, content_types_provided/2, process_post/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -29,6 +29,9 @@ init(_Config) -> {ok, #context{}}. allowed_methods(ReqData, Context) -> {['POST'], ReqData, Context}. +content_types_provided(ReqData, Context) -> + {[{"application/json", to_json}], ReqData, Context}. + resource_exists(ReqData, Context) -> {case rabbit_mgmt_wm_exchange:exchange(ReqData) of not_found -> false; diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchanges.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchanges.erl index f5cdf7d..f1df46a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchanges.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_exchanges.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_exchanges). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_extensions.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_extensions.erl index bb0f15e..b38bf1d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_extensions.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_extensions.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_extensions). @@ -29,7 +29,7 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. to_json(ReqData, Context) -> - Modules = rabbit_mgmt_dispatcher:modules(), + Modules = rabbit_mgmt_dispatcher:modules([]), rabbit_mgmt_util:reply( [Module:web_ui() || Module <- Modules], ReqData, Context). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_node.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_node.erl index 27522f6..646d183 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_node.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_node.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_node). @@ -45,19 +45,23 @@ is_authorized(ReqData, Context) -> %%-------------------------------------------------------------------- node0(ReqData) -> - Name = list_to_atom(binary_to_list(rabbit_mgmt_util:id(node, ReqData))), - case [N || N <- rabbit_mgmt_wm_nodes:all_nodes(), - proplists:get_value(name, N) == Name] of + Node = list_to_atom(binary_to_list(rabbit_mgmt_util:id(node, ReqData))), + case [N || N <- rabbit_mgmt_wm_nodes:all_nodes(ReqData), + proplists:get_value(name, N) == Node] of [] -> not_found; - [Node] -> augment(ReqData, Name, Node) + [Data] -> augment(ReqData, Node, Data) end. -augment(ReqData, Name, Node) -> - case wrq:get_qs_value("memory", ReqData) of - "true" -> Mem = case rpc:call(Name, rabbit_vm, memory, [], infinity) of +augment(ReqData, Node, Data) -> + lists:foldl(fun (Key, DataN) -> augment(Key, ReqData, Node, DataN) end, + Data, [memory, binary]). + +augment(Key, ReqData, Node, Data) -> + case wrq:get_qs_value(atom_to_list(Key), ReqData) of + "true" -> Res = case rpc:call(Node, rabbit_vm, Key, [], infinity) of {badrpc, _} -> not_available; - Memory -> Memory + Result -> Result end, - [{memory, Mem} | Node]; - _ -> Node + [{Key, Res} | Data]; + _ -> Data end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_nodes.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_nodes.erl index 0f5b532..fb86e1e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_nodes.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_nodes.erl @@ -11,13 +11,13 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_nodes). -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). --export([all_nodes/0]). +-export([all_nodes/1, all_nodes_raw/0]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -31,18 +31,21 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. to_json(ReqData, Context) -> - rabbit_mgmt_util:reply_list(all_nodes(), ReqData, Context). + rabbit_mgmt_util:reply_list(all_nodes(ReqData), ReqData, Context). is_authorized(ReqData, Context) -> rabbit_mgmt_util:is_authorized_monitor(ReqData, Context). %%-------------------------------------------------------------------- -all_nodes() -> +all_nodes(ReqData) -> + rabbit_mgmt_db:augment_nodes( + all_nodes_raw(), rabbit_mgmt_util:range_ceil(ReqData)). + +all_nodes_raw() -> S = rabbit_mnesia:status(), Nodes = proplists:get_value(nodes, S), Types = proplists:get_keys(Nodes), Running = proplists:get_value(running_nodes, S), - rabbit_mgmt_db:augment_nodes( - [[{name, Node}, {type, Type}, {running, lists:member(Node, Running)}] || - Type <- Types, Node <- proplists:get_value(Type, Nodes)]). + [[{name, Node}, {type, Type}, {running, lists:member(Node, Running)}] || + Type <- Types, Node <- proplists:get_value(Type, Nodes)]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_overview.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_overview.erl index db8f336..486016c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_overview.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_overview.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_overview). @@ -32,31 +32,31 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. to_json(ReqData, Context = #context{user = User = #user{tags = Tags}}) -> - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), + {ok, RatesMode} = application:get_env(rabbitmq_management, rates_mode), %% NB: this duplicates what's in /nodes but we want a global idea %% of this. And /nodes is not accessible to non-monitor users. ExchangeTypes = rabbit_mgmt_external_stats:list_registry_plugins(exchange), Overview0 = [{management_version, version(rabbitmq_management)}, - {statistics_level, StatsLevel}, + {rates_mode, RatesMode}, {exchange_types, ExchangeTypes}, {rabbitmq_version, version(rabbit)}, {cluster_name, rabbit_nodes:cluster_name()}, - {erlang_version, erl_version(otp_release)}, - {erlang_full_version, erl_version(system_version)}], + {erlang_version, erlang_version()}, + {erlang_full_version, erlang_full_version()}], Range = rabbit_mgmt_util:range(ReqData), Overview = case rabbit_mgmt_util:is_monitor(Tags) of true -> Overview0 ++ - [{K, {struct, V}} || - {K, V} <- rabbit_mgmt_db:get_overview(Range)] ++ + [{K, maybe_struct(V)} || + {K,V} <- rabbit_mgmt_db:get_overview(Range)] ++ [{node, node()}, {statistics_db_node, stats_db_node()}, {listeners, listeners()}, - {contexts, rabbit_web_dispatch_contexts()}]; + {contexts, web_contexts(ReqData)}]; _ -> Overview0 ++ - [{K, {struct, V}} || + [{K, maybe_struct(V)} || {K, V} <- rabbit_mgmt_db:get_overview(User, Range)] end, rabbit_mgmt_util:reply(Overview, ReqData, Context). @@ -82,16 +82,21 @@ listeners() -> || L <- rabbit_networking:active_listeners()], ["protocol", "port", "node"] ). +maybe_struct(L) when is_list(L) -> {struct, L}; +maybe_struct(V) -> V. + %%-------------------------------------------------------------------- -rabbit_web_dispatch_contexts() -> +web_contexts(ReqData) -> rabbit_mgmt_util:sort_list( lists:append( - [rabbit_web_dispatch_contexts(N) || N <- rabbit_mgmt_wm_nodes:all_nodes()]), + [fmt_contexts(N) || N <- rabbit_mgmt_wm_nodes:all_nodes(ReqData)]), ["description", "port", "node"]). -rabbit_web_dispatch_contexts(N) -> +fmt_contexts(N) -> [[{node, pget(name, N)} | C] || C <- pget(contexts, N, [])]. -erl_version(K) -> - list_to_binary(string:strip(erlang:system_info(K), both, $\n)). +erlang_version() -> list_to_binary(rabbit_misc:otp_release()). + +erlang_full_version() -> + list_to_binary(string:strip(erlang:system_info(system_version), both, $\n)). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameter.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameter.erl index b266542..5af5d38 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameter.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameter.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_parameter). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameters.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameters.erl index c2f1e1b..0664baf 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameters.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_parameters.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_parameters). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permission.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permission.erl index 55eca61..ec1046b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permission.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permission.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_permission). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions.erl index 456bbfb..52f4771 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_permissions). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_user.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_user.erl index 3428033..df19045 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_user.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_user.erl @@ -11,12 +11,13 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_permissions_user). --export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). +-export([init/1, to_json/2, content_types_provided/2, resource_exists/2, + is_authorized/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -29,6 +30,12 @@ init(_Config) -> {ok, #context{}}. content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. +resource_exists(ReqData, Context) -> + {case rabbit_mgmt_wm_user:user(ReqData) of + {ok, _} -> true; + {error, _} -> false + end, ReqData, Context}. + to_json(ReqData, Context) -> User = rabbit_mgmt_util:id(user, ReqData), Perms = rabbit_auth_backend_internal:list_user_permissions(User), diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_vhost.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_vhost.erl index 7472e96..062a902 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_vhost.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_permissions_vhost.erl @@ -11,12 +11,13 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_permissions_vhost). --export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). +-export([init/1, to_json/2, content_types_provided/2, resource_exists/2, + is_authorized/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -29,6 +30,9 @@ init(_Config) -> {ok, #context{}}. content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. +resource_exists(ReqData, Context) -> + {rabbit_vhost:exists(rabbit_mgmt_wm_vhost:id(ReqData)), ReqData, Context}. + to_json(ReqData, Context) -> VHost = rabbit_mgmt_util:id(vhost, ReqData), Perms = rabbit_auth_backend_internal:list_vhost_permissions(VHost), diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policies.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policies.erl index 74c9ee2..1236bcd 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policies.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policies.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_policies). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policy.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policy.erl index b22c369..fa9e1aa 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policy.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_policy.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_policy). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue.erl index 4c5a5a8..fb7e8ab 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_queue). @@ -58,7 +58,9 @@ delete_resource(ReqData, Context) -> rabbit_mgmt_util:amqp_request( rabbit_mgmt_util:vhost(ReqData), ReqData, Context, - #'queue.delete'{ queue = rabbit_mgmt_util:id(queue, ReqData) }). + #'queue.delete'{ queue = rabbit_mgmt_util:id(queue, ReqData), + if_empty = qs_true("if-empty", ReqData), + if_unused = qs_true("if-unused", ReqData) }). is_authorized(ReqData, Context) -> rabbit_mgmt_util:is_authorized_vhost(ReqData, Context). @@ -78,3 +80,5 @@ queue(VHost, QName) -> {ok, Q} -> rabbit_mgmt_format:queue(Q); {error, not_found} -> not_found end. + +qs_true(Key, ReqData) -> "true" =:= wrq:get_qs_value(Key, ReqData). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_get.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_get.erl index 1602457..8998bfc 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_get.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_get.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_queue_get). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_purge.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_purge.erl index 0d35113..42aeb95 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_purge.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queue_purge.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_queue_purge). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queues.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queues.erl index 58c3da0..51265c7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queues.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_queues.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_queues). @@ -51,7 +51,12 @@ augmented(ReqData, Context) -> rabbit_mgmt_util:range_ceil(ReqData), basic)). basic(ReqData) -> - [rabbit_mgmt_format:queue(Q) || Q <- queues0(ReqData)]. + [rabbit_mgmt_format:queue(Q) || Q <- queues0(ReqData)] ++ + [rabbit_mgmt_format:queue(Q#amqqueue{state = down}) || + Q <- down_queues(ReqData)]. queues0(ReqData) -> rabbit_mgmt_util:all_or_one_vhost(ReqData, fun rabbit_amqqueue:list/1). + +down_queues(ReqData) -> + rabbit_mgmt_util:all_or_one_vhost(ReqData, fun rabbit_amqqueue:list_down/1). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_user.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_user.erl index bf1c557..b8b6529 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_user.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_user.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_user). @@ -19,7 +19,7 @@ -export([init/1, resource_exists/2, to_json/2, content_types_provided/2, content_types_accepted/2, is_authorized/2, allowed_methods/2, accept_content/2, - delete_resource/2, put_user/1]). + delete_resource/2, user/1, put_user/1]). -import(rabbit_misc, [pget/2]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_users.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_users.erl index 013561b..7bf07bf 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_users.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_users.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhost.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhost.erl index 6c79064..0908d90 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhost.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhost.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_vhost). @@ -19,7 +19,7 @@ -export([init/1, resource_exists/2, to_json/2, content_types_provided/2, content_types_accepted/2, is_authorized/2, allowed_methods/2, accept_content/2, - delete_resource/2, put_vhost/2]). + delete_resource/2, id/1, put_vhost/2]). -import(rabbit_misc, [pget/2]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhosts.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhosts.erl index 54bf9ff..961b6f7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhosts.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_vhosts.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_vhosts). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_whoami.erl b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_whoami.erl index 0d75b04..564b394 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_whoami.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbit_mgmt_wm_whoami.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Plugin. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_wm_whoami). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbitmq_management.app.src b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbitmq_management.app.src index 37831f8..bf54277 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbitmq_management.app.src +++ b/rabbitmq-server/plugins-src/rabbitmq-management/src/rabbitmq_management.app.src @@ -7,8 +7,9 @@ {env, [{listener, [{port, 15672}]}, {http_log_dir, none}, {load_definitions, none}, + {rates_mode, basic}, {sample_retention_policies, - %% List of {MaxAgeSecs, IfTimestampDivisibleBySecs} + %% List of {MaxAgeInSeconds, SampleEveryNSeconds} [{global, [{605, 5}, {3660, 60}, {29400, 600}, {86400, 1800}]}, {basic, [{605, 5}, {3600, 60}]}, {detailed, [{10, 5}]}]} diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_db.erl b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_db.erl index ef2f5be..528ec33 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_db.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_db.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_test_db). @@ -204,11 +204,11 @@ delete_ch(Name, Timestamp) -> event(channel_closed, [{pid, pid_del(Name)}], Timestamp). event(Type, Stats, Timestamp) -> - gen_server:cast({global, rabbit_mgmt_db}, - {event, #event{type = Type, - props = Stats, - reference = none, - timestamp = sec_to_triple(Timestamp)}}). + ok = gen_server:call(rabbit_mgmt_db, + {event, #event{type = Type, + props = Stats, + reference = none, + timestamp = sec_to_triple(Timestamp)}}). sec_to_triple(Sec) -> {Sec div 1000000, Sec rem 1000000, 0}. @@ -218,7 +218,7 @@ sec_to_triple(Sec) -> {Sec div 1000000, Sec rem 1000000, 0}. range(F, L, I) -> R = #range{first = F * 1000, last = L * 1000, incr = I * 1000}, - {R, R, R}. + {R, R, R, R}. get_x(Name, Range) -> [X] = rabbit_mgmt_db:augment_exchanges([x2(Name)], Range, full), @@ -244,7 +244,7 @@ details0(R, AR, A, L) -> {avg_rate, AR}, {avg, A}]. -simple_details(Thing, N, {#range{first = First, last = Last}, _, _}) -> +simple_details(Thing, N, {#range{first = First, last = Last}, _, _, _}) -> [{Thing, N}, {atom_suffix(Thing, "_details"), details0(0.0, 0.0, N * 1.0, [{Last, N}, {First, N}])}]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_http.erl b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_http.erl index d794909..f56a330 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_http.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_http.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_test_http). @@ -174,6 +174,9 @@ permissions_list_test() -> 2 = length(http_get("/users/myuser1/permissions")), 1 = length(http_get("/users/myuser2/permissions")), + http_get("/users/notmyuser/permissions", ?NOT_FOUND), + http_get("/vhosts/notmyvhost/permissions", ?NOT_FOUND), + http_delete("/users/myuser1", ?NO_CONTENT), http_delete("/users/myuser2", ?NO_CONTENT), http_delete("/vhosts/myvhost1", ?NO_CONTENT), @@ -520,21 +523,25 @@ get_conn(Username, Password) -> [LocalPort]), {Conn, ConnPath, ChPath, ConnChPath}. -permissions_connection_channel_test() -> +permissions_connection_channel_consumer_test() -> PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}], http_put("/users/user", [{password, <<"user">>}, {tags, <<"management">>}], ?NO_CONTENT), http_put("/permissions/%2f/user", PermArgs, ?NO_CONTENT), http_put("/users/monitor", [{password, <<"monitor">>}, - {tags, <<"monitoring">>}], ?NO_CONTENT), + {tags, <<"monitoring">>}], ?NO_CONTENT), http_put("/permissions/%2f/monitor", PermArgs, ?NO_CONTENT), + http_put("/queues/%2f/test", [], ?NO_CONTENT), + {Conn1, UserConn, UserCh, UserConnCh} = get_conn("user", "user"), {Conn2, MonConn, MonCh, MonConnCh} = get_conn("monitor", "monitor"), {Conn3, AdmConn, AdmCh, AdmConnCh} = get_conn("guest", "guest"), - {ok, _Ch1} = amqp_connection:open_channel(Conn1), - {ok, _Ch2} = amqp_connection:open_channel(Conn2), - {ok, _Ch3} = amqp_connection:open_channel(Conn3), - + {ok, Ch1} = amqp_connection:open_channel(Conn1), + {ok, Ch2} = amqp_connection:open_channel(Conn2), + {ok, Ch3} = amqp_connection:open_channel(Conn3), + [amqp_channel:subscribe( + Ch, #'basic.consume'{queue = <<"test">>}, self()) || + Ch <- [Ch1, Ch2, Ch3]], AssertLength = fun (Path, User, Len) -> ?assertEqual(Len, length(http_get(Path, User, User, ?OK))) @@ -543,7 +550,7 @@ permissions_connection_channel_test() -> AssertLength(P, "user", 1), AssertLength(P, "monitor", 3), AssertLength(P, "guest", 3) - end || P <- ["/connections", "/channels"]], + end || P <- ["/connections", "/channels", "/consumers", "/consumers/%2f"]], AssertRead = fun(Path, UserStatus) -> http_get(Path, "user", "user", UserStatus), @@ -573,6 +580,22 @@ permissions_connection_channel_test() -> http_delete("/users/monitor", ?NO_CONTENT), http_get("/connections/foo", ?NOT_FOUND), http_get("/channels/foo", ?NOT_FOUND), + http_delete("/queues/%2f/test", ?NO_CONTENT), + ok. + +consumers_test() -> + http_put("/queues/%2f/test", [], ?NO_CONTENT), + {Conn, _ConnPath, _ChPath, _ConnChPath} = get_conn("guest", "guest"), + {ok, Ch} = amqp_connection:open_channel(Conn), + amqp_channel:subscribe( + Ch, #'basic.consume'{queue = <<"test">>, + no_ack = false, + consumer_tag = <<"my-ctag">> }, self()), + assert_list([[{exclusive, false}, + {ack_required, true}, + {consumer_tag, <<"my-ctag">>}]], http_get("/consumers")), + amqp_connection:close(Conn), + http_delete("/queues/%2f/test", ?NO_CONTENT), ok. defs(Key, URI, CreateMethod, Args) -> @@ -984,6 +1007,26 @@ publish_unrouted_test() -> ?assertEqual([{routed, false}], http_post("/exchanges/%2f/amq.default/publish", Msg, ?OK)). +if_empty_unused_test() -> + http_put("/exchanges/%2f/test", [], ?NO_CONTENT), + http_put("/queues/%2f/test", [], ?NO_CONTENT), + http_post("/bindings/%2f/e/test/q/test", [], ?CREATED), + http_post("/exchanges/%2f/amq.default/publish", + msg(<<"test">>, [], <<"Hello world">>), ?OK), + http_delete("/queues/%2f/test?if-empty=true", ?BAD_REQUEST), + http_delete("/exchanges/%2f/test?if-unused=true", ?BAD_REQUEST), + http_delete("/queues/%2f/test/contents", ?NO_CONTENT), + + {Conn, _ConnPath, _ChPath, _ConnChPath} = get_conn("guest", "guest"), + {ok, Ch} = amqp_connection:open_channel(Conn), + amqp_channel:subscribe(Ch, #'basic.consume'{queue = <<"test">> }, self()), + http_delete("/queues/%2f/test?if-unused=true", ?BAD_REQUEST), + amqp_connection:close(Conn), + + http_delete("/queues/%2f/test?if-empty=true", ?NO_CONTENT), + http_delete("/exchanges/%2f/test?if-unused=true", ?NO_CONTENT), + passed. + parameters_test() -> rabbit_runtime_parameters_test:register(), diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_unit.erl b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_unit.erl index a3ecc80..de71872 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_unit.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbit_mgmt_test_unit.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Management Console. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mgmt_test_unit). diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test-wrapper.sh b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test-wrapper.sh new file mode 100755 index 0000000..d684ec9 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test-wrapper.sh @@ -0,0 +1,27 @@ +#!/bin/sh -e +TWO=$(python2 -c 'import sys;print(sys.version_info[0])') +THREE=$(python3 -c 'import sys;print(sys.version_info[0])') + +if [ $TWO != 2 ] ; then + echo Python 2 not found! + exit 1 +fi + +if [ $THREE != 3 ] ; then + echo Python 3 not found! + exit 1 +fi + +echo +echo ---------------------- +echo Testing under Python 2 +echo ---------------------- + +python2 $(dirname $0)/rabbitmqadmin-test.py + +echo +echo ---------------------- +echo Testing under Python 3 +echo ---------------------- + +python3 $(dirname $0)/rabbitmqadmin-test.py diff --git a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test.py b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test.py index 8e1d310..470af56 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test.py +++ b/rabbitmq-server/plugins-src/rabbitmq-management/test/src/rabbitmqadmin-test.py @@ -156,12 +156,12 @@ tracing: False self.run_success(['declare', 'queue', 'name=test']) self.run_success(['publish', 'routing_key=test', 'payload=test_1']) self.run_success(['publish', 'routing_key=test', 'payload=test_2']) - self.run_success(['publish', 'routing_key=test'], stdin='test_3') + self.run_success(['publish', 'routing_key=test'], stdin=b'test_3') self.assert_table([exp_msg('test', 2, False, 'test_1')], ['get', 'queue=test', 'requeue=false']) self.assert_table([exp_msg('test', 1, False, 'test_2')], ['get', 'queue=test', 'requeue=true']) self.assert_table([exp_msg('test', 1, True, 'test_2')], ['get', 'queue=test', 'requeue=false']) self.assert_table([exp_msg('test', 0, False, 'test_3')], ['get', 'queue=test', 'requeue=false']) - self.run_success(['publish', 'routing_key=test'], stdin='test_4') + self.run_success(['publish', 'routing_key=test'], stdin=b'test_4') filename = '/tmp/rabbitmq-test/get.txt' self.run_success(['get', 'queue=test', 'requeue=false', 'payload_file=' + filename]) with open(filename) as f: @@ -212,24 +212,28 @@ tracing: False args.extend(args0) self.assertEqual(expected, [l.split('\t') for l in self.admin(args)[0].splitlines()]) - def admin(self, args, stdin=None): - return run('../../../bin/rabbitmqadmin', args, stdin) + def admin(self, args0, stdin=None): + args = ['python{0}'.format(sys.version_info[0]), + norm('../../../bin/rabbitmqadmin')] + args.extend(args0) + return run(args, stdin) def ctl(self, args0, stdin=None): - args = ['-n', 'rabbit-test'] + args = [norm('../../../../rabbitmq-server/scripts/rabbitmqctl'), '-n', 'rabbit-test'] args.extend(args0) - (stdout, ret) = run('../../../../rabbitmq-server/scripts/rabbitmqctl', args, stdin) + (stdout, ret) = run(args, stdin) if ret != 0: self.fail(stdout) -def run(cmd, args, stdin): - path = os.path.normpath(os.path.join(os.getcwd(), sys.argv[0], cmd)) - cmdline = [path] - cmdline.extend(args) - proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +def norm(cmd): + return os.path.normpath(os.path.join(os.getcwd(), sys.argv[0], cmd)) + +def run(args, stdin): + proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate(stdin) returncode = proc.returncode - return (stdout + stderr, returncode) + res = stdout.decode('utf-8') + stderr.decode('utf-8') + return (res, returncode) def l(thing): return ['list', thing, 'name'] @@ -239,6 +243,8 @@ def exp_msg(key, count, redelivered, payload): return [key, '', str(count), payload, str(len(payload)), 'string', '', str(redelivered)] if __name__ == '__main__': - print "\nrabbitmqadmin tests\n===================\n" + print("\nrabbitmqadmin tests\n===================\n") suite = unittest.TestLoader().loadTestsFromTestCase(TestRabbitMQAdmin) - unittest.TextTestRunner(verbosity=2).run(suite) + results = unittest.TextTestRunner(verbosity=2).run(suite) + if not results.wasSuccessful(): + sys.exit(1) diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-mqtt/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt.hrl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt.hrl index 8104c79..b620a31 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -define(CLIENT_ID_MAXLEN, 23). @@ -40,4 +40,5 @@ will_msg, channels, connection, - exchange }). + exchange, + ssl_login_name }). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt_frame.hrl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt_frame.hrl index 87f24d5..968f986 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt_frame.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/include/rabbit_mqtt_frame.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -define(PROTOCOL_NAMES, [{3, "MQIsdp"}, {4, "MQTT"}]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/package.mk b/rabbitmq-server/plugins-src/rabbitmq-mqtt/package.mk index e564fab..c5d5bcc 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/package.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/package.mk @@ -1,5 +1,19 @@ RELEASABLE:=true -DEPS:=rabbitmq-erlang-client - -RABBITMQ_TEST_PATH=$(PACKAGE_DIR)/../../rabbitmq-test +DEPS:=rabbitmq-server rabbitmq-erlang-client rabbitmq-test WITH_BROKER_TEST_SCRIPTS:=$(PACKAGE_DIR)/test/test.sh +WITH_BROKER_TEST_CONFIG:=$(PACKAGE_DIR)/test/ebin/test +WITH_BROKER_SETUP_SCRIPTS:=$(PACKAGE_DIR)/test/setup-rabbit-test.sh + +define package_rules + +$(PACKAGE_DIR)+pre-test:: + rm -rf $(PACKAGE_DIR)/test/certs + mkdir $(PACKAGE_DIR)/test/certs + mkdir -p $(PACKAGE_DIR)/test/ebin + sed -E -e "s|%%CERTS_DIR%%|$(abspath $(PACKAGE_DIR))/test/certs|g" < $(PACKAGE_DIR)/test/src/test.config > $(PACKAGE_DIR)/test/ebin/test.config + $(MAKE) -C $(PACKAGE_DIR)/../rabbitmq-test/certs all PASSWORD=bunnychow DIR=$(abspath $(PACKAGE_DIR))/test/certs + +$(PACKAGE_DIR)+clean:: + rm -rf $(PACKAGE_DIR)/test/certs + +endef diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt.erl index 25e191a..92c2916 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_collector.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_collector.erl index 26009ee..52a3da9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_collector.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_collector.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_collector). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_connection_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_connection_sup.erl index dd722c0..fd083a4 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_connection_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_connection_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_connection_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_frame.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_frame.erl index 39172c7..6282411 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_frame.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_frame.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_frame). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_processor.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_processor.erl index eb17673..8ab736c 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_processor.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_processor.erl @@ -11,12 +11,12 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_processor). --export([info/2, initial_state/1, +-export([info/2, initial_state/2, process_frame/2, amqp_pub/2, amqp_callback/2, send_will/1, close_connection/1]). @@ -24,10 +24,11 @@ -include("rabbit_mqtt_frame.hrl"). -include("rabbit_mqtt.hrl"). +-define(APP, rabbitmq_mqtt). -define(FRAME_TYPE(Frame, Type), Frame = #mqtt_frame{ fixed = #mqtt_frame_fixed{ type = Type }}). -initial_state(Socket) -> +initial_state(Socket,SSLLoginName) -> #proc_state{ unacked_pubs = gb_trees:empty(), awaiting_ack = gb_trees:empty(), message_id = 1, @@ -35,7 +36,8 @@ initial_state(Socket) -> consumer_tags = {undefined, undefined}, channels = {undefined, undefined}, exchange = rabbit_mqtt_util:env(exchange), - socket = Socket }. + socket = Socket, + ssl_login_name = SSLLoginName }. info(client_id, #proc_state{ client_id = ClientId }) -> ClientId. @@ -44,14 +46,8 @@ process_frame(#mqtt_frame{ fixed = #mqtt_frame_fixed{ type = Type }}, when Type =/= ?CONNECT -> {error, connect_expected, PState}; process_frame(Frame = #mqtt_frame{ fixed = #mqtt_frame_fixed{ type = Type }}, - PState ) -> - %%rabbit_log:info("MQTT received frame ~p ~n", [Frame]), - try process_request(Type, Frame, PState) of - Result -> Result - catch _:Error -> - close_connection(PState), - {error, Error} - end. + PState) -> + process_request(Type, Frame, PState). process_request(?CONNECT, #mqtt_frame{ variable = #mqtt_frame_connect{ @@ -60,7 +56,8 @@ process_request(?CONNECT, proto_ver = ProtoVersion, clean_sess = CleanSess, client_id = ClientId0, - keep_alive = Keepalive} = Var}, PState) -> + keep_alive = Keepalive} = Var}, + PState = #proc_state{ ssl_login_name = SSLLoginName }) -> ClientId = case ClientId0 of [] -> rabbit_mqtt_util:gen_client_id(); [_|_] -> ClientId0 @@ -73,7 +70,7 @@ process_request(?CONNECT, {_, true} -> {?CONNACK_INVALID_ID, PState}; _ -> - case creds(Username, Password) of + case creds(Username, Password, SSLLoginName) of nocreds -> rabbit_log:error("MQTT login failed - no credentials~n"), {?CONNACK_CREDENTIALS, PState}; @@ -82,6 +79,8 @@ process_request(?CONNECT, {?CONNACK_ACCEPT, Conn} -> link(Conn), {ok, Ch} = amqp_connection:open_channel(Conn), + link(Ch), + amqp_channel:enable_delivery_flow_control(Ch), ok = rabbit_mqtt_collector:register( ClientId, self()), Prefetch = rabbit_mqtt_util:env(prefetch), @@ -211,10 +210,12 @@ amqp_callback({#'basic.deliver'{ consumer_tag = ConsumerTag, delivery_tag = DeliveryTag, routing_key = RoutingKey }, #amqp_msg{ props = #'P_basic'{ headers = Headers }, - payload = Payload }} = Delivery, + payload = Payload }, + DeliveryCtx} = Delivery, #proc_state{ channels = {Channel, _}, awaiting_ack = Awaiting, message_id = MsgId } = PState) -> + amqp_channel:notify_received(DeliveryCtx), case {delivery_dup(Delivery), delivery_qos(ConsumerTag, Headers, PState)} of {true, {?QOS_0, ?QOS_1}} -> amqp_channel:cast( @@ -278,7 +279,8 @@ amqp_callback(#'basic.ack'{ multiple = false, delivery_tag = Tag }, {ok, PState #proc_state{ unacked_pubs = gb_trees:delete(Tag, UnackedPubs) }}. delivery_dup({#'basic.deliver'{ redelivered = Redelivered }, - #amqp_msg{ props = #'P_basic'{ headers = Headers }}}) -> + #amqp_msg{ props = #'P_basic'{ headers = Headers }}, + _DeliveryCtx}) -> case rabbit_mqtt_util:table_lookup(Headers, <<"x-mqtt-dup">>) of undefined -> Redelivered; {bool, Dup} -> Redelivered orelse Dup @@ -366,23 +368,27 @@ get_vhost_username(UserBin) -> [UserBin] -> {rabbit_mqtt_util:env(vhost), UserBin} end. -creds(User, Pass) -> - DefaultUser = rabbit_mqtt_util:env(default_user), - DefaultPass = rabbit_mqtt_util:env(default_pass), - Anon = rabbit_mqtt_util:env(allow_anonymous), - U = case {User =/= undefined, is_binary(DefaultUser), Anon =:= true} of - {true, _, _ } -> list_to_binary(User); - {false, true, true} -> DefaultUser; - _ -> nocreds +creds(User, Pass, SSLLoginName) -> + DefaultUser = rabbit_mqtt_util:env(default_user), + DefaultPass = rabbit_mqtt_util:env(default_pass), + {ok, Anon} = application:get_env(?APP, allow_anonymous), + {ok, TLSAuth} = application:get_env(?APP, ssl_cert_login), + U = case {User =/= undefined, is_binary(DefaultUser), + Anon =:= true, (TLSAuth andalso SSLLoginName =/= none)} of + {true, _, _, _} -> list_to_binary(User); + {false, _, _, true} -> SSLLoginName; + {false, true, true, false} -> DefaultUser; + _ -> nocreds end, case U of nocreds -> nocreds; _ -> - case {Pass =/= undefined, is_binary(DefaultPass), Anon =:= true} of - {true, _, _ } -> {U, list_to_binary(Pass)}; - {false, true, true} -> {U, DefaultPass}; - _ -> {U, none} + case {Pass =/= undefined, is_binary(DefaultPass), Anon =:= true, SSLLoginName == U} of + {true, _, _, _} -> {U, list_to_binary(Pass)}; + {false, _, _, _} -> {U, none}; + {false, true, true, _} -> {U, DefaultPass}; + _ -> {U, none} end end. @@ -489,7 +495,14 @@ amqp_pub(#mqtt_msg{ qos = Qos, adapter_info(Sock, ProtoVer) -> amqp_connection:socket_adapter_info( - Sock, {'MQTT', integer_to_list(ProtoVer)}). + Sock, {'MQTT', human_readable_mqtt_version(ProtoVer)}). + +human_readable_mqtt_version(3) -> + "3.1.0"; +human_readable_mqtt_version(4) -> + "3.1.1"; +human_readable_mqtt_version(_) -> + "N/A". send_client(Frame, #proc_state{ socket = Sock }) -> %rabbit_log:info("MQTT sending frame ~p ~n", [Frame]), @@ -508,4 +521,3 @@ close_connection(PState = #proc_state{ connection = Connection, catch amqp_connection:close(Connection), PState #proc_state{ channels = {undefined, undefined}, connection = undefined }. - diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_reader.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_reader.erl index d2d6b04..30cf032 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_reader.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_reader.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_reader). @@ -52,7 +52,7 @@ handle_cast({go, Sock0, SockTransform, KeepaliveSup}, undefined) -> {ok, Sock} -> rabbit_alarm:register( self(), {?MODULE, conserve_resources, []}), - ProcessorState = rabbit_mqtt_processor:initial_state(Sock), + ProcessorState = rabbit_mqtt_processor:initial_state(Sock,ssl_login_name(Sock)), {noreply, control_throttle( #state{socket = Sock, @@ -90,9 +90,10 @@ handle_cast(duplicate_id, handle_cast(Msg, State) -> {stop, {mqtt_unexpected_cast, Msg}, State}. -handle_info({#'basic.deliver'{}, #amqp_msg{}} = Delivery, +handle_info({#'basic.deliver'{}, #amqp_msg{}, _DeliveryCtx} = Delivery, State = #state{ proc_state = ProcState }) -> - callback_reply(State, rabbit_mqtt_processor:amqp_callback(Delivery, ProcState)); + callback_reply(State, rabbit_mqtt_processor:amqp_callback(Delivery, + ProcState)); handle_info(#'basic.ack'{} = Ack, State = #state{ proc_state = ProcState }) -> callback_reply(State, rabbit_mqtt_processor:amqp_callback(Ack, ProcState)); @@ -137,9 +138,10 @@ handle_info({start_keepalives, Keepalive}, KeepaliveSup, Sock, 0, SendFun, Keepalive, ReceiveFun), {noreply, State #state { keepalive = Heartbeater }}; -handle_info(keepalive_timeout, State = #state { conn_name = ConnStr }) -> +handle_info(keepalive_timeout, State = #state {conn_name = ConnStr, + proc_state = PState}) -> log(error, "closing MQTT connection ~p (keepalive timeout)~n", [ConnStr]), - {stop, {shutdown, keepalive_timeout}, State}; + send_will_and_terminate(PState, {shutdown, keepalive_timeout}, State); handle_info(Msg, State) -> {stop, {mqtt_unexpected_msg, Msg}, State}. @@ -177,19 +179,30 @@ terminate({network_error, Reason, ConnStr}, _State) -> terminate({network_error, Reason}, _State) -> log(error, "MQTT detected network error: ~p~n", [Reason]); -terminate(normal, State = #state{proc_state = ProcState, - conn_name = ConnName}) -> +terminate(normal, #state{proc_state = ProcState, + conn_name = ConnName}) -> rabbit_mqtt_processor:close_connection(ProcState), log(info, "closing MQTT connection ~p (~s)~n", [self(), ConnName]), ok; -terminate(_Reason, State = #state{proc_state = ProcState}) -> +terminate(_Reason, #state{proc_state = ProcState}) -> rabbit_mqtt_processor:close_connection(ProcState), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. +ssl_login_name(Sock) -> + case rabbit_net:peercert(Sock) of + {ok, C} -> case rabbit_ssl:peer_cert_auth_name(C) of + unsafe -> none; + not_found -> none; + Name -> Name + end; + {error, no_peercert} -> none; + nossl -> none + end. + %%---------------------------------------------------------------------------- process_received_bytes(<<>>, State) -> @@ -213,7 +226,7 @@ process_received_bytes(Bytes, proc_state = ProcState1 }); {error, Reason, ProcState1} -> log(info, "MQTT protocol error ~p for connection ~p~n", - [ConnStr, Reason]), + [Reason, ConnStr]), {stop, {shutdown, Reason}, pstate(State, ProcState1)}; {error, Error} -> log(error, "MQTT detected framing error '~p' for connection ~p~n", @@ -230,7 +243,7 @@ process_received_bytes(Bytes, callback_reply(State, {ok, ProcState}) -> {noreply, pstate(State, ProcState), hibernate}; -callback_reply(State, {err, Reason, ProcState}) -> +callback_reply(State, {error, Reason, ProcState}) -> {stop, Reason, pstate(State, ProcState)}. start_keepalive(_, 0 ) -> ok; @@ -241,13 +254,15 @@ pstate(State = #state {}, PState = #proc_state{}) -> %%---------------------------------------------------------------------------- -log(Level, Fmt) -> rabbit_log:log(connection, Level, Fmt, []). log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). send_will_and_terminate(PState, State) -> + send_will_and_terminate(PState, {shutdown, conn_closed}, State). + +send_will_and_terminate(PState, Reason, State) -> rabbit_mqtt_processor:send_will(PState), % todo: flush channel after publish - {stop, {shutdown, conn_closed}, State}. + {stop, Reason, State}. network_error(closed, State = #state{ conn_name = ConnStr, diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_sup.erl index a2b3a23..38edcf9 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_sup). @@ -26,12 +26,17 @@ start_link(Listeners, []) -> supervisor2:start_link({local, ?MODULE}, ?MODULE, [Listeners]). -init([{Listeners, SslListeners}]) -> +init([{Listeners, SslListeners0}]) -> {ok, SocketOpts} = application:get_env(rabbitmq_mqtt, tcp_listen_options), - SslOpts = case SslListeners of - [] -> none; - _ -> rabbit_networking:ensure_ssl() - end, + {SslOpts, SslListeners} + = case SslListeners0 of + [] -> {none, []}; + _ -> {rabbit_networking:ensure_ssl(), + case rabbit_networking:poodle_check('MQTT') of + ok -> SslListeners0; + danger -> [] + end} + end, {ok, {{one_for_all, 10, 10}, [{collector, {rabbit_mqtt_collector, start_link, []}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_util.erl b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_util.erl index c675ebd..9c1787a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbit_mqtt_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_mqtt_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbitmq_mqtt.app.src b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbitmq_mqtt.app.src index b687c0a..6b60610 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbitmq_mqtt.app.src +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/src/rabbitmq_mqtt.app.src @@ -6,6 +6,7 @@ {mod, {rabbit_mqtt, []}}, {env, [{default_user, <<"guest">>}, {default_pass, <<"guest">>}, + {ssl_cert_login,false}, {allow_anonymous, true}, {vhost, <<"/">>}, {exchange, <<"amq.topic">>}, diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/Makefile b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/Makefile index 6538214..190e740 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/Makefile @@ -11,21 +11,23 @@ JUNIT_JAR=../lib/junit.jar JAVA_AMQP_DIR=../../rabbitmq-java-client/ JAVA_AMQP_CLASSES=$(JAVA_AMQP_DIR)build/classes/ -TEST_SRCS:=$(shell find $(TEST_SRC) -name '*.java') ALL_CLASSES:=$(foreach f,$(shell find src -name '*.class'),'$(f)') -TEST_CLASSES:=$(TEST_SRCS:.java=.class) CP:=$(PAHO_JAR):$(JUNIT_JAR):$(TEST_SRC):$(JAVA_AMQP_CLASSES) +HOSTNAME:=$(shell hostname) + define class_from_path $(subst .class,,$(subst src.,,$(subst /,.,$(1)))) endef .PHONY: test -test: $(TEST_CLASSES) build_java_amqp - $(foreach test,$(TEST_CLASSES),CLASSPATH=$(CP) java junit.textui.TestRunner -text $(call class_from_path,$(test))) +test: build_java_amqp + ant test -Dhostname=$(HOSTNAME) clean: - rm -rf $(PAHO_JAR) $(ALL_CLASSES) + ant clean + rm -rf test_client + distclean: clean rm -rf $(CHECKOUT_DIR) @@ -34,13 +36,7 @@ $(CHECKOUT_DIR): git clone $(UPSTREAM_GIT) $@ (cd $@ && git checkout $(REVISION)) || rm -rf $@ -$(PAHO_JAR): $(CHECKOUT_DIR) - ant -buildfile $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/lib/junit.jar b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/lib/junit.jar similarity index 100% rename from rabbitmq-server/plugins-src/rabbitmq-mqtt/lib/junit.jar rename to rabbitmq-server/plugins-src/rabbitmq-mqtt/test/lib/junit.jar diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/rabbit-test.sh b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/rabbit-test.sh new file mode 100755 index 0000000..b0c6585 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/rabbit-test.sh @@ -0,0 +1,6 @@ +#!/bin/sh +CTL=$1 +USER="O=client,CN=$(hostname)" + +$CTL add_user "$USER" '' +$CTL set_permissions -p / "$USER" ".*" ".*" ".*" diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/setup-rabbit-test.sh b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/setup-rabbit-test.sh new file mode 100755 index 0000000..9b2708a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/setup-rabbit-test.sh @@ -0,0 +1,2 @@ +#!/bin/sh -e +sh -e `dirname $0`/rabbit-test.sh "`dirname $0`/../../rabbitmq-server/scripts/rabbitmqctl -n rabbit-test" diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/MqttTest.java b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/MqttTest.java index 0453c91..b5a4913 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/MqttTest.java +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/MqttTest.java @@ -11,7 +11,7 @@ // The Original Code is RabbitMQ. // // The Initial Developer of the Original Code is GoPivotal, Inc. -// Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +// Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. // package com.rabbitmq.mqtt.test; @@ -40,6 +40,7 @@ import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeoutException; /*** * MQTT v3.1 tests @@ -110,7 +111,7 @@ public class MqttTest extends TestCase implements MqttCallback { } catch (Exception _) {} } - private void setUpAmqp() throws IOException { + private void setUpAmqp() throws IOException, TimeoutException { connectionFactory = new ConnectionFactory(); connectionFactory.setHost(host); conn = connectionFactory.newConnection(); @@ -377,7 +378,7 @@ public class MqttTest extends TestCase implements MqttCallback { } } - public void testInteropM2A() throws MqttException, IOException, InterruptedException { + public void testInteropM2A() throws MqttException, IOException, InterruptedException, TimeoutException { setUpAmqp(); String queue = ch.queueDeclare().getQueue(); ch.queueBind(queue, "amq.topic", topic); @@ -393,7 +394,7 @@ public class MqttTest extends TestCase implements MqttCallback { tearDownAmqp(); } - public void testInteropA2M() throws MqttException, IOException, InterruptedException { + public void testInteropA2M() throws MqttException, IOException, InterruptedException, TimeoutException { client.connect(conOpt); client.setCallback(this); client.subscribe(topic, 1); diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/rabbit-test.sh b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/rabbit-test.sh new file mode 100644 index 0000000..3601b4c --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/rabbit-test.sh @@ -0,0 +1,7 @@ +#!/bin/sh +CTL=$1 +USER="O=client,CN=$(hostname)" + +# Test direct connections +$CTL add_user "$USER" '' +$CTL set_permissions -p / "$USER" ".*" ".*" ".*" diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/setup-rabbit-test.sh b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/setup-rabbit-test.sh new file mode 100644 index 0000000..9b2708a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/setup-rabbit-test.sh @@ -0,0 +1,2 @@ +#!/bin/sh -e +sh -e `dirname $0`/rabbit-test.sh "`dirname $0`/../../rabbitmq-server/scripts/rabbitmqctl -n rabbit-test" diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MqttSSLTest.java b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MqttSSLTest.java new file mode 100644 index 0000000..f89d963 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MqttSSLTest.java @@ -0,0 +1,166 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License +// at http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +// the License for the specific language governing rights and +// limitations under the License. +// +// The Original Code is RabbitMQ. +// +// The Initial Developer of the Original Code is GoPivotal, Inc. +// Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. +// + +package com.rabbitmq.mqtt.test.tls; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +import java.io.IOException; +import java.util.ArrayList; + + +/** + * MQTT v3.1 tests + * TODO: synchronise access to variables + */ + +public class MqttSSLTest extends TestCase implements MqttCallback { + + private final int port = 8883; + private final String brokerUrl = "ssl://" + getHost() + ":" + port; + private String clientId; + private String clientId2; + private MqttClient client; + private MqttClient client2; + private MqttConnectOptions conOpt; + private ArrayList receivedMessages; + + private long lastReceipt; + private boolean expectConnectionFailure; + + + private static final String getHost() { + Object host = System.getProperty("hostname"); + assertNotNull(host); + return host.toString(); + } + + // override 10s limit + private class MyConnOpts extends MqttConnectOptions { + private int keepAliveInterval = 60; + + @Override + public void setKeepAliveInterval(int keepAliveInterval) { + this.keepAliveInterval = keepAliveInterval; + } + + @Override + public int getKeepAliveInterval() { + return keepAliveInterval; + } + } + + + @Override + public void setUp() throws MqttException, IOException { + clientId = getClass().getSimpleName() + ((int) (10000 * Math.random())); + clientId2 = clientId + "-2"; + client = new MqttClient(brokerUrl, clientId, null); + client2 = new MqttClient(brokerUrl, clientId2, null); + conOpt = new MyConnOpts(); + conOpt.setSocketFactory(MutualAuth.getSSLContextWithoutCert().getSocketFactory()); + setConOpts(conOpt); + receivedMessages = new ArrayList(); + expectConnectionFailure = false; + } + + @Override + public void tearDown() throws MqttException { + // clean any sticky sessions + setConOpts(conOpt); + client = new MqttClient(brokerUrl, clientId, null); + try { + client.connect(conOpt); + client.disconnect(); + } catch (Exception _) { + } + + client2 = new MqttClient(brokerUrl, clientId2, null); + try { + client2.connect(conOpt); + client2.disconnect(); + } catch (Exception _) { + } + } + + + private void setConOpts(MqttConnectOptions conOpts) { + // provide authentication if the broker needs it + // conOpts.setUserName("guest"); + // conOpts.setPassword("guest".toCharArray()); + conOpts.setCleanSession(true); + conOpts.setKeepAliveInterval(60); + } + + public void testCertLogin() throws MqttException { + try { + conOpt.setSocketFactory(MutualAuth.getSSLContextWithClientCert().getSocketFactory()); + client.connect(conOpt); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception: " + e.getMessage()); + } + } + + + public void testInvalidUser() throws MqttException { + conOpt.setUserName("invalid-user"); + try { + client.connect(conOpt); + fail("Authentication failure expected"); + } catch (MqttException ex) { + Assert.assertEquals(MqttException.REASON_CODE_FAILED_AUTHENTICATION, ex.getReasonCode()); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception: " + e.getMessage()); + } + } + + public void testInvalidPassword() throws MqttException { + conOpt.setUserName("invalid-user"); + conOpt.setPassword("invalid-password".toCharArray()); + try { + client.connect(conOpt); + fail("Authentication failure expected"); + } catch (MqttException ex) { + Assert.assertEquals(MqttException.REASON_CODE_FAILED_AUTHENTICATION, ex.getReasonCode()); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception: " + e.getMessage()); + } + } + + + public void connectionLost(Throwable cause) { + if (!expectConnectionFailure) + fail("Connection unexpectedly lost"); + } + + public void messageArrived(String topic, MqttMessage message) throws Exception { + lastReceipt = System.currentTimeMillis(); + receivedMessages.add(message); + } + + public void deliveryComplete(IMqttDeliveryToken token) { + } +} diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MutualAuth.java b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MutualAuth.java new file mode 100644 index 0000000..a2d5d25 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/com/rabbitmq/mqtt/test/tls/MutualAuth.java @@ -0,0 +1,84 @@ +package com.rabbitmq.mqtt.test.tls; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.List; + +public class MutualAuth { + + private MutualAuth() { + + } + + private static String getStringProperty(String propertyName) throws IllegalArgumentException { + Object value = System.getProperty(propertyName); + if (value == null) throw new IllegalArgumentException("Property: " + propertyName + " not found"); + return value.toString(); + } + + private static TrustManagerFactory getServerTrustManagerFactory() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException { + char[] trustPhrase = getStringProperty("server.keystore.passwd").toCharArray(); + MutualAuth dummy = new MutualAuth(); + + // Server TrustStore + KeyStore tks = KeyStore.getInstance("JKS"); + tks.load(dummy.getClass().getResourceAsStream("/server.jks"), trustPhrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); + tmf.init(tks); + + return tmf; + } + + public static SSLContext getSSLContextWithClientCert() throws IOException { + + char[] clientPhrase = getStringProperty("client.keystore.passwd").toCharArray(); + + MutualAuth dummy = new MutualAuth(); + try { + SSLContext sslContext = getVanillaSSLContext(); + // Client Keystore + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(dummy.getClass().getResourceAsStream("/client.jks"), clientPhrase); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, clientPhrase); + + sslContext.init(kmf.getKeyManagers(), getServerTrustManagerFactory().getTrustManagers(), null); + return sslContext; + } catch (Exception e) { + throw new IOException(e); + } + + } + + private static SSLContext getVanillaSSLContext() throws NoSuchAlgorithmException { + SSLContext result = null; + List xs = Arrays.asList("TLSv1.2", "TLSv1.1", "TLSv1"); + for(String x : xs) { + try { + return SSLContext.getInstance(x); + } catch (NoSuchAlgorithmException nae) { + // keep trying + } + } + throw new NoSuchAlgorithmException("Could not obtain an SSLContext for TLS 1.0-1.2"); + } + + public static SSLContext getSSLContextWithoutCert() throws IOException { + try { + SSLContext sslContext = getVanillaSSLContext(); + sslContext.init(null, getServerTrustManagerFactory().getTrustManagers(), null); + return sslContext; + } catch (Exception e) { + throw new IOException(e); + } + } + +} diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/test.config b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/test.config new file mode 100644 index 0000000..3d6baff --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/src/test.config @@ -0,0 +1,14 @@ +[{rabbitmq_mqtt, [ + {ssl_cert_login, true}, + {allow_anonymous, true}, + {tcp_listeners, [1883]}, + {ssl_listeners, [8883]} + ]}, + {rabbit, [{ssl_options, [{cacertfile,"%%CERTS_DIR%%/testca/cacert.pem"}, + {certfile,"%%CERTS_DIR%%/server/cert.pem"}, + {keyfile,"%%CERTS_DIR%%/server/key.pem"}, + {verify,verify_peer}, + {fail_if_no_peer_cert,false} + ]} + ]} +]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/test.sh b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/test.sh index 313aeb5..ae60a49 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/test.sh +++ b/rabbitmq-server/plugins-src/rabbitmq-mqtt/test/test.sh @@ -1,3 +1,3 @@ #!/bin/sh -make -C `dirname $0` build_java_amqp -make -C `dirname $0` test +$MAKE -C `dirname $0` build_java_amqp +$MAKE -C `dirname $0` test diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/shovel.js b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/shovel.js index 42cad36..13827d6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/shovel.js +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/shovel.js @@ -18,7 +18,14 @@ dispatcher_add(function(sammy) { var num_keys = ['prefetch-count', 'reconnect-delay']; var bool_keys = ['add-forward-headers']; var arrayable_keys = ['src-uri', 'dest-uri']; + var redirect = this.params['redirect']; + if (redirect != undefined) { + delete this.params['redirect']; + } put_parameter(this, [], num_keys, bool_keys, arrayable_keys); + if (redirect != undefined) { + go_to(redirect); + } return false; }); sammy.del('#/shovel-parameters', function() { @@ -66,7 +73,7 @@ HELP['shovel-delete-after'] = '; function link_shovel(vhost, name) { - return _link_to(fmt_escape_html(name), '#/dynamic-shovels/' + esc(vhost) + '/' + esc(name)); + return _link_to(name, '#/dynamic-shovels/' + esc(vhost) + '/' + esc(name)); } function fmt_shovel_endpoint(prefix, shovel) { diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/tmpl/dynamic-shovel.ejs b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/tmpl/dynamic-shovel.ejs index 61b4aa0..9f7759b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/tmpl/dynamic-shovel.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/priv/www/js/tmpl/dynamic-shovel.ejs @@ -1,13 +1,9 @@ -

Dynamic Shovel: <%= fmt_string(shovel.name) %>

+

Dynamic Shovel: <%= fmt_string(shovel.name) %><%= fmt_maybe_vhost(shovel.vhost) %>

Overview

- - - - diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/src/rabbit_shovel_mgmt.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/src/rabbit_shovel_mgmt.erl index 86812ef..3f5c846 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/src/rabbit_shovel_mgmt.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/src/rabbit_shovel_mgmt.erl @@ -65,7 +65,7 @@ filter_vhost_req(List, ReqData) -> %% static shovels do not have a vhost, so only allow admins (not %% monitors) to see them. filter_vhost_user(List, _ReqData, #context{user = User = #user{tags = Tags}}) -> - VHosts = rabbit_mgmt_util:list_login_vhosts(User), + VHosts = rabbit_mgmt_util:list_login_vhosts(User, undefined), [I || I <- List, case pget(vhost, I) of undefined -> lists:member(administrator, Tags); VHost -> lists:member(VHost, VHosts) @@ -117,8 +117,14 @@ lookup_src_dest(static, _Name) -> []; lookup_src_dest(dynamic, {VHost, Name}) -> - Def = pget(value, - rabbit_runtime_parameters:lookup(VHost, <<"shovel">>, Name)), - Ks = [<<"src-queue">>, <<"src-exchange">>, <<"src-exchange-key">>, - <<"dest-queue">>, <<"dest-exchange">>, <<"dest-exchange-key">>], - [{definition, [{K, V} || {K, V} <- Def, lists:member(K, Ks)]}]. + case rabbit_runtime_parameters:lookup(VHost, <<"shovel">>, Name) of + %% We might not find anything if the shovel has been deleted + %% before we got here + not_found -> + []; + Props -> + Def = pget(value, Props), + Ks = [<<"src-queue">>, <<"src-exchange">>, <<"src-exchange-key">>, + <<"dest-queue">>,<<"dest-exchange">>,<<"dest-exchange-key">>], + [{definition, [{K, V} || {K, V} <- Def, lists:member(K, Ks)]}] + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_all.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_all.erl index 3fb8bdd..b82c4e1 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_all.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_all.erl @@ -23,6 +23,6 @@ all_tests() -> tests(Module, Timeout) -> {foreach, fun() -> ok end, - [{timeout, Timeout, fun Module:F/0} || + [{timeout, Timeout, fun () -> Module:F() end} || {F, _Arity} <- proplists:get_value(exports, Module:module_info()), string:right(atom_to_list(F), 5) =:= "_test"]}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_http.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_http.erl index d0a5c1b..b3407ce 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_http.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel-management/test/src/rabbit_shovel_mgmt_test_http.erl @@ -65,8 +65,27 @@ shovels_test() -> http_delete("/users/mon", ?NO_CONTENT), ok. +%% It's a bit arbitrary to be testing this here, but we want to be +%% able to test that mgmt extensions can be started and stopped +%% *somewhere*, and here is as good a place as any. +dynamic_plugin_enable_disable_test() -> + http_get("/shovels", ?OK), + disable_plugin("rabbitmq_shovel_management"), + http_get("/shovels", ?NOT_FOUND), + http_get("/overview", ?OK), + disable_plugin("rabbitmq_management"), + http_fail("/shovels"), + http_fail("/overview"), + enable_plugin("rabbitmq_management"), + http_get("/shovels", ?NOT_FOUND), + http_get("/overview", ?OK), + enable_plugin("rabbitmq_shovel_management"), + http_get("/shovels", ?OK), + http_get("/overview", ?OK), + passed. + %%--------------------------------------------------------------------------- -%% TODO this is all copypasta from the mgmt tests +%% TODO this is mostly copypasta from the mgmt tests http_get(Path) -> http_get(Path, ?OK). @@ -80,6 +99,9 @@ http_get(Path, User, Pass, CodeExp) -> assert_code(CodeExp, CodeAct, "GET", Path, ResBody), decode(CodeExp, Headers, ResBody). +http_fail(Path) -> + {error, {failed_connect, _}} = req(get, Path, []). + http_put(Path, List, CodeExp) -> http_put_raw(Path, format_for_upload(List), CodeExp). @@ -179,3 +201,17 @@ test_item(Exp, Act) -> test_item0(Exp, Act) -> [{did_not_find, ExpI, in, Act} || ExpI <- Exp, not lists:member(ExpI, Act)]. +%%--------------------------------------------------------------------------- + +enable_plugin(Plugin) -> + plugins_action(enable, [Plugin], []). + +disable_plugin(Plugin) -> + plugins_action(disable, [Plugin], []). + +plugins_action(Command, Args, Opts) -> + PluginsFile = os:getenv("RABBITMQ_ENABLED_PLUGINS_FILE"), + PluginsDir = os:getenv("RABBITMQ_PLUGINS_DIR"), + Node = node(), + rpc:call(Node, rabbit_plugins_main, action, + [Command, Node, Args, Opts, PluginsFile, PluginsDir]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-shovel/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/include/rabbit_shovel.hrl b/rabbitmq-server/plugins-src/rabbitmq-shovel/include/rabbit_shovel.hrl index b0f0aae..5168c8f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/include/rabbit_shovel.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/include/rabbit_shovel.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -record(endpoint, diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel.erl index e416787..c945321 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_config.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_config.erl index 8d6bc56..a20b73b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_config.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_config.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_config). @@ -23,15 +23,14 @@ -include("rabbit_shovel.hrl"). -define(IGNORE_FIELDS, [delete_after]). +-define(EXTRA_KEYS, [add_forward_headers]). parse(ShovelName, Config) -> {ok, Defaults} = application:get_env(defaults), try - {ok, run_state_monad( - [fun enrich_shovel_config/1, - fun parse_shovel_config_proplist/1, - fun parse_shovel_config_dict/1], - {Config, Defaults})} + {ok, parse_shovel_config_dict( + ShovelName, parse_shovel_config_proplist( + enrich_shovel_config(Config, Defaults)))} catch throw:{error, Reason} -> {error, {invalid_shovel_configuration, ShovelName, Reason}} end. @@ -44,7 +43,7 @@ ensure_defaults(ShovelConfig, ParsedShovel) -> {reconnect_delay, ParsedShovel#shovel.reconnect_delay}). -enrich_shovel_config({Config, Defaults}) -> +enrich_shovel_config(Config, Defaults) -> Config1 = proplists:unfold(Config), case [E || E <- Config1, not (is_tuple(E) andalso tuple_size(E) == 2)] of [] -> case duplicate_keys(Config1) of @@ -57,7 +56,7 @@ enrich_shovel_config({Config, Defaults}) -> parse_shovel_config_proplist(Config) -> Dict = dict:from_list(Config), Fields = record_info(fields, shovel) -- ?IGNORE_FIELDS, - Keys = dict:fetch_keys(Dict), + Keys = dict:fetch_keys(Dict) -- ?EXTRA_KEYS, case {Keys -- Fields, Fields -- Keys} of {[], []} -> {_Pos, Dict1} = lists:foldl( @@ -72,24 +71,29 @@ parse_shovel_config_proplist(Config) -> {Unknown, _} -> fail({unrecognised_parameters, Unknown}) end. -parse_shovel_config_dict(Dict) -> - run_state_monad( - [fun (Shovel) -> {ok, Value} = dict:find(Key, Dict), - try {ParsedValue, Pos} = Fun(Value), - return(setelement(Pos, Shovel, ParsedValue)) - catch throw:{error, Reason} -> - fail({invalid_parameter_value, Key, Reason}) - end - end || {Fun, Key} <- - [{fun parse_endpoint/1, sources}, - {fun parse_endpoint/1, destinations}, - {fun parse_non_negative_integer/1, prefetch_count}, - {fun parse_ack_mode/1, ack_mode}, - {fun parse_binary/1, queue}, - make_parse_publish(publish_fields), - make_parse_publish(publish_properties), - {fun parse_non_negative_number/1, reconnect_delay}]], - #shovel{}). +parse_shovel_config_dict(Name, Dict) -> + Cfg = run_state_monad( + [fun (Shovel) -> + {ok, Value} = dict:find(Key, Dict), + try {ParsedValue, Pos} = Fun(Value), + return(setelement(Pos, Shovel, ParsedValue)) + catch throw:{error, Reason} -> + fail({invalid_parameter_value, Key, Reason}) + end + end || {Fun, Key} <- + [{fun parse_endpoint/1, sources}, + {fun parse_endpoint/1, destinations}, + {fun parse_non_negative_integer/1, prefetch_count}, + {fun parse_ack_mode/1, ack_mode}, + {fun parse_binary/1, queue}, + make_parse_publish(publish_fields), + make_parse_publish(publish_properties), + {fun parse_non_negative_number/1, reconnect_delay}]], + #shovel{}), + case dict:find(add_forward_headers, Dict) of + {ok, true} -> add_forward_headers_fun(Name, Cfg); + _ -> Cfg + end. %% --=: Plain state monad implementation start :=-- run_state_monad(FunList, State) -> @@ -240,3 +244,14 @@ duplicate_keys(PropList) -> proplists:get_keys( lists:foldl(fun (K, L) -> lists:keydelete(K, 1, L) end, PropList, proplists:get_keys(PropList))). + +add_forward_headers_fun(Name, #shovel{publish_properties = PubProps} = Cfg) -> + PubProps2 = + fun(SrcUri, DestUri, Props) -> + rabbit_shovel_util:update_headers( + [{<<"shovelled-by">>, rabbit_nodes:cluster_name()}, + {<<"shovel-type">>, <<"static">>}, + {<<"shovel-name">>, list_to_binary(atom_to_list(Name))}], + [], SrcUri, DestUri, PubProps(SrcUri, DestUri, Props)) + end, + Cfg#shovel{publish_properties = PubProps2}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup.erl index d5d5268..0e5991d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_dyn_worker_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup_sup.erl index 9be7fd8..38bbd50 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_dyn_worker_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_dyn_worker_sup_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_parameters.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_parameters.erl index dfcb349..49bbcac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_parameters.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_parameters.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_parameters). @@ -21,21 +21,23 @@ -include("rabbit_shovel.hrl"). -export([validate/5, notify/4, notify_clear/3]). --export([register/0, parse/2]). +-export([register/0, unregister/0, parse/2]). -import(rabbit_misc, [pget/2, pget/3]). --define(ROUTING_HEADER, <<"x-shovelled">>). - -rabbit_boot_step({?MODULE, [{description, "shovel parameters"}, {mfa, {rabbit_shovel_parameters, register, []}}, + {cleanup, {?MODULE, unregister, []}}, {requires, rabbit_registry}, {enables, recovery}]}). register() -> rabbit_registry:register(runtime_parameter, <<"shovel">>, ?MODULE). +unregister() -> + rabbit_registry:unregister(runtime_parameter, <<"shovel">>). + validate(_VHost, <<"shovel">>, Name, Def, User) -> [case pget2(<<"src-exchange">>, <<"src-queue">>, Def) of zero -> {error, "Must specify 'src-exchange' or 'src-queue'", []}; @@ -84,6 +86,7 @@ validation(User) -> {<<"prefetch-count">>, fun rabbit_parameter_validation:number/2,optional}, {<<"reconnect-delay">>, fun rabbit_parameter_validation:number/2,optional}, {<<"add-forward-headers">>, fun rabbit_parameter_validation:boolean/2,optional}, + {<<"publish-properties">>, fun validate_properties/2, optional}, {<<"ack-mode">>, rabbit_parameter_validation:enum( ['no-ack', 'on-publish', 'on-confirm']), optional}, {<<"delete-after">>, fun validate_delete_after/2, optional} @@ -114,11 +117,12 @@ validate_uri(Name, Term, User) -> validate_params_user(#amqp_params_direct{}, none) -> ok; validate_params_user(#amqp_params_direct{virtual_host = VHost}, - User = #user{username = Username, - auth_backend = M}) -> - case rabbit_vhost:exists(VHost) andalso M:check_vhost_access(User, VHost) of - true -> ok; - false -> {error, "user \"~s\" may not connect to vhost \"~s\"", + User = #user{username = Username}) -> + case rabbit_vhost:exists(VHost) andalso + (catch rabbit_access_control:check_vhost_access( + User, VHost, undefined)) of + ok -> ok; + _ -> {error, "user \"~s\" may not connect to vhost \"~s\"", [Username, VHost]} end; validate_params_user(#amqp_params_network{}, _User) -> @@ -131,6 +135,25 @@ validate_delete_after(Name, Term) -> {error, "~s should be number, \"never\" or \"queue-length\", actually was " "~p", [Name, Term]}. +%% TODO headers? +validate_properties(Name, Term) -> + Str = fun rabbit_parameter_validation:binary/2, + Num = fun rabbit_parameter_validation:number/2, + rabbit_parameter_validation:proplist( + Name, [{<<"content_type">>, Str, optional}, + {<<"content_encoding">>, Str, optional}, + {<<"delivery_mode">>, Num, optional}, + {<<"priority">>, Num, optional}, + {<<"correlation_id">>, Str, optional}, + {<<"reply_to">>, Str, optional}, + {<<"expiration">>, Str, optional}, + {<<"message_id">>, Str, optional}, + {<<"timestamp">>, Num, optional}, + {<<"type">>, Str, optional}, + {<<"user_id">>, Str, optional}, + {<<"app_id">>, Str, optional}, + {<<"cluster_id">>, Str, optional}], Term). + %%---------------------------------------------------------------------------- parse({VHost, Name}, Def) -> @@ -182,14 +205,17 @@ parse({VHost, Name}, Def) -> end, AddHeaders = pget(<<"add-forward-headers">>, Def, false), Table0 = [{<<"shovelled-by">>, rabbit_nodes:cluster_name()}, + {<<"shovel-type">>, <<"dynamic">>}, {<<"shovel-name">>, Name}, {<<"shovel-vhost">>, VHost}], - PubPropsFun = fun (SrcURI, DestURI, P = #'P_basic'{headers = H}) -> + SetProps = lookup_indices(pget(<<"publish-properties">>, Def, []), + record_info(fields, 'P_basic')), + PubPropsFun = fun (SrcURI, DestURI, P0) -> + P = set_properties(P0, SetProps), case AddHeaders of - true -> H1 = update_headers( - Table0, Table1 ++ Table2, - SrcURI, DestURI, H), - P#'P_basic'{headers = H1}; + true -> rabbit_shovel_util:update_headers( + Table0, Table1 ++ Table2, + SrcURI, DestURI, P); false -> P end end, @@ -234,12 +260,19 @@ ensure_queue(Conn, Queue) -> catch amqp_channel:close(Ch) end. -update_headers(Table0, Table1, SrcURI, DestURI, Headers) -> - Table = Table0 ++ [{<<"src-uri">>, SrcURI}, - {<<"dest-uri">>, DestURI}] ++ Table1, - rabbit_basic:prepend_table_header( - ?ROUTING_HEADER, [{K, longstr, V} || {K, V} <- Table], - Headers). - opt_b2a(B) when is_binary(B) -> list_to_atom(binary_to_list(B)); opt_b2a(N) -> N. + +set_properties(Props, []) -> + Props; +set_properties(Props, [{Ix, V} | Rest]) -> + set_properties(setelement(Ix, Props, V), Rest). + +lookup_indices(KVs, L) -> + [{1 + list_find(list_to_atom(binary_to_list(K)), L), V} || {K, V} <- KVs]. + +list_find(K, L) -> list_find(K, L, 1). + +list_find(K, [K|_], N) -> N; +list_find(K, [], _N) -> exit({not_found, K}); +list_find(K, [_|L], N) -> list_find(K, L, N + 1). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_status.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_status.erl index f30dbc4..37d738e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_status.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_status.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_status). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_sup.erl index 488cef2..b0bce9d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_util.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_util.erl new file mode 100644 index 0000000..a3b0f9c --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_util.erl @@ -0,0 +1,32 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. +%% + +-module(rabbit_shovel_util). + +-export([update_headers/5]). + +-include_lib("rabbit_common/include/rabbit_framing.hrl"). + +-define(ROUTING_HEADER, <<"x-shovelled">>). + +update_headers(Prefix, Suffix, SrcURI, DestURI, + Props = #'P_basic'{headers = Headers}) -> + Table = Prefix ++ [{<<"src-uri">>, SrcURI}, + {<<"dest-uri">>, DestURI}] ++ Suffix, + Headers2 = rabbit_basic:prepend_table_header( + ?ROUTING_HEADER, [{K, longstr, V} || {K, V} <- Table], + Headers), + Props#'P_basic'{headers = Headers2}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker.erl index 38940d3..e5a8f63 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_worker). @@ -88,6 +88,10 @@ handle_cast(init, State = #state{config = Config}) -> end, Remaining = remaining(InboundChan, Config), + case Remaining of + 0 -> exit({shutdown, autodelete}); + _ -> ok + end, #'basic.consume_ok'{} = amqp_channel:subscribe( @@ -153,12 +157,16 @@ terminate(Reason, #state{inbound_conn = undefined, inbound_ch = undefined, name = Name, type = Type}) -> rabbit_shovel_status:report(Name, Type, {terminated, Reason}), ok; +terminate({shutdown, autodelete}, State = #state{name = {VHost, Name}, + type = dynamic}) -> + close_connections(State), + %% See rabbit_shovel_dyn_worker_sup_sup:stop_child/1 + put(shovel_worker_autodelete, true), + rabbit_runtime_parameters:clear(VHost, <<"shovel">>, Name), + rabbit_shovel_status:remove({VHost, Name}), + ok; terminate(Reason, State) -> - maybe_autodelete(Reason, State), - catch amqp_connection:close(State#state.inbound_conn, - ?MAX_CONNECTION_CLOSE_TIMEOUT), - catch amqp_connection:close(State#state.outbound_conn, - ?MAX_CONNECTION_CLOSE_TIMEOUT), + close_connections(State), rabbit_shovel_status:report(State#state.name, State#state.type, {terminated, Reason}), ok. @@ -228,14 +236,14 @@ make_conn_and_chan(URIs) -> {ok, Chan} = amqp_connection:open_channel(Conn), {Conn, Chan, list_to_binary(amqp_uri:remove_credentials(URI))}. -remaining(Ch, #shovel{delete_after = never}) -> +remaining(_Ch, #shovel{delete_after = never}) -> unlimited; remaining(Ch, #shovel{delete_after = 'queue-length', queue = Queue}) -> #'queue.declare_ok'{message_count = N} = amqp_channel:call(Ch, #'queue.declare'{queue = Queue, passive = true}), N; -remaining(Ch, #shovel{delete_after = Count}) -> +remaining(_Ch, #shovel{delete_after = Count}) -> Count. decr_remaining(_N, State = #state{remaining = unlimited}) -> @@ -253,10 +261,8 @@ decr_remaining_unacked(State = #state{remaining_unacked = 0}) -> decr_remaining_unacked(State = #state{remaining_unacked = N}) -> State#state{remaining_unacked = N - 1}. -maybe_autodelete({shutdown, autodelete}, #state{name = {VHost, Name}, - type = dynamic}) -> - %% See rabbit_shovel_dyn_worker_sup_sup:stop_child/1 - put(shovel_worker_autodelete, true), - rabbit_runtime_parameters:clear(VHost, <<"shovel">>, Name); -maybe_autodelete(_Reason, _State) -> - ok. +close_connections(State) -> + catch amqp_connection:close(State#state.inbound_conn, + ?MAX_CONNECTION_CLOSE_TIMEOUT), + catch amqp_connection:close(State#state.outbound_conn, + ?MAX_CONNECTION_CLOSE_TIMEOUT). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker_sup.erl index 3528f9b..1705d5f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/src/rabbit_shovel_worker_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_worker_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test.erl index efb62a7..6619112 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_test). @@ -153,7 +153,8 @@ main_test() -> {publish_fields, [{exchange, ?EXCHANGE}, {routing_key, ?FROM_SHOVEL}]}, {publish_properties, [{delivery_mode, 2}, {cluster_id, <<"my-cluster">>}, - {content_type, ?SHOVELLED}]} + {content_type, ?SHOVELLED}]}, + {add_forward_headers, true} ]}], infinity), @@ -196,7 +197,9 @@ main_test() -> routing_key = ?FROM_SHOVEL }, #amqp_msg { payload = <<42>>, props = #'P_basic' { delivery_mode = 2, - content_type = ?SHOVELLED } + content_type = ?SHOVELLED, + headers = [{<<"x-shovelled">>, + _, _}]} }} -> ok = amqp_channel:call(Chan, #'basic.ack'{ delivery_tag = AckTag }) after ?TIMEOUT -> throw(timeout_waiting_for_deliver1) diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_all.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_all.erl index 81b568a..2269ea8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_all.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_all.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_test_all). @@ -24,7 +24,7 @@ all_tests() -> tests(Module, Timeout) -> {foreach, fun() -> ok end, - [{timeout, Timeout, fun Module:F/0} || F <- funs(Module, "_test")] ++ + [{timeout, Timeout, fun () -> Module:F() end} || F <- funs(Module, "_test")] ++ [{timeout, Timeout, Fun} || Gen <- funs(Module, "_test_"), Fun <- Module:Gen()]}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_dyn.erl b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_dyn.erl index 5a8ead7..b3c74d8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_dyn.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-shovel/test/src/rabbit_shovel_test_dyn.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_shovel_test_dyn). @@ -29,6 +29,18 @@ simple_test() -> publish_expect(Ch, <<>>, <<"src">>, <<"dest">>, <<"hello">>) end). +set_properties_test() -> + with_ch( + fun (Ch) -> + Ps = [{<<"src-queue">>, <<"src">>}, + {<<"dest-queue">>, <<"dest">>}, + {<<"publish-properties">>, [{<<"cluster_id">>, <<"x">>}]}], + set_param(<<"test">>, Ps), + #amqp_msg{props = #'P_basic'{cluster_id = Cluster}} = + publish_expect(Ch, <<>>, <<"src">>, <<"dest">>, <<"hi">>), + ?assertEqual(<<"x">>, Cluster) + end). + exchange_test() -> with_ch( fun (Ch) -> @@ -141,6 +153,11 @@ validation_test() -> invalid_param([{<<"ack-mode">>, <<"whenever">>} | QURIs]), invalid_param([{<<"delete-after">>, <<"whenever">>} | QURIs]), + %% Check properties have to look property-ish + invalid_param([{<<"publish-properties">>, [{<<"nonexistent">>, <<>>}]}]), + invalid_param([{<<"publish-properties">>, [{<<"cluster_id">>, 2}]}]), + invalid_param([{<<"publish-properties">>, <<"something">>}]), + %% Can't use explicit message count and no-ack together invalid_param([{<<"delete-after">>, 1}, {<<"ack-mode">>, <<"no-ack">>} | QURIs]), @@ -197,13 +214,14 @@ expect(Ch, Q, Payload) -> receive #'basic.consume_ok'{consumer_tag = CTag} -> ok end, - receive - {#'basic.deliver'{}, #amqp_msg{payload = Payload}} -> - ok - after 1000 -> - exit({not_received, Payload}) - end, - amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}). + Msg = receive + {#'basic.deliver'{}, #amqp_msg{payload = Payload} = M} -> + M + after 1000 -> + exit({not_received, Payload}) + end, + amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}), + Msg. expect_empty(Ch, Q) -> ?assertMatch(#'basic.get_empty'{}, @@ -239,7 +257,7 @@ invalid_param(Value) -> invalid_param(Value, none). valid_param(Value) -> valid_param(Value, none). lookup_user(Name) -> - {ok, User} = rabbit_auth_backend_internal:check_user_login(Name, []), + {ok, User} = rabbit_access_control:check_user_login(Name, []), User. clear_param(Name) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-stomp/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/README.md b/rabbitmq-server/plugins-src/rabbitmq-stomp/README.md index df7b890..782d563 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/README.md +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/README.md @@ -5,9 +5,10 @@ it, use rabbitmq rabbitmq-plugins enable rabbitmq_stomp -Binaries for previous versions of the STOMP adapter can be obtained -from -. +## Supported STOMP Versions -Full usage instructions can be found at -. +1.0 through 1.2. + +## Documentation + +[RabbitMQ STOMP plugin documentation](http://www.rabbitmq.com/stomp.html). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/Makefile b/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/Makefile index a937fb5..40f5bd1 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/Makefile @@ -1,8 +1,8 @@ -UPSTREAM_HG=https://stomppy.googlecode.com/hg/ -REVISION=16a4000624a7 +UPSTREAM_GIT=https://github.com/jasonrbriggs/stomp.py.git +REVISION=v4.0.16 LIB_DIR=stomppy -CHECKOUT_DIR=stomppy-hg +CHECKOUT_DIR=stomppy-git TARGETS=$(LIB_DIR) @@ -14,14 +14,13 @@ clean: distclean: clean rm -rf $(CHECKOUT_DIR) -$(LIB_DIR) : $(CHECKOUT_DIR) rabbit.patch +$(LIB_DIR) : $(CHECKOUT_DIR) rm -rf $@ cp -R $< $@ - cd $@ && patch -p1 < ../rabbit.patch $(CHECKOUT_DIR): - hg clone $(UPSTREAM_HG) $@ - (cd $@ && hg up $(REVISION)) || rm -rf $@ + git clone $(UPSTREAM_GIT) $@ + (cd $@ && git checkout $(REVISION)) || rm -rf $@ echo-revision: @echo $(REVISION) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/rabbit.patch b/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/rabbit.patch deleted file mode 100644 index ceebd16..0000000 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/deps/stomppy/rabbit.patch +++ /dev/null @@ -1,107 +0,0 @@ -diff -r 16a4000624a7 stomp/connect.py ---- a/stomp/connect.py Sun May 02 18:15:34 2010 +0100 -+++ b/stomp/connect.py Fri Aug 26 15:35:33 2011 +0100 -@@ -88,7 +88,10 @@ - ssl_key_file = None, - ssl_cert_file = None, - ssl_ca_certs = None, -- ssl_cert_validator = None): -+ ssl_cert_validator = None, -+ version = None, -+ heartbeat = None, -+ virtual_host = None): - """ - Initialize and start this connection. - -@@ -159,6 +162,16 @@ - - where OK is a boolean, and cert is a certificate structure - as returned by ssl.SSLSocket.getpeercert() -+ -+ \param version -+ (optional) stomp version header to send (comma separated) -+ -+ \param heartbeat -+ (optional) heartbeat header to send (STOMP 1.1) -+ -+ \param virtual_host -+ (optional) virtual_host header to send (STOMP 1.1) -+ - """ - - sorted_host_and_ports = [] -@@ -205,6 +218,15 @@ - self.__connect_headers['login'] = user - self.__connect_headers['passcode'] = passcode - -+ if version is not None: -+ self.__connect_headers['accept-version'] = version -+ -+ if heartbeat is not None: -+ self.__connect_headers['heart-beat'] = heartbeat -+ -+ if virtual_host is not None: -+ self.__connect_headers['host'] = virtual_host -+ - self.__socket = None - self.__socket_semaphore = threading.BoundedSemaphore(1) - self.__current_host_and_port = None -@@ -383,6 +405,10 @@ - """ - self.__send_frame_helper('DISCONNECT', '', utils.merge_headers([self.__connect_headers, headers, keyword_headers]), [ ]) - self.__running = False -+ self.close_socket() -+ self.__current_host_and_port = None -+ -+ def close_socket(self): - if self.__socket is not None: - if self.__ssl: - # -@@ -390,20 +416,23 @@ - # - try: - self.__socket = self.__socket.unwrap() -- except Exception: -+ except Exception as e: - # - # unwrap seems flaky on Win with the backported ssl mod, so catch any exception and log it - # -- _, e, _ = sys.exc_info() -- log.warn(e) -+ log.warning("socket unwrap() threw exception: %s" % e) - elif hasattr(socket, 'SHUT_RDWR'): -- self.__socket.shutdown(socket.SHUT_RDWR) -+ try: -+ self.__socket.shutdown(socket.SHUT_RDWR) -+ except Exception as e: -+ log.warning("socket shutdown() threw exception: %s" % e) - # -- # split this into a separate check, because sometimes the socket is nulled between shutdown and this call -+ # caution, because sometimes the socket is nulled between shutdown and this call - # -- if self.__socket is not None: -+ try: - self.__socket.close() -- self.__current_host_and_port = None -+ except Exception as e: -+ log.warning("socket close() threw exception: %s" % e) - - def __convert_dict(self, payload): - """ -@@ -449,6 +478,9 @@ - raise KeyError("Command %s requires header %r" % (command, required_header_key)) - self.__send_frame(command, headers, payload) - -+ def send_frame(self, command, headers={}, payload=''): -+ self.__send_frame(command, headers, payload) -+ - def __send_frame(self, command, headers={}, payload=''): - """ - Send a STOMP frame. -@@ -680,4 +712,4 @@ - sleep_exp += 1 - - if not self.__socket: -- raise exception.ReconnectFailedException -\ No newline at end of file -+ raise exception.ReconnectFailedException diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp.hrl b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp.hrl index 62504a0..d1497f4 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -record(stomp_configuration, {default_login, diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_frame.hrl b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_frame.hrl index 77a946c..77d5810 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_frame.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_frame.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -record(stomp_frame, {command, headers, body_iolist}). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_headers.hrl b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_headers.hrl index c7ab43c..398ce42 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_headers.hrl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/include/rabbit_stomp_headers.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2011-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. %% -define(HEADER_ACCEPT_VERSION, "accept-version"). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/package.mk b/rabbitmq-server/plugins-src/rabbitmq-stomp/package.mk index bddbfde..67cb2c8 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/package.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/package.mk @@ -1,48 +1,24 @@ RELEASABLE:=true -DEPS:=rabbitmq-server rabbitmq-erlang-client -STANDALONE_TEST_COMMANDS:=eunit:test([rabbit_stomp_test_util,rabbit_stomp_test_frame],[verbose]) -WITH_BROKER_TEST_SCRIPTS:=$(PACKAGE_DIR)/test/src/test.py $(PACKAGE_DIR)/test/src/test_connect_options.py -WITH_BROKER_TEST_COMMANDS:=rabbit_stomp_test:all_tests() rabbit_stomp_amqqueue_test:all_tests() - -RABBITMQ_TEST_PATH=$(PACKAGE_DIR)/../rabbitmq-test -ABS_PACKAGE_DIR:=$(abspath $(PACKAGE_DIR)) - -CERTS_DIR:=$(ABS_PACKAGE_DIR)/test/certs -CAN_RUN_SSL:=$(shell if [ -d $(RABBITMQ_TEST_PATH) ]; then echo "true"; else echo "false"; fi) - -TEST_CONFIG_PATH=$(TEST_EBIN_DIR)/test.config -WITH_BROKER_TEST_CONFIG:=$(TEST_EBIN_DIR)/test - -.PHONY: $(TEST_CONFIG_PATH) - -ifeq ($(CAN_RUN_SSL),true) - -WITH_BROKER_TEST_SCRIPTS += $(PACKAGE_DIR)/test/src/test_ssl.py - -$(TEST_CONFIG_PATH): $(CERTS_DIR) $(ABS_PACKAGE_DIR)/test/src/ssl.config - sed -e "s|%%CERTS_DIR%%|$(CERTS_DIR)|g" < $(ABS_PACKAGE_DIR)/test/src/ssl.config > $@ - @echo "\nRunning SSL tests\n" - -$(CERTS_DIR): - mkdir -p $(CERTS_DIR) - make -C $(RABBITMQ_TEST_PATH)/certs all PASSWORD=test DIR=$(CERTS_DIR) - -else -$(TEST_CONFIG_PATH): $(ABS_PACKAGE_DIR)/test/src/non_ssl.config - cp $(ABS_PACKAGE_DIR)/test/src/non_ssl.config $@ - @echo "\nNOT running SSL tests - looked in $(RABBITMQ_TEST_PATH) \n" - -endif +DEPS:=rabbitmq-server rabbitmq-erlang-client rabbitmq-test +#STANDALONE_TEST_COMMANDS:=eunit:test([rabbit_stomp_test_util,rabbit_stomp_test_frame],[verbose]) +WITH_BROKER_TEST_SCRIPTS:=$(PACKAGE_DIR)/test/src/test.py $(PACKAGE_DIR)/test/src/test_connect_options.py $(PACKAGE_DIR)/test/src/test_ssl.py +#WITH_BROKER_TEST_COMMANDS:=rabbit_stomp_test:all_tests() rabbit_stomp_amqqueue_test:all_tests() +WITH_BROKER_TEST_CONFIG:=$(PACKAGE_DIR)/test/ebin/test define package_rules -$(PACKAGE_DIR)+pre-test:: $(TEST_CONFIG_PATH) - make -C $(PACKAGE_DIR)/deps/stomppy +$(PACKAGE_DIR)+pre-test:: + rm -rf $(PACKAGE_DIR)/test/certs + mkdir $(PACKAGE_DIR)/test/certs + mkdir -p $(PACKAGE_DIR)/test/ebin + sed -e "s|%%CERTS_DIR%%|$(abspath $(PACKAGE_DIR))/test/certs|g" < $(PACKAGE_DIR)/test/src/test.config > $(PACKAGE_DIR)/test/ebin/test.config + $(MAKE) -C $(PACKAGE_DIR)/../rabbitmq-test/certs all PASSWORD=test DIR=$(abspath $(PACKAGE_DIR))/test/certs + $(MAKE) -C $(PACKAGE_DIR)/deps/stomppy $(PACKAGE_DIR)+clean:: - rm -rf $(CERTS_DIR) + rm -rf $(PACKAGE_DIR)/test/certs $(PACKAGE_DIR)+clean-with-deps:: - make -C $(PACKAGE_DIR)/deps/stomppy distclean + $(MAKE) -C $(PACKAGE_DIR)/deps/stomppy distclean endef diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp.erl index bb8f7f9..bd867aa 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_client_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_client_sup.erl index d0f41b7..4f293ed 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_client_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_client_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_client_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_frame.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_frame.erl index 335a3a8..ecd6365 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_frame.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_frame.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% %% stomp_frame implements the STOMP framing protocol "version 1.0", as @@ -151,9 +151,11 @@ insert_header(Headers, Name, Value) -> false -> [{Name, Value} | Headers] end. -parse_body(Content, Frame) -> - parse_body(Content, Frame, [], - integer_header(Frame, ?HEADER_CONTENT_LENGTH, unknown)). +parse_body(Content, Frame = #stomp_frame{command = Command}) -> + case Command of + "SEND" -> parse_body(Content, Frame, [], integer_header(Frame, ?HEADER_CONTENT_LENGTH, unknown)); + _ -> parse_body(Content, Frame, [], unknown) + end. parse_body(Content, Frame, Chunks, unknown) -> parse_body2(Content, Frame, Chunks, case firstnull(Content) of @@ -193,6 +195,9 @@ boolean_header(#stomp_frame{headers = Headers}, Key) -> case lists:keysearch(Key, 1, Headers) of {value, {_, "true"}} -> {ok, true}; {value, {_, "false"}} -> {ok, false}; + %% some Python clients serialize True/False as "True"/"False" + {value, {_, "True"}} -> {ok, true}; + {value, {_, "False"}} -> {ok, false}; _ -> not_found end. @@ -228,7 +233,7 @@ serialize(#stomp_frame{command = Command, Len > 0 -> [?HEADER_CONTENT_LENGTH ++ ":", integer_to_list(Len), ?LF]; true -> [] end, - ?LF, BodyFragments, 0]. + ?LF, BodyFragments, 0, ?LF]. serialize_header({K, V}) when is_integer(V) -> hdr(escape(K), integer_to_list(V)); serialize_header({K, V}) when is_list(V) -> hdr(escape(K), escape(V)). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_processor.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_processor.erl index 7d8ce27..0a6dae7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_processor.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_processor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_processor). @@ -146,8 +146,11 @@ handle_info(#'basic.cancel_ok'{}, State) -> handle_info(#'basic.ack'{delivery_tag = Tag, multiple = IsMulti}, State) -> {noreply, flush_pending_receipts(Tag, IsMulti, State), hibernate}; handle_info({Delivery = #'basic.deliver'{}, - #amqp_msg{props = Props, payload = Payload}}, State) -> - {noreply, send_delivery(Delivery, Props, Payload, State), hibernate}; + #amqp_msg{props = Props, payload = Payload}, + DeliveryCtx}, State) -> + State1 = send_delivery(Delivery, Props, Payload, State), + amqp_channel:notify_received(DeliveryCtx), + {noreply, State1, hibernate}; handle_info(#'basic.cancel'{consumer_tag = Ctag}, State) -> process_request( fun(StateN) -> server_cancel_consumer(Ctag, StateN) end, State); @@ -155,9 +158,24 @@ handle_info({'EXIT', Conn, {shutdown, {server_initiated_close, Code, Explanation}}}, State = #state{connection = Conn}) -> amqp_death(Code, Explanation, State); +handle_info({'EXIT', Conn, + {shutdown, {connection_closing, + {server_initiated_close, Code, Explanation}}}}, + State = #state{connection = Conn}) -> + amqp_death(Code, Explanation, State); handle_info({'EXIT', Conn, Reason}, State = #state{connection = Conn}) -> send_error("AMQP connection died", "Reason: ~p", [Reason], State), {stop, {conn_died, Reason}, State}; + +handle_info({'EXIT', Ch, Reason}, State = #state{channel = Ch}) -> + send_error("AMQP channel died", "Reason: ~p", [Reason], State), + {stop, {channel_died, Reason}, State}; +handle_info({'EXIT', Ch, + {shutdown, {server_initiated_close, Code, Explanation}}}, + State = #state{channel = Ch}) -> + amqp_death(Code, Explanation, State); + + handle_info({inet_reply, _, ok}, State) -> {noreply, State, hibernate}; handle_info({bump_credit, Msg}, State) -> @@ -294,10 +312,10 @@ handle_frame("SEND", Frame, State) -> end); handle_frame("ACK", Frame, State) -> - ack_action("ACK", Frame, State, fun create_ack_method/2); + ack_action("ACK", Frame, State, fun create_ack_method/3); handle_frame("NACK", Frame, State) -> - ack_action("NACK", Frame, State, fun create_nack_method/2); + ack_action("NACK", Frame, State, fun create_nack_method/3); handle_frame("BEGIN", Frame, State) -> transactional_action(Frame, "BEGIN", fun begin_transaction/2, State); @@ -329,7 +347,8 @@ ack_action(Command, Frame, {ok, {ConsumerTag, _SessionId, DeliveryTag}} -> case dict:find(ConsumerTag, Subs) of {ok, Sub} -> - Method = MethodFun(DeliveryTag, Sub), + Requeue = rabbit_stomp_frame:boolean_header(Frame, "requeue", true), + Method = MethodFun(DeliveryTag, Sub, Requeue), case transactional(Frame) of {yes, Transaction} -> extend_transaction( @@ -430,7 +449,7 @@ maybe_delete_durable_sub({topic, Name}, Frame, ?HEADER_PERSISTENT, false) of true -> {ok, Id} = rabbit_stomp_frame:header(Frame, ?HEADER_ID), - QName = rabbit_stomp_util:durable_subscription_queue(Name, Id), + QName = rabbit_stomp_util:subscription_queue_name(Name, Id), amqp_channel:call(Channel, #'queue.delete'{queue = list_to_binary(QName), nowait = false}), @@ -507,6 +526,8 @@ do_login(Username, Passwd, VirtualHost, Heartbeat, AdapterInfo, Version, {ok, Connection} -> link(Connection), {ok, Channel} = amqp_connection:open_channel(Connection), + link(Channel), + amqp_channel:enable_delivery_flow_control(Channel), SessionId = rabbit_guid:string(rabbit_guid:gen_secure(), "session"), {{SendTimeout, ReceiveTimeout}, State1} = ensure_heartbeats(Heartbeat, State), @@ -656,13 +677,14 @@ do_send(Destination, _DestHdr, Err end. -create_ack_method(DeliveryTag, #subscription{multi_ack = IsMulti}) -> +create_ack_method(DeliveryTag, #subscription{multi_ack = IsMulti}, _) -> #'basic.ack'{delivery_tag = DeliveryTag, multiple = IsMulti}. -create_nack_method(DeliveryTag, #subscription{multi_ack = IsMulti}) -> +create_nack_method(DeliveryTag, #subscription{multi_ack = IsMulti}, Requeue) -> #'basic.nack'{delivery_tag = DeliveryTag, - multiple = IsMulti}. + multiple = IsMulti, + requeue = Requeue}. negotiate_version(Frame) -> ClientVers = re:split(rabbit_stomp_frame:header( @@ -691,6 +713,7 @@ send_delivery(Delivery = #'basic.deliver'{consumer_tag = ConsumerTag}, State) end. + send_method(Method, Channel, State) -> amqp_channel:call(Channel, Method), State. @@ -960,12 +983,20 @@ ensure_endpoint(source, EndPoint, Frame, Channel, State) -> {ok, Id} = rabbit_stomp_frame:header(Frame, ?HEADER_ID), {_, Name} = rabbit_routing_util:parse_routing(EndPoint), list_to_binary( - rabbit_stomp_util:durable_subscription_queue(Name, - Id)) + rabbit_stomp_util:subscription_queue_name(Name, + Id)) end}, {durable, true}]; false -> - [{durable, false}] + [{subscription_queue_name_gen, + fun () -> + Id = rabbit_guid:gen_secure(), + {_, Name} = rabbit_routing_util:parse_routing(EndPoint), + list_to_binary( + rabbit_stomp_util:subscription_queue_name(Name, + Id)) + end}, + {durable, false}] end, rabbit_routing_util:ensure_endpoint(source, Channel, EndPoint, Params, State); diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_reader.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_reader.erl index f4b0175..673afee 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_reader.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_reader.erl @@ -11,13 +11,14 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_reader). -export([start_link/3]). --export([init/3]). +-export([init/3, mainloop/2]). +-export([system_continue/3, system_terminate/4, system_code_change/4]). -export([conserve_resources/3]). -include("rabbit_stomp.hrl"). @@ -25,7 +26,8 @@ -include_lib("amqp_client/include/amqp_client.hrl"). -record(reader_state, {socket, parse_state, processor, state, - conserve_resources, recv_outstanding}). + conserve_resources, recv_outstanding, + parent}). %%---------------------------------------------------------------------------- @@ -48,7 +50,7 @@ go(SupHelperPid, ProcessorPid, Configuration) -> {ok, ConnStr} -> case SockTransform(Sock0) of {ok, Sock} -> - + DebugOpts = sys:debug_options([]), ProcInitArgs = processor_args(SupHelperPid, Configuration, Sock), @@ -59,7 +61,7 @@ go(SupHelperPid, ProcessorPid, Configuration) -> ParseState = rabbit_stomp_frame:initial_state(), try - mainloop( + mainloop(DebugOpts, register_resource_alarm( #reader_state{socket = Sock, parse_state = ParseState, @@ -72,35 +74,43 @@ go(SupHelperPid, ProcessorPid, Configuration) -> catch _:Ex -> log_network_error(ConnStr, Ex), rabbit_net:fast_close(Sock), + rabbit_stomp_processor:flush_and_die(ProcessorPid), exit(normal) end, done; {error, enotconn} -> rabbit_net:fast_close(Sock0), + rabbit_stomp_processor:flush_and_die(ProcessorPid), exit(normal); {error, Reason} -> log_network_error(ConnStr, Reason), rabbit_net:fast_close(Sock0), + rabbit_stomp_processor:flush_and_die(ProcessorPid), exit(normal) end end end. -mainloop(State0 = #reader_state{socket = Sock}) -> +mainloop(DebugOpts, State0 = #reader_state{socket = Sock}) -> State = run_socket(control_throttle(State0)), receive {inet_async, Sock, _Ref, {ok, Data}} -> - mainloop(process_received_bytes( + mainloop(DebugOpts, process_received_bytes( Data, State#reader_state{recv_outstanding = false})); {inet_async, _Sock, _Ref, {error, closed}} -> ok; {inet_async, _Sock, _Ref, {error, Reason}} -> throw({inet_error, Reason}); + {inet_reply, _Sock, {error, closed}} -> + ok; {conserve_resources, Conserve} -> - mainloop(State#reader_state{conserve_resources = Conserve}); + mainloop(DebugOpts, State#reader_state{conserve_resources = Conserve}); {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - mainloop(State); + mainloop(DebugOpts, State); + {system, From, Request} -> + sys:handle_system_msg(Request, From, State#reader_state.parent, + ?MODULE, DebugOpts, State); {'EXIT', _From, shutdown} -> ok; Other -> @@ -158,6 +168,17 @@ run_socket(State = #reader_state{socket = Sock}) -> %%---------------------------------------------------------------------------- +system_continue(Parent, DebugOpts, State) -> + mainloop(DebugOpts, State#reader_state{parent = Parent}). + +system_terminate(Reason, _Parent, _OldVsn, _Extra) -> + exit(Reason). + +system_code_change(Misc, _Module, _OldSvn, _Extra) -> + {ok, Misc}. + +%%---------------------------------------------------------------------------- + processor_args(SupPid, Configuration, Sock) -> SendFun = fun (sync, IoData) -> %% no messages emitted diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_sup.erl index 01fa595..d376e5e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_sup). @@ -25,14 +25,17 @@ start_link(Listeners, Configuration) -> supervisor:start_link({local, ?MODULE}, ?MODULE, [Listeners, Configuration]). -init([{Listeners, SslListeners}, Configuration]) -> +init([{Listeners, SslListeners0}, Configuration]) -> {ok, SocketOpts} = application:get_env(rabbitmq_stomp, tcp_listen_options), - - SslOpts = case SslListeners of - [] -> none; - _ -> rabbit_networking:ensure_ssl() - end, - + {SslOpts, SslListeners} + = case SslListeners0 of + [] -> {none, []}; + _ -> {rabbit_networking:ensure_ssl(), + case rabbit_networking:poodle_check('STOMP') of + ok -> SslListeners0; + danger -> [] + end} + end, {ok, {{one_for_all, 10, 10}, [{rabbit_stomp_client_sup_sup, {rabbit_client_sup, start_link, diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_util.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_util.erl index d91ba43..bb8530e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/src/rabbit_stomp_util.erl @@ -11,12 +11,12 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_util). --export([parse_message_id/1, durable_subscription_queue/2]). +-export([parse_message_id/1, subscription_queue_name/2]). -export([longstr_field/2]). -export([ack_mode/1, consumer_tag_reply_to/1, consumer_tag/1, message_headers/1, headers_post_process/1, headers/5, message_properties/1, tag_to_id/1, @@ -277,14 +277,14 @@ format_destination(Exchange, RoutingKey) -> %% Destination Parsing %%-------------------------------------------------------------------- -durable_subscription_queue(Destination, SubscriptionId) -> +subscription_queue_name(Destination, SubscriptionId) -> %% We need a queue name that a) can be derived from the %% Destination and SubscriptionId, and b) meets the constraints on %% AMQP queue names. It doesn't need to be secure; we use md5 here %% simply as a convenient means to bound the length. rabbit_guid:string( erlang:md5(term_to_binary({Destination, SubscriptionId})), - "stomp.dsub"). + "stomp-subscription"). %% ---- Helpers ---- diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ack.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ack.py index 0b24c42..4c47cd3 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ack.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ack.py @@ -6,14 +6,15 @@ import time class TestAck(base.BaseTest): def test_ack_client(self): - d = "/queue/ack-test" + destination = "/queue/ack-test" # subscribe and send message self.listener.reset(2) ## expecting 2 messages - self.conn.subscribe(destination=d, ack='client', + self.subscribe_dest(self.conn, destination, None, + ack='client', headers={'prefetch-count': '10'}) - self.conn.send("test1", destination=d) - self.conn.send("test2", destination=d) + self.conn.send(destination, "test1") + self.conn.send(destination, "test2") self.assertTrue(self.listener.await(4), "initial message not received") self.assertEquals(2, len(self.listener.messages)) @@ -26,37 +27,39 @@ class TestAck(base.BaseTest): listener2 = base.WaitableListener() listener2.reset(2) conn2.set_listener('', listener2) - conn2.subscribe(destination=d, ack='client', - headers={'prefetch-count': '10'}) + self.subscribe_dest(conn2, destination, None, + ack='client', + headers={'prefetch-count': '10'}) self.assertTrue(listener2.await(), "message not received again") self.assertEquals(2, len(listener2.messages)) # now ack only the last message - expecting cumulative behaviour - mid = listener2.messages[1]['headers']['message-id'] - conn2.ack({'message-id':mid}) + mid = listener2.messages[1]['headers'][self.ack_id_source_header] + self.ack_message(conn2, mid, None) finally: - conn2.stop() + conn2.disconnect() # now reconnect again, shouldn't see the message conn3 = self.create_connection() try: listener3 = base.WaitableListener() conn3.set_listener('', listener3) - conn3.subscribe(destination=d) + self.subscribe_dest(conn3, destination, None) self.assertFalse(listener3.await(3), "unexpected message. ACK not working?") finally: - conn3.stop() + conn3.disconnect() def test_ack_client_individual(self): - d = "/queue/ack-test-individual" + destination = "/queue/ack-test-individual" # subscribe and send message self.listener.reset(2) ## expecting 2 messages - self.conn.subscribe(destination=d, ack='client-individual', + self.subscribe_dest(self.conn, destination, None, + ack='client-individual', headers={'prefetch-count': '10'}) - self.conn.send("test1", destination=d) - self.conn.send("test2", destination=d) + self.conn.send(destination, "test1") + self.conn.send(destination, "test2") self.assertTrue(self.listener.await(4), "Both initial messages not received") self.assertEquals(2, len(self.listener.messages)) @@ -69,8 +72,9 @@ class TestAck(base.BaseTest): listener2 = base.WaitableListener() listener2.reset(2) ## expect 2 messages conn2.set_listener('', listener2) - conn2.subscribe(destination=d, ack='client-individual', - headers={'prefetch-count': '10'}) + self.subscribe_dest(conn2, destination, None, + ack='client-individual', + headers={'prefetch-count': '10'}) self.assertTrue(listener2.await(2.5), "Did not receive 2 messages") self.assertEquals(2, len(listener2.messages), "Not exactly 2 messages received") @@ -79,13 +83,13 @@ class TestAck(base.BaseTest): mid = None for ind in range(nummsgs): if listener2.messages[ind]['message']=="test2": - mid = listener2.messages[ind]['headers']['message-id'] + mid = listener2.messages[ind]['headers'][self.ack_id_source_header] self.assertEquals(1, ind, 'Expecting test2 to be second message') break self.assertTrue(mid, "Did not find test2 message id.") - conn2.ack({'message-id':mid}) + self.ack_message(conn2, mid, None) finally: - conn2.stop() + conn2.disconnect() # now reconnect again, shouldn't see the message conn3 = self.create_connection() @@ -93,21 +97,21 @@ class TestAck(base.BaseTest): listener3 = base.WaitableListener() listener3.reset(2) ## expecting a single message, but wait for two conn3.set_listener('', listener3) - conn3.subscribe(destination=d) + self.subscribe_dest(conn3, destination, None) self.assertFalse(listener3.await(2.5), "Expected to see only one message. ACK not working?") self.assertEquals(1, len(listener3.messages), "Expecting exactly one message") self.assertEquals("test1", listener3.messages[0]['message'], "Unexpected message remains") finally: - conn3.stop() + conn3.disconnect() def test_ack_client_tx(self): - d = "/queue/ack-test-tx" + destination = "/queue/ack-test-tx" # subscribe and send message self.listener.reset() - self.conn.subscribe(destination=d, ack='client') - self.conn.send("test", destination=d) + self.subscribe_dest(self.conn, destination, None, ack='client') + self.conn.send(destination, "test") self.assertTrue(self.listener.await(3), "initial message not received") self.assertEquals(1, len(self.listener.messages)) @@ -121,77 +125,94 @@ class TestAck(base.BaseTest): listener2 = base.WaitableListener() conn2.set_listener('', listener2) conn2.begin(transaction=tx) - conn2.subscribe(destination=d, ack='client') + self.subscribe_dest(conn2, destination, None, ack='client') self.assertTrue(listener2.await(), "message not received again") self.assertEquals(1, len(listener2.messages)) # now ack - mid = listener2.messages[0]['headers']['message-id'] - conn2.ack({'message-id':mid, 'transaction':tx}) + mid = listener2.messages[0]['headers'][self.ack_id_source_header] + self.ack_message(conn2, mid, None, transaction=tx) #now commit conn2.commit(transaction=tx) finally: - conn2.stop() + conn2.disconnect() # now reconnect again, shouldn't see the message conn3 = self.create_connection() try: listener3 = base.WaitableListener() conn3.set_listener('', listener3) - conn3.subscribe(destination=d) + self.subscribe_dest(conn3, destination, None) self.assertFalse(listener3.await(3), "unexpected message. TX ACK not working?") finally: - conn3.stop() + conn3.disconnect() def test_topic_prefetch(self): - d = "/topic/prefetch-test" + destination = "/topic/prefetch-test" # subscribe and send message self.listener.reset(6) ## expect 6 messages - self.conn.subscribe(destination=d, ack='client', + self.subscribe_dest(self.conn, destination, None, + ack='client', headers={'prefetch-count': '5'}) for x in range(10): - self.conn.send("test" + str(x), destination=d) + self.conn.send(destination, "test" + str(x)) self.assertFalse(self.listener.await(3), "Should not have been able to see 6 messages") self.assertEquals(5, len(self.listener.messages)) def test_nack(self): - d = "/queue/nack-test" + destination = "/queue/nack-test" #subscribe and send - self.conn.subscribe(destination=d, ack='client-individual') - self.conn.send("nack-test", destination=d) + self.subscribe_dest(self.conn, destination, None, + ack='client-individual') + self.conn.send(destination, "nack-test") self.assertTrue(self.listener.await(), "Not received message") - message_id = self.listener.messages[0]['headers']['message-id'] + message_id = self.listener.messages[0]['headers'][self.ack_id_source_header] self.listener.reset() - self.conn.send_frame("NACK", {"message-id" : message_id}) + self.nack_message(self.conn, message_id, None) self.assertTrue(self.listener.await(), "Not received message after NACK") - message_id = self.listener.messages[0]['headers']['message-id'] - self.conn.ack({'message-id' : message_id}) + message_id = self.listener.messages[0]['headers'][self.ack_id_source_header] + self.ack_message(self.conn, message_id, None) def test_nack_multi(self): - d = "/queue/nack-multi" + destination = "/queue/nack-multi" self.listener.reset(2) #subscribe and send - self.conn.subscribe(destination=d, ack='client', + self.subscribe_dest(self.conn, destination, None, + ack='client', headers = {'prefetch-count' : '10'}) - self.conn.send("nack-test1", destination=d) - self.conn.send("nack-test2", destination=d) + self.conn.send(destination, "nack-test1") + self.conn.send(destination, "nack-test2") self.assertTrue(self.listener.await(), "Not received messages") - message_id = self.listener.messages[1]['headers']['message-id'] + message_id = self.listener.messages[1]['headers'][self.ack_id_source_header] self.listener.reset(2) - self.conn.send_frame("NACK", {"message-id" : message_id}) + self.nack_message(self.conn, message_id, None) self.assertTrue(self.listener.await(), "Not received message again") - message_id = self.listener.messages[1]['headers']['message-id'] - self.conn.ack({'message-id' : message_id}) + message_id = self.listener.messages[1]['headers'][self.ack_id_source_header] + self.ack_message(self.conn, message_id, None) + + def test_nack_without_requeueing(self): + destination = "/queue/nack-test-no-requeue" + + self.subscribe_dest(self.conn, destination, None, + ack='client-individual') + self.conn.send(destination, "nack-test") + + self.assertTrue(self.listener.await(), "Not received message") + message_id = self.listener.messages[0]['headers'][self.ack_id_source_header] + self.listener.reset() + + self.conn.send_frame("NACK", {self.ack_id_header: message_id, "requeue": False}) + self.assertFalse(self.listener.await(4), "Received message after NACK with requeue = False") diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/base.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/base.py index 4db8433..e3d5819 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/base.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/base.py @@ -6,18 +6,84 @@ import threading class BaseTest(unittest.TestCase): - def create_connection(self, version=None, heartbeat=None): - conn = stomp.Connection(user="guest", passcode="guest", - version=version, heartbeat=heartbeat) + def create_connection_obj(self, version='1.0', vhost='/', heartbeats=(0, 0)): + if version == '1.0': + conn = stomp.StompConnection10() + self.ack_id_source_header = 'message-id' + self.ack_id_header = 'message-id' + elif version == '1.1': + conn = stomp.StompConnection11(vhost=vhost, + heartbeats=heartbeats) + self.ack_id_source_header = 'message-id' + self.ack_id_header = 'message-id' + elif version == '1.2': + conn = stomp.StompConnection12(vhost=vhost, + heartbeats=heartbeats) + self.ack_id_source_header = 'ack' + self.ack_id_header = 'id' + else: + conn = stomp.StompConnection12(vhost=vhost, + heartbeats=heartbeats) + conn.version = version + return conn + + def create_connection(self, user='guest', passcode='guest', wait=True, **kwargs): + conn = self.create_connection_obj(**kwargs) conn.start() - conn.connect() + conn.connect(user, passcode, wait=wait) return conn + def subscribe_dest(self, conn, destination, sub_id, **kwargs): + if type(conn) is stomp.StompConnection10: + # 'id' is optional in STOMP 1.0. + if sub_id != None: + kwargs['id'] = sub_id + conn.subscribe(destination, **kwargs) + else: + # 'id' is required in STOMP 1.1+. + if sub_id == None: + sub_id = 'ctag' + conn.subscribe(destination, sub_id, **kwargs) + + def unsubscribe_dest(self, conn, destination, sub_id, **kwargs): + if type(conn) is stomp.StompConnection10: + # 'id' is optional in STOMP 1.0. + if sub_id != None: + conn.unsubscribe(id=sub_id, **kwargs) + else: + conn.unsubscribe(destination=destination, **kwargs) + else: + # 'id' is required in STOMP 1.1+. + if sub_id == None: + sub_id = 'ctag' + conn.unsubscribe(sub_id, **kwargs) + + def ack_message(self, conn, msg_id, sub_id, **kwargs): + if type(conn) is stomp.StompConnection10: + conn.ack(msg_id, **kwargs) + elif type(conn) is stomp.StompConnection11: + if sub_id == None: + sub_id = 'ctag' + conn.ack(msg_id, sub_id, **kwargs) + elif type(conn) is stomp.StompConnection12: + conn.ack(msg_id, **kwargs) + + def nack_message(self, conn, msg_id, sub_id, **kwargs): + if type(conn) is stomp.StompConnection10: + # Normally unsupported by STOMP 1.0. + conn.send_frame("NACK", {"message-id": msg_id}) + elif type(conn) is stomp.StompConnection11: + if sub_id == None: + sub_id = 'ctag' + conn.nack(msg_id, sub_id, **kwargs) + elif type(conn) is stomp.StompConnection12: + conn.nack(msg_id, **kwargs) + def create_subscriber_connection(self, dest): conn = self.create_connection() listener = WaitableListener() conn.set_listener('', listener) - conn.subscribe(destination=dest, receipt="sub.receipt") + self.subscribe_dest(conn, dest, None, receipt="sub.receipt") listener.await() self.assertEquals(1, len(listener.receipts)) listener.reset() @@ -30,13 +96,14 @@ class BaseTest(unittest.TestCase): def tearDown(self): if self.conn.is_connected(): + self.conn.disconnect() self.conn.stop() def simple_test_send_rec(self, dest, route = None): self.listener.reset() - self.conn.subscribe(destination=dest) - self.conn.send("foo", destination=dest) + self.subscribe_dest(self.conn, dest, None) + self.conn.send(dest, "foo") self.assertTrue(self.listener.await(), "Timeout, no message received") diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/connect_options.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/connect_options.py index c9e4ad5..d802bc6 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/connect_options.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/connect_options.py @@ -14,7 +14,8 @@ class TestConnectOptions(base.BaseTest): new_conn.set_listener('', listener) new_conn.start() # not going to issue connect - new_conn.subscribe(destination="/topic/implicit", id='sub_implicit', receipt='implicit') + self.subscribe_dest(new_conn, "/topic/implicit", 'sub_implicit', + receipt='implicit') try: self.assertTrue(listener.await(5)) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/destinations.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/destinations.py index 18dec83..b1d0cd1 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/destinations.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/destinations.py @@ -25,7 +25,8 @@ class TestExchange(base.BaseTest): def test_invalid_exchange(self): ''' Test invalid exchange error ''' self.listener.reset(1) - self.conn.subscribe(destination="/exchange/does.not.exist") + self.subscribe_dest(self.conn, "/exchange/does.not.exist", None, + ack="auto") self.assertListener("Expecting an error", numErrs=1) err = self.listener.errors[0] self.assertEquals("not_found", err['headers']['message']) @@ -49,15 +50,15 @@ class TestQueue(base.BaseTest): def test_send_receive(self): ''' Test basic send/receive for /queue ''' - d = '/queue/test' - self.simple_test_send_rec(d) + destination = '/queue/test' + self.simple_test_send_rec(destination) def test_send_receive_in_other_conn(self): ''' Test send in one connection, receive in another ''' - d = '/queue/test2' + destination = '/queue/test2' # send - self.conn.send("hello", destination=d) + self.conn.send(destination, "hello") # now receive conn2 = self.create_connection() @@ -65,19 +66,19 @@ class TestQueue(base.BaseTest): listener2 = base.WaitableListener() conn2.set_listener('', listener2) - conn2.subscribe(destination=d) + self.subscribe_dest(conn2, destination, None, ack="auto") self.assertTrue(listener2.await(10), "no receive") finally: - conn2.stop() + conn2.disconnect() def test_send_receive_in_other_conn_with_disconnect(self): ''' Test send, disconnect, receive ''' - d = '/queue/test3' + destination = '/queue/test3' # send - self.conn.send("hello thar", destination=d, receipt="foo") + self.conn.send(destination, "hello thar", receipt="foo") self.listener.await(3) - self.conn.stop() + self.conn.disconnect() # now receive conn2 = self.create_connection() @@ -85,24 +86,24 @@ class TestQueue(base.BaseTest): listener2 = base.WaitableListener() conn2.set_listener('', listener2) - conn2.subscribe(destination=d) + self.subscribe_dest(conn2, destination, None, ack="auto") self.assertTrue(listener2.await(10), "no receive") finally: - conn2.stop() + conn2.disconnect() def test_multi_subscribers(self): ''' Test multiple subscribers against a single /queue destination ''' - d = '/queue/test-multi' + destination = '/queue/test-multi' ## set up two subscribers - conn1, listener1 = self.create_subscriber_connection(d) - conn2, listener2 = self.create_subscriber_connection(d) + conn1, listener1 = self.create_subscriber_connection(destination) + conn2, listener2 = self.create_subscriber_connection(destination) try: ## now send - self.conn.send("test1", destination=d) - self.conn.send("test2", destination=d) + self.conn.send(destination, "test1") + self.conn.send(destination, "test2") ## expect both consumers to get a message? self.assertTrue(listener1.await(2)) @@ -112,16 +113,16 @@ class TestQueue(base.BaseTest): self.assertEquals(1, len(listener2.messages), "unexpected message count") finally: - conn1.stop() - conn2.stop() + conn1.disconnect() + conn2.disconnect() def test_send_with_receipt(self): - d = '/queue/test-receipt' + destination = '/queue/test-receipt' def noop(): pass - self.__test_send_receipt(d, noop, noop) + self.__test_send_receipt(destination, noop, noop) def test_send_with_receipt_tx(self): - d = '/queue/test-receipt-tx' + destination = '/queue/test-receipt-tx' tx = 'receipt.tx' def before(): @@ -131,19 +132,19 @@ class TestQueue(base.BaseTest): self.assertFalse(self.listener.await(1)) self.conn.commit(transaction=tx) - self.__test_send_receipt(d, before, after, {'transaction': tx}) + self.__test_send_receipt(destination, before, after, {'transaction': tx}) def test_interleaved_receipt_no_receipt(self): ''' Test i-leaved receipt/no receipt, no-r bracketed by rs ''' - d = '/queue/ir' + destination = '/queue/ir' self.listener.reset(5) - self.conn.subscribe(destination=d) - self.conn.send('first', destination=d, receipt='a') - self.conn.send('second', destination=d) - self.conn.send('third', destination=d, receipt='b') + self.subscribe_dest(self.conn, destination, None, ack="auto") + self.conn.send(destination, 'first', receipt='a') + self.conn.send(destination, 'second') + self.conn.send(destination, 'third', receipt='b') self.assertListener("Missing messages/receipts", numMsgs=3, numRcts=2, timeout=3) @@ -152,18 +153,18 @@ class TestQueue(base.BaseTest): def test_interleaved_receipt_no_receipt_tx(self): ''' Test i-leaved receipt/no receipt, no-r bracketed by r+xactions ''' - d = '/queue/ir' + destination = '/queue/ir' tx = 'tx.ir' # three messages and two receipts self.listener.reset(5) - self.conn.subscribe(destination=d) + self.subscribe_dest(self.conn, destination, None, ack="auto") self.conn.begin(transaction=tx) - self.conn.send('first', destination=d, receipt='a', transaction=tx) - self.conn.send('second', destination=d, transaction=tx) - self.conn.send('third', destination=d, receipt='b', transaction=tx) + self.conn.send(destination, 'first', receipt='a', transaction=tx) + self.conn.send(destination, 'second', transaction=tx) + self.conn.send(destination, 'third', receipt='b', transaction=tx) self.conn.commit(transaction=tx) self.assertListener("Missing messages/receipts", numMsgs=3, numRcts=2, timeout=40) @@ -176,14 +177,14 @@ class TestQueue(base.BaseTest): def test_interleaved_receipt_no_receipt_inverse(self): ''' Test i-leaved receipt/no receipt, r bracketed by no-rs ''' - d = '/queue/ir' + destination = '/queue/ir' self.listener.reset(4) - self.conn.subscribe(destination=d) - self.conn.send('first', destination=d) - self.conn.send('second', destination=d, receipt='a') - self.conn.send('third', destination=d) + self.subscribe_dest(self.conn, destination, None, ack="auto") + self.conn.send(destination, 'first') + self.conn.send(destination, 'second', receipt='a') + self.conn.send(destination, 'third') self.assertListener("Missing messages/receipt", numMsgs=3, numRcts=1, timeout=3) @@ -199,7 +200,7 @@ class TestQueue(base.BaseTest): for x in range(0, count): receipt = "test" + str(x) expected_receipts.add(receipt) - self.conn.send("test receipt", destination=destination, + self.conn.send(destination, "test receipt", receipt=receipt, headers=headers) after() @@ -221,16 +222,16 @@ class TestTopic(base.BaseTest): def test_send_receive(self): ''' Test basic send/receive for /topic ''' - d = '/topic/test' - self.simple_test_send_rec(d) + destination = '/topic/test' + self.simple_test_send_rec(destination) def test_send_multiple(self): ''' Test /topic with multiple consumers ''' - d = '/topic/multiple' + destination = '/topic/multiple' ## set up two subscribers - conn1, listener1 = self.create_subscriber_connection(d) - conn2, listener2 = self.create_subscriber_connection(d) + conn1, listener1 = self.create_subscriber_connection(destination) + conn2, listener2 = self.create_subscriber_connection(destination) try: ## listeners are expecting 2 messages @@ -238,8 +239,8 @@ class TestTopic(base.BaseTest): listener2.reset(2) ## now send - self.conn.send("test1", destination=d) - self.conn.send("test2", destination=d) + self.conn.send(destination, "test1") + self.conn.send(destination, "test2") ## expect both consumers to get both messages self.assertTrue(listener1.await(5)) @@ -249,8 +250,38 @@ class TestTopic(base.BaseTest): self.assertEquals(2, len(listener2.messages), "unexpected message count") finally: - conn1.stop() - conn2.stop() + conn1.disconnect() + conn2.disconnect() + + def test_send_multiple_with_a_large_message(self): + ''' Test /topic with multiple consumers ''' + destination = '/topic/16mb' + # payload size + s = 1024 * 1024 * 16 + message = 'x' * s + + conn1, listener1 = self.create_subscriber_connection(destination) + conn2, listener2 = self.create_subscriber_connection(destination) + + try: + listener1.reset(2) + listener2.reset(2) + + self.conn.send(destination, message) + self.conn.send(destination, message) + + self.assertTrue(listener1.await(10)) + self.assertEquals(2, len(listener1.messages), + "unexpected message count") + self.assertTrue(len(listener2.messages[0]['message']) == s, + "unexpected message size") + + self.assertTrue(listener2.await(10)) + self.assertEquals(2, len(listener2.messages), + "unexpected message count") + finally: + conn1.disconnect() + conn2.disconnect() class TestReplyQueue(base.BaseTest): @@ -268,7 +299,7 @@ class TestReplyQueue(base.BaseTest): conn2, listener2 = self.create_subscriber_connection(known) try: - self.conn.send("test", destination=known, + self.conn.send(known, "test", headers = {"reply-to": reply}) self.assertTrue(listener2.await(5)) @@ -277,11 +308,11 @@ class TestReplyQueue(base.BaseTest): reply_to = listener2.messages[0]['headers']['reply-to'] self.assertTrue(reply_to.startswith('/reply-queue/')) - conn2.send("reply", destination=reply_to) + conn2.send(reply_to, "reply") self.assertTrue(self.listener.await(5)) self.assertEquals("reply", self.listener.messages[0]['message']) finally: - conn2.stop() + conn2.disconnect() def test_reuse_reply_queue(self): ''' Test re-use of reply-to queue ''' @@ -295,7 +326,7 @@ class TestReplyQueue(base.BaseTest): self.assertEquals(1, len(listna.messages)) reply_to = listna.messages[0]['headers']['reply-to'] self.assertTrue(reply_to.startswith('/reply-queue/')) - cntn.send("reply", destination=reply_to) + cntn.send(reply_to, "reply") ## Client 1 uses pre-supplied connection and listener ## Set up clients 2 and 3 @@ -303,9 +334,9 @@ class TestReplyQueue(base.BaseTest): conn3, listener3 = self.create_subscriber_connection(known3) try: self.listener.reset(2) - self.conn.send("test2", destination=known2, + self.conn.send(known2, "test2", headers = {"reply-to": reply}) - self.conn.send("test3", destination=known3, + self.conn.send(known3, "test3", headers = {"reply-to": reply}) respond(conn2, listener2) respond(conn3, listener3) @@ -315,8 +346,8 @@ class TestReplyQueue(base.BaseTest): self.assertEquals("reply", self.listener.messages[0]['message']) self.assertEquals("reply", self.listener.messages[1]['message']) finally: - conn2.stop() - conn3.stop() + conn2.disconnect() + conn3.disconnect() def test_perm_reply_queue(self): '''As test_reply_queue, but with a non-temp reply queue''' @@ -330,7 +361,7 @@ class TestReplyQueue(base.BaseTest): conn2, listener2 = self.create_subscriber_connection(known) try: - conn1.send("test", destination=known, + conn1.send(known, "test", headers = {"reply-to": reply}) self.assertTrue(listener2.await(5)) @@ -339,12 +370,12 @@ class TestReplyQueue(base.BaseTest): reply_to = listener2.messages[0]['headers']['reply-to'] self.assertTrue(reply_to == reply) - conn2.send("reply", destination=reply_to) + conn2.send(reply_to, "reply") self.assertTrue(listener1.await(5)) self.assertEquals("reply", listener1.messages[0]['message']) finally: - conn1.stop() - conn2.stop() + conn1.disconnect() + conn2.disconnect() class TestDurableSubscription(base.BaseTest): @@ -356,10 +387,9 @@ class TestDurableSubscription(base.BaseTest): if not id: id = TestDurableSubscription.ID - conn.subscribe(destination=dest, - headers ={'persistent': 'true', - 'receipt': 1, - 'id': id}) + self.subscribe_dest(conn, dest, id, ack="auto", + headers = {'persistent': 'true', + 'receipt': 1}) def __assert_receipt(self, listener=None, pos=None): if not listener: @@ -381,69 +411,69 @@ class TestDurableSubscription(base.BaseTest): self.assertEquals(pos, self.listener.messages[0]['msg_no']) def test_durable_subscription(self): - d = '/topic/durable' + destination = '/topic/durable' - self.__subscribe(d) + self.__subscribe(destination) self.__assert_receipt() # send first message without unsubscribing self.listener.reset(1) - self.conn.send("first", destination=d) + self.conn.send(destination, "first") self.__assert_message("first") # now unsubscribe (disconnect only) - self.conn.unsubscribe(id=TestDurableSubscription.ID) + self.unsubscribe_dest(self.conn, destination, TestDurableSubscription.ID) # send again self.listener.reset(2) - self.conn.send("second", destination=d) + self.conn.send(destination, "second") # resubscribe and expect receipt - self.__subscribe(d) + self.__subscribe(destination) self.__assert_receipt(pos=1) # and message self.__assert_message("second", pos=2) # now unsubscribe (cancel) - self.conn.unsubscribe(id=TestDurableSubscription.ID, + self.unsubscribe_dest(self.conn, destination, TestDurableSubscription.ID, headers={'persistent': 'true'}) # send again self.listener.reset(1) - self.conn.send("third", destination=d) + self.conn.send(destination, "third") # resubscribe and expect no message - self.__subscribe(d) + self.__subscribe(destination) self.assertTrue(self.listener.await(3)) self.assertEquals(0, len(self.listener.messages)) self.assertEquals(1, len(self.listener.receipts)) def test_share_subscription(self): - d = '/topic/durable-shared' + destination = '/topic/durable-shared' conn2 = self.create_connection() conn2.set_listener('', self.listener) try: - self.__subscribe(d) + self.__subscribe(destination) self.__assert_receipt() self.listener.reset(1) - self.__subscribe(d, conn2) + self.__subscribe(destination, conn2) self.__assert_receipt() self.listener.reset(100) # send 100 messages for x in xrange(0, 100): - self.conn.send("msg" + str(x), destination=d) + self.conn.send(destination, "msg" + str(x)) self.assertTrue(self.listener.await(5)) self.assertEquals(100, len(self.listener.messages)) finally: - conn2.stop() + conn2.disconnect() def test_separate_ids(self): - d = '/topic/durable-separate' + destination = '/topic/durable-separate' conn2 = self.create_connection() listener2 = base.WaitableListener() @@ -451,36 +481,35 @@ class TestDurableSubscription(base.BaseTest): try: # ensure durable subscription exists for each ID - self.__subscribe(d) + self.__subscribe(destination) self.__assert_receipt() - self.__subscribe(d, conn2, "other.id") + self.__subscribe(destination, conn2, "other.id") self.__assert_receipt(listener2) - self.conn.unsubscribe(id=TestDurableSubscription.ID) - conn2.unsubscribe(id="other.id") + self.unsubscribe_dest(self.conn, destination, TestDurableSubscription.ID) + self.unsubscribe_dest(conn2, destination, "other.id") self.listener.reset(101) listener2.reset(101) ## 100 messages and 1 receipt # send 100 messages for x in xrange(0, 100): - self.conn.send("msg" + str(x), destination=d) + self.conn.send(destination, "msg" + str(x)) - self.__subscribe(d) - self.__subscribe(d, conn2, "other.id") + self.__subscribe(destination) + self.__subscribe(destination, conn2, "other.id") for l in [self.listener, listener2]: - self.assertTrue(l.await(10)) + self.assertTrue(l.await(20)) self.assertEquals(100, len(l.messages)) finally: - conn2.stop() + conn2.disconnect() def test_durable_subscribe_no_id(self): - d = '/topic/durable-invalid' + destination = '/topic/durable-invalid' - self.conn.subscribe(destination=d, headers={'persistent':'true'}), + self.conn.send_frame('SUBSCRIBE', + {'destination': destination, 'ack': 'auto', 'persistent': 'true'}) self.listener.await(3) self.assertEquals(1, len(self.listener.errors)) self.assertEquals("Missing Header", self.listener.errors[0]['headers']['message']) - - diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/errors.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/errors.py index 9175429..e52b3ac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/errors.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/errors.py @@ -28,7 +28,7 @@ class TestErrors(base.BaseTest): def test_unknown_destination(self): self.listener.reset() - self.conn.send(destination="/something/interesting") + self.conn.send("/something/interesting", 'test_unknown_destination') self.assertTrue(self.listener.await()) self.assertEquals(1, len(self.listener.errors)) @@ -54,7 +54,7 @@ class TestErrors(base.BaseTest): def __test_invalid_destination(self, dtype, content): self.listener.reset() - self.conn.send(destination="/" + dtype + content) + self.conn.send("/" + dtype + content, '__test_invalid_destination:' + dtype + content) self.assertTrue(self.listener.await()) self.assertEquals(1, len(self.listener.errors)) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/lifecycle.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/lifecycle.py index 902994e..ff9b119 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/lifecycle.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/lifecycle.py @@ -45,6 +45,16 @@ class TestLifecycle(base.BaseTest): d = "/queue/unsub04" self.unsub_test(d, self.sub_and_send(d, subid="queid", receipt="unsub.rct"), numRcts=1) + def test_connect_version_1_0(self): + ''' Test CONNECT with version 1.0''' + self.conn.disconnect() + new_conn = self.create_connection(version="1.0") + try: + self.assertTrue(new_conn.is_connected()) + finally: + new_conn.disconnect() + self.assertFalse(new_conn.is_connected()) + def test_connect_version_1_1(self): ''' Test CONNECT with version 1.1''' self.conn.disconnect() @@ -55,10 +65,20 @@ class TestLifecycle(base.BaseTest): new_conn.disconnect() self.assertFalse(new_conn.is_connected()) + def test_connect_version_1_2(self): + ''' Test CONNECT with version 1.2''' + self.conn.disconnect() + new_conn = self.create_connection(version="1.2") + try: + self.assertTrue(new_conn.is_connected()) + finally: + new_conn.disconnect() + self.assertFalse(new_conn.is_connected()) + def test_heartbeat_disconnects_client(self): ''' Test heart-beat disconnection''' self.conn.disconnect() - new_conn = self.create_connection(heartbeat="1500,0") + new_conn = self.create_connection(version='1.1', heartbeats=(1500, 0)) try: self.assertTrue(new_conn.is_connected()) time.sleep(1) @@ -71,37 +91,28 @@ class TestLifecycle(base.BaseTest): def test_unsupported_version(self): ''' Test unsupported version on CONNECT command''' - self.bad_connect(stomp.Connection(user="guest", - passcode="guest", - version="100.1"), - "Supported versions are 1.0,1.1,1.2\n") + self.bad_connect("Supported versions are 1.0,1.1,1.2\n", version='100.1') def test_bad_username(self): ''' Test bad username''' - self.bad_connect(stomp.Connection(user="gust", - passcode="guest"), - "Access refused for user 'gust'\n") + self.bad_connect("Access refused for user 'gust'\n", user='gust') def test_bad_password(self): ''' Test bad password''' - self.bad_connect(stomp.Connection(user="guest", - passcode="gust"), - "Access refused for user 'guest'\n") + self.bad_connect("Access refused for user 'guest'\n", passcode='gust') def test_bad_vhost(self): ''' Test bad virtual host''' - self.bad_connect(stomp.Connection(user="guest", - passcode="guest", - virtual_host="//"), - "Virtual host '//' access denied") + self.bad_connect("Virtual host '//' access denied", version='1.1', vhost='//') - def bad_connect(self, new_conn, expected): + def bad_connect(self, expected, user='guest', passcode='guest', **kwargs): self.conn.disconnect() + new_conn = self.create_connection_obj(**kwargs) listener = base.WaitableListener() new_conn.set_listener('', listener) try: new_conn.start() - new_conn.connect() + new_conn.connect(user, passcode) self.assertTrue(listener.await()) self.assertEquals(expected, listener.errors[0]['message']) finally: @@ -136,7 +147,7 @@ class TestLifecycle(base.BaseTest): def unsub_test(self, dest, verbs, numRcts=0): def afterfun(): - self.conn.send("after-test", destination=dest) + self.conn.send(dest, "after-test") subverb, unsubverb = verbs self.assertListenerAfter(subverb, numMsgs=1, errMsg="FAILED to subscribe and send") @@ -145,20 +156,13 @@ class TestLifecycle(base.BaseTest): self.assertListenerAfter(afterfun, errMsg="Still receiving messages") - def sub_and_send(self, dest, subid="", receipt=""): + def sub_and_send(self, dest, subid=None, receipt=None): def subfun(): - if subid=="": - self.conn.subscribe(destination=dest) - else: - self.conn.subscribe(destination=dest, id=subid) - self.conn.send("test", destination=dest) + self.subscribe_dest(self.conn, dest, subid) + self.conn.send(dest, "test") def unsubfun(): - if subid=="" and receipt=="": - self.conn.unsubscribe(destination=dest) - elif receipt=="": - self.conn.unsubscribe(id=subid) - elif subid=="": - self.conn.unsubscribe(destination=dest, receipt=receipt) - else: - self.conn.unsubscribe(id=subid, receipt=receipt) + headers = {} + if receipt != None: + headers['receipt'] = receipt + self.unsubscribe_dest(self.conn, dest, subid, **headers) return subfun, unsubfun diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/non_ssl.config b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/non_ssl.config deleted file mode 100644 index f0c2ca7..0000000 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/non_ssl.config +++ /dev/null @@ -1,6 +0,0 @@ -[{rabbitmq_stomp, [{default_user, [{login, "guest"}, - {passcode, "guest"} - ]}, - {implicit_connect, true} - ]} -]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/parsing.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/parsing.py index 58d5bcf..2e08836 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/parsing.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/parsing.py @@ -51,8 +51,8 @@ class TestParsing(unittest.TestCase): def match(self, pattern, data): - ''' helper: try to match 'pattern' regexp with 'data' string. - Fail test if they don't match. + ''' helper: try to match a regexp with a string. + Fail test if they do not match. ''' matched = re.match(pattern, data) if matched: @@ -196,47 +196,6 @@ class TestParsing(unittest.TestCase): for cd in [self.cd1, self.cd2]: self.match(resp, cd.recv(4096)) - - @connect(['cd']) - def test_huge_message(self): - ''' Test sending/receiving huge (16MB) message. ''' - subscribe=( 'SUBSCRIBE\n' - 'id: xxx\n' - 'destination:/exchange/amq.topic/test_huge_message\n' - '\n\0') - self.cd.sendall(subscribe) - - message = 'x' * 1024*1024*16 - - self.cd.sendall('SEND\n' - 'destination:/exchange/amq.topic/test_huge_message\n' - 'content-type:text/plain\n' - '\n' - '%s' - '\0' % message) - - resp=('MESSAGE\n' - 'subscription:(.*)\n' - 'destination:/topic/test_huge_message\n' - 'message-id:(.*)\n' - 'content-type:text/plain\n' - 'content-length:%i\n' - '\n' - '%s(.*)' - % (len(message), message[:8000]) ) - - recv = [] - s = 0 - while len(recv) < 1 or recv[-1][-1] != '\0': - buf = self.cd.recv(4096*16) - s += len(buf) - recv.append( buf ) - buf = ''.join(recv) - - # matching 100MB regexp is way too expensive. - self.match(resp, buf[:8192]) - self.assertEqual(len(buf) > len(message), True) - @connect(['cd']) def test_message_with_embedded_nulls(self): ''' Test sending/receiving message with embedded nulls. ''' diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_amqqueue_test.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_amqqueue_test.erl index 013f7cb..42c18ed 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_amqqueue_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_amqqueue_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_amqqueue_test). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_publish_test.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_publish_test.erl index 615c6a7..a67e9aa 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_publish_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_publish_test.erl @@ -34,14 +34,14 @@ run() -> [put(K, 0) || K <- [sent, recd, last_sent, last_recd]], - put(last_ts, erlang:now()), + put(last_ts, os:timestamp()), {ok, Pub} = rabbit_stomp_client:connect(), {ok, Recv} = rabbit_stomp_client:connect(), Self = self(), - spawn(fun() -> publish(Self, Pub, 0, erlang:now()) end), + spawn(fun() -> publish(Self, Pub, 0, os:timestamp()) end), rabbit_stomp_client:send( Recv, "SUBSCRIBE", [{"destination", ?DESTINATION}]), - spawn(fun() -> recv(Self, Recv, 0, erlang:now()) end), + spawn(fun() -> recv(Self, Recv, 0, os:timestamp()) end), report(). report() -> @@ -49,13 +49,13 @@ report() -> {sent, C} -> put(sent, C); {recd, C} -> put(recd, C) end, - Diff = timer:now_diff(erlang:now(), get(last_ts)), + Diff = timer:now_diff(os:timestamp(), get(last_ts)), case Diff > ?MICROS_PER_UPDATE of true -> S = get(sent) - get(last_sent), R = get(recd) - get(last_recd), put(last_sent, get(sent)), put(last_recd, get(recd)), - put(last_ts, erlang:now()), + put(last_ts, os:timestamp()), io:format("Send ~p msg/s | Recv ~p msg/s~n", [trunc(S * ?MICROS_PER_SECOND / Diff), trunc(R * ?MICROS_PER_SECOND / Diff)]); @@ -67,10 +67,10 @@ publish(Owner, Client, Count, TS) -> rabbit_stomp_client:send( Client, "SEND", [{"destination", ?DESTINATION}], [integer_to_list(Count)]), - Diff = timer:now_diff(erlang:now(), TS), + Diff = timer:now_diff(os:timestamp(), TS), case Diff > ?MICROS_PER_UPDATE_MSG of true -> Owner ! {sent, Count + 1}, - publish(Owner, Client, Count + 1, erlang:now()); + publish(Owner, Client, Count + 1, os:timestamp()); false -> publish(Owner, Client, Count + 1, TS) end. @@ -79,10 +79,10 @@ recv(Owner, Client0, Count, TS) -> rabbit_stomp_client:recv(Client0), BodyInt = list_to_integer(binary_to_list(iolist_to_binary(Body))), Count = BodyInt, - Diff = timer:now_diff(erlang:now(), TS), + Diff = timer:now_diff(os:timestamp(), TS), case Diff > ?MICROS_PER_UPDATE_MSG of true -> Owner ! {recd, Count + 1}, - recv(Owner, Client1, Count + 1, erlang:now()); + recv(Owner, Client1, Count + 1, os:timestamp()); false -> recv(Owner, Client1, Count + 1, TS) end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test.erl index 2fdca2d..2529098 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_test). @@ -24,16 +24,40 @@ all_tests() -> test_messages_not_dropped_on_disconnect(), + test_direct_client_connections_are_not_leaked(), + ok. + +-define(GARBAGE, <<"bdaf63dda9d78b075c748b740e7c3510ad203b07\nbdaf63dd">>). + +count_connections() -> + length(supervisor2:which_children(rabbit_stomp_client_sup_sup)). + +test_direct_client_connections_are_not_leaked() -> + N = count_connections(), + lists:foreach(fun (_) -> + {ok, Client = {Socket, _}} = rabbit_stomp_client:connect(), + %% send garbage which trips up the parser + gen_tcp:send(Socket, ?GARBAGE), + rabbit_stomp_client:send( + Client, "LOL", [{"", ""}]) + end, + lists:seq(1, 1000)), + timer:sleep(5000), + N = count_connections(), ok. test_messages_not_dropped_on_disconnect() -> + N = count_connections(), {ok, Client} = rabbit_stomp_client:connect(), + N1 = N + 1, + N1 = count_connections(), [rabbit_stomp_client:send( Client, "SEND", [{"destination", ?DESTINATION}], [integer_to_list(Count)]) || Count <- lists:seq(1, 1000)], rabbit_stomp_client:disconnect(Client), QName = rabbit_misc:r(<<"/">>, queue, <<"bulk-test">>), timer:sleep(3000), + N = count_connections(), rabbit_amqqueue:with( QName, fun(Q) -> 1000 = pget(messages, rabbit_amqqueue:info(Q, [messages])) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_frame.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_frame.erl index 34b7472..a2cbdf3 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_frame.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_frame.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_test_frame). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_util.erl b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_util.erl index e7459e3..a25e306 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/rabbit_stomp_test_util.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_stomp_test_util). diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/reliability.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/reliability.py index 08476d5..b8bb150 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/reliability.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/reliability.py @@ -7,7 +7,7 @@ class TestReliability(base.BaseTest): def test_send_and_disconnect(self): ''' Test close socket after send does not lose messages ''' - d = "/queue/reliability" + destination = "/queue/reliability" pub_conn = self.create_connection() try: msg = "0" * (128) @@ -17,12 +17,12 @@ class TestReliability(base.BaseTest): listener = base.WaitableListener() listener.reset(count) self.conn.set_listener('', listener) - self.conn.subscribe(destination=d) + self.subscribe_dest(self.conn, destination, None) for x in range(0, count): - pub_conn.send(msg + str(x), destination=d) + pub_conn.send(destination, msg + str(x)) time.sleep(2.0) - pub_conn.close_socket() + pub_conn.disconnect() if listener.await(30): self.assertEquals(count, len(listener.messages)) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl_lifecycle.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl_lifecycle.py index 8a89a7a..53636df 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl_lifecycle.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl_lifecycle.py @@ -1,24 +1,29 @@ import unittest import os +import os.path +import sys import stomp import base +import ssl -ssl_key_file = os.path.abspath("test/certs/client/key.pem") -ssl_cert_file = os.path.abspath("test/certs/client/cert.pem") -ssl_ca_certs = os.path.abspath("test/certs/testca/cacert.pem") + +base_path = os.path.dirname(sys.argv[0]) + +ssl_key_file = os.path.abspath(base_path + "/../certs/client/key.pem") +ssl_cert_file = os.path.abspath(base_path + "/../certs/client/cert.pem") +ssl_ca_certs = os.path.abspath(base_path + "/../certs/testca/cacert.pem") class TestSslClient(unittest.TestCase): def __ssl_connect(self): - conn = stomp.Connection(user="guest", passcode="guest", - host_and_ports = [ ('localhost', 61614) ], + conn = stomp.Connection(host_and_ports = [ ('localhost', 61614) ], use_ssl = True, ssl_key_file = ssl_key_file, ssl_cert_file = ssl_cert_file, ssl_ca_certs = ssl_ca_certs) - + print "FILE: ", ssl_cert_file conn.start() - conn.connect() + conn.connect("guest", "guest") return conn def __ssl_auth_connect(self): @@ -32,11 +37,11 @@ class TestSslClient(unittest.TestCase): def test_ssl_connect(self): conn = self.__ssl_connect() - conn.stop() + conn.disconnect() def test_ssl_auth_connect(self): conn = self.__ssl_auth_connect() - conn.stop() + conn.disconnect() def test_ssl_send_receive(self): conn = self.__ssl_connect() @@ -53,7 +58,7 @@ class TestSslClient(unittest.TestCase): conn.set_listener('', listener) d = "/topic/ssl.test" - conn.subscribe(destination=d, receipt="sub") + conn.subscribe(destination=d, ack="auto", id="ctag", receipt="sub") self.assertTrue(listener.await(1)) @@ -61,7 +66,7 @@ class TestSslClient(unittest.TestCase): listener.receipts[0]['headers']['receipt-id']) listener.reset(1) - conn.send("Hello SSL!", destination=d) + conn.send(body="Hello SSL!", destination=d) self.assertTrue(listener.await()) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl.config b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.config similarity index 100% rename from rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/ssl.config rename to rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.config diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.py index 4979552..ddeb1fe 100755 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/test.py @@ -3,7 +3,14 @@ import test_runner if __name__ == '__main__': - modules = ['parsing', 'destinations', 'lifecycle', 'transactions', - 'ack', 'errors', 'reliability'] + modules = [ + 'parsing', + 'destinations', + 'lifecycle', + 'transactions', + 'ack', + 'errors', + 'reliability', + ] test_runner.run_unittests(modules) diff --git a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/transactions.py b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/transactions.py index 1d95f7e..d4f166b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/transactions.py +++ b/rabbitmq-server/plugins-src/rabbitmq-stomp/test/src/transactions.py @@ -7,14 +7,14 @@ class TestTransactions(base.BaseTest): def test_tx_commit(self): ''' Test TX with a COMMIT and ensure messages are delivered ''' - d = "/exchange/amq.fanout" + destination = "/exchange/amq.fanout" tx = "test.tx" self.listener.reset() - self.conn.subscribe(destination=d) + self.subscribe_dest(self.conn, destination, None) self.conn.begin(transaction=tx) - self.conn.send("hello!", destination=d, transaction=tx) - self.conn.send("again!", destination=d) + self.conn.send(destination, "hello!", transaction=tx) + self.conn.send(destination, "again!") ## should see the second message self.assertTrue(self.listener.await(3)) @@ -31,14 +31,14 @@ class TestTransactions(base.BaseTest): def test_tx_abort(self): ''' Test TX with an ABORT and ensure messages are discarded ''' - d = "/exchange/amq.fanout" + destination = "/exchange/amq.fanout" tx = "test.tx" self.listener.reset() - self.conn.subscribe(destination=d) + self.subscribe_dest(self.conn, destination, None) self.conn.begin(transaction=tx) - self.conn.send("hello!", destination=d, transaction=tx) - self.conn.send("again!", destination=d) + self.conn.send(destination, "hello!", transaction=tx) + self.conn.send(destination, "again!") ## should see the second message self.assertTrue(self.listener.await(3)) diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-test/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-test/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/Makefile b/rabbitmq-server/plugins-src/rabbitmq-test/Makefile index 74a96ae..03466a7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/Makefile +++ b/rabbitmq-server/plugins-src/rabbitmq-test/Makefile @@ -51,21 +51,27 @@ full: $(MAKE) prepare && \ { $(MAKE) -C $(BROKER_DIR) run-tests || { OK=false; $(TESTS_FAILED); } } && \ { $(MAKE) run-qpid-testsuite || { OK=false; $(TESTS_FAILED); } } && \ - { ( cd $(TEST_DIR) && ant test-suite ) || { OK=false; $(TESTS_FAILED); } } && \ + { ( cd $(TEST_DIR) && MAKE=$(MAKE) ant test-suite ) || { OK=false; $(TESTS_FAILED); } } && \ $(MAKE) cleanup && { $$OK || $(TESTS_FAILED); } && $$OK +unit: + OK=true && \ + $(MAKE) prepare && \ + { $(MAKE) -C $(BROKER_DIR) run-tests || OK=false; } && \ + $(MAKE) cleanup && $$OK + lite: OK=true && \ $(MAKE) prepare && \ { $(MAKE) -C $(BROKER_DIR) run-tests || OK=false; } && \ - { ( cd $(TEST_DIR) && ant test-suite ) || OK=false; } && \ + { ( cd $(TEST_DIR) && MAKE=$(MAKE) ant test-suite ) || OK=false; } && \ $(MAKE) cleanup && $$OK conformance16: OK=true && \ $(MAKE) prepare && \ { $(MAKE) -C $(BROKER_DIR) run-tests || OK=false; } && \ - { ( cd $(TEST_DIR) && ant test-suite ) || OK=false; } && \ + { ( cd $(TEST_DIR) && MAKE=$(MAKE) ant test-suite ) || OK=false; } && \ $(MAKE) cleanup && $$OK qpid_testsuite: @@ -170,3 +176,4 @@ cleanup: create_ssl_certs: $(MAKE) -C certs DIR=$(SSL_CERTS_DIR) clean all + diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/README b/rabbitmq-server/plugins-src/rabbitmq-test/README index 3afe826..9b19505 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/README +++ b/rabbitmq-server/plugins-src/rabbitmq-test/README @@ -1,5 +1,6 @@ Useful targets: +$ make unit # runs the Erlang unit tests $ make lite # runs the Erlang unit tests and the Java client / functional tests $ make full # runs both the above plus the QPid test suite $ make test # runs the Erlang multi-node integration tests diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/package.mk b/rabbitmq-server/plugins-src/rabbitmq-test/package.mk index d9b0f97..8808931 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/package.mk +++ b/rabbitmq-server/plugins-src/rabbitmq-test/package.mk @@ -1,6 +1,7 @@ DEPS:=rabbitmq-erlang-client FILTER:=all COVER:=false +WITH_BROKER_TEST_COMMANDS:=rabbit_test_runner:run_in_broker(\"$(PACKAGE_DIR)/test/ebin\",\"$(FILTER)\") STANDALONE_TEST_COMMANDS:=rabbit_test_runner:run_multi(\"$(UMBRELLA_BASE_DIR)/rabbitmq-server\",\"$(PACKAGE_DIR)/test/ebin\",\"$(FILTER)\",$(COVER),none) ## Require R15B to compile inet_proxy_dist since it requires includes diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_proxy_dist.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_proxy_dist.erl index 458966c..847ef2e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_proxy_dist.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_proxy_dist.erl @@ -66,7 +66,7 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> %% Modification START ProxyPort = case TcpPort >= 25672 andalso TcpPort < 25700 andalso inet_tcp_proxy:is_enabled() of - true -> TcpPort + 10000; + true -> TcpPort + 5000; false -> TcpPort end, case inet_tcp:connect(Ip, ProxyPort, diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy.erl index 18ffcc2..28d58e0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy.erl @@ -61,7 +61,7 @@ error_handler(Thunk) -> go() -> ets:new(?TABLE, [public, named_table]), {ok, Port} = application:get_env(kernel, inet_dist_listen_min), - ProxyPort = Port + 10000, + ProxyPort = Port + 5000, {ok, Sock} = gen_tcp:listen(ProxyPort, [inet, {reuseaddr, true}]), accept_loop(Sock, Port). diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy_manager.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy_manager.erl index 1bab158..a79ea9f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy_manager.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/inet_tcp_proxy_manager.erl @@ -62,6 +62,7 @@ name() -> %%---------------------------------------------------------------------------- init([]) -> + net_kernel:monitor_nodes(true), {ok, #state{ports = dict:new(), pending = []}}. @@ -90,6 +91,13 @@ handle_call(_Req, _From, State) -> handle_cast(_C, State) -> {noreply, State}. +handle_info({nodedown, Node}, State = #state{ports = Ports}) -> + Ports1 = dict:filter( + fun (_, {From, To}) -> + Node =/= From andalso Node =/= To + end, Ports), + {noreply, State#state{ports = Ports1}}; + handle_info(_I, State) -> {noreply, State}. diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_configs.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_configs.erl index ce82e93..f286733 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_configs.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_configs.erl @@ -21,16 +21,17 @@ -export([cluster/2, cluster_ab/1, cluster_abc/1, start_ab/1, start_abc/1]). -export([start_connections/1, build_cluster/1]). -export([ha_policy_all/1, ha_policy_two_pos/1]). --export([start_nodes/2, start_nodes/3, add_to_cluster/2]). +-export([start_nodes/2, start_nodes/3, add_to_cluster/2, + rabbitmqctl/2, rabbitmqctl_fail/2]). -export([stop_nodes/1, start_node/1, stop_node/1, kill_node/1, restart_node/1, - execute/1]). + start_node_fail/1, execute/1]). -export([cover_work_factor/2]). -import(rabbit_test_util, [set_ha_policy/3, set_ha_policy/4, a2b/1]). --import(rabbit_misc, [pget/2]). +-import(rabbit_misc, [pget/2, pget/3]). -define(INITIAL_KEYS, [cover, base, server, plugins]). --define(NON_RUNNING_KEYS, ?INITIAL_KEYS ++ [nodename, port]). +-define(NON_RUNNING_KEYS, ?INITIAL_KEYS ++ [nodename, port, mnesia_dir]). cluster_ab(InitialCfg) -> cluster(InitialCfg, [a, b]). cluster_abc(InitialCfg) -> cluster(InitialCfg, [a, b, c]). @@ -52,7 +53,9 @@ start_nodes(InitialCfg0, NodeNames, FirstPort) -> [{_, _}|_] -> [InitialCfg0 || _ <- NodeNames]; _ -> InitialCfg0 end, - Nodes = [[{nodename, N}, {port, P} | strip_non_initial(Cfg)] + Nodes = [[{nodename, N}, {port, P}, + {mnesia_dir, rabbit_misc:format("rabbitmq-~s-mnesia", [N])} | + strip_non_initial(Cfg)] || {N, P, Cfg} <- lists:zip3(NodeNames, Ports, InitialCfgs)], [start_node(Node) || Node <- Nodes]. @@ -68,54 +71,23 @@ strip_non_initial(Cfg) -> strip_running(Cfg) -> [{K, V} || {K, V} <- Cfg, lists:member(K, ?NON_RUNNING_KEYS)]. -enable_plugins(Cfg) -> enable_plugins(pget(plugins, Cfg), pget(server, Cfg)). +enable_plugins(Cfg) -> + enable_plugins(pget(plugins, Cfg), pget(server, Cfg), Cfg). -enable_plugins(none, _Server) -> ok; -enable_plugins(Dir, Server) -> - Env = plugins_env(Dir), - R = execute(Env, Server ++ "/scripts/rabbitmq-plugins list -m"), - Plugins = string:tokens(R, "\n"), - [execute(Env, {Server ++ "/scripts/rabbitmq-plugins enable ~s", [Plugin]}) - || Plugin <- Plugins], +enable_plugins(none, _Server, _Cfg) -> ok; +enable_plugins(_Dir, Server, Cfg) -> + R = execute(Cfg, Server ++ "/scripts/rabbitmq-plugins list -m"), + Plugins = string:join(string:tokens(R, "\n"), " "), + execute(Cfg, {Server ++ "/scripts/rabbitmq-plugins set --offline ~s", + [Plugins]}), ok. -plugins_env(none) -> - [{"RABBITMQ_ENABLED_PLUGINS_FILE", "/does-not-exist"}]; -plugins_env(Dir) -> - [{"RABBITMQ_PLUGINS_DIR", {"~s/plugins", [Dir]}}, - {"RABBITMQ_PLUGINS_EXPAND_DIR", {"~s/expand", [Dir]}}, - {"RABBITMQ_ENABLED_PLUGINS_FILE", {"~s/enabled_plugins", [Dir]}}]. - -start_node(Cfg) -> - Nodename = pget(nodename, Cfg), - Port = pget(port, Cfg), - Base = pget(base, Cfg), +start_node(Cfg0) -> + Node = rabbit_nodes:make(pget(nodename, Cfg0)), + Cfg = [{node, Node} | Cfg0], Server = pget(server, Cfg), - PidFile = rabbit_misc:format("~s/~s.pid", [Base, Nodename]), - Linked = - execute_bg( - [{"RABBITMQ_MNESIA_BASE", {"~s/rabbitmq-~s-mnesia", [Base,Nodename]}}, - {"RABBITMQ_LOG_BASE", {"~s", [Base]}}, - {"RABBITMQ_NODENAME", {"~s", [Nodename]}}, - {"RABBITMQ_NODE_PORT", {"~B", [Port]}}, - {"RABBITMQ_PID_FILE", PidFile}, - {"RABBITMQ_CONFIG_FILE", "/some/path/which/does/not/exist"}, - {"RABBITMQ_ALLOW_INPUT", "1"}, %% Needed to make it close on our exit - %% Bit of a hack - only needed for mgmt tests. - {"RABBITMQ_SERVER_START_ARGS", - {"-rabbitmq_management listener [{port,1~B}]", [Port]}}, - {"RABBITMQ_SERVER_ERL_ARGS", - %% Next two lines are defaults - {"+K true +A30 +P 1048576 " - "-kernel inet_default_connect_options [{nodelay,true}] " - %% Some tests need to be able to make distribution unhappy - "-pa ~s/../rabbitmq-test/ebin " - "-proto_dist inet_proxy", [Server]}} - | plugins_env(pget(plugins, Cfg))], - Server ++ "/scripts/rabbitmq-server"), - execute({Server ++ "/scripts/rabbitmqctl -n ~s wait ~s", - [Nodename, PidFile]}), - Node = rabbit_nodes:make(Nodename), + Linked = execute_bg(Cfg, Server ++ "/scripts/rabbitmq-server"), + rabbitmqctl(Cfg, {"wait ~s", [pid_file(Cfg)]}), OSPid = rpc:call(Node, os, getpid, []), %% The cover system thinks all nodes with the same name are the %% same node and will automaticaly re-establish cover as soon as @@ -125,11 +97,16 @@ start_node(Cfg) -> {true, false} -> cover:start([Node]); _ -> ok end, - [{node, Node}, - {pid_file, PidFile}, - {os_pid, OSPid}, + [{os_pid, OSPid}, {linked_pid, Linked} | Cfg]. +start_node_fail(Cfg0) -> + Node = rabbit_nodes:make(pget(nodename, Cfg0)), + Cfg = [{node, Node}, {acceptable_exit_codes, lists:seq(1, 255)} | Cfg0], + Server = pget(server, Cfg), + execute(Cfg, Server ++ "/scripts/rabbitmq-server"), + ok. + build_cluster([First | Rest]) -> add_to_cluster([First], Rest). @@ -139,14 +116,21 @@ add_to_cluster([First | _] = Existing, New) -> cluster_with(Cfg, NewCfg) -> Node = pget(node, Cfg), - NewNodename = pget(nodename, NewCfg), + rabbitmqctl(NewCfg, stop_app), + rabbitmqctl(NewCfg, {"join_cluster ~s", [Node]}), + rabbitmqctl(NewCfg, start_app). + +rabbitmqctl(Cfg, Str) -> + Node = pget(node, Cfg), Server = pget(server, Cfg), - execute({Server ++ "/scripts/rabbitmqctl -n ~s stop_app", - [NewNodename]}), - execute({Server ++ "/scripts/rabbitmqctl -n ~s join_cluster ~s", - [NewNodename, Node]}), - execute({Server ++ "/scripts/rabbitmqctl -n ~s start_app", - [NewNodename]}). + Cmd = case Node of + undefined -> {"~s", [fmt(Str)]}; + _ -> {"-n ~s ~s", [Node, fmt(Str)]} + end, + execute(Cfg, {Server ++ "/scripts/rabbitmqctl ~s", [fmt(Cmd)]}). + +rabbitmqctl_fail(Cfg, Str) -> + rabbitmqctl([{acceptable_exit_codes, lists:seq(1, 255)} | Cfg], Str). ha_policy_all([Cfg | _] = Cfgs) -> set_ha_policy(Cfg, <<".*">>, <<"all">>), @@ -155,9 +139,11 @@ ha_policy_all([Cfg | _] = Cfgs) -> ha_policy_two_pos([Cfg | _] = Cfgs) -> Members = [a2b(pget(node, C)) || C <- Cfgs], TwoNodes = [M || M <- lists:sublist(Members, 2)], - set_ha_policy(Cfg, <<"^ha.two.">>, {<<"nodes">>, TwoNodes}, []), + set_ha_policy(Cfg, <<"^ha.two.">>, {<<"nodes">>, TwoNodes}, + [{<<"ha-promote-on-shutdown">>, <<"always">>}]), set_ha_policy(Cfg, <<"^ha.auto.">>, {<<"nodes">>, TwoNodes}, - [{<<"ha-sync-mode">>, <<"automatic">>}]), + [{<<"ha-sync-mode">>, <<"automatic">>}, + {<<"ha-promote-on-shutdown">>, <<"always">>}]), Cfgs. start_connections(Nodes) -> [start_connection(Node) || Node <- Nodes]. @@ -171,17 +157,24 @@ start_connection(Cfg) -> stop_nodes(Nodes) -> [stop_node(Node) || Node <- Nodes]. stop_node(Cfg) -> - Server = pget(server, Cfg), maybe_flush_cover(Cfg), - catch execute({Server ++ "/scripts/rabbitmqctl -n ~s stop ~s", - [pget(nodename, Cfg), pget(pid_file, Cfg)]}), + catch rabbitmqctl(Cfg, {"stop ~s", [pid_file(Cfg)]}), strip_running(Cfg). kill_node(Cfg) -> maybe_flush_cover(Cfg), - catch execute({"kill -9 ~s", [pget(os_pid, Cfg)]}), + OSPid = pget(os_pid, Cfg), + catch execute(Cfg, {"kill -9 ~s", [OSPid]}), + await_os_pid_death(OSPid), strip_running(Cfg). +await_os_pid_death(OSPid) -> + case rabbit_misc:is_os_process_alive(OSPid) of + true -> timer:sleep(100), + await_os_pid_death(OSPid); + false -> ok + end. + restart_node(Cfg) -> start_node(stop_node(Cfg)). @@ -201,34 +194,86 @@ cover_work_factor(Without, Cfg) -> %%---------------------------------------------------------------------------- -execute(Cmd) -> execute([], Cmd). +execute(Cmd) -> + execute([], Cmd, [0]). + +execute(Cfg, Cmd) -> + %% code 137 -> killed with SIGKILL which we do in some tests + execute(environment(Cfg), Cmd, pget(acceptable_exit_codes, Cfg, [0, 137])). -execute(Env0, Cmd0) -> - Env = [{K, fmt(V)} || {K, V} <- Env0], +execute(Env0, Cmd0, AcceptableExitCodes) -> + Env = [{"RABBITMQ_" ++ K, fmt(V)} || {K, V} <- Env0], Cmd = fmt(Cmd0), + error_logger:info_msg("Invoking '~s'~n", [Cmd]), Port = erlang:open_port( {spawn, "/usr/bin/env sh -c \"" ++ Cmd ++ "\""}, [{env, Env}, exit_status, stderr_to_stdout, use_stdio]), - port_receive_loop(Port, ""). + port_receive_loop(Port, "", AcceptableExitCodes). -port_receive_loop(Port, Stdout) -> - receive - {Port, {exit_status, 0}} -> Stdout; - {Port, {exit_status, 137}} -> Stdout; %% [0] - {Port, {exit_status, X}} -> exit({exit_status, X, Stdout}); - {Port, {data, Out}} -> %%io:format(user, "~s", [Out]), - port_receive_loop(Port, Stdout ++ Out) +environment(Cfg) -> + Nodename = pget(nodename, Cfg), + Plugins = pget(plugins, Cfg), + case Nodename of + undefined -> + plugins_env(Plugins); + _ -> + Port = pget(port, Cfg), + Base = pget(base, Cfg), + Server = pget(server, Cfg), + [{"MNESIA_DIR", {"~s/~s", [Base, pget(mnesia_dir, Cfg)]}}, + {"PLUGINS_EXPAND_DIR", {"~s/~s-plugins-expand", [Base, Nodename]}}, + {"LOG_BASE", {"~s", [Base]}}, + {"NODENAME", {"~s", [Nodename]}}, + {"NODE_PORT", {"~B", [Port]}}, + {"PID_FILE", pid_file(Cfg)}, + {"CONFIG_FILE", "/some/path/which/does/not/exist"}, + {"ALLOW_INPUT", "1"}, %% Needed to make it close on exit + %% Bit of a hack - only needed for mgmt tests. + {"SERVER_START_ARGS", + {"-rabbitmq_management listener [{port,1~B}]", [Port]}}, + {"SERVER_ERL_ARGS", + %% Next two lines are defaults + {"+K true +A30 +P 1048576 " + "-kernel inet_default_connect_options [{nodelay,true}] " + %% Some tests need to be able to make distribution unhappy + "-pa ~s/../rabbitmq-test/ebin " + "-proto_dist inet_proxy", [Server]}} + | plugins_env(Plugins)] end. -%% [0] code 137 -> killed with SIGKILL which we do in some tests +plugins_env(none) -> + [{"ENABLED_PLUGINS_FILE", "/does-not-exist"}]; +plugins_env(Dir) -> + [{"PLUGINS_DIR", {"~s/plugins", [Dir]}}, + {"PLUGINS_EXPAND_DIR", {"~s/expand", [Dir]}}, + {"ENABLED_PLUGINS_FILE", {"~s/enabled_plugins", [Dir]}}]. + +pid_file(Cfg) -> + rabbit_misc:format("~s/~s.pid", [pget(base, Cfg), pget(nodename, Cfg)]). + +port_receive_loop(Port, Stdout, AcceptableExitCodes) -> + receive + {Port, {exit_status, X}} -> + Fmt = "Command exited with code ~p~nStdout: ~s~n", + Args = [X, Stdout], + case lists:member(X, AcceptableExitCodes) of + true -> error_logger:info_msg(Fmt, Args), + Stdout; + false -> error_logger:error_msg(Fmt, Args), + exit({exit_status, X, AcceptableExitCodes, Stdout}) + end; + {Port, {data, Out}} -> + port_receive_loop(Port, Stdout ++ Out, AcceptableExitCodes) + end. -execute_bg(Env, Cmd) -> +execute_bg(Cfg, Cmd) -> spawn_link(fun () -> - execute(Env, Cmd), + execute(Cfg, Cmd), {links, Links} = process_info(self(), links), [unlink(L) || L <- Links] end). fmt({Fmt, Args}) -> rabbit_misc:format(Fmt, Args); fmt(Str) -> Str. + diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_runner.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_runner.erl index 7193704..d0df292 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_runner.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_runner.erl @@ -25,6 +25,7 @@ -export([run_in_broker/2, run_multi/5]). run_in_broker(Dir, Filter) -> + add_server_test_ebin_dir(), io:format("~nIn-broker tests~n================~n~n", []), eunit:test(make_tests_single(Dir, Filter, ?TIMEOUT), []). @@ -124,8 +125,15 @@ make_test_multi(M, FWith, F, ShowHeading, Timeout, Width, InitialCfg) -> fun () -> [link(pget(linked_pid, N)) || N <- Nodes], io:format(user, " [running]", []), - M:F(Nodes), - io:format(user, " [PASSED]", []) + %%try + M:F(Nodes), + io:format(user, " [PASSED]", []) + %% catch + %% Type:Reason -> + %% io:format(user, "YYY stop~n", []), + %% rabbit_test_configs:stop_nodes(Nodes), + %% exit({Type, Reason, erlang:get_stacktrace()}) + %% end end}] end}. %% [0] If we didn't get as far as starting any nodes then we only have @@ -212,3 +220,11 @@ error_logger_logfile_filename() -> {error,_} -> {error, no_log_file}; Val -> Val end. + +add_server_test_ebin_dir() -> + %% Some tests need modules from this dir, but it's not on the path + %% by default. + {file, Path} = code:is_loaded(rabbit), + Ebin = filename:dirname(Path), + TestEbin = filename:join([Ebin, "..", "test", "ebin"]), + code:add_path(TestEbin). diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_util.erl b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_util.erl index c8b0f9a..973e1b0 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_util.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/src/rabbit_test_util.erl @@ -45,6 +45,12 @@ clear_param(Cfg, Component, Name) -> ok = rpc:call(pget(node, Cfg), rabbit_runtime_parameters, clear, [<<"/">>, Component, Name]). +enable_plugin(Cfg, Plugin) -> + plugins_action(enable, Cfg, [Plugin], []). + +disable_plugin(Cfg, Plugin) -> + plugins_action(disable, Cfg, [Plugin], []). + control_action(Command, Cfg) -> control_action(Command, Cfg, [], []). @@ -59,6 +65,13 @@ control_action(Command, Cfg, Args, Opts) -> error_logger:info_msg(F ++ "~n", A) end]). +plugins_action(Command, Cfg, Args, Opts) -> + PluginsFile = os:getenv("RABBITMQ_ENABLED_PLUGINS_FILE"), + PluginsDir = os:getenv("RABBITMQ_PLUGINS_DIR"), + Node = pget(node, Cfg), + rpc:call(Node, rabbit_plugins_main, action, + [Command, Node, Args, Opts, PluginsFile, PluginsDir]). + restart_app(Cfg) -> stop_app(Cfg), start_app(Cfg). diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/cluster_rename.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/cluster_rename.erl new file mode 100644 index 0000000..258c0dc --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/cluster_rename.erl @@ -0,0 +1,194 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% +-module(cluster_rename). + +-compile(export_all). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp_client/include/amqp_client.hrl"). + +-import(rabbit_misc, [pget/2]). + +-define(CLUSTER2, + fun(C) -> rabbit_test_configs:cluster(C, [bugs, bigwig]) end). + +-define(CLUSTER3, + fun(C) -> rabbit_test_configs:cluster(C, [bugs, bigwig, peter]) end). + +%% Rolling rename of a cluster, each node should do a secondary rename. +rename_cluster_one_by_one_with() -> ?CLUSTER3. +rename_cluster_one_by_one([Bugs, Bigwig, Peter]) -> + publish_all([{Bugs, <<"1">>}, {Bigwig, <<"2">>}, {Peter, <<"3">>}]), + + Jessica = stop_rename_start(Bugs, jessica, [bugs, jessica]), + Hazel = stop_rename_start(Bigwig, hazel, [bigwig, hazel]), + Flopsy = stop_rename_start(Peter, flopsy, [peter, flopsy]), + + consume_all([{Jessica, <<"1">>}, {Hazel, <<"2">>}, {Flopsy, <<"3">>}]), + stop_all([Jessica, Hazel, Flopsy]), + ok. + +%% Big bang rename of a cluster, bugs should do a primary rename. +rename_cluster_big_bang_with() -> ?CLUSTER3. +rename_cluster_big_bang([Bugs, Bigwig, Peter]) -> + publish_all([{Bugs, <<"1">>}, {Bigwig, <<"2">>}, {Peter, <<"3">>}]), + + Peter1 = rabbit_test_configs:stop_node(Peter), + Bigwig1 = rabbit_test_configs:stop_node(Bigwig), + Bugs1 = rabbit_test_configs:stop_node(Bugs), + + Map = [bugs, jessica, bigwig, hazel, peter, flopsy], + Jessica0 = rename_node(Bugs1, jessica, Map), + Hazel0 = rename_node(Bigwig1, hazel, Map), + Flopsy0 = rename_node(Peter1, flopsy, Map), + + Jessica = rabbit_test_configs:start_node(Jessica0), + Hazel = rabbit_test_configs:start_node(Hazel0), + Flopsy = rabbit_test_configs:start_node(Flopsy0), + + consume_all([{Jessica, <<"1">>}, {Hazel, <<"2">>}, {Flopsy, <<"3">>}]), + stop_all([Jessica, Hazel, Flopsy]), + ok. + +%% Here we test that bugs copes with things being renamed around it. +partial_one_by_one_with() -> ?CLUSTER3. +partial_one_by_one([Bugs, Bigwig, Peter]) -> + publish_all([{Bugs, <<"1">>}, {Bigwig, <<"2">>}, {Peter, <<"3">>}]), + + Jessica = stop_rename_start(Bugs, jessica, [bugs, jessica]), + Hazel = stop_rename_start(Bigwig, hazel, [bigwig, hazel]), + + consume_all([{Jessica, <<"1">>}, {Hazel, <<"2">>}, {Peter, <<"3">>}]), + stop_all([Jessica, Hazel, Peter]), + ok. + +%% Here we test that bugs copes with things being renamed around it. +partial_big_bang_with() -> ?CLUSTER3. +partial_big_bang([Bugs, Bigwig, Peter]) -> + publish_all([{Bugs, <<"1">>}, {Bigwig, <<"2">>}, {Peter, <<"3">>}]), + + Peter1 = rabbit_test_configs:stop_node(Peter), + Bigwig1 = rabbit_test_configs:stop_node(Bigwig), + Bugs1 = rabbit_test_configs:stop_node(Bugs), + + Map = [bigwig, hazel, peter, flopsy], + Hazel0 = rename_node(Bigwig1, hazel, Map), + Flopsy0 = rename_node(Peter1, flopsy, Map), + + Bugs2 = rabbit_test_configs:start_node(Bugs1), + Hazel = rabbit_test_configs:start_node(Hazel0), + Flopsy = rabbit_test_configs:start_node(Flopsy0), + + consume_all([{Bugs2, <<"1">>}, {Hazel, <<"2">>}, {Flopsy, <<"3">>}]), + stop_all([Bugs2, Hazel, Flopsy]), + ok. + +%% We should be able to specify the -n parameter on ctl with either +%% the before or after name for the local node (since in real cases +%% one might want to invoke the command before or after the hostname +%% has changed) - usually we test before so here we test after. +post_change_nodename_with() -> ?CLUSTER2. +post_change_nodename([Bugs, _Bigwig]) -> + publish(Bugs, <<"bugs">>), + + Bugs1 = rabbit_test_configs:stop_node(Bugs), + Bugs2 = [{nodename, jessica} | proplists:delete(nodename, Bugs1)], + Jessica0 = rename_node(Bugs2, jessica, [bugs, jessica]), + Jessica = rabbit_test_configs:start_node(Jessica0), + + consume(Jessica, <<"bugs">>), + stop_all([Jessica]), + ok. + +%% If we invoke rename but the node name does not actually change, we +%% should roll back. +abortive_rename_with() -> ?CLUSTER2. +abortive_rename([Bugs, _Bigwig]) -> + publish(Bugs, <<"bugs">>), + + Bugs1 = rabbit_test_configs:stop_node(Bugs), + _Jessica = rename_node(Bugs1, jessica, [bugs, jessica]), + Bugs2 = rabbit_test_configs:start_node(Bugs1), + + consume(Bugs2, <<"bugs">>), + ok. + +%% And test some ways the command can fail. +rename_fail_with() -> ?CLUSTER2. +rename_fail([Bugs, _Bigwig]) -> + Bugs1 = rabbit_test_configs:stop_node(Bugs), + %% Rename from a node that does not exist + rename_node_fail(Bugs1, [bugzilla, jessica]), + %% Rename to a node which does + rename_node_fail(Bugs1, [bugs, bigwig]), + %% Rename two nodes to the same thing + rename_node_fail(Bugs1, [bugs, jessica, bigwig, jessica]), + %% Rename while impersonating a node not in the cluster + rename_node_fail(set_node(rabbit, Bugs1), [bugs, jessica]), + ok. + +rename_twice_fail_with() -> ?CLUSTER2. +rename_twice_fail([Bugs, _Bigwig]) -> + Bugs1 = rabbit_test_configs:stop_node(Bugs), + Indecisive = rename_node(Bugs1, indecisive, [bugs, indecisive]), + rename_node_fail(Indecisive, [indecisive, jessica]), + ok. + +%% ---------------------------------------------------------------------------- + +%% Normal post-test stop does not work since names have changed... +stop_all(Cfgs) -> + [rabbit_test_configs:stop_node(Cfg) || Cfg <- Cfgs]. + +stop_rename_start(Cfg, Nodename, Map) -> + rabbit_test_configs:start_node( + rename_node(rabbit_test_configs:stop_node(Cfg), Nodename, Map)). + +rename_node(Cfg, Nodename, Map) -> + rename_node(Cfg, Nodename, Map, fun rabbit_test_configs:rabbitmqctl/2). + +rename_node_fail(Cfg, Map) -> + rename_node(Cfg, ignored, Map, fun rabbit_test_configs:rabbitmqctl_fail/2). + +rename_node(Cfg, Nodename, Map, Ctl) -> + MapS = string:join( + [atom_to_list(rabbit_nodes:make(N)) || N <- Map], " "), + Ctl(Cfg, {"rename_cluster_node ~s", [MapS]}), + set_node(Nodename, Cfg). + +publish(Cfg, Q) -> + Ch = pget(channel, Cfg), + amqp_channel:call(Ch, #'confirm.select'{}), + amqp_channel:call(Ch, #'queue.declare'{queue = Q, durable = true}), + amqp_channel:cast(Ch, #'basic.publish'{routing_key = Q}, + #amqp_msg{props = #'P_basic'{delivery_mode = 2}, + payload = Q}), + amqp_channel:wait_for_confirms(Ch). + +consume(Cfg, Q) -> + {_Conn, Ch} = rabbit_test_util:connect(Cfg), + amqp_channel:call(Ch, #'queue.declare'{queue = Q, durable = true}), + {#'basic.get_ok'{}, #amqp_msg{payload = Q}} = + amqp_channel:call(Ch, #'basic.get'{queue = Q}). + + +publish_all(CfgsKeys) -> + [publish(Cfg, Key) || {Cfg, Key} <- CfgsKeys]. + +consume_all(CfgsKeys) -> + [consume(Cfg, Key) || {Cfg, Key} <- CfgsKeys]. + +set_node(Nodename, Cfg) -> + [{nodename, Nodename} | proplists:delete(nodename, Cfg)]. diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/clustering_management.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/clustering_management.erl index 43eb06b..f8b6cfd 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/clustering_management.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/clustering_management.erl @@ -106,7 +106,7 @@ join_to_start_interval(Config) -> assert_clustered([Rabbit, Hare]). forget_cluster_node_with() -> start_abc. -forget_cluster_node(Config) -> +forget_cluster_node([_, HareCfg, _] = Config) -> [Rabbit, Hare, Bunny] = cluster_members(Config), %% Trying to remove a node not in the cluster should fail @@ -145,9 +145,13 @@ forget_cluster_node(Config) -> ok = stop_app(Bunny), %% This is fine but we need the flag assert_failure(fun () -> forget_cluster_node(Hare, Bunny) end), - %% Hare was not the second-to-last to go down - ok = forget_cluster_node(Hare, Bunny, true), - ok = start_app(Hare), + %% Also fails because hare node is still running + assert_failure(fun () -> forget_cluster_node(Hare, Bunny, true) end), + %% But this works + HareCfg2 = rabbit_test_configs:stop_node(HareCfg), + rabbit_test_configs:rabbitmqctl( + HareCfg2, {"forget_cluster_node --offline ~s", [Bunny]}), + _HareCfg3 = rabbit_test_configs:start_node(HareCfg2), ok = start_app(Rabbit), %% Bunny still thinks its clustered with Rabbit and Hare assert_failure(fun () -> start_app(Bunny) end), @@ -156,28 +160,137 @@ forget_cluster_node(Config) -> assert_not_clustered(Bunny), assert_clustered([Rabbit, Hare]). -forget_cluster_node_removes_things_with() -> start_abc. -forget_cluster_node_removes_things([RabbitCfg, HareCfg, _BunnyCfg] = Config) -> - [Rabbit, Hare, _Bunny] = cluster_members(Config), - stop_join_start(Rabbit, Hare), - {_RConn, RCh} = rabbit_test_util:connect(RabbitCfg), - #'queue.declare_ok'{} = - amqp_channel:call(RCh, #'queue.declare'{queue = <<"test">>, - durable = true}), +forget_removes_things_with() -> cluster_ab. +forget_removes_things(Cfg) -> + test_removes_things(Cfg, fun (R, H) -> ok = forget_cluster_node(H, R) end). + +reset_removes_things_with() -> cluster_ab. +reset_removes_things(Cfg) -> + test_removes_things(Cfg, fun (R, _H) -> ok = reset(R) end). +test_removes_things([RabbitCfg, HareCfg] = Config, LoseRabbit) -> + Unmirrored = <<"unmirrored-queue">>, + [Rabbit, Hare] = cluster_members(Config), + RCh = pget(channel, RabbitCfg), + declare(RCh, Unmirrored), ok = stop_app(Rabbit), {_HConn, HCh} = rabbit_test_util:connect(HareCfg), {'EXIT',{{shutdown,{server_initiated_close,404,_}}, _}} = - (catch amqp_channel:call(HCh, #'queue.declare'{queue = <<"test">>, - durable = true})), - - ok = forget_cluster_node(Hare, Rabbit), + (catch declare(HCh, Unmirrored)), + ok = LoseRabbit(Rabbit, Hare), {_HConn2, HCh2} = rabbit_test_util:connect(HareCfg), - #'queue.declare_ok'{} = - amqp_channel:call(HCh2, #'queue.declare'{queue = <<"test">>, - durable = true}), + declare(HCh2, Unmirrored), + ok. + +forget_offline_removes_things_with() -> cluster_ab. +forget_offline_removes_things([Rabbit, Hare]) -> + Unmirrored = <<"unmirrored-queue">>, + X = <<"X">>, + RCh = pget(channel, Rabbit), + declare(RCh, Unmirrored), + + amqp_channel:call(RCh, #'exchange.declare'{durable = true, + exchange = X, + auto_delete = true}), + amqp_channel:call(RCh, #'queue.bind'{queue = Unmirrored, + exchange = X}), + ok = stop_app(pget(node, Rabbit)), + + {_HConn, HCh} = rabbit_test_util:connect(Hare), + {'EXIT',{{shutdown,{server_initiated_close,404,_}}, _}} = + (catch declare(HCh, Unmirrored)), + + Hare2 = rabbit_test_configs:stop_node(Hare), + _Rabbit2 = rabbit_test_configs:stop_node(Rabbit), + rabbit_test_configs:rabbitmqctl( + Hare2, {"forget_cluster_node --offline ~s", [pget(node, Rabbit)]}), + Hare3 = rabbit_test_configs:start_node(Hare2), + + {_HConn2, HCh2} = rabbit_test_util:connect(Hare3), + declare(HCh2, Unmirrored), + {'EXIT',{{shutdown,{server_initiated_close,404,_}}, _}} = + (catch amqp_channel:call(HCh2,#'exchange.declare'{durable = true, + exchange = X, + auto_delete = true, + passive = true})), + ok. + +forget_promotes_offline_slave_with() -> + fun (Cfgs) -> + rabbit_test_configs:cluster(Cfgs, [a, b, c, d]) + end. + +forget_promotes_offline_slave([A, B, C, D]) -> + ACh = pget(channel, A), + ANode = pget(node, A), + Q = <<"mirrored-queue">>, + declare(ACh, Q), + set_ha_policy(Q, A, [B, C]), + set_ha_policy(Q, A, [C, D]), %% Test add and remove from recoverable_slaves + + %% Publish and confirm + amqp_channel:call(ACh, #'confirm.select'{}), + amqp_channel:cast(ACh, #'basic.publish'{routing_key = Q}, + #amqp_msg{props = #'P_basic'{delivery_mode = 2}}), + amqp_channel:wait_for_confirms(ACh), + + %% We kill nodes rather than stop them in order to make sure + %% that we aren't dependent on anything that happens as they shut + %% down (see bug 26467). + D2 = rabbit_test_configs:kill_node(D), + C2 = rabbit_test_configs:kill_node(C), + _B2 = rabbit_test_configs:kill_node(B), + _A2 = rabbit_test_configs:kill_node(A), + + rabbit_test_configs:rabbitmqctl(C2, "force_boot"), + + C3 = rabbit_test_configs:start_node(C2), + + %% We should now have the following dramatis personae: + %% A - down, master + %% B - down, used to be slave, no longer is, never had the message + %% C - running, should be slave, but has wiped the message on restart + %% D - down, recoverable slave, contains message + %% + %% So forgetting A should offline-promote the queue to D, keeping + %% the message. + + rabbit_test_configs:rabbitmqctl(C3, {"forget_cluster_node ~s", [ANode]}), + + D3 = rabbit_test_configs:start_node(D2), + {_DConn2, DCh2} = rabbit_test_util:connect(D3), + #'queue.declare_ok'{message_count = 1} = declare(DCh2, Q), + ok. + +set_ha_policy(Q, MasterCfg, SlaveCfgs) -> + Nodes = [list_to_binary(atom_to_list(pget(node, N))) || + N <- [MasterCfg | SlaveCfgs]], + rabbit_test_util:set_ha_policy(MasterCfg, Q, {<<"nodes">>, Nodes}), + await_slaves(Q, pget(node, MasterCfg), [pget(node, C) || C <- SlaveCfgs]). + +await_slaves(Q, MNode, SNodes) -> + {ok, #amqqueue{pid = MPid, + slave_pids = SPids}} = + rpc:call(MNode, rabbit_amqqueue, lookup, + [rabbit_misc:r(<<"/">>, queue, Q)]), + ActMNode = node(MPid), + ActSNodes = lists:usort([node(P) || P <- SPids]), + case {MNode, lists:usort(SNodes)} of + {ActMNode, ActSNodes} -> ok; + _ -> timer:sleep(100), + await_slaves(Q, MNode, SNodes) + end. + +force_boot_with() -> cluster_ab. +force_boot([Rabbit, Hare]) -> + rabbit_test_configs:rabbitmqctl_fail(Rabbit, force_boot), + Rabbit2 = rabbit_test_configs:stop_node(Rabbit), + _Hare2 = rabbit_test_configs:stop_node(Hare), + rabbit_test_configs:start_node_fail(Rabbit2), + rabbit_test_configs:rabbitmqctl(Rabbit2, force_boot), + _Rabbit3 = rabbit_test_configs:start_node(Rabbit2), ok. change_cluster_node_type_with() -> start_abc. @@ -260,8 +373,8 @@ change_cluster_when_node_offline(Config) -> [Rabbit, Hare]), assert_not_clustered(Bunny). -update_cluster_nodes_test_with() -> start_abc. -update_cluster_nodes_test(Config) -> +update_cluster_nodes_with() -> start_abc. +update_cluster_nodes(Config) -> [Rabbit, Hare, Bunny] = cluster_members(Config), %% Mnesia is running... @@ -283,9 +396,9 @@ update_cluster_nodes_test(Config) -> assert_not_clustered(Hare), assert_clustered([Rabbit, Bunny]). -erlang_config_with() -> start_abc. +erlang_config_with() -> start_ab. erlang_config(Config) -> - [Rabbit, Hare, _Bunny] = cluster_members(Config), + [Rabbit, Hare] = cluster_members(Config), ok = stop_app(Hare), ok = reset(Hare), @@ -302,6 +415,17 @@ erlang_config(Config) -> assert_cluster_status({[Rabbit, Hare], [Rabbit], [Rabbit, Hare]}, [Rabbit, Hare]), + %% Check having a stop_app'ed node around doesn't break completely. + ok = stop_app(Hare), + ok = reset(Hare), + ok = stop_app(Rabbit), + ok = rpc:call(Hare, application, set_env, + [rabbit, cluster_nodes, {[Rabbit], disc}]), + ok = start_app(Hare), + ok = start_app(Rabbit), + assert_not_clustered(Hare), + assert_not_clustered(Rabbit), + %% We get a warning but we start anyway ok = stop_app(Hare), ok = reset(Hare), @@ -311,17 +435,47 @@ erlang_config(Config) -> assert_not_clustered(Hare), assert_not_clustered(Rabbit), - %% If we use a legacy config file, it still works (and a warning is emitted) + %% If we use a legacy config file, the node fails to start. ok = stop_app(Hare), ok = reset(Hare), ok = rpc:call(Hare, application, set_env, [rabbit, cluster_nodes, [Rabbit]]), - ok = start_app(Hare), - assert_cluster_status({[Rabbit, Hare], [Rabbit], [Rabbit, Hare]}, - [Rabbit, Hare]). + assert_failure(fun () -> start_app(Hare) end), + assert_not_clustered(Rabbit), -force_reset_test_with() -> start_abc. -force_reset_test(Config) -> + %% If we use an invalid node name, the node fails to start. + ok = stop_app(Hare), + ok = reset(Hare), + ok = rpc:call(Hare, application, set_env, + [rabbit, cluster_nodes, {["Mike's computer"], disc}]), + assert_failure(fun () -> start_app(Hare) end), + assert_not_clustered(Rabbit), + + %% If we use an invalid node type, the node fails to start. + ok = stop_app(Hare), + ok = reset(Hare), + ok = rpc:call(Hare, application, set_env, + [rabbit, cluster_nodes, {[Rabbit], blue}]), + assert_failure(fun () -> start_app(Hare) end), + assert_not_clustered(Rabbit), + + %% If we use an invalid cluster_nodes conf, the node fails to start. + ok = stop_app(Hare), + ok = reset(Hare), + ok = rpc:call(Hare, application, set_env, + [rabbit, cluster_nodes, true]), + assert_failure(fun () -> start_app(Hare) end), + assert_not_clustered(Rabbit), + + ok = stop_app(Hare), + ok = reset(Hare), + ok = rpc:call(Hare, application, set_env, + [rabbit, cluster_nodes, "Yes, please"]), + assert_failure(fun () -> start_app(Hare) end), + assert_not_clustered(Rabbit). + +force_reset_node_with() -> start_abc. +force_reset_node(Config) -> [Rabbit, Hare, _Bunny] = cluster_members(Config), stop_join_start(Rabbit, Hare), @@ -445,3 +599,10 @@ control_action(Command, Node, Args, Opts) -> rpc:call(Node, rabbit_control_main, action, [Command, Node, Args, Opts, fun io:format/2]). + +declare(Ch, Name) -> + Res = amqp_channel:call(Ch, #'queue.declare'{durable = true, + queue = Name}), + amqp_channel:call(Ch, #'queue.bind'{queue = Name, + exchange = <<"amq.fanout">>}), + Res. diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/crashing_queues.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/crashing_queues.erl new file mode 100644 index 0000000..e34fd04 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/crashing_queues.erl @@ -0,0 +1,213 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% +-module(crashing_queues). + +-compile(export_all). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp_client/include/amqp_client.hrl"). + +-import(rabbit_test_util, [set_ha_policy/3, a2b/1]). +-import(rabbit_misc, [pget/2]). + +crashing_unmirrored_with() -> [cluster_ab]. +crashing_unmirrored([CfgA, CfgB]) -> + A = pget(node, CfgA), + ChA = pget(channel, CfgA), + ConnB = pget(connection, CfgB), + amqp_channel:call(ChA, #'confirm.select'{}), + test_queue_failure(A, ChA, ConnB, 1, 0, + #'queue.declare'{queue = <<"test">>, durable = true}), + test_queue_failure(A, ChA, ConnB, 0, 0, + #'queue.declare'{queue = <<"test">>, durable = false}), + ok. + +crashing_mirrored_with() -> [cluster_ab, ha_policy_all]. +crashing_mirrored([CfgA, CfgB]) -> + A = pget(node, CfgA), + ChA = pget(channel, CfgA), + ConnB = pget(connection, CfgB), + amqp_channel:call(ChA, #'confirm.select'{}), + test_queue_failure(A, ChA, ConnB, 2, 1, + #'queue.declare'{queue = <<"test">>, durable = true}), + test_queue_failure(A, ChA, ConnB, 2, 1, + #'queue.declare'{queue = <<"test">>, durable = false}), + ok. + +test_queue_failure(Node, Ch, RaceConn, MsgCount, SlaveCount, Decl) -> + #'queue.declare_ok'{queue = QName} = amqp_channel:call(Ch, Decl), + publish(Ch, QName, transient), + publish(Ch, QName, durable), + Racer = spawn_declare_racer(RaceConn, Decl), + kill_queue(Node, QName), + assert_message_count(MsgCount, Ch, QName), + assert_slave_count(SlaveCount, Node, QName), + stop_declare_racer(Racer), + amqp_channel:call(Ch, #'queue.delete'{queue = QName}). + +give_up_after_repeated_crashes_with() -> [cluster_ab]. +give_up_after_repeated_crashes([CfgA, CfgB]) -> + A = pget(node, CfgA), + ChA = pget(channel, CfgA), + ChB = pget(channel, CfgB), + QName = <<"test">>, + amqp_channel:call(ChA, #'confirm.select'{}), + amqp_channel:call(ChA, #'queue.declare'{queue = QName, + durable = true}), + await_state(A, QName, running), + publish(ChA, QName, durable), + kill_queue_hard(A, QName), + {'EXIT', _} = (catch amqp_channel:call( + ChA, #'queue.declare'{queue = QName, + durable = true})), + await_state(A, QName, crashed), + amqp_channel:call(ChB, #'queue.delete'{queue = QName}), + amqp_channel:call(ChB, #'queue.declare'{queue = QName, + durable = true}), + await_state(A, QName, running), + + %% Since it's convenient, also test absent queue status here. + rabbit_test_configs:stop_node(CfgB), + await_state(A, QName, down), + ok. + + +publish(Ch, QName, DelMode) -> + Publish = #'basic.publish'{exchange = <<>>, routing_key = QName}, + Msg = #amqp_msg{props = #'P_basic'{delivery_mode = del_mode(DelMode)}}, + amqp_channel:cast(Ch, Publish, Msg), + amqp_channel:wait_for_confirms(Ch). + +del_mode(transient) -> 1; +del_mode(durable) -> 2. + +spawn_declare_racer(Conn, Decl) -> + Self = self(), + spawn_link(fun() -> declare_racer_loop(Self, Conn, Decl) end). + +stop_declare_racer(Pid) -> + Pid ! stop, + MRef = erlang:monitor(process, Pid), + receive + {'DOWN', MRef, process, Pid, _} -> ok + end. + +declare_racer_loop(Parent, Conn, Decl) -> + receive + stop -> unlink(Parent) + after 0 -> + %% Catch here because we might happen to catch the queue + %% while it is in the middle of recovering and thus + %% explode with NOT_FOUND because crashed. Doesn't matter, + %% we are only in this loop to try to fool the recovery + %% code anyway. + try + case amqp_connection:open_channel(Conn) of + {ok, Ch} -> amqp_channel:call(Ch, Decl); + closing -> ok + end + catch + exit:_ -> + ok + end, + declare_racer_loop(Parent, Conn, Decl) + end. + +await_state(Node, QName, State) -> + await_state(Node, QName, State, 30000). + +await_state(Node, QName, State, Time) -> + case state(Node, QName) of + State -> + ok; + Other -> + case Time of + 0 -> exit({timeout_awaiting_state, State, Other}); + _ -> timer:sleep(100), + await_state(Node, QName, State, Time - 100) + end + end. + +state(Node, QName) -> + V = <<"/">>, + Res = rabbit_misc:r(V, queue, QName), + [[{name, Res}, + {state, State}]] = + rpc:call(Node, rabbit_amqqueue, info_all, [V, [name, state]]), + State. + +kill_queue_hard(Node, QName) -> + case kill_queue(Node, QName) of + crashed -> ok; + _NewPid -> timer:sleep(100), + kill_queue_hard(Node, QName) + end. + +kill_queue(Node, QName) -> + Pid1 = queue_pid(Node, QName), + exit(Pid1, boom), + await_new_pid(Node, QName, Pid1). + +queue_pid(Node, QName) -> + #amqqueue{pid = QPid, + state = State} = lookup(Node, QName), + case State of + crashed -> case sup_child(Node, rabbit_amqqueue_sup_sup) of + {ok, _} -> QPid; %% restarting + {error, no_child} -> crashed %% given up + end; + _ -> QPid + end. + +sup_child(Node, Sup) -> + case rpc:call(Node, supervisor2, which_children, [Sup]) of + [{_, Child, _, _}] -> {ok, Child}; + [] -> {error, no_child}; + {badrpc, {'EXIT', {noproc, _}}} -> {error, no_sup} + end. + +lookup(Node, QName) -> + {ok, Q} = rpc:call(Node, rabbit_amqqueue, lookup, + [rabbit_misc:r(<<"/">>, queue, QName)]), + Q. + +await_new_pid(Node, QName, OldPid) -> + case queue_pid(Node, QName) of + OldPid -> timer:sleep(10), + await_new_pid(Node, QName, OldPid); + New -> New + end. + +assert_message_count(Count, Ch, QName) -> + #'queue.declare_ok'{message_count = Count} = + amqp_channel:call(Ch, #'queue.declare'{queue = QName, + passive = true}). + +assert_slave_count(Count, Node, QName) -> + Q = lookup(Node, QName), + [{_, Pids}] = rpc:call(Node, rabbit_amqqueue, info, [Q, [slave_pids]]), + RealCount = case Pids of + '' -> 0; + _ -> length(Pids) + end, + case RealCount of + Count -> + ok; + _ when RealCount < Count -> + timer:sleep(10), + assert_slave_count(Count, Node, QName); + _ -> + exit({too_many_slaves, Count, RealCount}) + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/dynamic_ha.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/dynamic_ha.erl index 8be3eeb..e9acb52 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/dynamic_ha.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/dynamic_ha.erl @@ -87,12 +87,13 @@ change_cluster([CfgA, _CfgB, _CfgC] = CfgsABC) -> %% Add D and E, D joins in [CfgD, CfgE] = CfgsDE = rabbit_test_configs:start_nodes(CfgA, [d, e], 5675), D = pget(node, CfgD), + E = pget(node, CfgE), rabbit_test_configs:add_to_cluster(CfgsABC, CfgsDE), assert_slaves(A, ?QNAME, {A, [B, C, D]}), - %% Remove D, E does not join in + %% Remove D, E joins in rabbit_test_configs:stop_node(CfgD), - assert_slaves(A, ?QNAME, {A, [B, C]}), + assert_slaves(A, ?QNAME, {A, [B, C, E]}), %% Clean up since we started this by hand rabbit_test_configs:stop_node(CfgE), @@ -101,13 +102,11 @@ change_cluster([CfgA, _CfgB, _CfgC] = CfgsABC) -> rapid_change_with() -> cluster_abc. rapid_change([CfgA, _CfgB, _CfgC]) -> ACh = pget(channel, CfgA), - Self = self(), - spawn_link( - fun() -> - [rapid_amqp_ops(ACh, I) || I <- lists:seq(1, 100)], - Self ! done - end), - rapid_loop(CfgA), + {_Pid, MRef} = spawn_monitor( + fun() -> + [rapid_amqp_ops(ACh, I) || I <- lists:seq(1, 100)] + end), + rapid_loop(CfgA, MRef), ok. rapid_amqp_ops(Ch, I) -> @@ -125,13 +124,16 @@ rapid_amqp_ops(Ch, I) -> end, amqp_channel:call(Ch, #'queue.delete'{queue = ?QNAME}). -rapid_loop(Cfg) -> - receive done -> - ok +rapid_loop(Cfg, MRef) -> + receive + {'DOWN', MRef, process, _Pid, normal} -> + ok; + {'DOWN', MRef, process, _Pid, Reason} -> + exit({amqp_ops_died, Reason}) after 0 -> set_ha_policy(Cfg, ?POLICY, <<"all">>), clear_policy(Cfg, ?POLICY), - rapid_loop(Cfg) + rapid_loop(Cfg, MRef) end. %% Vhost deletion needs to successfully tear down policies and queues @@ -144,6 +146,38 @@ vhost_deletion([CfgA, _CfgB]) -> ok = rpc:call(Node, rabbit_vhost, delete, [<<"/">>]), ok. +promote_on_shutdown_with() -> cluster_ab. +promote_on_shutdown([CfgA, CfgB]) -> + set_ha_policy(CfgA, <<"^ha.promote">>, <<"all">>, + [{<<"ha-promote-on-shutdown">>, <<"always">>}]), + set_ha_policy(CfgA, <<"^ha.nopromote">>, <<"all">>), + + ACh = pget(channel, CfgA), + [begin + amqp_channel:call(ACh, #'queue.declare'{queue = Q, + durable = true}), + publish(ACh, Q, 10) + end || Q <- [<<"ha.promote.test">>, <<"ha.nopromote.test">>]], + rabbit_test_configs:restart_node(CfgB), + CfgA1 = rabbit_test_configs:stop_node(CfgA), + {_, BCh} = rabbit_test_util:connect(CfgB), + #'queue.declare_ok'{message_count = 0} = + amqp_channel:call( + BCh, #'queue.declare'{queue = <<"ha.promote.test">>, + durable = true}), + ?assertExit( + {{shutdown, {server_initiated_close, 404, _}}, _}, + amqp_channel:call( + BCh, #'queue.declare'{queue = <<"ha.nopromote.test">>, + durable = true})), + CfgA2 = rabbit_test_configs:start_node(CfgA1), + {_, ACh2} = rabbit_test_util:connect(CfgA2), + #'queue.declare_ok'{message_count = 10} = + amqp_channel:call( + ACh2, #'queue.declare'{queue = <<"ha.nopromote.test">>, + durable = true}), + ok. + %%---------------------------------------------------------------------------- assert_slaves(RPCNode, QName, Exp) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/partitions.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/partitions.erl index 7ad6a07..56b99ca 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/partitions.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/partitions.erl @@ -30,15 +30,15 @@ ignore_with() -> ?CONFIG. ignore(Cfgs) -> [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], - block_unblock([{B, C}]), + block_unblock([{A, B}, {A, C}]), timer:sleep(?DELAY), - [] = partitions(A), - [C] = partitions(B), - [B] = partitions(C), + [B, C] = partitions(A), + [A] = partitions(B), + [A] = partitions(C), ok. -pause_on_down_with() -> ?CONFIG. -pause_on_down([CfgA, CfgB, CfgC] = Cfgs) -> +pause_minority_on_down_with() -> ?CONFIG. +pause_minority_on_down([CfgA, CfgB, CfgC] = Cfgs) -> A = pget(node, CfgA), set_mode(Cfgs, pause_minority), true = is_running(A), @@ -51,10 +51,34 @@ pause_on_down([CfgA, CfgB, CfgC] = Cfgs) -> await_running(A, false), ok. -pause_on_blocked_with() -> ?CONFIG. -pause_on_blocked(Cfgs) -> +pause_minority_on_blocked_with() -> ?CONFIG. +pause_minority_on_blocked(Cfgs) -> [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], set_mode(Cfgs, pause_minority), + pause_on_blocked(A, B, C). + +pause_if_all_down_on_down_with() -> ?CONFIG. +pause_if_all_down_on_down([_, CfgB, CfgC] = Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + set_mode(Cfgs, {pause_if_all_down, [C], ignore}), + [(true = is_running(N)) || N <- [A, B, C]], + + rabbit_test_util:kill(CfgB, sigkill), + timer:sleep(?DELAY), + [(true = is_running(N)) || N <- [A, C]], + + rabbit_test_util:kill(CfgC, sigkill), + timer:sleep(?DELAY), + await_running(A, false), + ok. + +pause_if_all_down_on_blocked_with() -> ?CONFIG. +pause_if_all_down_on_blocked(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + set_mode(Cfgs, {pause_if_all_down, [C], ignore}), + pause_on_blocked(A, B, C). + +pause_on_blocked(A, B, C) -> [(true = is_running(N)) || N <- [A, B, C]], block([{A, B}, {A, C}]), await_running(A, false), @@ -77,23 +101,39 @@ pause_on_blocked(Cfgs) -> %% test to pass since there are a lot of things in the broker that can %% suddenly take several seconds to time out when TCP connections %% won't establish. -pause_false_promises_mirrored_with() -> +pause_minority_false_promises_mirrored_with() -> [start_ab, fun enable_dist_proxy/1, build_cluster, short_ticktime(10), start_connections, ha_policy_all]. -pause_false_promises_mirrored(Cfgs) -> - pause_false_promises(Cfgs). +pause_minority_false_promises_mirrored(Cfgs) -> + pause_false_promises(Cfgs, pause_minority). -pause_false_promises_unmirrored_with() -> +pause_minority_false_promises_unmirrored_with() -> [start_ab, fun enable_dist_proxy/1, build_cluster, short_ticktime(10), start_connections]. -pause_false_promises_unmirrored(Cfgs) -> - pause_false_promises(Cfgs). +pause_minority_false_promises_unmirrored(Cfgs) -> + pause_false_promises(Cfgs, pause_minority). -pause_false_promises([CfgA, CfgB | _] = Cfgs) -> +pause_if_all_down_false_promises_mirrored_with() -> + [start_ab, fun enable_dist_proxy/1, + build_cluster, short_ticktime(10), start_connections, ha_policy_all]. + +pause_if_all_down_false_promises_mirrored([_, CfgB | _] = Cfgs) -> + B = pget(node, CfgB), + pause_false_promises(Cfgs, {pause_if_all_down, [B], ignore}). + +pause_if_all_down_false_promises_unmirrored_with() -> + [start_ab, fun enable_dist_proxy/1, + build_cluster, short_ticktime(10), start_connections]. + +pause_if_all_down_false_promises_unmirrored([_, CfgB | _] = Cfgs) -> + B = pget(node, CfgB), + pause_false_promises(Cfgs, {pause_if_all_down, [B], ignore}). + +pause_false_promises([CfgA, CfgB | _] = Cfgs, ClusterPartitionHandling) -> [A, B] = [pget(node, Cfg) || Cfg <- Cfgs], - set_mode([CfgA], pause_minority), + set_mode([CfgA], ClusterPartitionHandling), ChA = pget(channel, CfgA), ChB = pget(channel, CfgB), amqp_channel:call(ChB, #'queue.declare'{queue = <<"test">>, @@ -164,22 +204,99 @@ prompt_disconnect_detection([CfgA, CfgB]) -> [] = rpc(CfgA, rabbit_amqqueue, info_all, [<<"/">>], ?DELAY), ok. +ctl_ticktime_sync_with() -> [start_ab, short_ticktime(1)]. +ctl_ticktime_sync([CfgA | _]) -> + %% Server has 1s net_ticktime, make sure ctl doesn't get disconnected + "ok\n" = rabbit_test_configs:rabbitmqctl(CfgA, "eval 'timer:sleep(5000).'"), + ok. + +%% NB: we test full and partial partitions here. autoheal_with() -> ?CONFIG. autoheal(Cfgs) -> - [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], set_mode(Cfgs, autoheal), + do_autoheal(Cfgs). + +autoheal_after_pause_if_all_down_with() -> ?CONFIG. +autoheal_after_pause_if_all_down([_, CfgB, CfgC | _] = Cfgs) -> + B = pget(node, CfgB), + C = pget(node, CfgC), + set_mode(Cfgs, {pause_if_all_down, [B, C], autoheal}), + do_autoheal(Cfgs). + +do_autoheal(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], Test = fun (Pairs) -> block_unblock(Pairs), - [await_running(N, true) || N <- [A, B, C]], - [] = partitions(A), - [] = partitions(B), - [] = partitions(C) + %% Sleep to make sure all the partitions are noticed + %% ?DELAY for the net_tick timeout + timer:sleep(?DELAY), + [await_listening(N, true) || N <- [A, B, C]], + [await_partitions(N, []) || N <- [A, B, C]] end, Test([{B, C}]), Test([{A, C}, {B, C}]), Test([{A, B}, {A, C}, {B, C}]), ok. +partial_false_positive_with() -> ?CONFIG. +partial_false_positive(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + block([{A, B}]), + timer:sleep(1000), + block([{A, C}]), + timer:sleep(?DELAY), + unblock([{A, B}, {A, C}]), + timer:sleep(?DELAY), + %% When B times out A's connection, it will check with C. C will + %% not have timed out A yet, but already it can't talk to it. We + %% need to not consider this a partial partition; B and C should + %% still talk to each other. + [B, C] = partitions(A), + [A] = partitions(B), + [A] = partitions(C), + ok. + +partial_to_full_with() -> ?CONFIG. +partial_to_full(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + block_unblock([{A, B}]), + timer:sleep(?DELAY), + %% There are several valid ways this could go, depending on how + %% the DOWN messages race: either A gets disconnected first and BC + %% stay together, or B gets disconnected first and AC stay + %% together, or both make it through and all three get + %% disconnected. + case {partitions(A), partitions(B), partitions(C)} of + {[B, C], [A], [A]} -> ok; + {[B], [A, C], [B]} -> ok; + {[B, C], [A, C], [A, B]} -> ok; + Partitions -> exit({partitions, Partitions}) + end. + +partial_pause_minority_with() -> ?CONFIG. +partial_pause_minority(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + set_mode(Cfgs, pause_minority), + block([{A, B}]), + [await_running(N, false) || N <- [A, B]], + await_running(C, true), + unblock([{A, B}]), + [await_listening(N, true) || N <- [A, B, C]], + [await_partitions(N, []) || N <- [A, B, C]], + ok. + +partial_pause_if_all_down_with() -> ?CONFIG. +partial_pause_if_all_down(Cfgs) -> + [A, B, C] = [pget(node, Cfg) || Cfg <- Cfgs], + set_mode(Cfgs, {pause_if_all_down, [B], ignore}), + block([{A, B}]), + await_running(A, false), + [await_running(N, true) || N <- [B, C]], + unblock([{A, B}]), + [await_listening(N, true) || N <- [A, B, C]], + [await_partitions(N, []) || N <- [A, B, C]], + ok. + set_mode(Cfgs, Mode) -> [set_env(Cfg, rabbit, cluster_partition_handling, Mode) || Cfg <- Cfgs]. @@ -195,7 +312,14 @@ block(Pairs) -> [block(X, Y) || {X, Y} <- Pairs]. unblock(Pairs) -> [allow(X, Y) || {X, Y} <- Pairs]. partitions(Node) -> - rpc:call(Node, rabbit_node_monitor, partitions, []). + case rpc:call(Node, rabbit_node_monitor, partitions, []) of + {badrpc, {'EXIT', E}} = R -> case rabbit_misc:is_abnormal_exit(E) of + true -> R; + false -> timer:sleep(1000), + partitions(Node) + end; + Partitions -> Partitions + end. block(X, Y) -> rpc:call(X, inet_tcp_proxy, block, [Y]), @@ -205,14 +329,15 @@ allow(X, Y) -> rpc:call(X, inet_tcp_proxy, allow, [Y]), rpc:call(Y, inet_tcp_proxy, allow, [X]). -await_running (Node, Bool) -> await(Node, Bool, fun is_running/1). -await_listening(Node, Bool) -> await(Node, Bool, fun is_listening/1). +await_running (Node, Bool) -> await(Node, Bool, fun is_running/1). +await_listening (Node, Bool) -> await(Node, Bool, fun is_listening/1). +await_partitions(Node, Parts) -> await(Node, Parts, fun partitions/1). -await(Node, Bool, Fun) -> +await(Node, Res, Fun) -> case Fun(Node) of - Bool -> ok; - _ -> timer:sleep(100), - await(Node, Bool, Fun) + Res -> ok; + _ -> timer:sleep(100), + await(Node, Res, Fun) end. is_running(Node) -> rpc:call(Node, rabbit, is_running, []). diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/rabbit_priority_queue_test.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/rabbit_priority_queue_test.erl new file mode 100644 index 0000000..44228ff --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/rabbit_priority_queue_test.erl @@ -0,0 +1,335 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% + +-module(rabbit_priority_queue_test). + +-compile(export_all). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp_client/include/amqp_client.hrl"). + +-import(rabbit_misc, [pget/2]). + +%% The BQ API is used in all sorts of places in all sorts of +%% ways. Therefore we have to jump through a few different hoops +%% in order to integration-test it. +%% +%% * start/1, stop/0, init/3, terminate/2, delete_and_terminate/2 +%% - starting and stopping rabbit. durable queues / persistent msgs needed +%% to test recovery +%% +%% * publish/5, drain_confirmed/1, fetch/2, ack/2, is_duplicate/2, msg_rates/1, +%% needs_timeout/1, timeout/1, invoke/3, resume/1 [0] +%% - regular publishing and consuming, with confirms and acks and durability +%% +%% * publish_delivered/4 - publish with acks straight through +%% * discard/3 - publish without acks straight through +%% * dropwhile/2 - expire messages without DLX +%% * fetchwhile/4 - expire messages with DLX +%% * ackfold/4 - reject messages with DLX +%% * requeue/2 - reject messages without DLX +%% * drop/2 - maxlen messages without DLX +%% * purge/1 - issue AMQP queue.purge +%% * purge_acks/1 - mirror queue explicit sync with unacked msgs +%% * fold/3 - mirror queue explicit sync +%% * depth/1 - mirror queue implicit sync detection +%% * len/1, is_empty/1 - info items +%% * handle_pre_hibernate/1 - hibernation +%% +%% * set_ram_duration_target/2, ram_duration/1, status/1 +%% - maybe need unit testing? +%% +%% [0] publish enough to get credit flow from msg store + +recovery_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 3), + publish(Ch, Q, [1, 2, 3, 1, 2, 3, 1, 2, 3]), + amqp_connection:close(Conn), + + %% TODO these break coverage + rabbit:stop(), + rabbit:start(), + + {Conn2, Ch2} = open(), + get_all(Ch2, Q, do_ack, [3, 3, 3, 2, 2, 2, 1, 1, 1]), + delete(Ch2, Q), + amqp_connection:close(Conn2), + passed. + +simple_order_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 3), + publish(Ch, Q, [1, 2, 3, 1, 2, 3, 1, 2, 3]), + get_all(Ch, Q, do_ack, [3, 3, 3, 2, 2, 2, 1, 1, 1]), + publish(Ch, Q, [2, 3, 1, 2, 3, 1, 2, 3, 1]), + get_all(Ch, Q, no_ack, [3, 3, 3, 2, 2, 2, 1, 1, 1]), + publish(Ch, Q, [3, 1, 2, 3, 1, 2, 3, 1, 2]), + get_all(Ch, Q, do_ack, [3, 3, 3, 2, 2, 2, 1, 1, 1]), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +matching_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 5), + %% We round priority down, and 0 is the default + publish(Ch, Q, [undefined, 0, 5, 10, undefined]), + get_all(Ch, Q, do_ack, [5, 10, undefined, 0, undefined]), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +resume_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 5), + amqp_channel:call(Ch, #'confirm.select'{}), + publish_many(Ch, Q, 10000), + amqp_channel:wait_for_confirms(Ch), + amqp_channel:call(Ch, #'queue.purge'{queue = Q}), %% Assert it exists + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +straight_through_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 3), + [begin + consume(Ch, Q, Ack), + [begin + publish1(Ch, Q, P), + assert_delivered(Ch, Ack, P) + end || P <- [1, 2, 3]], + cancel(Ch) + end || Ack <- [do_ack, no_ack]], + get_empty(Ch, Q), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +dropwhile_fetchwhile_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + [begin + declare(Ch, Q, Args ++ arguments(3)), + publish(Ch, Q, [1, 2, 3, 1, 2, 3, 1, 2, 3]), + timer:sleep(10), + get_empty(Ch, Q), + delete(Ch, Q) + end || + Args <- [[{<<"x-message-ttl">>, long, 1}], + [{<<"x-message-ttl">>, long, 1}, + {<<"x-dead-letter-exchange">>, longstr, <<"amq.fanout">>}] + ]], + amqp_connection:close(Conn), + passed. + +ackfold_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + Q2 = <<"test2">>, + declare(Ch, Q, + [{<<"x-dead-letter-exchange">>, longstr, <<>>}, + {<<"x-dead-letter-routing-key">>, longstr, Q2} + | arguments(3)]), + declare(Ch, Q2, none), + publish(Ch, Q, [1, 2, 3]), + [_, _, DTag] = get_all(Ch, Q, manual_ack, [3, 2, 1]), + amqp_channel:cast(Ch, #'basic.nack'{delivery_tag = DTag, + multiple = true, + requeue = false}), + timer:sleep(100), + get_all(Ch, Q2, do_ack, [3, 2, 1]), + delete(Ch, Q), + delete(Ch, Q2), + amqp_connection:close(Conn), + passed. + +requeue_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 3), + publish(Ch, Q, [1, 2, 3]), + [_, _, DTag] = get_all(Ch, Q, manual_ack, [3, 2, 1]), + amqp_channel:cast(Ch, #'basic.nack'{delivery_tag = DTag, + multiple = true, + requeue = true}), + get_all(Ch, Q, do_ack, [3, 2, 1]), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +drop_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, [{<<"x-max-length">>, long, 4} | arguments(3)]), + publish(Ch, Q, [1, 2, 3, 1, 2, 3, 1, 2, 3]), + %% We drop from the head, so this is according to the "spec" even + %% if not likely to be what the user wants. + get_all(Ch, Q, do_ack, [2, 1, 1, 1]), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +purge_test() -> + {Conn, Ch} = open(), + Q = <<"test">>, + declare(Ch, Q, 3), + publish(Ch, Q, [1, 2, 3]), + amqp_channel:call(Ch, #'queue.purge'{queue = Q}), + get_empty(Ch, Q), + delete(Ch, Q), + amqp_connection:close(Conn), + passed. + +ram_duration_test() -> + QName = rabbit_misc:r(<<"/">>, queue, <<"pseudo">>), + Q0 = rabbit_amqqueue:pseudo_queue(QName, self()), + Q = Q0#amqqueue{arguments = [{<<"x-max-priority">>, long, 5}]}, + PQ = rabbit_priority_queue, + BQS1 = PQ:init(Q, new, fun(_, _) -> ok end), + {_Duration1, BQS2} = PQ:ram_duration(BQS1), + BQS3 = PQ:set_ram_duration_target(infinity, BQS2), + BQS4 = PQ:set_ram_duration_target(1, BQS3), + {_Duration2, BQS5} = PQ:ram_duration(BQS4), + PQ:delete_and_terminate(a_whim, BQS5), + passed. + +mirror_queue_sync_with() -> cluster_ab. +mirror_queue_sync([CfgA, _CfgB]) -> + Ch = pget(channel, CfgA), + Q = <<"test">>, + declare(Ch, Q, 3), + publish(Ch, Q, [1, 2, 3]), + ok = rabbit_test_util:set_ha_policy(CfgA, <<".*">>, <<"all">>), + publish(Ch, Q, [1, 2, 3, 1, 2, 3]), + %% master now has 9, slave 6. + get_partial(Ch, Q, manual_ack, [3, 3, 3, 2, 2, 2]), + %% So some but not all are unacked at the slave + rabbit_test_util:control_action(sync_queue, CfgA, [binary_to_list(Q)], + [{"-p", "/"}]), + wait_for_sync(CfgA, rabbit_misc:r(<<"/">>, queue, Q)), + passed. + +%%---------------------------------------------------------------------------- + +open() -> + {ok, Conn} = amqp_connection:start(#amqp_params_network{}), + {ok, Ch} = amqp_connection:open_channel(Conn), + {Conn, Ch}. + +declare(Ch, Q, Args) when is_list(Args) -> + amqp_channel:call(Ch, #'queue.declare'{queue = Q, + durable = true, + arguments = Args}); +declare(Ch, Q, Max) -> + declare(Ch, Q, arguments(Max)). + +delete(Ch, Q) -> + amqp_channel:call(Ch, #'queue.delete'{queue = Q}). + +publish(Ch, Q, Ps) -> + amqp_channel:call(Ch, #'confirm.select'{}), + [publish1(Ch, Q, P) || P <- Ps], + amqp_channel:wait_for_confirms(Ch). + +publish_many(_Ch, _Q, 0) -> ok; +publish_many( Ch, Q, N) -> publish1(Ch, Q, random:uniform(5)), + publish_many(Ch, Q, N - 1). + +publish1(Ch, Q, P) -> + amqp_channel:cast(Ch, #'basic.publish'{routing_key = Q}, + #amqp_msg{props = props(P), + payload = priority2bin(P)}). + +props(undefined) -> #'P_basic'{delivery_mode = 2}; +props(P) -> #'P_basic'{priority = P, + delivery_mode = 2}. + +consume(Ch, Q, Ack) -> + amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q, + no_ack = Ack =:= no_ack, + consumer_tag = <<"ctag">>}, + self()), + receive + #'basic.consume_ok'{consumer_tag = <<"ctag">>} -> + ok + end. + +cancel(Ch) -> + amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = <<"ctag">>}). + +assert_delivered(Ch, Ack, P) -> + PBin = priority2bin(P), + receive + {#'basic.deliver'{delivery_tag = DTag}, #amqp_msg{payload = PBin2}} -> + ?assertEqual(PBin, PBin2), + maybe_ack(Ch, Ack, DTag) + end. + +get_all(Ch, Q, Ack, Ps) -> + DTags = get_partial(Ch, Q, Ack, Ps), + get_empty(Ch, Q), + DTags. + +get_partial(Ch, Q, Ack, Ps) -> + [get_ok(Ch, Q, Ack, P) || P <- Ps]. + +get_empty(Ch, Q) -> + #'basic.get_empty'{} = amqp_channel:call(Ch, #'basic.get'{queue = Q}). + +get_ok(Ch, Q, Ack, P) -> + PBin = priority2bin(P), + {#'basic.get_ok'{delivery_tag = DTag}, #amqp_msg{payload = PBin2}} = + amqp_channel:call(Ch, #'basic.get'{queue = Q, + no_ack = Ack =:= no_ack}), + ?assertEqual(PBin, PBin2), + maybe_ack(Ch, Ack, DTag). + +maybe_ack(Ch, do_ack, DTag) -> + amqp_channel:cast(Ch, #'basic.ack'{delivery_tag = DTag}), + DTag; +maybe_ack(_Ch, _, DTag) -> + DTag. + +arguments(none) -> []; +arguments(Max) -> [{<<"x-max-priority">>, byte, Max}]. + +priority2bin(undefined) -> <<"undefined">>; +priority2bin(Int) -> list_to_binary(integer_to_list(Int)). + +%%---------------------------------------------------------------------------- + +wait_for_sync(Cfg, Q) -> + case synced(Cfg, Q) of + true -> ok; + false -> timer:sleep(100), + wait_for_sync(Cfg, Q) + end. + +synced(Cfg, Q) -> + Info = rpc:call(pget(node, Cfg), + rabbit_amqqueue, info_all, + [<<"/">>, [name, synchronised_slave_pids]]), + [SSPids] = [Pids || [{name, Q1}, {synchronised_slave_pids, Pids}] <- Info, + Q =:= Q1], + length(SSPids) =:= 1. + +%%---------------------------------------------------------------------------- diff --git a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/simple_ha.erl b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/simple_ha.erl index 7b13f0f..389ff23 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-test/test/src/simple_ha.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-test/test/src/simple_ha.erl @@ -35,6 +35,26 @@ rapid_redeclare([CfgA | _]) -> end || _I <- lists:seq(1, 20)], ok. +%% Check that by the time we get a declare-ok back, the slaves are up +%% and in Mnesia. +declare_synchrony_with() -> [cluster_ab, ha_policy_all]. +declare_synchrony([Rabbit, Hare]) -> + RabbitCh = pget(channel, Rabbit), + HareCh = pget(channel, Hare), + Q = <<"mirrored-queue">>, + declare(RabbitCh, Q), + amqp_channel:call(RabbitCh, #'confirm.select'{}), + amqp_channel:cast(RabbitCh, #'basic.publish'{routing_key = Q}, + #amqp_msg{props = #'P_basic'{delivery_mode = 2}}), + amqp_channel:wait_for_confirms(RabbitCh), + _Rabbit2 = rabbit_test_configs:kill_node(Rabbit), + + #'queue.declare_ok'{message_count = 1} = declare(HareCh, Q), + ok. + +declare(Ch, Name) -> + amqp_channel:call(Ch, #'queue.declare'{durable = true, queue = Name}). + consume_survives_stop_with() -> ?CONFIG. consume_survives_sigkill_with() -> ?CONFIG. consume_survives_policy_with() -> ?CONFIG. diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-tracing/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/README b/rabbitmq-server/plugins-src/rabbitmq-tracing/README index c91d414..61b5ae4 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/README +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/README @@ -36,5 +36,7 @@ Example for how to create a trace: $ curl -i -u guest:guest -H "content-type:application/json" -XPUT \ http://localhost:55672/api/traces/%2f/my-trace \ - -d'{"format":"text","pattern":"#"}' + -d'{"format":"text","pattern":"#", "max_payload_bytes":1000}' +max_payload_bytes is optional (omit it to prevent payload truncation), +format and pattern are mandatory. \ No newline at end of file diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tmpl/traces.ejs b/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tmpl/traces.ejs index b8c6354..470527d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tmpl/traces.ejs +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tmpl/traces.ejs @@ -16,6 +16,7 @@ + @@ -33,9 +34,10 @@ + <% if (trace.queue) { %> + + + +
Virtual host<%= fmt_string(shovel.vhost) %>
Source <%= fmt_string(shovel.value['src-uri']) %>Name Pattern FormatPayload limit Rate Queued <%= fmt_string(trace.name) %> <%= fmt_string(trace.pattern) %> <%= fmt_string(trace.format) %><%= fmt_string(trace.max_payload_bytes, 'Unlimited') %> - <%= fmt_rate(trace.queue.message_stats, 'ack', false) %> + <%= fmt_detail_rate(trace.queue.message_stats, 'deliver_no_ack') %> <%= trace.queue.messages %> @@ -131,6 +133,12 @@
+ +
diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tracing.js b/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tracing.js index 89852ba..73c8b09 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tracing.js +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/priv/www/js/tracing.js @@ -11,6 +11,13 @@ dispatcher_add(function(sammy) { 'trace', '#/traces'); }); sammy.put('#/traces', function() { + if (this.params['max_payload_bytes'] === '') { + delete this.params['max_payload_bytes']; + } + else { + this.params['max_payload_bytes'] = + parseInt(this.params['max_payload_bytes']); + } if (sync_put(this, '/traces/:vhost/:name')) update(); return false; @@ -29,8 +36,11 @@ dispatcher_add(function(sammy) { NAVIGATION['Admin'][0]['Tracing'] = ['#/traces', 'administrator']; +HELP['tracing-max-payload'] = + 'Maximum size of payload to log, in bytes. Payloads larger than this limit will be truncated. Leave blank to prevent truncation. Set to 0 to prevent logging of payload altogether.'; + function link_trace(name) { - return _link_to(fmt_escape_html(name), 'api/trace-files/' + esc(name)); + return _link_to(name, 'api/trace-files/' + esc(name)); } function link_trace_queue(trace) { diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_app.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_app.erl index bfa249f..815855b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_app.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_app.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_app). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer.erl index 2194226..ca2273b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_consumer). @@ -22,11 +22,14 @@ -import(rabbit_misc, [pget/2, pget/3, table_lookup/2]). --record(state, {conn, ch, vhost, queue, file, filename, format}). --record(log_record, {timestamp, type, exchange, queue, node, routing_keys, +-record(state, {conn, ch, vhost, queue, file, filename, format, buf, buf_cnt, + max_payload}). +-record(log_record, {timestamp, type, exchange, queue, node, connection, + vhost, username, channel, routing_keys, routed_queues, properties, payload}). -define(X, <<"amq.rabbitmq.trace">>). +-define(MAX_BUF, 100). -export([start_link/1, info_all/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -44,6 +47,7 @@ init(Args) -> process_flag(trap_exit, true), Name = pget(name, Args), VHost = pget(vhost, Args), + MaxPayload = pget(max_payload_bytes, Args, unlimited), {ok, Conn} = amqp_connection:start( #amqp_params_direct{virtual_host = VHost}), link(Conn), @@ -56,16 +60,15 @@ init(Args) -> amqp_channel:call( Ch, #'queue.bind'{exchange = ?X, queue = Q, routing_key = pget(pattern, Args)}), - #'basic.qos_ok'{} = - amqp_channel:call(Ch, #'basic.qos'{prefetch_count = 10}), + amqp_channel:enable_delivery_flow_control(Ch), #'basic.consume_ok'{} = amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q, - no_ack = false}, self()), + no_ack = true}, self()), {ok, Dir} = application:get_env(directory), Filename = Dir ++ "/" ++ binary_to_list(Name) ++ ".log", case filelib:ensure_dir(Filename) of ok -> - case file:open(Filename, [append]) of + case prim_file:open(Filename, [append]) of {ok, F} -> rabbit_tracing_traces:announce(VHost, Name, self()), Format = list_to_atom(binary_to_list(pget(format, Args))), @@ -73,7 +76,8 @@ init(Args) -> "format ~p~n", [Filename, Format]), {ok, #state{conn = Conn, ch = Ch, vhost = VHost, queue = Q, file = F, filename = Filename, - format = Format}}; + format = Format, buf = [], buf_cnt = 0, + max_payload = MaxPayload}}; {error, E} -> {stop, {could_not_open, Filename, E}} end; @@ -93,21 +97,25 @@ handle_call(_Req, _From, State) -> handle_cast(_C, State) -> {noreply, State}. -handle_info(Delivery = {#'basic.deliver'{delivery_tag = Seq}, #amqp_msg{}}, - State = #state{ch = Ch, file = F, format = Format}) -> - Print = fun(Fmt, Args) -> io:format(F, Fmt, Args) end, - log(Format, Print, delivery_to_log_record(Delivery)), - amqp_channel:cast(Ch, #'basic.ack'{delivery_tag = Seq}), - {noreply, State}; +handle_info({BasicDeliver, Msg, DeliveryCtx}, + State = #state{format = Format}) -> + amqp_channel:notify_received(DeliveryCtx), + {noreply, log(Format, delivery_to_log_record({BasicDeliver, Msg}, State), + State), + 0}; + +handle_info(timeout, State) -> + {noreply, flush(State)}; handle_info(_I, State) -> {noreply, State}. -terminate(shutdown, #state{conn = Conn, ch = Ch, - file = F, filename = Filename}) -> +terminate(shutdown, State = #state{conn = Conn, ch = Ch, + file = F, filename = Filename}) -> + flush(State), catch amqp_channel:close(Ch), catch amqp_connection:close(Conn), - catch file:close(F), + catch prim_file:close(F), rabbit_log:info("Tracer closed log file ~p~n", [Filename]), ok; @@ -120,49 +128,104 @@ code_change(_, State, _) -> {ok, State}. delivery_to_log_record({#'basic.deliver'{routing_key = Key}, #amqp_msg{props = #'P_basic'{headers = H}, - payload = Payload}}) -> - {Type, Q} = case Key of - <<"publish.", _Rest/binary>> -> {published, none}; - <<"deliver.", Rest/binary>> -> {received, Rest} - end, - {longstr, Node} = table_lookup(H, <<"node">>), - {longstr, X} = table_lookup(H, <<"exchange_name">>), - {array, Keys} = table_lookup(H, <<"routing_keys">>), - {table, Props} = table_lookup(H, <<"properties">>), - #log_record{timestamp = rabbit_mgmt_format:timestamp(os:timestamp()), + payload = Payload}}, State) -> + {Type, Q, RQs} = case Key of + <<"publish.", _Rest/binary>> -> + {array, Qs} = table_lookup(H, <<"routed_queues">>), + {published, none, [Q || {_, Q} <- Qs]}; + <<"deliver.", Rest/binary>> -> + {received, Rest, none} + end, + {longstr, Node} = table_lookup(H, <<"node">>), + {longstr, X} = table_lookup(H, <<"exchange_name">>), + {array, Keys} = table_lookup(H, <<"routing_keys">>), + {table, Props} = table_lookup(H, <<"properties">>), + {longstr, Conn} = table_lookup(H, <<"connection">>), + {longstr, VHost} = table_lookup(H, <<"vhost">>), + {longstr, User} = table_lookup(H, <<"user">>), + {signedint, Chan} = table_lookup(H, <<"channel">>), + #log_record{timestamp = rabbit_mgmt_format:now_to_str_ms(os:timestamp()), type = Type, exchange = X, queue = Q, node = Node, + connection = Conn, + vhost = VHost, + username = User, + channel = Chan, routing_keys = [K || {_, K} <- Keys], + routed_queues= RQs, properties = Props, - payload = Payload}. - -log(text, P, Record) -> - P("~n~s~n", [string:copies("=", 80)]), - P("~s: ", [Record#log_record.timestamp]), - case Record#log_record.type of - published -> P("Message published~n~n", []); - received -> P("Message received~n~n", []) - end, - P("Node: ~s~n", [Record#log_record.node]), - P("Exchange: ~s~n", [Record#log_record.exchange]), - case Record#log_record.queue of - none -> ok; - Q -> P("Queue: ~s~n", [Q]) - end, - P("Routing keys: ~p~n", [Record#log_record.routing_keys]), - P("Properties: ~p~n", [Record#log_record.properties]), - P("Payload: ~n~s~n", [Record#log_record.payload]); - -log(json, P, Record) -> - P("~s~n", [mochijson2:encode( - [{timestamp, Record#log_record.timestamp}, - {type, Record#log_record.type}, - {node, Record#log_record.node}, - {exchange, Record#log_record.exchange}, - {queue, Record#log_record.queue}, - {routing_keys, Record#log_record.routing_keys}, - {properties, rabbit_mgmt_format:amqp_table( + payload = truncate(Payload, State)}. + +log(text, Record, State) -> + Fmt = "~n========================================" + "========================================~n~s: Message ~s~n~n" + "Node: ~s~nConnection: ~s~n" + "Virtual host: ~s~nUser: ~s~n" + "Channel: ~p~nExchange: ~s~n" + "Routing keys: ~p~n" ++ + case Record#log_record.queue of + none -> ""; + _ -> "Queue: ~s~n" + end ++ + case Record#log_record.routed_queues of + none -> ""; + _ -> "Routed queues: ~p~n" + end ++ + "Properties: ~p~nPayload: ~n~s~n", + Args = + [Record#log_record.timestamp, + Record#log_record.type, + Record#log_record.node, Record#log_record.connection, + Record#log_record.vhost, Record#log_record.username, + Record#log_record.channel, Record#log_record.exchange, + Record#log_record.routing_keys] ++ + case Record#log_record.queue of + none -> []; + Q -> [Q] + end ++ + case Record#log_record.routed_queues of + none -> []; + RQs -> [RQs] + end ++ + [Record#log_record.properties, Record#log_record.payload], + print_log(io_lib:format(Fmt, Args), State); + +log(json, Record, State) -> + print_log(mochijson2:encode( + [{timestamp, Record#log_record.timestamp}, + {type, Record#log_record.type}, + {node, Record#log_record.node}, + {connection, Record#log_record.connection}, + {vhost, Record#log_record.vhost}, + {user, Record#log_record.username}, + {channel, Record#log_record.channel}, + {exchange, Record#log_record.exchange}, + {queue, Record#log_record.queue}, + {routed_queues, Record#log_record.routed_queues}, + {routing_keys, Record#log_record.routing_keys}, + {properties, rabbit_mgmt_format:amqp_table( Record#log_record.properties)}, - {payload, base64:encode(Record#log_record.payload)}])]). + {payload, base64:encode(Record#log_record.payload)}]) + ++ "\n", + State). + +print_log(LogMsg, State = #state{buf = Buf, buf_cnt = BufCnt}) -> + maybe_flush(State#state{buf = [LogMsg | Buf], buf_cnt = BufCnt + 1}). + +maybe_flush(State = #state{buf_cnt = ?MAX_BUF}) -> + flush(State); +maybe_flush(State) -> + State. + +flush(State = #state{file = F, buf = Buf}) -> + prim_file:write(F, lists:reverse(Buf)), + State#state{buf = [], buf_cnt = 0}. + +truncate(Payload, #state{max_payload = Max}) -> + case Max =:= unlimited orelse size(Payload) =< Max of + true -> Payload; + false -> <> = Payload, + Trunc + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer_sup.erl index 10289a6..8dbc22f 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_consumer_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ Federation. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_consumer_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_files.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_files.erl index 2005fdf..c5520e7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_files.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_files.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_files). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_mgmt.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_mgmt.erl index 25a4bd8..3d2d44d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_mgmt.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_mgmt.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_mgmt). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_sup.erl index e0f352e..502ef7e 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_sup). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_traces.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_traces.erl index 7fc7c14..53336d7 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_traces.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_traces.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_traces). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_file.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_file.erl index 4d67f73..30a134b 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_file.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_file.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -module(rabbit_tracing_wm_file). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_files.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_files.erl index b19cfba..d3a8004 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_files.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_files.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_wm_files). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_trace.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_trace.erl index d7f8acb..a9cdbac 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_trace.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_trace.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. -module(rabbit_tracing_wm_trace). @@ -23,6 +23,8 @@ -define(ERR, <<"Something went wrong trying to start the trace - check the " "logs.">>). +-import(rabbit_misc, [pget/2, pget/3]). + -include_lib("rabbitmq_management/include/rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -47,20 +49,24 @@ resource_exists(ReqData, Context) -> to_json(ReqData, Context) -> rabbit_mgmt_util:reply(trace(ReqData), ReqData, Context). -accept_content(ReqData, Context) -> - case rabbit_mgmt_util:vhost(ReqData) of - not_found -> not_found; - VHost -> Name = rabbit_mgmt_util:id(name, ReqData), - rabbit_mgmt_util:with_decode( - [format], ReqData, Context, - fun([_], Trace) -> - case rabbit_tracing_traces:create( - VHost, Name, Trace) of - {ok, _} -> {true, ReqData, Context}; - _ -> rabbit_mgmt_util:bad_request( - ?ERR, ReqData, Context) - end - end) +accept_content(RD, Ctx) -> + case rabbit_mgmt_util:vhost(RD) of + not_found -> + not_found; + VHost -> + Name = rabbit_mgmt_util:id(name, RD), + rabbit_mgmt_util:with_decode( + [format, pattern], RD, Ctx, + fun([_, _], Trace) -> + Fs = [fun val_payload_bytes/3, fun val_format/3, + fun val_create/3], + case lists:foldl(fun (F, ok) -> F(VHost, Name, Trace); + (_F, Err) -> Err + end, ok, Fs) of + ok -> {true, RD, Ctx}; + Err -> rabbit_mgmt_util:bad_request(Err, RD, Ctx) + end + end) end. delete_resource(ReqData, Context) -> @@ -80,3 +86,21 @@ trace(ReqData) -> VHost -> rabbit_tracing_traces:lookup( VHost, rabbit_mgmt_util:id(name, ReqData)) end. + +val_payload_bytes(_VHost, _Name, Trace) -> + case is_integer(pget(max_payload_bytes, Trace, 0)) of + false -> <<"max_payload_bytes not integer">>; + true -> ok + end. + +val_format(_VHost, _Name, Trace) -> + case lists:member(pget(format, Trace), [<<"json">>, <<"text">>]) of + false -> <<"format not json or text">>; + true -> ok + end. + +val_create(VHost, Name, Trace) -> + case rabbit_tracing_traces:create(VHost, Name, Trace) of + {ok, _} -> ok; + _ -> ?ERR + end. diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_traces.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_traces.erl index 9f42355..ef0fe50 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_traces.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/src/rabbit_tracing_wm_traces.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_wm_traces). diff --git a/rabbitmq-server/plugins-src/rabbitmq-tracing/test/src/rabbit_tracing_test.erl b/rabbitmq-server/plugins-src/rabbitmq-tracing/test/src/rabbit_tracing_test.erl index 1c0ed0a..df184eb 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-tracing/test/src/rabbit_tracing_test.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-tracing/test/src/rabbit_tracing_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. %% -module(rabbit_tracing_test). @@ -69,6 +69,21 @@ tracing_test() -> http_delete("/trace-files/test.log", ?NO_CONTENT), ok. +tracing_validation_test() -> + Path = "/traces/%2f/test", + http_put(Path, [{pattern, <<"#">>}], ?BAD_REQUEST), + http_put(Path, [{format, <<"json">>}], ?BAD_REQUEST), + http_put(Path, [{format, <<"ebcdic">>}, + {pattern, <<"#">>}], ?BAD_REQUEST), + http_put(Path, [{format, <<"text">>}, + {pattern, <<"#">>}, + {max_payload_bytes, <<"abc">>}], ?BAD_REQUEST), + http_put(Path, [{format, <<"json">>}, + {pattern, <<"#">>}, + {max_payload_bytes, 1000}], ?NO_CONTENT), + http_delete(Path, ?NO_CONTENT), + ok. + %%--------------------------------------------------------------------------- %% Below is copypasta from rabbit_mgmt_test_http, it's not obvious how %% to share that given the build system. diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_registry.erl b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_registry.erl index 93eb4d4..e15530a 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_registry.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_registry.erl @@ -112,7 +112,7 @@ handle_call(list_all, _From, undefined) -> {reply, list(), undefined}; handle_call(Req, _From, State) -> - error_logger:format("Unexpected call to ~p: ~p~n", [?MODULE, Req]), + rabbit_log:error("Unexpected call to ~p: ~p~n", [?MODULE, Req]), {stop, unknown_request, State}. handle_cast(_, State) -> diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_sup.erl b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_sup.erl index 83be7b1..5582d47 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_sup.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_web_dispatch_sup.erl @@ -64,8 +64,8 @@ init([]) -> mochi_options(Listener) -> [{name, name(Listener)}, {loop, loopfun(Listener)} | - easy_ssl(proplists:delete( - name, proplists:delete(ignore_in_use, Listener)))]. + ssl_config(proplists:delete( + name, proplists:delete(ignore_in_use, Listener)))]. loopfun(Listener) -> fun (Req) -> @@ -83,19 +83,31 @@ name(Listener) -> Port = proplists:get_value(port, Listener), list_to_atom(atom_to_list(?MODULE) ++ "_" ++ integer_to_list(Port)). -easy_ssl(Options) -> - case {proplists:get_value(ssl, Options), - proplists:get_value(ssl_opts, Options)} of - {true, undefined} -> - {ok, ServerOpts} = application:get_env(rabbit, ssl_options), - SSLOpts = [{K, V} || - {K, V} <- ServerOpts, - not lists:member(K, [verify, fail_if_no_peer_cert])], - [{ssl_opts, SSLOpts}|Options]; - _ -> - Options +ssl_config(Options) -> + case proplists:get_value(ssl, Options) of + true -> rabbit_networking:ensure_ssl(), + case rabbit_networking:poodle_check('HTTP') of + ok -> case proplists:get_value(ssl_opts, Options) of + undefined -> auto_ssl(Options); + _ -> fix_ssl(Options) + end; + danger -> proplists:delete(ssl, Options) + end; + _ -> Options end. +auto_ssl(Options) -> + {ok, ServerOpts} = application:get_env(rabbit, ssl_options), + Remove = [verify, fail_if_no_peer_cert], + SSLOpts = [{K, V} || {K, V} <- ServerOpts, + not lists:member(K, Remove)], + fix_ssl([{ssl_opts, SSLOpts} | Options]). + +fix_ssl(Options) -> + SSLOpts = proplists:get_value(ssl_opts, Options), + rabbit_misc:pset(ssl_opts, + rabbit_networking:fix_ssl_options(SSLOpts), Options). + check_error(Listener, Error) -> Ignore = proplists:get_value(ignore_in_use, Listener, false), case {Error, Ignore} of diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine.erl b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine.erl index b62e7ad..a199a31 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine.erl +++ b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine.erl @@ -22,7 +22,8 @@ -export([makeloop/1, setup/0]). setup() -> - application:set_env(webmachine, error_handler, webmachine_error_handler). + application:set_env( + webmachine, error_handler, rabbit_webmachine_error_handler). makeloop(Dispatch) -> fun (MochiReq) -> @@ -33,11 +34,11 @@ makeloop(Dispatch) -> %% however, we don't need to dispatch by the host name. case webmachine_dispatcher:dispatch(Path, Dispatch, ReqData) of {no_dispatch_match, _Host, _PathElements} -> - {ErrorHTML, ReqState1} = - webmachine_error_handler:render_error( + {ErrorBody, ReqState1} = + rabbit_webmachine_error_handler:render_error( 404, Req, {none, none, []}), Req1 = {webmachine_request, ReqState1}, - {ok, ReqState2} = Req1:append_to_response_body(ErrorHTML), + {ok, ReqState2} = Req1:append_to_response_body(ErrorBody), Req2 = {webmachine_request, ReqState2}, {ok, ReqState3} = Req2:send_response(404), maybe_log_access(ReqState3); diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine_error_handler.erl b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine_error_handler.erl new file mode 100644 index 0000000..849e5b9 --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-web-dispatch/src/rabbit_webmachine_error_handler.erl @@ -0,0 +1,57 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. +%% + +%% We need to ensure all responses are application/json; anything +%% coming back as text/html could constitute an XSS vector. Also I'm +%% sure it's easier on our clients if they can always expect JSON +%% responses. +%% +%% Based on webmachine_error_handler, but I'm not sure enough remains +%% to be copyrightable. + +-module(rabbit_webmachine_error_handler). + +-export([render_error/3]). + +render_error(Code, Req, Reason) -> + case Req:has_response_body() of + {true, _} -> maybe_log(Req, Reason), + Req:response_body(); + {false, _} -> render_error_body(Code, Req:trim_state(), Reason) + end. + +render_error_body(404, Req, Reason) -> error_body(404, Req, "Not Found"); +render_error_body(Code, Req, Reason) -> error_body(Code, Req, Reason). + +error_body(Code, Req, Reason) -> + {ok, ReqState} = Req:add_response_header("Content-Type","application/json"), + case Code of + 500 -> maybe_log(Req, Reason); + _ -> ok + end, + Json = {struct, + [{error, list_to_binary(httpd_util:reason_phrase(Code))}, + {reason, list_to_binary(rabbit_misc:format("~p~n", [Reason]))}]}, + {mochijson2:encode(Json), ReqState}. + +maybe_log(_Req, {error, {exit, normal, _Stack}}) -> + %% webmachine_request did an exit(normal), so suppress this + %% message. This usually happens when a chunked upload is + %% interrupted by network failure. + ok; +maybe_log(Req, Reason) -> + {Path, _} = Req:path(), + error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]). diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/CONTRIBUTING.md b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/CONTRIBUTING.md new file mode 100644 index 0000000..69a4b4a --- /dev/null +++ b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Overview + +RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. +Pull requests is the primary place of discussing code changes. + +## How to Contribute + +The process is fairly standard: + + * Fork the repository or repositories you plan on contributing to + * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) + * `cd umbrella`, `make co` + * Create a branch with a descriptive name in the relevant repositories + * Make your changes, run tests, commit with a [descriptive message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork + * Submit pull requests with an explanation what has been changed and **why** + * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) + * Be patient. We will get to your pull request eventually + +If what you are going to work on is a substantial change, please first ask the core team +of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). + + +## (Brief) Code of Conduct + +In one line: don't be a dick. + +Be respectful to the maintainers and other contributors. Open source +contributors put long hours into developing projects and doing user +support. Those projects and user support are available for free. We +believe this deserves some respect. + +Be respectful to people of all races, genders, religious beliefs and +political views. Regardless of how brilliant a pull request is +technically, we will not tolerate disrespectful or aggressive +behaviour. + +Contributors who violate this straightforward Code of Conduct will see +their pull requests closed and locked. + + +## Contributor Agreement + +If you want to contribute a non-trivial change, please submit a signed copy of our +[Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time +you submit your pull request. This will make it much easier (in some cases, possible) +for the RabbitMQ team at Pivotal to merge your contribution. + + +## Where to Ask Questions + +If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/README.md b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/README.md index d9a4b6e..0008d57 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/README.md +++ b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/README.md @@ -1,4 +1,3 @@ - RabbitMQ-Web-Stomp-Examples plugin ================================== diff --git a/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/priv/bunny.html b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/priv/bunny.html index bb0d34c..15f686d 100644 --- a/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/priv/bunny.html +++ b/rabbitmq-server/plugins-src/rabbitmq-web-stomp-examples/priv/bunny.html @@ -1,7 +1,7 @@ - +