├── cmake_uninstall.cmake.in ├── LICENSE ├── CMakeLists.txt ├── src ├── cstomp.h └── cstomp.c ├── example ├── share_sess_sample.c └── main.c └── README.md /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif(NOT "${rm_retval}" STREQUAL 0) 18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 20 | endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | endforeach(file) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Taymindis Woon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(c_stomp) 3 | 4 | set(PROJNAME cstomp) 5 | 6 | IF (NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) 7 | SET(CMAKE_INSTALL_INCLUDEDIR /usr/local/include) 8 | ENDIF(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) 9 | 10 | file(GLOB_RECURSE sources src/*.c src/*.h) 11 | file(GLOB_RECURSE testfile example/main.c src/*.h) 12 | file(GLOB_RECURSE testfile2 example/share_sess_sample.c src/*.h) 13 | 14 | 15 | add_executable(run-test ${sources} ${testfile}) 16 | add_executable(run-test2 ${sources} ${testfile2}) 17 | 18 | target_include_directories(run-test PUBLIC src) 19 | target_include_directories(run-test2 PUBLIC src) 20 | 21 | target_link_libraries(run-test PUBLIC pthread) 22 | target_link_libraries(run-test2 PUBLIC pthread) 23 | 24 | include_directories(src /usr/local/include) 25 | 26 | IF (DEFINED SHARED_CONNECTION) 27 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCSTOMP_READ_WRITE_SHR_LOCK=1") 28 | ENDIF (DEFINED SHARED_CONNECTION) 29 | # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wstrict-prototypes -Wmissing-prototypes") 30 | # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual") 31 | # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-compare -std=c11 -pedantic") 32 | 33 | configure_file( 34 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 35 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 36 | IMMEDIATE @ONLY) 37 | 38 | add_custom_target(uninstall 39 | COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 40 | 41 | ############################################################################### 42 | ## packaging ################################################################## 43 | ############################################################################### 44 | add_library(${PROJNAME}.static STATIC ${sources}) 45 | set_target_properties(${PROJNAME}.static PROPERTIES OUTPUT_NAME ${PROJNAME}) 46 | add_library(${PROJNAME} SHARED ${sources}) 47 | 48 | # link_directories(/usr/local/lib /usr/lib) 49 | 50 | ## For .a library 51 | install(TARGETS ${PROJNAME}.static EXPORT ${PROJNAME} 52 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 53 | LIBRARY DESTINATION lib${LIB_SUFFIX} 54 | # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 55 | ) 56 | 57 | ## For .so library 58 | install(TARGETS ${PROJNAME} EXPORT ${PROJNAME} 59 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 60 | LIBRARY DESTINATION lib${LIB_SUFFIX} 61 | # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 62 | ) 63 | install(FILES src/cstomp.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 64 | 65 | install(CODE "execute_process(COMMAND ldconfig)") 66 | 67 | set(CPACK_PACKAGE_NAME "c-stomp") 68 | set(CPACK_PACKAGE_VERSION "1.0.0") 69 | 70 | # we don't want to split our program up into several things 71 | set(CPACK_MONOLITHIC_INSTALL 1) 72 | 73 | # This must be last 74 | include(CPack) 75 | -------------------------------------------------------------------------------- /src/cstomp.h: -------------------------------------------------------------------------------- 1 | #ifndef CSTOMP_H 2 | #define CSTOMP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef u_char 11 | #define u_char unsigned char 12 | #endif 13 | 14 | /**VALID STOMP COMMAND**/ 15 | // "SEND", 16 | // "SUBSCRIBE", 17 | // "UNSUBSCRIBE", 18 | // "BEGIN", 19 | // "COMMIT", 20 | // "ABORT", 21 | // "ACK", 22 | // "NACK", 23 | // "DISCONNECT", 24 | // "CONNECT", 25 | // "STOMP", 26 | // "CONNECTED", 27 | // "MESSAGE", 28 | // "RECEIPT", 29 | // "ERROR", 30 | /*********/ 31 | 32 | /** 33 | * Noted: 34 | * cstmp_readwrite_frame are safe to use if you are connect 1 frame per connection session. 35 | * 36 | * If session being sharing session to multiple frame, only each frame should only have one type either read or write only 37 | * 38 | **/ 39 | typedef struct cstmp_frame_val_s { 40 | u_char *data; 41 | size_t len; 42 | } cstmp_frame_val_t; 43 | 44 | typedef struct cstmp_frame_buf_s { 45 | u_char *start; 46 | u_char *last; 47 | size_t total_size; 48 | } cstmp_frame_buf_t; 49 | 50 | typedef struct cstmp_session_s { 51 | int sock; 52 | struct sockaddr_in addr; 53 | int send_timeout; 54 | int recv_timeout; 55 | #ifdef CSTOMP_READ_WRITE_SHR_LOCK 56 | /*Atomic*/int read_lock; 57 | /*Atomic*/int write_lock; 58 | #endif 59 | } cstmp_session_t; 60 | 61 | typedef struct cstmp_frame_s { 62 | u_char *cmd; 63 | cstmp_frame_buf_t headers; 64 | cstmp_frame_buf_t body; 65 | cstmp_session_t *sess; 66 | } cstmp_frame_t; 67 | 68 | 69 | /** Extra malloc and free customization **/ 70 | extern void cstmp_set_malloc_management(void* (*stp_alloc)(void* arg, size_t sz), void (*stp_free)(void* arg, void* ptr), void* arg ); 71 | 72 | extern cstmp_session_t* cstmp_connect(const char *hostname, int port ); 73 | extern cstmp_session_t* cstmp_connect_t(const char *hostname, int port, int send_timeout, int recv_timeout ); 74 | extern cstmp_session_t* cstmp_new_session( cstmp_session_t* curr_sess ); 75 | 76 | 77 | /** Do take note that if you disc the session, some other frame instance might using it**/ 78 | extern void cstmp_disconnect(cstmp_session_t* stp_sess); 79 | 80 | /**To create new socket, prevent concurrent issue**/ 81 | extern cstmp_frame_t* cstmp_new_frame(); 82 | 83 | extern void cstmp_destroy_frame(cstmp_frame_t *fr); 84 | 85 | extern int cstmp_add_header_str(cstmp_frame_t *fr, const u_char *keyval); 86 | 87 | extern int cstmp_add_header_str_and_len(cstmp_frame_t *fr, u_char *keyval, size_t keyval_len); 88 | 89 | extern int cstmp_add_header(cstmp_frame_t *fr, const u_char *key, const u_char* val); 90 | 91 | extern int cstmp_add_body_content(cstmp_frame_t *fr, u_char* content); 92 | 93 | extern int cstmp_add_body_content_and_len(cstmp_frame_t *fr, u_char* content, size_t content_len); 94 | 95 | extern u_char* cstmp_get_cmd(cstmp_frame_t *fr); 96 | 97 | extern int cstmp_get_header(cstmp_frame_t *fr, const u_char *key, cstmp_frame_val_t *hdr_val); 98 | 99 | extern void cstmp_get_body(cstmp_frame_t *fr, cstmp_frame_val_t *body_val); 100 | 101 | extern void cstmp_dump_frame_raw(cstmp_frame_t *fr); 102 | 103 | extern void cstmp_dump_frame_pretty(cstmp_frame_t *fr); 104 | 105 | extern void cstmp_reset_frame(cstmp_frame_t *fr); 106 | 107 | extern int cstmp_send_direct(cstmp_session_t *sess, const u_char *frame_str, int tries); 108 | 109 | extern int cstmp_send(cstmp_session_t *sess, cstmp_frame_t *fr, int tries); 110 | 111 | extern int cstmp_recv(cstmp_session_t *sess, cstmp_frame_t *fr, int tries); 112 | 113 | extern void cstmp_consume(cstmp_session_t *sess, cstmp_frame_t *fr, void (*callback)(cstmp_frame_t *), int *consuming); 114 | 115 | #endif -------------------------------------------------------------------------------- /example/share_sess_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int consuming = 1; 10 | #define HOST "10.2.110.202" 11 | #define PORT 61618 12 | #define QUEUE_NAME "/amq/queue/stompqueue" 13 | 14 | static cstmp_session_t *sess; 15 | void *consuming_thread(void* none) { 16 | if (sess) { 17 | /** This session already subscribe(ref line 64), so we just create a readonly frame. **/ 18 | cstmp_frame_t *consume_fr = cstmp_new_frame(); 19 | if (consume_fr) { 20 | void (*consume_handler)(cstmp_frame_t* consume_fr) = 21 | ({ 22 | void __fn__ (cstmp_frame_t* consume_fr) { 23 | assert( strlen(cstmp_get_cmd(consume_fr)) > 0 && "Error, no command found"); 24 | cstmp_dump_frame_pretty(consume_fr); 25 | } 26 | __fn__; 27 | }); 28 | 29 | /** Keep hooking until consuming = false / 0 **/ 30 | cstmp_consume(sess, (cstmp_frame_t*) consume_fr, consume_handler, &consuming); 31 | 32 | cstmp_destroy_frame(consume_fr); 33 | 34 | } 35 | } else 36 | printf("%s\n", "consuming Failed"); 37 | 38 | pthread_exit(NULL); 39 | } 40 | 41 | int main() { 42 | 43 | printf("%s %s\n", "Starting stomp connection, sending and consuming", QUEUE_NAME); 44 | usleep(1000 * 3000); 45 | 46 | sess = cstmp_connect_t(HOST, PORT, 2000, 2000); 47 | 48 | /** This frame are readable and writable**/ 49 | cstmp_frame_t *fr = cstmp_new_frame(); 50 | 51 | /** since fr and consume_fr are using same session **/ 52 | fr->cmd = "CONNECT"; 53 | cstmp_add_header_str(fr, "version:1.2"); // for direct string set method 54 | cstmp_add_header(fr, "login", "guest"); // for key val set method 55 | cstmp_add_header_str_and_len(fr, "passcode:guest", sizeof("passcode:guest") - 1); // in case you need len specified 56 | 57 | if (cstmp_send(sess, fr, 0) && cstmp_recv(sess, fr, 0)) { 58 | cstmp_dump_frame_pretty(fr); 59 | } 60 | 61 | /*** remember to reset frame before prepare the new command ***/ 62 | cstmp_reset_frame(fr); 63 | 64 | fr->cmd = "SUBSCRIBE"; 65 | cstmp_add_header(fr, "destination", QUEUE_NAME); 66 | cstmp_add_header(fr, "ack", "auto"); 67 | cstmp_add_header(fr, "id", "0"); 68 | cstmp_add_header(fr, "durable", "false"); 69 | cstmp_send(sess, fr, 3); 70 | 71 | /** Create a thread for consuming **/ 72 | pthread_t t; 73 | if (pthread_create(&t, NULL, consuming_thread, NULL)) { 74 | fprintf(stderr, "Error creating thread\n"); 75 | return 1; 76 | } 77 | pthread_detach(t); 78 | 79 | /*** Sending ***/ 80 | if (sess) { 81 | 82 | if (fr) { 83 | /*** Send 100 times ***/ 84 | int send_count = 100; 85 | while (send_count--) { 86 | cstmp_reset_frame(fr); 87 | fr->cmd = "SEND"; 88 | cstmp_add_header(fr, "destination", QUEUE_NAME); 89 | cstmp_add_header(fr, "persistent", "false"); 90 | cstmp_add_header(fr, "content-type", "text/plain"); 91 | cstmp_add_body_content(fr, "{\"my_key\":\"akjdlkajdklj2ljladasjldjasljdl@ASD2\", "); // for direct added 92 | cstmp_add_body_content_and_len(fr, "\"my_name\":\"John D\"}", sizeof("\"my_name\":\"John D\"}") - 1); // for len specified 93 | 94 | if (! cstmp_send(sess, fr, 1)) { 95 | assert(0 && "Unable to send frame"); 96 | break; 97 | } 98 | usleep(1000 * 50); 99 | } 100 | 101 | consuming = 0; 102 | printf("%s\n", "disconnecting Stomp"); 103 | usleep(1000 * 3000); 104 | 105 | fr->cmd = "DISCONNECT"; 106 | cstmp_add_header(fr, "receipt", "dummy-send-consume-test"); 107 | if ( cstmp_send(sess, fr, 1) && cstmp_recv(sess, fr, 1) ) { 108 | cstmp_dump_frame_pretty(fr); 109 | } 110 | 111 | cstmp_destroy_frame(fr); 112 | } else { 113 | printf("%s\n", "Test Failed"); 114 | return 0; 115 | } 116 | cstmp_disconnect(sess); 117 | printf("%s\n", "Test Passed"); 118 | } else 119 | printf("%s\n", "Test Failed"); 120 | 121 | 122 | return 0; 123 | } -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int consuming = 1; 10 | #define HOST "10.2.110.202" 11 | #define PORT 61618 12 | #define QUEUE_NAME "/amq/queue/stompqueue" 13 | 14 | /*** 15 | * This example showing each frame has each connection session, it won't affect the session read write issue. 16 | ***/ 17 | 18 | void *consuming_thread(void* nothing) { 19 | 20 | cstmp_session_t *consuming_sess = cstmp_connect_t(HOST, PORT, 500, 500); 21 | if (consuming_sess) { 22 | cstmp_frame_t *consume_fr = cstmp_new_frame(); 23 | consume_fr->cmd = "CONNECT"; 24 | cstmp_add_header(consume_fr, "login", "guest"); 25 | cstmp_add_header(consume_fr, "passcode", "guest"); 26 | cstmp_add_header(consume_fr, "version", "1.2"); 27 | 28 | if (cstmp_send(consuming_sess, consume_fr, 0) && cstmp_recv(consuming_sess, consume_fr, 0)) { 29 | cstmp_dump_frame_pretty(consume_fr); 30 | } 31 | 32 | /*** remember to reset frame before prepare the new command ***/ 33 | cstmp_reset_frame(consume_fr); 34 | 35 | consume_fr->cmd = "SUBSCRIBE"; 36 | cstmp_add_header(consume_fr, "destination", QUEUE_NAME); 37 | cstmp_add_header(consume_fr, "ack", "auto"); 38 | cstmp_add_header(consume_fr, "id", "0"); 39 | cstmp_add_header(consume_fr, "durable", "false"); 40 | cstmp_send(consuming_sess, consume_fr, 3); 41 | 42 | void (*consume_handler)(cstmp_frame_t* consume_fr) = 43 | ({ 44 | void __fn__ (cstmp_frame_t* consume_fr) { 45 | assert( strlen(cstmp_get_cmd(consume_fr)) > 0 && "Error, no command found"); 46 | cstmp_dump_frame_pretty(consume_fr); 47 | } 48 | __fn__; 49 | }); 50 | 51 | /** Keep hooking until consuming = false / 0 **/ 52 | cstmp_consume(consuming_sess, (cstmp_frame_t*) consume_fr, consume_handler, &consuming); 53 | 54 | /** you can use direct string if you don't want frame to send **/ 55 | if (cstmp_send_direct(consuming_sess, "UNSUBSCRIBE\nid:0\n\n", 0) && 56 | cstmp_recv(consuming_sess, consume_fr, 0) ) { 57 | cstmp_dump_frame_pretty(consume_fr); 58 | } 59 | 60 | 61 | consume_fr->cmd = "DISCONNECT"; 62 | cstmp_add_header(consume_fr, "receipt", "dummy-recv-test"); 63 | if ( cstmp_send(consuming_sess, consume_fr, 0) && 64 | cstmp_recv(consuming_sess, consume_fr, 0) ) { 65 | cstmp_dump_frame_pretty(consume_fr); 66 | } 67 | 68 | cstmp_destroy_frame(consume_fr); 69 | cstmp_disconnect(consuming_sess); 70 | } else 71 | printf("%s\n", "consuming Failed"); 72 | 73 | pthread_exit(NULL); 74 | } 75 | 76 | int main() { 77 | 78 | printf("%s %s\n", "Starting stomp connection, sending and consuming", QUEUE_NAME); 79 | usleep(1000 * 3000); 80 | 81 | /** Create a thread for consuming **/ 82 | pthread_t t; 83 | if (pthread_create(&t, NULL, consuming_thread, NULL)) { 84 | fprintf(stderr, "Error creating thread\n"); 85 | return 1; 86 | } 87 | pthread_detach(t); 88 | 89 | 90 | /*** Sending ***/ 91 | cstmp_session_t *sess = cstmp_connect(HOST, PORT); 92 | if (sess) { 93 | cstmp_frame_t *fr = cstmp_new_frame(); 94 | if (fr) { 95 | fr->cmd = "CONNECT"; 96 | cstmp_add_header_str(fr, "version:1.2"); // for direct string set method 97 | cstmp_add_header(fr, "login", "guest"); // for key val set method 98 | cstmp_add_header_str_and_len(fr, "passcode:guest", sizeof("passcode:guest") - 1); // in case you need len specified 99 | 100 | cstmp_frame_val_t val; 101 | 102 | if (cstmp_send(sess, fr, 0) && cstmp_recv(sess, fr, 0)) { 103 | cstmp_dump_frame_pretty(fr); 104 | } 105 | 106 | /*** Send 100 times ***/ 107 | int send_count = 100; 108 | while (send_count--) { 109 | cstmp_reset_frame(fr); 110 | fr->cmd = "SEND"; 111 | cstmp_add_header(fr, "destination", QUEUE_NAME); 112 | cstmp_add_header(fr, "persistent", "false"); 113 | cstmp_add_header(fr, "content-type", "text/plain"); 114 | cstmp_add_body_content(fr, "{\"my_key\":\"akjdlkajdklj2ljladasjldjasljdl@ASD2\"}"); 115 | 116 | if (! cstmp_send(sess, fr, 1)) { 117 | assert(0 && "Unable to send frame"); 118 | break; 119 | } 120 | usleep(1000 * 50); 121 | } 122 | 123 | consuming = 0; 124 | printf("%s\n", "disconnecting Stomp"); 125 | usleep(1000 * 3000); 126 | 127 | fr->cmd = "DISCONNECT"; 128 | cstmp_add_header(fr, "receipt", "dummy-send-test"); 129 | if ( cstmp_send(sess, fr, 3) && cstmp_recv(sess, fr, 3) ) { 130 | cstmp_dump_frame_pretty(fr); 131 | } 132 | 133 | 134 | cstmp_destroy_frame(fr); 135 | } else { 136 | printf("%s\n", "Test Failed"); 137 | return 0; 138 | } 139 | cstmp_disconnect(sess); 140 | printf("%s\n", "Test Passed"); 141 | } else 142 | printf("%s\n", "Test Failed"); 143 | 144 | 145 | return 0; 146 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | c-stomp 2 | ======= 3 | 4 | A STOMP client written in c, [STOMP](https://stomp.github.io/) is the Simple (or Streaming) Text Orientated Messaging Protocol. 5 | 6 | Table of Contents 7 | ================= 8 | 9 | * [Introduction](#introduction) 10 | * [Example](#example) 11 | * [Installation](#installation) 12 | * [Uninstall](#uninstall) 13 | * [Tips And Tricks](#tips-and-tricks) 14 | * [Support](#support) 15 | * [Copyright & License](#copyright--license) 16 | 17 | Introduction 18 | ============ 19 | 20 | c-stomp is a C library to write STOMP protocol in order to working with ActiveMQ, RabbitMQ, HornetQ, ActiveMQ Apollo and other messaging protocol which support stomp. 21 | 22 | Example 23 | ====== 24 | ```c 25 | /*For Connecting to Stomp */ 26 | cstmp_session_t *consuming_sess = cstmp_connect_t(HOST, PORT, 500/*send_timeout*/, 500/*recv_timeout*/); 27 | if (consuming_sess) { 28 | cstmp_frame_t *consume_fr = cstmp_new_frame(); 29 | consume_fr->cmd = "CONNECT"; 30 | cstmp_add_header(consume_fr, "login", "guest"); 31 | cstmp_add_header(consume_fr, "passcode", "guest"); 32 | cstmp_add_header(consume_fr, "version", "1.2"); 33 | 34 | if (cstmp_send(consuming_sess, consume_fr, 0) && cstmp_recv(consuming_sess, consume_fr, 0)) { 35 | cstmp_dump_frame_pretty(consume_fr); 36 | } 37 | } 38 | ``` 39 | ```c 40 | /* For Sending to */ 41 | cstmp_reset_frame(fr); 42 | fr->cmd = "SEND"; 43 | cstmp_add_header(fr, "destination", QUEUE_NAME); 44 | cstmp_add_header(fr, "persistent", "false"); 45 | cstmp_add_header(fr, "content-type", "text/plain"); 46 | cstmp_add_body_content(fr, "{\"my_key\":\"akjdlkajdklj2ljladasjldjasljdl@ASD2\"}"); 47 | 48 | if (! cstmp_send(sess, fr, 1)) { 49 | assert(0 && "Unable to send frame"); 50 | break; 51 | } 52 | ``` 53 | ```c 54 | /* For Reading Response */ 55 | if(cstmp_recv(sess, fr, 2 /*conn retry 2 times*/)) { 56 | cstmp_dump_frame_raw(fr); /*For Raw message display, good for debugging purpose*/ 57 | } 58 | ``` 59 | ```c 60 | 61 | /** To create a hooker to consume the message and callback handler **/ 62 | 63 | /*** remember to reset frame before prepare the new command ***/ 64 | cstmp_reset_frame(consume_fr); 65 | 66 | consume_fr->cmd = "SUBSCRIBE"; 67 | cstmp_add_header(consume_fr, "destination", QUEUE_NAME); 68 | cstmp_add_header(consume_fr, "ack", "auto"); 69 | cstmp_add_header(consume_fr, "id", "0"); 70 | cstmp_add_header(consume_fr, "durable", "false"); 71 | cstmp_send(consuming_sess, consume_fr, 3); 72 | 73 | void (*consume_handler)(cstmp_frame_t* consume_fr) = 74 | ({ 75 | void __fn__ (cstmp_frame_t* consume_fr) { 76 | assert( strlen(cstmp_get_cmd(consume_fr)) > 0 && "Error, no command found"); 77 | cstmp_dump_frame_pretty(consume_fr); 78 | } 79 | __fn__; 80 | }); 81 | 82 | /** Keep hooking until consuming = false / 0 **/ 83 | cstmp_consume(consuming_sess, (cstmp_frame_t*) consume_fr, consume_handler, &consuming); 84 | ``` 85 | [Back to TOC](#table-of-contents) 86 | 87 | 88 | Installation 89 | ============ 90 | 91 | ##### For non sharing connection 92 | 93 | ```bash 94 | cd $project_root_dir 95 | mkdir build 96 | cd build 97 | cmake .. 98 | make -j2 99 | ./run-test 100 | sudo make install 101 | ``` 102 | 103 | ##### For sharing the connection 104 | 105 | ```bash 106 | cd $project_root_dir 107 | mkdir build 108 | cd build 109 | cmake -DSHARED_CONNECTION=1 .. 110 | make -j2 111 | ./run-test2 112 | sudo make install 113 | ``` 114 | 115 | [Back to TOC](#table-of-contents) 116 | 117 | Uninstall 118 | ========= 119 | ```bash 120 | cd $project_root_dir/build 121 | sudo make uninstall 122 | ``` 123 | 124 | 125 | Support 126 | ======= 127 | 128 | Please do not hesitate to contact minikawoon2017@gmail.com/minikawoon99@gmail.com for any queries. 129 | 130 | 131 | [Back to TOC](#table-of-contents) 132 | 133 | Copyright & License 134 | =================== 135 | 136 | MIT License 137 | 138 | Copyright (c) 2018, Taymindis Woon 139 | 140 | Permission is hereby granted, free of charge, to any person obtaining a copy 141 | of this software and associated documentation files (the "Software"), to deal 142 | in the Software without restriction, including without limitation the rights 143 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 144 | copies of the Software, and to permit persons to whom the Software is 145 | furnished to do so, subject to the following conditions: 146 | 147 | The above copyright notice and this permission notice shall be included in all 148 | copies or substantial portions of the Software. 149 | 150 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 151 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 152 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 153 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 154 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 155 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 156 | SOFTWARE. 157 | -------------------------------------------------------------------------------- /src/cstomp.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE 3 | #endif 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "cstomp.h" 16 | 17 | typedef void* (*cstmp_malloc_fn)(void* arg, size_t sz); 18 | typedef void (*cstmp_free_fn)(void* arg, void* ptr); 19 | 20 | static 21 | void* cstmp_default_malloc(void *arg, size_t sz) { 22 | return malloc(sz); 23 | } 24 | 25 | static 26 | void cstmp_default_free(void* arg, void* ptr) { 27 | free(ptr); 28 | } 29 | 30 | static cstmp_malloc_fn __cstmp_alloc__ = cstmp_default_malloc; 31 | static cstmp_free_fn __cstmp_free__ = cstmp_default_free; 32 | static void* __stp_arg__ = NULL; 33 | 34 | #define cstmp_def_header_size 256 35 | #define cstmp_def_message_size 1024 36 | #define cstmp_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n)) 37 | #define cstmp_buf_size(b) (size_t) (b->last - b->start) 38 | #define cstmp_buf_left(b) (size_t) ( (b->start + b->total_size) - b->last) 39 | #define LF_CHAR (u_char) '\n' 40 | #define LF (u_char*) "\n" 41 | #define CRLF (u_char*)"\r\n" 42 | #define C_STMP_POLL_ERR (-1) 43 | #define C_STMP_POLL_EXPIRE (0) 44 | #define C_STMP_WRITE_CTR_AT_LF(fd) send(fd, "\0\n", 2, 0) 45 | 46 | /*** 47 | * Sharing the socket for read and write will make the things split up 48 | * Make sure each socket only proceed one frame fully requested 49 | **/ 50 | #ifdef CSTOMP_READ_WRITE_SHR_LOCK 51 | #define CSTMP_LOCK_READING while(__sync_lock_test_and_set(&sess->read_lock, 1)) 52 | #define CSTMP_RELEASE_READING __sync_lock_release(&sess->read_lock) 53 | #define CSTMP_LOCK_WRITING while(__sync_lock_test_and_set(&sess->write_lock, 1)) 54 | #define CSTMP_RELEASE_WRITING __sync_lock_release(&sess->write_lock) 55 | #else 56 | #define CSTMP_LOCK_READING 57 | #define CSTMP_RELEASE_READING 58 | #define CSTMP_LOCK_WRITING 59 | #define CSTMP_RELEASE_WRITING 60 | #endif 61 | 62 | #define CHECK_ERROR(n) \ 63 | if(n<0){\ 64 | if (errno == EWOULDBLOCK || errno == EINTR) {\ 65 | continue;\ 66 | }else if (errno != EAGAIN){\ 67 | fprintf(stderr, "Error while process socket read/write: %s\n",strerror(errno));\ 68 | tries=0;success=0;/*FAIL*/\ 69 | }} 70 | 71 | #define CHECK_OR_GOTO(n, __step) \ 72 | if(n<0){\ 73 | if ((errno == EWOULDBLOCK || errno == EINTR) && tries--) {\ 74 | goto __step;\ 75 | }else if (errno != EAGAIN){\ 76 | fprintf(stderr, "Error while process socket read/write: %s\n",strerror(errno));\ 77 | tries=0;success=0;/*FAIL*/\ 78 | }} 79 | 80 | #define FRAME_READ_RETURN(success) \ 81 | CSTMP_RELEASE_READING;\ 82 | if(!success){\ 83 | fprintf(stderr, "%s\n", "Error, Invalid frame IO reading");\ 84 | }return success; 85 | 86 | 87 | static const u_char *__cstmp_commands[16] = { 88 | (u_char*)"SEND", 89 | (u_char*)"SUBSCRIBE", 90 | (u_char*)"UNSUBSCRIBE", 91 | (u_char*)"BEGIN", 92 | (u_char*)"COMMIT", 93 | (u_char*)"ABORT", 94 | (u_char*)"ACK", 95 | (u_char*)"NACK", 96 | (u_char*)"DISCONNECT", 97 | (u_char*)"CONNECT", 98 | (u_char*)"STOMP", 99 | (u_char*)"CONNECTED", 100 | (u_char*)"MESSAGE", 101 | (u_char*)"RECEIPT", 102 | (u_char*)"ERROR", 103 | (u_char*)"" 104 | }; 105 | 106 | 107 | /** Extra malloc and free customization **/ 108 | void 109 | cstmp_set_malloc_management(void* (*stp_alloc)(void* arg, size_t sz), void (*stp_free)(void* arg, void* ptr), void* arg ) { 110 | if ( !stp_alloc || !stp_free ) { 111 | fprintf( stderr, "%s\n", "invalid memory allocation function"); 112 | } 113 | 114 | __cstmp_alloc__ = stp_alloc; 115 | __cstmp_free__ = stp_free; 116 | __stp_arg__ = arg; 117 | } 118 | 119 | #define ROLLBACK_SESSION(sess) __cstmp_free__(__stp_arg__, sess); 120 | 121 | /*Default*/ 122 | cstmp_session_t* 123 | cstmp_connect(const char *hostname, int port ) { 124 | return cstmp_connect_t(hostname, port, 3000, 3000); 125 | } 126 | 127 | cstmp_session_t* 128 | cstmp_connect_t(const char *hostname, int port, int send_timeout, int recv_timeout ) { 129 | int connfd; 130 | struct sockaddr_in *servaddr; 131 | size_t sizeofaddr; 132 | struct hostent *hostip; 133 | cstmp_session_t* sess = NULL; 134 | 135 | sess = __cstmp_alloc__(__stp_arg__, sizeof(cstmp_session_t)); 136 | 137 | if (sess == NULL) { 138 | fprintf( stderr, "%s\n", "Err: No enough memory allocated"); 139 | } 140 | 141 | servaddr = &sess->addr; 142 | sizeofaddr = sizeof(sess->addr); 143 | 144 | if ( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { 145 | fprintf( stderr, "%s\n", "Error: Unable to create socket"); 146 | ROLLBACK_SESSION(sess); 147 | return NULL; 148 | } 149 | 150 | // initialize the address 151 | bzero(servaddr, sizeofaddr); 152 | servaddr->sin_family = AF_INET; 153 | servaddr->sin_port = htons(port); 154 | // inet_pton(AF_INET, argv[1], &servaddr.sin_addr); 155 | /* Connect to NR Stomp server */ 156 | if (!(hostip = gethostbyname(hostname))) 157 | { 158 | fprintf(stderr, "Unable to determine IP address of host %s\n", hostname); 159 | ROLLBACK_SESSION(sess); 160 | return NULL; 161 | } 162 | 163 | servaddr->sin_addr = *(struct in_addr *)hostip->h_addr; 164 | 165 | struct timeval send_tmout_val; 166 | send_tmout_val.tv_sec = send_timeout / 1000; // Default 1 sec time out 167 | send_tmout_val.tv_usec = (send_timeout % 1000) * 1000 ; 168 | if (setsockopt (connfd, SOL_SOCKET, SO_SNDTIMEO, &send_tmout_val, 169 | sizeof(send_tmout_val)) < 0) 170 | fprintf(stderr, "%s\n", "setsockopt send_tmout_val failed\n"); 171 | 172 | struct timeval recv_tmout_val; 173 | recv_tmout_val.tv_sec = recv_timeout / 1000; // Default 1 sec time out 174 | recv_tmout_val.tv_usec = (recv_timeout % 1000) * 1000 ; 175 | if (setsockopt (connfd, SOL_SOCKET, SO_RCVTIMEO, &recv_tmout_val, 176 | sizeof(recv_tmout_val)) < 0) 177 | fprintf(stderr, "%s\n", "setsockopt recv_tmout_val failed\n"); 178 | 179 | if ( connect( connfd, ( struct sockaddr * )servaddr, sizeofaddr ) < 0 ) { 180 | fprintf( stderr, "Error: unable to connect to %s:%d\n", hostname, port); 181 | ROLLBACK_SESSION(sess); 182 | return NULL; 183 | } 184 | 185 | sess->sock = connfd; 186 | #ifdef CSTOMP_READ_WRITE_SHR_LOCK 187 | sess->read_lock = 0; 188 | sess->write_lock = 0; 189 | 190 | #else 191 | fprintf(stderr, "%s\n", "Not allowed Read write sharing"); 192 | #endif 193 | sess->send_timeout = send_timeout; 194 | sess->recv_timeout = recv_timeout; 195 | 196 | // int flags = fcntl(connfd, F_GETFL, 0); 197 | // flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); 198 | // if (fcntl(connfd, F_SETFL, flags) != 0) 199 | // { 200 | // fprintf(stderr, "Error setting non-blocking mode on socket: %s\n", 201 | // strerror(errno)); 202 | // return NULL; 203 | // } 204 | 205 | // sess->pfds[0].fd = connfd; 206 | // sess->pfds[0].events = POLLIN | POLLOUT; 207 | 208 | return sess; 209 | } 210 | 211 | /**To create new socket, prevent concurrent issue**/ 212 | cstmp_session_t* 213 | cstmp_new_session( cstmp_session_t* curr_sess ) { 214 | int connfd; 215 | struct sockaddr_in *servaddr; 216 | cstmp_session_t* sess = NULL; 217 | int send_timeout = curr_sess->send_timeout, 218 | recv_timeout = curr_sess->recv_timeout; 219 | 220 | sess = __cstmp_alloc__(__stp_arg__, sizeof(cstmp_session_t)); 221 | 222 | if (sess == NULL) { 223 | fprintf( stderr, "%s\n", "Err: No enough memory allocated"); 224 | } 225 | 226 | 227 | if ( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { 228 | fprintf( stderr, "%s\n", "Error: Unable to create socket"); 229 | ROLLBACK_SESSION(sess); 230 | return NULL; 231 | } 232 | 233 | 234 | if ( connect( connfd, ( struct sockaddr * )&curr_sess->addr, sizeof(curr_sess->addr) ) < 0 ) { 235 | fprintf( stderr, "Error: unable to connect while creating new session\n"); 236 | ROLLBACK_SESSION(sess); 237 | return NULL; 238 | } 239 | 240 | struct timeval send_tmout_val; 241 | send_tmout_val.tv_sec = send_timeout / 1000; // Default 1 sec time out 242 | send_tmout_val.tv_usec = (send_timeout % 1000) * 1000 ; 243 | if (setsockopt (connfd, SOL_SOCKET, SO_SNDTIMEO, &send_tmout_val, 244 | sizeof(send_tmout_val)) < 0) 245 | fprintf(stderr, "%s\n", "setsockopt send_tmout_val failed\n"); 246 | 247 | struct timeval recv_tmout_val; 248 | recv_tmout_val.tv_sec = recv_timeout / 1000; // Default 1 sec time out 249 | recv_tmout_val.tv_usec = (recv_timeout % 1000) * 1000 ; 250 | if (setsockopt (connfd, SOL_SOCKET, SO_RCVTIMEO, &recv_tmout_val, 251 | sizeof(recv_tmout_val)) < 0) 252 | fprintf(stderr, "%s\n", "setsockopt recv_tmout_val failed\n"); 253 | 254 | sess->addr = sess->addr; 255 | sess->sock = connfd; 256 | #ifdef CSTOMP_READ_WRITE_SHR_LOCK 257 | sess->read_lock = 0; 258 | sess->write_lock = 0; 259 | #else 260 | fprintf(stderr, "%s\n", "Not allowed Read write sharing"); 261 | #endif 262 | sess->send_timeout = send_timeout; 263 | sess->recv_timeout = recv_timeout; 264 | 265 | return sess; 266 | } 267 | 268 | /** Do take note that if you disc the session, the frame instance is not longer valid **/ 269 | void 270 | cstmp_disconnect(cstmp_session_t* stp_sess) { 271 | if (stp_sess) { 272 | shutdown(stp_sess->sock, SHUT_RDWR); 273 | close(stp_sess->sock); 274 | __cstmp_free__(__stp_arg__, stp_sess); 275 | } 276 | } 277 | 278 | cstmp_frame_t* 279 | cstmp_new_frame() { 280 | cstmp_frame_buf_t *headers, *body; 281 | 282 | cstmp_frame_t *fr = __cstmp_alloc__(__stp_arg__, sizeof(cstmp_frame_t)); 283 | if (fr == NULL) { 284 | return NULL; 285 | } 286 | fr->cmd = ""; 287 | headers = &fr->headers; 288 | body = &fr->body; 289 | headers->start = headers->last = __cstmp_alloc__(__stp_arg__, cstmp_def_header_size * sizeof(u_char)); 290 | if (headers->start == NULL) { 291 | __cstmp_free__(__stp_arg__, fr); 292 | return NULL; 293 | } 294 | headers->total_size = cstmp_def_header_size; 295 | 296 | body->start = body->last = __cstmp_alloc__(__stp_arg__, cstmp_def_message_size * sizeof(u_char)); 297 | if (body->start == NULL) { 298 | __cstmp_free__(__stp_arg__, headers->start ); 299 | __cstmp_free__(__stp_arg__, fr); 300 | return NULL; 301 | } 302 | body->total_size = cstmp_def_message_size; 303 | 304 | return fr; 305 | } 306 | 307 | /** Extra malloc and free customization **/ 308 | void 309 | cstmp_destroy_frame(cstmp_frame_t *fr) { 310 | if (fr) { 311 | if (fr->headers.start) 312 | __cstmp_free__(__stp_arg__, fr->headers.start); 313 | if (fr->body.start) 314 | __cstmp_free__(__stp_arg__, fr->body.start); 315 | __cstmp_free__(__stp_arg__, fr); 316 | } 317 | } 318 | 319 | static int 320 | _cstmp_reload_buf_size (cstmp_frame_buf_t * buf, size_t needed_size) { 321 | size_t new_size = buf->total_size; 322 | do { 323 | new_size *= 2; 324 | } while (new_size < needed_size); 325 | 326 | u_char *last, *start = __cstmp_alloc__(__stp_arg__, new_size * sizeof(u_char) ); 327 | last = cstmp_cpymem(start, buf->start, cstmp_buf_size(buf)); 328 | __cstmp_free__(__stp_arg__, buf->start ); // remove the old buf 329 | buf->start = start; 330 | buf->last = last; 331 | buf->total_size = new_size; 332 | return 1; 333 | } 334 | 335 | static int 336 | _cstmp_add_buf(cstmp_frame_buf_t * buf, const u_char *val, size_t val_len) { 337 | if ( ( cstmp_buf_size(buf) + val_len) > buf->total_size ) { 338 | _cstmp_reload_buf_size(buf, cstmp_buf_size(buf) + val_len/*for : and LF and \0*/); 339 | } 340 | buf->last = cstmp_cpymem(buf->last, val, val_len); 341 | return 1; 342 | } 343 | 344 | int 345 | cstmp_add_header_str(cstmp_frame_t *fr, const u_char *keyval) { 346 | cstmp_frame_buf_t *headers; 347 | size_t keyval_len; 348 | 349 | if (!keyval) 350 | return 0; 351 | 352 | keyval_len = strlen(keyval); 353 | 354 | headers = &fr->headers; 355 | 356 | if ( ( cstmp_buf_size(headers) + keyval_len + 2) > headers->total_size ) { 357 | _cstmp_reload_buf_size(headers, cstmp_buf_size(headers) + keyval_len + 2/*for : and LF and \0*/); 358 | } 359 | 360 | headers->last = cstmp_cpymem(headers->last, keyval, keyval_len); 361 | headers->last = cstmp_cpymem(headers->last, LF, 1 * sizeof(u_char)); 362 | *headers->last = '\0'; 363 | return 1; 364 | } 365 | 366 | int 367 | cstmp_add_header_str_and_len(cstmp_frame_t *fr, u_char *keyval, size_t keyval_len) { 368 | cstmp_frame_buf_t *headers; 369 | 370 | if (!keyval || !keyval_len) 371 | return 0; 372 | 373 | headers = &fr->headers; 374 | 375 | if ( ( cstmp_buf_size(headers) + keyval_len + 2) > headers->total_size ) { 376 | _cstmp_reload_buf_size(headers, cstmp_buf_size(headers) + keyval_len + 2/*for : and LF and \0*/); 377 | } 378 | 379 | headers->last = cstmp_cpymem(headers->last, keyval, keyval_len); 380 | headers->last = cstmp_cpymem(headers->last, LF, 1 * sizeof(u_char)); 381 | *headers->last = '\0'; 382 | return 1; 383 | } 384 | 385 | int 386 | cstmp_add_header(cstmp_frame_t *fr, const u_char *key, const u_char* val) { 387 | cstmp_frame_buf_t *headers; 388 | size_t key_len, val_len; 389 | 390 | if (!key && !val) 391 | return 0; 392 | 393 | key_len = strlen(key); 394 | val_len = strlen(val); 395 | 396 | headers = &fr->headers; 397 | 398 | if ( ( cstmp_buf_size(headers) + key_len + val_len + 3/*for : and LF and \0*/) > headers->total_size ) { 399 | _cstmp_reload_buf_size(headers, cstmp_buf_size(headers) + key_len + val_len + 3/*for : and LF and \0*/); 400 | } 401 | 402 | headers->last = cstmp_cpymem(headers->last, key, key_len); 403 | *headers->last++ = ':'; 404 | headers->last = cstmp_cpymem(headers->last, val, val_len); 405 | headers->last = cstmp_cpymem(headers->last, LF, 1 * sizeof(u_char)); 406 | *headers->last = '\0'; 407 | return 1; 408 | } 409 | 410 | int 411 | cstmp_add_body_content(cstmp_frame_t *fr, u_char* content) { 412 | cstmp_frame_buf_t *body; 413 | size_t body_len; 414 | if (!content) 415 | return 0; 416 | 417 | body_len = strlen(content); 418 | body = &fr->body; 419 | 420 | if ( ( cstmp_buf_size(body) + body_len ) > body->total_size ) { 421 | _cstmp_reload_buf_size(body, cstmp_buf_size(body) + body_len); 422 | } 423 | 424 | body->last = cstmp_cpymem(body->last, content, body_len); 425 | return 1; 426 | } 427 | 428 | int 429 | cstmp_add_body_content_and_len(cstmp_frame_t *fr, u_char* content, size_t content_len) { 430 | cstmp_frame_buf_t *body; 431 | if (!content) 432 | return 0; 433 | 434 | body = &fr->body; 435 | 436 | if ( ( cstmp_buf_size(body) + content_len ) > body->total_size ) { 437 | _cstmp_reload_buf_size(body, cstmp_buf_size(body) + content_len); 438 | } 439 | 440 | body->last = cstmp_cpymem(body->last, content, content_len); 441 | return 1; 442 | } 443 | 444 | u_char* 445 | cstmp_get_cmd(cstmp_frame_t *fr) { 446 | if (fr) { 447 | return fr->cmd; 448 | } 449 | return ""; 450 | } 451 | 452 | int 453 | cstmp_get_header(cstmp_frame_t *fr, const u_char *key, cstmp_frame_val_t *hdr_val) { 454 | size_t klen = key ? strlen(key) : 0; 455 | u_char *ret = fr->headers.start; 456 | while ( ret = strstr(ret, key) ) { 457 | ret = ret + klen; 458 | if ( *ret == ':' ) break; 459 | } 460 | 461 | if (ret) { 462 | hdr_val->data = ++ret; 463 | hdr_val->len = ((u_char*)strchr(hdr_val->data, LF_CHAR)) - hdr_val->data; 464 | return 1; 465 | } else { 466 | hdr_val->data = NULL; 467 | hdr_val->len = 0; 468 | return 0; 469 | } 470 | } 471 | 472 | void 473 | cstmp_get_body(cstmp_frame_t *fr, cstmp_frame_val_t *body_val) { 474 | body_val->data = fr->body.start; 475 | body_val->len = cstmp_buf_size((&fr->body)); 476 | } 477 | 478 | static void 479 | cstmp_parse_cmd(cstmp_frame_t *fr, u_char* cmd) { 480 | static size_t cmd_size = sizeof(__cstmp_commands) / sizeof(u_char*) - 1; 481 | u_char **cmds = (u_char**) __cstmp_commands; 482 | int i; 483 | fr->cmd = ""; // empty cmd by default 484 | 485 | /***only 15 valid commands*/ 486 | for (i = 0; i < cmd_size; i++) { 487 | if (strcmp(cmds[i], cmd) == 0) { 488 | fr->cmd = cmds[i]; 489 | break; 490 | } 491 | } 492 | } 493 | 494 | static void 495 | cstmp_print_raw(u_char *str, size_t len) { 496 | int i; 497 | char c; 498 | for (i = 0; i < len; i++) { 499 | c = str[i]; 500 | if (isprint(c)) 501 | putchar(c); // just print printable characters 502 | else if (c == '\n') 503 | printf("\\n"); // display newline as \n 504 | else if (c == '\r') 505 | printf("\\r"); // display newline as \n 506 | else if (c == '\0') 507 | printf("\\0"); // display newline as \n 508 | else 509 | printf("%02x", c); // print everything else as a number 510 | } 511 | } 512 | 513 | void 514 | cstmp_dump_frame_raw(cstmp_frame_t *fr) { 515 | printf("%s", fr->cmd); 516 | printf("\\n"); 517 | cstmp_print_raw(fr->headers.start, cstmp_buf_size((&fr->headers))); 518 | printf("\\n"); 519 | cstmp_print_raw(fr->body.start, cstmp_buf_size((&fr->body))); 520 | printf("\n"); 521 | } 522 | 523 | void 524 | cstmp_dump_frame_pretty(cstmp_frame_t *fr) { 525 | printf("%s\n", fr->cmd); 526 | printf("%.*s\n\n", (int) cstmp_buf_size((&fr->headers)), fr->headers.start); 527 | printf("%.*s\n", (int)cstmp_buf_size((&fr->body)), fr->body.start); 528 | printf("\n"); 529 | } 530 | 531 | void 532 | cstmp_reset_frame(cstmp_frame_t *fr) { 533 | if (fr) { 534 | fr->cmd = ""; 535 | memset(fr->headers.start, 0, cstmp_buf_size((&fr->headers))); 536 | memset(fr->body.start, 0, cstmp_buf_size((&fr->body))); 537 | fr->headers.last = fr->headers.start; 538 | fr->body.last = fr->body.start; 539 | } 540 | } 541 | 542 | int 543 | cstmp_send_direct(cstmp_session_t *sess, const u_char *frame_str, int tries) { 544 | int success = 0, connfd, rv; 545 | if (sess) { 546 | connfd = sess->sock; 547 | CSTMP_LOCK_WRITING; 548 | do { 549 | if ( (rv = send(connfd, frame_str, strlen(frame_str), 0)) < 0 || 550 | (rv = C_STMP_WRITE_CTR_AT_LF(connfd)) < 0) { 551 | CHECK_ERROR(rv); 552 | } else { 553 | success = 1; 554 | tries = 0; 555 | } 556 | } while (tries--); /*while try*/ 557 | CSTMP_RELEASE_WRITING; 558 | } 559 | return success; 560 | } 561 | 562 | int 563 | cstmp_send(cstmp_session_t *sess, cstmp_frame_t *fr, int tries) { 564 | int success = 0, connfd, rv; 565 | if (fr && sess) { 566 | connfd = sess->sock; 567 | const u_char* cmd = fr->cmd; 568 | const size_t header_len = cstmp_buf_size((&fr->headers)), body_len = cstmp_buf_size((&fr->body)); 569 | CSTMP_LOCK_WRITING; 570 | do { 571 | if ( 572 | (rv = send(connfd, cmd , strlen(cmd), 0)) < 0 || 573 | (rv = send(connfd, LF, 1 * sizeof(u_char), 0)) < 0 || 574 | (header_len && (rv = send(connfd, fr->headers.start , header_len, 0)) < 0) || 575 | (rv = send(connfd, LF, 1 * sizeof(u_char), 0)) < 0 || 576 | (body_len && (rv = send(connfd, fr->body.start , body_len, 0)) < 0) || 577 | (rv = C_STMP_WRITE_CTR_AT_LF(connfd)) < 0 578 | ) { 579 | CHECK_ERROR(rv); 580 | } else { 581 | success = 1; 582 | tries = 0; 583 | } 584 | } while (tries--);/*while try*/ 585 | CSTMP_RELEASE_WRITING; 586 | } else fprintf(stderr, "%s\n", "Invalid Frame or session type"); 587 | return success; 588 | } 589 | 590 | int 591 | cstmp_recv(cstmp_session_t *sess, cstmp_frame_t *fr, int tries) { 592 | int success = 0, connfd, content_len_i; 593 | u_char* content_len_s; 594 | if (fr && sess) { 595 | u_char cmd_buff[1], cmd[12]; 596 | cstmp_reset_frame(fr); 597 | connfd = sess->sock; 598 | int n, i = 0; 599 | CSTMP_LOCK_READING; 600 | do { 601 | /*Parse Cmd*/ 602 | while ( (n = recv( connfd , cmd_buff, 1, 0)) > 0) { 603 | if (cmd_buff[0] == '\n') { 604 | cmd[i] = '\0'; 605 | cstmp_parse_cmd(fr, cmd); 606 | break; 607 | } else if (i == 12) { 608 | FRAME_READ_RETURN(0); 609 | } 610 | cmd[i++] = cmd_buff[0]; 611 | } 612 | CHECK_ERROR(n); 613 | /***parse Header ***/ 614 | u_char last_char = 0; 615 | cstmp_frame_buf_t *headers = &fr->headers; 616 | 617 | while ((n = recv( connfd , headers->last, 1, 0)) > 0) { 618 | if (*headers->last == '\n' && last_char == '\n') { 619 | _cstmp_add_buf(headers, "\0", 1 * sizeof(u_char) ); 620 | 621 | /***Add Body ***/ 622 | cstmp_frame_buf_t *body = &fr->body; 623 | if ( content_len_s = strstr(headers->start, "content-length:") ) { 624 | content_len_i = atoi(content_len_s + 15 /* sizeof content-length: */ ); 625 | if (content_len_i > cstmp_buf_left(body)) { 626 | _cstmp_reload_buf_size(body, (size_t) content_len_i); 627 | } 628 | REREAD_WHOLE_BODY: 629 | if ((n = recv( connfd , body->last, content_len_i, 0)) > 0) { 630 | body->last += n; 631 | char terminator_linecheck[2]; 632 | if (((n = recv( connfd , terminator_linecheck, 2, 0)) > 1) && 633 | terminator_linecheck[0] == 0 && terminator_linecheck[1] == '\n') { 634 | FRAME_READ_RETURN(1); 635 | } 636 | FRAME_READ_RETURN(0); 637 | } 638 | CHECK_OR_GOTO(n, REREAD_WHOLE_BODY); 639 | } else { 640 | REREAD_BODY: 641 | while ((n = recv( connfd , body->last, 1, 0)) > 0) { 642 | if (*body->last == 0) { 643 | char recv_buff[1]; 644 | /** Check the Frame Terminator**/ 645 | if (((n = recv( connfd , recv_buff, 1, 0)) > 0)) { 646 | if (recv_buff[0] != '\n') { 647 | FRAME_READ_RETURN(0); 648 | } 649 | FRAME_READ_RETURN(1); 650 | } 651 | } 652 | *body->last++; 653 | if (cstmp_buf_size(body) == body->total_size) { 654 | _cstmp_reload_buf_size(body, body->total_size * 2); 655 | } 656 | } 657 | CHECK_OR_GOTO(n, REREAD_BODY); 658 | } 659 | FRAME_READ_RETURN(0); 660 | } 661 | last_char = *headers->last++; /*only plus 1*/ 662 | if (cstmp_buf_size(headers) == headers->total_size) { 663 | _cstmp_reload_buf_size(headers, headers->total_size * 2); 664 | } 665 | } 666 | /** end of file **/ 667 | CHECK_ERROR(n); 668 | } while (tries--);/*while try*/ 669 | CSTMP_RELEASE_READING; 670 | } else fprintf(stderr, "%s\n", "Invalid Frame type"); 671 | return success; /*Failed*/ 672 | } 673 | 674 | void 675 | cstmp_consume(cstmp_session_t *sess, cstmp_frame_t *fr, void (*callback)(cstmp_frame_t *), int *consuming) { 676 | while (*consuming) { 677 | if (cstmp_recv(sess, fr, 0)) { 678 | callback(fr); 679 | } 680 | } 681 | } --------------------------------------------------------------------------------