diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c364398 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +node_modules/ +.DS_Store diff --git a/COMMERCIAL-LICENSE b/COMMERCIAL-LICENSE new file mode 100644 index 0000000..a981607 --- /dev/null +++ b/COMMERCIAL-LICENSE @@ -0,0 +1,93 @@ +END-USER LICENSE AGREEMENT + +------------------------------------------------------------------------------ + +IMPORTANT: THIS SOFTWARE END-USER LICENSE AGREEMENT ("EULA") IS A LEGAL AGREEMENT (“Agreement”) BETWEEN YOU (THE CUSTOMER, EITHER AS AN INDIVIDUAL OR, IF PURCHASED OR OTHERWISE ACQUIRED BY OR FOR AN ENTITY, AS AN ENTITY) AND SHAKACODE. READ IT CAREFULLY BEFORE COMPLETING THE INSTALLATION PROCESS AND USING SSR-RS-PRO AND RELATED SOFTWARE COMPONENTS (“SOFTWARE”). IT PROVIDES A LICENSE TO USE THE SOFTWARE AND CONTAINS WARRANTY INFORMATION AND LIABILITY DISCLAIMERS. BY INSTALLING AND USING THE SOFTWARE, YOU ARE CONFIRMING YOUR ACCEPTANCE OF THE SOFTWARE AND AGREEING TO BECOME BOUND BY THE TERMS OF THIS AGREEMENT. + +------------------------------------------------------------------------------ + +In order to use the Software under this Agreement, you must receive a “Source URL” at the time of purchase, in accordance with the scope of use and other terms specified for each type of Software and as set forth in this Section 1 of this Agreement. + +1. License Grant + +1.1 General Use. This Agreement grants you a non-exclusive, non-transferable, limited license to the use rights for the Software, without the right to grant sublicenses, subject to the terms and conditions in this Agreement. The Software is licensed, not sold. + +1.2 Unlimited Organization License. If you purchased an Organization License, you may install the Software on an unlimited number of Hosts. “Host” means any physical or virtual machine which is controlled by you. You may also run an unlimited number of Workers. “Worker” means a Node.js process within a Ssr server process which executes jobs. You may concurrently run the software on an unlimited number of Hosts, with each host running an unlimited number of Workers. + +1.3 Appliance License. If you purchased an Appliance License, you may distribute the Software in any applications, frameworks, or elements (collectively referred to as an “Application” or “Applications”) that you develop using the Software in accordance with this EULA, provided that such distribution does not violate the restrictions set forth in section 3 of this EULA. You must not remove, obscure or interfere with any copyright, acknowledgment, attribution, trademark, warning or disclaimer statement affixed to, incorporated in or otherwise applied in connection with the Software. You are required to ensure that the Software is not reused by or with any applications other than those with which you distribute it as permitted herein. For example, if You install the Software on a customer's server, that customer is not permitted to use the Software independently of your Application. You must inform ShakaCode of your knowledge of any infringing use of the Software by any of your customers. You are liable for compliance by those third parties with the terms and conditions of this EULA. You will not owe ShakaCode any royalties for your distribution of the Software in accordance with this EULA. + +1.4 Archive Copies. You are entitled to make a reasonable amount of copies of the Software for archival purposes. Each copy must reproduce all copyright and other proprietary rights notices on or in the Software Product. + +1.5 Electronic Delivery. All Software and license documentation shall be delivered by electronic means unless otherwise specified on the applicable invoice or at the time of purchase. Software shall be deemed delivered when it is made available for download by you (“Delivery”). + +2. Modifications. ShakaCode shall provide you with source code so that you can create Modifications of the original software. “Modification” means: (a) any addition to or deletion from the contents of a file included in the original Software or previous Modifications created by You, or (b) any new file that contains any part of the original Software or previous Modifications. While you retain all rights to any original work authored by you as part of the Modifications, We continue to own all copyright and other intellectual property rights in the Software. + +3. Restricted Uses. + +3.1 You shall not (and shall not allow any third party to): (a) decompile, disassemble, or otherwise reverse engineer the Software or attempt to reconstruct or discover any source code, underlying ideas, algorithms, file formats or programming interfaces of the Software by any means whatsoever (except and only to the extent that applicable law prohibits or restricts reverse engineering restrictions); (b) distribute, sell, sublicense, rent, lease or use the Software for time sharing, hosting, service provider or like purposes, except as expressly permitted under this Agreement; (c) redistribute the Software or Modifications other than by including the Software or a portion thereof within your own product, which must have substantially different functionality than the Software or Modifications and must not allow any third party to use the Software or Modifications, or any portions thereof, for software development or application development purposes; (d) redistribute the Software as part of a product, "appliance" or "virtual server"; (e) redistribute the Software on any server which is not directly under your control; (f) remove any product identification, proprietary, copyright or other notices contained in the Software; (g) modify any part of the Software, create a derivative work of any part of the Software (except as permitted in Section 4), or incorporate the Software, except to the extent expressly authorized in writing by ShakaCode; (h) publicly disseminate performance information or analysis (including, without limitation, benchmarks) from any source relating to the Software; (i) utilize any equipment, device, software, or other means designed to circumvent or remove any form of Source URL or copy protection used by ShakaCode in connection with the Software, or use the Software together with any authorization code, Source URL, serial number, or other copy protection device not supplied by ShakaCode; (j) use the Software to develop a product which is competitive with any ShakaCode product offerings; or (k) use unauthorized Source URLS or keycode(s) or distribute or publish Source URLs or keycode(s), except as may be expressly permitted by ShakaCode in writing. If your unique Source URL is ever published, ShakaCode reserves the right to terminate your access without notice. + +3.2 UNDER NO CIRCUMSTANCES MAY YOU USE THE SOFTWARE AS PART OF A PRODUCT OR SERVICE THAT PROVIDES SIMILAR FUNCTIONALITY TO THE SOFTWARE ITSELF. + +The Open Source version of the Software (“LGPL Version”) is licensed +under the terms of the GNU Lesser General Public License version 3.0 +(“LGPL”) and not under this EULA. + +4. Ownership. Notwithstanding anything to the contrary contained herein, except for the limited license rights expressly provided herein, ShakaCode and its suppliers have and will retain all rights, title and interest (including, without limitation, all patent, copyright, trademark, trade secret and other intellectual property rights) in and to the Software and all copies, modifications and derivative works thereof (including any changes which incorporate any of your ideas, feedback or suggestions). You acknowledge that you are obtaining only a limited license right to the Software, and that irrespective of any use of the words “purchase”, “sale” or like terms hereunder no ownership rights are being conveyed to you under this Agreement or otherwise. + +5. Fees and Payment. The Software license fees will be due and payable in full as set forth in the applicable invoice or at the time of purchase. If the Software does not function properly within two weeks of purchase, please contact us within those two weeks for a refund. You shall be responsible for all taxes, withholdings, duties and levies arising from the order (excluding taxes based on the net income of ShakaCode). + +6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, support and maintenance services may be included with the purchase of your license subscription. + +7. Term of Agreement. + +7.1 Term. This Agreement is effective as of the Delivery of the Software and expires at such time as all license and service subscriptions hereunder have expired in accordance with their own terms (the “Term”). For clarification, the term of your license under this Agreement may be perpetual, limited for Evaluation Version, or designated as a fixed-term license in the Invoice, and shall be specified at your time of purchase. Either party may terminate this Agreement (including all related Invoices) if the other party: (a) fails to cure any material breach of this Agreement within thirty (30) days after written notice of such breach, provided that ShakaCode may terminate this Agreement immediately upon any breach of Section 3 or if you exceed any other restrictions contained in Section 1, unless otherwise specified in this agreement; (b) ceases operation without a successor; or (c) seeks protection under any bankruptcy, receivership, trust deed, creditors arrangement, composition or comparable proceeding, or if any such proceeding is instituted against such party (and not dismissed within sixty (60) days)). Termination is not an exclusive remedy and the exercise by either party of any remedy under this Agreement will be without prejudice to any other remedies it may have under this Agreement, by law, or otherwise. + +7.2 Termination. Upon any termination of this Agreement, you shall cease any and all use of any Software and destroy all copies thereof. + +7.3 Expiration of License. Upon the expiration of any term under this Agreement, (a) all Software updates and services pursuant to the license shall cease, (b) you may only continue to run existing installations of the Software, (c) you may not install the Software on any additional Hosts, and (d) any new installation of the Software shall require the purchase of a new license subscription from ShakaCode. + +8. Disclaimer of Warranties. The Software is provided "as is," with all faults, defects and errors, and without warranty of any kind. ShakaCode does not warrant that the Software will be free of bugs, errors, viruses or other defects, and ShakaCode shall have no liability of any kind for the use of or inability to use the Software, the Software content or any associated service, and you acknowledge that it is not technically practicable for ShakaCode to do so. +To the maximum extent permitted by applicable law, ShakaCode disclaims all warranties, express, implied, arising by law or otherwise, regarding the Software, the Software content and their respective performance or suitability for your intended use, including without limitation any implied warranty of merchantability, fitness for a particular purpose. + +9. Limitation of Liability. + +In no event will ShakaCode be liable for any direct, indirect, consequential, incidental, special, exemplary, or punitive damages or liabilities whatsoever arising from or relating to the Software, the Software content or this Agreement, whether based on contract, tort (including negligence), strict liability or other theory, even if ShakaCode has been advised of the possibility of such damages. + +In no event will ShakaCode liability exceed the Software license price as indicated in the invoice. The existence of more than one claim will not enlarge or extend this limit. + +10. Remedies. Your exclusive remedy and ShakaCode entire liability for breach of this Agreement shall be limited, at ShakaCode sole and exclusive discretion, to (a) replacement of any defective software or documentation; or (b) refund of the license fee paid to ShakaCode, payable in accordance with ShakaCode refund policy. + +11. Acknowledgements. + +11.1 Consent to the Use of Data. You agree that ShakaCode and its affiliates may collect and use technical information gathered as part of the product support services. ShakaCode may use this information solely to improve products and services and will not disclose this information in a form that personally identifies you. + +11.2 Verification. We or a certified auditor acting on our behalf, may, upon its reasonable request and at its expense, audit you with respect to the use of the Software. Such audit may be conducted by mail, electronic means or through an in-person visit to your place of business. Any such in-person audit shall be conducted during regular business hours at your facilities and shall not unreasonably interfere with your business activities. We shall not remove, copy, or redistribute any electronic material during the course of an audit. If an audit reveals that you are using the Software in a way that is in material violation of the terms of the EULA, then you shall pay our reasonable costs of conducting the audit. In the case of a material violation, you agree to pay Us any amounts owing that are attributable to the unauthorized use. In the alternative, We reserve the right, at our sole option, to terminate the licenses for the Software. + +11.3 Government End Users. If the Software and related documentation are supplied to or purchased by or on behalf of the United States Government, then the Software is deemed to be "commercial software" as that term is used in the Federal Acquisition Regulation system. Rights of the United States shall not exceed the minimum rights set forth in FAR 52.227-19 for "restricted computer software". All other terms and conditions of this Agreement apply. + +12. Third Party Software. Examples included in Software may provide links to third party libraries or code (collectively “Third Party Software”) to implement various functions. Third Party Software does not comprise part of the Software. In some cases, access to Third Party Software may be included along with the Software delivery as a convenience for demonstration purposes. Such source code and libraries may be included in the “…/examples” source tree delivered with the Software and do not comprise the Software. Licensee acknowledges (1) that some part of Third Party Software may require additional licensing of copyright and patents from the owners of such, and (2) that distribution of any of the Software referencing or including any portion of a Third Party Software may require appropriate licensing from such third parties. + + +13. Miscellaneous + +13.1 Entire Agreement. This Agreement sets forth our entire agreement with respect to the Software and the subject matter hereof and supersedes all prior and contemporaneous understandings and agreements whether written or oral. + +13.2 Amendment. ShakaCode reserves the right, in its sole discretion, to amend this Agreement from time. + +13.3 Assignment. You may not assign this Agreement or any of its rights under this Agreement without the prior written consent of ShakaCode and any attempted assignment without such consent shall be void. + +13.4 Export Compliance. You agree to comply with all applicable laws and regulations, including laws, regulations, orders or other restrictions on export, re-export or redistribution of software. + +13.5 Indemnification. You agree to defend, indemnify, and hold harmless ShakaCode from and against any lawsuits, claims, losses, damages, fines and expenses (including attorneys' fees and costs) arising out of your use of the Software or breach of this Agreement. + +13.6 Governing Law. This Agreement is governed by the laws of the State of Hawaii and the United States without regard to conflicts of laws provisions thereof, and without regard to the United Nations Convention on the International Sale of Goods or the Uniform Computer Information Transactions Act, as currently enacted by any jurisdiction or as may be codified or amended from time to time by any jurisdiction. The jurisdiction and venue for actions related to the subject matter hereof shall be the state of Hawaii and United States federal courts, and both parties hereby submit to the personal jurisdiction of such courts. + +13.7 Attorneys' Fees and Costs. The prevailing party in any action to enforce this Agreement will be entitled to recover its attorneys' fees and costs in connection with such action. + +13.8 Severability. If any provision of this Agreement is held by a court of competent jurisdiction to be invalid, illegal, or unenforceable, the remainder of this Agreement will remain in full force and effect. + +13.9 Waiver. Failure or neglect by either party to enforce at any time any of the provisions of this license Agreement shall not be construed or deemed to be a waiver of that party's rights under this Agreement. + +13.10 Headings. The headings of sections and paragraphs of this Agreement are for convenience of reference only and are not intended to restrict, affect or be of any weight in the interpretation or construction of the provisions of such sections or paragraphs. + +14. Contact Information. If you have any questions about this EULA, or if you want to contact ShakaCode for any reason, please direct correspondence to contact@shakacode.com. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..175390e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2215 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "actix-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project 0.4.27", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-connect" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures-util", + "http", + "log", + "trust-dns-proto", + "trust-dns-resolver", +] + +[[package]] +name = "actix-http" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "452299e87817ae5673910e53c243484ca38be3828db819b6011736fc6982e874" +dependencies = [ + "actix-codec", + "actix-connect", + "actix-rt", + "actix-service", + "actix-threadpool", + "actix-utils", + "base64", + "bitflags", + "brotli2", + "bytes", + "cookie 0.14.3", + "copyless", + "derive_more", + "either", + "encoding_rs", + "flate2", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "h2", + "http", + "httparse", + "indexmap", + "itoa", + "language-tags", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project 1.0.2", + "rand", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "sha-1", + "slab", + "time", +] + +[[package]] +name = "actix-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd1f7dbda1645bf7da33554db60891755f6c01c1b2169e2f4c492098d30c235" +dependencies = [ + "bytestring", + "http", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" +dependencies = [ + "actix-macros", + "actix-threadpool", + "copyless", + "futures-channel", + "futures-util", + "smallvec", + "tokio", +] + +[[package]] +name = "actix-server" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "futures-channel", + "futures-util", + "log", + "mio", + "mio-uds", + "num_cpus", + "slab", + "socket2", +] + +[[package]] +name = "actix-service" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" +dependencies = [ + "futures-util", + "pin-project 0.4.27", +] + +[[package]] +name = "actix-testing" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" +dependencies = [ + "actix-macros", + "actix-rt", + "actix-server", + "actix-service", + "log", + "socket2", +] + +[[package]] +name = "actix-threadpool" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" +dependencies = [ + "derive_more", + "futures-channel", + "lazy_static", + "log", + "num_cpus", + "parking_lot", + "threadpool", +] + +[[package]] +name = "actix-tls" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb" +dependencies = [ + "actix-codec", + "actix-service", + "actix-utils", + "futures-util", +] + +[[package]] +name = "actix-utils" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "bitflags", + "bytes", + "either", + "futures-channel", + "futures-sink", + "futures-util", + "log", + "pin-project 0.4.27", + "slab", +] + +[[package]] +name = "actix-web" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-testing", + "actix-threadpool", + "actix-tls", + "actix-utils", + "actix-web-codegen", + "awc", + "bytes", + "derive_more", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "log", + "mime", + "pin-project 1.0.2", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "socket2", + "time", + "tinyvec", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix-web-hello-world" +version = "0.0.0" +dependencies = [ + "actix-web", + "env_logger", + "log", + "ssr", +] + +[[package]] +name = "addr2line" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281" +dependencies = [ + "autocfg", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "awc" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "base64", + "bytes", + "cfg-if 1.0.0", + "derive_more", + "futures-core", + "log", + "mime", + "percent-encoding", + "rand", + "serde", + "serde_json", + "serde_urlencoded", +] + +[[package]] +name = "backtrace" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "brotli2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +dependencies = [ + "brotli-sys", + "libc", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytestring" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_fn" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" + +[[package]] +name = "cookie" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie" +version = "0.15.0-dev" +source = "git+https://github.com/SergioBenitez/cookie-rs.git?rev=1c3ca83#1c3ca838543b60a4448d279dc4b903cc7a2bc22a" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "copyless" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "devise" +version = "0.3.0" +source = "git+https://github.com/SergioBenitez/Devise.git?rev=3648468#3648468a9ede9ca896cd35bc1eb818c7a9fb3047" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.3.0" +source = "git+https://github.com/SergioBenitez/Devise.git?rev=3648468#3648468a9ede9ca896cd35bc1eb818c7a9fb3047" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.3.0" +source = "git+https://github.com/SergioBenitez/Devise.git?rev=3648468#3648468a9ede9ca896cd35bc1eb818c7a9fb3047" +dependencies = [ + "bitflags", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "figment" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f273b2f81f067bb5553f562feb98794d6c704f049d333f920d69211965a30448" +dependencies = [ + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "flate2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" + +[[package]] +name = "futures-executor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" + +[[package]] +name = "futures-macro" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" + +[[package]] +name = "futures-task" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project 1.0.2", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + +[[package]] +name = "http" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "humantime" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" + +[[package]] +name = "hyper" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.2", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inlinable_string" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77" + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipconfig" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +dependencies = [ + "socket2", + "widestring", + "winapi 0.3.9", + "winreg", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" + +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.6", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "pear" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f612cbd0f9dd03f5dd28a191c48e4148c3b027e41207b32eee130373c6c941" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602cf1780ee9bbca663ea75769e05643e16fe87d7c8ac9f4f385a2ed8940a75c" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +dependencies = [ + "pin-project-internal 1.0.2", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "ref-cast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17626b2f4bcf35b84bf379072a66e28cfe5c3c6ae58b38e4914bb8891dabece" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c523ccaed8ac4b0288948849a350b37d3035827413c458b6a40ddb614bb4f72" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rocket" +version = "0.5.0-dev" +source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +dependencies = [ + "async-trait", + "atomic", + "atty", + "binascii", + "either", + "figment", + "futures", + "log", + "memchr", + "num_cpus", + "parking_lot", + "rand", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "state", + "time", + "tokio", + "ubyte", + "version_check", + "yansi", +] + +[[package]] +name = "rocket-hello-world" +version = "0.0.0" +dependencies = [ + "env_logger", + "http", + "log", + "rocket", + "ssr", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.0-dev" +source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +dependencies = [ + "devise", + "glob", + "indexmap", + "quote", + "rocket_http", +] + +[[package]] +name = "rocket_http" +version = "0.5.0-dev" +source = "git+https://github.com/SergioBenitez/Rocket#9671115796e42865eaebd020ac27542802027b02" +dependencies = [ + "cookie 0.15.0-dev", + "either", + "http", + "hyper", + "indexmap", + "log", + "mime", + "parking_lot", + "pear", + "percent-encoding", + "ref-cast", + "smallvec", + "state", + "time", + "tokio", + "uncased", + "unicode-xid", + "version_check", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "ssr" +version = "0.0.1" +dependencies = [ + "http", + "log", + "serde", + "serde_json", + "tokio", + "uuid", +] + +[[package]] +name = "standback" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" +dependencies = [ + "version_check", +] + +[[package]] +name = "state" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483" + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "syn" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi 0.3.9", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite 0.1.11", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.11", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "tracing" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.0", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + +[[package]] +name = "trust-dns-proto" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53861fcb288a166aae4c508ae558ed18b53838db728d4d310aad08270a7d4c2b" +dependencies = [ + "async-trait", + "backtrace", + "enum-as-inner", + "futures", + "idna", + "lazy_static", + "log", + "rand", + "smallvec", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6759e8efc40465547b0dfce9500d733c65f969a4cbbfbe3ccf68daaa46ef179e" +dependencies = [ + "backtrace", + "cfg-if 0.1.10", + "futures", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "ubyte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42756bb9e708855de2f8a98195643dff31a97f0485d90d8467b39dc24be9e8fe" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369fa7fd7969c5373541d3c9a40dc1b76ce676fc87aba30d87c0ad3b97fad179" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +dependencies = [ + "rand", + "serde", +] + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" + +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..af24c01 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "ssr", + "examples/actix-web-hello-world", + "examples/rocket-hello-world" +] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..70b6aa1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2020 Alex Fedoseev and ShakaCode, http://www.shakacode.com + +SSR-RS is an Open Source project licensed under the terms of +the LGPLv3 license. Please see +for license text. + +SSR-RS has a commercial-friendly license allowing private forks +and modifications of SSR-RS. You can find the commercial license terms +in COMMERCIAL-LICENSE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a886893 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# ssr-rs + +[![crates.io](https://meritbadge.herokuapp.com/ssr)](https://crates.io/crates/ssr) +[![API docs](https://docs.rs/ssr/badge.svg)](https://docs.rs/ssr) +[![LGPL-3.0 licensed](https://img.shields.io/badge/license-LGPL-blue.svg)](./LICENSE) + +Server-Side Rendering of JS web apps for Rust web servers using Node.js. + +## Documentation +See [docs.rs/ssr](https://docs.rs/ssr). + +## Pro version +We offer a Pro version of this crate which includes: +- scalable workers pool +- auto-restartable workers +- logging system, integrated with Rust's [`log`](https://crates.io/crates/log) crate + +Please, contact us via [contact@shakacode.com](mailto:contact@shakacode.com) for more details. + +## License +`ssr` is an Open Source project licensed under the terms of the LGPLv3 license. Please see for license text. + +`ssr` has a commercial-friendly license allowing private forks and modifications of `ssr`. You can find the commercial license terms in [COMMERCIAL-LICENSE](./COMMERCIAL-LICENSE). diff --git a/examples/actix-web-hello-world/Cargo.toml b/examples/actix-web-hello-world/Cargo.toml new file mode 100644 index 0000000..6db9c5d --- /dev/null +++ b/examples/actix-web-hello-world/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "actix-web-hello-world" +version = "0.0.0" +edition = "2018" +authors = ["Alex Fedoseev "] +workspace = "../../" +publish = false + +[dependencies] +actix-web = "3.2.0" +ssr = { path = "../../ssr" } +log = "0.4.11" +env_logger = "0.8.1" diff --git a/examples/actix-web-hello-world/src/main.rs b/examples/actix-web-hello-world/src/main.rs new file mode 100644 index 0000000..6c9f72b --- /dev/null +++ b/examples/actix-web-hello-world/src/main.rs @@ -0,0 +1,42 @@ +#[macro_use] +extern crate log; + +use std::path::PathBuf; + +use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer}; +use ssr::{JsRenderer, Ssr, SsrConfig}; + +#[actix_web::main] +pub async fn main() -> std::io::Result<()> { + env_logger::init(); + + let ssr = Ssr::new(SsrConfig { + port: 9000, + js_worker: PathBuf::from("./ssr/js/worker.js"), + global_js_renderer: Some(PathBuf::from( + "./examples/actix-web-hello-world/src/renderer.js", + )), + }) + .await + .unwrap(); + + HttpServer::new(move || { + App::new() + .data(ssr.clone()) + .route("/", web::get().to(hello_world)) + }) + .bind("127.0.0.1:3000")? + .run() + .await +} + +pub async fn hello_world(ssr: Data, req: HttpRequest) -> HttpResponse { + let uri = req.uri(); + match ssr.render(uri, &"Hello, world!", JsRenderer::Global).await { + Ok(html) => HttpResponse::Ok().body(html), + Err(error) => { + error!("Error: {}", error); + HttpResponse::InternalServerError().finish() + } + } +} diff --git a/examples/actix-web-hello-world/src/renderer.js b/examples/actix-web-hello-world/src/renderer.js new file mode 100644 index 0000000..b8847d3 --- /dev/null +++ b/examples/actix-web-hello-world/src/renderer.js @@ -0,0 +1,12 @@ +module.exports.render = ({url, jsonData, hydrationData}) => ` + + + + + SSR + + +
${jsonData}
+ + +`; diff --git a/examples/rocket-hello-world/Cargo.toml b/examples/rocket-hello-world/Cargo.toml new file mode 100644 index 0000000..4c206a8 --- /dev/null +++ b/examples/rocket-hello-world/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rocket-hello-world" +version = "0.0.0" +edition = "2018" +authors = ["Alex Fedoseev "] +workspace = "../../" +publish = false + +[dependencies] +rocket = { git = "https://github.com/SergioBenitez/Rocket", version = "0.5.0-dev" } +http = "0.2.2" +ssr = { path = "../../ssr" } +log = "0.4.11" +env_logger = "0.8.1" diff --git a/examples/rocket-hello-world/src/main.rs b/examples/rocket-hello-world/src/main.rs new file mode 100644 index 0000000..7c4e7f1 --- /dev/null +++ b/examples/rocket-hello-world/src/main.rs @@ -0,0 +1,56 @@ +#[macro_use] +extern crate rocket; +#[macro_use] +extern crate log; + +use std::path::PathBuf; + +use rocket::{ + http::uri::Origin, + response::{content::Html, status}, + Rocket, State, +}; +use ssr::{JsRenderer, Ssr, SsrConfig}; + +#[launch] +async fn rocket() -> Rocket { + env_logger::init(); + + let ssr = Ssr::new(SsrConfig { + port: 9000, + js_worker: PathBuf::from("./ssr/js/worker.js"), + global_js_renderer: Some(PathBuf::from( + "./examples/rocket-hello-world/src/renderer.js", + )), + }) + .await + .unwrap(); + + rocket::ignite() + .mount("/", routes![hello_world]) + .manage(ssr) +} + +#[get("/")] +async fn hello_world( + ssr: State<'_, Ssr>, + origin: &Origin<'_>, +) -> Result, status::Custom<()>> { + // TODO: Prolly, we should expect different uri type depending on specific feature + let uri = match origin.query() { + Some(query) => format!("{}?{}", origin.path(), query) + .parse::() + .unwrap(), + None => origin.path().parse::().unwrap(), + }; + match ssr.render(&uri, &"Hello, world!", JsRenderer::Global).await { + Ok(html) => Ok(Html(html)), + Err(error) => { + error!("Error: {}", error); + Err(status::Custom( + rocket::http::Status::InternalServerError, + (), + )) + } + } +} diff --git a/examples/rocket-hello-world/src/renderer.js b/examples/rocket-hello-world/src/renderer.js new file mode 100644 index 0000000..b8847d3 --- /dev/null +++ b/examples/rocket-hello-world/src/renderer.js @@ -0,0 +1,12 @@ +module.exports.render = ({url, jsonData, hydrationData}) => ` + + + + + SSR + + +
${jsonData}
+ + +`; diff --git a/ssr/Cargo.toml b/ssr/Cargo.toml new file mode 100644 index 0000000..4d30b19 --- /dev/null +++ b/ssr/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ssr" +description = "Server-Side Rendering for Rust web servers using Node.js" +version = "0.0.1" +authors = ["Alex Fedoseev "] +license = "LGPL-3.0" +edition = "2018" +keywords = ["nodejs", "ssr", "react", "vue", "angular"] + +[dependencies] +tokio = { version = "0.2", features = ["process", "net", "sync", "time", "io-util"] } +http = "0.2.2" +uuid = { version = "0.8.1", features = ["v4", "serde"] } +serde = "1.0.117" +serde_json = "1.0.59" +log = "0.4.11" diff --git a/ssr/js/package.json b/ssr/js/package.json new file mode 100644 index 0000000..d83fec7 --- /dev/null +++ b/ssr/js/package.json @@ -0,0 +1,17 @@ +{ + "name": "ssr-rs", + "description": "Node.js worker for ssr Rust crate", + "version": "0.0.1", + "author": "Alex Fedoseev ", + "license": "LGPL-3.0", + "main": "./worker.js", + "keywords": [ + "rust", + "nodejs", + "ssr", + "react", + "vue", + "angular", + "svelte" + ] +} diff --git a/ssr/js/worker.js b/ssr/js/worker.js new file mode 100644 index 0000000..1761ef1 --- /dev/null +++ b/ssr/js/worker.js @@ -0,0 +1,130 @@ +const net = require("net"); + +const WORKER_ID = process.pid; +const ENCODING = "utf8"; +const MESSAGE_LENGTH_BUFFER_SIZE = 4; // 32-bit + +const env = { + port: process.env["PORT"], + globalRenderer: process.env["GLOBAL_RENDERER"], +}; + +const log = (msg, reqId) => { + const worker = + !!reqId + ? `[JS] Worker [id: ${WORKER_ID} port: ${env.port} request: ${reqId}]` + : `[JS] Worker [id: ${WORKER_ID} port: ${env.port}]`; + const message = msg.charAt(msg.length - 1) === "\n" ? msg : `${msg}\n`; + process.stderr.write(`${worker}: ${message}`); +}; + +log(`Node.js version: ${process.version}`); + +process.on("uncaughtException", (err, origin) => { + log(`Uncaught Exception: ${err}`); + process.exit(1); +}); + +const server = net.createServer(); + +const port = (() => { + if (env.port === undefined) { + log("Port is not provided"); + return process.exit(1); + } + const port = parseInt(env.port, 10); + if (!Number.isInteger(port)) { + log(`Port is invalid: ${env.port}`); + return process.exit(1); + } + return port; +})(); + +const globalRenderer = env.globalRenderer ? require(env.globalRenderer) : null; + +server.on("connection", connection => { + log("New connection"); + + let metaLength = null; + let dataLength = null; + let bytesRead = 0; + let contents = null; + + connection.on("data", bytes => { + log(`New data chunk`); + + try { + let chunk = Buffer.from(bytes, ENCODING); + + if (metaLength === null && dataLength === null) { + log(`Received initial chunk: ${chunk}`); + const metaLengthStartIdx = 0; + const dataLengthStartIdx = MESSAGE_LENGTH_BUFFER_SIZE; + metaLength = chunk.readUIntBE(metaLengthStartIdx, MESSAGE_LENGTH_BUFFER_SIZE); + dataLength = chunk.readUIntBE(dataLengthStartIdx, MESSAGE_LENGTH_BUFFER_SIZE); + contents = chunk.slice(dataLengthStartIdx + MESSAGE_LENGTH_BUFFER_SIZE); + bytesRead = contents.length; + } else { + log(`Received subsequent chunk: ${chunk}`); + const totalLength = contents.length + chunk.length; + contents = Buffer.concat([contents, chunk], totalLength); + bytesRead = totalLength; + } + + log(`Meta length: ${metaLength}`); + log(`Data length: ${dataLength}`); + log(`Contents: ${contents}`); + log(`Bytes read: ${bytesRead}`); + + if (metaLength + dataLength > bytesRead) { + log("Waiting for the next chunk"); + } else { + log("Finished reading data"); + // We can safely parse meta b/c this is what we get from Rust + const meta = JSON.parse(contents.slice(0, metaLength).toString(ENCODING)); + + log(`Parsed meta: ${JSON.stringify(meta)}`, meta.requestId); + + // However, applying JSON.parse on data is not safe since it might contain + // malicious contents, which has been escaped by the renderer + // and it would undo the escaping + const hydrationData = contents.slice(metaLength, metaLength + dataLength).toString(ENCODING); + + log(`Hydration data: ${hydrationData}`, meta.requestId); + + const jsonData = JSON.parse(JSON.parse(hydrationData)); + + log(`JSON data: ${JSON.stringify(jsonData)}`, meta.requestId); + + const renderer = meta.requestRenderer ? require(meta.requestRenderer) : globalRenderer; + + if (!renderer) { + throw new Error(`Renderer is not provided for request ${meta.requestId}`); + } else if (!renderer.render) { + throw new Error(`Renderer.render function is not defined for request ${meta.requestId}`); + } + + const output = renderer.render({url: meta.url, jsonData, hydrationData}); + + log(`Rendered output: ${output}`, meta.requestId); + + connection.end(Buffer.from(output, "utf8")); + } + } catch (err) { + log(err.stack); + connection.end(Buffer.from(`ERROR:${err.stack}`, "utf8")); + } + }); + + connection.once("close", () => { + log("Connection closed"); + }); + + connection.on("error", err => { + log(`Connection error: ${err.message}`); + }); +}); + +server.listen(port, () => { + log(`Ready on port ${server.address().port}`); +}); diff --git a/ssr/src/error.rs b/ssr/src/error.rs new file mode 100644 index 0000000..bfd0ea3 --- /dev/null +++ b/ssr/src/error.rs @@ -0,0 +1,74 @@ +use std::{fmt, io, net::AddrParseError}; + +#[derive(Debug)] +pub enum InitializationError { + InvalidAddr(AddrParseError), + InvalidJsWorkerPath(io::Error), + InvalidGlobalJsRendererPath(io::Error), + SpawnNodeProcessError(io::Error), +} + +impl From for InitializationError { + fn from(err: AddrParseError) -> Self { + Self::InvalidAddr(err) + } +} + +impl From for InitializationError { + fn from(err: io::Error) -> Self { + Self::SpawnNodeProcessError(err) + } +} + +impl fmt::Display for InitializationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidAddr(err) => write!(f, "Invalid worker address: {}", err), + Self::InvalidJsWorkerPath(err) => write!( + f, + "Invalid js worker path: {}. Make sure file at path exists and path is valid.", + err + ), + Self::InvalidGlobalJsRendererPath(err) => write!( + f, + "Invalid global js renderer path: {}. Make sure file at path exists and path is valid.", + err + ), + Self::SpawnNodeProcessError(err) => { + write!(f, "Failed to spawn worker process: {}", err) + } + } + } +} + +#[derive(Debug)] +pub enum RenderingError { + WorkerIsUnavailable, + ConnectionError(io::Error), + InvalidUri, + GlobalRendererNotProvided, + UrlSerializationError(serde_json::Error), + DataSerializationError(serde_json::Error), + RenderRequestError(io::Error), + RenderResponseError(io::Error), + JsExceptionDuringRendering(String), +} + +impl fmt::Display for RenderingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::WorkerIsUnavailable => write!(f, "Worker is unavailable"), + Self::ConnectionError(err) => write!(f, "Connection error: {}", err), + Self::InvalidUri => write!(f, "Invalid URI"), + Self::GlobalRendererNotProvided => write!(f, "Rendering request supposed to use global js renderer but it wasn't provided on renderer initialization"), + Self::UrlSerializationError(err) => write!(f, "Failed to serialize URL: {}", err), + Self::DataSerializationError(err) => write!(f, "Failed to serialize data: {}", err), + Self::RenderRequestError(err) | Self::RenderResponseError(err) => { + write!(f, "Failed to communicate with rendering process: {}", err) + } + Self::JsExceptionDuringRendering(err) => { + write!(f, "JS Exception during rendering: {}", err) + } + } + } +} diff --git a/ssr/src/json.rs b/ssr/src/json.rs new file mode 100644 index 0000000..0973d37 --- /dev/null +++ b/ssr/src/json.rs @@ -0,0 +1,56 @@ +// This is slightly modified version of formatter taken from +// https://github.com/xd009642/tarpaulin/blob/fd7059131cf7f838a68e681a9ddeefb26a8adf7c/src/report/safe_json.rs +// All copyrights belong to the author of this implementation. + +use std::{default::Default, io}; + +use serde_json::{ + ser::{CharEscape, CompactFormatter, Formatter}, + Serializer, +}; + +struct JsonFormatter(CompactFormatter); + +impl Default for JsonFormatter { + fn default() -> Self { + JsonFormatter(CompactFormatter) + } +} + +impl Formatter for JsonFormatter { + fn write_string_fragment(&mut self, writer: &mut W, fragment: &str) -> io::Result<()> + where + W: io::Write, + { + let mut start = 0; + let mut code_length = 0; + for char in fragment.chars() { + code_length += char.len_utf8(); + let escape = match char { + '<' | '>' | '&' => CharEscape::AsciiControl(char as u8), + _ => continue, + }; + if start < code_length - 1 { + self.0 + .write_string_fragment(writer, &fragment[start..code_length - 1])?; + } + + self.write_char_escape(writer, escape)?; + + start = code_length; + } + + if start < code_length { + self.0.write_string_fragment(writer, &fragment[start..])?; + } + + Ok(()) + } +} + +pub fn to_vec(value: &T) -> Result, serde_json::Error> { + let mut writer = Vec::new(); + let mut ser = Serializer::with_formatter(&mut writer, JsonFormatter::default()); + value.serialize(&mut ser)?; + Ok(writer) +} diff --git a/ssr/src/lib.rs b/ssr/src/lib.rs new file mode 100644 index 0000000..f9401c3 --- /dev/null +++ b/ssr/src/lib.rs @@ -0,0 +1,86 @@ +//! ## Installation +//! Add to your `Cargo.toml`: +//! +//! ```toml +//! ssr = "0.0.1" +//! ``` +//! +//! And install node worker from `npm`: +//! +//! ```sh +//! // using npm +//! npm install --save ssr-rs +//! +//! // or yarn +//! yarn add ssr-rs +//! ``` +//! +//! ## How it works +//! On application start, you create an `Ssr` instance. Under the hood, it spins up a Node.js +//! worker ready to accept rendering requests. `Ssr` instance should be stored in a web server's +//! state, so handlers can access it during a handling of incoming requests. +//! +//! `Ssr` instance exposes a single method `render`, which accepts `Uri` and serializable data as +//! an input. If everything went smooth, it returns a rendered `String`. This string can be a plain +//! HTML or an app-specific encoded object with additional metadata—whatever returned from a JS +//! renderer, supplied by the app. +//! +//! ## Initialization +//! +//! ```rust +//! let ssr = +//! Ssr::new( +//! SsrConfig { +//! port: 9000, +//! js_worker: PathBuf::from("./node_modules/ssr-rs/worker.js"), +//! global_js_renderer: Some(PathBuf::from("./js/ssr.js")), +//! } +//! ); +//! ``` +//! +//! ### `port` +//! A port that Node.js worker will be listening on. +//! +//! ### `js_worker` +//! Path to Node.js worker installed from `npm`. It should be relative to the +//! [`std::env::current_dir`](std::env::current_dir). +//! +//! ### `global_js_renderer` +//! If your web app is a SPA (Single Page Application), then you should have a single entry point +//! for all rendering requests. If it's the case, provide a path to this file here and it will be +//! used by the worker to render all responses. Another option is to provide a JS renderer per +//! request but keep in mind that it would introduce additional runtime overhead since JS module +//! has to be required during a request as opposed to requiring it once on application startup. +//! +//! ## Rendering +//! In request handlers, you need to get [`Ssr`](Ssr) instance from your server's state. Once you +//! have it (as well as all the required data to handle the current request), call +//! [`ssr.render`](Ssr::render) function with the following input: +//! - [`Uri`](http::Uri): uri of the current request +//! - `Data: impl Serialize`: anything that implements [`Serialize`](serde::Serialize) +//! - [`JsRenderer`](JsRenderer): an enum that tells to use either a global JS renderer or a +//! renderer specific to this request. +//! +//! ```rust +//! let uri = req.uri(); +//! let data = db::get_data(); +//! match ssr.render(uri, &data, JsRenderer::Global).await { +//! Ok(html) => HttpResponse::Ok().body(html), +//! Err(error) => { +//! error!("Error: {}", error); +//! HttpResponse::InternalServerError().finish() +//! } +//! } +//! ``` + +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_json; + +mod error; +mod json; +mod ssr; +mod worker; + +pub use ssr::{JsRenderer, Ssr, SsrConfig}; diff --git a/ssr/src/ssr.rs b/ssr/src/ssr.rs new file mode 100644 index 0000000..c9ff4de --- /dev/null +++ b/ssr/src/ssr.rs @@ -0,0 +1,230 @@ +use std::{fs, net::Shutdown, path::PathBuf, sync::Arc}; + +use http::Uri; +use serde::Serialize; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; +use uuid::Uuid; + +use crate::{ + error::{InitializationError, RenderingError}, + worker::{Port, Worker}, +}; + +pub enum JsRenderer { + /// Global JS renderer that was passed to [`Ssr::new`](Ssr::new) during initialization via + /// [`SsrConfig`](SsrConfig::global_js_renderer). + Global, + /// JS renderer specific to the current request. + PerRequest { path: PathBuf }, +} + +pub struct SsrConfig { + /// A port that Node.js worker will be listening on. + pub port: u16, + /// Path to Node.js worker installed from `npm`. It should be relative to the + /// [`std::env::current_dir`](std::env::current_dir). + pub js_worker: PathBuf, + /// If your web app is a SPA (Single Page Application), then you should have a single entry + /// point for all rendering requests. If it's the case, provide a path to this file here and it + /// will be used by the worker to render all responses. Another option is to provide a JS + /// renderer per request but keep in mind that it would introduce additional runtime overhead + /// since JS module has to be required during a request as opposed to requiring it once on + /// application startup. + pub global_js_renderer: Option, +} + +#[derive(Clone)] +pub struct Ssr { + worker: Arc, + js_worker: PathBuf, + global_js_renderer: Option, +} + +impl Ssr { + /// Creates an [`Ssr`](Ssr) instance. + /// + /// # Example + /// + /// ```rust + /// let ssr = + /// Ssr::new( + /// SsrConfig { + /// port: 9000, + /// js_worker: PathBuf::from("./node_modules/ssr-rs/worker.js"), + /// global_js_renderer: Some(PathBuf::from("./js/ssr.js")), + /// } + /// ); + /// ``` + pub async fn new(cfg: SsrConfig) -> Result { + let port = Port::new(cfg.port); + let js_worker = match fs::canonicalize(cfg.js_worker) { + Ok(path) => path, + Err(err) => return Err(InitializationError::InvalidJsWorkerPath(err)), + }; + let global_js_renderer = match cfg.global_js_renderer { + Some(path) => match fs::canonicalize(path) { + Ok(path) => Some(path), + Err(err) => return Err(InitializationError::InvalidGlobalJsRendererPath(err)), + }, + None => None, + }; + let worker = Worker::new(&port, &js_worker, &global_js_renderer).await?; + Ok(Self { + worker: Arc::new(worker), + js_worker, + global_js_renderer, + }) + } + + /// Renders a response to an incoming request using Node.js worker. + /// + /// # Example + /// + /// ```rust + /// let uri = req.uri(); + /// let data = db::get_data(); + /// match ssr.render(uri, &data, JsRenderer::Global).await { + /// Ok(html) => HttpResponse::Ok().body(html), + /// Err(error) => { + /// error!("Error: {}", error); + /// HttpResponse::InternalServerError().finish() + /// } + /// } + pub async fn render( + &self, + uri: &Uri, + data: &D, + js_renderer: JsRenderer, + ) -> Result { + let request_id = Uuid::new_v4(); + + trace!("Starting request {}", request_id); + + let worker = &self.worker; + + let mut stream = match worker.connect().await { + Ok(stream) => stream, + Err(err) => { + error!( + "{worker}: Failed to connect: {err}", + worker = worker.display_with_request_id(&request_id), + err = err + ); + return Err(RenderingError::ConnectionError(err)); + } + }; + + let url = match uri.path_and_query() { + Some(url) => url, + None => { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::InvalidUri); + } + }; + + let request_renderer = match (&self.global_js_renderer, js_renderer) { + (Some(_), JsRenderer::Global) => None, + (_, JsRenderer::PerRequest { path }) => Some(path), + (None, JsRenderer::Global) => { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::GlobalRendererNotProvided); + } + }; + + let meta = json!({ + "requestId": request_id, + "requestRenderer": request_renderer, + "url": json!({"path": url.path(), "query": url.query()}), + }); + let meta_bytes = match serde_json::to_vec(&meta) { + Ok(bytes) => bytes, + Err(err) => { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::UrlSerializationError(err)); + } + }; + let data = match serde_json::to_string(&data) { + Ok(data) => data, + Err(err) => { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::DataSerializationError(err)); + } + }; + let data_bytes = match crate::json::to_vec(&data) { + Ok(bytes) => bytes, + Err(err) => { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::DataSerializationError(err)); + } + }; + let meta_len = meta_bytes.len() as u32; + let data_len = data_bytes.len() as u32; + let meta_len_bytes = meta_len.to_be_bytes(); + let data_len_bytes = data_len.to_be_bytes(); + let mut input = meta_len_bytes.to_vec(); + input.extend_from_slice(&data_len_bytes); + input.extend(meta_bytes); + input.extend(data_bytes); + + let mut res = String::new(); + + trace!( + "{worker}: Writing input to socket", + worker = worker.display_with_request_id(&request_id), + ); + + if let Err(err) = stream.write_all(input.as_slice()).await { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::RenderRequestError(err)); + }; + + trace!( + "{worker}: Input written to socket", + worker = worker.display_with_request_id(&request_id), + ); + + if let Err(err) = stream.read_to_string(&mut res).await { + Self::finalize_rendering_session(&worker, &stream, &request_id); + return Err(RenderingError::RenderResponseError(err)); + }; + + trace!( + "{worker}: Output written to result buffer", + worker = worker.display_with_request_id(&request_id), + ); + + // No need to shutdown connection as it's already closed by the js worker + if res.starts_with("ERROR:") { + trace!( + "{worker}: Output is an error", + worker = worker.display_with_request_id(&request_id), + ); + match res.splitn(2, ':').collect::>().as_slice() { + ["ERROR", stack] => Err(RenderingError::JsExceptionDuringRendering( + stack.to_string(), + )), + _ => unreachable!(), + } + } else { + trace!( + "{worker}: Output is ok", + worker = worker.display_with_request_id(&request_id), + ); + Self::finalize_rendering_session(&worker, &stream, &request_id); + Ok(res) + } + } + + fn finalize_rendering_session(worker: &Worker, connection: &TcpStream, request_id: &Uuid) { + if let Err(err) = connection.shutdown(Shutdown::Both) { + warn!( + "{worker}: Failed to shutdown connection to the js worker: {err}", + worker = worker.display_with_request_id(&request_id), + err = err + ); + }; + } +} diff --git a/ssr/src/worker.rs b/ssr/src/worker.rs new file mode 100644 index 0000000..4e6b4f1 --- /dev/null +++ b/ssr/src/worker.rs @@ -0,0 +1,173 @@ +use std::{fmt, io, net::SocketAddr, path::PathBuf, process::Stdio}; + +use tokio::{ + net::TcpStream, + process::{Child, Command}, + time, +}; +use uuid::Uuid; + +use crate::error::InitializationError; + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub(crate) struct Port(u16); + +impl Port { + pub fn new(port: u16) -> Self { + Self(port) + } + + pub fn to_string(&self) -> String { + self.0.to_string() + } + + pub fn to_socket_addr(&self) -> SocketAddr { + format!("127.0.0.1:{}", self.0) + .parse::() + .expect("Unable to build socket address for js worker") + } +} + +impl From for Port { + fn from(addr: SocketAddr) -> Port { + Port(addr.port()) + } +} + +struct Process; + +impl Process { + #[cfg(unix)] + pub const SHELL: &'static str = "/bin/sh"; + + #[cfg(windows)] + pub const SHELL: &'static str = "cmd"; + + #[cfg(unix)] + pub fn cmd(cmd: &str) -> Vec<&str> { + vec!["-c", &cmd] + } + + #[cfg(windows)] + pub fn cmd(cmd: &str) -> Vec<&str> { + vec!["/c", &cmd] + } + + pub fn spawn( + port: &Port, + js_worker: &PathBuf, + global_js_renderer: &Option, + ) -> Result { + let mut cmd = Command::new(Process::SHELL); + + cmd.args(Process::cmd(&format!( + "node {}", + js_worker.as_path().display() + ))); + cmd.env("PORT", port.to_string()); + + if let Some(global_renderer) = global_js_renderer { + cmd.env( + "GLOBAL_RENDERER", + global_renderer.as_path().display().to_string(), + ); + } + + cmd.stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn() + } +} + +pub(crate) struct Worker { + addr: SocketAddr, + process: Child, +} + +impl Worker { + pub async fn new( + port: &Port, + js_worker: &PathBuf, + global_js_renderer: &Option, + ) -> Result { + let process = Process::spawn(port, js_worker, global_js_renderer)?; + + Ok(Self { + addr: port.to_socket_addr(), + process, + }) + } + + pub fn display(&self) -> String { + format!( + "[RS] Worker [id: {} port: {}]", + self.process.id(), + self.addr.port() + ) + } + + pub fn display_with_request_id(&self, request_id: &Uuid) -> String { + format!( + "[RS] Worker [id: {} port: {} request: {}]", + self.process.id(), + self.addr.port(), + request_id + ) + } + + pub async fn connect(&self) -> Result { + let max_attempts = 5; + let mut attempt = 1; + loop { + match attempt { + 1 => trace!("{worker}: Connecting to the js worker", worker = self), + _ => { + let delay = attempt * 3; + trace!( + "{worker}: Trying to reconnect to the js worker. Attempt: {attempt}. Delay: {delay}ms", + worker = self, + attempt = attempt, + delay = delay + ); + time::delay_for(std::time::Duration::from_millis(delay)).await + } + } + match TcpStream::connect(self.addr).await { + Ok(stream) => { + trace!("{worker}: Connected to the js worker", worker = self); + return Ok(stream); + } + Err(err) => match err.kind() { + io::ErrorKind::ConnectionRefused => { + if attempt == max_attempts { + trace!( + "{worker}: Failed to connect to the js worker. Exiting.", + worker = self + ); + return Err(err); + } + trace!( + "{worker}: Failed to connect to the js worker. Retrying.", + worker = self + ); + attempt += 1; + } + _ => { + trace!( + "{worker}: Failed to connected to the js worker due to unexpected error: {err}", + worker = self, + err = err + ); + return Err(err); + } + }, + }; + } + } +} + +impl fmt::Display for Worker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.display()) + } +}