├── .gitignore ├── LICENSE.MIT ├── Makefile ├── README.md ├── examples ├── 128b.dat ├── 1k.dat ├── 1m.dat ├── echo-client.js └── echo-server.js └── source ├── CMakeLists.txt ├── echo-client.c ├── echo-server.c ├── evn-assert.c ├── evn-buffer-list-test.c ├── evn-buffer-list.c ├── evn-inbuf-test.c ├── evn-inbuf.c ├── evn-test-old.c ├── evn.c └── include ├── bool.h ├── crossnet.h ├── evn-buffer-list.h ├── evn-inbuf.h └── evn.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by AJ ONeal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | mkdir -p build 3 | cd build; cmake ../source -DCMAKE_BUILD_TYPE=; make 4 | 5 | debug: 6 | mkdir -p build 7 | cd build; cmake ../source -DCMAKE_BUILD_TYPE=Debug; make 8 | 9 | clean: 10 | make clean -C build 11 | 12 | install: all 13 | cd build; make install 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moved 2 | ### [libevn](https://git.daplie.com/coolaj86/libevn) is now at [git.daplie.com/coolaj86/libevn](https://git.daplie.com/coolaj86/libevn) 3 | -------------------------------------------------------------------------------- /examples/128b.dat: -------------------------------------------------------------------------------- 1 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 2 | -------------------------------------------------------------------------------- /examples/1k.dat: -------------------------------------------------------------------------------- 1 | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz 2 | yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 3 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 4 | wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 5 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 6 | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 7 | ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 8 | ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd 9 | -------------------------------------------------------------------------------- /examples/echo-client.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function () { 3 | "use strict"; 4 | 5 | var net = require("net"), 6 | client; 7 | 8 | client = net.createConnection('/tmp/libevnet-echo.' + process.getuid() + '.sock'); 9 | // client = net.createConnection(3355, 'localhost'); 10 | 11 | client.setTimeout(6000); 12 | 13 | client.on('connect', function () { 14 | console.log("[Client] On Connect"); 15 | client.write("Hello Server"); 16 | }); 17 | 18 | client.on('secure', function () { 19 | // not used in this example 20 | console.log("[Client] On Secure"); 21 | }); 22 | 23 | client.on('data', function (data) { 24 | console.log("[Client] On Data: " + data.toString()); 25 | }); 26 | 27 | client.on('end', function () { 28 | console.log("[Client] On End (received FIN).\n\t\treadyState: " + client.readyState); 29 | }); 30 | 31 | client.on('timeout', function () { 32 | console.log("[Client] On Timeout"); 33 | client.end(); 34 | console.log("[Client] Closing (sent FIN).\n\t\treadyState: " + client.readyState); 35 | }); 36 | 37 | client.on('drain', function () { 38 | console.log("[Client] On Drain"); 39 | }); 40 | 41 | client.on('error', function (err) { 42 | console.log("[Client] On Error: " + err.message); 43 | }); 44 | 45 | client.on('close', function () { 46 | console.log("[Client] On Close.\n\t\treadyState: " + client.readyState); 47 | }); 48 | 49 | }()); 50 | -------------------------------------------------------------------------------- /examples/echo-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function () { 3 | "use strict" 4 | 5 | var net = require("net"), 6 | fs = require('fs'), 7 | server, 8 | interval, 9 | socket_addr, 10 | timed_out = false; 11 | 12 | function on_connection(stream) { 13 | var out_file = -1; 14 | 15 | console.log("[Server] On Connection. [" + server.connections + "]"); 16 | 17 | stream.pause(); 18 | // Write stream to file once it is ready 19 | fs.open('./js-server-received.dat', 'w', function(err, fd) { 20 | if (err) { 21 | throw err; 22 | } 23 | out_file = fd; 24 | stream.resume(); 25 | }); 26 | 27 | stream.setTimeout(5000); 28 | // stream.setNoDelay(); 29 | // stream.setKeepAlive(); 30 | 31 | console.log("\t[Stream] readyState: " + stream.readyState); 32 | 33 | stream.on('connect', function () { 34 | timed_out = false; 35 | console.log("\t[Stream] On Connect"); 36 | }); 37 | 38 | stream.on('secure', function () { 39 | console.log("\t[Stream] On Secure"); 40 | }); 41 | 42 | stream.on('data', function (data) { 43 | timed_out = false; 44 | console.log("\t[Stream] On Data"); 45 | stream.write(data); 46 | 47 | stream.pause(); 48 | fs.write(out_file, data, 0, data.length, null, function (err, written) { 49 | if (err) { 50 | throw err; 51 | } 52 | stream.resume(); 53 | }); 54 | }); 55 | 56 | stream.on('end', function () { 57 | fs.close(out_file); 58 | console.log("\t[Stream] On End (received FIN).\n\t\treadyState: " + stream.readyState); 59 | }); 60 | 61 | stream.on('timeout', function () { 62 | console.log("\t[Stream] On Timeout"); 63 | stream.end(); 64 | console.log("\t[Stream] Closing (sent FIN).\n\t\treadyState: " + stream.readyState); 65 | }); 66 | 67 | stream.on('drain', function () { 68 | console.log("\t[Stream] On Drain"); 69 | }); 70 | 71 | stream.on('error', function (err) { 72 | console.log("\t[Stream] On Error: " + err.message); 73 | }); 74 | 75 | stream.on('close', function (had_error) { 76 | console.log("\t[Stream] On Close (file descriptor closed).\n\t\treadyState: " + stream.readyState); 77 | // 'closed', 'open', 'opening', 'readOnly', or 'writeOnly' 78 | if ('open' === stream.readyState) { 79 | stream.write("cause error"); 80 | } 81 | }); 82 | } 83 | 84 | function on_close() { 85 | console.log("[Server] closing; waiting for all streams to close"); 86 | clearInterval(interval); 87 | } 88 | 89 | function check_for_timeout() { 90 | if (true === timed_out) { 91 | server.close(); 92 | } 93 | timed_out = true; 94 | } 95 | 96 | server = net.createServer(on_connection); 97 | server.on('close', on_close); 98 | server.maxConnections = 100; 99 | 100 | socket_addr = '/tmp/libevnet-echo.' + process.getuid() + '.sock'; 101 | if (isNaN(socket_addr)) { 102 | server.listen(socket_addr); 103 | console.log("[Server] listening on " + socket_addr); 104 | } 105 | else if (socket_addr < 65536) { 106 | server.listen(socket_addr, 'localhost'); 107 | console.log("[Server] listening on port" + socket_addr); 108 | } 109 | 110 | //interval = setInterval(check_for_timeout, 5000); 111 | }()); 112 | -------------------------------------------------------------------------------- /source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(EVN C) 3 | include_directories(${EVN_SOURCE_DIR}/include) 4 | #include_directories("${CMAKE_SOURCE_DIR}") 5 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") 6 | SET(CMAKE_C_FLAGS_DEBUG "-DDEBUG -g3 -ggdb3") 7 | 8 | add_library(evn-buffer-list STATIC evn-buffer-list.c) 9 | add_library(evn-inbuf STATIC evn-inbuf.c) 10 | 11 | add_library(evn SHARED evn.c include/evn.h evn-buffer-list evn-inbuf) 12 | target_link_libraries(evn ev) 13 | 14 | add_executable(evn-bufferlist-test evn-buffer-list-test.c evn-buffer-list) 15 | add_executable(evn-inbuf-test evn-inbuf-test.c evn-inbuf) 16 | 17 | add_executable(echo-server echo-server.c) 18 | target_link_libraries(echo-server evn) 19 | 20 | add_executable(echo-client echo-client.c) 21 | target_link_libraries(echo-client evn) 22 | 23 | INSTALL(TARGETS evn 24 | RUNTIME DESTINATION bin 25 | LIBRARY DESTINATION lib 26 | ARCHIVE DESTINATION lib 27 | ) 28 | INSTALL(FILES ${EVN_SOURCE_DIR}/include/bool.h DESTINATION include) 29 | INSTALL(FILES ${EVN_SOURCE_DIR}/include/evn.h DESTINATION include) 30 | INSTALL(FILES ${EVN_SOURCE_DIR}/include/evn-buffer-list.h DESTINATION include) 31 | INSTALL(FILES ${EVN_SOURCE_DIR}/include/evn-inbuf.h DESTINATION include) 32 | -------------------------------------------------------------------------------- /source/echo-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "evn.h" 6 | 7 | // 8 | // Client Callbacks 9 | // 10 | 11 | static void* sent_data = NULL; 12 | static int sent_data_len = 0; 13 | 14 | // Secure 15 | static void on_secure(EV_P_ struct evn_stream* stream) { 16 | puts("[Client] Stream On Secure"); 17 | } 18 | 19 | // Data 20 | static void on_data(EV_P_ struct evn_stream* stream, void* data, int size) { 21 | int i; 22 | char* msg = (char*) data; 23 | char* sent = (char*) sent_data; 24 | puts("[Client] Stream On Data"); 25 | 26 | if (sent_data_len != size) 27 | { 28 | fprintf(stderr, "\t ERR - did not receive(%d) same data size as was sent(%d)\n", size, sent_data_len); 29 | return; 30 | } 31 | 32 | for (i = 0; i < size; i += 1) 33 | { 34 | if (*msg != *sent) 35 | { 36 | fprintf(stderr, "\t ERR - sent data and received data differ at byte %d\n", i); 37 | return; 38 | } 39 | msg += 1; 40 | sent += 1; 41 | } 42 | 43 | printf("\t received data matches what was sent\n"); 44 | } 45 | 46 | // End 47 | static void on_end(EV_P_ struct evn_stream* stream) { 48 | puts("[Client] Stream On End"); 49 | 50 | //evn_stream_end(EV_A_ stream); 51 | } 52 | 53 | // Timeout 54 | static void on_timeout(EV_P_ struct evn_stream* stream) { 55 | puts("[Client] Stream On Timeout"); 56 | } 57 | 58 | // Drain 59 | static void on_drain(EV_P_ struct evn_stream* stream) { 60 | puts("[Client] Stream On Drain"); 61 | puts("\t now ending the stream"); 62 | evn_stream_end(EV_A, stream); 63 | } 64 | 65 | // Error 66 | static void on_error(EV_P_ struct evn_stream* stream, struct evn_exception* error) { 67 | puts("[Client] Stream On Error"); 68 | fprintf(stderr, "\t %s\n", error->message); 69 | } 70 | 71 | // Close 72 | static void on_close(EV_P_ struct evn_stream* stream, bool had_error) { 73 | puts("[Client] Stream On Close"); 74 | } 75 | 76 | // Connect 77 | static void on_connect(EV_P_ struct evn_stream* stream) { 78 | bool all_sent; 79 | 80 | puts("[Client] Stream On Connect"); 81 | printf("\t sending %d bytes to the server\n", sent_data_len); 82 | all_sent = evn_stream_write(EV_A_ stream, sent_data, sent_data_len); 83 | 84 | if (false == all_sent) 85 | { 86 | stream->on_drain = on_drain; 87 | puts("\t all or part of the data was queued in user memory"); 88 | puts("\t 'drain' will be emitted when the buffer is again free"); 89 | } 90 | else 91 | { 92 | puts("\t wrote all data in one shot."); 93 | evn_stream_end(EV_A_ stream); 94 | } 95 | } 96 | 97 | // 98 | // Run Application 99 | // 100 | 101 | int main (int argc, char* argv[]) { 102 | EV_P = EV_DEFAULT; 103 | char socket_address[256] = {}; 104 | 105 | if (argc > 2) { 106 | fprintf(stderr, "too many arguments\n"); 107 | return 1; 108 | } 109 | else if (1 == argc) { 110 | char* msg = "Hello World\n"; 111 | sent_data_len = strlen(msg) + 1; 112 | sent_data = malloc(sent_data_len); 113 | strncpy(sent_data, msg, sent_data_len); 114 | } 115 | else if (2 == argc) { 116 | int fd; 117 | fd = open(argv[1], O_RDONLY); 118 | if (fd > 0) { 119 | struct stat sb; 120 | int err_check; 121 | 122 | err_check = fstat(fd, &sb); 123 | if(-1 == err_check) 124 | { 125 | fprintf(stderr, "failed to stat %s: %s", argv[1], strerror(errno)); 126 | return 1; 127 | } 128 | sent_data_len = (int)sb.st_size; 129 | 130 | sent_data = (char*)calloc(1, sent_data_len + 100); 131 | err_check = (int)read(fd, sent_data, sent_data_len + 100); // try to read more just to see if it's there 132 | if (err_check != sent_data_len) 133 | { 134 | fprintf(stderr, "read %d bytes instead of the expected %d for %s: %s\n", err_check, sent_data_len, argv[1], strerror(errno)); 135 | free(sent_data); 136 | close(fd); 137 | return 1; 138 | } 139 | 140 | close(fd); 141 | } 142 | else { 143 | sent_data_len = strlen(argv[1]) + 1; 144 | sent_data = malloc(sent_data_len); 145 | strncpy(sent_data, argv[1], sent_data_len); 146 | } 147 | } 148 | else { 149 | fprintf(stderr, "I don't understand how you got an argc of %d\n", argc); 150 | return 1; 151 | } 152 | 153 | snprintf(socket_address, 256, "%s%i%s", "/tmp/libevnet-echo.", (int) getuid(), ".sock"); 154 | printf("socket_address: %s\n", socket_address); 155 | 156 | struct evn_stream* stream = evn_create_connection(EV_A_ 0, socket_address); 157 | 158 | if (stream) { 159 | stream->on_connect = on_connect; 160 | stream->on_secure = on_secure; 161 | stream->on_data = on_data; 162 | stream->oneshot = true; 163 | stream->on_end = on_end; 164 | stream->on_timeout = on_timeout; 165 | //stream->on_drain = on_drain; 166 | stream->on_error = on_error; 167 | stream->on_close = on_close; 168 | } 169 | 170 | ev_loop(EV_A_ 0); 171 | free(sent_data); 172 | puts("[Client] No events left"); 173 | return 0; 174 | } 175 | 176 | -------------------------------------------------------------------------------- /source/echo-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "evn.h" 5 | 6 | // 7 | // Event Callbacks 8 | // 9 | 10 | static void on_drain(EV_P, struct evn_stream* stream) { 11 | puts("[Server] On (Stream) Drain"); 12 | evn_stream_end(EV_A, stream); 13 | } 14 | 15 | // Data 16 | static void on_data(EV_P_ struct evn_stream* stream, void* data, int size) 17 | { 18 | bool all_sent; 19 | FILE* file; 20 | 21 | file = fopen("./echo-server-receive.dat", "wb"); 22 | if (NULL == file) 23 | { 24 | perror("failed to open file to write received data"); 25 | } 26 | else 27 | { 28 | fwrite(data, size, 1, file); 29 | fclose(file); 30 | } 31 | 32 | puts("[Server] On (Stream) Data"); 33 | printf("\t received %d bytes, now resending to the client\n", size); 34 | all_sent = evn_stream_write(EV_A_ stream, data, size); 35 | 36 | if (false == all_sent) 37 | { 38 | puts("\t all or part of the data was queued in user memory"); 39 | puts("\t 'drain' will be emitted when the buffer is again free, and we will then end the socket"); 40 | stream->on_drain = on_drain; 41 | } 42 | else 43 | { 44 | puts("\t wrote all data in one shot."); 45 | evn_stream_end(EV_A_ stream); 46 | } 47 | } 48 | 49 | // End 50 | static void on_stream_end(EV_P_ struct evn_stream* stream) 51 | { 52 | puts("[Server] On (Stream) End"); 53 | } 54 | 55 | // Close 56 | static void on_stream_close(EV_P_ struct evn_stream* stream, bool had_error) 57 | { 58 | //struct evn_server* server = stream->server; 59 | puts("[Server] On (Stream) Close"); 60 | if (true == had_error) 61 | { 62 | fprintf(stderr, "\t[Server] Stream Close had an error\n"); 63 | } 64 | 65 | //evn_server_close(EV_A_ server); 66 | } 67 | 68 | static void on_stream_timeout(EV_P, struct evn_stream* stream) 69 | { 70 | puts("connection timed out, destroying stream"); 71 | evn_stream_destroy(EV_A, stream); 72 | } 73 | 74 | // Connection 75 | static void on_connection(EV_P_ struct evn_server* server, struct evn_stream* stream) 76 | { 77 | puts("[Server] On Connection"); 78 | stream->on_data = on_data; 79 | stream->on_end = on_stream_end; 80 | stream->on_close = on_stream_close; 81 | stream->on_timeout = on_stream_timeout; 82 | stream->oneshot = true; 83 | 84 | evn_stream_set_timeout(EV_A, stream, 4000); 85 | } 86 | 87 | //// Listen 88 | //static void on_listen(EV_P_ struct evn_server* server) 89 | //{ 90 | //puts("[Server] On Listen"); 91 | //} 92 | 93 | // Close 94 | static void on_server_close(EV_P, struct evn_server* server) 95 | { 96 | puts("[Server] on server close"); 97 | } 98 | 99 | // 100 | // Run Application 101 | // 102 | int main (int argc, char* argv[]) 103 | { 104 | EV_P = ev_default_loop(0); 105 | struct evn_server* server; 106 | 107 | // Create unix socket in non-blocking fashion 108 | char socket_address[256] = {}; 109 | snprintf(socket_address, 256, "%s%i%s", "/tmp/libevnet-echo.", (int) getuid(), ".sock"); 110 | printf("socket_address: %s\n", socket_address); 111 | unlink(socket_address); 112 | 113 | server = evn_server_create(EV_A_ on_connection); 114 | //server->on_listen = on_listen; 115 | server->on_close = on_server_close; 116 | evn_server_listen(server, 0, socket_address); 117 | 118 | ev_loop(EV_A_ 0); 119 | puts("[Server] No events left"); 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /source/evn-assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void assert(int truth) 5 | { 6 | static int count = 0; 7 | if (!truth) 8 | { 9 | fprintf(stderr, "failed assertion %d\n", count); 10 | exit(EXIT_FAILURE); 11 | } 12 | else 13 | { 14 | printf("Pass\n"); 15 | } 16 | count += 1; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /source/evn-buffer-list-test.c: -------------------------------------------------------------------------------- 1 | #include // strlen 2 | #include // printf 3 | #include // exit 4 | 5 | #include "evn-buffer-list.h" 6 | #include "evn-assert.c" 7 | 8 | int main (int argc, char* argv[]) 9 | { 10 | char* str = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 )!@#$%^&*("; // 75 chars // 76 bytes 11 | char* str_buf2 = malloc(strlen(str)); 12 | int bytes_copied = 0; 13 | memcpy(str_buf2, str, strlen(str) + 1); 14 | printf("%s\n%d\n", str_buf2, (int) strlen(str_buf2)); 15 | 16 | 17 | // 18 | // Buffer 19 | // 20 | 21 | // Create a buffer of `size` bytes 22 | evn_buffer* buffer0 = evn_buffer_create(128); 23 | assert(NULL != buffer0); 24 | assert(NULL != buffer0->data); 25 | assert(128 == buffer0->capacity); 26 | assert(0 == buffer0->used); 27 | 28 | // Create a buffer of `size` by `memcpy`ing `data` 29 | evn_buffer* buffer1 = evn_buffer_create_copy(str, 27); 30 | printf("abcdefghijklmnopqrstuvwxyz\n==\n%s\n", (char*) buffer1->data); 31 | assert(NULL != buffer1); 32 | assert(NULL != buffer1->data); 33 | assert(27 == buffer1->capacity); 34 | assert(27 == buffer1->used); 35 | assert(0 == memcmp(buffer1->data, str, 27)); 36 | ((char*)buffer1->data)[26] = '\0'; 37 | 38 | // Create a buffer from existing data where `index` is the first unused byte (or `size` if used) 39 | evn_buffer* buffer2 = evn_buffer_create_as(str_buf2, strlen(str) + 1, 27); 40 | assert(NULL != buffer2); 41 | assert(NULL != buffer2->data); 42 | assert(strlen(str) + 1 == buffer2->capacity); 43 | assert(27 == buffer2->used); 44 | assert(0 == memcmp(buffer2->data, str, 27)); 45 | 46 | // Copy `size` bytes of `data` to existing buffer 47 | bytes_copied = evn_buffer_add(buffer2, "0123456789 ", 11); // discards trailing \0 48 | printf("abcdefghijklmnopqrstuvwxyz 0123456789 ...\n==\n%s\n%d\n", (char*) buffer2->data, bytes_copied); 49 | assert(11 == bytes_copied); 50 | assert(strlen(str) + 1 == buffer2->capacity); 51 | assert(38 == buffer2->used); 52 | 53 | // TODO evn_buffer_add_realloc 54 | 55 | 56 | // check that remainder is correct 57 | bytes_copied = evn_buffer_add(buffer2, str, 40); // should copy 38 bytes, leaving 2 uncopied 58 | ((char*)buffer2->data)[75] = '\0'; 59 | printf("abcdefghijklmnopqrstuvwxyz 0123456789 abcdef...\n==\n%s\n", (char*) buffer2->data); 60 | assert(strlen(str) + 1 == buffer2->capacity); 61 | assert(buffer2->used == buffer2->capacity); 62 | assert(buffer2->capacity - 38 == bytes_copied); 63 | 64 | 65 | // 66 | // BufferList 67 | // 68 | printf("BufferList tests...\n"); 69 | 70 | // Create a "smart" buffer 71 | evn_bufferlist* bufferlist = evn_bufferlist_create(40, 1); 72 | assert(40 == bufferlist->block_size); 73 | assert(1 == bufferlist->length); 74 | assert(0 == bufferlist->used); 75 | assert(0 == bufferlist->size); 76 | 77 | // Copy data into the buffer 78 | int result; 79 | result = evn_bufferlist_add(bufferlist, &str[60], 15); 80 | assert(0 == result); 81 | assert(1 == bufferlist->length); 82 | assert(15 == bufferlist->used); 83 | assert(40 == bufferlist->size); 84 | 85 | // Copy more data into the buffer than the buffer can hold 86 | result = evn_bufferlist_add(bufferlist, &str, 75); 87 | assert(0 == result); 88 | assert(2 == bufferlist->length); 89 | assert(90 == bufferlist->used); 90 | assert(120 == bufferlist->size); // 40 + 80 91 | 92 | // Copy all data into one big buffer 93 | evn_buffer* buffer3 = evn_bufferlist_concat(bufferlist); 94 | assert(90 == buffer3->used); 95 | assert(90 == buffer3->capacity); 96 | assert(0 == buffer3->free); 97 | 98 | // 99 | // Memory Leak Tests 100 | // 101 | evn_buffer_destroy(buffer3); 102 | evn_buffer_destroy(buffer2); 103 | evn_buffer_destroy(buffer1); 104 | evn_buffer_destroy(buffer0); 105 | evn_bufferlist_destroy(bufferlist); 106 | /* 107 | printf("bs %d\n", bufferlist->block_size); 108 | printf("len %d\n", bufferlist->length); 109 | printf("used %d\n", bufferlist->used); 110 | printf("size %d\n", bufferlist->size); 111 | 112 | // Realloc the current buffer and then add a buffer after it 113 | int evn_bufferlist_add_buffer(evn_bufferlist* bufferlist, evn_buffer* buffer); 114 | 115 | evn_buffer* buffer2 = evn_buffer_create_using((void*) data, 2048); 116 | 117 | evn_bufferlist* bufferlist = evn_bufferlist_create(1,1); 118 | 119 | assert(strlen(str) == bufferlist->block_size) 120 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 121 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 122 | 123 | evn_bufferlist_add_buffer(bufferlist, buffer1); 124 | assert(2048 == bufferlist->block_size) 125 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 126 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 127 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 128 | 129 | evn_bufferlist_add_buffer(bufferlist, buffer2); 130 | evn_bufferlist_add_copy(bufferlist, str, strlen(str)); 131 | */ 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /source/evn-buffer-list.c: -------------------------------------------------------------------------------- 1 | // TODO variant with compile-time sizes 2 | 3 | #include // malloc, calloc, realloc 4 | #include // memcpy 5 | 6 | #include "evn-buffer-list.h" 7 | 8 | // indices are always at the 'free' position 9 | // if the position is equal to the length, the buffer is full 10 | // 11 | 12 | inline evn_buffer* evn_buffer_create(int size) 13 | { 14 | void* data = malloc(size); 15 | evn_buffer* buffer = malloc(sizeof(evn_buffer)); 16 | // TODO check memory 17 | buffer->data = data; 18 | buffer->position = data; 19 | buffer->used = 0; 20 | buffer->capacity = size; 21 | buffer->free = size; 22 | 23 | return buffer; 24 | } 25 | 26 | // Destory a buffer created with `evn_buffer_create<_xyz_>(int size)` 27 | void evn_buffer_destroy(evn_buffer* buffer) 28 | { 29 | free(buffer->data); 30 | free(buffer); 31 | } 32 | 33 | int evn_buffer_init(evn_buffer* buffer, int size) 34 | { 35 | void* data = malloc(size); 36 | if (NULL == data) 37 | { 38 | return -1; 39 | } 40 | buffer->data = data; 41 | buffer->position = data; 42 | buffer->used = 0; 43 | buffer->capacity = size; 44 | buffer->free = size; 45 | 46 | return 0; 47 | } 48 | 49 | inline evn_buffer* evn_buffer_create_copy(void* data, int size) 50 | { 51 | evn_buffer* buffer = evn_buffer_create(size); 52 | memcpy(buffer->data, data, size); 53 | buffer->position = data + size; 54 | buffer->used = size; 55 | buffer->capacity = size; 56 | buffer->free = 0; 57 | 58 | return buffer; 59 | } 60 | 61 | inline evn_buffer* evn_buffer_create_as(void* data, int size, int index) 62 | { 63 | evn_buffer* buffer = malloc(sizeof(evn_buffer)); 64 | buffer->data = data; 65 | buffer->position = data + index; 66 | buffer->used = index; 67 | buffer->capacity = size; 68 | buffer->free = size - index; 69 | 70 | return buffer; 71 | } 72 | 73 | // copies data into existing buffer and 74 | // returns the number of bytes added 75 | int evn_buffer_add(evn_buffer* buffer, void* data, int size) 76 | { 77 | int free_space = buffer->capacity - buffer->used; 78 | int bytes_to_add; 79 | if (free_space >= size) 80 | { 81 | bytes_to_add = size; 82 | } 83 | else 84 | { 85 | bytes_to_add = free_space; 86 | } 87 | memcpy(buffer->position, data, bytes_to_add); 88 | buffer->position += bytes_to_add; 89 | 90 | buffer->free -= bytes_to_add; 91 | buffer->used += bytes_to_add; 92 | 93 | return bytes_to_add; 94 | } 95 | 96 | // creates bufferlist 97 | evn_bufferlist* evn_bufferlist_create(int min_block_size, int min_slice_count) 98 | { 99 | if (min_block_size < 1) 100 | { 101 | min_block_size = 4096; 102 | } 103 | if (min_slice_count < 1) 104 | { 105 | min_slice_count = 128; 106 | } 107 | evn_bufferlist* bufferlist = (evn_bufferlist*) calloc(1, sizeof(evn_bufferlist)); 108 | bufferlist->block_size = min_block_size; 109 | bufferlist->length = min_slice_count; 110 | bufferlist->index = -1; 111 | bufferlist->used = 0; 112 | bufferlist->size = 0; 113 | 114 | bufferlist->list = (evn_buffer*) calloc(bufferlist->length, sizeof(evn_buffer)); 115 | bufferlist->current = bufferlist->list - 1; 116 | return bufferlist; 117 | } 118 | 119 | int evn_bufferlist_next_buffer(evn_bufferlist* bufferlist) 120 | { 121 | bufferlist->index += 1; 122 | bufferlist->current += 1; 123 | if (bufferlist->index == bufferlist->length) 124 | { 125 | bufferlist->length *= 2; 126 | bufferlist->list = (evn_buffer*) realloc(bufferlist->list, bufferlist->length * sizeof(evn_buffer)); 127 | if (NULL == bufferlist->list) 128 | { 129 | // TODO printf("Out-of-Memory! Start Panicking!"); 130 | return -1; 131 | } 132 | bufferlist->current = bufferlist->list + bufferlist->index; 133 | } 134 | evn_buffer_init(bufferlist->current, bufferlist->block_size); 135 | bufferlist->size += bufferlist->block_size; 136 | 137 | return 0; 138 | } 139 | 140 | // copies data into underlying buffers 141 | int evn_bufferlist_add(evn_bufferlist* bufferlist, void* data, int size) 142 | { 143 | int total_bytes_copied = 0; 144 | int bytes_to_copy = size; 145 | int bytes_copied; 146 | 147 | while (bufferlist->block_size < size) 148 | { 149 | bufferlist->block_size *= 2; 150 | } 151 | 152 | if (-1 == bufferlist->index) 153 | { 154 | evn_bufferlist_next_buffer(bufferlist); 155 | } 156 | 157 | while (total_bytes_copied != bytes_to_copy) 158 | { 159 | if (total_bytes_copied > bytes_to_copy) { 160 | fprintf(stderr, "[EVN-buffer-list] somehow wrote more data to the buffer list than told to\n"); 161 | exit(EXIT_FAILURE); 162 | } 163 | 164 | bytes_copied = evn_buffer_add(bufferlist->current, data, size); 165 | if (bytes_copied != size) 166 | { 167 | evn_bufferlist_next_buffer(bufferlist); 168 | } 169 | size -= bytes_copied; 170 | data += bytes_copied; 171 | total_bytes_copied += bytes_copied; 172 | } 173 | bufferlist->used += total_bytes_copied; 174 | 175 | // TODO check memory copy 176 | return 0; 177 | } 178 | 179 | evn_buffer* evn_bufferlist_concat(evn_bufferlist* bufferlist) 180 | { 181 | int i; 182 | int data_moved = 0; 183 | void* data = malloc(bufferlist->used); 184 | void* temp_ptr = data; 185 | evn_buffer* item = bufferlist->list; 186 | evn_buffer* buffer; 187 | 188 | for (i = 0; i <= bufferlist->index; i += 1) 189 | { 190 | memcpy(temp_ptr, item->data, item->used); 191 | temp_ptr += item->used; 192 | data_moved += item->used; 193 | item += 1; 194 | } 195 | 196 | if(data_moved != bufferlist->used) { 197 | fprintf(stderr, "[EVN-buffer-list] bufferlist_concat did not move the proper amount of data to the new buffer\n"); 198 | fprintf(stderr, "\tmoved %d, and expected to move %d\n", data_moved, bufferlist->used); 199 | } 200 | 201 | buffer = evn_buffer_create_as(data, bufferlist->used, bufferlist->used); 202 | return buffer; 203 | } 204 | 205 | void evn_bufferlist_destroy(evn_bufferlist* bufferlist) 206 | { 207 | int i; 208 | evn_buffer* item = bufferlist->list; 209 | 210 | for (i = 0; i < bufferlist->length; i += 1) 211 | { 212 | // we calloc the list in bufferlist, so if we didn't malloc anything item->data should be NULL (ok to free) 213 | free(item->data); 214 | item += 1; 215 | } 216 | 217 | free(bufferlist->list); 218 | free(bufferlist); 219 | } 220 | /* 221 | 222 | // adds the buffer, possibly leaving the previous buffer partially used 223 | int evn_bufferlist_add_buffer(evn_bufferlist* bufferlist, evn_buffer* buffer) 224 | { 225 | void ** list; 226 | // Check that there is room in the buffer list for more data 227 | if (bufferlist->size == bufferlist->index) 228 | { 229 | list = realloc(bufferlist->list, sizeof(size_t) * buffrlist->size * 2); 230 | if (NULL == list) 231 | { 232 | evn_error("Out of Memory\n"); 233 | return -1; 234 | } 235 | bufferlist->list = list; 236 | bufferlist->size *= 2; 237 | } 238 | bufferlist->size += buffer->index + 1; // in case the buffer isn't full 239 | bufferlist->index += 1; 240 | bufferlist->list[bufferlist->index] = buffer; 241 | return 0; 242 | } 243 | */ 244 | -------------------------------------------------------------------------------- /source/evn-inbuf-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "evn-inbuf.h" 4 | #include "evn-assert.c" 5 | #include "string.h" 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | evn_inbuf* buf; 10 | const char* str_const = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 36 + 1 11 | char* str = malloc(strlen(str_const) + 1); 12 | memcpy(str, str_const, strlen(str_const) + 1); 13 | 14 | buf = evn_inbuf_create(2); 15 | assert(4 == buf->end - buf->start); 16 | assert(0 == buf->top - buf->bottom); 17 | assert(0 == buf->size); 18 | 19 | evn_inbuf_add(buf, "ab", 2); 20 | assert(4 == buf->end - buf->start); 21 | assert(2 == buf->top - buf->bottom); 22 | assert(2 == buf->size); 23 | 24 | evn_inbuf_add(buf, "cd", 2); 25 | assert(4 == buf->end - buf->start); 26 | assert(4 == buf->top - buf->bottom); 27 | assert(4 == buf->size); 28 | 29 | evn_inbuf_add(buf, "efg", 3); // ignore \0 30 | assert(8 == buf->end - buf->start); 31 | assert(7 == buf->top - buf->bottom); 32 | assert(7 == buf->size); 33 | 34 | assert(-1 == evn_inbuf_peek(buf, str, 8)); 35 | 36 | evn_inbuf_peek(buf, str, 7); 37 | printf("abcdefg789... ==\n%s\n", str); // abcdefg789...\0 38 | 39 | evn_inbuf_toss(buf, 4); 40 | assert(8 == buf->end - buf->start); 41 | assert(3 == buf->top - buf->bottom); 42 | assert(3 == buf->size); 43 | 44 | evn_inbuf_add(buf, "hi", 3); // include \0 45 | assert(8 == buf->end - buf->start); 46 | assert(6 == buf->top - buf->bottom); 47 | assert(6 == buf->size); 48 | 49 | printf("efghi ==\n%s\n", (char*) buf->bottom); // efghi\0 50 | 51 | evn_inbuf_destroy(buf); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /source/evn-inbuf.c: -------------------------------------------------------------------------------- 1 | #include // malloc 2 | #include // memcpy 3 | 4 | //#include 5 | 6 | #include "evn-inbuf.h" 7 | 8 | static inline void* bufcpy(void* dst, void* src, size_t size) 9 | { 10 | char* d = (char*) dst; 11 | char* s = (char*) src; 12 | size_t i; 13 | for (i = 0; i < size; i += 1) 14 | { 15 | *d = *s; 16 | d += 1; 17 | s += 1; 18 | } 19 | return dst; 20 | } 21 | 22 | evn_inbuf* evn_inbuf_create(int size) 23 | { 24 | evn_inbuf* buf = malloc(sizeof (evn_inbuf)); 25 | evn_inbuf_init(buf, size); 26 | return buf; 27 | } 28 | 29 | void evn_inbuf_destroy(evn_inbuf* buf) 30 | { 31 | free(buf->start); 32 | free(buf); 33 | } 34 | 35 | int evn_inbuf_init(evn_inbuf* buf, int size) 36 | { 37 | void * new_data; 38 | 39 | size *= 2; 40 | new_data = malloc(size); 41 | 42 | buf->start = new_data; 43 | buf->end = new_data + size; 44 | buf->bottom = new_data; 45 | buf->top = new_data; 46 | 47 | buf->size = 0; 48 | 49 | // TODO don't assume that malloc worked 50 | return 0; 51 | } 52 | 53 | int evn_inbuf_peek(evn_inbuf* buf, void* data, int size) 54 | { 55 | //void* new_data; 56 | 57 | if (buf->size < size) 58 | { 59 | return -1; 60 | } 61 | 62 | //new_data = malloc(size); 63 | memcpy(data, buf->bottom, size); 64 | 65 | //return new_data; 66 | return 0; 67 | } 68 | 69 | void evn_inbuf_toss(evn_inbuf* buf, int size) 70 | { 71 | if (buf->bottom + size >= buf->top) 72 | { 73 | buf->bottom = buf->start; 74 | buf->top = buf->start; 75 | buf->size = 0; 76 | return; 77 | } 78 | 79 | buf->bottom += size; 80 | buf->size -= size; 81 | } 82 | 83 | int evn_inbuf_add(evn_inbuf* buf, void* data, int size) 84 | { 85 | size_t capacity = buf->end - buf->start; 86 | size_t used = buf->top - buf->bottom; 87 | size_t leading = buf->bottom - buf->start; 88 | size_t trailing = buf->end - buf->top; 89 | void* new_data; 90 | 91 | if (NULL == data || 0 == size) 92 | { 93 | return 0; 94 | } 95 | 96 | if (used != buf->size) 97 | { 98 | //fprintf(stderr, "inbuf size(%d) does not match the spacing between top and bottom pointers(%zu)\n", buf->size, used); 99 | return 1; 100 | } 101 | 102 | if ( (0 == used) && (buf->bottom != buf->start) ) 103 | { 104 | // this shouldn't happen, but just in case it does ... 105 | //printf("start = %p, bottom = %p, top = %p\n", buf->start, buf->bottom, buf->top); 106 | buf->bottom = buf->start; 107 | buf->top = buf->start; 108 | 109 | capacity = buf->end - buf->start; 110 | used = buf->top - buf->bottom; 111 | leading = buf->bottom - buf->start; 112 | trailing = buf->end - buf->top; 113 | } 114 | 115 | buf->size = used + size; 116 | 117 | if (size <= trailing) 118 | { 119 | // use the space we still have available 120 | buf->top = memcpy(buf->top, data, size) + size; 121 | } 122 | else if (size <= (leading + trailing) && leading >= (capacity / 2)) 123 | { 124 | // since memmove() might use a terciary buffer 125 | // it's faster to alloc new 126 | // in cases where the buffer is ~100 chars, a loop would prove faster 127 | new_data = malloc(capacity); 128 | memcpy(new_data, buf->bottom, used); 129 | memcpy(new_data + used, data, size); 130 | free(buf->start); 131 | 132 | buf->start = new_data; 133 | buf->end = new_data + capacity; 134 | buf->bottom = new_data; 135 | buf->top = new_data + used + size; 136 | } 137 | else 138 | { 139 | do { 140 | trailing += capacity; 141 | capacity *= 2; 142 | } while (size > trailing); 143 | 144 | new_data = realloc(buf->start, capacity); 145 | // TODO if new_data == buf->start 146 | buf->start = new_data; 147 | buf->end = new_data + capacity; 148 | buf->bottom = new_data + leading; 149 | buf->top = buf->bottom + used; 150 | 151 | buf->top = memcpy(buf->top, data, size) + size; 152 | } 153 | 154 | // TODO don't assume that malloc worked 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /source/evn-test-old.c: -------------------------------------------------------------------------------- 1 | #include "evn.h" 2 | #include // realloc, calloc 3 | #include "stdio.h" // puts 4 | #include "stdio.h" // puts 5 | #include // unlink 6 | 7 | struct my_state { 8 | bool acquiring; 9 | char type; 10 | char* data; 11 | long long size; 12 | }; 13 | 14 | inline static struct my_state* 15 | my_state_new() 16 | { 17 | return (struct my_state*) calloc(1, sizeof(struct my_state)); 18 | } 19 | 20 | /* 21 | * Client Event Handlers 22 | * 23 | */ 24 | void str_connect(struct evn_server* server, struct evn_stream* client) 25 | { 26 | puts("client ready to Send and Receive data"); 27 | struct timeval tp = {0, 500000}; // 100ms 28 | evn_stream_set_timeout(client, tp); // us 29 | } 30 | 31 | void str_data(struct evn_server* server, struct evn_stream* client, int size) 32 | { 33 | struct my_state* state = (struct my_state*) client->data; 34 | 35 | 36 | // TODO create inline callback / macro 37 | state->data = malloc((size_t) size); 38 | if (size != recv(client->fd, state->data, size, 0)) 39 | { 40 | puts("very much unexpected size"); 41 | } 42 | else 43 | { 44 | puts("received expected size"); 45 | } 46 | 47 | if (state->acquiring) 48 | { 49 | // add this data to prior data 50 | puts("aggregating data"); 51 | } 52 | else 53 | { 54 | switch (state->data[0]) { 55 | case '.': 56 | // do something 57 | puts("received '.'"); 58 | break; 59 | case 's': 60 | // update settings 61 | puts("received 's'"); 62 | break; 63 | case 'x': 64 | // close and quit 65 | puts("received 'x'"); 66 | evn_server_close(server); 67 | break; 68 | default: 69 | // accumulate data 70 | puts("received unknown signal"); 71 | break; 72 | } 73 | } 74 | free(state->data); 75 | } 76 | 77 | void str_end(struct evn_server* server, struct evn_stream* client) 78 | { 79 | evn_stream_close(client); 80 | puts("client disconnected"); 81 | } 82 | 83 | void str_timeout(struct evn_server* server, struct evn_stream* client) 84 | { 85 | evn_stream_close(client); 86 | puts("client timed out (and was forced to disconnect)"); 87 | } 88 | 89 | void str_close(struct evn_server* server, struct evn_stream* client, bool had_error) 90 | { 91 | puts("disconnect complete"); 92 | } 93 | 94 | 95 | /* 96 | * Server Event Handlers 97 | * 98 | */ 99 | void srv_connection(struct evn_server* server, struct evn_stream* client) 100 | { 101 | puts("A client stream connection is established"); 102 | struct evn_stream_callbacks* cbs = &client->callbacks; 103 | 104 | cbs->connect = str_connect; 105 | cbs->data = str_data; 106 | cbs->end = str_end; 107 | cbs->timeout = str_timeout; 108 | cbs->close = str_close; 109 | 110 | // store custom data 111 | client->data = (void*) my_state_new(); 112 | } 113 | 114 | void srv_close(struct evn_server* server) { 115 | puts("The server has closed"); 116 | } 117 | 118 | /* 119 | * Run Application 120 | * 121 | */ 122 | int main (int argc, char* argv[]) 123 | { 124 | EV_P; 125 | EVN_SRV_P; 126 | struct evn_server_callbacks srv_callbacks = { 127 | NULL, 128 | srv_connection, 129 | srv_close 130 | }; 131 | 132 | EV_A = ev_default_loop(0); 133 | 134 | EVN_SRV_A = evn_server_create(EV_A_ srv_callbacks); 135 | evn_server_set_max_queue(EVN_SRV_A, 128); 136 | // We don't care if this did or didn't exist 137 | unlink("/tmp/evn-test.sock"); 138 | evn_server_listen(EVN_SRV_A, 0, "/tmp/evn-test.sock"); 139 | 140 | // TODO set timeout 141 | // evn_server_close(srv); 142 | 143 | ev_loop(EV_A_ 0); 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /source/evn.c: -------------------------------------------------------------------------------- 1 | 2 | #include // errno 3 | #include // close 4 | #include // free 5 | #include // memcpy 6 | 7 | #include "evn.h" 8 | #include "crossnet.h" 9 | 10 | #define EVN_MAX_RECV 4096 11 | 12 | static int evn_priv_unix_serverfd_create(struct sockaddr_un* socket_un, char* sock_path); 13 | static int evn_priv_tcp_serverfd_create(struct sockaddr_in* socket_in, int port, char* sock_path); 14 | static void evn_server_priv_on_connection(EV_P_ ev_io *w, int revents); 15 | 16 | static void evn_stream_priv_on_connect(EV_P_ ev_io *w, int revents); 17 | static inline void evn_stream_priv_on_activity(EV_P_ ev_io *w, int revents); 18 | static void evn_stream_timer_priv_cb(EV_P, ev_timer* w, int revents); 19 | static void evn_stream_priv_on_readable(EV_P_ ev_io *w, int revents); 20 | static void evn_stream_priv_on_writable(EV_P_ ev_io *w, int revents); 21 | static bool evn_stream_priv_send(EV_P, struct evn_stream* stream, void* data, int size); 22 | 23 | // Simply adds O_NONBLOCK to the file descriptor of choice 24 | int evn_set_nonblock(int fd) { 25 | int flags; 26 | 27 | flags = fcntl(fd, F_GETFL); 28 | flags |= O_NONBLOCK; 29 | return fcntl(fd, F_SETFL, flags); 30 | } 31 | 32 | struct evn_server* evn_server_create(EV_P_ evn_server_on_connection* on_connection) { 33 | struct evn_server* server = calloc(1, sizeof(struct evn_server)); 34 | server->EV_A = EV_A; 35 | server->socket = NULL; 36 | server->on_connection = on_connection; 37 | return server; 38 | } 39 | 40 | int evn_server_listen(struct evn_server* server, int port, char* address) { 41 | int max_queue = 1024; 42 | int fd; 43 | struct sockaddr_un* sock_unix; 44 | struct sockaddr_in* sock_inet; 45 | struct evn_exception error; 46 | int reuse = 1; 47 | // TODO array_init(&server->streams, 128); 48 | 49 | if (0 == port) 50 | { 51 | server->socket = malloc(sizeof(struct sockaddr_un)); 52 | sock_unix = (struct sockaddr_un*) server->socket; 53 | evn_debug("%s\n", address); 54 | fd = evn_priv_unix_serverfd_create(sock_unix, address); 55 | server->socket_len = sizeof(sock_unix->sun_family) + strlen(sock_unix->sun_path) + 1; 56 | } 57 | else 58 | { 59 | server->socket = malloc(sizeof(struct sockaddr_in)); 60 | sock_inet = (struct sockaddr_in*) server->socket; 61 | fd = evn_priv_tcp_serverfd_create(sock_inet, port, address); 62 | server->socket_len = sizeof(struct sockaddr); 63 | } 64 | 65 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse); 66 | 67 | // put this up here so that if we need to destroy the server on error, the fd will be available 68 | // in io so we can close it. 69 | ev_io_init(&server->io, evn_server_priv_on_connection, fd, EV_READ); 70 | 71 | if (-1 == fd) 72 | { 73 | if (server->on_error) 74 | { 75 | error.error_number = errno; 76 | snprintf(error.message, sizeof error.message, "[EVN] failed to open server file descriptor: %s", strerror(errno)); 77 | server->on_error(server->EV_A, server, &error); 78 | } 79 | evn_server_destroy(server->EV_A, server); 80 | return -1; 81 | } 82 | 83 | 84 | if (-1 == bind(fd, (struct sockaddr*) server->socket, server->socket_len)) 85 | { 86 | if (server->on_error) 87 | { 88 | error.error_number = errno; 89 | snprintf(error.message, sizeof error.message, "[EVN] failed to bind server: %s", strerror(errno)); 90 | server->on_error(server->EV_A, server, &error); 91 | } 92 | evn_server_destroy(server->EV_A, server); 93 | return -1; 94 | } 95 | 96 | // TODO max_queue 97 | if (-1 == listen(fd, max_queue)) { 98 | if (server->on_error) 99 | { 100 | error.error_number = errno; 101 | snprintf(error.message, sizeof error.message, "[EVN] listen failed: %s", strerror(errno)); 102 | server->on_error(server->EV_A, server, &error); 103 | } 104 | evn_server_destroy(server->EV_A, server); 105 | return -1; 106 | } 107 | 108 | ev_io_start(server->EV_A_ &server->io); 109 | 110 | return 0; 111 | } 112 | 113 | static int evn_priv_tcp_serverfd_create(struct sockaddr_in* socket_in, int port, char* address) { 114 | int fd; 115 | 116 | // int optval = 1 117 | // setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) 118 | 119 | // Setup a tcp socket listener. 120 | fd = socket(AF_INET, SOCK_STREAM, 0); 121 | if (-1 == fd) { 122 | return -1; 123 | } 124 | 125 | // Set it non-blocking 126 | if (-1 == evn_set_nonblock(fd)) { 127 | close(fd); 128 | return -1; 129 | } 130 | 131 | // Set it as a tcp socket 132 | socket_in->sin_family = AF_INET; 133 | socket_in->sin_addr.s_addr = inet_addr(address); 134 | socket_in->sin_port = htons(port); 135 | 136 | return fd; 137 | } 138 | 139 | static int evn_priv_unix_serverfd_create(struct sockaddr_un* socket_un, char* sock_path) { 140 | int fd; 141 | 142 | unlink(sock_path); 143 | 144 | // Setup a unix socket listener. 145 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 146 | if (-1 == fd) { 147 | return -1; 148 | } 149 | 150 | // Set it non-blocking 151 | if (-1 == evn_set_nonblock(fd)) { 152 | close(fd); 153 | return -1; 154 | } 155 | 156 | // Set it as unix socket 157 | socket_un->sun_family = AF_UNIX; 158 | strcpy(socket_un->sun_path, sock_path); 159 | evn_debug("sock_path: %s\n", sock_path); 160 | evn_debug("socket_un->sun_path: %s\n", socket_un->sun_path); 161 | 162 | return fd; 163 | } 164 | 165 | static void evn_server_priv_on_connection(EV_P_ ev_io *w, int revents) { 166 | evn_debugs("new connection - EV_READ - server->io.fd has become readable"); 167 | 168 | int stream_fd; 169 | struct evn_stream* stream; 170 | struct evn_exception error; 171 | 172 | // since ev_io is the first member, 173 | // watcher `w` has the address of the 174 | // start of the evn_server struct 175 | struct evn_server* server = (struct evn_server*) w; 176 | 177 | while (1) 178 | { 179 | stream_fd = accept(server->io.fd, NULL, NULL); 180 | if (stream_fd == -1) 181 | { 182 | if( errno != EAGAIN && errno != EWOULDBLOCK ) 183 | { 184 | if (server->on_error) 185 | { 186 | error.error_number = errno; 187 | snprintf(error.message, sizeof error.message, "[EVN] accept failed for abnormal reason: %s", strerror(errno)); 188 | server->on_error(server->EV_A, server, &error); 189 | } 190 | evn_server_destroy(EV_A, server); 191 | return; 192 | } 193 | break; 194 | } 195 | stream = evn_stream_create(EV_A, stream_fd); 196 | stream->server = server; 197 | stream->ready_state = evn_OPEN; 198 | if (server->on_connection) { server->on_connection(EV_A_ server, stream); } 199 | if (true == stream->oneshot) 200 | { 201 | // each buffer chunk should be at least 4096 and we'll start with 128 chunks 202 | // the size will grow if the actual data received is larger 203 | stream->bufferlist = evn_bufferlist_create(4096, 128); 204 | } 205 | //stream->index = array_push(&server->streams, stream); 206 | ev_io_start(EV_A_ &stream->io); 207 | } 208 | evn_debugs("."); 209 | } 210 | 211 | int evn_server_close(EV_P_ struct evn_server* server) { 212 | close(server->io.fd); 213 | ev_io_stop(server->EV_A_ &server->io); 214 | 215 | if (server->on_close) { server->on_close(server->EV_A_ server); } 216 | 217 | if (NULL != server->socket) 218 | { 219 | free(server->socket); 220 | server->socket = NULL; 221 | } 222 | free(server); 223 | return 0; 224 | } 225 | 226 | int evn_server_destroy(EV_P_ struct evn_server* server) { 227 | return evn_server_close(EV_A_ server); 228 | } 229 | 230 | // this function creates a stream struct that uses the specified file descriptor 231 | // used both server side and client side 232 | inline struct evn_stream* evn_stream_create(EV_P, int fd) { 233 | evn_debug("evn_stream_create"); 234 | struct evn_stream* stream; 235 | 236 | stream = calloc(1, sizeof(struct evn_stream)); 237 | stream->EV_A = EV_A; 238 | stream->_priv_out_buffer = evn_inbuf_create(EVN_MAX_RECV); 239 | stream->oneshot = false; 240 | stream->bufferlist = NULL; 241 | stream->socket = NULL; 242 | stream->server = NULL; 243 | stream->timer.stream = stream; 244 | stream->timer.timeout = 0; 245 | evn_set_nonblock(fd); 246 | ev_io_init(&stream->io, evn_stream_priv_on_activity, fd, EV_READ | EV_WRITE); 247 | 248 | evn_debugs("."); 249 | return stream; 250 | } 251 | 252 | inline struct evn_stream* evn_create_connection(EV_P_ int port, char* address) { 253 | if (0 == port ) 254 | { 255 | return evn_create_connection_unix_stream(EV_A_ address); 256 | } 257 | return evn_create_connection_tcp_stream(EV_A_ port, address); 258 | } 259 | 260 | struct evn_stream* evn_create_connection_tcp_stream(EV_P_ int port, char* address) { 261 | int stream_fd; 262 | struct evn_stream* stream; 263 | struct sockaddr_in* socket_in; 264 | 265 | stream_fd = socket(AF_INET, SOCK_STREAM, 0); 266 | if (-1 == stream_fd) { 267 | #ifdef DEBUG 268 | perror("[EVN] TCP socket connection"); 269 | #endif 270 | return NULL; 271 | } 272 | stream = evn_stream_create(EV_A, stream_fd); 273 | stream->socket = malloc(sizeof(struct sockaddr_in)); 274 | socket_in = (struct sockaddr_in*) stream->socket; 275 | 276 | stream->ready_state = evn_OPENING; 277 | 278 | ev_io_stop(EV_A_ &stream->io); 279 | ev_io_init(&stream->io, evn_stream_priv_on_connect, stream_fd, EV_WRITE); 280 | 281 | socket_in->sin_family = AF_INET; 282 | socket_in->sin_addr.s_addr = inet_addr(address); 283 | socket_in->sin_port = htons(port); 284 | stream->socket_len = sizeof(struct sockaddr); 285 | 286 | if (-1 == connect(stream_fd, (struct sockaddr*) stream->socket, stream->socket_len)) { 287 | #ifdef DEBUG 288 | fprintf(stderr, "[EVN] connect to %s: %s\n", address, strerror(errno)); 289 | #endif 290 | evn_stream_destroy(EV_A_ stream); 291 | stream = NULL; 292 | } 293 | 294 | if (NULL != stream) 295 | { 296 | ev_io_start(EV_A_ &stream->io); 297 | } 298 | return stream; 299 | } 300 | 301 | struct evn_stream* evn_create_connection_unix_stream(EV_P_ char* sock_path) { 302 | int stream_fd; 303 | struct evn_stream* stream; 304 | struct sockaddr_un* sock; 305 | 306 | stream_fd = socket(AF_UNIX, SOCK_STREAM, 0); 307 | if (-1 == stream_fd) { 308 | #ifdef DEBUG 309 | perror("[EVN] Unix Stream socket connection"); 310 | #endif 311 | return NULL; 312 | } 313 | stream = evn_stream_create(EV_A, stream_fd); 314 | stream->socket = malloc(sizeof(struct sockaddr_un)); 315 | sock = (struct sockaddr_un*) stream->socket; 316 | 317 | stream->ready_state = evn_OPENING; 318 | 319 | ev_io_stop(EV_A_ &stream->io); 320 | ev_io_init(&stream->io, evn_stream_priv_on_connect, stream_fd, EV_WRITE); 321 | 322 | sock->sun_family = AF_UNIX; 323 | strcpy(sock->sun_path, sock_path); 324 | stream->socket_len = strlen(sock->sun_path) + 1 + sizeof(sock->sun_family); 325 | 326 | if (-1 == connect(stream_fd, (struct sockaddr *) sock, stream->socket_len)) { 327 | #ifdef DEBUG 328 | fprintf(stderr, "[EVN] connect to %s: %s\n", sock_path, strerror(errno)); 329 | #endif 330 | evn_stream_destroy(EV_A_ stream); 331 | stream = NULL; 332 | } 333 | 334 | if (NULL != stream) 335 | { 336 | ev_io_start(EV_A_ &stream->io); 337 | } 338 | return stream; 339 | } 340 | 341 | static void evn_stream_priv_on_connect(EV_P_ ev_io *w, int revents) { 342 | struct evn_stream* stream = (struct evn_stream*) w; 343 | evn_debugs("Stream Connect"); 344 | 345 | //ev_cb_set (ev_TYPE *watcher, callback) 346 | //ev_io_set (&w, STDIN_FILENO, EV_READ); 347 | 348 | stream->ready_state = evn_OPEN; 349 | 350 | int fd = stream->io.fd; 351 | ev_io_stop(EV_A_ &stream->io); 352 | ev_io_init(&stream->io, evn_stream_priv_on_activity, fd, EV_READ | EV_WRITE); 353 | ev_io_start(EV_A_ &stream->io); 354 | //ev_cb_set(&stream->io, evn_stream_priv_on_activity); 355 | 356 | if (stream->on_connect) { stream->on_connect(EV_A_ stream); } 357 | if (true == stream->oneshot) 358 | { 359 | // each buffer chunk should be at least 4096 and we'll start with 128 chunks 360 | // the size will grow if the actual data received is larger 361 | stream->bufferlist = evn_bufferlist_create(4096, 128); 362 | } 363 | 364 | stream->timer.last_activity = ev_now(EV_A); 365 | if ( (false == stream->timer.active) && (0 != stream->timer.timeout) ) 366 | { 367 | evn_stream_timer_priv_cb(EV_A, &(stream->timer.timer), EV_TIMEOUT); 368 | evn_debugs("reactivated expired timer"); 369 | } 370 | } 371 | 372 | int evn_stream_get_timeout(EV_P, struct evn_stream* stream) { 373 | return (int)(stream->timer.timeout * 1000); 374 | } 375 | 376 | void evn_stream_set_timeout(EV_P, struct evn_stream* stream, int timeout) { 377 | 378 | ev_timer_stop (EV_A, &(stream->timer.timer)); 379 | stream->timer.active = false; 380 | 381 | if(0 == timeout) 382 | { 383 | return; 384 | } 385 | 386 | stream->timer.timeout = (ev_tstamp)timeout / 1000; 387 | stream->timer.last_activity = ev_now(loop); 388 | 389 | ev_init (&(stream->timer.timer), evn_stream_timer_priv_cb); 390 | evn_stream_timer_priv_cb(EV_A, &(stream->timer.timer), EV_TIMEOUT); 391 | } 392 | 393 | static void evn_stream_timer_priv_cb(EV_P, ev_timer* w, int revents) { 394 | ev_tstamp timeout; 395 | ev_tstamp now = ev_now(EV_A); 396 | struct evn_stream_timer* stream_timer = (struct evn_stream_timer*) w; 397 | struct evn_stream* stream = stream_timer->stream; 398 | 399 | timeout = stream_timer->last_activity + stream_timer->timeout; 400 | 401 | if (timeout < now) 402 | { 403 | evn_debug(" stream timed out. Current time = %f, Last avtivity = %f, Timeout = %f\n", now, stream_timer->last_activity, stream_timer->timeout); 404 | if (stream->on_timeout) { stream->on_timeout(EV_A, stream); } 405 | ev_timer_stop (EV_A, &(stream->timer.timer)); 406 | stream_timer->active = false; 407 | } 408 | else 409 | { 410 | w->repeat = timeout-now; 411 | ev_timer_again(EV_A, w); 412 | stream_timer->active = true; 413 | evn_debug("timer callback made at %f, but stream not timed out. callback with occur again in %f secs\n", now, w->repeat); 414 | } 415 | } 416 | 417 | bool evn_stream_write(EV_P, struct evn_stream* stream, void* data, int size) { 418 | 419 | if (evn_READ_ONLY == stream->ready_state) 420 | { 421 | if (stream->on_error) 422 | { 423 | struct evn_exception error; 424 | error.error_number = -1; 425 | snprintf(error.message, sizeof error.message, "[EVN] trying to write to a read only stream"); 426 | stream->on_error(EV_A, stream, &error); 427 | } 428 | return false; 429 | } 430 | 431 | stream->timer.last_activity = ev_now(EV_A); 432 | evn_debug("user write reset timeout at time %f\n", stream->timer.last_activity); 433 | if ( (false == stream->timer.active) && (0 != stream->timer.timeout) ) 434 | { 435 | evn_stream_timer_priv_cb(EV_A, &(stream->timer.timer), EV_TIMEOUT); 436 | evn_debugs("reactivated expired timer"); 437 | } 438 | 439 | if (0 == stream->_priv_out_buffer->size) 440 | { 441 | // priv_send will send the data over the socket, and add the leftover data to priv_out_buffer if it doesn't send everything 442 | if (true == evn_stream_priv_send(EV_A, stream, data, size)) 443 | { 444 | evn_debugs("All data was sent without queueing"); 445 | return true; 446 | } 447 | evn_debugs("Some data was queued"); 448 | } 449 | else 450 | { 451 | evn_debugs("ABQ data"); 452 | // we aren't ready to send yet, so add the new data to the buffer to be sent when the socket is writable 453 | evn_inbuf_add(stream->_priv_out_buffer, data, size); 454 | } 455 | 456 | // Ensure that we listen for EV_WRITE 457 | if (!(stream->io.events & EV_WRITE)) 458 | { 459 | // store the file descriptor to make sure we don't lose it when we stop the event. 460 | int fd = stream->io.fd; 461 | // bitwise or the events we are looking for with EV_WRITE to make sure EV_WRITE is inlcuded. 462 | int new_events = stream->io.events | EV_WRITE; 463 | ev_io_stop(EV_A_ &stream->io); 464 | ev_io_set(&stream->io, fd, new_events); 465 | } 466 | ev_io_start(EV_A_ &stream->io); 467 | 468 | return false; 469 | } 470 | 471 | static inline void evn_stream_priv_on_activity(EV_P_ ev_io *w, int revents) { 472 | struct evn_stream* stream = (struct evn_stream*) w; 473 | evn_debugs("Stream Activity"); 474 | 475 | stream->timer.last_activity = ev_now(EV_A); 476 | evn_debug("socket file activity reset timeout at time %f\n", stream->timer.last_activity); 477 | if ( (false == stream->timer.active) && (0 != stream->timer.timeout) ) 478 | { 479 | evn_stream_timer_priv_cb(EV_A, &(stream->timer.timer), EV_TIMEOUT); 480 | evn_debugs("reactivated expired timer"); 481 | } 482 | 483 | if (revents & EV_READ) 484 | { 485 | evn_stream_priv_on_readable(EV_A, w, revents); 486 | } 487 | else if (revents & EV_WRITE) 488 | { 489 | evn_stream_priv_on_writable(EV_A, w, revents); 490 | } 491 | else 492 | { 493 | // Never Happens 494 | fprintf(stderr, "[evn] [ERR] ev_io received something other than EV_READ or EV_WRITE"); 495 | } 496 | } 497 | 498 | // This callback is called when data is readable on the unix socket. 499 | static void evn_stream_priv_on_readable(EV_P_ ev_io *w, int revents) { 500 | void* data; 501 | struct evn_exception error; 502 | int length; 503 | struct evn_stream* stream = (struct evn_stream*) w; 504 | char recv_data[EVN_MAX_RECV]; 505 | 506 | evn_debugs("EV_READ - stream->io.fd"); 507 | length = recv(stream->io.fd, &recv_data, EVN_MAX_RECV, 0); 508 | 509 | if (length < 0) 510 | { 511 | if (stream->on_error) { 512 | error.error_number = errno; 513 | snprintf(error.message, sizeof error.message, "[EVN] read failed, now destroying stream: %s", strerror(errno)); 514 | stream->on_error(EV_A, stream, &error); 515 | } 516 | evn_stream_destroy(EV_A, stream); 517 | return; 518 | } 519 | else if (0 == length) 520 | { 521 | evn_debugs("received FIN"); 522 | 523 | if (stream->oneshot) 524 | { 525 | evn_debugs("oneshot shot"); 526 | // TODO put on stack char data[stream->bufferlist->used]; 527 | if (stream->bufferlist->used) 528 | { 529 | if (stream->on_data) 530 | { 531 | evn_buffer* buffer = evn_bufferlist_concat(stream->bufferlist); 532 | stream->on_data(EV_A_ stream, buffer->data, buffer->used); 533 | free(buffer); // does not free buffer->data, that's up to the user 534 | } 535 | } 536 | } 537 | if (stream->on_end) { stream->on_end(EV_A_ stream); } 538 | 539 | // if we've already sent to FIN packet, nothing left but to close the stream entirely 540 | if (evn_READ_ONLY == stream->ready_state) 541 | { 542 | evn_debugs("destroying socket now that both ends are closed"); 543 | stream->ready_state = evn_CLOSED; 544 | evn_stream_destroy(EV_A_ stream); 545 | } 546 | // otherwise leave the socket open so we can write to it, but stop listening for READ events. 547 | else 548 | { 549 | evn_debugs("settings the socket to write only mode"); 550 | stream->ready_state = evn_WRITE_ONLY; 551 | 552 | // store the file descriptor to make sure we don't lose it when we stop the event. 553 | int fd = stream->io.fd; 554 | // bitwise and the events we are looking for with the bitwise not of EV_READ to remove it. 555 | int new_events = stream->io.events & ~EV_READ; 556 | 557 | // stop the event on the loop, change the settings, and restart it 558 | ev_io_stop(EV_A_ &stream->io); 559 | ev_io_set(&stream->io, fd, new_events); 560 | ev_io_start(EV_A_ &stream->io); 561 | } 562 | } 563 | else if (length > 0) 564 | { 565 | if (stream->oneshot) 566 | { 567 | evn_debug("adding to %d bytes to the buffer (current size = %d)\n", length, stream->bufferlist->size); 568 | // if (stream->on_progress) { stream->on_progress(EV_A_ stream, data, length); } 569 | // if time - stream->started_at > stream->max_wait; stream->timeout(); 570 | // if buffer->used > stream->max_size; stream->timeout(); 571 | evn_bufferlist_add(stream->bufferlist, &recv_data, length); 572 | return; 573 | } 574 | if (stream->on_data) 575 | { 576 | evn_debug("read %d from the socket\n", length); 577 | data = malloc(length); 578 | memcpy(data, &recv_data, length); 579 | stream->on_data(EV_A_ stream, data, length); 580 | } 581 | } 582 | } 583 | 584 | static void evn_stream_priv_on_writable(EV_P_ ev_io *w, int revents) { 585 | struct evn_stream* stream = (struct evn_stream*) w; 586 | 587 | evn_debugs("EV_WRITE"); 588 | 589 | evn_stream_priv_send(EV_A, stream, NULL, 0); 590 | if (evn_CLOSED == stream->ready_state) 591 | { 592 | // we experienced a problem while writing, don't continue 593 | return; 594 | } 595 | 596 | // If the buffer is finally empty, send the `drain` event 597 | if (0 == stream->_priv_out_buffer->size) 598 | { 599 | // store the file descriptor to make sure we don't lose it when we stop the event. 600 | int fd = stream->io.fd; 601 | // bitwise and the events we are looking for with the bitwise not of EV_WRITE to remove it. 602 | int new_events = stream->io.events & ~EV_WRITE; 603 | 604 | // stop the event on the loop, change the settings, and restart it 605 | ev_io_stop(EV_A_ &stream->io); 606 | ev_io_set(&stream->io, fd, new_events); 607 | ev_io_start(EV_A_ &stream->io); 608 | 609 | evn_debugs("pre-drain"); 610 | if (stream->on_drain) { stream->on_drain(EV_A_ stream); } 611 | 612 | return; 613 | } 614 | evn_debugs("post-null"); 615 | } 616 | 617 | static bool evn_stream_priv_send(EV_P, struct evn_stream* stream, void* data, int size) { 618 | //const int MAX_SEND = 4096; 619 | struct evn_exception error; 620 | int sent; 621 | evn_inbuf* buf = stream->_priv_out_buffer; 622 | int buf_size = buf->size; 623 | 624 | evn_debugs("priv_send"); 625 | if (0 != buf_size) 626 | { 627 | evn_debug("has buffer with %d bytes of data\n", buf_size); 628 | sent = send(stream->io.fd, buf->bottom, buf->size, MSG_DONTWAIT | EVN_NOSIGNAL); 629 | if (sent < 0) 630 | { 631 | if (stream->on_error) { 632 | error.error_number = errno; 633 | snprintf(error.message, sizeof error.message, "[EVN] send failed, now destroying stream: %s", strerror(errno)); 634 | stream->on_error(EV_A, stream, &error); 635 | } 636 | evn_stream_destroy(EV_A, stream); 637 | return false; 638 | } 639 | evn_inbuf_toss(buf, sent); 640 | 641 | if (sent != buf_size) 642 | { 643 | evn_debug("buffer has %d bytes remaining after sending %d bytes\n", buf->size, sent); 644 | if (NULL != data && 0 != size) { evn_inbuf_add(buf, data, size); } 645 | return false; 646 | } 647 | 648 | if (NULL != data && 0 != size) 649 | { 650 | evn_debugs("and has more data"); 651 | sent = send(stream->io.fd, data, size, MSG_DONTWAIT | EVN_NOSIGNAL); 652 | if (sent < 0) 653 | { 654 | if (stream->on_error) { 655 | error.error_number = errno; 656 | snprintf(error.message, sizeof error.message, "[EVN] send failed, now destroying stream: %s", strerror(errno)); 657 | stream->on_error(EV_A, stream, &error); 658 | } 659 | evn_stream_destroy(EV_A, stream); 660 | return false; 661 | } 662 | if (sent != size) { 663 | evn_debugs("enqueued remaining data"); 664 | evn_inbuf_add(buf, data + sent, size - sent); 665 | return false; 666 | } 667 | } 668 | return true; 669 | } 670 | 671 | if (NULL != data && 0 != size) 672 | { 673 | evn_debugs("doesn't have data in buffer, but does have data"); 674 | sent = send(stream->io.fd, data, size, MSG_DONTWAIT | EVN_NOSIGNAL); 675 | if (sent < 0) 676 | { 677 | if (stream->on_error) { 678 | error.error_number = errno; 679 | snprintf(error.message, sizeof error.message, "[EVN] send failed, now destroying stream: %s", strerror(errno)); 680 | stream->on_error(EV_A, stream, &error); 681 | } 682 | evn_stream_destroy(EV_A, stream); 683 | return false; 684 | } 685 | if (sent != size) { 686 | evn_debugs("could not send all of the data"); 687 | evn_inbuf_add(buf, data + sent, size - sent); 688 | return false; 689 | } 690 | evn_debugs("sent all of the data"); 691 | } 692 | 693 | return true; 694 | } 695 | 696 | bool evn_stream_end(EV_P_ struct evn_stream* stream) { 697 | 698 | if (evn_WRITE_ONLY == stream->ready_state) 699 | { 700 | stream->ready_state = evn_CLOSED; 701 | evn_stream_destroy(EV_A_ stream); 702 | return true; 703 | } 704 | 705 | stream->ready_state = evn_READ_ONLY; 706 | // this command closes the socket for writing and sends the FIN packet 707 | shutdown(stream->io.fd, SHUT_WR); 708 | return false; 709 | } 710 | 711 | bool evn_stream_destroy(EV_P_ struct evn_stream* stream) { 712 | bool result; 713 | 714 | // TODO delay freeing of server until streams have closed 715 | // or link loop directly to stream? 716 | result = close(stream->io.fd) ? true : false; 717 | result = (evn_CLOSED != stream->ready_state) ? true : result; 718 | ev_io_stop(EV_A_ &stream->io); 719 | stream->ready_state = evn_CLOSED; 720 | 721 | ev_timer_stop (EV_A, &(stream->timer.timer)); 722 | 723 | evn_debugs("starting on_close callback"); 724 | if (stream->on_close) { stream->on_close(EV_A_ stream, result); } 725 | evn_debugs("finished on_close callback"); 726 | 727 | if (NULL != stream->_priv_out_buffer) 728 | { 729 | evn_inbuf_destroy(stream->_priv_out_buffer); 730 | stream->_priv_out_buffer = NULL; 731 | } 732 | else 733 | { 734 | fprintf(stderr, "[EVN] out buffer is null in stream about to be destroyed\n\tPossible double destroy in progress\n"); 735 | } 736 | if (NULL != stream->bufferlist) 737 | { 738 | evn_bufferlist_destroy(stream->bufferlist); 739 | stream->bufferlist = NULL; 740 | } 741 | else if (true == stream->oneshot) 742 | { 743 | fprintf(stderr, "[EVN] bufferlist is null in stream about to be destroyed (with one shot = true)\n\tPossible double destroy in progress\n"); 744 | } 745 | if (NULL != stream->socket) 746 | { 747 | free(stream->socket); 748 | stream->socket = NULL; 749 | } 750 | else if (NULL == stream->server) 751 | { 752 | fprintf(stderr, "[EVN] socket is null in stream about to be destroyed (client side)\n\tPossible double destroy in progress\n"); 753 | } 754 | free(stream); 755 | 756 | return result; 757 | } 758 | 759 | -------------------------------------------------------------------------------- /source/include/bool.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Include this file to assert that you choose 3 | * 4 | * The Good Parts (TM) 5 | * 6 | * of C... even though it's C. 7 | * 8 | * Congratulations! Way to not contribute to the programming horrors of our day! 9 | */ 10 | #ifndef _BOOL_H_ 11 | #define _BOOL_H_ 12 | 13 | // You can choose to reserve the words true and false! 14 | #ifndef __cplusplus 15 | #ifndef _BOOL_ 16 | #define _BOOL_ 17 | typedef enum { false = 0, true = 1 } bool; 18 | #endif 19 | #endif 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /source/include/crossnet.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef MSG_NOSIGNAL 3 | #define EVN_NOSIGNAL MSG_NOSIGNAL 4 | #else 5 | #define EVN_NOSIGNAL SO_NOSIGPIPE 6 | #endif 7 | -------------------------------------------------------------------------------- /source/include/evn-buffer-list.h: -------------------------------------------------------------------------------- 1 | #ifndef EVN_BUFFER_LIST_H_ 2 | #define EVN_BUFFER_LIST_H_ 3 | 4 | #include // printf 5 | 6 | #define evn_error(...) printf("[EVN] " __VA_ARGS__) 7 | 8 | typedef struct { 9 | void* data; 10 | void* position; 11 | 12 | int free; 13 | int used; // -> index 14 | int capacity; // -> size 15 | } evn_buffer; 16 | 17 | typedef struct { 18 | evn_buffer* list; 19 | evn_buffer* current; 20 | 21 | int block_size; 22 | int index; 23 | int length; 24 | int used; 25 | int size; 26 | } evn_bufferlist; 27 | 28 | 29 | // Create a buffer of `size` bytes 30 | evn_buffer* evn_buffer_create(int size); 31 | // Create a buffer of `size` by `memcpy`ing `data` 32 | evn_buffer* evn_buffer_create_copy(void* data, int size); 33 | // Create a buffer from existing data where `index` is the first unused byte (or `size` if used) 34 | evn_buffer* evn_buffer_create_as(void* data, int size, int index); 35 | 36 | // Copy `size` bytes of `data` to existing buffer 37 | int evn_buffer_add(evn_buffer* buffer, void* data, int size); 38 | 39 | // Free all memory associated with the buffer 40 | void evn_buffer_destroy(evn_buffer* buffer); 41 | 42 | // Create a "smart" buffer 43 | evn_bufferlist* evn_bufferlist_create(int min_block_size, int slices); 44 | // Copy data into the buffer 45 | int evn_bufferlist_add(evn_bufferlist* bufferlist, void* data, int size); 46 | // Copy all data into a single buffer 47 | evn_buffer* evn_bufferlist_concat(evn_bufferlist* bufferlist); 48 | // Realloc the current buffer and then add a buffer after it 49 | //int evn_bufferlist_add_buffer(evn_bufferlist* bufferlist, evn_buffer* buffer); 50 | 51 | // Free all memory associated with the bufferlist and the buffers it contains 52 | void evn_bufferlist_destroy(evn_bufferlist* bufferlist); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /source/include/evn-inbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVN_INBUF_H_ 2 | #define _EVN_INBUF_H_ 3 | 4 | // This buffer is optimized for situations where the 5 | // drain is roughly equal to the increase. 6 | // (i.e. the user listens to the `drain` event before 7 | // stacking on another full chunk of data 8 | 9 | 10 | struct evn_inbuf; 11 | 12 | typedef struct { 13 | void* start; 14 | void* end; 15 | void* bottom; 16 | void* top; 17 | int size; 18 | } evn_inbuf; 19 | 20 | /* 21 | 22 | Anotomy of evn_inbuf 23 | 24 | start| |size| |end 25 | | | | | 26 | DATA |oooooooo|xxxx|oooooooo| 27 | | | 28 | bottom| |top 29 | | | 30 | |leading| |trailing| 31 | | | 32 | |used| 33 | 34 | The buffer will hold up to two chunks. 35 | It is assumed that one chunk should dain at about the same rate that the next chunk comes in. 36 | The buffer will realloc to become twice its size if the incoming data if space is no avaibale. 37 | It will memcpy if there is at least a chunks-worth of space already drained 38 | 39 | */ 40 | 41 | // Create a buffer about the size you expect the maximum incoming chunk to be 42 | // No need to get crazy, the size will grow on it's own, but it will never shrink 43 | evn_inbuf* evn_inbuf_create(int size); 44 | int evn_inbuf_init(evn_inbuf* buf, int size); 45 | // Add a chunk to the buffer 46 | int evn_inbuf_add(evn_inbuf* buf, void* data, int size); 47 | // Peek at a copy of some amount of data (you malloc the data) 48 | int evn_inbuf_peek(evn_inbuf* buf, void* data, int size); 49 | // Toss some amount of data out 50 | void evn_inbuf_toss(evn_inbuf* buf, int size); 51 | // Free memory associated with this inbuf 52 | void evn_inbuf_destroy(evn_inbuf* buf); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /source/include/evn.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVN_H_ 2 | #define _EVN_H_ 3 | 4 | #include // struct timeval 5 | 6 | // Network Stuff 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "evn-buffer-list.h" 18 | #include "evn-inbuf.h" 19 | #include "bool.h" 20 | 21 | #ifndef EVN_DEBUG 22 | #if defined DEBUG 23 | #define EVN_DEBUG 1 24 | #else 25 | #define EVN_DEBUG 0 26 | #endif 27 | #endif 28 | 29 | #if EVN_DEBUG 30 | #include 31 | #define evn_debug(...) printf("[EVN] " __VA_ARGS__) 32 | #define evn_debugs(...) puts("[EVN] " __VA_ARGS__) 33 | #else 34 | #define evn_debug(...) 35 | #define evn_debugs(...) 36 | #endif 37 | 38 | 39 | struct evn_stream; 40 | struct evn_server; 41 | struct evn_exception; 42 | 43 | #define EVN_SERVER_P struct evn_server* server 44 | #define EVN_STREAM_P struct evn_stream* stream 45 | 46 | // Server Callbacks 47 | typedef void (evn_server_on_listen)(EV_P_ struct evn_server* server); 48 | typedef void (evn_server_on_connection)(EV_P_ struct evn_server* server, struct evn_stream* stream); 49 | typedef void (evn_server_on_close)(EV_P_ struct evn_server* server); 50 | typedef void (evn_server_on_error)(EV_P_ struct evn_server* server, struct evn_exception* error); 51 | 52 | // Client Callbacks 53 | typedef void (evn_stream_on_connect)(EV_P_ struct evn_stream* stream); 54 | typedef void (evn_stream_on_secure)(EV_P_ struct evn_stream* stream); // TODO Implement 55 | typedef void (evn_stream_on_data)(EV_P_ struct evn_stream* stream, void* blob, int size); 56 | typedef void (evn_stream_on_end)(EV_P_ struct evn_stream* stream); 57 | typedef void (evn_stream_on_timeout)(EV_P_ struct evn_stream* stream); 58 | typedef void (evn_stream_on_drain)(EV_P_ struct evn_stream* stream); 59 | typedef void (evn_stream_on_error)(EV_P_ struct evn_stream* stream, struct evn_exception* error); 60 | typedef void (evn_stream_on_close)(EV_P_ struct evn_stream* stream, bool had_error); 61 | 62 | typedef enum 63 | { 64 | evn_CLOSED, 65 | evn_OPEN, 66 | evn_OPENING, 67 | evn_READ_ONLY, 68 | evn_WRITE_ONLY 69 | } evn_ready_state; 70 | 71 | struct evn_exception { 72 | int error_number; 73 | char message[256]; 74 | }; 75 | 76 | // we need a seperate struct for the timer so we can dereference the pointer 77 | // the the ev_timer as something that can get us back the the stream pointer 78 | struct evn_stream_timer { 79 | ev_timer timer; 80 | struct evn_stream* stream; 81 | ev_tstamp last_activity; 82 | ev_tstamp timeout; 83 | bool active; 84 | }; 85 | 86 | struct evn_server { 87 | ev_io io; 88 | EV_P; 89 | //evn_server_on_listen* on_listen; // not in the node API 90 | evn_server_on_connection* on_connection; 91 | evn_server_on_close* on_close; 92 | evn_server_on_error* on_error; 93 | struct sockaddr* socket; 94 | int socket_len; 95 | //array streams; 96 | }; 97 | 98 | struct evn_stream { 99 | ev_io io; 100 | evn_stream_on_connect* on_connect; 101 | evn_stream_on_secure* on_secure; 102 | evn_stream_on_data* on_data; 103 | evn_stream_on_end* on_end; 104 | evn_stream_on_timeout* on_timeout; 105 | evn_stream_on_drain* on_drain; 106 | evn_stream_on_error* on_error; 107 | evn_stream_on_close* on_close; 108 | evn_ready_state ready_state; 109 | int index; 110 | bool oneshot; 111 | evn_bufferlist* bufferlist; 112 | evn_inbuf* _priv_out_buffer; 113 | struct evn_server* server; 114 | struct sockaddr* socket; 115 | int socket_len; 116 | EV_P; 117 | char type; 118 | void* send_data; 119 | struct evn_stream_timer timer; 120 | }; 121 | 122 | 123 | int evn_set_nonblock(int fd); 124 | 125 | struct evn_server* evn_server_create(EV_P_ evn_server_on_connection* on_connection); 126 | int evn_server_listen(struct evn_server* server, int port, char* address); 127 | int evn_server_close(EV_P_ struct evn_server* server); 128 | int evn_server_destroy(EV_P_ struct evn_server* server); 129 | 130 | inline struct evn_stream* evn_stream_create(EV_P, int fd); 131 | inline struct evn_stream* evn_create_connection(EV_P_ int port, char* address); 132 | struct evn_stream* evn_create_connection_unix_stream(EV_P_ char* sock_path); 133 | struct evn_stream* evn_create_connection_tcp_stream(EV_P_ int port, char* address); 134 | void evn_stream_set_timeout(EV_P, struct evn_stream* stream, int timeout); 135 | int evn_stream_get_timeout(EV_P, struct evn_stream* stream); 136 | bool evn_stream_write(EV_P_ struct evn_stream* stream, void* data, int size); 137 | bool evn_stream_end(EV_P_ struct evn_stream* stream); 138 | bool evn_stream_destroy(EV_P_ struct evn_stream* stream); 139 | 140 | #endif 141 | --------------------------------------------------------------------------------