├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── c_src ├── Makefile ├── build_deps.sh ├── client.cc ├── client.h ├── coder.cc ├── coder.h ├── drv_types.cc ├── drv_types.h ├── globals.cc ├── globals.h ├── handler.cc ├── handler.h ├── libutp-f904d1b.tar.gz ├── listener.cc ├── listener.h ├── locker.h ├── main_handler.cc ├── main_handler.h ├── server.cc ├── server.h ├── socket_handler.cc ├── socket_handler.h ├── utils.cc ├── utils.h ├── utp_handler.cc ├── utp_handler.h ├── utpdrv.cc ├── write_queue.cc └── write_queue.h ├── rebar.config ├── src ├── gen_utp.app.src ├── gen_utp.erl ├── gen_utp_app.erl ├── gen_utp_opts.erl ├── gen_utp_opts.hrl └── gen_utp_sup.erl └── test ├── gen_utp_active_tests.erl ├── gen_utp_client_tests.erl ├── gen_utp_close_tests.erl ├── gen_utp_listen_tests.erl ├── gen_utp_port_tests.erl └── gen_utp_tests_setup.hrl /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit/* 2 | ebin/* 3 | priv/* 4 | c_src/*.o 5 | c_src/*.dep 6 | c_src/libutp 7 | .DS_Store 8 | gen_utp.plt 9 | *~ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PLT := gen_utp.plt 2 | 3 | all: 4 | rebar compile 5 | 6 | test: all 7 | rebar -vv eunit 8 | 9 | clean: 10 | rebar clean 11 | rm -f $(PLT) 12 | 13 | dialyzer: $(PLT) 14 | dialyzer --plt $< -r ebin 15 | 16 | $(PLT): all 17 | dialyzer --build_plt --output_plt $@ -r ebin --apps erts kernel stdlib 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gen_utp: an API and driver for the uTP protocol 2 | 3 | ## Description 4 | 5 | `gen_utp` provides an API and driver for the 6 | [Micro Transport Protocol](http://en.wikipedia.org/wiki/Micro_Transport_Protocol) 7 | (uTP), similar to `gen_tcp` and `gen_udp`. It attempts to provide a 8 | TCP-like API with `listen`, `accept`, and `connect` calls, but due to the 9 | nature of the underlying 10 | [libutp library](https://github.com/bittorrent/libutp) the semantics are 11 | not identical. 12 | 13 | `gen_utp` provides the following support: 14 | 15 | * server `listen` and `accept` 16 | * client `connect` 17 | * both `list` and `binary` modes for incoming messages 18 | * `active` settings of `true`, `false`, and `once` 19 | * controlling processes 20 | * `setopts` and `getopts` calls 21 | * IPv4 and IPv6 22 | * server `accept` can be async, or blocking with optional timeout 23 | * `recv` with optional timeout 24 | 25 | Currently, the server `listen` call differs from TCP in that it doesn't 26 | store a backlog; if async accept is not enabled and there are no waiting 27 | acceptors, the `listen` socket just drops incoming connection 28 | attempts. Hopefully this shortcoming will be fixed in the future. 29 | 30 | Look at the tests under `test/gen_utp_tests.erl` for usage examples. More 31 | documentation to follow, and more tests are needed as well. 32 | -------------------------------------------------------------------------------- /c_src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Since rebar doesn't manage dependencies for C++/C code, manage them here. 3 | # The .dep files depend on the source files each .o file depends on. If a 4 | # source file changes, the .cc -> .dep rule fires. The rule deletes the 5 | # corresponding .o file and creates or touches the .dep file. Since "make all" 6 | # here executes in rebar's prebuild script, by the time rebar runs any 7 | # out-of-date .o files will have been deleted and it will rebuild them. 8 | # 9 | TGTS := client.dep coder.dep drv_types.dep globals.dep handler.dep \ 10 | listener.dep main_handler.dep server.dep socket_handler.dep \ 11 | utils.dep utp_handler.dep utpdrv.dep write_queue.dep 12 | 13 | all: $(TGTS) 14 | 15 | clean: 16 | @rm -f $(TGTS) 17 | 18 | .SUFFIXES: .dep 19 | 20 | .cc.dep: 21 | @rm -f ${@:.dep=.o} 22 | @touch $@ 23 | 24 | client.dep: client.cc client.h utp_handler.h socket_handler.h handler.h \ 25 | libutp/utp.h libutp/utypes.h drv_types.h coder.h utils.h \ 26 | write_queue.h globals.h locker.h 27 | coder.dep: coder.cc coder.h 28 | drv_types.dep: drv_types.cc drv_types.h coder.h 29 | globals.dep: globals.cc globals.h 30 | handler.dep: handler.cc handler.h libutp/utp.h libutp/utypes.h globals.h 31 | listener.dep: listener.cc listener.h socket_handler.h handler.h \ 32 | libutp/utp.h libutp/utypes.h drv_types.h coder.h utils.h \ 33 | globals.h main_handler.h utp_handler.h write_queue.h locker.h server.h 34 | main_handler.dep: main_handler.cc main_handler.h handler.h \ 35 | libutp/utp.h libutp/utypes.h \ 36 | utils.h coder.h utp_handler.h socket_handler.h drv_types.h write_queue.h \ 37 | globals.h locker.h client.h listener.h 38 | server.dep: server.cc server.h utp_handler.h socket_handler.h handler.h \ 39 | libutp/utp.h libutp/utypes.h drv_types.h coder.h \ 40 | utils.h write_queue.h listener.h globals.h locker.h 41 | socket_handler.dep: socket_handler.cc socket_handler.h handler.h \ 42 | libutp/utp.h libutp/utypes.h drv_types.h coder.h globals.h utils.h 43 | utils.dep: utils.cc utils.h coder.h globals.h main_handler.h handler.h \ 44 | libutp/utp.h libutp/utypes.h utp_handler.h socket_handler.h \ 45 | drv_types.h write_queue.h 46 | utp_handler.dep: utp_handler.cc utp_handler.h socket_handler.h handler.h \ 47 | libutp/utp.h libutp/utypes.h drv_types.h coder.h \ 48 | utils.h write_queue.h locker.h globals.h main_handler.h 49 | utpdrv.dep: utpdrv.cc globals.h \ 50 | main_handler.h handler.h libutp/utp.h libutp/utypes.h utils.h coder.h \ 51 | utp_handler.h socket_handler.h drv_types.h write_queue.h 52 | write_queue.dep: write_queue.cc write_queue.h 53 | -------------------------------------------------------------------------------- /c_src/build_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # libutp versions come from https://github.com/bittorrent/libutp. 6 | # To retrieve a new version: 7 | # 8 | # git clone git@github.com:bittorrent/libutp.git 9 | # cd libutp 10 | # VSN=`git log --pretty=format:'%h' | head -1` 11 | # git archive --prefix=./libutp/ --format=tar HEAD | \ 12 | # gzip > libutp-${VSN}.tar.gz 13 | # 14 | # where the shell variable VSN ends up being the commit sha of HEAD of the 15 | # libutp repo, the value of which should also be set in the VSN var below. 16 | # 17 | VSN=f904d1b 18 | 19 | [ `basename $PWD` = c_src ] || cd c_src 20 | 21 | case "$1" in 22 | clean) 23 | make clean 24 | rm -rf libutp 25 | ;; 26 | 27 | *) 28 | if [ ! -f libutp/libutp.a ]; then 29 | [ -d libutp ] || tar -xzf libutp-${VSN}.tar.gz 30 | ( cd libutp && make CXXFLAGS+="$DRV_CFLAGS" ) 31 | fi 32 | make all 33 | ;; 34 | esac 35 | -------------------------------------------------------------------------------- /c_src/client.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // client.cc: uTP client port 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "client.h" 24 | #include "globals.h" 25 | #include "locker.h" 26 | #include "drv_types.h" 27 | 28 | 29 | using namespace UtpDrv; 30 | 31 | UtpDrv::Client::Client(int sock, const SockOpts& so) : 32 | UtpHandler(sock, so) 33 | { 34 | UTPDRV_TRACER << "Client::Client " << this 35 | << ", socket " << sock << UTPDRV_TRACE_ENDL; 36 | } 37 | 38 | UtpDrv::Client::~Client() 39 | { 40 | UTPDRV_TRACER << "Client::~Client " << this << UTPDRV_TRACE_ENDL; 41 | } 42 | 43 | ErlDrvSSizeT 44 | UtpDrv::Client::control(unsigned command, const char* buf, ErlDrvSizeT len, 45 | char** rbuf, ErlDrvSizeT rlen) 46 | { 47 | UTPDRV_TRACER << "Client::control " << this << UTPDRV_TRACE_ENDL; 48 | switch (command) { 49 | case UTP_CONNECT_VALIDATE: 50 | return connect_validate(buf, len, rbuf, rlen); 51 | } 52 | return UtpHandler::control(command, buf, len, rbuf, rlen); 53 | } 54 | 55 | void 56 | UtpDrv::Client::connect_to(const SockAddr& addr) 57 | { 58 | UTPDRV_TRACER << "Client::connect_to " << this << UTPDRV_TRACE_ENDL; 59 | status = connect_pending; 60 | MutexLocker lock(utp_mutex); 61 | utp = UTP_Create(&Client::send_to, this, addr, addr.slen); 62 | set_utp_callbacks(); 63 | UTP_Connect(utp); 64 | } 65 | 66 | void 67 | UtpDrv::Client::do_incoming(UTPSocket* utp) 68 | { 69 | UTPDRV_TRACER << "Client::do_incoming " << this << UTPDRV_TRACE_ENDL; 70 | } 71 | 72 | ErlDrvSSizeT 73 | UtpDrv::Client::connect_validate(const char* buf, ErlDrvSizeT len, 74 | char** rbuf, ErlDrvSizeT rlen) 75 | { 76 | UTPDRV_TRACER << "Client::connect_validate " << this << UTPDRV_TRACE_ENDL; 77 | Binary ref; 78 | try { 79 | int type, size; 80 | EiDecoder decoder(buf, len); 81 | decoder.type(type, size); 82 | if (type != ERL_BINARY_EXT) { 83 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 84 | } 85 | ref.decode(decoder, size); 86 | } catch (const EiError&) { 87 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 88 | } 89 | 90 | EiEncoder encoder; 91 | switch (status) { 92 | case connect_pending: 93 | encoder.atom("wait"); 94 | caller_ref.swap(ref); 95 | break; 96 | 97 | case connect_failed: 98 | encoder.tuple_header(2).atom("error").atom(erl_errno_id(error_code)); 99 | status = not_connected; 100 | break; 101 | 102 | case connected: 103 | encoder.atom("ok"); 104 | break; 105 | 106 | default: 107 | encoder.tuple_header(2).atom("error"); 108 | { 109 | char err[128]; 110 | sprintf(err, "utpdrv illegal connect state: %d", status); 111 | encoder.string(err); 112 | } 113 | break; 114 | } 115 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 116 | return encoder.copy_to_binary(binptr, rlen); 117 | } 118 | -------------------------------------------------------------------------------- /c_src/client.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_CLIENT_H 2 | #define UTPDRV_CLIENT_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // client.h: uTP client port 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include "utp_handler.h" 27 | #include "utils.h" 28 | 29 | 30 | namespace UtpDrv { 31 | 32 | class Client : public UtpHandler 33 | { 34 | public: 35 | Client(int sock, const SockOpts& so); 36 | ~Client(); 37 | 38 | ErlDrvSSizeT 39 | control(unsigned command, const char* buf, ErlDrvSizeT len, 40 | char** rbuf, ErlDrvSizeT rlen); 41 | 42 | void connect_to(const SockAddr& addr); 43 | 44 | private: 45 | ErlDrvSSizeT 46 | connect_validate(const char* buf, ErlDrvSizeT len, 47 | char** rbuf, ErlDrvSizeT rlen); 48 | 49 | void do_incoming(UTPSocket* utp); 50 | 51 | // prevent copies 52 | Client(const Client&); 53 | void operator=(const Client&); 54 | }; 55 | 56 | } 57 | 58 | 59 | // this block comment is for emacs, do not delete 60 | // Local Variables: 61 | // mode: c++ 62 | // c-file-style: "stroustrup" 63 | // c-file-offsets: ((innamespace . 0)) 64 | // End: 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /c_src/coder.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // coder.cc: ei encoder and decoder 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "coder.h" 25 | 26 | 27 | using namespace UtpDrv; 28 | 29 | UtpDrv::EiEncoder::EiEncoder() 30 | { 31 | if (ei_x_new_with_version(this) != 0) { 32 | throw EiError(); 33 | } 34 | } 35 | 36 | UtpDrv::EiEncoder::~EiEncoder() 37 | { 38 | ei_x_free(this); 39 | } 40 | 41 | EiEncoder& 42 | UtpDrv::EiEncoder::tuple_header(int arity) 43 | { 44 | if (ei_x_encode_tuple_header(this, arity) != 0) { 45 | throw EiError(); 46 | } 47 | return *this; 48 | } 49 | 50 | EiEncoder& 51 | UtpDrv::EiEncoder::list_header(int arity) 52 | { 53 | if (ei_x_encode_list_header(this, arity) != 0) { 54 | throw EiError(); 55 | } 56 | return *this; 57 | } 58 | 59 | EiEncoder& 60 | UtpDrv::EiEncoder::empty_list() 61 | { 62 | if (ei_x_encode_empty_list(this) != 0) { 63 | throw EiError(); 64 | } 65 | return *this; 66 | } 67 | 68 | EiEncoder& 69 | UtpDrv::EiEncoder::atom(const char* a) 70 | { 71 | if (ei_x_encode_atom(this, a) != 0) { 72 | throw EiError(); 73 | } 74 | return *this; 75 | } 76 | 77 | EiEncoder& 78 | UtpDrv::EiEncoder::atom(const char* a, int len) 79 | { 80 | if (ei_x_encode_atom_len(this, a, len) != 0) { 81 | throw EiError(); 82 | } 83 | return *this; 84 | } 85 | 86 | EiEncoder& 87 | UtpDrv::EiEncoder::atom(const std::string& a) 88 | { 89 | if (ei_x_encode_atom(this, a.c_str()) != 0) { 90 | throw EiError(); 91 | } 92 | return *this; 93 | } 94 | 95 | EiEncoder& 96 | UtpDrv::EiEncoder::atom(const std::string& a, int len) 97 | { 98 | if (ei_x_encode_atom_len(this, a.c_str(), len) != 0) { 99 | throw EiError(); 100 | } 101 | return *this; 102 | } 103 | 104 | EiEncoder& 105 | UtpDrv::EiEncoder::string(const char* str) 106 | { 107 | if (ei_x_encode_string(this, str) != 0) { 108 | throw EiError(); 109 | } 110 | return *this; 111 | } 112 | 113 | EiEncoder& 114 | UtpDrv::EiEncoder::string(const char* str, int len) 115 | { 116 | if (ei_x_encode_string_len(this, str, len) != 0) { 117 | throw EiError(); 118 | } 119 | return *this; 120 | } 121 | 122 | EiEncoder& 123 | UtpDrv::EiEncoder::string(const std::string& str) 124 | { 125 | if (ei_x_encode_string(this, str.c_str()) != 0) { 126 | throw EiError(); 127 | } 128 | return *this; 129 | } 130 | 131 | EiEncoder& 132 | UtpDrv::EiEncoder::string(const std::string& str, int len) 133 | { 134 | if (ei_x_encode_string_len(this, str.c_str(), len) != 0) { 135 | throw EiError(); 136 | } 137 | return *this; 138 | } 139 | 140 | EiEncoder& 141 | UtpDrv::EiEncoder::longval(long val) 142 | { 143 | if (ei_x_encode_long(this, val) != 0) { 144 | throw EiError(); 145 | } 146 | return *this; 147 | } 148 | 149 | EiEncoder& 150 | UtpDrv::EiEncoder::ulongval(unsigned long val) 151 | { 152 | if (ei_x_encode_ulong(this, val) != 0) { 153 | throw EiError(); 154 | } 155 | return *this; 156 | } 157 | 158 | EiEncoder& 159 | UtpDrv::EiEncoder::longlongval(long long val) 160 | { 161 | if (ei_x_encode_longlong(this, val) != 0) { 162 | throw EiError(); 163 | } 164 | return *this; 165 | } 166 | 167 | EiEncoder& 168 | UtpDrv::EiEncoder::ulonglongval(unsigned long long val) 169 | { 170 | if (ei_x_encode_ulonglong(this, val) != 0) { 171 | throw EiError(); 172 | } 173 | return *this; 174 | } 175 | 176 | EiEncoder& 177 | UtpDrv::EiEncoder::doubleval(double val) 178 | { 179 | if (ei_x_encode_double(this, val) != 0) { 180 | throw EiError(); 181 | } 182 | return *this; 183 | } 184 | 185 | EiEncoder& 186 | UtpDrv::EiEncoder::boolval(bool val) 187 | { 188 | if (ei_x_encode_boolean(this, val) != 0) { 189 | throw EiError(); 190 | } 191 | return *this; 192 | } 193 | 194 | EiEncoder& 195 | UtpDrv::EiEncoder::charval(char val) 196 | { 197 | if (ei_x_encode_char(this, val) != 0) { 198 | throw EiError(); 199 | } 200 | return *this; 201 | } 202 | 203 | EiEncoder& 204 | UtpDrv::EiEncoder::binary(const void* buf, long len) 205 | { 206 | if (ei_x_encode_binary(this, buf, len) != 0) { 207 | throw EiError(); 208 | } 209 | return *this; 210 | } 211 | 212 | EiEncoder& 213 | UtpDrv::EiEncoder::pid(const erlang_pid& p) 214 | { 215 | if (ei_x_encode_pid(this, &p) != 0) { 216 | throw EiError(); 217 | } 218 | return *this; 219 | } 220 | 221 | EiEncoder& 222 | UtpDrv::EiEncoder::fun(const EiFun& f) 223 | { 224 | if (ei_x_encode_fun(this, &f) != 0) { 225 | throw EiError(); 226 | } 227 | return *this; 228 | } 229 | 230 | EiEncoder& 231 | UtpDrv::EiEncoder::port(const erlang_port& p) 232 | { 233 | if (ei_x_encode_port(this, &p) != 0) { 234 | throw EiError(); 235 | } 236 | return *this; 237 | } 238 | 239 | EiEncoder& 240 | UtpDrv::EiEncoder::ref(const erlang_ref& r) 241 | { 242 | if (ei_x_encode_ref(this, &r) != 0) { 243 | throw EiError(); 244 | } 245 | return *this; 246 | } 247 | 248 | EiEncoder& 249 | UtpDrv::EiEncoder::append(const EiEncoder& enc) 250 | { 251 | if (ei_x_append(this, &enc) != 0) { 252 | throw EiError(); 253 | } 254 | return *this; 255 | } 256 | 257 | EiEncoder& 258 | UtpDrv::EiEncoder::append(const char* buf, int len) 259 | { 260 | if (ei_x_append_buf(this, buf, len) != 0) { 261 | throw EiError(); 262 | } 263 | return *this; 264 | } 265 | 266 | const char* 267 | UtpDrv::EiEncoder::buffer(int& len) const 268 | { 269 | len = index+1; 270 | return buff; 271 | } 272 | 273 | ErlDrvSSizeT 274 | UtpDrv::EiEncoder::copy_to_binary(ErlDrvBinary** binp, ErlDrvSizeT rlen) const 275 | { 276 | ErlDrvSSizeT size = index+1; 277 | if (size > ErlDrvSSizeT(rlen)) { 278 | // We do not free *binp here because we assume the pointer-to-binary 279 | // passed in follows the rules of the rbuf argument to the Erlang 280 | // driver control and call entry point functions. If we reallocate 281 | // the binary as below, the Erlang runtime takes care of freeing it. 282 | *binp = driver_alloc_binary(size); 283 | if (*binp == 0) { 284 | throw std::bad_alloc(); 285 | } 286 | memcpy((*binp)->orig_bytes, buff, size); 287 | } else { 288 | char** p = reinterpret_cast(binp); 289 | memcpy(*p, buff, size); 290 | } 291 | return size; 292 | } 293 | 294 | //-------------------------------------------------------------------- 295 | 296 | UtpDrv::EiDecoder::EiDecoder(const char* bf, int ln) : 297 | buf(bf), len(ln), index(0) 298 | { 299 | int vsn; 300 | if (ei_decode_version(buf, &index, &vsn) != 0) { 301 | throw EiError(); 302 | } 303 | } 304 | 305 | UtpDrv::EiDecoder::~EiDecoder() 306 | {} 307 | 308 | EiDecoder& 309 | UtpDrv::EiDecoder::tuple_header(int& arity) 310 | { 311 | if (ei_decode_tuple_header(buf, &index, &arity) != 0) { 312 | throw EiError(); 313 | } 314 | return *this; 315 | } 316 | 317 | EiDecoder& 318 | UtpDrv::EiDecoder::list_header(int& arity) 319 | { 320 | if (ei_decode_list_header(buf, &index, &arity) != 0) { 321 | throw EiError(); 322 | } 323 | return *this; 324 | } 325 | 326 | EiDecoder& 327 | UtpDrv::EiDecoder::atom(char* str) 328 | { 329 | if (ei_decode_atom(buf, &index, str) != 0) { 330 | throw EiError(); 331 | } 332 | return *this; 333 | } 334 | 335 | EiDecoder& 336 | UtpDrv::EiDecoder::atom(std::string& str) 337 | { 338 | char s[MAXATOMLEN]; 339 | if (ei_decode_atom(buf, &index, s) != 0) { 340 | throw EiError(); 341 | } 342 | str.assign(s); 343 | return *this; 344 | } 345 | 346 | EiDecoder& 347 | UtpDrv::EiDecoder::string(char* str) 348 | { 349 | if (ei_decode_string(buf, &index, str) != 0) { 350 | throw EiError(); 351 | } 352 | return *this; 353 | } 354 | 355 | EiDecoder& 356 | UtpDrv::EiDecoder::string(std::string& str) 357 | { 358 | char s[8192]; 359 | if (ei_decode_string(buf, &index, s) != 0) { 360 | throw EiError(); 361 | } 362 | str.assign(s); 363 | return *this; 364 | } 365 | 366 | EiDecoder& 367 | UtpDrv::EiDecoder::longval(long& val) 368 | { 369 | if (ei_decode_long(buf, &index, &val) != 0) { 370 | throw EiError(); 371 | } 372 | return *this; 373 | } 374 | 375 | EiDecoder& 376 | UtpDrv::EiDecoder::ulongval(unsigned long& val) 377 | { 378 | if (ei_decode_ulong(buf, &index, &val) != 0) { 379 | throw EiError(); 380 | } 381 | return *this; 382 | } 383 | 384 | EiDecoder& 385 | UtpDrv::EiDecoder::longlongval(long long& val) 386 | { 387 | if (ei_decode_longlong(buf, &index, &val) != 0) { 388 | throw EiError(); 389 | } 390 | return *this; 391 | } 392 | 393 | EiDecoder& 394 | UtpDrv::EiDecoder::ulonglongval(unsigned long long& val) 395 | { 396 | if (ei_decode_ulonglong(buf, &index, &val) != 0) { 397 | throw EiError(); 398 | } 399 | return *this; 400 | } 401 | 402 | EiDecoder& 403 | UtpDrv::EiDecoder::doubleval(double& val) 404 | { 405 | if (ei_decode_double(buf, &index, &val) != 0) { 406 | throw EiError(); 407 | } 408 | return *this; 409 | } 410 | 411 | EiDecoder& 412 | UtpDrv::EiDecoder::boolval(bool& val) 413 | { 414 | int v; 415 | if (ei_decode_boolean(buf, &index, &v) != 0) { 416 | throw EiError(); 417 | } 418 | val = (v == 1); 419 | return *this; 420 | } 421 | 422 | EiDecoder& 423 | UtpDrv::EiDecoder::charval(char& val) 424 | { 425 | if (ei_decode_char(buf, &index, &val) != 0) { 426 | throw EiError(); 427 | } 428 | return *this; 429 | } 430 | 431 | EiDecoder& 432 | UtpDrv::EiDecoder::binary(char* bin, long& size) 433 | { 434 | if (ei_decode_binary(buf, &index, bin, &size) != 0) { 435 | throw EiError(); 436 | } 437 | return *this; 438 | } 439 | 440 | EiDecoder& 441 | UtpDrv::EiDecoder::pid(erlang_pid& p) 442 | { 443 | if (ei_decode_pid(buf, &index, &p) != 0) { 444 | throw EiError(); 445 | } 446 | return *this; 447 | } 448 | 449 | EiDecoder& 450 | UtpDrv::EiDecoder::fun(EiFun& f) 451 | { 452 | if (ei_decode_fun(buf, &index, &f) != 0) { 453 | throw EiError(); 454 | } 455 | f.allocated = true; 456 | return *this; 457 | } 458 | 459 | EiDecoder& 460 | UtpDrv::EiDecoder::port(erlang_port& p) 461 | { 462 | if (ei_decode_port(buf, &index, &p) != 0) { 463 | throw EiError(); 464 | } 465 | return *this; 466 | } 467 | 468 | EiDecoder& 469 | UtpDrv::EiDecoder::ref(erlang_ref& r) 470 | { 471 | if (ei_decode_ref(buf, &index, &r) != 0) { 472 | throw EiError(); 473 | } 474 | return *this; 475 | } 476 | 477 | EiDecoder& 478 | UtpDrv::EiDecoder::skip() 479 | { 480 | if (ei_skip_term(buf, &index) != 0) { 481 | throw EiError(); 482 | } 483 | return *this; 484 | } 485 | 486 | EiDecoder& 487 | UtpDrv::EiDecoder::type(int& type, int& size) 488 | { 489 | if (ei_get_type(buf, &index, &type, &size) != 0) { 490 | throw EiError(); 491 | } 492 | return *this; 493 | } 494 | -------------------------------------------------------------------------------- /c_src/coder.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_CODER_H 2 | #define UTPDRV_CODER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // coder.h: ei encoder and decoder 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include 28 | #include 29 | #include "erl_driver.h" 30 | #include "ei.h" 31 | 32 | 33 | namespace UtpDrv { 34 | 35 | struct EiError : public std::exception {}; 36 | struct EiBufferTooSmall : public EiError {}; 37 | 38 | class EiFun; 39 | 40 | class EiEncoder : public ei_x_buff 41 | { 42 | public: 43 | EiEncoder(); 44 | ~EiEncoder(); 45 | 46 | EiEncoder& tuple_header(int arity); 47 | EiEncoder& list_header(int arity); 48 | EiEncoder& empty_list(); 49 | EiEncoder& atom(const char* a); 50 | EiEncoder& atom(const char* a, int len); 51 | EiEncoder& atom(const std::string& s); 52 | EiEncoder& atom(const std::string& s, int len); 53 | EiEncoder& string(const char* str); 54 | EiEncoder& string(const char* str, int len); 55 | EiEncoder& string(const std::string& str); 56 | EiEncoder& string(const std::string& str, int len); 57 | EiEncoder& longval(long val); 58 | EiEncoder& ulongval(unsigned long val); 59 | EiEncoder& longlongval(long long val); 60 | EiEncoder& ulonglongval(unsigned long long val); 61 | EiEncoder& doubleval(double val); 62 | EiEncoder& boolval(bool val); 63 | EiEncoder& charval(char val); 64 | EiEncoder& binary(const void* buf, long len); 65 | EiEncoder& pid(const erlang_pid& p); 66 | EiEncoder& fun(const EiFun& f); 67 | EiEncoder& port(const erlang_port& p); 68 | EiEncoder& ref(const erlang_ref& r); 69 | EiEncoder& append(const EiEncoder&); 70 | EiEncoder& append(const char* buf, int len); 71 | 72 | const char* buffer(int& len) const; 73 | 74 | ErlDrvSSizeT 75 | copy_to_binary(ErlDrvBinary** binptr, ErlDrvSizeT rlen) const; 76 | 77 | private: 78 | // prevent copies 79 | EiEncoder(const EiEncoder&); 80 | void operator=(const EiEncoder&); 81 | }; 82 | 83 | 84 | 85 | 86 | 87 | class EiDecoder 88 | { 89 | public: 90 | EiDecoder(const char* buf, int len); 91 | ~EiDecoder(); 92 | 93 | EiDecoder& tuple_header(int& arity); 94 | EiDecoder& list_header(int& arity); 95 | EiDecoder& atom(char* val); 96 | EiDecoder& atom(std::string& val); 97 | EiDecoder& string(char* str); 98 | EiDecoder& string(std::string& str); 99 | EiDecoder& longval(long& val); 100 | EiDecoder& ulongval(unsigned long& val); 101 | EiDecoder& longlongval(long long& val); 102 | EiDecoder& ulonglongval(unsigned long long& val); 103 | EiDecoder& doubleval(double& val); 104 | EiDecoder& boolval(bool& val); 105 | EiDecoder& charval(char& val); 106 | EiDecoder& binary(char* bin, long& size); 107 | EiDecoder& pid(erlang_pid& p); 108 | EiDecoder& fun(EiFun& f); 109 | EiDecoder& port(erlang_port& p); 110 | EiDecoder& ref(erlang_ref& r); 111 | EiDecoder& skip(); 112 | EiDecoder& type(int& type, int& size); 113 | 114 | private: 115 | const char* buf; 116 | const int len; 117 | int index; 118 | 119 | // prevent copies 120 | EiDecoder(const EiDecoder&); 121 | void operator=(const EiDecoder&); 122 | }; 123 | 124 | class EiFun : public erlang_fun 125 | { 126 | public: 127 | EiFun() : allocated(false) {} 128 | ~EiFun() { 129 | if (allocated) { 130 | free_fun(this); 131 | } 132 | } 133 | 134 | friend class EiDecoder; 135 | 136 | private: 137 | bool allocated; 138 | 139 | // prevent copies 140 | EiFun(const EiFun&); 141 | void operator=(const EiFun&); 142 | }; 143 | 144 | } 145 | 146 | 147 | // this block comment is for emacs, do not delete 148 | // Local Variables: 149 | // mode: c++ 150 | // c-file-style: "stroustrup" 151 | // c-file-offsets: ((innamespace . 0)) 152 | // End: 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /c_src/drv_types.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // drv_types.h: wrap Erlang driver types for uTP driver 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "drv_types.h" 24 | 25 | 26 | using namespace UtpDrv; 27 | 28 | UtpDrv::Binary::Binary() : bin(0) 29 | {} 30 | 31 | UtpDrv::Binary::Binary(const Binary& b) 32 | { 33 | if (b.bin != 0) { 34 | bin = b.bin; 35 | driver_binary_inc_refc(bin); 36 | } else { 37 | bin = 0; 38 | } 39 | } 40 | 41 | UtpDrv::Binary::Binary(ErlDrvBinary* b) : bin(b) 42 | { 43 | } 44 | 45 | UtpDrv::Binary::~Binary() 46 | { 47 | reset(); 48 | } 49 | 50 | UtpDrv::Binary& 51 | UtpDrv::Binary::operator=(const Binary& b) 52 | { 53 | if (&b != this) { 54 | reset(); 55 | if (b.bin != 0) { 56 | bin = b.bin; 57 | driver_binary_inc_refc(bin); 58 | } 59 | } 60 | return *this; 61 | } 62 | 63 | void 64 | UtpDrv::Binary::alloc(size_t size) 65 | { 66 | reset(); 67 | bin = driver_alloc_binary(size); 68 | } 69 | 70 | void 71 | UtpDrv::Binary::reset(ErlDrvBinary* b) 72 | { 73 | if (bin != 0) { 74 | driver_free_binary(bin); 75 | } 76 | bin = b; 77 | } 78 | 79 | void 80 | UtpDrv::Binary::swap(Binary& b) 81 | { 82 | ErlDrvBinary* tmp = b.bin; 83 | b.bin = bin; 84 | bin = tmp; 85 | } 86 | 87 | long 88 | UtpDrv::Binary::decode(EiDecoder& decoder, size_t size) 89 | { 90 | long sz; 91 | alloc(size); 92 | decoder.binary(bin->orig_bytes, sz); 93 | return sz; 94 | } 95 | 96 | const char* 97 | UtpDrv::Binary::data() const 98 | { 99 | return bin != 0 ? bin->orig_bytes : 0; 100 | } 101 | 102 | size_t 103 | UtpDrv::Binary::size() const 104 | { 105 | return bin != 0 ? bin->orig_size : 0; 106 | } 107 | 108 | bool 109 | UtpDrv::Binary::operator==(const Binary& b) const 110 | { 111 | if (b.bin == 0) { 112 | return bin == 0; 113 | } 114 | if (bin == 0) { 115 | return false; 116 | } 117 | if (b.bin->orig_size != bin->orig_size) { 118 | return false; 119 | } 120 | return memcmp(b.bin->orig_bytes, bin->orig_bytes, bin->orig_size) == 0; 121 | } 122 | 123 | UtpDrv::Binary::operator ErlDrvTermData() const 124 | { 125 | return reinterpret_cast(bin != 0 ? bin->orig_bytes : 0); 126 | } 127 | 128 | UtpDrv::Binary::operator bool() const 129 | { 130 | return bin != 0; 131 | } 132 | -------------------------------------------------------------------------------- /c_src/drv_types.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_DRV_TYPES_H 2 | #define UTPDRV_DRV_TYPES_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // drv_types.h: wrap Erlang driver types for uTP driver 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "erl_driver.h" 28 | #include "coder.h" 29 | 30 | 31 | namespace UtpDrv { 32 | 33 | class Binary 34 | { 35 | public: 36 | Binary(); 37 | Binary(const Binary&); 38 | explicit Binary(ErlDrvBinary* b); 39 | ~Binary(); 40 | 41 | Binary& operator=(const Binary&); 42 | 43 | void alloc(size_t size); 44 | void reset(ErlDrvBinary* b = 0); 45 | void swap(Binary&); 46 | 47 | long decode(EiDecoder& decoder, size_t size); 48 | 49 | const char* data() const; 50 | size_t size() const; 51 | 52 | bool operator==(const Binary&) const; 53 | 54 | operator ErlDrvTermData() const; 55 | operator bool() const; 56 | 57 | private: 58 | ErlDrvBinary* bin; 59 | }; 60 | 61 | typedef std::basic_string ustring; 62 | 63 | } 64 | 65 | 66 | 67 | // this block comment is for emacs, do not delete 68 | // Local Variables: 69 | // mode: c++ 70 | // c-file-style: "stroustrup" 71 | // c-file-offsets: ((innamespace . 0)) 72 | // End: 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /c_src/globals.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // globals.h: uTP driver global variables 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "globals.h" 24 | 25 | 26 | using namespace UtpDrv; 27 | 28 | // non-const due to Erlang driver function requirements 29 | char* UtpDrv::drv_name = const_cast("utpdrv"); 30 | 31 | ErlDrvMutex* UtpDrv::utp_mutex = 0; 32 | -------------------------------------------------------------------------------- /c_src/globals.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_GLOBALS_H 2 | #define UTPDRV_GLOBALS_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // globals.h: uTP driver global variables 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "erl_driver.h" 28 | 29 | #if ERL_DRV_EXTENDED_MAJOR_VERSION >= 2 && ERL_DRV_EXTENDED_MINOR_VERSION >= 1 30 | #define utp_output_term(P,T,N) erl_drv_output_term(driver_mk_port(P),T,N) 31 | #define utp_send_term(P,R,T,N) erl_drv_send_term(driver_mk_port(P),R,T,N) 32 | #else 33 | #define utp_output_term(P,T,N) driver_output_term(P,T,N) 34 | #define utp_send_term(P,R,T,N) driver_send_term(P,R,T,N) 35 | #endif 36 | 37 | #define UTPDRV_DEBUG 0 38 | #if UTPDRV_DEBUG 39 | #define UTPDRV_TRACER if (false) ; else std::cerr 40 | #else 41 | #define UTPDRV_TRACER if (true) ; else std::cerr 42 | #endif 43 | #define UTPDRV_TRACE_ENDL "\r" << std::endl 44 | 45 | namespace UtpDrv { 46 | 47 | const int INVALID_SOCKET = -1; 48 | 49 | extern char* drv_name; 50 | 51 | extern ErlDrvMutex* utp_mutex; 52 | 53 | } 54 | 55 | 56 | // this block comment is for emacs, do not delete 57 | // Local Variables: 58 | // mode: c++ 59 | // c-file-style: "stroustrup" 60 | // c-file-offsets: ((innamespace . 0)) 61 | // End: 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /c_src/handler.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // handler.cc: abstract base class for driver handlers 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "handler.h" 24 | #include "globals.h" 25 | 26 | 27 | using namespace UtpDrv; 28 | 29 | UtpDrv::Handler::Handler() : port(0) 30 | { 31 | } 32 | 33 | UtpDrv::Handler::Handler(ErlDrvPort p) 34 | { 35 | set_port(p); 36 | } 37 | 38 | UtpDrv::Handler::~Handler() 39 | { 40 | port = 0; 41 | } 42 | 43 | void 44 | UtpDrv::Handler::set_port(ErlDrvPort p) 45 | { 46 | UTPDRV_TRACER << "Handler::set_port " << this << UTPDRV_TRACE_ENDL; 47 | port = p; 48 | if (port != 0) { 49 | set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); 50 | } 51 | } 52 | 53 | void 54 | UtpDrv::Handler::process_exited(const ErlDrvMonitor*, ErlDrvTermData proc) 55 | { 56 | ErlDrvTermData connected = driver_connected(port); 57 | if (proc == connected) { 58 | driver_failure_eof(port); 59 | } 60 | } 61 | 62 | void* 63 | UtpDrv::Handler::operator new(size_t s) 64 | { 65 | return driver_alloc(s); 66 | } 67 | 68 | void 69 | UtpDrv::Handler::operator delete(void* p) 70 | { 71 | driver_free(p); 72 | } 73 | -------------------------------------------------------------------------------- /c_src/handler.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_HANDLER_H 2 | #define UTPDRV_HANDLER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // handler.h: abstract base class for driver handlers 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "erl_driver.h" 28 | #include "libutp/utp.h" 29 | 30 | 31 | namespace UtpDrv { 32 | 33 | // Command values must match those defined in gen_utp.erl 34 | enum Commands { 35 | UTP_LISTEN = 1, 36 | UTP_ACCEPT, 37 | UTP_CANCEL_ACCEPT, 38 | UTP_CONNECT_START, 39 | UTP_CONNECT_VALIDATE, 40 | UTP_CLOSE, 41 | UTP_SOCKNAME, 42 | UTP_PEERNAME, 43 | UTP_SETOPTS, 44 | UTP_GETOPTS, 45 | UTP_CANCEL_SEND, 46 | UTP_RECV, 47 | UTP_CANCEL_RECV 48 | }; 49 | 50 | // Type for delivery of data from a port back to Erlang: binary or list 51 | enum DeliveryMode { 52 | DATA_LIST, 53 | DATA_BINARY 54 | }; 55 | 56 | class Handler 57 | { 58 | public: 59 | virtual ~Handler(); 60 | 61 | virtual ErlDrvSSizeT 62 | control(unsigned command, const char* buf, ErlDrvSizeT len, 63 | char** rbuf, ErlDrvSizeT rlen) = 0; 64 | 65 | virtual void 66 | outputv(ErlIOVec& ev) = 0; 67 | 68 | virtual void stop() = 0; 69 | 70 | virtual void set_port(ErlDrvPort p); 71 | 72 | virtual void 73 | process_exited(const ErlDrvMonitor* mon, ErlDrvTermData proc); 74 | 75 | void* operator new(size_t s); 76 | void operator delete(void* p); 77 | 78 | protected: 79 | Handler(); 80 | explicit Handler(ErlDrvPort p); 81 | 82 | ErlDrvPort port; 83 | }; 84 | 85 | } 86 | 87 | 88 | 89 | // this block comment is for emacs, do not delete 90 | // Local Variables: 91 | // mode: c++ 92 | // c-file-style: "stroustrup" 93 | // c-file-offsets: ((innamespace . 0)) 94 | // End: 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /c_src/libutp-f904d1b.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basho-labs/gen_utp/63dd01722dfdfc87c94c6de5d59029a1c0682f4a/c_src/libutp-f904d1b.tar.gz -------------------------------------------------------------------------------- /c_src/listener.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // listener.cc: uTP listen port 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "libutp/utp.h" 25 | #include "listener.h" 26 | #include "globals.h" 27 | #include "main_handler.h" 28 | #include "utils.h" 29 | #include "locker.h" 30 | #include "server.h" 31 | 32 | 33 | using namespace UtpDrv; 34 | 35 | UtpDrv::Listener::Listener(int sock, const SockOpts& so) : 36 | SocketHandler(sock, so) 37 | { 38 | UTPDRV_TRACER << "Listener::Listener " << this 39 | << ", socket " << sock << UTPDRV_TRACE_ENDL; 40 | if (getsockname(udp_sock, my_addr, &my_addr.slen) < 0) { 41 | throw SocketFailure(errno); 42 | } 43 | queue_mutex = erl_drv_mutex_create(const_cast("queue_mutex")); 44 | } 45 | 46 | UtpDrv::Listener::~Listener() 47 | { 48 | UTPDRV_TRACER << "Listener::~Listener " << this << UTPDRV_TRACE_ENDL; 49 | erl_drv_mutex_destroy(queue_mutex); 50 | } 51 | 52 | ErlDrvSSizeT 53 | UtpDrv::Listener::control(unsigned command, const char* buf, ErlDrvSizeT len, 54 | char** rbuf, ErlDrvSizeT rlen) 55 | { 56 | UTPDRV_TRACER << "Listen::control " << this << UTPDRV_TRACE_ENDL; 57 | switch (command) { 58 | case UTP_ACCEPT: 59 | return accept(buf, len, rbuf, rlen); 60 | case UTP_CANCEL_ACCEPT: 61 | return cancel_accept(buf, len, rbuf, rlen); 62 | } 63 | return SocketHandler::control(command, buf, len, rbuf, rlen); 64 | } 65 | 66 | void 67 | UtpDrv::Listener::outputv(ErlIOVec&) 68 | { 69 | UTPDRV_TRACER << "Listener::outputv " << this << UTPDRV_TRACE_ENDL; 70 | send_not_connected(port); 71 | } 72 | 73 | void 74 | UtpDrv::Listener::stop() 75 | { 76 | UTPDRV_TRACER << "Listener::stop " << this << UTPDRV_TRACE_ENDL; 77 | if (selected) { 78 | MainHandler::stop_input(udp_sock); 79 | selected = false; 80 | } 81 | delete this; 82 | } 83 | 84 | void 85 | UtpDrv::Listener::input_ready() 86 | { 87 | UTPDRV_TRACER << "Listener::input_ready " << this << UTPDRV_TRACE_ENDL; 88 | unsigned char buf[512]; 89 | SockAddr from; 90 | int len = recvfrom(udp_sock, buf, sizeof buf, 0, from, &from.slen); 91 | if (len <= 0) { 92 | return; 93 | } 94 | // if we have nobody accepting connections, just drop the message 95 | MutexLocker qlock(queue_mutex); 96 | size_t qsize = acceptor_queue.size(); 97 | if (qsize == 0) { 98 | return; 99 | } 100 | int sock; 101 | if (open_udp_socket(sock, my_addr, true) < 0) { 102 | return; 103 | } 104 | for (;;) { 105 | int res = connect(sock, from, from.slen); 106 | if (res == 0) { 107 | break; 108 | } else if (res < 0 && errno != EINTR) { 109 | int err = errno; 110 | ::close(sock); 111 | Acceptor& acc = acceptor_queue.front(); 112 | MainHandler::del_monitor(acc.caller); 113 | ErlDrvTermData term[] = { 114 | ERL_DRV_ATOM, driver_mk_atom(const_cast("utp_async")), 115 | ERL_DRV_PORT, driver_mk_port(port), 116 | ERL_DRV_EXT2TERM, acc.ref, acc.ref.size(), 117 | ERL_DRV_ATOM, driver_mk_atom(const_cast("error")), 118 | ERL_DRV_ATOM, driver_mk_atom(erl_errno_id(err)), 119 | ERL_DRV_TUPLE, 2, 120 | ERL_DRV_TUPLE, 4, 121 | }; 122 | utp_send_term(port, acc.caller, term, sizeof term/sizeof *term); 123 | acceptor_queue.pop_front(); 124 | return; 125 | } 126 | } 127 | Server* server = new Server(sock, sockopts); 128 | MainHandler* mh = new MainHandler(server); 129 | bool is_utp; 130 | { 131 | MutexLocker lock(utp_mutex); 132 | is_utp = UTP_IsIncomingUTP(&UtpHandler::utp_incoming, 133 | &UtpHandler::send_to, server, 134 | buf, len, from, from.slen); 135 | } 136 | if (is_utp) { 137 | Acceptor& acc = acceptor_queue.front(); 138 | MainHandler::del_monitor(acc.caller); 139 | ErlDrvData port_drv_data = reinterpret_cast(mh); 140 | ErlDrvPort new_port = driver_create_port(MainHandler::drv_port(), 141 | acc.caller, drv_name, 142 | port_drv_data); 143 | server->set_port(new_port); 144 | ErlDrvTermData term[] = { 145 | ERL_DRV_ATOM, driver_mk_atom(const_cast("utp_async")), 146 | ERL_DRV_PORT, driver_mk_port(port), 147 | ERL_DRV_EXT2TERM, acc.ref, acc.ref.size(), 148 | ERL_DRV_ATOM, driver_mk_atom(const_cast("ok")), 149 | ERL_DRV_PORT, driver_mk_port(new_port), 150 | ERL_DRV_TUPLE, 2, 151 | ERL_DRV_TUPLE, 4, 152 | }; 153 | utp_send_term(port, acc.caller, term, sizeof term/sizeof *term); 154 | acceptor_queue.pop_front(); 155 | } else { 156 | ::close(sock); 157 | delete server; 158 | delete mh; 159 | } 160 | } 161 | 162 | void 163 | UtpDrv::Listener::process_exited(const ErlDrvMonitor* mon, ErlDrvTermData proc) 164 | { 165 | UTPDRV_TRACER << "Listener::process_exited " << this << UTPDRV_TRACE_ENDL; 166 | MutexLocker qlock(queue_mutex); 167 | AcceptorQueue::iterator it = acceptor_queue.begin(); 168 | while (it != acceptor_queue.end()) { 169 | if (it->caller == proc) { 170 | it = acceptor_queue.erase(it); 171 | } else { 172 | ++it; 173 | } 174 | } 175 | } 176 | 177 | void 178 | UtpDrv::Listener::do_write(byte* bytes, size_t count) 179 | { 180 | UTPDRV_TRACER << "Listener::do_write " << this << UTPDRV_TRACE_ENDL; 181 | // do nothing 182 | } 183 | 184 | void 185 | UtpDrv::Listener::do_incoming(UTPSocket* utp) 186 | { 187 | UTPDRV_TRACER << "Listener::do_incoming " << this << UTPDRV_TRACE_ENDL; 188 | } 189 | 190 | ErlDrvSSizeT 191 | UtpDrv::Listener::close(const char* buf, ErlDrvSizeT len, 192 | char** rbuf, ErlDrvSizeT rlen) 193 | { 194 | const char* retval = "ok"; 195 | EiEncoder encoder; 196 | encoder.atom(retval); 197 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 198 | return encoder.copy_to_binary(binptr, rlen); 199 | } 200 | 201 | ErlDrvSSizeT 202 | UtpDrv::Listener::peername(const char* buf, ErlDrvSizeT len, 203 | char** rbuf, ErlDrvSizeT rlen) 204 | { 205 | UTPDRV_TRACER << "Listener::peername " << this << UTPDRV_TRACE_ENDL; 206 | return encode_error(rbuf, rlen, ENOTCONN); 207 | } 208 | 209 | ErlDrvSSizeT 210 | UtpDrv::Listener::accept(const char* buf, ErlDrvSizeT len, 211 | char** rbuf, ErlDrvSizeT rlen) 212 | { 213 | UTPDRV_TRACER << "Listener::accept " << this << UTPDRV_TRACE_ENDL; 214 | Acceptor acc; 215 | try { 216 | EiDecoder decoder(buf, len); 217 | int type, size; 218 | decoder.type(type, size); 219 | if (type != ERL_BINARY_EXT) { 220 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 221 | } 222 | acc.ref.decode(decoder, size); 223 | } catch (const EiError&) { 224 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 225 | } 226 | acc.caller = driver_caller(port); 227 | if (MainHandler::add_monitor(acc.caller, this)) { 228 | { 229 | MutexLocker qlock(queue_mutex); 230 | acceptor_queue.push_back(acc); 231 | } 232 | EiEncoder encoder; 233 | encoder.tuple_header(2).atom("ok"); 234 | encoder.binary(acc.ref.data(), acc.ref.size()); 235 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 236 | return encoder.copy_to_binary(binptr, rlen); 237 | } 238 | return 0; 239 | } 240 | 241 | ErlDrvSSizeT 242 | UtpDrv::Listener::cancel_accept(const char* buf, ErlDrvSizeT len, 243 | char** rbuf, ErlDrvSizeT rlen) 244 | { 245 | UTPDRV_TRACER << "Listener::cancel_accept " << this << UTPDRV_TRACE_ENDL; 246 | Binary ref; 247 | try { 248 | EiDecoder decoder(buf, len); 249 | int type, size; 250 | decoder.type(type, size); 251 | if (type != ERL_BINARY_EXT) { 252 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 253 | } 254 | ref.decode(decoder, size); 255 | } catch (const EiError&) { 256 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 257 | } 258 | 259 | MutexLocker qlock(queue_mutex); 260 | AcceptorQueue::iterator it = acceptor_queue.begin(); 261 | while (it != acceptor_queue.end()) { 262 | if (it->ref == ref) { 263 | MainHandler::del_monitor(it->caller); 264 | acceptor_queue.erase(it); 265 | break; 266 | } 267 | ++it; 268 | } 269 | return 0; 270 | } 271 | -------------------------------------------------------------------------------- /c_src/listener.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_LISTENER_H 2 | #define UTPDRV_LISTENER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // listener.h: uTP listen port 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "socket_handler.h" 28 | #include "utils.h" 29 | #include "libutp/utp.h" 30 | 31 | 32 | namespace UtpDrv { 33 | 34 | class Server; 35 | 36 | class Listener : public SocketHandler 37 | { 38 | public: 39 | Listener(int sock, const SockOpts& so); 40 | ~Listener(); 41 | 42 | ErlDrvSSizeT 43 | control(unsigned command, const char* buf, ErlDrvSizeT len, 44 | char** rbuf, ErlDrvSizeT rlen); 45 | 46 | void outputv(ErlIOVec& ev); 47 | 48 | void stop(); 49 | 50 | void input_ready(); 51 | 52 | void process_exited(const ErlDrvMonitor* mon, ErlDrvTermData proc); 53 | 54 | protected: 55 | ErlDrvSSizeT close(const char* buf, ErlDrvSizeT len, 56 | char** rbuf, ErlDrvSizeT rlen); 57 | 58 | ErlDrvSSizeT peername(const char* buf, ErlDrvSizeT len, 59 | char** rbuf, ErlDrvSizeT rlen); 60 | 61 | ErlDrvSSizeT accept(const char* buf, ErlDrvSizeT len, 62 | char** rbuf, ErlDrvSizeT rlen); 63 | 64 | ErlDrvSSizeT cancel_accept(const char* buf, ErlDrvSizeT len, 65 | char** rbuf, ErlDrvSizeT rlen); 66 | 67 | private: 68 | struct Acceptor { 69 | ErlDrvTermData caller; 70 | Binary ref; 71 | }; 72 | typedef std::list AcceptorQueue; 73 | 74 | AcceptorQueue acceptor_queue; 75 | SockAddr my_addr; 76 | ErlDrvMutex* queue_mutex; 77 | 78 | void do_write(byte* bytes, size_t count); 79 | void do_incoming(UTPSocket* utp); 80 | 81 | // prevent copies 82 | Listener(const Listener&); 83 | void operator=(const Listener&); 84 | }; 85 | 86 | } 87 | 88 | 89 | // this block comment is for emacs, do not delete 90 | // Local Variables: 91 | // mode: c++ 92 | // c-file-style: "stroustrup" 93 | // c-file-offsets: ((innamespace . 0)) 94 | // End: 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /c_src/locker.h: -------------------------------------------------------------------------------- 1 | #ifndef GEN_UTP_LOCKER_H 2 | #define GEN_UTP_LOCKER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // locker.h: Erlang driver lock/unlock 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include "erl_driver.h" 27 | 28 | namespace UtpDrv { 29 | 30 | template 31 | class BaseLocker 32 | { 33 | public: 34 | explicit BaseLocker(T m) : mtx(m) { lock(mtx); } 35 | ~BaseLocker() { unlock(mtx); } 36 | 37 | private: 38 | T mtx; 39 | 40 | BaseLocker(const BaseLocker&); 41 | BaseLocker& operator=(const BaseLocker&); 42 | }; 43 | 44 | typedef BaseLocker MutexLocker; 46 | 47 | typedef BaseLocker PdlLocker; 49 | 50 | } 51 | 52 | 53 | 54 | // this block comment is for emacs, do not delete 55 | // Local Variables: 56 | // mode: c++ 57 | // c-file-style: "stroustrup" 58 | // c-file-offsets: ((innamespace . 0)) 59 | // End: 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /c_src/main_handler.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // main_handler.cc: handler for primary uTP driver port 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "main_handler.h" 25 | #include "globals.h" 26 | #include "locker.h" 27 | #include "libutp/utp.h" 28 | #include "utp_handler.h" 29 | #include "client.h" 30 | #include "listener.h" 31 | 32 | 33 | using namespace UtpDrv; 34 | 35 | const unsigned long timeout_check = 10; 36 | 37 | UtpDrv::MainHandler* UtpDrv::MainHandler::main_handler = 0; 38 | 39 | UtpDrv::MainHandler::MainHandler(ErlDrvPort p) : 40 | Handler(p), delegatee(0), map_mutex(0) 41 | { 42 | } 43 | 44 | UtpDrv::MainHandler::MainHandler(Handler* dg) : 45 | Handler(0), delegatee(dg), map_mutex(0) 46 | { 47 | } 48 | 49 | UtpDrv::MainHandler::~MainHandler() 50 | { 51 | delete delegatee; 52 | } 53 | 54 | int 55 | UtpDrv::MainHandler::driver_init() 56 | { 57 | UTPDRV_TRACER << "MainHandler::driver_init" << UTPDRV_TRACE_ENDL; 58 | utp_mutex = erl_drv_mutex_create(const_cast("utp")); 59 | return 0; 60 | } 61 | 62 | void 63 | UtpDrv::MainHandler::driver_finish() 64 | { 65 | UTPDRV_TRACER << "MainHandler::driver_finish" << UTPDRV_TRACE_ENDL; 66 | erl_drv_mutex_destroy(utp_mutex); 67 | } 68 | 69 | void 70 | UtpDrv::MainHandler::check_utp_timeouts() const 71 | { 72 | if (main_handler != 0) { 73 | MutexLocker lock(utp_mutex); 74 | UTP_CheckTimeouts(); 75 | driver_set_timer(port, timeout_check); 76 | } 77 | } 78 | 79 | ErlDrvSSizeT 80 | UtpDrv::MainHandler::control(unsigned command, const char* buf, ErlDrvSizeT len, 81 | char** rbuf, ErlDrvSizeT rlen) 82 | { 83 | UTPDRV_TRACER << "MainHandler::control " << this << UTPDRV_TRACE_ENDL; 84 | if (delegatee != 0) { 85 | return delegatee->control(command, buf, len, rbuf, rlen); 86 | } else { 87 | switch (command) { 88 | case UTP_LISTEN: 89 | return listen(buf, len, rbuf, rlen); 90 | break; 91 | case UTP_CONNECT_START: 92 | return connect_start(buf, len, rbuf, rlen); 93 | break; 94 | default: 95 | return encode_error(rbuf, rlen, "enotsup"); 96 | break; 97 | } 98 | } 99 | } 100 | 101 | void 102 | UtpDrv::MainHandler::start() 103 | { 104 | UTPDRV_TRACER << "MainHandler::start " << this << UTPDRV_TRACE_ENDL; 105 | MutexLocker lock(utp_mutex); 106 | if (main_handler == 0) { 107 | UTPDRV_TRACER << "main_handler set to " << this << UTPDRV_TRACE_ENDL; 108 | main_handler = this; 109 | driver_set_timer(port, timeout_check); 110 | map_mutex = erl_drv_mutex_create(const_cast("utpmap")); 111 | } 112 | } 113 | 114 | void 115 | UtpDrv::MainHandler::stop() 116 | { 117 | UTPDRV_TRACER << "MainHandler::stop " << this << UTPDRV_TRACE_ENDL; 118 | if (delegatee != 0) { 119 | delegatee->stop(); 120 | } 121 | MutexLocker lock(utp_mutex); 122 | if (main_handler == this) { 123 | main_handler = 0; 124 | if (port != 0) { 125 | FdMap::iterator it = fdmap.begin(); 126 | FdMap::iterator end = fdmap.end(); 127 | while (it != end) { 128 | int fd = it->first; 129 | driver_select(port, reinterpret_cast(fd), 130 | ERL_DRV_READ|ERL_DRV_USE, 0); 131 | ++it; 132 | } 133 | } 134 | driver_cancel_timer(port); 135 | erl_drv_mutex_destroy(map_mutex); 136 | } 137 | } 138 | 139 | void 140 | UtpDrv::MainHandler::ready_input(long fd) 141 | { 142 | SocketHandler* hndlr = 0; 143 | { 144 | MutexLocker lock(map_mutex); 145 | FdMap::iterator it = fdmap.find(fd); 146 | if (it != fdmap.end()) { 147 | hndlr = it->second; 148 | } 149 | } 150 | if (hndlr != 0) { 151 | hndlr->input_ready(); 152 | } 153 | } 154 | 155 | void 156 | UtpDrv::MainHandler::outputv(ErlIOVec& iovec) 157 | { 158 | UTPDRV_TRACER << "MainHandler::outputv" << UTPDRV_TRACE_ENDL; 159 | if (delegatee != 0) { 160 | delegatee->outputv(iovec); 161 | } 162 | } 163 | 164 | void 165 | UtpDrv::MainHandler::process_exit(ErlDrvMonitor* monitor) 166 | { 167 | UTPDRV_TRACER << "MainHandler::process_exit" << UTPDRV_TRACE_ENDL; 168 | Handler* h = 0; 169 | ErlDrvTermData proc; 170 | { 171 | MutexLocker lock(map_mutex); 172 | MonMap::iterator it = mon_map.find(*monitor); 173 | if (it != mon_map.end()) { 174 | h = it->second; 175 | mon_map.erase(it); 176 | proc = driver_get_monitored_process(port, monitor); 177 | proc_mon_map.erase(proc); 178 | } 179 | } 180 | if (h != 0) { 181 | h->process_exited(monitor, proc); 182 | } 183 | } 184 | 185 | ErlDrvPort 186 | UtpDrv::MainHandler::drv_port() 187 | { 188 | return main_handler->port; 189 | } 190 | 191 | void 192 | UtpDrv::MainHandler::start_input(int fd, SocketHandler* handler) 193 | { 194 | UTPDRV_TRACER << "MainHandler::start_input for socket " << fd 195 | << UTPDRV_TRACE_ENDL; 196 | if (main_handler != 0) { 197 | main_handler->select(fd, handler); 198 | } 199 | } 200 | 201 | void 202 | UtpDrv::MainHandler::stop_input(int fd) 203 | { 204 | UTPDRV_TRACER << "MainHandler::stop_input for socket " << fd 205 | << UTPDRV_TRACE_ENDL; 206 | if (main_handler != 0) { 207 | main_handler->deselect(fd); 208 | } 209 | } 210 | 211 | bool 212 | UtpDrv::MainHandler::add_monitor(ErlDrvTermData proc, Handler* h) 213 | { 214 | UTPDRV_TRACER << "MainHandler::add_monitor" << UTPDRV_TRACE_ENDL; 215 | return main_handler != 0 ? main_handler->add_mon(proc, h) : false; 216 | } 217 | 218 | bool 219 | UtpDrv::MainHandler::add_mon(ErlDrvTermData proc, Handler* h) 220 | { 221 | UTPDRV_TRACER << "MainHandler::add_mon " << this << UTPDRV_TRACE_ENDL; 222 | MutexLocker lock(map_mutex); 223 | ErlDrvMonitor mon; 224 | bool result = (driver_monitor_process(port, proc, &mon) == 0); 225 | if (result) { 226 | MonMap::value_type v1(mon, h); 227 | ProcMonMap::value_type v2(proc, mon); 228 | mon_map.insert(v1); 229 | proc_mon_map.insert(v2); 230 | } 231 | return result; 232 | } 233 | 234 | void 235 | UtpDrv::MainHandler::del_monitor(ErlDrvTermData proc) 236 | { 237 | UTPDRV_TRACER << "MainHandler::del_monitor" << UTPDRV_TRACE_ENDL; 238 | if (main_handler != 0) { 239 | main_handler->del_mon(proc); 240 | } 241 | } 242 | 243 | void 244 | UtpDrv::MainHandler::del_monitors(Handler* h) 245 | { 246 | UTPDRV_TRACER << "MainHandler::del_monitors" << UTPDRV_TRACE_ENDL; 247 | if (main_handler != 0) { 248 | main_handler->del_mons(h); 249 | } 250 | } 251 | 252 | void 253 | UtpDrv::MainHandler::del_mon(ErlDrvTermData proc) 254 | { 255 | UTPDRV_TRACER << "MainHandler::del_mon " << this << UTPDRV_TRACE_ENDL; 256 | MutexLocker lock(map_mutex); 257 | ProcMonMap::iterator it = proc_mon_map.find(proc); 258 | if (it != proc_mon_map.end()) { 259 | driver_demonitor_process(port, &it->second); 260 | mon_map.erase(it->second); 261 | proc_mon_map.erase(it); 262 | } 263 | } 264 | 265 | void 266 | UtpDrv::MainHandler::del_mons(Handler* h) 267 | { 268 | if (h != this) { 269 | UTPDRV_TRACER << "MainHandler::del_mons " << this << UTPDRV_TRACE_ENDL; 270 | MutexLocker lock(map_mutex); 271 | MonMap::iterator it = mon_map.begin(); 272 | while (it != mon_map.end()) { 273 | if (it->second == h) { 274 | ProcMonMap::iterator itp = proc_mon_map.begin(); 275 | while (itp != proc_mon_map.end()) { 276 | if (driver_compare_monitors(&it->first, &itp->second) == 0) { 277 | driver_demonitor_process(port, &it->first); 278 | proc_mon_map.erase(itp++); 279 | } else { 280 | ++itp; 281 | } 282 | } 283 | mon_map.erase(it++); 284 | } else { 285 | ++it; 286 | } 287 | } 288 | } 289 | } 290 | 291 | ErlDrvSSizeT 292 | UtpDrv::MainHandler::connect_start(const char* buf, ErlDrvSizeT len, 293 | char** rbuf, ErlDrvSizeT rlen) 294 | { 295 | UTPDRV_TRACER << "MainHandler::connect_start " << this << UTPDRV_TRACE_ENDL; 296 | Binary binopts; 297 | char addrstr[INET6_ADDRSTRLEN]; 298 | unsigned long addrport; 299 | try { 300 | EiDecoder decoder(buf, len); 301 | int arity, type, size; 302 | decoder.tuple_header(arity); 303 | if (arity != 3) { 304 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 305 | } 306 | decoder.string(addrstr); 307 | decoder.ulongval(addrport); 308 | decoder.type(type, size); 309 | if (type != ERL_BINARY_EXT) { 310 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 311 | } 312 | binopts.decode(decoder, size); 313 | } catch (const EiError&) { 314 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 315 | } 316 | 317 | SockAddr addr; 318 | try { 319 | addr.from_addrport(addrstr, addrport); 320 | } catch (const BadSockAddr&) { 321 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 322 | } 323 | 324 | SocketHandler::SockOpts opts; 325 | opts.decode(binopts); 326 | int udp_sock, err; 327 | if (opts.fd != INVALID_SOCKET) { 328 | udp_sock = opts.fd; 329 | SockAddr fdaddr; 330 | if (getsockname(udp_sock, fdaddr, &fdaddr.slen) < 0) { 331 | err = errno; 332 | } else { 333 | err = 0; 334 | } 335 | } else if (opts.addr_set) { 336 | err = SocketHandler::open_udp_socket(udp_sock, opts.addr); 337 | } else if (opts.inet6) { 338 | SockAddr in6_any("::", 0); 339 | err = SocketHandler::open_udp_socket(udp_sock, in6_any); 340 | } else { 341 | err = SocketHandler::open_udp_socket(udp_sock, opts.port); 342 | } 343 | if (err != 0) { 344 | return encode_error(rbuf, rlen, erl_errno_id(err)); 345 | } else { 346 | Client* client = new Client(udp_sock, opts); 347 | delegatee = client; 348 | client->set_port(port); 349 | port = 0; 350 | client->connect_to(addr); 351 | return encode_atom(rbuf, rlen, "ok"); 352 | } 353 | return 0; 354 | } 355 | 356 | ErlDrvSSizeT 357 | UtpDrv::MainHandler::listen(const char* buf, ErlDrvSizeT len, 358 | char** rbuf, ErlDrvSizeT rlen) 359 | { 360 | UTPDRV_TRACER << "MainHandler::listen " << this << UTPDRV_TRACE_ENDL; 361 | Binary binopts; 362 | try { 363 | EiDecoder decoder(buf, len); 364 | int type, size; 365 | decoder.type(type, size); 366 | if (type != ERL_BINARY_EXT) { 367 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 368 | } 369 | binopts.decode(decoder, size); 370 | } catch (const EiError&) { 371 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 372 | } 373 | 374 | SocketHandler::SockOpts opts; 375 | opts.decode(binopts); 376 | int udp_sock, err; 377 | if (opts.fd != INVALID_SOCKET) { 378 | udp_sock = opts.fd; 379 | SockAddr fdaddr; 380 | if (getsockname(udp_sock, fdaddr, &fdaddr.slen) < 0) { 381 | err = errno; 382 | } else { 383 | err = 0; 384 | } 385 | } else if (opts.addr_set) { 386 | err = SocketHandler::open_udp_socket(udp_sock, opts.addr, true); 387 | } else if (opts.inet6) { 388 | SockAddr in6_any("::", 0); 389 | err = SocketHandler::open_udp_socket(udp_sock, in6_any, true); 390 | } else { 391 | err = SocketHandler::open_udp_socket(udp_sock, opts.port, true); 392 | } 393 | if (err != 0) { 394 | return encode_error(rbuf, rlen, erl_errno_id(err)); 395 | } else { 396 | delegatee = new Listener(udp_sock, opts); 397 | delegatee->set_port(port); 398 | port = 0; 399 | return encode_atom(rbuf, rlen, "ok"); 400 | } 401 | } 402 | 403 | void 404 | UtpDrv::MainHandler::select(int fd, SocketHandler* handler) 405 | { 406 | UTPDRV_TRACER << "MainHandler::select for socket " << fd 407 | << ", handler " << handler << UTPDRV_TRACE_ENDL; 408 | if (port != 0) { 409 | driver_select(port, reinterpret_cast(fd), 410 | ERL_DRV_READ|ERL_DRV_USE, 1); 411 | } 412 | FdMap::value_type val(fd, handler); 413 | MutexLocker lock(map_mutex); 414 | fdmap.insert(val); 415 | } 416 | 417 | void 418 | UtpDrv::MainHandler::deselect(int& fd) 419 | { 420 | UTPDRV_TRACER << "MainHandler::deselect socket " << fd << UTPDRV_TRACE_ENDL; 421 | if (fd != INVALID_SOCKET) { 422 | if (port != 0) { 423 | driver_select(port, reinterpret_cast(fd), 424 | ERL_DRV_READ|ERL_DRV_USE, 0); 425 | } 426 | MutexLocker lock(map_mutex); 427 | fdmap.erase(fd); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /c_src/main_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_MAIN_HANDLER_H 2 | #define UTPDRV_MAIN_HANDLER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // main_handler.h: handler for primary uTP driver port 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include 28 | #include "handler.h" 29 | #include "utils.h" 30 | #include "utp_handler.h" 31 | #include "drv_types.h" 32 | 33 | 34 | namespace UtpDrv { 35 | 36 | class MainHandler : public Handler 37 | { 38 | public: 39 | explicit MainHandler(ErlDrvPort p); 40 | explicit MainHandler(Handler* delegatee); 41 | ~MainHandler(); 42 | 43 | static int driver_init(); 44 | static void driver_finish(); 45 | 46 | void check_utp_timeouts() const; 47 | 48 | ErlDrvSSizeT 49 | control(unsigned command, const char* buf, ErlDrvSizeT len, 50 | char** rbuf, ErlDrvSizeT rlen); 51 | 52 | void start(); 53 | void stop(); 54 | void ready_input(long fd); 55 | void outputv(ErlIOVec& ev); 56 | void process_exit(ErlDrvMonitor* monitor); 57 | 58 | static ErlDrvPort drv_port(); 59 | 60 | static void start_input(int fd, SocketHandler* handler); 61 | static void stop_input(int fd); 62 | 63 | static bool add_monitor(ErlDrvTermData proc, Handler* h); 64 | static void del_monitor(ErlDrvTermData proc); 65 | static void del_monitors(Handler* h); 66 | 67 | private: 68 | // MainHandler singleton 69 | static MainHandler* main_handler; 70 | Handler* delegatee; 71 | 72 | ErlDrvTermData owner; 73 | 74 | struct MonCompare { 75 | bool 76 | operator()(const ErlDrvMonitor& m1, const ErlDrvMonitor& m2) { 77 | return driver_compare_monitors(&m1, &m2) < 0; 78 | } 79 | }; 80 | 81 | ErlDrvMutex* map_mutex; 82 | 83 | typedef std::map FdMap; 84 | FdMap fdmap; 85 | typedef std::map MonMap; 86 | typedef std::map ProcMonMap; 87 | MonMap mon_map; 88 | ProcMonMap proc_mon_map; 89 | 90 | ErlDrvSSizeT 91 | connect_start(const char* buf, ErlDrvSizeT len, 92 | char** rbuf, ErlDrvSizeT rlen); 93 | 94 | ErlDrvSSizeT 95 | listen(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 96 | 97 | void select(int fd, SocketHandler* handler); 98 | void deselect(int& fd); 99 | 100 | bool add_mon(ErlDrvTermData proc, Handler* h); 101 | void del_mon(ErlDrvTermData proc); 102 | void del_mons(Handler* h); 103 | 104 | // prevent copies 105 | MainHandler(const MainHandler&); 106 | void operator=(const MainHandler&); 107 | }; 108 | 109 | } 110 | 111 | 112 | 113 | // this block comment is for emacs, do not delete 114 | // Local Variables: 115 | // mode: c++ 116 | // c-file-style: "stroustrup" 117 | // c-file-offsets: ((innamespace . 0)) 118 | // End: 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /c_src/server.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // server.cc: uTP server port 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "server.h" 24 | #include "listener.h" 25 | #include "globals.h" 26 | #include "locker.h" 27 | 28 | 29 | using namespace UtpDrv; 30 | 31 | UtpDrv::Server::Server(int sock, const SockOpts& so) : 32 | UtpHandler(sock, so) 33 | { 34 | UTPDRV_TRACER << "Server::Server " << this 35 | << ", socket " << sock << UTPDRV_TRACE_ENDL; 36 | } 37 | 38 | UtpDrv::Server::~Server() 39 | { 40 | UTPDRV_TRACER << "Server::~Server " << this << UTPDRV_TRACE_ENDL; 41 | } 42 | 43 | void 44 | UtpDrv::Server::do_send_to(const byte* p, size_t len, 45 | const sockaddr* to, socklen_t slen) 46 | { 47 | UTPDRV_TRACER << "Server::do_send_to " << this << UTPDRV_TRACE_ENDL; 48 | if (udp_sock != INVALID_SOCKET) { 49 | int index = 0; 50 | for (;;) { 51 | ssize_t count = send(udp_sock, p+index, len-index, 0); 52 | if (count == ssize_t(len-index)) { 53 | break; 54 | } else if (count < 0 && errno != EINTR && 55 | errno != EAGAIN && errno != EWOULDBLOCK) { 56 | do_error(errno); 57 | break; 58 | } else { 59 | index += count; 60 | } 61 | } 62 | } 63 | } 64 | 65 | 66 | void 67 | UtpDrv::Server::do_incoming(UTPSocket* utp_sock) 68 | { 69 | UTPDRV_TRACER << "Server::do_incoming " << this << UTPDRV_TRACE_ENDL; 70 | if (utp == 0) { 71 | utp = utp_sock; 72 | set_utp_callbacks(); 73 | writable = true; 74 | status = connected; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /c_src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_SERVER_H 2 | #define UTPDRV_SERVER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // server.h: uTP server port 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include "utp_handler.h" 27 | #include "libutp/utp.h" 28 | 29 | 30 | namespace UtpDrv { 31 | 32 | class Server : public UtpHandler 33 | { 34 | public: 35 | Server(int sock, const SockOpts& so); 36 | ~Server(); 37 | 38 | private: 39 | void do_send_to(const byte* p, size_t len, const sockaddr* to, 40 | socklen_t slen); 41 | void do_incoming(UTPSocket* utp); 42 | 43 | // prevent copies 44 | Server(const Server&); 45 | void operator=(const Server&); 46 | }; 47 | 48 | } 49 | 50 | 51 | 52 | // this block comment is for emacs, do not delete 53 | // Local Variables: 54 | // mode: c++ 55 | // c-file-style: "stroustrup" 56 | // c-file-offsets: ((innamespace . 0)) 57 | // End: 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /c_src/socket_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_SOCKET_HANDLER_H 2 | #define UTPDRV_SOCKET_HANDLER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // socket_handler.h: abstract base class for handlers owning a socket 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include 28 | #include 29 | #include "handler.h" 30 | #include "drv_types.h" 31 | 32 | 33 | namespace UtpDrv { 34 | 35 | struct BadSockAddr : public std::exception {}; 36 | struct SocketFailure : public std::exception 37 | { 38 | explicit SocketFailure(int err) : error(err) {} 39 | int error; 40 | }; 41 | 42 | struct SockAddr { 43 | sockaddr_storage addr; 44 | socklen_t slen; 45 | SockAddr(); 46 | SockAddr(const sockaddr& sa, socklen_t sl); 47 | SockAddr(const char* addrstr, unsigned short port); 48 | SockAddr(in_addr_t inaddr, unsigned short port); 49 | SockAddr(const in6_addr& inaddr6, unsigned short port); 50 | void from_addrport(const char* addrstr, unsigned short port); 51 | void from_addrport(in_addr_t inaddr, unsigned short port); 52 | void from_addrport(const in6_addr& inaddr6, unsigned short port); 53 | void to_addrport(char* addrstr, size_t alen, unsigned short& port) const; 54 | int family() const; 55 | ErlDrvSSizeT encode(char** rbuf, ErlDrvSizeT rlen) const; 56 | bool operator<(const SockAddr& sa) const; 57 | bool operator==(const SockAddr& sa) const { return !(*this < sa || sa < *this); } 58 | operator sockaddr*(); 59 | operator const sockaddr*() const; 60 | }; 61 | 62 | const int UTP_SNDBUF_DEFAULT = 16384; 63 | const int UTP_RECBUF_DEFAULT = 16384; 64 | 65 | class SocketHandler : public Handler 66 | { 67 | public: 68 | ~SocketHandler(); 69 | 70 | // the following enums must match option values in gen_utp_opts.hrl 71 | enum Opts { 72 | UTP_IP_OPT = 1, 73 | UTP_PORT_OPT, 74 | UTP_LIST_OPT, 75 | UTP_BINARY_OPT, 76 | UTP_MODE_OPT, 77 | UTP_INET_OPT, 78 | UTP_INET6_OPT, 79 | UTP_SEND_TMOUT_OPT, 80 | UTP_SEND_TMOUT_INFINITE_OPT, 81 | UTP_ACTIVE_OPT, 82 | UTP_PACKET_OPT, 83 | UTP_HEADER_OPT, 84 | UTP_SNDBUF_OPT, 85 | UTP_RECBUF_OPT 86 | }; 87 | typedef std::vector OptsList; 88 | 89 | enum Active { 90 | ACTIVE_FALSE, 91 | ACTIVE_ONCE, 92 | ACTIVE_TRUE 93 | }; 94 | 95 | struct SockOpts { 96 | SockOpts(); 97 | 98 | void decode(const Binary& bin, OptsList* opts_decoded = 0); 99 | void decode_and_merge(const Binary& bin); 100 | 101 | SockAddr addr; 102 | char addrstr[INET6_ADDRSTRLEN]; 103 | long send_tmout; 104 | Active active; 105 | int fd; 106 | int header; 107 | int sndbuf, recbuf; 108 | unsigned short port; 109 | DeliveryMode delivery_mode; 110 | unsigned char packet; 111 | bool inet6; 112 | bool addr_set; 113 | }; 114 | 115 | virtual void set_port(ErlDrvPort p); 116 | 117 | static int 118 | open_udp_socket(int& udp_sock, unsigned short port = 0, 119 | bool reuseaddr = false); 120 | static int 121 | open_udp_socket(int& udp_sock, const SockAddr& sa, bool reuseaddr = false); 122 | 123 | ErlDrvSSizeT 124 | control(unsigned command, const char* buf, ErlDrvSizeT len, 125 | char** rbuf, ErlDrvSizeT rlen); 126 | 127 | virtual ErlDrvSSizeT 128 | sockname(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 129 | 130 | virtual void input_ready() = 0; 131 | 132 | protected: 133 | SocketHandler(); 134 | SocketHandler(int fd, const SockOpts& so); 135 | 136 | virtual ErlDrvSSizeT 137 | close(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen) = 0; 138 | 139 | virtual ErlDrvSSizeT 140 | setopts(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 141 | 142 | virtual ErlDrvSSizeT 143 | getopts(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 144 | 145 | virtual ErlDrvSSizeT 146 | peername(const char* buf, ErlDrvSizeT len, char** rbuf, 147 | ErlDrvSizeT rlen) = 0; 148 | 149 | // In the send_read_buffer function, a Receiver object is used to 150 | // determine where to send the data. If the send_to_connected field is 151 | // true, send the message to the connected process; if false, send it 152 | // to the process identified by the caller field, including the 153 | // caller_ref in the message. 154 | struct Receiver { 155 | Receiver() : send_to_connected(true) {} 156 | Receiver(bool b, ErlDrvTermData td, const Binary& bin) : 157 | caller_ref(bin), caller(td), send_to_connected(b) {} 158 | Binary caller_ref; 159 | ErlDrvTermData caller; 160 | bool send_to_connected; 161 | }; 162 | 163 | bool 164 | emit_read_buffer(ErlDrvSizeT len, const Receiver& receiver, 165 | ErlDrvSizeT& new_queue_size); 166 | 167 | void reduce_read_count(size_t reduction); 168 | 169 | size_t 170 | move_read_data(const SysIOVec* vec, int vlen, ustring& buf, size_t sz); 171 | 172 | bool 173 | emit_closed_message(); 174 | 175 | typedef std::list ReadCount; 176 | ReadCount read_count; 177 | SockOpts sockopts; 178 | int udp_sock; 179 | bool close_pending, selected; 180 | }; 181 | 182 | } 183 | 184 | 185 | 186 | // this block comment is for emacs, do not delete 187 | // Local Variables: 188 | // mode: c++ 189 | // c-file-style: "stroustrup" 190 | // c-file-offsets: ((innamespace . 0)) 191 | // End: 192 | 193 | #endif 194 | -------------------------------------------------------------------------------- /c_src/utils.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // utils.cc: utilities for uTP driver 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "utils.h" 25 | #include "coder.h" 26 | #include "globals.h" 27 | #include "main_handler.h" 28 | #include "utp_handler.h" 29 | #include "locker.h" 30 | 31 | 32 | using namespace UtpDrv; 33 | 34 | const UtpDrv::NoMemError UtpDrv::enomem_error; 35 | 36 | ErlDrvSSizeT 37 | UtpDrv::encode_atom(char** rbuf, ErlDrvSizeT rlen, const char* atom) 38 | { 39 | EiEncoder encoder; 40 | try { 41 | encoder.atom(atom); 42 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 43 | return encoder.copy_to_binary(binptr, rlen); 44 | } catch (std::exception&) { 45 | memcpy(*rbuf, enomem_error.buffer(), enomem_error.size()); 46 | return enomem_error.size(); 47 | } 48 | } 49 | 50 | ErlDrvSSizeT 51 | UtpDrv::encode_error(char** rbuf, ErlDrvSizeT rlen, const char* error) 52 | { 53 | EiEncoder encoder; 54 | try { 55 | encoder.tuple_header(2).atom("error").atom(error); 56 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 57 | return encoder.copy_to_binary(binptr, rlen); 58 | } catch (std::exception&) { 59 | memcpy(*rbuf, enomem_error.buffer(), enomem_error.size()); 60 | return enomem_error.size(); 61 | } 62 | } 63 | 64 | ErlDrvSSizeT 65 | UtpDrv::encode_error(char** rbuf, ErlDrvSizeT rlen, int error) 66 | { 67 | return encode_error(rbuf, rlen, erl_errno_id(error)); 68 | } 69 | 70 | void 71 | UtpDrv::send_not_connected(ErlDrvPort port) 72 | { 73 | ErlDrvTermData caller = driver_caller(port); 74 | ErlDrvTermData term[] = { 75 | ERL_DRV_ATOM, driver_mk_atom(const_cast("utp_reply")), 76 | ERL_DRV_PORT, driver_mk_port(port), 77 | ERL_DRV_ATOM, driver_mk_atom(const_cast("error")), 78 | ERL_DRV_ATOM, driver_mk_atom(erl_errno_id(ENOTCONN)), 79 | ERL_DRV_TUPLE, 2, 80 | ERL_DRV_TUPLE, 3, 81 | }; 82 | utp_send_term(port, caller, term, sizeof term/sizeof *term); 83 | } 84 | 85 | UtpDrv::NoMemError::NoMemError() : bin(0) 86 | { 87 | encode_error(reinterpret_cast(&bin), 0, ENOMEM); 88 | } 89 | 90 | UtpDrv::NoMemError::~NoMemError() 91 | { 92 | if (bin != 0) { 93 | driver_free_binary(bin); 94 | } 95 | } 96 | 97 | const void* 98 | UtpDrv::NoMemError::buffer() const 99 | { 100 | return bin->orig_bytes; 101 | } 102 | 103 | size_t 104 | UtpDrv::NoMemError::size() const 105 | { 106 | return bin->orig_size; 107 | } 108 | -------------------------------------------------------------------------------- /c_src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_UTILS_H 2 | #define UTPDRV_UTILS_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // utils.h: utilities for uTP driver 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "erl_driver.h" 28 | #include "coder.h" 29 | 30 | 31 | namespace UtpDrv { 32 | 33 | class SocketHandler; 34 | 35 | extern ErlDrvSSizeT 36 | encode_atom(char** rbuf, ErlDrvSizeT rlen, const char* atom); 37 | 38 | extern ErlDrvSSizeT 39 | encode_error(char** rbuf, ErlDrvSizeT rlen, const char* error); 40 | 41 | extern ErlDrvSSizeT 42 | encode_error(char** rbuf, ErlDrvSizeT rlen, int error); 43 | 44 | extern void 45 | send_not_connected(ErlDrvPort port); 46 | 47 | // A static instance of the following class is created up front to hold the 48 | // binary form of the {error, enomem} Erlang term. The binary is then used 49 | // as a return value from the driver if an out-of-memory condition arises. 50 | class NoMemError 51 | { 52 | public: 53 | NoMemError(); 54 | ~NoMemError(); 55 | 56 | const void* buffer() const; 57 | size_t size() const; 58 | 59 | private: 60 | ErlDrvBinary* bin; 61 | 62 | // prevent copies 63 | NoMemError(const NoMemError&); 64 | void operator=(const NoMemError&); 65 | }; 66 | 67 | extern const NoMemError enomem_error; 68 | 69 | } 70 | 71 | 72 | // this block comment is for emacs, do not delete 73 | // Local Variables: 74 | // mode: c++ 75 | // c-file-style: "stroustrup" 76 | // c-file-offsets: ((innamespace . 0)) 77 | // End: 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /c_src/utp_handler.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // utp_handler.cc: base class for uTP port handlers 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "utp_handler.h" 25 | #include "locker.h" 26 | #include "globals.h" 27 | #include "main_handler.h" 28 | #include "utils.h" 29 | 30 | 31 | using namespace UtpDrv; 32 | 33 | UtpDrv::UtpHandler::UtpHandler(int sock, const SockOpts& so) : 34 | SocketHandler(sock, so), 35 | caller(driver_term_nil), utp(0), recv_len(0), status(not_connected), state(0), 36 | error_code(0), writable(false), sender_waiting(false), receiver_waiting(false), 37 | eof_seen(false) 38 | { 39 | } 40 | 41 | UtpDrv::UtpHandler::~UtpHandler() 42 | { 43 | } 44 | 45 | void 46 | UtpDrv::UtpHandler::input_ready() 47 | { 48 | // TODO: when recv buffer sizes can be varied, the following buffer will 49 | // need to come from a pool 50 | byte buf[8192]; 51 | SockAddr addr; 52 | int len = recvfrom(udp_sock, buf, sizeof buf, 0, addr, &addr.slen); 53 | if (len > 0) { 54 | MutexLocker lock(utp_mutex); 55 | UTP_IsIncomingUTP(&UtpHandler::utp_incoming, 56 | &UtpHandler::send_to, this, 57 | buf, len, addr, addr.slen); 58 | } 59 | } 60 | 61 | void 62 | UtpDrv::UtpHandler::set_utp_callbacks() 63 | { 64 | UTPDRV_TRACER << "UtpHandler::set_utp_callbacks " << this << UTPDRV_TRACE_ENDL; 65 | if (utp != 0) { 66 | UTPFunctionTable funcs = { 67 | &UtpHandler::utp_read, 68 | &UtpHandler::utp_write, 69 | &UtpHandler::utp_get_rb_size, 70 | &UtpHandler::utp_state_change, 71 | &UtpHandler::utp_error, 72 | &UtpHandler::utp_overhead, 73 | }; 74 | UTP_SetCallbacks(utp, &funcs, this); 75 | UTP_SetSockopt(utp, SO_SNDBUF, UTP_SNDBUF_DEFAULT); 76 | UTP_SetSockopt(utp, SO_RCVBUF, UTP_RECBUF_DEFAULT); 77 | } 78 | } 79 | 80 | ErlDrvSSizeT 81 | UtpDrv::UtpHandler::control(unsigned command, const char* buf, ErlDrvSizeT len, 82 | char** rbuf, ErlDrvSizeT rlen) 83 | { 84 | UTPDRV_TRACER << "UtpHandler::control " << this << UTPDRV_TRACE_ENDL; 85 | switch (command) { 86 | case UTP_CANCEL_SEND: 87 | return cancel_send(); 88 | case UTP_RECV: 89 | return recv(buf, len, rbuf, rlen); 90 | case UTP_CANCEL_RECV: 91 | return cancel_recv(); 92 | case UTP_SETOPTS: 93 | return setopts(buf, len, rbuf, rlen); 94 | } 95 | return SocketHandler::control(command, buf, len, rbuf, rlen); 96 | } 97 | 98 | ErlDrvSSizeT 99 | UtpDrv::UtpHandler::peername(const char* buf, ErlDrvSizeT len, 100 | char** rbuf, ErlDrvSizeT rlen) 101 | { 102 | UTPDRV_TRACER << "UtpHandler::peername " << this << UTPDRV_TRACE_ENDL; 103 | if (status != connected || utp == 0) { 104 | return encode_error(rbuf, rlen, ENOTCONN); 105 | } 106 | SockAddr addr; 107 | { 108 | MutexLocker lock(utp_mutex); 109 | UTP_GetPeerName(utp, addr, &addr.slen); 110 | } 111 | return addr.encode(rbuf, rlen); 112 | } 113 | 114 | void 115 | UtpDrv::UtpHandler::outputv(ErlIOVec& ev) 116 | { 117 | UTPDRV_TRACER << "UtpHandler::outputv " << this << UTPDRV_TRACE_ENDL; 118 | if (status != connected || utp == 0) { 119 | send_not_connected(port); 120 | return; 121 | } 122 | 123 | ErlDrvTermData local_caller, utp_reply; 124 | local_caller = driver_caller(port); 125 | utp_reply = driver_mk_atom(const_cast("utp_reply")); 126 | size_t write_total = 0; 127 | if (writable) { 128 | if (ev.size > 0) { 129 | if (sockopts.packet > 0) { 130 | ErlDrvBinary* pbin = driver_alloc_binary(sockopts.packet); 131 | union { 132 | char* p1; 133 | uint16_t* p2; 134 | uint32_t* p4; 135 | }; 136 | p1 = pbin->orig_bytes; 137 | switch (sockopts.packet) { 138 | case 1: 139 | *p1 = ev.size & 0xFF; 140 | break; 141 | case 2: 142 | *p2 = htons(ev.size & 0xFFFF); 143 | break; 144 | case 4: 145 | *p4 = htonl(ev.size & 0xFFFFFFFF); 146 | break; 147 | } 148 | write_queue.push_back(pbin); 149 | write_total += sockopts.packet; 150 | } 151 | for (int i = 0; i < ev.vsize; ++i) { 152 | ErlDrvBinary* bin = 0; 153 | if (ev.iov[i].iov_len > 0) { 154 | const SysIOVec& vec = ev.iov[i]; 155 | bin = driver_alloc_binary(vec.iov_len); 156 | memcpy(bin->orig_bytes, vec.iov_base, vec.iov_len); 157 | } else if (ev.binv[i] != 0) { 158 | bin = ev.binv[i]; 159 | driver_binary_inc_refc(bin); 160 | } 161 | if (bin != 0) { 162 | write_queue.push_back(bin); 163 | } 164 | } 165 | write_total += ev.size; 166 | } 167 | { 168 | MutexLocker lock(utp_mutex); 169 | writable = UTP_Write(utp, write_total); 170 | } 171 | ErlDrvTermData term[] = { 172 | ERL_DRV_ATOM, utp_reply, 173 | ERL_DRV_PORT, driver_mk_port(port), 174 | ERL_DRV_ATOM, driver_mk_atom(const_cast("ok")), 175 | ERL_DRV_TUPLE, 3, 176 | }; 177 | utp_send_term(port, local_caller, term, sizeof term/sizeof *term); 178 | } else { 179 | if (sockopts.send_tmout == 0) { 180 | ErlDrvTermData term[] = { 181 | ERL_DRV_ATOM, utp_reply, 182 | ERL_DRV_PORT, driver_mk_port(port), 183 | ERL_DRV_ATOM, driver_mk_atom(const_cast("error")), 184 | ERL_DRV_ATOM, driver_mk_atom(erl_errno_id(ETIMEDOUT)), 185 | ERL_DRV_TUPLE, 2, 186 | ERL_DRV_TUPLE, 3, 187 | }; 188 | utp_send_term(port, local_caller, term, sizeof term/sizeof *term); 189 | } else { 190 | { 191 | MutexLocker lock(utp_mutex); 192 | caller = local_caller; 193 | sender_waiting = true; 194 | } 195 | ErlDrvTermData term[12] = { 196 | ERL_DRV_ATOM, utp_reply, 197 | ERL_DRV_PORT, driver_mk_port(port), 198 | ERL_DRV_ATOM, driver_mk_atom(const_cast("wait")), 199 | }; 200 | size_t size = 6; 201 | if (sockopts.send_tmout == -1) { 202 | term[size++] = ERL_DRV_TUPLE; 203 | term[size++] = 3; 204 | } else { 205 | term[size++] = ERL_DRV_UINT; 206 | term[size++] = sockopts.send_tmout; 207 | term[size++] = ERL_DRV_TUPLE; 208 | term[size++] = 2; 209 | term[size++] = ERL_DRV_TUPLE; 210 | term[size++] = 3; 211 | } 212 | utp_send_term(port, local_caller, term, size); 213 | } 214 | } 215 | } 216 | 217 | void 218 | UtpDrv::UtpHandler::stop() 219 | { 220 | UTPDRV_TRACER << "UtpHandler::stop " << this << UTPDRV_TRACE_ENDL; 221 | if (utp != 0) { 222 | MutexLocker lock(utp_mutex); 223 | close_utp(); 224 | } 225 | ErlDrvSizeT qsize = driver_sizeq(port); 226 | if (qsize != 0) { 227 | driver_deq(port, qsize); 228 | } 229 | if (status == destroying) { 230 | delete this; 231 | } else { 232 | status = stopped; 233 | if (selected) { 234 | // TODO: this is bad -- the port is closing but our udp socket 235 | // is still in the driver select set. We can't deselect it here 236 | // because there might still be uTP traffic occurring between 237 | // our utp socket and the remote side. 238 | UTPDRV_TRACER << "UtpHandler::stop: deselecting " 239 | << udp_sock << " for " << this << UTPDRV_TRACE_ENDL; 240 | } 241 | } 242 | } 243 | 244 | ErlDrvSSizeT 245 | UtpDrv::UtpHandler::setopts(const char* buf, ErlDrvSizeT len, 246 | char** rbuf, ErlDrvSizeT rlen) 247 | { 248 | UTPDRV_TRACER << "UtpHandler::setopts " << this << UTPDRV_TRACE_ENDL; 249 | int saved_sndbuf = sockopts.sndbuf; 250 | int saved_recbuf = sockopts.recbuf; 251 | ErlDrvSSizeT result = SocketHandler::setopts(buf, len, rbuf, rlen); 252 | if (saved_sndbuf != sockopts.sndbuf) { 253 | UTP_SetSockopt(utp, SO_SNDBUF, sockopts.sndbuf); 254 | } 255 | if (saved_recbuf != sockopts.recbuf) { 256 | UTP_SetSockopt(utp, SO_RCVBUF, sockopts.recbuf); 257 | } 258 | return result; 259 | } 260 | 261 | ErlDrvSSizeT 262 | UtpDrv::UtpHandler::close(const char* buf, ErlDrvSizeT len, 263 | char** rbuf, ErlDrvSizeT rlen) 264 | { 265 | UTPDRV_TRACER << "UtpHandler::close " << this 266 | << ", status = " << status << ", close pending = " 267 | << close_pending << ", eof_seen = " << eof_seen 268 | << UTPDRV_TRACE_ENDL; 269 | const char* retval = "ok"; 270 | if (!close_pending && 271 | (eof_seen || (status != closing && status != destroying))) { 272 | MutexLocker lock(utp_mutex); 273 | status = closing; 274 | close_pending = true; 275 | eof_seen = false; 276 | try { 277 | EiDecoder decoder(buf, len); 278 | int type, size; 279 | decoder.type(type, size); 280 | if (type != ERL_BINARY_EXT) { 281 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 282 | } 283 | caller_ref.decode(decoder, size); 284 | } catch (const EiError&) { 285 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 286 | } 287 | caller = driver_caller(port); 288 | retval = "wait"; 289 | close_utp(); 290 | } 291 | EiEncoder encoder; 292 | encoder.atom(retval); 293 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 294 | return encoder.copy_to_binary(binptr, rlen); 295 | } 296 | 297 | ErlDrvSSizeT 298 | UtpDrv::UtpHandler::recv(const char* buf, ErlDrvSizeT len, 299 | char** rbuf, ErlDrvSizeT rlen) 300 | { 301 | UTPDRV_TRACER << "UtpHandler::recv " << this << UTPDRV_TRACE_ENDL; 302 | if (sockopts.active != ACTIVE_FALSE) { 303 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 304 | } 305 | Binary ref; 306 | unsigned long length; 307 | try { 308 | EiDecoder decoder(buf, len); 309 | int arity, type, size; 310 | decoder.tuple_header(arity); 311 | if (arity != 2) { 312 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 313 | } 314 | decoder.ulongval(length); 315 | decoder.type(type, size); 316 | if (type != ERL_BINARY_EXT) { 317 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 318 | } 319 | ref.decode(decoder, size); 320 | } catch (const EiError&) { 321 | return reinterpret_cast(ERL_DRV_ERROR_BADARG); 322 | } 323 | 324 | ErlDrvTermData local_caller = driver_caller(port); 325 | Receiver rcvr(false, local_caller, ref); 326 | ErlDrvSizeT qsize = 1; // any non-zero value will do 327 | bool sent = emit_read_buffer(length, rcvr, qsize); 328 | if (sent && qsize == 0) { 329 | MutexLocker lock(utp_mutex); 330 | UTP_RBDrained(utp); 331 | } if (!sent) { 332 | MutexLocker lock(utp_mutex); 333 | caller_ref.swap(ref); 334 | caller = local_caller; 335 | recv_len = length; 336 | receiver_waiting = true; 337 | } 338 | EiEncoder encoder; 339 | encoder.atom("wait"); 340 | ErlDrvBinary** binptr = reinterpret_cast(rbuf); 341 | return encoder.copy_to_binary(binptr, rlen); 342 | } 343 | 344 | ErlDrvSSizeT 345 | UtpDrv::UtpHandler::cancel_send() 346 | { 347 | UTPDRV_TRACER << "UtpHandler::cancel_send " << this << UTPDRV_TRACE_ENDL; 348 | MutexLocker lock(utp_mutex); 349 | sender_waiting = false; 350 | caller = driver_term_nil; 351 | return 0; 352 | } 353 | 354 | ErlDrvSSizeT 355 | UtpDrv::UtpHandler::cancel_recv() 356 | { 357 | UTPDRV_TRACER << "UtpHandler::cancel_recv " << this << UTPDRV_TRACE_ENDL; 358 | MutexLocker lock(utp_mutex); 359 | reset_waiting_recv(); 360 | return 0; 361 | } 362 | 363 | void 364 | UtpDrv::UtpHandler::close_utp() 365 | { 366 | UTPDRV_TRACER << "UtpHandler::close_utp " << this << UTPDRV_TRACE_ENDL; 367 | if (utp != 0 && status != destroying) { 368 | if (write_queue.size() != 0) { 369 | return; 370 | } 371 | status = closing; 372 | UTP_Close(utp); 373 | utp = 0; 374 | } 375 | } 376 | 377 | void 378 | UtpDrv::UtpHandler::reset_waiting_recv() 379 | { 380 | receiver_waiting = false; 381 | recv_len = 0; 382 | caller_ref.reset(); 383 | caller = 0; 384 | } 385 | 386 | void 387 | UtpDrv::UtpHandler::do_send_to(const byte* p, size_t len, 388 | const sockaddr* to, socklen_t slen) 389 | { 390 | UTPDRV_TRACER << "UtpHandler::do_send_to " << this << UTPDRV_TRACE_ENDL; 391 | if (udp_sock != INVALID_SOCKET) { 392 | int index = 0; 393 | for (;;) { 394 | ssize_t count = sendto(udp_sock, p+index, len-index, 0, to, slen); 395 | if (count == ssize_t(len-index)) { 396 | break; 397 | } else if (count < 0 && errno != EINTR && 398 | errno != EAGAIN && errno != EWOULDBLOCK) { 399 | do_error(errno); 400 | break; 401 | } else { 402 | index += count; 403 | } 404 | } 405 | } 406 | } 407 | 408 | void 409 | UtpDrv::UtpHandler::do_read(const byte* bytes, size_t count) 410 | { 411 | UTPDRV_TRACER << "UtpHandler::do_read " << this << UTPDRV_TRACE_ENDL; 412 | if (count != 0) { 413 | ErlDrvSizeT qsize = 1; // any non-zero value will do 414 | char* buf = const_cast(reinterpret_cast(bytes)); 415 | driver_enq(port, buf, count); 416 | read_count.push_back(count); 417 | if (sockopts.active == ACTIVE_FALSE) { 418 | if (receiver_waiting) { 419 | Receiver rcvr(false, caller, caller_ref); 420 | if (emit_read_buffer(recv_len, rcvr, qsize)) { 421 | reset_waiting_recv(); 422 | } 423 | } 424 | } else { 425 | Receiver rcvr; 426 | emit_read_buffer(0, rcvr, qsize); 427 | } 428 | if (qsize == 0) { 429 | UTP_RBDrained(utp); 430 | } 431 | } 432 | } 433 | 434 | void 435 | UtpDrv::UtpHandler::do_write(byte* bytes, size_t count) 436 | { 437 | UTPDRV_TRACER << "UtpHandler::do_write " << this 438 | << ": writing " << count << " bytes" << UTPDRV_TRACE_ENDL; 439 | if (count == 0) return; 440 | write_queue.pop_bytes(bytes, count); 441 | } 442 | 443 | size_t 444 | UtpDrv::UtpHandler::do_get_rb_size() 445 | { 446 | UTPDRV_TRACER << "UtpHandler::do_get_rb_size " << this << UTPDRV_TRACE_ENDL; 447 | return status == connected ? driver_sizeq(port) : 0; 448 | } 449 | 450 | void 451 | UtpDrv::UtpHandler::do_state_change(int s) 452 | { 453 | UTPDRV_TRACER << "UtpHandler::do_state_change " << this << ": " 454 | << "status " << status << ", current state: " << state 455 | << ", new state: " << s << UTPDRV_TRACE_ENDL; 456 | state = s; 457 | switch (state) { 458 | case UTP_STATE_EOF: 459 | write_queue.clear(); 460 | if (status != stopped) { 461 | close_utp(); 462 | } 463 | writable = false; 464 | eof_seen = true; 465 | break; 466 | 467 | case UTP_STATE_WRITABLE: 468 | writable = true; 469 | { 470 | size_t sz = write_queue.size(); 471 | if (sz != 0) { 472 | writable = UTP_Write(utp, sz); 473 | } 474 | } 475 | if (writable && sender_waiting) { 476 | ErlDrvTermData term[] = { 477 | ERL_DRV_ATOM, driver_mk_atom(const_cast("utp_reply")), 478 | ERL_DRV_PORT, driver_mk_port(port), 479 | ERL_DRV_ATOM, driver_mk_atom(const_cast("retry")), 480 | ERL_DRV_TUPLE, 3, 481 | }; 482 | utp_send_term(port, caller, term, sizeof term/sizeof *term); 483 | sender_waiting = false; 484 | } 485 | if (close_pending) { 486 | close_utp(); 487 | } 488 | break; 489 | 490 | case UTP_STATE_CONNECT: 491 | if (status == connect_pending && caller_ref) { 492 | ErlDrvTermData term[] = { 493 | ERL_DRV_EXT2TERM, caller_ref, caller_ref.size(), 494 | ERL_DRV_ATOM, driver_mk_atom(const_cast("ok")), 495 | ERL_DRV_TUPLE, 2, 496 | }; 497 | utp_output_term(port, term, sizeof term/sizeof *term); 498 | } 499 | status = connected; 500 | writable = true; 501 | break; 502 | 503 | case UTP_STATE_DESTROYING: 504 | if (selected) { 505 | UTPDRV_TRACER << "UtpHandler::do_state_change: deselecting " 506 | << udp_sock << " for " << this << UTPDRV_TRACE_ENDL; 507 | MainHandler::stop_input(udp_sock); 508 | selected = false; 509 | } 510 | if (status == closing) { 511 | if (close_pending && caller_ref) { 512 | UTPDRV_TRACER << "UtpHandler::do_state_change: " 513 | << "sending close message to caller for " << this 514 | << UTPDRV_TRACE_ENDL; 515 | ErlDrvTermData term[] = { 516 | ERL_DRV_EXT2TERM, caller_ref, caller_ref.size(), 517 | ERL_DRV_ATOM, driver_mk_atom(const_cast("ok")), 518 | ERL_DRV_TUPLE, 2, 519 | }; 520 | if (caller != driver_term_nil) { 521 | utp_send_term(port, caller, term, sizeof term/sizeof *term); 522 | } else { 523 | utp_output_term(port, term, sizeof term/sizeof *term); 524 | } 525 | caller = driver_term_nil; 526 | close_pending = false; 527 | } else if (sockopts.active != ACTIVE_FALSE) { 528 | emit_closed_message(); 529 | } 530 | writable = false; 531 | status = destroying; 532 | eof_seen = false; 533 | } else if (status == stopped) { 534 | delete this; 535 | } 536 | break; 537 | } 538 | } 539 | 540 | void 541 | UtpDrv::UtpHandler::do_error(int errcode) 542 | { 543 | UTPDRV_TRACER << "UtpHandler::do_error " << this << ": " 544 | << "status " << status << ", error code " << errcode 545 | << UTPDRV_TRACE_ENDL; 546 | error_code = errcode; 547 | switch (status) { 548 | case connect_pending: 549 | status = connect_failed; 550 | if (caller_ref) { 551 | ErlDrvTermData term[] = { 552 | ERL_DRV_EXT2TERM, caller_ref, caller_ref.size(), 553 | ERL_DRV_ATOM, driver_mk_atom(const_cast("error")), 554 | ERL_DRV_ATOM, driver_mk_atom(erl_errno_id(error_code)), 555 | ERL_DRV_TUPLE, 2, 556 | ERL_DRV_TUPLE, 2, 557 | }; 558 | utp_output_term(port, term, sizeof term/sizeof *term); 559 | } 560 | break; 561 | case connected: 562 | if (errcode == ECONNRESET) { 563 | close_utp(); 564 | } else if (sockopts.active != ACTIVE_FALSE) { 565 | ErlDrvTermData term[] = { 566 | ERL_DRV_ATOM, 567 | driver_mk_atom(const_cast("utp_error")), 568 | ERL_DRV_PORT, driver_mk_port(port), 569 | ERL_DRV_ATOM, driver_mk_atom(erl_errno_id(errcode)), 570 | ERL_DRV_TUPLE, 3 571 | }; 572 | utp_output_term(port, term, sizeof term/sizeof *term); 573 | } 574 | break; 575 | default: 576 | break; 577 | } 578 | } 579 | 580 | void 581 | UtpDrv::UtpHandler::do_overhead(bool send, size_t count, int type) 582 | { 583 | // do nothing 584 | } 585 | 586 | void 587 | UtpDrv::UtpHandler::send_to(void* data, const byte* p, size_t len, 588 | const sockaddr* to, socklen_t slen) 589 | { 590 | (static_cast(data))->do_send_to(p, len, to, slen); 591 | } 592 | 593 | void 594 | UtpDrv::UtpHandler::utp_read(void* data, const byte* bytes, size_t count) 595 | { 596 | (static_cast(data))->do_read(bytes, count); 597 | } 598 | 599 | void 600 | UtpDrv::UtpHandler::utp_write(void* data, byte* bytes, size_t count) 601 | { 602 | (static_cast(data))->do_write(bytes, count); 603 | } 604 | 605 | size_t 606 | UtpDrv::UtpHandler::utp_get_rb_size(void* data) 607 | { 608 | return (static_cast(data))->do_get_rb_size(); 609 | } 610 | 611 | void 612 | UtpDrv::UtpHandler::utp_state_change(void* data, int state) 613 | { 614 | (static_cast(data))->do_state_change(state); 615 | } 616 | 617 | void 618 | UtpDrv::UtpHandler::utp_error(void* data, int errcode) 619 | { 620 | (static_cast(data))->do_error(errcode); 621 | } 622 | 623 | void 624 | UtpDrv::UtpHandler::utp_overhead(void* data, bool send, size_t count, int type) 625 | { 626 | (static_cast(data))->do_overhead(send, count, type); 627 | } 628 | 629 | void 630 | UtpDrv::UtpHandler::utp_incoming(void* data, UTPSocket* utp) 631 | { 632 | (static_cast(data))->do_incoming(utp); 633 | } 634 | -------------------------------------------------------------------------------- /c_src/utp_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_UTP_HANDLER_H 2 | #define UTPDRV_UTP_HANDLER_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // utp_handler.h: base class for uTP port handlers 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "socket_handler.h" 28 | #include "utils.h" 29 | #include "drv_types.h" 30 | #include "write_queue.h" 31 | 32 | 33 | namespace UtpDrv { 34 | 35 | class UtpHandler : public SocketHandler 36 | { 37 | public: 38 | ~UtpHandler(); 39 | 40 | void outputv(ErlIOVec& ev); 41 | 42 | void stop(); 43 | 44 | void input_ready(); 45 | 46 | static void send_to(void* data, const byte* p, size_t len, 47 | const sockaddr* to, socklen_t slen); 48 | static void utp_read(void* data, const byte* bytes, size_t count); 49 | static void utp_write(void* data, byte* bytes, size_t count); 50 | static size_t utp_get_rb_size(void* data); 51 | static void utp_state_change(void* data, int state); 52 | static void utp_error(void* data, int errcode); 53 | static void utp_overhead(void* data, bool send, size_t count, int type); 54 | static void utp_incoming(void* data, UTPSocket* utp); 55 | 56 | protected: 57 | UtpHandler(int sock, const SockOpts& so); 58 | 59 | void set_utp_callbacks(); 60 | void set_empty_utp_callbacks(); 61 | 62 | ErlDrvSSizeT 63 | control(unsigned command, const char* buf, ErlDrvSizeT len, 64 | char** rbuf, ErlDrvSizeT rlen); 65 | 66 | virtual ErlDrvSSizeT 67 | peername(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 68 | 69 | virtual ErlDrvSSizeT 70 | close(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 71 | 72 | virtual ErlDrvSSizeT 73 | setopts(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 74 | 75 | virtual ErlDrvSSizeT 76 | recv(const char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rlen); 77 | 78 | ErlDrvSSizeT cancel_send(); 79 | ErlDrvSSizeT cancel_recv(); 80 | 81 | ErlDrvSSizeT new_controlling_process(); 82 | 83 | void close_utp(); 84 | 85 | void reset_waiting_recv(); 86 | 87 | virtual void do_send_to(const byte* p, size_t len, const sockaddr* to, 88 | socklen_t slen); 89 | virtual void do_read(const byte* bytes, size_t count); 90 | virtual void do_write(byte* bytes, size_t count); 91 | virtual size_t do_get_rb_size(); 92 | virtual void do_state_change(int state); 93 | virtual void do_error(int errcode); 94 | virtual void do_overhead(bool send, size_t count, int type); 95 | virtual void do_incoming(UTPSocket* utp) = 0; 96 | 97 | enum UtpPortStatus { 98 | not_connected, 99 | listening, 100 | connect_pending, 101 | connected, 102 | connect_failed, 103 | closing, 104 | destroying, 105 | stopped 106 | }; 107 | 108 | WriteQueue write_queue; 109 | Binary caller_ref; 110 | ErlDrvTermData caller; 111 | UTPSocket* utp; 112 | ErlDrvSizeT recv_len; 113 | UtpPortStatus status; 114 | int state, error_code; 115 | bool writable, sender_waiting, receiver_waiting, eof_seen; 116 | }; 117 | 118 | } 119 | 120 | 121 | 122 | // this block comment is for emacs, do not delete 123 | // Local Variables: 124 | // mode: c++ 125 | // c-file-style: "stroustrup" 126 | // c-file-offsets: ((innamespace . 0)) 127 | // End: 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /c_src/utpdrv.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // utpdrv.cc: driver entry points for libutp functions 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include 24 | #include "erl_driver.h" 25 | #include "globals.h" 26 | #include "main_handler.h" 27 | 28 | 29 | using namespace UtpDrv; 30 | 31 | static int 32 | utp_init() 33 | { 34 | return MainHandler::driver_init(); 35 | } 36 | 37 | static void 38 | utp_finish() 39 | { 40 | MainHandler::driver_finish(); 41 | } 42 | 43 | static ErlDrvData 44 | utp_start(ErlDrvPort port, char* command) 45 | { 46 | MainHandler* drv = new MainHandler(port); 47 | drv->start(); 48 | return reinterpret_cast(drv); 49 | } 50 | 51 | static void 52 | utp_stop(ErlDrvData drv_data) 53 | { 54 | Handler* drv = reinterpret_cast(drv_data); 55 | drv->stop(); 56 | } 57 | 58 | static void 59 | utp_check_timeouts(ErlDrvData drv_data) 60 | { 61 | MainHandler* drv = reinterpret_cast(drv_data); 62 | drv->check_utp_timeouts(); 63 | } 64 | 65 | static void 66 | utp_outputv(ErlDrvData drv_data, ErlIOVec *ev) 67 | { 68 | Handler* drv = reinterpret_cast(drv_data); 69 | drv->outputv(*ev); 70 | } 71 | 72 | static ErlDrvSSizeT 73 | utp_control(ErlDrvData drv_data, unsigned int command, 74 | char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) 75 | { 76 | Handler* drv = reinterpret_cast(drv_data); 77 | return drv->control(command, buf, len, rbuf, rlen); 78 | } 79 | 80 | static void 81 | utp_ready_input(ErlDrvData drv_data, ErlDrvEvent event) 82 | { 83 | MainHandler* drv = reinterpret_cast(drv_data); 84 | drv->ready_input(reinterpret_cast(event)); 85 | } 86 | 87 | static void 88 | utp_process_exit(ErlDrvData drv_data, ErlDrvMonitor* monitor) 89 | { 90 | MainHandler* drv = reinterpret_cast(drv_data); 91 | drv->process_exit(monitor); 92 | } 93 | 94 | static void 95 | utp_stop_select(ErlDrvEvent event, void*) 96 | { 97 | long fd = reinterpret_cast(event); 98 | UTPDRV_TRACER << "utp_stop_select: closing fd " << fd << UTPDRV_TRACE_ENDL; 99 | ::close(fd); 100 | } 101 | 102 | static ErlDrvEntry drv_entry = { 103 | utp_init, 104 | utp_start, 105 | utp_stop, 106 | 0, 107 | utp_ready_input, 108 | 0, 109 | UtpDrv::drv_name, 110 | utp_finish, 111 | 0, 112 | utp_control, 113 | utp_check_timeouts, 114 | utp_outputv, 115 | 0, 116 | 0, 117 | 0, 118 | 0, 119 | static_cast(ERL_DRV_EXTENDED_MARKER), 120 | ERL_DRV_EXTENDED_MAJOR_VERSION, 121 | ERL_DRV_EXTENDED_MINOR_VERSION, 122 | ERL_DRV_FLAG_USE_PORT_LOCKING, 123 | 0, 124 | utp_process_exit, 125 | utp_stop_select, 126 | }; 127 | 128 | extern "C" { 129 | DRIVER_INIT(utpdrv) 130 | { 131 | return &drv_entry; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /c_src/write_queue.cc: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------- 2 | // 3 | // write_queue.cc: queue for uTP write data 4 | // 5 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | // 7 | // This file is provided to you under the Apache License, 8 | // Version 2.0 (the "License"); you may not use this file 9 | // except in compliance with the License. You may obtain 10 | // a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, 15 | // software distributed under the License is distributed on an 16 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | // KIND, either express or implied. See the License for the 18 | // specific language governing permissions and limitations 19 | // under the License. 20 | // 21 | // ------------------------------------------------------------------- 22 | 23 | #include "write_queue.h" 24 | 25 | 26 | using namespace UtpDrv; 27 | 28 | UtpDrv::WriteQueue::WriteQueue() : sz(0), offset(0) 29 | { 30 | } 31 | 32 | UtpDrv::WriteQueue::WriteQueue(const WriteQueue& wq) : 33 | sz(wq.sz), offset(wq.offset) 34 | { 35 | BinaryQueue::const_iterator it = wq.queue.begin(); 36 | while (it != wq.queue.end()) { 37 | driver_binary_inc_refc(*it); 38 | queue.push_back(*it++); 39 | } 40 | } 41 | 42 | UtpDrv::WriteQueue::~WriteQueue() 43 | { 44 | BinaryQueue::iterator it = queue.begin(); 45 | while (it != queue.end()) { 46 | driver_free_binary(*it++); 47 | } 48 | } 49 | 50 | void 51 | UtpDrv::WriteQueue::push_back(ErlDrvBinary* bin) 52 | { 53 | queue.push_back(bin); 54 | sz += bin->orig_size; 55 | } 56 | 57 | void 58 | UtpDrv::WriteQueue::pop_bytes(void* buf, size_t count) 59 | { 60 | char* to = reinterpret_cast(buf); 61 | while (count > 0) { 62 | ErlDrvBinary* bin = queue.front(); 63 | ErlDrvSizeT bsize = bin->orig_size; 64 | if (bsize - offset > count) { 65 | memcpy(to, bin->orig_bytes+offset, count); 66 | offset += count; 67 | sz -= count; 68 | break; 69 | } else { 70 | size_t to_copy = bsize - offset; 71 | memcpy(to, bin->orig_bytes+offset, to_copy); 72 | queue.pop_front(); 73 | driver_free_binary(bin); 74 | offset = 0; 75 | count -= to_copy; 76 | sz -= to_copy; 77 | to += to_copy; 78 | } 79 | } 80 | } 81 | 82 | void 83 | UtpDrv::WriteQueue::clear() 84 | { 85 | queue.clear(); 86 | sz = offset = 0; 87 | } 88 | -------------------------------------------------------------------------------- /c_src/write_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef UTPDRV_WRITE_QUEUE_H 2 | #define UTPDRV_WRITE_QUEUE_H 3 | 4 | // ------------------------------------------------------------------- 5 | // 6 | // write_queue.h: queue for uTP write data 7 | // 8 | // Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 9 | // 10 | // This file is provided to you under the Apache License, 11 | // Version 2.0 (the "License"); you may not use this file 12 | // except in compliance with the License. You may obtain 13 | // a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, 18 | // software distributed under the License is distributed on an 19 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | // KIND, either express or implied. See the License for the 21 | // specific language governing permissions and limitations 22 | // under the License. 23 | // 24 | // ------------------------------------------------------------------- 25 | 26 | #include 27 | #include "erl_driver.h" 28 | 29 | 30 | namespace UtpDrv { 31 | 32 | class WriteQueue 33 | { 34 | public: 35 | WriteQueue(); 36 | WriteQueue(const WriteQueue&); 37 | ~WriteQueue(); 38 | 39 | void push_back(ErlDrvBinary* bin); 40 | void pop_bytes(void* to, size_t count); 41 | 42 | size_t size() const { return sz; } 43 | 44 | void clear(); 45 | 46 | private: 47 | typedef std::list BinaryQueue; 48 | BinaryQueue queue; 49 | size_t sz, offset; 50 | }; 51 | 52 | } 53 | 54 | 55 | 56 | // this block comment is for emacs, do not delete 57 | // Local Variables: 58 | // mode: c++ 59 | // c-file-style: "stroustrup" 60 | // c-file-offsets: ((innamespace . 0)) 61 | // End: 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | 4 | {port_specs, [{"priv/utpdrv.so", ["c_src/*.cc"]}]}. 5 | 6 | {port_env, [ 7 | {"DRV_CFLAGS", "$DRV_CFLAGS -Werror -I c_src -D_REENTRANT -O3"}, 8 | {"DRV_LDFLAGS", "$DRV_LDFLAGS -O3 -lstdc++ c_src/libutp/libutp.a"} 9 | ]}. 10 | 11 | {pre_hooks, [{compile, "c_src/build_deps.sh"}]}. 12 | 13 | {post_hooks, [{clean, "c_src/build_deps.sh clean"}]}. 14 | 15 | {cover_enabled, true}. 16 | -------------------------------------------------------------------------------- /src/gen_utp.app.src: -------------------------------------------------------------------------------- 1 | {application, gen_utp, 2 | [ 3 | {description, "uTP Protocol API and driver"}, 4 | {vsn, "0.3.0"}, 5 | {registered, [gen_utp, utpdrv]}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {mod, {gen_utp_app,[]}}, 11 | {env, [ 12 | ]} 13 | ]}. 14 | -------------------------------------------------------------------------------- /src/gen_utp.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp: uTP protocol 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp). 23 | -behaviour(gen_server). 24 | -author('Steve Vinoski '). 25 | 26 | -include("gen_utp_opts.hrl"). 27 | 28 | -ifdef(TEST). 29 | -include_lib("eunit/include/eunit.hrl"). 30 | -endif. 31 | 32 | -export([start_link/0, start/0, stop/0, 33 | listen/1, listen/2, accept/1, accept/2, async_accept/1, 34 | connect/2, connect/3, connect/4, 35 | close/1, send/2, recv/2, recv/3, 36 | sockname/1, peername/1, port/1, 37 | setopts/2, getopts/2, 38 | controlling_process/2]). 39 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 40 | terminate/2, code_change/3]). 41 | 42 | -record(state, { 43 | port :: port() 44 | }). 45 | 46 | %% driver command IDs 47 | -define(UTP_LISTEN, 1). 48 | -define(UTP_ACCEPT, 2). 49 | -define(UTP_CANCEL_ACCEPT, 3). 50 | -define(UTP_CONNECT_START, 4). 51 | -define(UTP_CONNECT_VALIDATE, 5). 52 | -define(UTP_CLOSE, 6). 53 | -define(UTP_SOCKNAME, 7). 54 | -define(UTP_PEERNAME, 8). 55 | -define(UTP_SETOPTS, 9). 56 | -define(UTP_GETOPTS, 10). 57 | -define(UTP_CANCEL_SEND, 11). 58 | -define(UTP_RECV, 12). 59 | -define(UTP_CANCEL_RECV, 13). 60 | 61 | -define(SHLIB, "utpdrv"). 62 | 63 | -type utpstate() :: #state{}. 64 | -type from() :: {pid(), any()}. 65 | -type utpaddr() :: inet:ip_address() | inet:hostname(). 66 | -type utpport() :: inet:port_number(). 67 | -type utpsock() :: port(). 68 | -type utpdata() :: binary() | list(). 69 | -export_type([utpaddr/0, utpport/0]). 70 | 71 | -spec start_link() -> {ok, pid()} | ignore | {error, term()}. 72 | start_link() -> 73 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 74 | 75 | -spec start() -> {ok, pid()} | ignore | {error, any()}. 76 | start() -> 77 | gen_server:start({local, ?MODULE}, ?MODULE, [], []). 78 | 79 | -spec stop() -> ok. 80 | stop() -> 81 | gen_server:cast(?MODULE, stop). 82 | 83 | -spec listen(utpport()) -> {ok, utpsock()} | {error, any()}. 84 | listen(Port) when Port >= 0, Port < 65536 -> 85 | listen(Port, []). 86 | 87 | -spec listen(utpport(), gen_utp_opts:utpopts()) -> {ok, utpsock()} | 88 | {error, any()}. 89 | listen(Port, Options) when Port >= 0, Port < 65536 -> 90 | ValidOpts = gen_utp_opts:validate([{port,Port}|Options]), 91 | Sock = erlang:open_port({spawn_driver, ?SHLIB}, [binary]), 92 | OptBin = options_to_binary(ValidOpts), 93 | Args = term_to_binary(OptBin), 94 | try binary_to_term(erlang:port_control(Sock, ?UTP_LISTEN, Args)) of 95 | ok -> 96 | {ok, Sock}; 97 | Error -> 98 | erlang:port_close(Sock), 99 | Error 100 | catch 101 | error:badarg -> 102 | erlang:port_close(Sock), 103 | {error, einval} 104 | end. 105 | 106 | -spec accept(utpsock()) -> {ok, utpsock()} | {error, any()}. 107 | accept(Sock) -> 108 | accept(Sock, infinity). 109 | 110 | -spec accept(utpsock(), timeout()) -> {ok, utpsock()} | {error, any()}. 111 | accept(Sock, Timeout) -> 112 | Ref = make_ref(), 113 | Args = term_to_binary(term_to_binary(Ref)), 114 | case async_accept(Sock, Args) of 115 | {ok, Ref} -> 116 | receive 117 | {utp_async, Sock, Ref, {ok, _}=Reply} -> 118 | Reply 119 | after 120 | Timeout -> 121 | try 122 | erlang:port_control(Sock, ?UTP_CANCEL_ACCEPT, Args), 123 | %% if the reply comes back while the cancel 124 | %% call completes, return it 125 | receive 126 | {utp_async, Sock, Ref, {ok, _}=Reply} -> 127 | Reply 128 | after 129 | 0 -> 130 | {error, etimedout} 131 | end 132 | catch 133 | error:badarg -> 134 | {error, einval} 135 | end 136 | end; 137 | Error -> 138 | Error 139 | end. 140 | 141 | -spec async_accept(utpsock()) -> {ok, reference()} | {error, any()}. 142 | async_accept(Sock) -> 143 | Ref = make_ref(), 144 | Args = term_to_binary(term_to_binary(Ref)), 145 | async_accept(Sock, Args). 146 | 147 | -spec async_accept(utpsock(), binary()) -> {ok, reference()} | {error, any()}. 148 | async_accept(Sock, Args) -> 149 | try 150 | Result = erlang:port_control(Sock, ?UTP_ACCEPT, Args), 151 | case binary_to_term(Result) of 152 | {ok, Bin} -> {ok, binary_to_term(Bin)}; 153 | Error -> Error 154 | end 155 | catch 156 | error:badarg -> 157 | {error, einval} 158 | end. 159 | 160 | -spec connect(utpaddr(), utpport()) -> {ok, utpsock()} | {error, any()}. 161 | connect(Addr, Port) when Port > 0, Port =< 65535 -> 162 | connect(Addr, Port, []). 163 | 164 | -spec connect(utpaddr(), utpport(), gen_utp_opts:utpopts()) -> {ok, utpsock()} | 165 | {error, any()}. 166 | connect(Addr, Port, Opts) when is_tuple(Addr), Port > 0, Port =< 65535 -> 167 | try inet_parse:ntoa(Addr) of 168 | ListAddr -> 169 | connect(ListAddr, Port, Opts) 170 | catch 171 | _:_ -> 172 | throw(badarg) 173 | end; 174 | connect(Addr, Port, Opts) when Port > 0, Port =< 65535 -> 175 | AddrStr = case inet:getaddr(Addr, inet) of 176 | {ok, AddrTuple} -> 177 | inet_parse:ntoa(AddrTuple); 178 | _ -> 179 | case inet:getaddr(Addr, inet6) of 180 | {ok, AddrTuple} -> 181 | inet_parse:ntoa(AddrTuple); 182 | Error -> 183 | Error 184 | end 185 | end, 186 | case AddrStr of 187 | {error, _}=Err -> 188 | Err; 189 | _ -> 190 | ValidOpts = gen_utp_opts:validate(Opts), 191 | Sock = erlang:open_port({spawn_driver, ?SHLIB}, [binary]), 192 | OptBin = options_to_binary(ValidOpts), 193 | Args = term_to_binary({AddrStr, Port, OptBin}), 194 | try 195 | Result = erlang:port_control(Sock, ?UTP_CONNECT_START, Args), 196 | case binary_to_term(Result) of 197 | ok -> 198 | validate_connect(Sock); 199 | ConnectError -> 200 | erlang:port_close(Sock), 201 | ConnectError 202 | end 203 | catch 204 | error:badarg -> 205 | erlang:port_close(Sock), 206 | {error, closed} 207 | end 208 | end. 209 | 210 | -spec connect(utpaddr(), utpport(), gen_utp_opts:utpopts(), timeout()) -> 211 | {ok, utpsock()} | {error, any()}. 212 | connect(Addr, Port, Opts, _Timeout) when Port > 0, Port =< 65535 -> 213 | %% uTP doesn't give us any control over connect timeouts, 214 | %% so ignore the Timeout parameter for now 215 | connect(Addr, Port, Opts). 216 | 217 | -spec close(utpsock()) -> ok. 218 | close(Sock) -> 219 | try 220 | Ref = make_ref(), 221 | Args = term_to_binary(term_to_binary(Ref)), 222 | Result = erlang:port_control(Sock, ?UTP_CLOSE, Args), 223 | case binary_to_term(Result) of 224 | wait -> 225 | receive 226 | {Ref, ok} -> ok 227 | end; 228 | ok -> 229 | ok 230 | end 231 | after 232 | true = erlang:port_close(Sock) 233 | end, 234 | ok. 235 | 236 | -spec send(utpsock(), iodata()) -> ok | {error, any()}. 237 | send(Sock, Data) -> 238 | send(Sock, Data, os:timestamp()). 239 | 240 | -spec recv(utpsock(), non_neg_integer()) -> {ok, utpdata()} | 241 | {error, any()}. 242 | recv(Sock, Length) -> 243 | recv(Sock, Length, infinity). 244 | 245 | -spec recv(utpsock(), non_neg_integer(), timeout()) -> {ok, utpdata()} | 246 | {error, any()}. 247 | recv(Sock, Length, Timeout) -> 248 | try 249 | Ref = make_ref(), 250 | Args = term_to_binary({Length, term_to_binary(Ref)}), 251 | Result = erlang:port_control(Sock, ?UTP_RECV, Args), 252 | case binary_to_term(Result) of 253 | wait -> 254 | receive 255 | {Ref, Reply} -> 256 | Reply 257 | after 258 | Timeout -> 259 | erlang:port_control(Sock, ?UTP_CANCEL_RECV, <<>>), 260 | %% if the reply comes back while the cancel 261 | %% call completes, return it 262 | receive 263 | {Ref, Reply} -> 264 | Reply 265 | after 266 | 0 -> 267 | {error, etimedout} 268 | end 269 | end; 270 | Reply -> 271 | Reply 272 | end 273 | catch 274 | error:badarg -> 275 | {error, closed} 276 | end. 277 | 278 | -spec sockname(utpsock()) -> {ok, {utpaddr(), utpport()}} | {error, any()}. 279 | sockname(Sock) -> 280 | try 281 | Result = erlang:port_control(Sock, ?UTP_SOCKNAME, <<>>), 282 | case binary_to_term(Result) of 283 | {ok, {AddrStr, Port}} -> 284 | {ok, Addr} = inet_parse:address(AddrStr), 285 | {ok, {Addr, Port}}; 286 | Error -> 287 | Error 288 | end 289 | catch 290 | error:badarg -> 291 | {error,einval} 292 | end. 293 | 294 | -spec peername(utpsock()) -> {ok, {utpaddr(), utpport()}} | {error, any()}. 295 | peername(Sock) -> 296 | try 297 | Result = erlang:port_control(Sock, ?UTP_PEERNAME, <<>>), 298 | case binary_to_term(Result) of 299 | {ok, {AddrStr, Port}} -> 300 | {ok, Addr} = inet_parse:address(AddrStr), 301 | {ok, {Addr, Port}}; 302 | Error -> 303 | Error 304 | end 305 | catch 306 | error:badarg -> 307 | {error,einval} 308 | end. 309 | 310 | -spec port(utpsock()) -> {ok, utpport()} | {error, any()}. 311 | port(Sock) -> 312 | case sockname(Sock) of 313 | {ok, {_Addr, Port}} -> 314 | {ok, Port}; 315 | Error -> 316 | Error 317 | end. 318 | 319 | -spec setopts(utpsock(), gen_utp_opts:utpopts()) -> ok | {error, any()}. 320 | setopts(Sock, Opts) when is_list(Opts) -> 321 | ValidOpts = gen_utp_opts:validate(Opts), 322 | OptCheck = fun(Opt) -> Opt =/= undefined end, 323 | case lists:any(OptCheck, [ValidOpts#utp_options.ip, 324 | ValidOpts#utp_options.port, 325 | ValidOpts#utp_options.family]) of 326 | true -> 327 | erlang:error(badarg, Opts); 328 | false -> 329 | OptBin = options_to_binary(ValidOpts), 330 | Args = term_to_binary(OptBin), 331 | try 332 | Result = erlang:port_control(Sock, ?UTP_SETOPTS, Args), 333 | binary_to_term(Result) 334 | catch 335 | error:badarg -> 336 | {error, closed} 337 | end 338 | end. 339 | 340 | -spec getopts(utpsock(), gen_utp_opts:utpgetoptnames()) -> 341 | {ok, gen_utp_opts:utpopts()} | {error, any()}. 342 | getopts(Sock, OptNames) when is_list(OptNames) -> 343 | case gen_utp_opts:validate_names(OptNames) of 344 | {ok, OptNameBin} -> 345 | Args = term_to_binary(OptNameBin), 346 | try 347 | Result = erlang:port_control(Sock, ?UTP_GETOPTS, Args), 348 | binary_to_term(Result) 349 | catch 350 | error:badarg -> 351 | {error, closed} 352 | end; 353 | Error -> 354 | Error 355 | end. 356 | 357 | -spec controlling_process(utpsock(), pid()) -> ok | {error, any()}. 358 | controlling_process(Sock, NewOwner) -> 359 | case erlang:port_info(Sock, connected) of 360 | {connected, NewOwner} -> 361 | ok; 362 | {connected, Pid} when Pid =/= self() -> 363 | {error, not_owner}; 364 | undefined -> 365 | {error, einval}; 366 | _ -> 367 | try erlang:port_connect(Sock, NewOwner) of 368 | true -> 369 | unlink(Sock), 370 | ok 371 | catch 372 | error:Reason -> 373 | {error, Reason} 374 | end 375 | end. 376 | 377 | %% gen_server functions 378 | 379 | -spec init([]) -> ignore | {ok, utpstate()} | {stop, any()}. 380 | init([]) -> 381 | process_flag(trap_exit, true), 382 | PrivDir = case code:priv_dir(?MODULE) of 383 | {error, bad_name} -> 384 | EbinDir = filename:dirname(code:which(?MODULE)), 385 | AppPath = filename:dirname(EbinDir), 386 | filename:join(AppPath, "priv"); 387 | Path -> 388 | Path 389 | end, 390 | LoadResult = case erl_ddll:load_driver(PrivDir, ?SHLIB) of 391 | ok -> ok; 392 | {error, already_loaded} -> ok; 393 | {error, LoadError} -> 394 | LoadErrorStr = erl_ddll:format_error(LoadError), 395 | EStr = lists:flatten( 396 | io_lib:format("could not load driver ~s: ~p", 397 | [?SHLIB, LoadErrorStr])), 398 | {stop, EStr} 399 | end, 400 | case LoadResult of 401 | ok -> 402 | Port = erlang:open_port({spawn, ?SHLIB}, [binary]), 403 | register(utpdrv, Port), 404 | {ok, #state{port=Port}}; 405 | Error -> 406 | Error 407 | end. 408 | 409 | -spec handle_call(any(), from(), utpstate()) -> {reply, any(), utpstate()}. 410 | handle_call(_Request, _From, State) -> 411 | {reply, ok, State}. 412 | 413 | -spec handle_cast(any(), utpstate()) -> {noreply, utpstate()} | 414 | {stop, any(), utpstate()}. 415 | handle_cast(stop, State) -> 416 | {stop, normal, State}; 417 | handle_cast(_Msg, State) -> 418 | {noreply, State}. 419 | 420 | -spec handle_info(any(), utpstate()) -> {noreply, utpstate()}. 421 | handle_info(_Info, State) -> 422 | {noreply, State}. 423 | 424 | -spec terminate(any(), utpstate()) -> ok. 425 | terminate(_Reason, #state{port=Port}) -> 426 | unregister(utpdrv), 427 | erlang:port_close(Port), 428 | ok. 429 | 430 | -spec code_change(any(), utpstate(), any()) -> {ok, utpstate()}. 431 | code_change(_OldVsn, State, _Extra) -> 432 | {ok, State}. 433 | 434 | 435 | %% Internal functions 436 | 437 | %% 438 | %% The send/3 function takes the socket and data to be sent, as well as a 439 | %% timestamp of when the operation was started. The send timeout is set 440 | %% when the socket is created and is stored in the driver. This function 441 | %% uses the starting timestamp to help enforce any send timeout. 442 | %% 443 | %% If the socket is writable, the driver takes the data and makes sure it 444 | %% gets sent. If it's not writable, the driver sends a message telling the 445 | %% sender to wait. If a send timeout is set on the socket, it includes that 446 | %% timeout in the message. The process then awaits a message from the 447 | %% driver telling it to retry. If a timeout occurs before the retry message 448 | %% arrives, an error is returned to the caller. When a retry arrives, check 449 | %% the start timestamp to ensure a timeout hasn't occurred, and if not try 450 | %% the send operation again. 451 | %% 452 | -spec send(utpsock(), iodata(), erlang:timestamp()) -> ok | {error, any()}. 453 | send(Sock, Data, Start) -> 454 | try 455 | true = erlang:port_command(Sock, Data), 456 | receive 457 | {utp_reply, Sock, wait} -> 458 | receive 459 | {utp_reply, Sock, retry} -> 460 | send(Sock, Data, Start) 461 | end; 462 | {utp_reply, Sock, {wait, Timeout}} -> 463 | Current = os:timestamp(), 464 | TDiff = timer:now_diff(Current, Start) div 1000, 465 | case TDiff >= Timeout of 466 | true -> 467 | {error, etimedout}; 468 | false -> 469 | {StartMega, StartSec, StartMicro} = Start, 470 | Done = {StartMega, StartSec, Timeout*1000+StartMicro}, 471 | Wait = timer:now_diff(Done, Current) div 1000, 472 | receive 473 | {utp_reply, Sock, retry} -> 474 | send(Sock, Data, Start) 475 | after 476 | Wait -> 477 | erlang:port_control(Sock,?UTP_CANCEL_SEND,<<>>), 478 | %% if the reply comes back while the cancel 479 | %% call completes, return it 480 | receive 481 | {utp_reply, Sock, ok} -> 482 | ok; 483 | {utp_reply, Sock, _} -> 484 | {error, etimedout} 485 | after 486 | 0 -> 487 | {error, etimedout} 488 | end 489 | end 490 | end; 491 | {utp_reply, Sock, Result} -> 492 | Result 493 | end 494 | catch 495 | error:badarg -> 496 | {error, einval} 497 | end. 498 | 499 | -spec validate_connect(utpsock()) -> {ok, utpsock()} | {error, any()}. 500 | validate_connect(Sock) -> 501 | Ref = make_ref(), 502 | Args = term_to_binary(term_to_binary(Ref)), 503 | Bin = erlang:port_control(Sock, ?UTP_CONNECT_VALIDATE, Args), 504 | case binary_to_term(Bin) of 505 | ok -> 506 | {ok, Sock}; 507 | wait -> 508 | receive 509 | {Ref, ok} -> 510 | {ok, Sock}; 511 | {Ref, Err} -> 512 | close(Sock), 513 | Err 514 | end; 515 | Error -> 516 | erlang:port_close(Sock), 517 | Error 518 | end. 519 | 520 | -spec options_to_binary(#utp_options{}) -> binary(). 521 | options_to_binary(UtpOpts) -> 522 | list_to_binary([ 523 | case UtpOpts#utp_options.mode of 524 | undefined -> 525 | <<>>; 526 | list -> 527 | <>; 528 | binary -> 529 | <> 530 | end, 531 | case UtpOpts#utp_options.family of 532 | undefined -> 533 | <<>>; 534 | inet -> 535 | <<>>; 536 | inet6 -> 537 | <> 538 | end, 539 | case UtpOpts#utp_options.ip of 540 | undefined -> 541 | <<>>; 542 | AddrStr -> 543 | [?UTP_IP_OPT, AddrStr, 0] 544 | end, 545 | case UtpOpts#utp_options.port of 546 | undefined -> 547 | <<>>; 548 | Port -> 549 | <> 550 | end, 551 | case UtpOpts#utp_options.send_tmout of 552 | undefined -> 553 | <<>>; 554 | infinity -> 555 | <>; 556 | Tm -> 557 | <> 558 | end, 559 | case UtpOpts#utp_options.active of 560 | undefined -> 561 | <<>>; 562 | false -> 563 | <>; 564 | once -> 565 | <>; 566 | true -> 567 | <> 568 | end, 569 | case UtpOpts#utp_options.packet of 570 | undefined -> 571 | <<>>; 572 | Val -> 573 | <> 574 | end, 575 | case UtpOpts#utp_options.header of 576 | undefined -> 577 | <<>>; 578 | HdrSize -> 579 | <> 580 | end, 581 | case UtpOpts#utp_options.sndbuf of 582 | undefined -> 583 | <<>>; 584 | SndBuf -> 585 | <> 586 | end, 587 | case UtpOpts#utp_options.recbuf of 588 | undefined -> 589 | <<>>; 590 | RecBuf -> 591 | <> 592 | end 593 | ]). 594 | -------------------------------------------------------------------------------- /src/gen_utp_app.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_app: uTP protocol application 4 | %% 5 | %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_app). 23 | -behaviour(application). 24 | -export([start/2, stop/1]). 25 | 26 | start(_StartType, _StartArgs) -> 27 | gen_utp_sup:start_link(). 28 | 29 | stop(_State) -> 30 | ok. 31 | -------------------------------------------------------------------------------- /src/gen_utp_opts.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_opts: socket options for uTP protocol 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_opts). 23 | -author('Steve Vinoski '). 24 | 25 | -include("gen_utp_opts.hrl"). 26 | 27 | -ifdef(TEST). 28 | -include_lib("eunit/include/eunit.hrl"). 29 | -endif. 30 | 31 | -export([validate/1, validate_names/1]). 32 | 33 | -type utpmode() :: list | binary. 34 | -type utptimeout() :: pos_integer() | infinity. 35 | -type utpfamily() :: inet | inet6. 36 | -type utpipopt() :: {ip,gen_utp:utpaddr()} | {ifaddr,gen_utp:utpaddr()}. 37 | -type utpportopt() :: {port,gen_utp:utpport()}. 38 | -type utpmodeopt() :: {mode,utpmode()} | utpmode(). 39 | -type utpsendopt() :: {send_timeout,utptimeout()}. 40 | -type utpactive() :: once | boolean(). 41 | -type utpactiveopt() :: {active, utpactive()}. 42 | -type utppacketsize() :: raw | 0 | 1 | 2 | 4. 43 | -type utppacketopt() :: {packet, utppacketsize()}. 44 | -type utpheadersize() :: pos_integer(). 45 | -type utpheaderopt() :: {header, utpheadersize()}. 46 | -type utpbufsize() :: pos_integer(). 47 | -type utpbuftype() :: sndbuf | recbuf. 48 | -type utpsetbuf() :: {utpbuftype(), utpbufsize()}. 49 | -type utpopt() :: utpipopt() | utpportopt() | utpmodeopt() | 50 | utpfamily() | utpsendopt() | utpactiveopt() | 51 | utppacketopt() | utpheaderopt() | utpsetbuf(). 52 | -type utpopts() :: [utpopt()]. 53 | -type utpgetoptname() :: active | mode | send_timeout | 54 | packet | header | utpbuftype(). 55 | -type utpgetoptnames() :: [utpgetoptname()]. 56 | -export_type([utpactive/0, utpbufsize/0, utpfamily/0, utpgetoptnames/0, 57 | utpheadersize/0, utpmode/0, utpopts/0, utppacketsize/0, 58 | utptimeout/0]). 59 | 60 | -spec validate(utpopts()) -> #utp_options{}. 61 | validate(Opts) when is_list(Opts) -> 62 | validate(Opts, #utp_options{}). 63 | 64 | -spec validate_names(utpgetoptnames()) -> {ok, binary()} | {error, any()}. 65 | validate_names(OptNames) when is_list(OptNames) -> 66 | Result = lists:foldl(fun(_, {error, _}=Error) -> 67 | Error; 68 | (active, Bin) -> 69 | <>; 70 | (mode, Bin) -> 71 | <>; 72 | (send_timeout, Bin) -> 73 | <>; 74 | (packet, Bin) -> 75 | <>; 76 | (header, Bin) -> 77 | <>; 78 | (sndbuf, Bin) -> 79 | <>; 80 | (recbuf, Bin) -> 81 | <>; 82 | (_, _) -> 83 | {error, einval} 84 | end, <<>>, OptNames), 85 | case Result of 86 | {error, _}=Error -> 87 | Error; 88 | BinOpts -> 89 | {ok, BinOpts} 90 | end. 91 | 92 | %% Internal functions 93 | 94 | -spec validate(utpopts(), #utp_options{}) -> #utp_options{}. 95 | validate([{mode,Mode}|Opts], UtpOpts) when Mode =:= list; Mode =:= binary -> 96 | validate(Opts, UtpOpts#utp_options{mode=Mode}); 97 | validate([{mode,_}=Mode|_], _) -> 98 | erlang:error(badarg, [Mode]); 99 | validate([binary|Opts], UtpOpts) -> 100 | validate(Opts, UtpOpts#utp_options{mode=binary}); 101 | validate([list|Opts], UtpOpts) -> 102 | validate(Opts, UtpOpts#utp_options{mode=list}); 103 | validate([{ip,IpAddr}|Opts], UtpOpts) when is_tuple(IpAddr) -> 104 | validate(Opts, validate_ipaddr(IpAddr, UtpOpts)); 105 | validate([{ip,IpAddr}|Opts], UtpOpts) when is_list(IpAddr) -> 106 | validate(Opts, validate_ipaddr(IpAddr, UtpOpts)); 107 | validate([{ifaddr,IpAddr}|Opts], UtpOpts) when is_tuple(IpAddr) -> 108 | validate(Opts, validate_ipaddr(IpAddr, UtpOpts)); 109 | validate([{ifaddr,IpAddr}|Opts], UtpOpts) when is_list(IpAddr) -> 110 | validate(Opts, validate_ipaddr(IpAddr, UtpOpts)); 111 | validate([{port,Port}|Opts], UtpOpts) 112 | when is_integer(Port), Port >= 0, Port < 65536 -> 113 | validate(Opts, UtpOpts#utp_options{port=Port}); 114 | validate([{port,_}=Port|_], _) -> 115 | erlang:error(badarg, [Port]); 116 | validate([inet|Opts], UtpOpts) -> 117 | case UtpOpts#utp_options.ip of 118 | undefined -> 119 | validate(Opts, UtpOpts#utp_options{family=inet}); 120 | _ -> 121 | case UtpOpts#utp_options.family of 122 | inet -> 123 | validate(Opts, UtpOpts); 124 | _ -> 125 | erlang:error(badarg) 126 | end 127 | end; 128 | validate([inet6|Opts], UtpOpts) -> 129 | case UtpOpts#utp_options.ip of 130 | undefined -> 131 | validate(Opts, UtpOpts#utp_options{family=inet6}); 132 | _ -> 133 | case UtpOpts#utp_options.family of 134 | inet6 -> 135 | validate(Opts, UtpOpts); 136 | _ -> 137 | erlang:error(badarg) 138 | end 139 | end; 140 | validate([{send_timeout,Tm}|Opts], UtpOpts) 141 | when Tm =:= infinity; is_integer(Tm), Tm > 0 -> 142 | validate(Opts, UtpOpts#utp_options{send_tmout=Tm}); 143 | validate([{send_timeout,_}=ST|_], _) -> 144 | erlang:error(badarg, [ST]); 145 | validate([{active,Active}|Opts], UtpOpts) 146 | when is_boolean(Active); Active =:= once -> 147 | validate(Opts, UtpOpts#utp_options{active=Active}); 148 | validate([{active,_}=Active|_], _) -> 149 | erlang:error(badarg, [Active]); 150 | validate([{packet,raw}|Opts], UtpOpts) -> 151 | validate([{packet,0}|Opts], UtpOpts); 152 | validate([{packet,P}|Opts], UtpOpts) 153 | when P == 0; P == 1; P == 2; P == 4 -> 154 | validate(Opts, UtpOpts#utp_options{packet=P}); 155 | validate([{packet,_}=Packet|_], _) -> 156 | erlang:error(badarg, [Packet]); 157 | validate([{header,Sz}|Opts], UtpOpts) 158 | when is_integer(Sz), Sz > 0, Sz < 65536 -> 159 | validate(Opts, UtpOpts#utp_options{header=Sz}); 160 | validate([{header,_}=Hdr|_], _) -> 161 | erlang:error(badarg, [Hdr]); 162 | validate([{sndbuf,Sz}|Opts], UtpOpts) when is_integer(Sz), Sz > 0 -> 163 | validate(Opts, UtpOpts#utp_options{sndbuf=Sz}); 164 | validate([{sndbuf,_}=Hdr|_], _) -> 165 | erlang:error(badarg, [Hdr]); 166 | validate([{recbuf,Sz}|Opts], UtpOpts) when is_integer(Sz), Sz > 0 -> 167 | validate(Opts, UtpOpts#utp_options{recbuf=Sz}); 168 | validate([{recbuf,_}=Hdr|_], _) -> 169 | erlang:error(badarg, [Hdr]); 170 | validate([], UtpOpts) -> 171 | case UtpOpts#utp_options.header of 172 | undefined -> 173 | UtpOpts; 174 | Size -> 175 | %% {header,Size} valid only in binary mode 176 | case UtpOpts#utp_options.mode of 177 | binary -> 178 | UtpOpts; 179 | _ -> 180 | erlang:error(badarg, [{mode,list},{header,Size}]) 181 | end 182 | end. 183 | 184 | validate_ipaddr(IpAddr, UtpOpts) when is_tuple(IpAddr) -> 185 | try inet_parse:ntoa(IpAddr) of 186 | ListAddr -> 187 | validate_ipaddr(ListAddr, UtpOpts) 188 | catch 189 | _:_ -> 190 | erlang:error(badarg, [IpAddr]) 191 | end; 192 | validate_ipaddr(IpAddr, UtpOpts) when is_list(IpAddr) -> 193 | case inet:getaddr(IpAddr, inet) of 194 | {ok, AddrTuple} -> 195 | UtpOpts#utp_options{family=inet, ip=inet_parse:ntoa(AddrTuple)}; 196 | _ -> 197 | case inet:getaddr(IpAddr, inet6) of 198 | {ok, AddrTuple} -> 199 | UtpOpts#utp_options{family=inet6, 200 | ip=inet_parse:ntoa(AddrTuple)}; 201 | _Error -> 202 | erlang:error(badarg, [IpAddr]) 203 | end 204 | end. 205 | 206 | 207 | -ifdef(TEST). 208 | 209 | validate_test() -> 210 | #utp_options{ip=IP1,family=Family1} = validate([{ip,"::"},inet6]), 211 | ?assertMatch("::",IP1), 212 | ?assertMatch(inet6,Family1), 213 | #utp_options{ip=IP2,family=Family2} = validate([{ip,"127.0.0.1"},inet]), 214 | ?assertMatch("127.0.0.1",IP2), 215 | ?assertMatch(inet,Family2), 216 | #utp_options{family=Family3} = validate([inet]), 217 | ?assertMatch(inet,Family3), 218 | #utp_options{family=Family4} = validate([inet6]), 219 | ?assertMatch(inet6,Family4), 220 | ?assertMatch(#utp_options{packet=0}, validate([{packet,raw}])), 221 | ?assertMatch(#utp_options{packet=0}, validate([{packet,0}])), 222 | ?assertMatch(#utp_options{packet=1}, validate([{packet,1}])), 223 | ?assertMatch(#utp_options{packet=2}, validate([{packet,2}])), 224 | ?assertMatch(#utp_options{packet=4}, validate([{packet,4}])), 225 | ?assertMatch(#utp_options{header=1}, validate([binary,{header,1}])), 226 | ?assertMatch(#utp_options{sndbuf=16384}, validate([{sndbuf,16384}])), 227 | ?assertMatch(#utp_options{recbuf=32768}, validate([{recbuf,32768}])), 228 | 229 | ?assertException(error, badarg, validate([{mode,bin}])), 230 | ?assertException(error, badarg, validate([{port,65536}])), 231 | ?assertException(error, badarg, validate([{ip,{127,0,0,1}},inet6])), 232 | ?assertException(error, badarg, validate([{ip,"::"},inet])), 233 | ?assertException(error, badarg, validate([{send_timeout,0}])), 234 | ?assertException(error, badarg, validate([{active,never}])), 235 | ?assertException(error, badarg, validate([{ip,{1,2,3,4,5}}])), 236 | ?assertException(error, badarg, validate([{ip,"1.2.3.4.5"}])), 237 | ?assertException(error, badarg, validate([{packet,3}])), 238 | ?assertException(error, badarg, validate([{header,1}])), 239 | ?assertException(error, badarg, validate([{sndbuf,0}])), 240 | ?assertException(error, badarg, validate([{recbuf,0}])), 241 | ok. 242 | 243 | validate_names_test() -> 244 | OkOpts = [active,mode,send_timeout,packet,header,sndbuf,recbuf], 245 | ?assertMatch({ok,_}, validate_names(OkOpts)), 246 | ?assertMatch({error, einval}, validate_names([list])), 247 | ?assertMatch({error, einval}, validate_names([binary])), 248 | ?assertMatch({error, einval}, validate_names([binary,list])), 249 | ?assertMatch({error, einval}, validate_names([inet])), 250 | ?assertMatch({error, einval}, validate_names([inet6])), 251 | ?assertMatch({error, einval}, validate_names([ip])), 252 | ?assertMatch({error, einval}, validate_names([ifaddr])), 253 | ?assertMatch({error, einval}, validate_names([port])), 254 | ok. 255 | 256 | -endif. 257 | -------------------------------------------------------------------------------- /src/gen_utp_opts.hrl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_opts: socket options for uTP protocol 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | 23 | %% IDs for binary-encoded options 24 | -define(UTP_IP_OPT, 1). 25 | -define(UTP_PORT_OPT, 2). 26 | -define(UTP_LIST_OPT, 3). 27 | -define(UTP_BINARY_OPT, 4). 28 | -define(UTP_MODE_OPT, 5). 29 | -define(UTP_INET_OPT, 6). 30 | -define(UTP_INET6_OPT, 7). 31 | -define(UTP_SEND_TMOUT_OPT, 8). 32 | -define(UTP_SEND_TMOUT_INFINITE_OPT, 9). 33 | -define(UTP_ACTIVE_OPT, 10). 34 | -define(UTP_PACKET_OPT, 11). 35 | -define(UTP_HEADER_OPT, 12). 36 | -define(UTP_SNDBUF_OPT, 13). 37 | -define(UTP_RECBUF_OPT, 14). 38 | 39 | %% IDs for values of the active option 40 | -define(UTP_ACTIVE_FALSE, 0). 41 | -define(UTP_ACTIVE_ONCE, 1). 42 | -define(UTP_ACTIVE_TRUE, 2). 43 | 44 | -record(utp_options, { 45 | mode :: gen_utp_opts:utpmode(), 46 | ip :: string(), 47 | port :: gen_utp:utpport(), 48 | family :: gen_utp_opts:utpfamily(), 49 | send_tmout :: gen_utp_opts:utptimeout(), 50 | active :: gen_utp_opts:utpactive(), 51 | packet :: gen_utp_opts:utppacketsize(), 52 | header :: gen_utp_opts:utpheadersize(), 53 | sndbuf :: gen_utp_opts:utpbufsize(), 54 | recbuf :: gen_utp_opts:utpbufsize() 55 | }). 56 | -------------------------------------------------------------------------------- /src/gen_utp_sup.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_sup: uTP protocol supervisor 4 | %% 5 | %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_sup). 23 | -behaviour(supervisor). 24 | -export([start_link/0]). 25 | -export([init/1]). 26 | 27 | -define(CHILD(I, Type), {I,{I,start_link,[]},permanent,5000,Type,[I]}). 28 | 29 | start_link() -> 30 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 31 | 32 | init([]) -> 33 | {ok, {{one_for_one, 10, 10}, [?CHILD(gen_utp, worker)]}}. 34 | -------------------------------------------------------------------------------- /test/gen_utp_active_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_active_tests: active mode tests for gen_utp 4 | %% 5 | %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_active_tests). 23 | -author('Steve Vinoski '). 24 | 25 | -include_lib("eunit/include/eunit.hrl"). 26 | -include("gen_utp_tests_setup.hrl"). 27 | 28 | active_test_() -> 29 | {setup, 30 | fun setup/0, 31 | fun cleanup/1, 32 | fun(_) -> 33 | {inorder, 34 | [{"active false test", 35 | fun active_false/0}, 36 | {"active once test", 37 | fun active_once/0}, 38 | {"active true test", 39 | fun active_true/0} 40 | ]} 41 | end}. 42 | 43 | active_false() -> 44 | {ok, LSock} = gen_utp:listen(0, [{mode,binary},{active,false}]), 45 | {ok, Ref} = gen_utp:async_accept(LSock), 46 | {ok, {_, Port}} = gen_utp:sockname(LSock), 47 | {ok, Sock} = gen_utp:connect("localhost", Port, [binary]), 48 | receive 49 | {utp_async, LSock, Ref, {ok, ASock}} -> 50 | Words = ["We", "make", "Riak,", "the", "most", "powerful", 51 | "open-source,", "distributed", "database", "you'll", 52 | "ever", "put", "into", "production."], 53 | ?assertEqual([ok || _ <- Words], 54 | [gen_utp:send(Sock, Data) || Data <- Words]), 55 | AllBin = list_to_binary(Words), 56 | timer:sleep(500), 57 | ?assertMatch({ok, AllBin}, gen_utp:recv(ASock, 0, 2000)), 58 | ok = gen_utp:close(ASock), 59 | ok = gen_utp:close(Sock); 60 | {utp_async, LSock, Ref, Error} -> 61 | exit({utp_async, Error}) 62 | after 63 | 3000 -> exit(failure) 64 | end, 65 | ok = gen_utp:close(LSock), 66 | ok. 67 | 68 | active_once() -> 69 | {ok, LSock} = gen_utp:listen(0, [{mode,list},{active,false},{packet,1}]), 70 | {ok, Ref} = gen_utp:async_accept(LSock), 71 | {ok, {_, Port}} = gen_utp:sockname(LSock), 72 | {ok, Sock} = gen_utp:connect("localhost", Port, [binary,{packet,1}]), 73 | receive 74 | {utp_async, LSock, Ref, {ok, ASock}} -> 75 | Words = ["We", "make", "Riak,", "the", "most", "powerful", 76 | "open-source,", "distributed", "database", "you'll", 77 | "ever", "put", "into", "production."], 78 | ?assertEqual([ok || _ <- Words], 79 | [gen_utp:send(Sock, Data) || Data <- Words]), 80 | timer:sleep(500), 81 | F = fun(Fn, Acc) -> 82 | ok = gen_utp:setopts(ASock, [{active,once}]), 83 | receive 84 | {utp, ASock, Word} -> 85 | case lists:reverse(Word) of 86 | [$.|_] -> 87 | lists:reverse([Word|Acc]); 88 | _ -> 89 | Fn(Fn, [Word|Acc]) 90 | end 91 | after 92 | 500 -> 93 | lists:reverse(Acc) 94 | end 95 | end, 96 | ?assertMatch(Words, F(F,[])), 97 | ok = gen_utp:close(ASock), 98 | ok = gen_utp:close(Sock); 99 | {utp_async, LSock, Ref, Error} -> 100 | exit({utp_async, Error}) 101 | after 102 | 3000 -> exit(failure) 103 | end, 104 | ok = gen_utp:close(LSock), 105 | ok. 106 | 107 | active_true() -> 108 | {ok, LSock} = gen_utp:listen(0, [{mode,binary},{active,false}]), 109 | {ok, Ref} = gen_utp:async_accept(LSock), 110 | {ok, {_, Port}} = gen_utp:sockname(LSock), 111 | {ok, Sock} = gen_utp:connect("localhost", Port, [binary]), 112 | receive 113 | {utp_async, LSock, Ref, {ok, ASock}} -> 114 | ok = gen_utp:setopts(ASock, [{active,true}]), 115 | Words = ["We", "make", "Riak,", "the", "most", "powerful", 116 | "open-source,", "distributed", "database", "you'll", 117 | "ever", "put", "into", "production."], 118 | ?assertEqual([ok || _ <- Words], 119 | [gen_utp:send(Sock, Data) || Data <- Words]), 120 | AllBin = list_to_binary(Words), 121 | timer:sleep(500), 122 | F = fun(Fn, Bin) -> 123 | receive 124 | {utp, ASock, Data} -> 125 | Fn(Fn, <>) 126 | after 127 | 500 -> 128 | Bin 129 | end 130 | end, 131 | ?assertMatch(AllBin, F(F,<<>>)), 132 | ok = gen_utp:close(ASock), 133 | ok = gen_utp:close(Sock); 134 | {utp_async, LSock, Ref, Error} -> 135 | exit({utp_async, Error}) 136 | after 137 | 3000 -> exit(failure) 138 | end, 139 | ok = gen_utp:close(LSock), 140 | ok. 141 | -------------------------------------------------------------------------------- /test/gen_utp_client_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_client_tests: client tests for gen_utp 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_client_tests). 23 | -author('Steve Vinoski '). 24 | 25 | -include_lib("eunit/include/eunit.hrl"). 26 | -include("gen_utp_tests_setup.hrl"). 27 | 28 | client_timeout_test_() -> 29 | {setup, 30 | fun setup/0, 31 | fun cleanup/1, 32 | fun(_) -> 33 | {timeout, 15, 34 | [{"client timeout test", 35 | ?_test( 36 | begin 37 | {ok, LSock} = gen_utp:listen(0), 38 | {ok, {_, Port}} = gen_utp:sockname(LSock), 39 | ok = gen_utp:close(LSock), 40 | ?assertMatch({error, etimedout}, 41 | gen_utp:connect("localhost", Port)) 42 | end)} 43 | ]} 44 | end}. 45 | 46 | client_server_test_() -> 47 | {setup, 48 | fun setup/0, 49 | fun cleanup/1, 50 | fun(_) -> 51 | {inorder, 52 | [{"simple connect test", 53 | fun simple_connect/0}, 54 | {"simple send binary test, active true", 55 | fun() -> simple_send(binary, true) end}, 56 | {"simple send binary test, active once", 57 | fun() -> simple_send(binary, once) end}, 58 | {"simple send binary test, active false", 59 | fun() -> simple_send(binary, false) end}, 60 | {"simple send list test", 61 | fun() -> simple_send(list, true) end}, 62 | {"two clients test", 63 | fun two_clients/0}, 64 | {"client large send", 65 | fun large_send/0}, 66 | {"two servers test", 67 | fun two_servers/0}, 68 | {"send timeout test", 69 | fun send_timeout/0}, 70 | {"invalid accept test", 71 | fun invalid_accept/0}, 72 | {"packet size test", 73 | fun packet_size/0}, 74 | {"header size test", 75 | fun header_size/0}, 76 | {"set send/recv buffer sizes test", 77 | fun buf_size/0} 78 | ]} 79 | end}. 80 | 81 | simple_connect() -> 82 | Self = self(), 83 | Ref = make_ref(), 84 | spawn_link(fun() -> ok = simple_connect_server(Self, Ref) end), 85 | ok = simple_connect_client(Ref), 86 | ok. 87 | 88 | simple_connect_server(Client, Ref) -> 89 | Opts = [{mode,binary}], 90 | {ok, LSock} = gen_utp:listen(0, Opts), 91 | Client ! gen_utp:sockname(LSock), 92 | {ok, ARef} = gen_utp:async_accept(LSock), 93 | receive 94 | {utp_async, LSock, ARef, {ok, Sock}} -> 95 | ?assertMatch(true, is_port(Sock)), 96 | ?assertMatch(true, is_reference(Ref)), 97 | ?assertMatch(ok, gen_utp:close(Sock)); 98 | {utp_async, LSock, ARef, Error} -> 99 | exit({utp_async, Error}) 100 | after 101 | 3000 -> exit(failure) 102 | end, 103 | ok = gen_utp:close(LSock), 104 | Client ! {done, Ref}, 105 | ok. 106 | 107 | simple_connect_client(Ref) -> 108 | receive 109 | {ok, {_, LPort}} -> 110 | Opts = [{mode,binary}], 111 | {ok, Sock} = gen_utp:connect({127,0,0,1}, LPort, Opts), 112 | ?assertMatch(true, erlang:is_port(Sock)), 113 | ?assertEqual({connected, self()}, erlang:port_info(Sock, connected)), 114 | ok = gen_utp:close(Sock), 115 | receive 116 | {done, Ref} -> ok 117 | after 118 | 5000 -> exit(failure) 119 | end 120 | after 121 | 5000 -> exit(failure) 122 | end, 123 | ok. 124 | 125 | simple_send(Mode, ActiveMode) -> 126 | Self = self(), 127 | Ref = make_ref(), 128 | spawn_link(fun() -> 129 | ok = simple_send_server(Self, Ref, Mode, ActiveMode) 130 | end), 131 | ok = simple_send_client(Ref, Mode, ActiveMode), 132 | ok. 133 | 134 | simple_send_server(Client, Ref, Mode, ActiveMode) -> 135 | Opts = [{active,ActiveMode}, {mode,Mode}], 136 | {ok, LSock} = gen_utp:listen(0, Opts), 137 | Client ! gen_utp:sockname(LSock), 138 | {ok, Sock} = gen_utp:accept(LSock, 2000), 139 | SentVal = case ActiveMode of 140 | false -> 141 | {ok, RecvData} = gen_utp:recv(Sock, 0, 5000), 142 | RecvData; 143 | _ -> 144 | receive 145 | {utp, Sock, Val} -> 146 | Val; 147 | Error -> 148 | exit(Error) 149 | after 150 | 5000 -> exit(failure) 151 | end 152 | end, 153 | case Mode of 154 | binary -> 155 | ?assertMatch(<<"simple send client">>, SentVal); 156 | list -> 157 | ?assertMatch("simple send client", SentVal) 158 | end, 159 | ok = gen_utp:send(Sock, <<"simple send server">>), 160 | ok = gen_utp:close(LSock), 161 | Client ! {done, Ref}, 162 | ok. 163 | 164 | simple_send_client(Ref, Mode, ActiveMode) -> 165 | receive 166 | {ok, {_, LPort}} -> 167 | Opts = [Mode,{active,ActiveMode}], 168 | {ok, Sock} = gen_utp:connect("127.0.0.1", LPort, Opts), 169 | ok = gen_utp:send(Sock, <<"simple send client">>), 170 | Reply = case ActiveMode of 171 | false -> 172 | {ok, RecvData} = gen_utp:recv(Sock, 0, 5000), 173 | RecvData; 174 | _ -> 175 | receive 176 | {utp, Sock, Val} -> 177 | Val 178 | after 179 | 5000 -> exit(failure) 180 | end 181 | end, 182 | case Mode of 183 | binary -> 184 | ?assertMatch(<<"simple send server">>, Reply); 185 | list -> 186 | ?assertMatch("simple send server", Reply) 187 | end, 188 | receive 189 | {done, Ref} -> ok 190 | after 191 | 5000 -> exit(failure) 192 | end, 193 | ok = gen_utp:close(Sock) 194 | after 195 | 5000 -> exit(failure) 196 | end, 197 | ok. 198 | 199 | two_clients() -> 200 | Self = self(), 201 | Ref = make_ref(), 202 | spawn_link(fun() -> ok = two_client_server(Self, Ref) end), 203 | ok = two_clients(Ref), 204 | ok. 205 | 206 | two_client_server(Client, Ref) -> 207 | Opts = [{active,true}, {mode,binary}], 208 | {ok, LSock} = gen_utp:listen(0, Opts), 209 | Client ! gen_utp:sockname(LSock), 210 | {ok, Sock1} = gen_utp:accept(LSock, 2000), 211 | receive 212 | {utp, Sock1, <<"client1">>} -> 213 | ok = gen_utp:send(Sock1, <<"client1">>), 214 | {ok, Sock2} = gen_utp:accept(LSock, 2000), 215 | receive 216 | {utp, Sock2, <<"client2">>} -> 217 | ok = gen_utp:send(Sock2, <<"client2">>), 218 | ok = gen_utp:close(Sock2); 219 | Error -> 220 | exit(Error) 221 | after 222 | 5000 -> exit(failure) 223 | end, 224 | ok = gen_utp:close(Sock1); 225 | Error -> 226 | exit(Error) 227 | after 228 | 5000 -> exit(failure) 229 | end, 230 | ok = gen_utp:close(LSock), 231 | Client ! {done, Ref}, 232 | ok. 233 | 234 | two_clients(Ref) -> 235 | receive 236 | {ok, {_, LPort}} -> 237 | Opts = [{active,true}, {mode,binary}], 238 | {ok, Sock1} = gen_utp:connect("127.0.0.1", LPort, Opts), 239 | ok = gen_utp:send(Sock1, <<"client1">>), 240 | {ok, Sock2} = gen_utp:connect("127.0.0.1", LPort, Opts), 241 | receive 242 | {utp, Sock1, <<"client1">>} -> 243 | ok = gen_utp:send(Sock2, <<"client2">>), 244 | receive 245 | {utp, Sock2, <<"client2">>} -> 246 | receive 247 | {done, Ref} -> ok 248 | after 249 | 5000 -> exit(failure) 250 | end 251 | after 252 | 5000 -> exit(failure) 253 | end 254 | after 255 | 5000 -> exit(failure) 256 | end, 257 | ok = gen_utp:close(Sock1), 258 | ok = gen_utp:close(Sock2) 259 | after 260 | 5000 -> exit(failure) 261 | end, 262 | ok. 263 | 264 | large_send() -> 265 | Self = self(), 266 | Ref = make_ref(), 267 | Bin = list_to_binary(lists:duplicate(1000000, $A)), 268 | spawn_link(fun() -> ok = large_send_server(Self, Ref, Bin) end), 269 | ok = large_send_client(Ref, Bin), 270 | ok. 271 | 272 | large_send_server(Client, Ref, Bin) -> 273 | Opts = [{active,true}, {mode,binary}], 274 | {ok, LSock} = gen_utp:listen(0, Opts), 275 | Client ! gen_utp:sockname(LSock), 276 | {ok, Sock} = gen_utp:accept(LSock, 2000), 277 | Bin = large_receive(Sock, byte_size(Bin)), 278 | ok = gen_utp:send(Sock, <<"large send server">>), 279 | ok = gen_utp:close(Sock), 280 | ok = gen_utp:close(LSock), 281 | Client ! {done, Ref}, 282 | ok. 283 | 284 | large_receive(Sock, Size) -> 285 | large_receive(Sock, Size, 0, <<>>). 286 | large_receive(_, Size, Size, Bin) -> 287 | Bin; 288 | large_receive(Sock, Size, Count, Bin) -> 289 | receive 290 | {utp, Sock, Data} -> 291 | NBin = <>, 292 | large_receive(Sock, Size, Count+byte_size(Data), NBin); 293 | Error -> 294 | exit(Error) 295 | after 296 | 5000 -> exit(failure) 297 | end. 298 | 299 | large_send_client(Ref, Bin) -> 300 | receive 301 | {ok, {_, LPort}} -> 302 | Opts = [{active,true},{mode,binary}], 303 | {ok, Sock} = gen_utp:connect("127.0.0.1", LPort, Opts), 304 | ok = gen_utp:send(Sock, Bin), 305 | receive 306 | {utp, Sock, Reply} -> 307 | ?assertMatch(Reply, <<"large send server">>), 308 | receive 309 | {done, Ref} -> ok 310 | after 311 | 5000 -> exit(failure) 312 | end 313 | after 314 | 5000 -> exit(failure) 315 | end, 316 | ok = gen_utp:close(Sock) 317 | after 318 | 5000 -> exit(failure) 319 | end, 320 | ok. 321 | 322 | two_servers() -> 323 | Self = self(), 324 | Ref = make_ref(), 325 | spawn_link(fun() -> ok = two_servers(Self, Ref) end), 326 | ok = two_server_client(Ref), 327 | ok. 328 | 329 | two_servers(Client, Ref) -> 330 | {ok, LSock} = gen_utp:listen(0, [{active,true}]), 331 | {ok, Sockname} = gen_utp:sockname(LSock), 332 | Client ! {Ref, Sockname}, 333 | {ok, Ref1} = gen_utp:async_accept(LSock), 334 | Self = self(), 335 | Pid1 = spawn_link(fun() -> two_servers_do_server(Self) end), 336 | Pid2 = spawn_link(fun() -> two_servers_do_server(Self) end), 337 | receive 338 | {utp_async, LSock, Ref1, {ok, Sock1}} -> 339 | ok = gen_utp:controlling_process(Sock1, Pid1), 340 | Pid1 ! {go, Sock1}; 341 | {utp_async, LSock, Ref1, Error1} -> 342 | exit({utp_async, Error1}) 343 | after 344 | 5000 -> exit(failure) 345 | end, 346 | {ok, Ref2} = gen_utp:async_accept(LSock), 347 | receive 348 | {utp_async, LSock, Ref2, {ok, Sock2}} -> 349 | ok = gen_utp:controlling_process(Sock2, Pid2), 350 | Pid2 ! {go, Sock2}; 351 | {utp_async, LSock, Ref2, Error2} -> 352 | exit({utp_async, Error2}) 353 | after 354 | 5000 -> exit(failure) 355 | end, 356 | Client ! {Ref, send}, 357 | receive 358 | {Pid1, ok} -> 359 | receive 360 | {Pid2, ok} -> 361 | Pid1 ! check, 362 | Pid2 ! check; 363 | Err2 -> 364 | exit(Err2) 365 | after 366 | 5000 -> exit(failure) 367 | end; 368 | Err1 -> 369 | exit(Err1) 370 | after 371 | 5000 -> exit(failure) 372 | end, 373 | ?assertMatch({message_queue_len,0}, 374 | erlang:process_info(self(), message_queue_len)), 375 | ok = gen_utp:close(LSock). 376 | 377 | two_servers_do_server(Pid) -> 378 | Sock = receive 379 | {go, S} -> 380 | S 381 | after 382 | 5000 -> exit(failure) 383 | end, 384 | receive 385 | {utp, Sock, Msg} -> 386 | ok = gen_utp:send(Sock, Msg), 387 | Pid ! {self(), ok}; 388 | Error -> 389 | exit(Error) 390 | after 391 | 5000 -> exit(failure) 392 | end, 393 | receive 394 | check -> 395 | ?assertMatch({message_queue_len,0}, 396 | erlang:process_info(self(), message_queue_len)) 397 | after 398 | 5000 -> exit(failure) 399 | end, 400 | ok = gen_utp:close(Sock). 401 | 402 | two_server_client(Ref) -> 403 | receive 404 | {Ref, {_, LPort}} -> 405 | Opts = [{active,true},{mode,binary}], 406 | {ok, Sock1} = gen_utp:connect("127.0.0.1", LPort, Opts), 407 | Msg1 = list_to_binary(["two servers", term_to_binary(Ref)]), 408 | {ok, Sock2} = gen_utp:connect("127.0.0.1", LPort, Opts), 409 | Msg2 = list_to_binary(lists:reverse(binary_to_list(Msg1))), 410 | receive 411 | {Ref, send} -> 412 | ok = gen_utp:send(Sock1, Msg1), 413 | ok = gen_utp:send(Sock2, Msg2), 414 | ok = two_server_client_receive(Sock1, Msg1), 415 | ok = two_server_client_receive(Sock2, Msg2) 416 | after 417 | 5000 -> exit(failure) 418 | end 419 | after 420 | 5000 -> exit(failure) 421 | end, 422 | ok. 423 | 424 | two_server_client_receive(Sock, Msg) -> 425 | receive 426 | {utp, Sock, Msg} -> 427 | ok 428 | after 429 | 5000 -> exit(failure) 430 | end, 431 | ok = gen_utp:close(Sock). 432 | 433 | send_timeout() -> 434 | {ok, LSock} = gen_utp:listen(0), 435 | {ok, {_, Port}} = gen_utp:sockname(LSock), 436 | {ok, Ref} = gen_utp:async_accept(LSock), 437 | Pid = spawn(fun() -> 438 | {ok,_} = gen_utp:connect("localhost", Port), 439 | receive 440 | exit -> 441 | ok 442 | end 443 | end), 444 | receive 445 | {utp_async, LSock, Ref, {ok, S}} -> 446 | Pid ! exit, 447 | ok = gen_utp:send(S, lists:duplicate(1000, $X)), 448 | ?assertMatch(ok, gen_utp:setopts(S, [{send_timeout, 1}])), 449 | ?assertMatch({error,etimedout}, 450 | gen_utp:send(S, lists:duplicate(1000, $Y))), 451 | ok = gen_utp:close(S); 452 | {utp_async, LSock, Ref, Error} -> 453 | exit({utp_async, Error}) 454 | after 455 | 2000 -> 456 | exit(failure) 457 | end, 458 | ok = gen_utp:close(LSock). 459 | 460 | invalid_accept() -> 461 | {ok, LSock} = gen_utp:listen(0), 462 | {ok, {_, Port}} = gen_utp:sockname(LSock), 463 | {ok, Ref} = gen_utp:async_accept(LSock), 464 | spawn(fun() -> 465 | {ok,_} = gen_utp:connect("localhost", Port), 466 | receive 467 | exit -> 468 | ok 469 | end 470 | end), 471 | receive 472 | {utp_async, LSock, Ref, {ok, S}} -> 473 | ?assertMatch({error,einval}, gen_utp:accept(S)), 474 | ok = gen_utp:close(S); 475 | {utp_async, LSock, Ref, Error} -> 476 | exit({utp_async, Error}) 477 | after 478 | 2000 -> 479 | exit(failure) 480 | end, 481 | ok = gen_utp:close(LSock). 482 | 483 | packet_size() -> 484 | {ok, LSock} = gen_utp:listen(0, [binary,{active,false}]), 485 | {ok, {_, Port}} = gen_utp:sockname(LSock), 486 | Data = <<"1234567890">>, 487 | lists:foreach(fun(Pkt) -> 488 | spawn(fun() -> 489 | {ok,S} = gen_utp:connect("localhost", 490 | Port, 491 | [{packet,Pkt}]), 492 | ok = gen_utp:send(S, Data), 493 | gen_utp:close(S) 494 | end), 495 | {ok, Ref} = gen_utp:async_accept(LSock), 496 | receive 497 | {utp_async, LSock, Ref, {ok, S}} -> 498 | ok = gen_utp:setopts(S, [{packet,Pkt}]), 499 | ?assertMatch({ok,Data}, gen_utp:recv(S, 0, 2000)), 500 | ok = gen_utp:close(S); 501 | {utp_async, LSock, Ref, Error} -> 502 | exit({utp_async, Error}) 503 | after 504 | 2000 -> 505 | exit(failure) 506 | end 507 | end, [raw, 0, 1, 2, 4]), 508 | ok = gen_utp:close(LSock), 509 | ok. 510 | 511 | header_size() -> 512 | {ok, LSock} = gen_utp:listen(0, [binary, {header,5}, {active,false}]), 513 | {ok, {_, Port}} = gen_utp:sockname(LSock), 514 | Data = [ $B,$a,$s,$h,$o | <<"1234567890">> ], 515 | {ok, Ref} = gen_utp:async_accept(LSock), 516 | spawn(fun() -> 517 | {ok,S} = gen_utp:connect("localhost", Port, [binary, {header,5}]), 518 | ok = gen_utp:send(S, Data), 519 | gen_utp:close(S) 520 | end), 521 | receive 522 | {utp_async, LSock, Ref, {ok, S}} -> 523 | ?assertMatch({ok,Data}, gen_utp:recv(S, 0, 2000)), 524 | ok = gen_utp:close(S); 525 | {utp_async, LSock, Ref, Error} -> 526 | exit({utp_async, Error}) 527 | after 528 | 2000 -> 529 | exit(failure) 530 | end, 531 | ok = gen_utp:close(LSock), 532 | ok. 533 | 534 | buf_size() -> 535 | {ok, LSock} = gen_utp:listen(0, [{sndbuf,4096},{recbuf,8192},{active,false}]), 536 | {ok, {_, Port}} = gen_utp:sockname(LSock), 537 | {ok, Ref} = gen_utp:async_accept(LSock), 538 | {ok,S} = gen_utp:connect("localhost", Port, [{active,false}]), 539 | {ok, [{sndbuf,Sndbuf}]} = gen_utp:getopts(S, [sndbuf]), 540 | {ok, [{recbuf,Recbuf}]} = gen_utp:getopts(S, [recbuf]), 541 | NSndbuf = Sndbuf*8, 542 | NRecbuf = Recbuf*16, 543 | ok = gen_utp:setopts(S, [{sndbuf,NSndbuf},{recbuf,NRecbuf}]), 544 | {ok, [{sndbuf,NSndbuf}]} = gen_utp:getopts(S, [sndbuf]), 545 | {ok, [{recbuf,NRecbuf}]} = gen_utp:getopts(S, [recbuf]), 546 | receive 547 | {utp_async, LSock, Ref, {ok, AS}} -> 548 | ?assertMatch({ok,[{sndbuf,4096}]}, gen_utp:getopts(AS, [sndbuf])), 549 | ?assertMatch({ok,[{recbuf,8192}]}, gen_utp:getopts(AS, [recbuf])), 550 | ok = gen_utp:close(AS); 551 | {utp_async, LSock, Ref, Error} -> 552 | exit({utp_async, Error}) 553 | after 554 | 2000 -> 555 | exit(failure) 556 | end, 557 | ok = gen_utp:close(S), 558 | ok = gen_utp:close(LSock), 559 | ok. 560 | -------------------------------------------------------------------------------- /test/gen_utp_close_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_close_tests: socket close tests for gen_utp and the utpdrv driver 4 | %% 5 | %% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_close_tests). 23 | -author('Steve Vinoski '). 24 | 25 | -include_lib("eunit/include/eunit.hrl"). 26 | -include("gen_utp_tests_setup.hrl"). 27 | 28 | close_test_() -> 29 | {setup, 30 | fun setup/0, 31 | fun cleanup/1, 32 | fun(_) -> 33 | [{"close client message test", 34 | fun close_client/0}, 35 | {"close server message test", 36 | fun close_server/0}] 37 | end}. 38 | 39 | close_client() -> 40 | {ok, LSock} = gen_utp:listen(0), 41 | {ok, {_, Port}} = gen_utp:sockname(LSock), 42 | {ok, Ref} = gen_utp:async_accept(LSock), 43 | {ok, Sock} = gen_utp:connect("localhost", Port), 44 | receive 45 | {utp_async, LSock, Ref, {ok, ASock}} -> 46 | ok = gen_utp:close(Sock), 47 | receive 48 | {utp_closed, ASock} -> 49 | ok 50 | after 51 | 2000 -> 52 | exit(server_not_closed) 53 | end; 54 | {utp_async, LSock, Ref, Error} -> 55 | exit({utp_async, Error}) 56 | after 57 | 2000 -> 58 | exit(async_accept_fail) 59 | end, 60 | ok = gen_utp:close(LSock), 61 | ok. 62 | 63 | close_server() -> 64 | {ok, LSock} = gen_utp:listen(0), 65 | {ok, {_, Port}} = gen_utp:sockname(LSock), 66 | {ok, Ref} = gen_utp:async_accept(LSock), 67 | {ok, Sock} = gen_utp:connect("localhost", Port), 68 | receive 69 | {utp_async, LSock, Ref, {ok, ASock}} -> 70 | ok = gen_utp:close(ASock), 71 | receive 72 | {utp_closed, Sock} -> 73 | ok 74 | after 75 | 2000 -> 76 | exit(client_not_closed) 77 | end; 78 | {utp_async, LSock, Ref, Error} -> 79 | exit({utp_async, Error}) 80 | after 81 | 2000 -> 82 | exit(async_accept_fail) 83 | end, 84 | ok = gen_utp:close(LSock), 85 | ok. 86 | -------------------------------------------------------------------------------- /test/gen_utp_listen_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_listen_tests: listen tests for gen_utp 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_listen_tests). 23 | -author('Steve Vinoski '). 24 | 25 | -include_lib("eunit/include/eunit.hrl"). 26 | -include("gen_utp_tests_setup.hrl"). 27 | 28 | listen_test_() -> 29 | {setup, 30 | fun setup/0, 31 | fun cleanup/1, 32 | fun(_) -> 33 | {inorder, 34 | [{"simple listen test", 35 | fun simple_listen/0}, 36 | {"listen not connected test", 37 | fun listen_notconn/0}, 38 | {"two listen test", 39 | fun two_listen/0}, 40 | {"specific interface listen test", 41 | fun specific_interface/0}, 42 | {"async accept test", 43 | fun async_accept/0}, 44 | {"accept timeout test", 45 | fun accept_timeout/0}, 46 | {"concurrent accepts", 47 | fun concurrent_accepts/0} 48 | ]} 49 | end}. 50 | 51 | simple_listen() -> 52 | Ports = length(erlang:ports()), 53 | {ok, LSock} = gen_utp:listen(0), 54 | ?assert(erlang:is_port(LSock)), 55 | ?assertEqual(Ports+1, length(erlang:ports())), 56 | Self = self(), 57 | ?assertMatch({connected, Self}, erlang:port_info(LSock, connected)), 58 | ?assertMatch({error, enotconn}, gen_utp:send(LSock, <<"send">>)), 59 | {ok, {Addr, Port}} = gen_utp:sockname(LSock), 60 | ?assert(is_tuple(Addr)), 61 | ?assert(is_number(Port)), 62 | ?assertEqual({ok, Port}, gen_utp:port(LSock)), 63 | ?assertMatch({error, enotconn}, gen_utp:peername(LSock)), 64 | ?assertMatch(ok, gen_utp:close(LSock)), 65 | ?assertMatch(undefined, erlang:port_info(LSock)), 66 | ?assertEqual(Ports, length(erlang:ports())), 67 | ok. 68 | 69 | listen_notconn() -> 70 | {ok, LSock} = gen_utp:listen(0), 71 | ?assertMatch({error,enotconn}, gen_utp:send(LSock, "data")), 72 | ?assertMatch({error,enotconn}, gen_utp:recv(LSock, 0, 1000)), 73 | ok. 74 | 75 | two_listen() -> 76 | {ok, LSock1} = gen_utp:listen(0), 77 | {ok, {_, Port1}} = gen_utp:sockname(LSock1), 78 | ?assertMatch(ok, gen_utp:close(LSock1)), 79 | {ok, LSock2} = gen_utp:listen(Port1), 80 | ?assertNotEqual(LSock1, LSock2), 81 | {ok, {_, Port2}} = gen_utp:sockname(LSock2), 82 | ?assertEqual(Port1, Port2), 83 | {ok, LSock3} = gen_utp:listen(0), 84 | {ok, {_, Port3}} = gen_utp:sockname(LSock3), 85 | ?assertNotEqual(Port2, Port3), 86 | ?assertMatch(ok, gen_utp:close(LSock2)), 87 | ok. 88 | 89 | specific_interface() -> 90 | {ok, LSock1} = gen_utp:listen(0, [{ip, "127.0.0.1"}]), 91 | ?assertMatch({ok, {{127,0,0,1}, _}}, gen_utp:sockname(LSock1)), 92 | ?assertMatch(ok, gen_utp:close(LSock1)), 93 | {ok, LSock2} = gen_utp:listen(0, [{ifaddr, "127.0.0.1"}]), 94 | ?assertMatch({ok, {{127,0,0,1}, _}}, gen_utp:sockname(LSock2)), 95 | ?assertMatch(ok, gen_utp:close(LSock2)), 96 | {ok, LSock3} = gen_utp:listen(0, [{ip, {127,0,0,1}}]), 97 | ?assertMatch({ok, {{127,0,0,1}, _}}, gen_utp:sockname(LSock3)), 98 | ?assertMatch(ok, gen_utp:close(LSock3)), 99 | {ok, LSock4} = gen_utp:listen(0, [{ifaddr, {127,0,0,1}}]), 100 | ?assertMatch({ok, {{127,0,0,1}, _}}, gen_utp:sockname(LSock4)), 101 | ?assertMatch(ok, gen_utp:close(LSock4)), 102 | ok. 103 | 104 | async_accept() -> 105 | {ok, LSock} = gen_utp:listen(0), 106 | ?assertMatch({ok, _Ref}, gen_utp:async_accept(LSock)), 107 | ok. 108 | 109 | accept_timeout() -> 110 | {ok, LSock} = gen_utp:listen(0), 111 | ?assertMatch({error, etimedout}, gen_utp:accept(LSock, 2000)), 112 | ok. 113 | 114 | concurrent_accepts() -> 115 | Self = self(), 116 | Count = 1000, 117 | Servers = [spawn(fun() -> server() end) || _ <- lists:seq(1,Count)], 118 | Clients = [spawn(fun() -> client(Server, Self) end) || Server <- Servers], 119 | Results = lists:map(fun(_) -> 120 | receive 121 | done -> done; 122 | _ -> exit({error, unexpected_message}) 123 | after 124 | 5000 -> 125 | exit({error, missing_client}) 126 | end 127 | end, Clients), 128 | ?assert(lists:all(fun(R) -> R =:= done end, Results)), 129 | ok. 130 | 131 | server() -> 132 | {ok,LS} = gen_utp:listen(0), 133 | {ok,{_,Port}} = gen_utp:sockname(LS), 134 | {ok,Ref} = gen_utp:async_accept(LS), 135 | server(LS, Port, Ref). 136 | 137 | server(LS, Port, Ref) -> 138 | receive 139 | {port, Pid} -> 140 | Pid ! {self(), Port}, 141 | server(LS, Port, Ref); 142 | {utp_async,LS,Ref,{ok,S}} -> 143 | ?assert(is_port(S)), 144 | gen_utp:close(S); 145 | _ -> 146 | exit({error, unknown_message}) 147 | end. 148 | 149 | client(Pid, Parent) -> 150 | Pid ! {port, self()}, 151 | receive 152 | {Pid, Port} -> 153 | {ok,S} = gen_utp:connect("127.0.0.1", Port, [binary]), 154 | gen_utp:close(S), 155 | Parent ! done 156 | after 157 | 5000 -> 158 | exit({error, client_timeout}) 159 | end. 160 | -------------------------------------------------------------------------------- /test/gen_utp_port_tests.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_port_tests: port number tests for gen_utp 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(gen_utp_port_tests). 23 | -author('Steve Vinoski '). 24 | 25 | -include_lib("eunit/include/eunit.hrl"). 26 | -include("gen_utp_tests_setup.hrl"). 27 | 28 | port_number_test_() -> 29 | {setup, 30 | fun setup/0, 31 | fun cleanup/1, 32 | fun(_) -> 33 | {"port number range test", 34 | fun port_number_range/0} 35 | end}. 36 | 37 | port_number_range() -> 38 | Error = {error, function_clause}, 39 | ?assertMatch(Error, 40 | try gen_utp:listen(-1) catch C1:R1 -> {C1,R1} end), 41 | ?assertMatch(Error, 42 | try gen_utp:listen(65536) catch C2:R2 -> {C2,R2} end), 43 | ?assertMatch(Error, 44 | try gen_utp:connect("localhost", -1) 45 | catch C3:R3 -> {C3,R3} end), 46 | ?assertMatch(Error, 47 | try gen_utp:connect("localhost", 0) catch C4:R4 -> {C4,R4} end), 48 | ?assertMatch(Error, 49 | try gen_utp:connect("localhost", 65536) 50 | catch C5:R5 -> {C5,R5} end), 51 | ok. 52 | -------------------------------------------------------------------------------- /test/gen_utp_tests_setup.hrl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% gen_utp_tests_setup: setup/cleanup for gen_utp tests 4 | %% 5 | %% Copyright (c) 2012-2013 Basho Technologies, Inc. All Rights Reserved. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | 23 | -ifndef(GEN_UTP_TESTS_SETUP). 24 | -define(GEN_UTP_TESTS_SETUP, 1). 25 | 26 | setup() -> 27 | gen_utp:start_link(). 28 | 29 | cleanup(_) -> 30 | gen_utp:stop(), 31 | Check = fun(F) -> 32 | case whereis(gen_utp) of 33 | undefined -> ok; 34 | _ -> F(F) 35 | end 36 | end, 37 | Check(Check). 38 | 39 | -endif. 40 | --------------------------------------------------------------------------------