├── .gitignore ├── CMakeLists.txt ├── CMakeMacro.cmake ├── README ├── TODO ├── debian ├── changelog ├── compat ├── control ├── copyright ├── rules ├── ugh-dev.install └── ugh.install ├── tmp ├── coro.c ├── coro_ucontext_native.c ├── dns.3.1 ├── dns.8.8 ├── dyn │ ├── app.c │ ├── app.h │ └── lib.c ├── gai.c ├── inet_addr.c ├── test_pool.c ├── test_resolver.c └── yandex.ru.txt ├── ugh.spec ├── ugh ├── autoconf.h.in ├── aux │ ├── buffer.c │ ├── buffer.h │ ├── config.h │ ├── daemon.c │ ├── daemon.h │ ├── gmtime.h │ ├── hashes.h │ ├── logger.c │ ├── logger.h │ ├── memory.c │ ├── memory.h │ ├── random.c │ ├── random.h │ ├── resolver.c │ ├── resolver.h │ ├── socket.h │ ├── string.c │ ├── string.h │ ├── system.c │ └── system.h ├── channel.c ├── client.c ├── config.c ├── config.h ├── coro │ ├── coro.c │ └── coro.h ├── coro_ucontext │ ├── coro_ucontext.c │ └── coro_ucontext.h ├── daemon.c ├── module.c ├── module.h ├── module_add_header.c ├── module_core.c ├── module_import.c ├── module_map.c ├── module_proxy.c ├── module_push_pass.c ├── module_push_publisher.c ├── module_push_subscriber.c ├── module_return.c ├── module_set.c ├── module_upstream.c ├── parser.c ├── resolver.c ├── resolver.h ├── server.c ├── status.c ├── subreq.c ├── template.c ├── ugh.cfg.in ├── ugh.h ├── upstream.c └── variables.c └── ugh_example ├── config.cfg.in └── module.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | *.sw[op] 4 | *gz 5 | *.Z 6 | *.rpm 7 | *.zip 8 | *.deb 9 | *.log 10 | 11 | CMakeFiles/ 12 | CMakeCache.txt 13 | _CPack_Packages/ 14 | cmake_install.cmake 15 | CPackConfig.cmake 16 | CPackSourceConfig.cmake 17 | Makefile 18 | install_manifest.txt 19 | 20 | /ugh.spec 21 | *.a 22 | /ugh-*.sh 23 | 24 | /auto/ 25 | /bin/ 26 | /build/ 27 | /libugh_example.so 28 | /tags 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED (VERSION 3.0) 2 | PROJECT (ugh VERSION 0.1.15) 3 | INCLUDE (CMakeMacro.cmake) 4 | 5 | USE_PACKAGE (ev ev.h) 6 | USE_PACKAGE (Judy Judy.h) 7 | 8 | 9 | IF (NOT LIB_INSTALL_DIR) 10 | SET (LIB_INSTALL_DIR lib/) 11 | ENDIF (NOT LIB_INSTALL_DIR) 12 | 13 | SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -export-dynamic -no-pie") 14 | SET (EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 15 | AUX_SOURCE_DIRECTORY (ugh/aux SRC_AUX) 16 | ADD_LIBRARY (aux STATIC ${SRC_AUX}) 17 | SET_PROPERTY (TARGET aux PROPERTY POSITION_INDEPENDENT_CODE ON) 18 | 19 | #ADD_DEFINITIONS (-DCORO_UCONTEXT) 20 | #ADD_LIBRARY (coro ugh/coro/coro.c) 21 | 22 | ADD_LIBRARY (coro_ucontext STATIC ugh/coro_ucontext/coro_ucontext.c) 23 | SET_PROPERTY (TARGET coro_ucontext PROPERTY POSITION_INDEPENDENT_CODE ON) 24 | 25 | #ADD_EXECUTABLE (test_coro tmp/coro.c) 26 | #TARGET_LINK_LIBRARIES (test_coro coro) 27 | #INSTALL (TARGETS test_coro DESTINATION bin) 28 | 29 | ADD_EXECUTABLE (ugh_test_resolver tmp/test_resolver.c) 30 | TARGET_LINK_LIBRARIES (ugh_test_resolver aux) 31 | INSTALL (TARGETS ugh_test_resolver DESTINATION bin) 32 | 33 | ADD_EXECUTABLE (test_pool tmp/test_pool.c) 34 | TARGET_LINK_LIBRARIES (test_pool aux) 35 | 36 | INCLUDE_DIRECTORIES (${PROJECT_BINARY_DIR}/auto) 37 | 38 | SET (UGH_VERSION ${PROJECT_VERSION}) 39 | 40 | AUX_SOURCE_DIRECTORY (ugh SRC_ugh) 41 | ADD_EXECUTABLE (ugh ${SRC_ugh}) 42 | SET_PROPERTY (TARGET ugh PROPERTY POSITION_INDEPENDENT_CODE TRUE) 43 | TARGET_LINK_LIBRARIES (ugh aux coro_ucontext ${LIB_Judy} ${LIB_ev} pthread m) 44 | 45 | IF (CMAKE_SYSTEM_NAME STREQUAL Linux) 46 | TARGET_LINK_LIBRARIES (ugh dl) 47 | ENDIF () 48 | 49 | INSTALL (TARGETS ugh DESTINATION bin) 50 | INSTALL_TEMPLATE (ugh/ugh.cfg.in DESTINATION etc) 51 | INSTALL (DIRECTORY ugh DESTINATION include FILES_MATCHING PATTERN "*.h") 52 | INSTALL_TEMPLATE (ugh/autoconf.h.in DESTINATION include/ugh) 53 | 54 | # modules 55 | 56 | AUX_SOURCE_DIRECTORY (ugh_example SRC_UGH_EXAMPLE) 57 | ADD_LIBRARY (ugh_example MODULE ${SRC_UGH_EXAMPLE}) 58 | SET_PROPERTY (TARGET ugh_example PROPERTY POSITION_INDEPENDENT_CODE ON) 59 | INSTALL (TARGETS ugh_example DESTINATION lib/ugh) 60 | INSTALL_TEMPLATE (ugh_example/config.cfg.in DESTINATION etc/ugh_example) 61 | 62 | IF (NOT CPACK_GENERATOR) 63 | SET (CPACK_GENERATOR "RPM" "DEB" "ZIP" "TGZ") 64 | ENDIF (NOT CPACK_GENERATOR) 65 | SET (CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) 66 | SET (CPACK_PACKAGE_NAME "ugh") 67 | SET (CPACK_PACKAGE_CONTACT "bachan@yandex.ru") 68 | SET (CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/bachan/ugh/") 69 | SET (CPACK_RPM_PACKAGE_LICENSE "GPL") 70 | SET (CPACK_PACKAGE_RELEASE 1) 71 | SET (CPACK_RPM_COMPONENT_INSTALL 1) 72 | SET (CPACK_RPM_PACKAGE_AUTOREQ "no") 73 | SET (CPACK_RPM_PACKAGE_AUTOREQPROV "no") 74 | SET (CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) 75 | SET (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}") 76 | SET (CPACK_PACKAGE_VENDOR "UGH Team") 77 | INCLUDE (CPack) 78 | -------------------------------------------------------------------------------- /CMakeMacro.cmake: -------------------------------------------------------------------------------- 1 | SET (FLAGS_DEFAULT "-fPIC -pipe") 2 | SET (FLAGS_WARNING "-Wall -Werror -Wno-long-long -Wno-variadic-macros -Wno-strict-aliasing")# -Wextra -pedantic") 3 | SET (FLAGS_CXX_LANG "-Wno-deprecated") 4 | SET (FLAGS_RELEASE "-O3 -DNDEBUG") # -fomit-frame-pointer -funroll-loops 5 | SET (FLAGS_DEBUG "-ggdb") 6 | 7 | # This is needed because debian package builder sets -DCMAKE_BUILD_TYPE=None 8 | IF (CMAKE_BUILD_TYPE STREQUAL None) 9 | SET (CMAKE_BUILD_TYPE Release) 10 | ENDIF () 11 | 12 | SET (CMAKE_C_FLAGS_DEBUG "${FLAGS_DEFAULT} ${FLAGS_WARNING} ${FLAGS_DEBUG}") 13 | SET (CMAKE_C_FLAGS_RELEASE "${FLAGS_DEFAULT} ${FLAGS_WARNING} ${FLAGS_DEBUG} ${FLAGS_RELEASE}") 14 | 15 | SET (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS_CXX_LANG}") 16 | SET (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS_CXX_LANG}") 17 | 18 | IF (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 19 | SET (CMAKE_BUILD_TYPE RELEASE) 20 | SET (CMAKE_BUILD_TYPE RELEASE CACHE STRING "Build type" FORCE) 21 | ENDIF (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 22 | 23 | ############################################################################### 24 | 25 | # Enable printf format macros from in C++ code. 26 | ADD_DEFINITIONS (-D__STDC_FORMAT_MACROS) 27 | 28 | # Enable type limit macros from in C++ code. 29 | ADD_DEFINITIONS (-D__STDC_LIMIT_MACROS) 30 | 31 | # Enable 64-bit off_t type to work with big files. 32 | ADD_DEFINITIONS (-D_FILE_OFFSET_BITS=64) 33 | 34 | # Make FIND_LIBRARY search for static libs first and make it search inside lib64/ 35 | # directory in addition to the usual lib/ one. 36 | 37 | SET (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_SHARED_LIBRARY_SUFFIX}) 38 | SET (CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_STATIC_LIBRARY_PREFIX} ${CMAKE_SHARED_LIBRARY_PREFIX}) 39 | SET (FIND_LIBRARY_USE_LIB64_PATHS TRUE) 40 | SET (LINK_SEARCH_END_STATIC TRUE) 41 | 42 | # Include source tree root, include directory inside it and build tree root, 43 | # which is for files, generated by cmake from templates (e.g. autogenerated 44 | # C/C++ includes). 45 | 46 | INCLUDE_DIRECTORIES (${PROJECT_BINARY_DIR}) 47 | INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}) 48 | 49 | ############################################################################### 50 | # USE_PROGRAM (bin) 51 | # ----------------------------------------------------------------------------- 52 | # Find program [bin] using standard FIND_PROGRAM command and save its path into 53 | # variable named BIN_[bin]. 54 | 55 | MACRO (USE_PROGRAM bin) 56 | FIND_PROGRAM (BIN_${bin} ${bin}) 57 | IF (BIN_${bin}) 58 | MESSAGE (STATUS "FOUND ${BIN_${bin}}") 59 | ELSE () 60 | MESSAGE (STATUS "ERROR ${BIN_${bin}}") 61 | ENDIF () 62 | ENDMACRO (USE_PROGRAM) 63 | 64 | # USE_INCLUDE (inc [FIND_PATH_ARGS ...]) 65 | # ----------------------------------------------------------------------------- 66 | # Find include [inc] using standard FIND_PATH command and save its dirname into 67 | # variable named INC_[inc]. Also include its dirname into project. 68 | 69 | MACRO (USE_INCLUDE inc) 70 | FIND_PATH (INC_${inc} ${inc} ${ARGN}) 71 | IF (INC_${inc}) 72 | MESSAGE (STATUS "FOUND ${INC_${inc}}/${inc}") # SHOULD BE BOLD GREEN 73 | INCLUDE_DIRECTORIES (${INC_${inc}}) 74 | ELSE () 75 | MESSAGE (STATUS "ERROR ${INC_${inc}}/${inc}") # SHOULD BE BOLD RED 76 | ENDIF () 77 | ENDMACRO (USE_INCLUDE) 78 | 79 | # USE_LIBRARY (lib [FIND_LIBRARY_ARGS ...]) 80 | # ----------------------------------------------------------------------------- 81 | # Find library [lib] using standard FIND_LIBRARY command and save its path into 82 | # variable named LIB_[lib]. 83 | 84 | MACRO (USE_LIBRARY lib) 85 | FIND_LIBRARY (LIB_${lib} ${lib} ${ARGN}) 86 | IF (LIB_${lib}) 87 | MESSAGE (STATUS "FOUND ${LIB_${lib}}") # SHOULD BE BOLD GREEN 88 | ELSE () 89 | MESSAGE (STATUS "ERROR ${LIB_${lib}}") # SHOULD BE BOLD RED 90 | ENDIF () 91 | ENDMACRO (USE_LIBRARY) 92 | 93 | # USE_PACKAGE (var lib inc [FIND_PATH_ARGS ...]) 94 | # ----------------------------------------------------------------------------- 95 | # Find package using USE_LIBRARY and USE_INCLUDE macros. 96 | 97 | MACRO (USE_PACKAGE lib inc) 98 | USE_LIBRARY (${lib} ${ARGN}) 99 | USE_INCLUDE (${inc} ${ARGN}) 100 | ENDMACRO (USE_PACKAGE) 101 | 102 | MACRO (USE_PACKAGE_STATIC lib inc) 103 | SET (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) 104 | USE_PACKAGE (${lib} ${inc} ${ARGN}) 105 | SET (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_SHARED_LIBRARY_SUFFIX}) 106 | ENDMACRO (USE_PACKAGE_STATIC) 107 | 108 | MACRO (USE_PACKAGE_SHARED lib inc) 109 | SET (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) 110 | USE_PACKAGE (${lib} ${inc} ${ARGN}) 111 | SET (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_SHARED_LIBRARY_SUFFIX}) 112 | ENDMACRO (USE_PACKAGE_SHARED) 113 | 114 | # USE_SUBPATH (var sub) 115 | # ----------------------------------------------------------------------------- 116 | # Find subpath [sub] using standard FIND_PATH command and save its dirname into 117 | # variable named [var]. 118 | 119 | MACRO (USE_SUBPATH var sub) 120 | FIND_PATH (${var}_PREFIX ${sub} ONLY_CMAKE_FIND_ROOT_PATH) 121 | IF (${var}_PREFIX) 122 | GET_FILENAME_COMPONENT (${var} "${${var}_PREFIX}/${sub}" PATH) 123 | MESSAGE (STATUS "FOUND ${var}=${${var}}") 124 | ELSE (${var}_PREFIX) 125 | MESSAGE (STATUS "ERROR ${var}") 126 | ENDIF (${var}_PREFIX) 127 | ENDMACRO (USE_SUBPATH) 128 | 129 | ############################################################################### 130 | # MAKE_LIBRARY (apath [LIBRARIES_TO_LINK_WITH [...]]) 131 | # ----------------------------------------------------------------------------- 132 | # Make library of SHARED or STATIC type from source code inside the [apath] 133 | # subfolder and install it and all header files from the subfolder. 134 | 135 | MACRO (MAKE_LIBRARY apath atype) 136 | GET_FILENAME_COMPONENT (${apath}_NAME "${apath}" NAME) 137 | AUX_SOURCE_DIRECTORY (${apath} SRC_${${apath}_NAME}) 138 | ADD_LIBRARY (${${apath}_NAME} ${atype} ${SRC_${${apath}_NAME}}) 139 | IF (${ARGC} GREATER 2) 140 | TARGET_LINK_LIBRARIES (${${apath}_NAME} ${ARGN}) 141 | ENDIF (${ARGC} GREATER 2) 142 | # TODO SET_TARGET_PROPERTIES (...) 143 | INSTALL (TARGETS ${${apath}_NAME} DESTINATION lib) 144 | INSTALL (DIRECTORY ${apath} DESTINATION include FILES_MATCHING PATTERN "*.h") 145 | INSTALL (DIRECTORY ${apath} DESTINATION include FILES_MATCHING PATTERN "*.hpp") 146 | INSTALL (DIRECTORY ${apath} DESTINATION include FILES_MATCHING PATTERN "*.tcc") 147 | ENDMACRO (MAKE_LIBRARY) 148 | 149 | # MAKE_SHARED (apath [LIBRARIES_TO_LINK_WITH [...]]) 150 | # ----------------------------------------------------------------------------- 151 | # Make SHARED library with MAKE_LIBRARY macro. 152 | 153 | MACRO (MAKE_SHARED apath) 154 | MAKE_LIBRARY (${apath} SHARED ${ARGN}) 155 | ENDMACRO (MAKE_SHARED) 156 | 157 | # MAKE_STATIC (apath [LIBRARIES_TO_LINK_WITH [...]]) 158 | # ----------------------------------------------------------------------------- 159 | # Make STATIC library with MAKE_LIBRARY macro. 160 | 161 | MACRO (MAKE_STATIC apath) 162 | MAKE_LIBRARY (${apath} STATIC ${ARGN}) 163 | ENDMACRO (MAKE_STATIC) 164 | 165 | # MAKE_PROGRAM (apath) 166 | # ----------------------------------------------------------------------------- 167 | # Make program (executable) from source code inside the [apath] subfolder and 168 | # install it. 169 | 170 | MACRO (MAKE_PROGRAM apath) 171 | GET_FILENAME_COMPONENT (${apath}_NAME "${apath}" NAME) 172 | AUX_SOURCE_DIRECTORY (${apath} SRC_${${apath}_NAME}) 173 | ADD_EXECUTABLE (${${apath}_NAME} ${SRC_${${apath}_NAME}}) 174 | IF (${ARGC} GREATER 1) 175 | TARGET_LINK_LIBRARIES (${${apath}_NAME} ${ARGN}) 176 | ENDIF (${ARGC} GREATER 1) 177 | INSTALL (TARGETS ${${apath}_NAME} DESTINATION bin) 178 | ENDMACRO (MAKE_PROGRAM) 179 | 180 | # MAKE_TEST (apath) 181 | # ----------------------------------------------------------------------------- 182 | # Make test from source code inside the [apath] subfolder. 183 | 184 | MACRO (MAKE_TEST apath) 185 | GET_FILENAME_COMPONENT (${apath}_NAME "${apath}" NAME) 186 | AUX_SOURCE_DIRECTORY (${apath} SRC_test_${${apath}_NAME}) 187 | ADD_EXECUTABLE (test_${${apath}_NAME} ${SRC_test_${${apath}_NAME}}) 188 | IF (${ARGC} GREATER 1) 189 | TARGET_LINK_LIBRARIES (test_${${apath}_NAME} ${ARGN}) 190 | ENDIF (${ARGC} GREATER 1) 191 | ADD_TEST (test_${${apath}_NAME} test_${${apath}_NAME}}) 192 | ENDMACRO (MAKE_TEST) 193 | 194 | # INSTALL_TEMPLATE (sub [INSTALL_ARGS [...]]) 195 | # ----------------------------------------------------------------------------- 196 | # Install template files (*.in) with one line of code, all arguments except the 197 | # first one will be left untouched and proxied to INSTALL (FILES) call. 198 | 199 | MACRO (INSTALL_TEMPLATE sub) 200 | STRING (REGEX REPLACE "\\.in$" "" ${sub}_NOIN ${sub}) 201 | CONFIGURE_FILE (${sub} ${PROJECT_BINARY_DIR}/auto/${${sub}_NOIN}) 202 | INSTALL (FILES ${PROJECT_BINARY_DIR}/auto/${${sub}_NOIN} ${ARGN}) 203 | ENDMACRO (INSTALL_TEMPLATE) 204 | 205 | ############################################################################### 206 | 207 | function(PROTOBUF_GENERATE_CPP SRCS HDRS) 208 | if(NOT ARGN) 209 | message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files") 210 | return() 211 | endif(NOT ARGN) 212 | 213 | set(${SRCS}) 214 | set(${HDRS}) 215 | foreach(FIL ${ARGN}) 216 | get_filename_component(ABS_FIL ${FIL} ABSOLUTE) 217 | string(REPLACE ".proto" "" FIL_WE ${FIL}) 218 | 219 | list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc") 220 | list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h") 221 | 222 | add_custom_command( 223 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc" 224 | "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h" 225 | COMMAND protoc 226 | ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} --proto_path ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FIL} 227 | DEPENDS ${ABS_FIL} 228 | COMMENT "Running C++ protocol buffer compiler on ${FIL}" 229 | VERBATIM ) 230 | endforeach() 231 | 232 | set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) 233 | set(${SRCS} ${${SRCS}} PARENT_SCOPE) 234 | set(${HDRS} ${${HDRS}} PARENT_SCOPE) 235 | endfunction() 236 | 237 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | MAIN PURPOSE 2 | 3 | Ugh is HTTP server, for which it's very simple to write module, which goes to 4 | different backends simultaneously, then does something with their response, 5 | then goes again to some other backends et cetera. 6 | 7 | This is achieved by using coroutines mechanism. Each request is handled in a 8 | separate coroutine and the main coroutine is for server's event loop. That's 9 | why you could ask for several subrequests, then just call ugh_subreq_wait() to 10 | wait for their results and then, just after ugh_subreq_wait() is returned, you 11 | could use all the results from subrequest, like it was synchronous code. 12 | 13 | You could do simultaneous subrequests even without writing your own module, but 14 | ugh is much more flexible and powerful if you're using its API, not only 15 | configuration. 16 | 17 | CONFIGURATION 18 | 19 | Configuration of ugh tries to be like nginx. Actually it's much simpler, cause 20 | ugh doesn't support most of nginx's features, and does only its specific kind 21 | of job (simultaneous subrequests with non-trivial logic of handling their 22 | responses). 23 | 24 | Example of config file is included in repository and is self-explanatory. 25 | Possible directives include: 26 | 27 | error_log file [debug | info | notice | warn | error | crit | alert | emerg]; 28 | Log errors into file (with optional severity argument). 29 | 30 | listen [address:]port; 31 | What address:port to listen to. 32 | 33 | resolver host; 34 | Use specific DNS-server to resolve hosts of subrequest URLs. 35 | 36 | import module_name [path/to/module.so]; 37 | Import third-party module from .so-library. By default ugh searches inside 38 | PREFIX/lib/ugh/ for library named libugh_module_name.so. 39 | 40 | map { ... } 41 | Just like nginx's map (doesn't support hostnames flag, though). 42 | 43 | upstream { ... } 44 | Just like nginx's upstream, but doesn't support all server's options and 45 | backup is not an option, but is different directive. See config example. 46 | Supported options are max_fails and fail_timeout, but there's an importante 47 | difference between nginx and ugh in how they work: 48 | a) in ugh max_fails / fail_timeout work even for one-server upstream; 49 | b) max_fails is counted as consequtive failures, not as failures for 50 | fail_timeout period of time. 51 | 52 | proxy_pass url; 53 | Just like nginx's proxy_pass, but do not use scheme in its argument. 54 | 55 | proxy_nowait on | off; 56 | Wait or don't wait for response of the latest previous proxy_pass in config. 57 | 58 | proxy_recv_timeout timeout; 59 | Just like nginx's one, affects only latest previous proxy_pass in config. 60 | 61 | proxy_next_upstream type; 62 | Just like nginx's one, affects only latest previous proxy_pass in config. 63 | 64 | return data; 65 | Return some data with 200 OK. 66 | 67 | One more note about configuration is that you could use variables and templates 68 | (strings with variables inside) like in nginx. There are some built-in 69 | variables. You can make your own with map directive. Directives return and 70 | proxy_pass support variables in their arguments. The list of built-in 71 | variables: 72 | 73 | $arg_... - GET-argument with "..." name. 74 | $http_... - HTTP header with "..." name. 75 | $cookie_... - HTTP cookie with "..." name. 76 | $hash_... - hash of value of variable with "..." name. 77 | $c0_... - first char of value of variable with "..." name. 78 | $cl_... - last char of value of variable with "..." name. 79 | 80 | $var_... - value of custom variable with "..." name (can be set by module, this 81 | might be removed in later versions in favor of more flexible mechanism). 82 | 83 | COMPILE AND INSTALL 84 | 85 | Any Unix/Linux system: 86 | $ cmake . 87 | $ make 88 | $ make install 89 | 90 | Debial/Ubuntu package: 91 | $ dpkg-buildpackage -rfakeroot 92 | 93 | WRITING MODULES 94 | 95 | See module example in repository. 96 | 97 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | ugh_subreq_set_header 2 | автоматические обработчики для простых директив конфига (типа "записать в строку", "записать в число" итп) 3 | директива location (+ потенциально вложенные локейшены и захват паттернов (?)) 4 | директива subreq должна понимать схему 5 | разделить subreq на составление запроса и общение по сети, чтоб можно было выделять протоколы в отдельные модули 6 | keepalive с бекендами 7 | встроить libev 8 | поддержка GET http://... запросов 9 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | ugh (0.1.15-1) unstable; urgency=medium 2 | 3 | * Added support for HTTP statuses 417 and 451 4 | 5 | -- Ruslan Osmanov Mon, 13 Sep 2019 18:13:00 +0300 6 | 7 | ugh (0.1.14-1) unstable; urgency=medium 8 | 9 | * supported templates of unlimited length 10 | 11 | -- Sergey Bochenkov Thu, 13 Sep 2018 15:45:03 +0700 12 | 13 | ugh (0.1.13-1) unstable; urgency=medium 14 | 15 | * Fixed dependency to library 'm' for Debian 9 16 | 17 | -- Konstantin Akimov Wed, 06 Dec 2017 19:45:03 +0700 18 | 19 | ugh (0.1.12-1) unstable; urgency=medium 20 | 21 | * fixed subreq headers reading 22 | 23 | -- Sergey Bochenkov Tue, 04 Apr 2017 12:38:58 +0700 24 | 25 | ugh (0.1.11-1) unstable; urgency=medium 26 | 27 | * support setting out headers for subrequest 28 | 29 | -- Sergey Bochenkov Tue, 20 Dec 2016 00:38:58 +0700 30 | 31 | ugh (0.1.10-10) unstable; urgency=medium 32 | 33 | * clear next_upstream value if user sets it from config 34 | 35 | -- Sergey Bochenkov Fri, 16 Dec 2016 17:38:58 +0700 36 | 37 | ugh (0.1.10-9) unstable; urgency=medium 38 | 39 | * fixed bug in ugh_subreq_set_method 40 | 41 | -- Sergey Bochenkov Wed, 30 Nov 2016 16:08:58 +0700 42 | 43 | ugh (0.1.10-8) unstable; urgency=medium 44 | 45 | * version bump 46 | 47 | -- Sergey Bochenkov Thu, 24 Nov 2016 19:18:58 +0700 48 | 49 | ugh (0.1.10-7) unstable; urgency=low 50 | 51 | * support 204 and 304 responses properly 52 | 53 | -- Sergey Bochenkov Mon, 08 Nov 2016 14:54:05 +0700 54 | 55 | ugh (0.1.10-6) unstable; urgency=low 56 | 57 | * support failover for close-after-body responses 58 | 59 | -- Sergey Bochenkov Mon, 24 Oct 2016 17:54:05 +0700 60 | 61 | ugh (0.1.10-5) unstable; urgency=low 62 | 63 | * always write correct content-length for POST subrequests 64 | 65 | -- Sergey Bochenkov Mon, 16 May 2016 15:23:05 +0700 66 | 67 | ugh (0.1.10-4) unstable; urgency=low 68 | 69 | * fixed max_fails / fail_timeout logic 70 | 71 | -- bachan Mon, 16 Oct 2015 10:57:12 +0700 72 | 73 | ugh (0.1.10-3) unstable; urgency=low 74 | 75 | * install modules into lib 76 | 77 | -- bachan Mon, 05 Oct 2015 10:57:12 +0700 78 | 79 | ugh (0.1.10-2) unstable; urgency=low 80 | 81 | * fixed max_fails feature 82 | 83 | -- bachan Thu, 01 Oct 2015 13:46:30 +0700 84 | 85 | ugh (0.1.10-1) unstable; urgency=low 86 | 87 | * support max_fails / fail_timeout (meaning is different from nginx) 88 | 89 | -- bachan Tue, 22 Sep 2015 12:38:02 +0700 90 | 91 | ugh (0.1.9-1) unstable; urgency=low 92 | 93 | * fixed resolver cache 94 | * added cookie_va function 95 | 96 | -- bachan Thu, 06 Aug 2015 19:14:18 +0700 97 | 98 | ugh (0.1.8-1) unstable; urgency=low 99 | 100 | * added connection_time field 101 | 102 | -- bachan Mon, 15 Dec 2014 14:09:29 +0700 103 | 104 | ugh (0.1.7-5) unstable; urgency=high 105 | 106 | * reinit http parser state during failover 107 | 108 | -- bachan Thu, 4 Sep 2014 18:10:00 +0700 109 | 110 | ugh (0.1.7-4) unstable; urgency=high 111 | 112 | * fixed failover in case of connections prematurely closed by upstream 113 | 114 | -- bachan Tue, 19 Aug 2014 14:44:00 +0700 115 | 116 | ugh (0.1.7-3) unstable; urgency=high 117 | 118 | * fixed default next_upstream setting 119 | 120 | -- bachan Mon, 14 Jul 2014 17:21:00 +0700 121 | 122 | ugh (0.1.7-2) unstable; urgency=low 123 | 124 | * renamed some macros to avoid compilation errors with some libraries 125 | 126 | -- bachan Wed, 7 Jul 2014 12:50:27 +0700 127 | 128 | ugh (0.1.7-1) unstable; urgency=low 129 | 130 | * add separate ft_type for connect timeout 131 | 132 | -- bachan Wed, 19 Mar 2014 14:50:27 +0700 133 | 134 | ugh (0.1.6-4) unstable; urgency=low 135 | 136 | * fix pid-file race condition 137 | 138 | -- bachan Wed, 19 Mar 2014 11:50:27 +0700 139 | 140 | ugh (0.1.6-3) unstable; urgency=low 141 | 142 | * log string about connect timeout 143 | 144 | -- bachan Mon, 17 Mar 2014 22:13:27 +0700 145 | 146 | ugh (0.1.6-2) unstable; urgency=low 147 | 148 | * log string about connect timeout 149 | 150 | -- bachan Mon, 17 Mar 2014 22:03:27 +0700 151 | 152 | ugh (0.1.6-1) unstable; urgency=low 153 | 154 | * connection timeouts support 155 | 156 | -- bachan Mon, 17 Mar 2014 18:03:27 +0700 157 | 158 | ugh (0.1.5-1) unstable; urgency=low 159 | 160 | * multiple set-cookie header support 161 | 162 | -- bachan Wed, 13 Nov 2013 16:30:20 +0700 163 | 164 | ugh (0.1.4-1) unstable; urgency=low 165 | 166 | * made UGH_TEMPLATE_MAX_CHUNKS bigger 167 | 168 | -- bachan Mon, 12 Oct 2013 14:30:20 +0700 169 | 170 | ugh (0.1.3-1) unstable; urgency=low 171 | 172 | * fixed possibility of buffer overflow in subrequest 173 | 174 | -- bachan Thu, 03 Oct 2013 17:30:20 +0700 175 | 176 | ugh (0.1.2-2) unstable; urgency=low 177 | 178 | * aux_urlenc, aux_urldec 179 | 180 | -- bachan Fri, 28 Aug 2013 18:40:20 +0700 181 | 182 | ugh (0.1.2-1) unstable; urgency=low 183 | 184 | * choose random directive in upstream 185 | 186 | -- bachan Fri, 23 Aug 2013 17:53:20 +0700 187 | 188 | ugh (0.1.1-7) unstable; urgency=low 189 | 190 | * ugh_client_setvar_va 191 | 192 | -- bachan Fri, 23 Aug 2013 17:02:20 +0700 193 | 194 | ugh (0.1.1-6) unstable; urgency=low 195 | 196 | * ugh_make_command_double 197 | 198 | -- bachan Fri, 23 Aug 2013 15:02:20 +0700 199 | 200 | ugh (0.1.1-5) unstable; urgency=low 201 | 202 | * ugh_client_setvar_cp 203 | 204 | -- bachan Tue, 21 Aug 2013 14:02:20 +0700 205 | 206 | ugh (0.1.1-4) unstable; urgency=low 207 | 208 | * bugfix in resolv_conf parser 209 | 210 | -- bachan Tue, 20 Aug 2013 18:20:20 +0700 211 | 212 | ugh (0.1.1-3) unstable; urgency=low 213 | 214 | * fix clearing full timeout when going to the next upstream 215 | 216 | -- bachan Tue, 30 Jul 2013 20:57:20 +0700 217 | 218 | ugh (0.1.1-2) unstable; urgency=low 219 | 220 | * recv_timeout fixes, version fix 221 | 222 | -- bachan Mon, 29 Jul 2013 18:20:20 +0700 223 | 224 | ugh (0.0.10-1) unstable; urgency=low 225 | 226 | * changed module API 227 | 228 | -- bachan Mon, 29 Jul 2013 17:33:20 +0700 229 | 230 | ugh (0.0.9-1) unstable; urgency=low 231 | 232 | * lots of fixes around channel stuff 233 | 234 | -- bachan Tue, 23 Jul 2013 11:36:28 +0700 235 | 236 | ugh (0.0.7-2) unstable; urgency=low 237 | 238 | * don't remove debug symbols 239 | 240 | -- bachan Wed, 10 Jul 2013 18:26:24 +0700 241 | 242 | ugh (0.0.7-1) unstable; urgency=low 243 | 244 | * support http/1.0 close after body responses 245 | 246 | -- bachan Wed, 10 Jul 2013 18:11:10 +0700 247 | 248 | ugh (0.0.6-6) unstable; urgency=low 249 | 250 | * customizable header names in channel.c 251 | 252 | -- bachan Wed, 03 Jul 2013 15:39:34 +0700 253 | 254 | ugh (0.0.6-5) unstable; urgency=low 255 | 256 | * milliseconds in log 257 | * destination port in log 258 | * bugfix in cookie parser 259 | * changed output format in resolver test 260 | 261 | -- bachan Tue, 02 Jul 2013 14:33:06 +0700 262 | 263 | ugh (0.0.6-4) unstable; urgency=low 264 | 265 | * fixed infinite loop in resolver 266 | 267 | -- bachan Wed, 12 Jun 2013 17:24:57 +0700 268 | 269 | ugh (0.0.6-2) unstable; urgency=low 270 | 271 | * push/relay bugfixes 272 | 273 | -- bachan Mon, 10 Jun 2013 19:15:19 +0700 274 | 275 | ugh (0.0.6-1) unstable; urgency=low 276 | 277 | * push/relay protocol 278 | 279 | -- bachan Thu, 06 Jun 2013 18:18:49 +0700 280 | 281 | ugh (0.0.5-5) unstable; urgency=low 282 | 283 | * show correct error message in case of async connect error 284 | 285 | -- bachan Tue, 12 Mar 2013 18:29:23 +0700 286 | 287 | ugh (0.0.5-4) unstable; urgency=low 288 | 289 | * C++ type conversion fix 290 | 291 | -- bachan Tue, 19 Feb 2013 18:22:10 +0700 292 | 293 | ugh (0.0.5-3) unstable; urgency=low 294 | 295 | * ugh_make_command_sint 296 | 297 | -- bachan Tue, 29 Jan 2013 21:07:20 +0700 298 | 299 | ugh (0.0.5-2) unstable; urgency=low 300 | 301 | * Calculate response_time correctly in case of resolver errors 302 | 303 | -- bachan Thu, 24 Jan 2013 15:35:35 +0700 304 | 305 | ugh (0.0.5-1) unstable; urgency=low 306 | 307 | * resolver_cache 308 | 309 | -- bachan Tue, 08 Jan 2013 11:49:58 +0700 310 | 311 | ugh (0.0.4-13) unstable; urgency=low 312 | 313 | * support full operation timeouts 314 | 315 | -- bachan Thu, 03 Jan 2013 17:55:43 +0700 316 | 317 | ugh (0.0.4-12) unstable; urgency=low 318 | 319 | * SUBREQ_BUF 320 | 321 | -- bachan Tue, 11 Dec 2012 19:35:26 +0700 322 | 323 | ugh (0.0.4-11) unstable; urgency=low 324 | 325 | * Fixed htons on upstream config load 326 | 327 | -- bachan Fri, 07 Dec 2012 05:54:38 +0700 328 | 329 | ugh (0.0.4-10) unstable; urgency=low 330 | 331 | * Added support for default values 332 | 333 | -- bachan Tue, 04 Dec 2012 18:29:32 +0700 334 | 335 | ugh (0.0.4-9) unstable; urgency=low 336 | 337 | * Removed garbage in logs in resolver 338 | 339 | -- bachan Thu, 22 Nov 2012 17:05:13 +0700 340 | 341 | ugh (0.0.4-8) unstable; urgency=low 342 | 343 | * Write localtime in logs 344 | 345 | -- bachan Mon, 19 Nov 2012 12:35:11 +0700 346 | 347 | ugh (0.0.4-7) unstable; urgency=low 348 | 349 | * set_size_slot 350 | 351 | -- bachan Wed, 31 Oct 2012 16:38:56 +0700 352 | 353 | ugh (0.0.4-6) unstable; urgency=high 354 | 355 | * Fixed resolver bug 356 | 357 | -- bachan Wed, 11 Oct 2012 18:21:00 +0700 358 | 359 | ugh (0.0.4-5) unstable; urgency=high 360 | 361 | * Access log 362 | 363 | -- bachan Wed, 12 Sep 2012 11:12:00 +0700 364 | 365 | ugh (0.0.4-4) unstable; urgency=high 366 | 367 | * Fixed chunked POST body bug 368 | 369 | -- bachan Thu, 10 Aug 2012 18:57:00 +0700 370 | 371 | ugh (0.0.4-3) unstable; urgency=high 372 | 373 | * Increased UGH_HDRBUF size 374 | 375 | -- bachan Thu, 7 Aug 2012 20:10:00 +0700 376 | 377 | ugh (0.0.4-2) unstable; urgency=high 378 | 379 | * Remove Content-Length header if original request was POST but we're 380 | subrequesting GET 381 | 382 | -- bachan Thu, 2 Aug 2012 18:20:00 +0700 383 | 384 | ugh (0.0.4-1) unstable; urgency=high 385 | 386 | * Fixed crash if client was freed earlier than subreq 387 | * Added README 388 | 389 | -- bachan Thu, 7 Jul 2012 20:49:00 +0700 390 | 391 | ugh (0.0.3-2) unstable; urgency=high 392 | 393 | * Fixed log levels 394 | * Stub for worker_threads 395 | 396 | -- bachan Mon, 11 Jun 2012 18:43:00 +0700 397 | 398 | ugh (0.0.3-1) unstable; urgency=high 399 | 400 | * Added support for POST requests 401 | 402 | -- bachan Wed, 23 May 2012 20:11:00 +0700 403 | 404 | ugh (0.0.2-7) unstable; urgency=high 405 | 406 | * Fixed short chunked responses crash 407 | * Fixed content-length check 408 | 409 | -- bachan Mon, 21 May 2012 13:36:00 +0700 410 | 411 | ugh (0.0.2-6) unstable; urgency=high 412 | 413 | * Logging socket errors 414 | 415 | -- bachan Tue, 18 May 2012 18:20:00 +0700 416 | 417 | ugh (0.0.2-5) unstable; urgency=high 418 | 419 | * Added ugh_subreq_get_host() 420 | * Added ugh_subreq_get_port() 421 | * Fixed size of UGH_HDRBUF buffer 422 | 423 | -- bachan Tue, 18 May 2012 16:03:00 +0700 424 | 425 | ugh (0.0.2-4) unstable; urgency=high 426 | 427 | * Fixed ugh_subreq_get_upstream_curr() in case of one server in upstream 428 | 429 | -- bachan Tue, 15 May 2012 19:33:00 +0700 430 | 431 | ugh (0.0.2-3) unstable; urgency=high 432 | 433 | * REALLY fixed wrong name of ugh_subreq_get_upstream_curr() implementation 434 | 435 | -- bachan Tue, 15 May 2012 18:39:00 +0700 436 | 437 | ugh (0.0.2-2) unstable; urgency=high 438 | 439 | * Fixed wrong name of ugh_subreq_get_upstream_curr() implementation 440 | 441 | -- bachan Tue, 15 May 2012 18:13:00 +0700 442 | 443 | ugh (0.0.2-1) unstable; urgency=high 444 | 445 | * Added chunked transfer encoding support 446 | * Added ugh_subreq_get_upstream_curr() function 447 | 448 | -- bachan Tue, 15 May 2012 17:22:00 +0700 449 | 450 | ugh (0.0.1-6) unstable; urgency=low 451 | 452 | * Added error handling on startup. 453 | 454 | -- bachan Wed, 9 May 2012 19:47:00 +0700 455 | 456 | ugh (0.0.1-5) unstable; urgency=high 457 | 458 | * Fixed response_time bug. 459 | 460 | -- bachan Wed, 9 May 2012 18:23:00 +0700 461 | 462 | ugh (0.0.1-4) unstable; urgency=high 463 | 464 | * Added response_time, ft_type to ugh_subreq_t structure. 465 | 466 | -- bachan Wed, 9 May 2012 11:04:00 +0700 467 | 468 | ugh (0.0.1-3) unstable; urgency=high 469 | 470 | * Fixing directives count, now 1024 471 | 472 | -- bachan Thu, 26 Apr 2012 19:52:00 +0700 473 | 474 | ugh (0.0.1-2) unstable; urgency=low 475 | 476 | * Fixing upstram count, now 1024 477 | 478 | -- pianist Mon, 26 Apr 2012 13:26:55 +0700 479 | 480 | ugh (0.0.1-1) unstable; urgency=low 481 | 482 | * Initial release (Closes: #nnnn) 483 | 484 | -- pianist Mon, 26 Mar 2012 20:44:55 +0700 485 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: ugh 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: bachan 5 | Build-Depends: debhelper (>= 7.0.50~), cmake 6 | Standards-Version: 3.8.4 7 | Homepage: https://github.com/bachan/ugh 8 | #Vcs-Git: git://git.debian.org/collab-maint/ugh.git 9 | #Vcs-Browser: http://git.debian.org/?p=collab-maint/ugh.git;a=summary 10 | 11 | Package: ugh 12 | Architecture: any 13 | Depends: ${shlibs:Depends}, ${misc:Depends} 14 | Description: ugh http server 15 | ugh http server (single-threaded with coroutines) 16 | 17 | Package: ugh-dev 18 | Architecture: any 19 | Depends: ugh ${shlibs:Depends}, ${misc:Depends} 20 | Description: ugh http server dev headers 21 | ugh server headers for module development 22 | 23 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This work was packaged for Debian by: 2 | 3 | Sergey Bochenkov on Mon, 26 Mar 2012 20:44:55 +0700 4 | 5 | It was downloaded from: 6 | 7 | https://github.com/bachan/ugh 8 | 9 | Upstream Author(s): 10 | 11 | Sergey Bochenkov 12 | 13 | Copyright: 14 | 15 | Copyright (C) 2011-2013 Sergey Bochenkov 16 | 17 | License: 18 | 19 | AS-IS 20 | 21 | The Debian packaging is: 22 | 23 | Copyright (C) 2011-2013 Sergey Bochenkov 24 | 25 | # Please chose a license for your packaging work. If the program you package 26 | # uses a mainstream license, using the same license is the safest choice. 27 | # Please avoid to pick license terms that are more restrictive than the 28 | # packaged work, as it may make Debian's contributions unacceptable upstream. 29 | # If you just want it to be GPL version 3, leave the following lines in. 30 | 31 | and is licensed under the GPL version 3, 32 | see "/usr/share/common-licenses/GPL-3". 33 | 34 | # Please also look if there are files or directories which have a 35 | # different copyright/license attached and list them here. 36 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | %: 13 | dh $@ 14 | 15 | override_dh_strip: 16 | 17 | -------------------------------------------------------------------------------- /debian/ugh-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/ugh/* 2 | -------------------------------------------------------------------------------- /debian/ugh.install: -------------------------------------------------------------------------------- 1 | usr/bin/ugh 2 | usr/etc/ugh.cfg 3 | usr/lib*/ugh/libugh_example.so 4 | usr/etc/ugh_example/config.cfg 5 | 6 | -------------------------------------------------------------------------------- /tmp/coro.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_D 10 8 | 9 | coro_context ctx_main; 10 | coro_context ctx_func; 11 | 12 | int d; 13 | int done; 14 | 15 | void func(void *arg) 16 | { 17 | d = random() % MAX_D; 18 | 19 | printf("func: switching to main, d=%d, done=%d\n", d, done); 20 | coro_transfer(&ctx_func, &ctx_main); 21 | 22 | done = 1; 23 | 24 | printf("func: switching to main, d=%d, done=%d\n", d, done); 25 | coro_transfer(&ctx_func, &ctx_main); 26 | } 27 | 28 | int main(int argc, char **argv) 29 | { 30 | char stack [4096]; 31 | 32 | /* coro_create(&ctx_main, NULL, NULL, stack, 4096); */ 33 | coro_create(&ctx_func, func, NULL, stack, 4096); 34 | 35 | srandom(1337); 36 | d = random() % MAX_D; 37 | 38 | done = 0; 39 | 40 | for (;;) 41 | { 42 | time_t t = time(NULL); 43 | printf("main: time=%d, d=%d, done=%d\n", (int) t, d, done); 44 | 45 | if (0 != done) 46 | { 47 | printf("main: done\n"); 48 | break; 49 | } 50 | 51 | if (d == t % 10) 52 | { 53 | printf("main: switching to func, d=%d, done=%d\n", d, done); 54 | coro_transfer(&ctx_main, &ctx_func); 55 | } 56 | 57 | sleep(1); 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /tmp/coro_ucontext_native.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static ucontext_t uctx_main, uctx_func1, uctx_func2; 6 | 7 | #define handle_error(msg) \ 8 | do { perror(msg); exit(EXIT_FAILURE); } while (0) 9 | 10 | static void 11 | func1(void) 12 | { 13 | printf("func1: started\n"); 14 | printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n"); 15 | if (swapcontext(&uctx_func1, &uctx_func2) == -1) 16 | handle_error("swapcontext"); 17 | printf("func1: returning\n"); 18 | } 19 | 20 | static void 21 | func2(void) 22 | { 23 | printf("func2: started\n"); 24 | printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n"); 25 | if (swapcontext(&uctx_func2, &uctx_func1) == -1) 26 | handle_error("swapcontext"); 27 | printf("func2: returning\n"); 28 | } 29 | 30 | int 31 | main(int argc, char *argv[]) 32 | { 33 | char func1_stack[16384]; 34 | char func2_stack[16384]; 35 | 36 | if (getcontext(&uctx_func1) == -1) 37 | handle_error("getcontext"); 38 | uctx_func1.uc_stack.ss_sp = func1_stack; 39 | uctx_func1.uc_stack.ss_size = sizeof(func1_stack); 40 | uctx_func1.uc_link = &uctx_main; 41 | makecontext(&uctx_func1, func1, 0); 42 | 43 | if (getcontext(&uctx_func2) == -1) 44 | handle_error("getcontext"); 45 | uctx_func2.uc_stack.ss_sp = func2_stack; 46 | uctx_func2.uc_stack.ss_size = sizeof(func2_stack); 47 | /* Successor context is f1(), unless argc > 1 */ 48 | uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1; 49 | makecontext(&uctx_func2, func2, 0); 50 | 51 | printf("main: swapcontext(&uctx_main, &uctx_func2)\n"); 52 | if (swapcontext(&uctx_main, &uctx_func2) == -1) 53 | handle_error("swapcontext"); 54 | 55 | printf("main: exiting\n"); 56 | exit(EXIT_SUCCESS); 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /tmp/dns.3.1: -------------------------------------------------------------------------------- 1 | a9 a8 = qq.ident 2 | 81 80 = qq.flags 3 | 00 01 = qq.nqs 4 | 00 02 = qq.nan 5 | 00 02 = qq.nns 6 | 00 02 = qq.nar 7 | 8 | 09 61 64 76 61 63 74 69 6f 6e 02 72 75 00 = \9advaction\2ru\0 9 | 10 | 00 01 = qs.type 11 | 00 01 = qs.class 12 | 13 | c0 0c 14 | 00 01 = an.type 15 | 00 01 = an.class 16 | 00 00 01 03 = an.ttl 17 | 00 04 = an.len 18 | c1 6a 5f 91 = an 19 | 20 | c0 0c 21 | 00 01 22 | 00 01 23 | 00 00 01 03 24 | 00 04 25 | c1 6a 5f 90 26 | 27 | c0 0c 28 | 00 02 29 | 00 01 30 | 00 00 01 03 31 | 00 0d 32 | 03 6e 73 32 06 6f 6f 30 30 6f 6f c0 16 = \3ns2\6oo00oo.. (ns2.oo00oo.ru) 33 | 34 | c0 0c 35 | 00 02 36 | 00 01 37 | 00 00 01 03 38 | 00 06 39 | 03 6e 73 31 c0 4e = \3ns1.. (ns1.oo00oo.ru) 40 | 41 | c0 63 42 | 00 01 43 | 00 01 44 | 00 02 1c 2c 45 | 00 04 46 | 59 bc 6d 72 47 | 48 | c0 4a 49 | 00 01 50 | 00 01 51 | 00 02 04 c0 52 | 00 04 53 | 59 bc 6d 72 54 | 55 | 00 56 | 57 | -------------------------------------------------------------------------------- /tmp/dns.8.8: -------------------------------------------------------------------------------- 1 | a9 a8 = qq.ident 2 | 81 80 = qq.flags 3 | 00 01 = qq.nqs 4 | 00 02 = qq.nan 5 | 00 00 = qq.nns 6 | 00 00 = qq.nar 7 | 8 | 09 61 64 76 61 63 74 69 6f 6e 02 72 75 00 = \9advaction\2ru\0 9 | 10 | 00 01 = qs.type 11 | 00 01 = qs.class 12 | 13 | c0 0c 14 | 00 01 = an.type 15 | 00 01 = an.class 16 | 00 00 01 2b = an.ttl 17 | 00 04 = an.len 18 | c1 6a 5f 91 = an 19 | 20 | c0 0c 21 | 00 01 22 | 00 01 23 | 00 00 01 2b 24 | 00 04 25 | c1 6a 5f 90 26 | 27 | -------------------------------------------------------------------------------- /tmp/dyn/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "app.h" 4 | 5 | int func(int arg) 6 | { 7 | return arg; 8 | } 9 | 10 | int main(int argc, char **argv) 11 | { 12 | if (2 > argc) 13 | { 14 | fprintf(stderr, "Usage: %s /path/to/library.so\n", argv[0]); 15 | return -1; 16 | } 17 | 18 | void *main_handle = dlopen(NULL, RTLD_NOW); 19 | printf("main_handle = %p\n", main_handle); 20 | 21 | char *path = argv[1]; 22 | int flags = RTLD_NOW; 23 | 24 | void *handle = dlopen(path, flags); 25 | 26 | if (NULL == handle) 27 | { 28 | fprintf(stderr, "dlopen(%s, %d): %s\n", path, flags, dlerror()); 29 | return -1; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /tmp/dyn/app.h: -------------------------------------------------------------------------------- 1 | #ifndef __APP_H__ 2 | #define __APP_H__ 3 | 4 | int func(int arg); 5 | 6 | #endif /* __APP_H__ */ 7 | -------------------------------------------------------------------------------- /tmp/dyn/lib.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | int lib_handle(int arg) 4 | { 5 | return func(arg); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tmp/gai.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | * int getaddrinfo(const char *node, const char *service, 10 | * const struct addrinfo *hints, 11 | * struct addrinfo **res); 12 | * 13 | * void freeaddrinfo(struct addrinfo *res); 14 | * 15 | * const char *gai_strerror(int errcode); 16 | * 17 | * struct addrinfo { 18 | * int ai_flags; 19 | * int ai_family; 20 | * int ai_socktype; 21 | * int ai_protocol; 22 | * size_t ai_addrlen; 23 | * struct sockaddr *ai_addr; 24 | * char *ai_canonname; 25 | * struct addrinfo *ai_next; 26 | * }; 27 | */ 28 | 29 | int main(int argc, char **argv) 30 | { 31 | int rc; 32 | struct addrinfo *res; 33 | 34 | rc = getaddrinfo(argv[1], argv[2], NULL, &res); 35 | 36 | if (0 != rc) 37 | { 38 | fprintf(stderr, "gai error: %d: %s\n", rc, gai_strerror(rc)); 39 | freeaddrinfo(res); 40 | return -1; 41 | } 42 | 43 | for (; res->ai_next; res = res->ai_next) 44 | { 45 | printf("ai_flags = %d\n", res->ai_flags); 46 | printf("ai_family = %d\n", res->ai_family); 47 | printf("ai_socktype = %d\n", res->ai_socktype); 48 | printf("ai_protocol = %d\n", res->ai_protocol); 49 | printf("ai_addrlen = %u\n", (unsigned) res->ai_addrlen); 50 | printf("ai_addr = %s\n", inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr)); 51 | printf("ai_canonname = %s\n", res->ai_canonname); 52 | printf("\n"); 53 | } 54 | 55 | freeaddrinfo(res); 56 | 57 | return 0; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tmp/inet_addr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char **argv) 7 | { 8 | in_addr_t a = inet_addr(argv[1]); 9 | 10 | unsigned char oct1 = (a) & 0xff; 11 | unsigned char oct2 = (a >> 8) & 0xff; 12 | unsigned char oct3 = (a >> 16) & 0xff; 13 | unsigned char oct4 = (a >> 24) & 0xff; 14 | 15 | printf("%u.%u.%u.%u\n", oct1, oct2, oct3, oct4); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tmp/test_pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../ugh/aux/memory.h" 3 | 4 | int main() 5 | { 6 | aux_pool_t *pool; 7 | 8 | pool = aux_pool_init(0); 9 | 10 | aux_pool_malloc(pool, 1024 * 512); 11 | aux_pool_malloc(pool, 1024); 12 | /* aux_pool_malloc(pool, 1024); */ 13 | /* aux_pool_malloc(pool, 1024); */ 14 | /* aux_pool_malloc(pool, 1024); */ 15 | /* aux_pool_malloc(pool, 1024); */ 16 | /* aux_pool_malloc(pool, 1024); */ 17 | /* aux_pool_malloc(pool, 1024); */ 18 | /* aux_pool_malloc(pool, 1024); */ 19 | /* aux_pool_malloc(pool, 1024); */ 20 | /* aux_pool_malloc(pool, 1024); */ 21 | /* aux_pool_malloc(pool, 1024); */ 22 | /* aux_pool_malloc(pool, 1024); */ 23 | /* aux_pool_malloc(pool, 1024); */ 24 | /* aux_pool_malloc(pool, 1024); */ 25 | /* aux_pool_malloc(pool, 1024); */ 26 | /* aux_pool_malloc(pool, 1024); */ 27 | /* aux_pool_malloc(pool, 1024); */ 28 | 29 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 30 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 31 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 32 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 33 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 34 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 35 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 36 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 37 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 38 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 39 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 40 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 41 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 42 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 43 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 44 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 45 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 46 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 47 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 48 | /* printf("%p\n", aux_pool_malloc(pool, 32333)); */ 49 | /* printf("%p\n", aux_pool_malloc(pool, 1024)); */ 50 | 51 | aux_pool_free(pool); 52 | 53 | return 0; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /tmp/test_resolver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../ugh/aux/resolver.h" 7 | 8 | int main(int argc, char **argv) 9 | { 10 | if (3 > argc) 11 | { 12 | fprintf(stderr, "Usage: %s dns_server host [...]\n", argv[0]); 13 | return -1; 14 | } 15 | 16 | char q [4096]; 17 | char a [4096]; 18 | 19 | int sd = socket(AF_INET, SOCK_DGRAM, 0); 20 | if (0 > sd) return -1; 21 | 22 | struct sockaddr_in addr; 23 | socklen_t addrlen = sizeof(addr); 24 | 25 | addr.sin_family = AF_INET; 26 | addr.sin_addr.s_addr = inet_addr(argv[1]); 27 | addr.sin_port = htons(53); 28 | 29 | int rc = connect(sd, (struct sockaddr *) &addr, addrlen); 30 | if (0 > rc) return -1; 31 | 32 | int i; 33 | 34 | for (i = 2; i < argc; ++i) 35 | { 36 | struct timeval tv1, tv2; 37 | gettimeofday(&tv1, NULL); 38 | 39 | int qlen = create_name_query(q, argv[i], strlen(argv[i])); 40 | if (0 > qlen) continue; 41 | 42 | rc = send(sd, q, qlen, 0); 43 | /* printf("sent %d bytes\n", rc); */ 44 | 45 | rc = recv(sd, a, 4096, 0); 46 | /* printf("recv %d bytes\n", rc); */ 47 | 48 | /* debug */ 49 | /* write(1, a, rc); */ 50 | 51 | in_addr_t addrs [8]; 52 | int naddrs = 8; 53 | int j; 54 | 55 | strt name; 56 | char name_data [1024]; 57 | name.data = name_data; 58 | 59 | naddrs = process_response(a, rc, addrs, naddrs, &name); 60 | 61 | gettimeofday(&tv2, NULL); 62 | 63 | printf("%s (name=%.*s): ", argv[i], (int) name.size, name.data); 64 | 65 | for (j = 0; j < naddrs; ++j) 66 | { 67 | struct in_addr res_addr = { addrs[j] }; 68 | printf("%s ", inet_ntoa(res_addr)); 69 | } 70 | 71 | unsigned diff = (tv2.tv_sec - tv1.tv_sec) * 1000000 + tv2.tv_usec - tv1.tv_usec; 72 | printf("%u.%06us (%u.%06u-%u.%06u)\n", diff / 1000000, diff % 1000000, (unsigned) tv1.tv_sec, (unsigned) tv1.tv_usec, (unsigned) tv2.tv_sec, (unsigned) tv2.tv_usec); 73 | } 74 | 75 | close(sd); 76 | 77 | return 0; 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /tmp/yandex.ru.txt: -------------------------------------------------------------------------------- 1 | 2 | 00 04 = qq.ident 3 | 81 80 = qq.flags 4 | 00 01 = qq.nqs 5 | 00 06 = qq.nan 6 | 00 04 = qq.nns 7 | 00 05 = qq.nar 8 | 9 | 06 79 61 6e 64 65 78 02 72 75 00 = \6yandex\2ru\0 10 | 11 | 00 01 = qs.type 12 | 00 01 = qs.class 13 | 14 | c0 0c = ... 15 | 00 01 = an.type 16 | 00 01 = an.class 17 | 00 00 0b 10 = an.ttl 18 | 00 04 = an.len 19 | 57 fa fb 0b 20 | 21 | c0 0c 22 | 00 01 23 | 00 01 24 | 00 00 0b 10 25 | 00 04 26 | 5d 9e 86 0b 27 | 28 | c0 0c 29 | 00 01 30 | 00 01 31 | 00 00 0b 10 32 | 00 04 33 | d5 b4 cc 0b 34 | 35 | c0 0c 36 | 00 01 37 | 00 01 38 | 00 00 0b 10 39 | 00 04 40 | d5 b4 cc d3 41 | 42 | c0 0c 43 | 00 01 44 | 00 01 45 | 00 00 0b 10 46 | 00 04 47 | 4d 58 15 0b 48 | 49 | c0 0c 50 | 00 01 51 | 00 01 52 | 00 00 0b 10 53 | 00 04 54 | 57 fa fa 0b 55 | 56 | c0 0c 57 | 00 02 58 | 00 01 59 | 00 04 d2 7f 60 | 00 06 61 | 03 6e 73 32 c0 0c 62 | 63 | c0 0c 64 | 00 02 65 | 00 01 66 | 00 04 d2 7f 67 | 00 06 68 | 03 6e 73 34 c0 0c 69 | 70 | c0 0c 71 | 00 02 72 | 00 01 73 | 00 04 d2 7f 74 | 00 06 75 | 03 6e 73 35 c0 0c 76 | 77 | c0 0c 78 | 00 02 79 | 00 01 80 | 00 04 d2 7f 81 | 00 06 82 | 03 6e 73 31 c0 0c 83 | 84 | c0 bd 85 | 00 01 86 | 00 01 87 | 00 04 d7 46 88 | 00 04 89 | d5 b4 c1 01 90 | 91 | c0 bd 92 | 00 1c 93 | 00 01 94 | 00 00 0b 10 95 | 00 10 96 | 2a 02 06 b8 00 00 00 00 00 00 00 00 00 00 00 01 97 | 98 | c0 87 99 | 00 01 100 | 00 01 101 | 00 04 d7 46 102 | 00 04 103 | d5 b4 c7 22 104 | 105 | c0 99 106 | 00 01 107 | 00 01 108 | 00 04 d7 46 109 | 00 04 110 | 4d 58 13 3c 111 | 112 | c0 ab 113 | 00 01 114 | 00 01 115 | 00 04 d7 46 116 | 00 04 117 | d5 b4 cc 01 118 | 119 | -------------------------------------------------------------------------------- /ugh.spec: -------------------------------------------------------------------------------- 1 | Summary: UGH http server 2 | Name: ugh 3 | Version: 0.1.15 4 | Release: 1%{?dist} 5 | Group: Networking/Daemons 6 | License: GPL 7 | Source: ugh-%{version}.tar.gz 8 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 9 | BuildRequires: Judy-devel libev-devel cmake gcc-c++ 10 | 11 | %package devel 12 | Summary: Header files and development documentation for %{name} 13 | Group: Development/Libraries 14 | Requires: %{name} = %{version}-%{release} 15 | 16 | %description 17 | UGH http server 18 | 19 | %description devel 20 | UGH header files. 21 | 22 | This package contains the header files, static libraries and development 23 | documentation for %{name}. If you like to develop modules for %{name}, 24 | you will need to install %{name}-devel. 25 | 26 | %prep 27 | %setup -q -n %{name}-%{version} 28 | 29 | %build 30 | %cmake . 31 | make %{?_smp_mflags} 32 | 33 | %install 34 | rm -rf %{buildroot} 35 | mkdir %{buildroot} 36 | make install DESTDIR=%{buildroot} 37 | 38 | %clean 39 | rm -rf %{buildroot} 40 | 41 | %files 42 | %defattr(-,root,root,-) 43 | %{_bindir}/ugh 44 | %{_bindir}/ugh_test_resolver 45 | %{_prefix}/etc/ugh.cfg 46 | %{_prefix}/etc/ugh_example/config.cfg 47 | %{_libdir}/ugh/libugh_example.so 48 | 49 | %files devel 50 | %defattr(-,root,root,-) 51 | %{_includedir}/ugh/autoconf.h 52 | %{_includedir}/ugh/aux/buffer.h 53 | %{_includedir}/ugh/aux/config.h 54 | %{_includedir}/ugh/aux/daemon.h 55 | %{_includedir}/ugh/aux/gmtime.h 56 | %{_includedir}/ugh/aux/hashes.h 57 | %{_includedir}/ugh/aux/logger.h 58 | %{_includedir}/ugh/aux/memory.h 59 | %{_includedir}/ugh/aux/random.h 60 | %{_includedir}/ugh/aux/resolver.h 61 | %{_includedir}/ugh/aux/socket.h 62 | %{_includedir}/ugh/aux/string.h 63 | %{_includedir}/ugh/aux/system.h 64 | %{_includedir}/ugh/config.h 65 | %{_includedir}/ugh/coro/coro.h 66 | %{_includedir}/ugh/coro_ucontext/coro_ucontext.h 67 | %{_includedir}/ugh/module.h 68 | %{_includedir}/ugh/resolver.h 69 | %{_includedir}/ugh/ugh.h 70 | 71 | %changelog 72 | 73 | * Mon Sep 13 2019 Ruslan Osmanov - 0.1.15-1 74 | + Added support for HTTP statuses 417 and 451 75 | 76 | * Mon Jan 05 2015 Alexander Pankov - 0.1.8-1 77 | + First RPM build 78 | 79 | -------------------------------------------------------------------------------- /ugh/autoconf.h.in: -------------------------------------------------------------------------------- 1 | #ifndef __UGH_AUTOCONF_H__ 2 | #define __UGH_AUTOCONF_H__ 3 | 4 | #define UGH_VERSION "@UGH_VERSION@" 5 | 6 | #define UGH_PREFIX "@CMAKE_INSTALL_PREFIX@" 7 | #define UGH_MODULE_PREFIX UGH_PREFIX"/lib/ugh/libugh_" 8 | #define UGH_MODULE_SUFFIX ".so" 9 | 10 | #endif /* __UGH_AUTOCONF_H__ */ 11 | -------------------------------------------------------------------------------- /ugh/aux/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "buffer.h" 4 | 5 | int aux_buffer_init(aux_buffer_t *b, aux_pool_t *pool, size_t size) 6 | { 7 | b->data = aux_pool_nalloc(pool, size); 8 | if (NULL == b->data) return -1; 9 | 10 | b->size = size; 11 | b->rpos = 0; 12 | b->wpos = 0; 13 | 14 | return 0; 15 | } 16 | 17 | int aux_buffer_memcpy(aux_buffer_t *b, aux_pool_t *pool, char *data, size_t size) 18 | { 19 | if (b->size < b->wpos + size) 20 | { 21 | char *old_data = b->data; 22 | 23 | b->size = b->size < size ? b->size + size : b->size * 2; 24 | b->data = aux_pool_nalloc(pool, b->size); 25 | if (NULL == b->data) return -1; 26 | 27 | memcpy(b->data, old_data, b->wpos); 28 | } 29 | 30 | memcpy(b->data + b->wpos, data, size); 31 | b->wpos += size; 32 | 33 | return 0; 34 | } 35 | 36 | int aux_buffer_printf(aux_buffer_t *b, aux_pool_t *pool, const char *fmt, ...) 37 | { 38 | va_list ap; 39 | 40 | va_start(ap, fmt); 41 | size_t size = vsnprintf(NULL, 0, fmt, ap); 42 | va_end(ap); 43 | 44 | if (b->size < b->wpos + size + 1) /* we need +1 cause vsnprintf writes terminating null */ 45 | { 46 | char *old_data = b->data; 47 | 48 | b->size = b->size < size + 1 ? b->size + size + 1 : b->size * 2; 49 | b->data = aux_pool_nalloc(pool, b->size); 50 | if (NULL == b->data) return -1; 51 | 52 | memcpy(b->data, old_data, b->wpos); 53 | } 54 | 55 | va_start(ap, fmt); 56 | b->wpos += vsnprintf(b->data + b->wpos, b->size - b->wpos, fmt, ap); 57 | va_end(ap); 58 | 59 | return 0; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /ugh/aux/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_BUFFER_H__ 2 | #define __AUX_BUFFER_H__ 3 | 4 | #include "config.h" 5 | #include "memory.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef struct aux_buffer 12 | aux_buffer_t; 13 | 14 | struct aux_buffer 15 | { 16 | size_t size; 17 | char *data; 18 | size_t rpos; 19 | size_t wpos; 20 | }; 21 | 22 | int aux_buffer_init(aux_buffer_t *b, aux_pool_t *pool, size_t size); 23 | #define aux_buffer_strcpy(b, pool, data) aux_buffer_memcpy(b, pool, data, strlen(data)) 24 | int aux_buffer_memcpy(aux_buffer_t *b, aux_pool_t *pool, char *data, size_t size); 25 | int aux_buffer_printf(aux_buffer_t *b, aux_pool_t *pool, const char *fmt, ...) AUX_FORMAT(printf, 3, 4); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif /* __AUX_BUFFER_H__ */ 32 | -------------------------------------------------------------------------------- /ugh/aux/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_CONFIG_H__ 2 | #define __AUX_CONFIG_H__ 3 | 4 | #if __GNUC__ >= 3 5 | #define AUX_FORMAT(n,f,e) __attribute__ ((format (n, f, e))) 6 | #else 7 | #define AUX_FORMAT(n,f,e) /* void */ 8 | #endif /* */ 9 | 10 | #endif /* __AUX_CONFIG_H__ */ 11 | -------------------------------------------------------------------------------- /ugh/aux/daemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "string.h" 9 | #include "daemon.h" 10 | #include "logger.h" 11 | #include "system.h" 12 | 13 | static struct option aux_getopt_opts [] = 14 | { 15 | { "config" , 1, NULL, 'c' }, 16 | { "pid" , 1, NULL, 'p' }, 17 | { "daemon" , 0, NULL, 'd' }, 18 | { "no-daemon" , 0, NULL, 'D' }, 19 | { NULL , 0, NULL, 0 }, 20 | }; 21 | 22 | #define AUX_GETOPT_OPTS "c:p:dD" /* don't forget to change this after changing the array above */ 23 | 24 | int aux_getopt_usage(int argc, char** argv) 25 | { 26 | std_msg( 27 | "Usage: \n" 28 | " %s \n" 29 | " \n" 30 | "Options: \n" 31 | " -c, --config : custom config \n" 32 | " -p, --pid : custom pid \n" 33 | " -d, --daemon : detach \n" 34 | " -D, --no-daemon : do not detach \n" 35 | " " 36 | , argv[0]); 37 | 38 | return 0; 39 | } 40 | 41 | int aux_getopt_parse(int argc, char** argv, aux_getopt_t* opt) 42 | { 43 | int c; 44 | 45 | aux_clrptr(opt); 46 | 47 | while (-1 != (c = getopt_long(argc, argv, AUX_GETOPT_OPTS, 48 | aux_getopt_opts, NULL))) 49 | { 50 | switch (c) 51 | { 52 | case 'c': opt->config = optarg; break; 53 | case 'p': opt->pid = optarg; break; 54 | case 'd': opt->daemon = 1; break; 55 | case 'D': opt->daemon = 0; break; 56 | default : return -1; 57 | } 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | int aux_daemon_fork() 64 | { 65 | int fd; 66 | 67 | switch (fork()) 68 | { 69 | case -1: return -1; 70 | case 0: break; 71 | default: exit(EXIT_SUCCESS); 72 | } 73 | 74 | if (0 > (int) setsid()) 75 | { 76 | return -1; 77 | } 78 | 79 | umask(0); 80 | 81 | if (0 > (fd = open("/dev/null", O_RDWR))) 82 | { 83 | return -1; 84 | } 85 | 86 | if (0 > dup2(fd, STDIN_FILENO)) 87 | { 88 | return -1; 89 | } 90 | 91 | if (0 > dup2(fd, STDOUT_FILENO)) 92 | { 93 | return -1; 94 | } 95 | 96 | if (fd > STDERR_FILENO && 0 > close(fd)) 97 | { 98 | return -1; 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | int aux_daemon_load(aux_getopt_t* opt) 105 | { 106 | if (0 > aux_signal_init()) 107 | { 108 | return -1; 109 | } 110 | 111 | if (opt->daemon && 0 > aux_daemon_fork()) 112 | { 113 | return -1; 114 | } 115 | 116 | if (opt->pid && 0 > aux_mkpidf(opt->pid)) 117 | { 118 | return -1; 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | int aux_daemon_stop(aux_getopt_t* opt) 125 | { 126 | if (opt->pid && 0 > unlink(opt->pid)) 127 | { 128 | return -1; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | /* 135 | * SIGIO is equal to SIGPOLL, so it was removed from the list, SIGCLD is 136 | * SIGCHLD equivalent, so it was removed too. 137 | * 138 | * SIGPWR, SIGSTKFLT are not defined under FreeBSD 139 | * SIGPOLL is SIGIO under FreeBSD (?) 140 | * 141 | */ 142 | 143 | #define AUX_SIG_LIST(_,...) \ 144 | /* _(KILL , evanescen, ##__VA_ARGS__) */ \ 145 | /* _(STOP , evanescen, ##__VA_ARGS__) */ \ 146 | /* _(ILL , dangerous, ##__VA_ARGS__) */ \ 147 | /* _(ABRT , dangerous, ##__VA_ARGS__) */ \ 148 | /* _(FPE , dangerous, ##__VA_ARGS__) */ \ 149 | /* _(SEGV , dangerous, ##__VA_ARGS__) */ \ 150 | /* _(BUS , dangerous, ##__VA_ARGS__) */ \ 151 | /* _(IOT , dangerous, ##__VA_ARGS__) */ \ 152 | _(HUP , changecfg, ##__VA_ARGS__) \ 153 | _(INT , terminate, ##__VA_ARGS__) \ 154 | _(TERM , terminate, ##__VA_ARGS__) \ 155 | _(USR1 , rotatelog, ##__VA_ARGS__) \ 156 | _(USR2 , changebin, ##__VA_ARGS__) \ 157 | _(ALRM , ignoremsg, ##__VA_ARGS__) \ 158 | _(QUIT , ignoremsg, ##__VA_ARGS__) \ 159 | _(PIPE , ignoremsg, ##__VA_ARGS__) \ 160 | _(CHLD , ignoremsg, ##__VA_ARGS__) \ 161 | _(CONT , ignoremsg, ##__VA_ARGS__) \ 162 | _(TSTP , ignoremsg, ##__VA_ARGS__) \ 163 | _(TTIN , ignoremsg, ##__VA_ARGS__) \ 164 | _(TTOU , ignoremsg, ##__VA_ARGS__) \ 165 | /* _(POLL , ignoremsg, ##__VA_ARGS__) */ \ 166 | _(PROF , ignoremsg, ##__VA_ARGS__) \ 167 | _(SYS , ignoremsg, ##__VA_ARGS__) \ 168 | _(TRAP , ignoremsg, ##__VA_ARGS__) \ 169 | _(URG , ignoremsg, ##__VA_ARGS__) \ 170 | _(VTALRM , ignoremsg, ##__VA_ARGS__) \ 171 | _(XCPU , ignoremsg, ##__VA_ARGS__) \ 172 | _(XFSZ , ignoremsg, ##__VA_ARGS__) \ 173 | /* _(STKFLT , ignoremsg, ##__VA_ARGS__) */ \ 174 | /* _(IO , ignoremsg, ##__VA_ARGS__) */ \ 175 | /* _(CLD , ignoremsg, ##__VA_ARGS__) */ \ 176 | /* _(PWR , ignoremsg, ##__VA_ARGS__) */ \ 177 | _(WINCH , ignoremsg, ##__VA_ARGS__) \ 178 | /* _(UNUSED , ignoremsg, ##__VA_ARGS__) */ \ 179 | /* _(EMT , ignoremsg, ##__VA_ARGS__) */ 180 | 181 | #define AUX_SIG_CASE(name,hand) case SIG##name: log_notice(QUOTES_NAME(SIG##name)"("QUOTES_DATA(SIG##name)"): "#hand); AUX_SIG_CASE_##hand; break; 182 | #define AUX_SIG_CASE_DEFAULT(n) default: log_notice("DEFAULT CASE in aux_signal_handle(%d) - sigaction is not defined", n); break; 183 | #define AUX_SIG_INIT(name,hand,glob_hand) if (SIG_ERR == signal((SIG##name), (glob_hand))) return -1; 184 | #define AUX_SIG_MASK(name,hand,sset,mask) if (((mask) & (1 << (SIG##name))) && 0 > sigaddset(&(sset),(SIG##name))) return -1; 185 | #define AUX_SIG_CASE_terminate aux_terminate = 1 186 | #define AUX_SIG_CASE_changecfg aux_changecfg = 1 187 | #define AUX_SIG_CASE_rotatelog aux_rotatelog = 1 188 | #define AUX_SIG_CASE_changebin aux_changebin = 1 189 | #define AUX_SIG_CASE_ignoremsg /* void */ 190 | 191 | volatile int aux_terminate = 0; 192 | volatile int aux_changecfg = 0; 193 | volatile int aux_rotatelog = 0; 194 | volatile int aux_changebin = 0; 195 | 196 | static 197 | void aux_signal_handle(int sig) 198 | { 199 | switch (sig) 200 | { 201 | AUX_SIG_LIST(AUX_SIG_CASE) 202 | AUX_SIG_CASE_DEFAULT(sig) 203 | } 204 | } 205 | 206 | int aux_signal_init() 207 | { 208 | AUX_SIG_LIST(AUX_SIG_INIT, aux_signal_handle) 209 | return 0; 210 | } 211 | 212 | int aux_signal_mask(int how, unsigned mask) 213 | { 214 | sigset_t sset; 215 | sigemptyset(&sset); 216 | AUX_SIG_LIST(AUX_SIG_MASK, sset, mask); 217 | return sigprocmask(how, &sset, NULL); /* TODO check if this is okay with threads (nowdays) or use pthread_sigmask */ 218 | } 219 | 220 | -------------------------------------------------------------------------------- /ugh/aux/daemon.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_DAEMON_H__ 2 | #define __AUX_DAEMON_H__ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct aux_getopt 11 | aux_getopt_t; 12 | 13 | struct aux_getopt 14 | { 15 | const char* config; 16 | const char* pid; 17 | 18 | unsigned daemon:1; 19 | }; 20 | 21 | int aux_getopt_usage(int argc, char** argv); 22 | int aux_getopt_parse(int argc, char** argv, aux_getopt_t* opt); 23 | 24 | int aux_daemon_fork(); 25 | int aux_daemon_load(aux_getopt_t* opt); 26 | int aux_daemon_stop(aux_getopt_t* opt); 27 | 28 | extern volatile int aux_terminate; 29 | extern volatile int aux_changecfg; 30 | extern volatile int aux_rotatelog; 31 | extern volatile int aux_changebin; 32 | 33 | int aux_signal_init(); 34 | int aux_signal_mask(int how, unsigned mask); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif /* __AUX_DAEMON_H__ */ 41 | -------------------------------------------------------------------------------- /ugh/aux/gmtime.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_GMTIME_H__ 2 | #define __AUX_GMTIME_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | static inline 12 | void aux_gmtime(time_t t, struct tm *tp) 13 | { 14 | int yday; 15 | uintptr_t n, sec, min, hour, mday, mon, year, wday, days, leap; 16 | 17 | /* the calculation is valid for positive time_t only */ 18 | 19 | n = (uintptr_t) t; 20 | days = n / 86400; 21 | 22 | /* Jaunary 1, 1970 was Thursday */ 23 | 24 | wday = (4 + days) % 7; 25 | n %= 86400; 26 | hour = n / 3600; 27 | n %= 3600; 28 | min = n / 60; 29 | sec = n % 60; 30 | 31 | /* 32 | * the algorithm based on Gauss' formula, 33 | * see src/http/ngx_http_parse_time.c 34 | */ 35 | 36 | /* days since March 1, 1 BC */ 37 | days = days - (31 + 28) + 719527; 38 | 39 | /* 40 | * The "days" should be adjusted to 1 only, however, some March 1st's go 41 | * to previous year, so we adjust them to 2. This causes also shift of the 42 | * last Feburary days to next year, but we catch the case when "yday" 43 | * becomes negative. 44 | */ 45 | 46 | year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1); 47 | yday = days - (365 * year + year / 4 - year / 100 + year / 400); 48 | 49 | if (yday < 0) 50 | { 51 | leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0)); 52 | yday = 365 + leap + yday; 53 | year--; 54 | } 55 | 56 | /* 57 | * The empirical formula that maps "yday" to month. 58 | * There are at least 10 variants, some of them are: 59 | * mon = (yday + 31) * 15 / 459 60 | * mon = (yday + 31) * 17 / 520 61 | * mon = (yday + 31) * 20 / 612 62 | */ 63 | 64 | mon = (yday + 31) * 10 / 306; 65 | 66 | /* the Gauss' formula that evaluates days before the month */ 67 | 68 | mday = yday - (367 * mon / 12 - 30) + 1; 69 | 70 | if (yday >= 306) 71 | { 72 | year++; 73 | mon -= 10; 74 | 75 | /* 76 | * there is no "yday" in Win32 SYSTEMTIME 77 | * 78 | * yday -= 306; 79 | */ 80 | } 81 | else 82 | { 83 | mon += 2; 84 | 85 | /* 86 | * there is no "yday" in Win32 SYSTEMTIME 87 | * 88 | * yday += 31 + 28 + leap; 89 | */ 90 | } 91 | 92 | tp->tm_sec = (int) sec; 93 | tp->tm_min = (int) min; 94 | tp->tm_hour = (int) hour; 95 | tp->tm_mday = (int) mday; 96 | tp->tm_mon = (int) mon; 97 | tp->tm_year = (int) year; 98 | tp->tm_wday = (int) wday; 99 | } 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif 104 | 105 | #endif /* __AUX_GMTIME_H__ */ 106 | -------------------------------------------------------------------------------- /ugh/aux/hashes.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_HASHES_H__ 2 | #define __AUX_HASHES_H__ 3 | 4 | #include 5 | #include "string.h" 6 | 7 | #define aux_hash(key,c) ((uintptr_t) (key) * 31 + ((uint8_t) (c))) 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | static inline 14 | uintptr_t aux_hash_key(const char *data, size_t size) 15 | { 16 | uintptr_t key = 0; 17 | 18 | for (; size--; ++data) 19 | { 20 | key = aux_hash(key, *data); 21 | } 22 | 23 | return key; 24 | } 25 | 26 | static inline 27 | uintptr_t aux_hash_key_nt(const char *data) 28 | { 29 | uintptr_t key = 0; 30 | 31 | for (; *data; ++data) 32 | { 33 | key = aux_hash(key, *data); 34 | } 35 | 36 | return key; 37 | } 38 | 39 | static inline 40 | uintptr_t aux_hash_key_lc(const char *data, size_t size) 41 | { 42 | uintptr_t key = 0; 43 | 44 | for (; size--; ++data) 45 | { 46 | key = aux_hash(key, aux_tolower(*data)); 47 | } 48 | 49 | return key; 50 | } 51 | 52 | static inline 53 | uintptr_t aux_hash_key_lc_nt(const char *data) 54 | { 55 | uintptr_t key = 0; 56 | 57 | for (; *data; ++data) 58 | { 59 | key = aux_hash(key, aux_tolower(*data)); 60 | } 61 | 62 | return key; 63 | } 64 | 65 | static inline 66 | uintptr_t aux_hash_key_lc_header(const char *data, size_t size) 67 | { 68 | uintptr_t key = 0; 69 | 70 | for (; size--; ++data) 71 | { 72 | key = aux_hash(key, ('-' != *data) ? aux_tolower(*data) : '_'); 73 | } 74 | 75 | return key; 76 | } 77 | 78 | static inline 79 | uintptr_t aux_hash_key_lc_header_nt(const char *data) 80 | { 81 | uintptr_t key = 0; 82 | 83 | for (; *data; ++data) 84 | { 85 | key = aux_hash(key, ('-' != *data) ? aux_tolower(*data) : '_'); 86 | } 87 | 88 | return key; 89 | } 90 | 91 | static inline 92 | uintptr_t aux_hash_strlow(char *dest, const char *data, size_t size) 93 | { 94 | uintptr_t key = 0; 95 | 96 | for (; size--; ++dest, ++data) 97 | { 98 | *dest = aux_tolower(*data); 99 | key = aux_hash(key, *dest); 100 | } 101 | 102 | return key; 103 | } 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | #endif /* __AUX_HASHES_H__ */ 110 | -------------------------------------------------------------------------------- /ugh/aux/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "logger.h" 6 | 7 | volatile int log_level = LOG_info; 8 | 9 | int log_levels(const char* level) 10 | { 11 | switch (level[0]) 12 | { 13 | case 'a': case 'A': 14 | switch (level[1]) 15 | { 16 | case 'c': case 'C': return LOG_access; 17 | case 'l': case 'L': return LOG_alert; 18 | } 19 | break; 20 | case 'd': case 'D': return LOG_debug; 21 | case 'c': case 'C': return LOG_crit; 22 | case 'e': case 'E': 23 | switch (level[1]) 24 | { 25 | case 'm': case 'M': return LOG_emerg; 26 | case 'r': case 'R': return LOG_error; 27 | } 28 | break; 29 | case 'i': case 'I': return LOG_info; 30 | case 'n': case 'N': return LOG_notice; 31 | case 'w': case 'W': return LOG_warn; 32 | } 33 | 34 | return LOG_info; /* default */ 35 | } 36 | 37 | int log_create(const char* path, int level) 38 | { 39 | if (0 > aux_mkpath((char *) path)) return -1; 40 | if (0 > aux_fdopen(STDERR_FILENO, path, O_CREAT|O_APPEND|O_WRONLY)) return -1; 41 | 42 | log_level = level; 43 | 44 | return STDERR_FILENO; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ugh/aux/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_LOGGER_H__ 2 | #define __AUX_LOGGER_H__ 3 | 4 | #include 5 | #include 6 | #include "gmtime.h" 7 | #include "system.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #define LOG_access 0 /* ACC: special level for access log */ 14 | #define LOG_emerg 1 /* EME: system is unusable */ 15 | #define LOG_alert 2 /* ALE: action must be taken immediately */ 16 | #define LOG_crit 3 /* CRI: critical conditions */ 17 | #define LOG_error 4 /* ERR: error conditions */ 18 | #define LOG_warn 5 /* WRN: warning conditions */ 19 | #define LOG_notice 6 /* NOT: normal, but significant condition */ 20 | #define LOG_info 7 /* NFO: informational message */ 21 | #define LOG_debug 8 /* DBG: debug message */ 22 | 23 | #define log_msg(lev,fmt,...) log_fmt(stderr,LOG_##lev,#lev,fmt,##__VA_ARGS__) 24 | #define log_err(err,fmt,...) log_msg(error, fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)) 25 | #define log_ret(err,fmt,...) log_msg(error, fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)), err 26 | #define log_die(err,fmt,...) log_msg(emerg, fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)); exit(EXIT_FAILURE) 27 | 28 | #define std_msg(fmt,...) fprintf(stderr,fmt "\n", ##__VA_ARGS__) 29 | #define std_err(err,fmt,...) fprintf(stderr,fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)) 30 | #define std_ret(err,fmt,...) fprintf(stderr,fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)), err 31 | #define std_die(err,fmt,...) fprintf(stderr,fmt " (%d: %s)\n", ##__VA_ARGS__, err, aux_strerror(err)); exit(EXIT_FAILURE) 32 | 33 | #define log_access( fmt,...) log_msg(access,fmt,##__VA_ARGS__) 34 | #define log_emerg( fmt,...) log_msg(emerg, fmt,##__VA_ARGS__) 35 | #define log_alert( fmt,...) log_msg(alert, fmt,##__VA_ARGS__) 36 | #define log_crit( fmt,...) log_msg(crit, fmt,##__VA_ARGS__) 37 | #define log_error( fmt,...) log_msg(error, fmt,##__VA_ARGS__) 38 | #define log_warn( fmt,...) log_msg(warn, fmt,##__VA_ARGS__) 39 | #define log_notice( fmt,...) log_msg(notice,fmt,##__VA_ARGS__) 40 | #define log_info( fmt,...) log_msg(info, fmt,##__VA_ARGS__) 41 | #ifndef NDEBUG 42 | #define log_debug( fmt,...) log_msg(debug, fmt,##__VA_ARGS__) 43 | #else 44 | #define log_debug( fmt,...) /* nothing here */ 45 | #endif /* NDEBUG */ 46 | 47 | #define LOG_FORMAT(lvstr,fmt) "%04u-%02u-%02u %02u:%02u:%02u.%03u " lvstr " " fmt "\n" 48 | #define LOG_VALUES(tmloc,tv,...) tmloc.tm_year + 1900, tmloc.tm_mon + 1, tmloc.tm_mday, \ 49 | tmloc.tm_hour, tmloc.tm_min, tmloc.tm_sec, (unsigned) tv.tv_usec / 1000, ##__VA_ARGS__ 50 | 51 | #define log_fmt(fp,level,lvstr,fmt,...) do { \ 52 | \ 53 | if (level <= log_level) \ 54 | { \ 55 | struct tm tmloc; \ 56 | struct timeval tv; \ 57 | gettimeofday(&tv, NULL); \ 58 | localtime_r(&tv.tv_sec, &tmloc); \ 59 | \ 60 | fprintf(fp, LOG_FORMAT(lvstr,fmt), \ 61 | LOG_VALUES(tmloc,tv,##__VA_ARGS__)); \ 62 | } \ 63 | \ 64 | } while (0) 65 | 66 | extern volatile int log_level; 67 | int log_levels(const char* level); 68 | int log_create(const char* path, int level); 69 | 70 | #define log_create_from_str(path, level) log_create(path, log_levels(level)) 71 | #define log_rotate(path) log_create(path, log_level) 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif /* __AUX_LOGGER_H__ */ 78 | -------------------------------------------------------------------------------- /ugh/aux/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | aux_pool_t *aux_pool_init(size_t size) 4 | { 5 | aux_pool_t *pool; 6 | 7 | if (size <= sizeof(*pool)) 8 | { 9 | size = AUX_POOL_PAGESIZE; 10 | } 11 | 12 | pool = malloc(size); /* [posix_]memalign(AUX_POOL_MEMALIGN, size) */ 13 | if (NULL == pool) return NULL; 14 | 15 | pool->data = (char *) pool + sizeof(*pool); 16 | pool->last = (char *) pool + size; 17 | 18 | pool->curr = pool; 19 | pool->next = NULL; 20 | 21 | pool->link = 1; 22 | 23 | return pool; 24 | } 25 | 26 | void aux_pool_link(aux_pool_t *pool) 27 | { 28 | ++pool->link; 29 | } 30 | 31 | void aux_pool_drop(aux_pool_t *pool) 32 | { 33 | if (--pool->link) return; 34 | 35 | pool->curr = pool; 36 | 37 | for (; pool; pool = pool->next) 38 | { 39 | pool->data = (char *) pool + sizeof(*pool); 40 | } 41 | } 42 | 43 | void aux_pool_free(aux_pool_t *pool) 44 | { 45 | if (--pool->link) return; 46 | 47 | aux_pool_t *next; 48 | 49 | for (next = pool->next; next; pool = next, 50 | next = pool->next) 51 | { 52 | free(pool); 53 | } 54 | 55 | free(pool); 56 | } 57 | 58 | static 59 | void *aux_pool_huge(aux_pool_t *pool, size_t size) 60 | { 61 | void *dest; 62 | aux_pool_t *next; 63 | 64 | next = aux_pool_init(size + sizeof(*pool)); 65 | if (NULL == next) return NULL; 66 | 67 | dest = next->data; 68 | next->data += size; 69 | 70 | /* pool->curr->next = next; */ 71 | /* pool->curr = next; */ 72 | 73 | next->next = pool->next; /* huge pages are allocated just after the first page */ 74 | pool->next = next; 75 | 76 | return dest; 77 | } 78 | 79 | static 80 | void *aux_pool_page(aux_pool_t *pool, size_t size) 81 | { 82 | void *dest; 83 | aux_pool_t *next; 84 | 85 | next = aux_pool_init(aux_pool_size(pool)); 86 | if (NULL == next) return NULL; 87 | 88 | dest = next->data; 89 | next->data += size; 90 | 91 | /* 92 | * TODO 93 | * 94 | * Здесь мы теряем хвост страницы, не бегая по списку страниц с проверкой 95 | * размера оставшегося хвоста (или количества раз, когда мы эту страницу 96 | * уже пробегали, как щас сделано в nginx). 97 | * 98 | */ 99 | 100 | /* 101 | * Check if pool->curr->next is not NULL (which can occur if it was 102 | * allocated as huge page) 103 | */ 104 | 105 | for (; pool->curr->next; pool->curr = pool->curr->next) 106 | { 107 | /* void */ 108 | } 109 | 110 | pool->curr->next = next; 111 | pool->curr = next; 112 | 113 | return dest; 114 | } 115 | 116 | void *aux_pool_nalloc(aux_pool_t *pool, size_t size) 117 | { 118 | char *dest; 119 | aux_pool_t *curr; 120 | 121 | if (size > aux_pool_size(pool) - sizeof(*pool)) 122 | { 123 | return aux_pool_huge(pool, size); 124 | } 125 | 126 | for (curr = pool->curr; curr; curr = curr->next) 127 | { 128 | dest = curr->data; 129 | 130 | if (dest + size <= curr->last) 131 | { 132 | curr->data = dest + size; 133 | return dest; 134 | } 135 | } 136 | 137 | return aux_pool_page(pool, size); 138 | } 139 | 140 | void *aux_pool_malloc(aux_pool_t *pool, size_t size) 141 | { 142 | char *dest; 143 | aux_pool_t *curr; 144 | 145 | if (size > aux_pool_size(pool) - sizeof(*pool)) 146 | { 147 | return aux_pool_huge(pool, size); 148 | } 149 | 150 | for (curr = pool->curr; curr; curr = curr->next) 151 | { 152 | dest = aux_align_ptr(curr->data, AUX_MEMALIGN); 153 | 154 | if (dest + size <= curr->last) 155 | { 156 | curr->data = dest + size; 157 | return dest; 158 | } 159 | } 160 | 161 | return aux_pool_page(pool, size); 162 | } 163 | 164 | void *aux_pool_calloc(aux_pool_t *pool, size_t size) 165 | { 166 | void *dest; 167 | 168 | dest = aux_pool_malloc(pool, size); 169 | 170 | if (NULL != dest) 171 | { 172 | aux_clrmem(dest, size); 173 | } 174 | 175 | return dest; 176 | } 177 | 178 | char *aux_pool_strdup(aux_pool_t *pool, strp data) 179 | { 180 | char *dest; 181 | 182 | dest = aux_pool_nalloc(pool, data->size); 183 | 184 | if (NULL != dest) 185 | { 186 | memcpy(dest, data->data, data->size); 187 | } 188 | 189 | return dest; 190 | } 191 | 192 | char *aux_pool_memdup(aux_pool_t *pool, const char *data, size_t size) 193 | { 194 | char *dest; 195 | 196 | dest = aux_pool_nalloc(pool, size); 197 | 198 | if (NULL != dest) 199 | { 200 | memcpy(dest, data, size); 201 | } 202 | 203 | return dest; 204 | } 205 | 206 | -------------------------------------------------------------------------------- /ugh/aux/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_MEMORY_H__ 2 | #define __AUX_MEMORY_H__ 3 | 4 | #include 5 | #include 6 | #include "string.h" 7 | 8 | #define AUX_PAGESIZE 4096 9 | #define AUX_MEMALIGN sizeof(uintptr_t) 10 | 11 | #define aux_align(x,a) (((x) + ((a) - 1)) & ~((a) - 1)) 12 | #define aux_align_ptr(p,a) ((char *) (((uintptr_t) (p) + ((uintptr_t) (a) - 1)) & ~((uintptr_t) (a) - 1))) 13 | 14 | #define AUX_POOL_MEMALIGN 16 15 | #define AUX_POOL_PAGESIZE 16384 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | typedef struct aux_pool 22 | aux_pool_t; 23 | 24 | struct aux_pool 25 | { 26 | char *data; 27 | char *last; 28 | aux_pool_t *curr; 29 | aux_pool_t *next; 30 | unsigned int link; 31 | }; 32 | 33 | aux_pool_t *aux_pool_init(size_t size); 34 | void aux_pool_link(aux_pool_t *pool); 35 | void aux_pool_drop(aux_pool_t *pool); 36 | void aux_pool_free(aux_pool_t *pool); 37 | 38 | #define aux_pool_size(p) ((size_t) ((p)->last - ((char *) (p)))) 39 | 40 | void *aux_pool_nalloc(aux_pool_t *pool, size_t size); /* non-aligned */ 41 | void *aux_pool_malloc(aux_pool_t *pool, size_t size); /* aligned */ 42 | void *aux_pool_calloc(aux_pool_t *pool, size_t size); /* aligned */ 43 | 44 | char *aux_pool_strdup(aux_pool_t *pool, strp data); /* non-aligned */ 45 | char *aux_pool_memdup(aux_pool_t *pool, const char *data, size_t size); /* non-aligned */ 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* __AUX_MEMORY_H__ */ 52 | -------------------------------------------------------------------------------- /ugh/aux/random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "random.h" 7 | 8 | int aux_random_init() 9 | { 10 | int fd = open("/dev/urandom", O_RDONLY); 11 | if (0 > fd) return -1; 12 | 13 | uint32_t rnd_from_device; 14 | 15 | int rc = read(fd, &rnd_from_device, sizeof(rnd_from_device)); 16 | if (0 > rc) return -1; 17 | 18 | close(fd); 19 | 20 | srandom(rnd_from_device); 21 | 22 | return 0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /ugh/aux/random.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_RANDOM_H__ 2 | #define __AUX_RANDOM_H__ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | int aux_random_init(); 11 | #define aux_random() random() 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | #endif /* __AUX_RANDOM_H__ */ 18 | -------------------------------------------------------------------------------- /ugh/aux/resolver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "hashes.h" 4 | #include "resolver.h" 5 | 6 | #define AUX_RESOLVE_A 1 7 | #define AUX_RESOLVE_CNAME 5 8 | #define AUX_RESOLVE_PTR 12 9 | #define AUX_RESOLVE_MX 15 10 | #define AUX_RESOLVE_TXT 16 11 | #define AUX_RESOLVE_DNAME 39 12 | 13 | #define AUX_RESOLVE_FORMERR 1 14 | #define AUX_RESOLVE_SERVFAIL 2 15 | #define AUX_RESOLVE_NXDOMAIN 3 16 | #define AUX_RESOLVE_NOTIMP 4 17 | #define AUX_RESOLVE_REFUSED 5 18 | 19 | typedef struct { 20 | unsigned char ident_hi; 21 | unsigned char ident_lo; 22 | unsigned char flags_hi; 23 | unsigned char flags_lo; 24 | unsigned char nqs_hi; 25 | unsigned char nqs_lo; 26 | unsigned char nan_hi; 27 | unsigned char nan_lo; 28 | unsigned char nns_hi; 29 | unsigned char nns_lo; 30 | unsigned char nar_hi; 31 | unsigned char nar_lo; 32 | } aux_resolver_query_t; 33 | 34 | typedef struct { 35 | unsigned char type_hi; 36 | unsigned char type_lo; 37 | unsigned char class_hi; 38 | unsigned char class_lo; 39 | } aux_resolver_qs_t; 40 | 41 | typedef struct { 42 | unsigned char type_hi; 43 | unsigned char type_lo; 44 | unsigned char class_hi; 45 | unsigned char class_lo; 46 | unsigned char ttl [4]; 47 | unsigned char len_hi; 48 | unsigned char len_lo; 49 | } aux_resolver_an_t; 50 | 51 | int create_name_query(char *p, char *name, size_t size) 52 | { 53 | size_t nlen = size ? (1 + size + 1) : 1; 54 | size_t qlen = sizeof(aux_resolver_query_t) + nlen + sizeof(aux_resolver_qs_t); 55 | 56 | aux_resolver_query_t *qq = (aux_resolver_query_t *) p; 57 | 58 | uint16_t ident = aux_hash_key(name, size) & 0xffff; 59 | 60 | qq->ident_hi = (unsigned char) ((ident >> 8) & 0xff); 61 | qq->ident_lo = (unsigned char) (ident & 0xff); 62 | 63 | /* recursion query */ 64 | qq->flags_hi = 1; 65 | qq->flags_lo = 0; 66 | 67 | /* one question */ 68 | qq->nqs_hi = 0; qq->nqs_lo = 1; 69 | qq->nan_hi = 0; qq->nan_lo = 0; 70 | qq->nns_hi = 0; qq->nns_lo = 0; 71 | qq->nar_hi = 0; qq->nar_lo = 0; 72 | 73 | p += sizeof(aux_resolver_query_t) + nlen; 74 | 75 | aux_resolver_qs_t *qs = (aux_resolver_qs_t *) p; 76 | 77 | /* query type */ 78 | qs->type_hi = 0; 79 | qs->type_lo = AUX_RESOLVE_A; /* TODO (unsigned char) ctx->type */ 80 | 81 | /* IP query class */ 82 | qs->class_hi = 0; 83 | qs->class_lo = 1; 84 | 85 | /* convert "www.example.com" to "\3www\7example\3com\0" */ 86 | 87 | nlen = 0; p--; *p-- = '\0'; 88 | 89 | char *s; 90 | 91 | for (s = name + size - 1; s >= name; --s) 92 | { 93 | if ('.' != *s) 94 | { 95 | *p = *s; 96 | ++nlen; 97 | } 98 | else 99 | { 100 | if (0 == nlen) 101 | { 102 | return -1; 103 | } 104 | 105 | *p = (unsigned char) nlen; 106 | nlen = 0; 107 | } 108 | 109 | --p; 110 | } 111 | 112 | *p = (unsigned char) nlen; 113 | 114 | return qlen; 115 | } 116 | 117 | int process_response(char *data, size_t size, in_addr_t *addrs, int addrs_len, strp name) 118 | { 119 | aux_resolver_query_t *q = (aux_resolver_query_t *) data; 120 | 121 | /* uint16_t ident = (q->ident_hi << 8) + q->ident_lo; */ 122 | uint16_t flags = (q->flags_hi << 8) + q->flags_lo; 123 | 124 | uint16_t nqs = (q->nqs_hi << 8) + q->nqs_lo; 125 | uint16_t nan = (q->nan_hi << 8) + q->nan_lo; 126 | 127 | /* printf("ident=%u, flags=%u, nqs=%u, nan=%u\n", ident, flags, nqs, nan); */ 128 | 129 | if (0 == (flags & 0x8000)) /* invalid DNS response */ 130 | { 131 | return -1; 132 | } 133 | 134 | unsigned int code = flags & 0x7f; 135 | 136 | if (AUX_RESOLVE_FORMERR == code || AUX_RESOLVE_REFUSED < code) /* error in DNS response */ 137 | { 138 | return -1; 139 | } 140 | 141 | if (1 != nqs) /* invalid number of questions */ 142 | { 143 | return -1; 144 | } 145 | 146 | unsigned int i = sizeof(aux_resolver_query_t); 147 | size_t len; 148 | 149 | name->size = 0; 150 | 151 | while (i < size) 152 | { 153 | if ('\0' == data[i]) 154 | { 155 | break; 156 | } 157 | 158 | if (0 != name->size) 159 | { 160 | name->data[name->size++] = '.'; 161 | } 162 | 163 | name->size += aux_cpymsz(name->data + name->size, &data[i+1], (unsigned char) data[i]); 164 | 165 | len = (unsigned char) data[i]; 166 | i += 1 + len; 167 | } 168 | 169 | i++; 170 | 171 | if (i + sizeof(aux_resolver_qs_t) + nan * (2 + sizeof(aux_resolver_an_t)) > size) /* short DNS response */ 172 | { 173 | return -1; 174 | } 175 | 176 | aux_resolver_qs_t *qs = (aux_resolver_qs_t *) &data[i]; 177 | 178 | uint16_t qtype = (qs->type_hi << 8) + qs->type_lo; 179 | uint16_t qclass = (qs->class_hi << 8) + qs->class_lo; 180 | 181 | /* printf("resolver DNS response qtype=%u, qclass=%u\n", qtype, qclass); */ 182 | 183 | if (1 != qclass) /* unknown query class */ 184 | { 185 | return -1; 186 | } 187 | 188 | if (AUX_RESOLVE_A != qtype) /* unknown query type */ 189 | { 190 | return -1; 191 | } 192 | 193 | /* process a */ 194 | 195 | i += sizeof(aux_resolver_qs_t); 196 | 197 | unsigned int a; 198 | in_addr_t addr = 0; 199 | int naddrs = 0; 200 | 201 | for (a = 0; a < nan; ++a) 202 | { 203 | unsigned int start = i; 204 | 205 | while (i < size) 206 | { 207 | if (data[i] & 0xc0) 208 | { 209 | i += 2; 210 | break; 211 | } 212 | 213 | if (data[i] == 0) 214 | { 215 | i++; 216 | 217 | if (i - start < 2) /* invalid name in DNS response */ 218 | { 219 | return -1; 220 | } 221 | } 222 | 223 | i += 1 + (unsigned char) data[i]; 224 | } 225 | 226 | if (i + sizeof(aux_resolver_an_t) >= size) /* short DNS response */ 227 | { 228 | return -1; 229 | } 230 | 231 | aux_resolver_an_t *an = (aux_resolver_an_t *) &data[i]; 232 | 233 | qtype = (an->type_hi << 8) + an->type_lo; 234 | len = (an->len_hi << 8) + an->len_lo; 235 | 236 | if (AUX_RESOLVE_A == qtype) 237 | { 238 | i += sizeof(aux_resolver_an_t); 239 | 240 | if (i + len > size) /* short DNS response */ 241 | { 242 | return -1; 243 | } 244 | 245 | addr = htonl( 246 | (((unsigned char) data[i]) << 24) + 247 | (((unsigned char) data[i+1]) << 16) + 248 | (((unsigned char) data[i+2]) << 8) + 249 | ((unsigned char) data[i+3]) 250 | ); 251 | addrs[naddrs++] = addr; 252 | 253 | if (naddrs >= addrs_len) 254 | { 255 | break; 256 | } 257 | 258 | i += len; 259 | } 260 | else if (AUX_RESOLVE_CNAME == qtype) 261 | { 262 | /* char *cname = &data[i] + sizeof(aux_resolver_an_t); */ 263 | i += sizeof(aux_resolver_an_t) + len; 264 | 265 | /* printf("cname %u (%p)\n", (unsigned int) len, cname); [> \6yandex\2ru\0 <] */ 266 | } 267 | else if (AUX_RESOLVE_DNAME == qtype) 268 | { 269 | i += sizeof(aux_resolver_an_t) + len; 270 | } 271 | else 272 | { 273 | fprintf(stderr, "unexpected qtype=%u, len=%u\n", qtype, (unsigned int) len); 274 | } 275 | } 276 | 277 | return naddrs; 278 | } 279 | 280 | #define S_RESOLV_CONF_BEGIN 0 281 | #define S_RESOLV_CONF_PARAM 1 282 | #define S_RESOLV_CONF_VALUE_BEGIN 2 283 | #define S_RESOLV_CONF_VALUE 3 284 | #define S_RESOLV_CONF_VALUE_SPACE 4 285 | #define S_RESOLV_CONF_SHARP 5 286 | 287 | const char *parse_resolv_conf(aux_pool_t *pool) 288 | { 289 | strt resolv_conf; 290 | char *ns; 291 | 292 | int rc = aux_mmap_file(&resolv_conf, "/etc/resolv.conf"); 293 | if (0 > rc) return NULL; 294 | 295 | int state = S_RESOLV_CONF_BEGIN; 296 | char *p = resolv_conf.data; 297 | char *e = resolv_conf.data + resolv_conf.size; 298 | 299 | char *param_b = NULL; 300 | char *param_e = NULL; 301 | char *value_b = NULL; 302 | char *value_e = NULL; 303 | 304 | for (; p < e; ++p) 305 | { 306 | char ch = *p; 307 | 308 | switch (state) 309 | { 310 | case S_RESOLV_CONF_BEGIN: 311 | if (value_e) 312 | { 313 | if (0 == strncmp(param_b, "nameserver", param_e - param_b)) 314 | { 315 | goto found; 316 | } 317 | 318 | param_b = NULL; 319 | param_e = NULL; 320 | value_b = NULL; 321 | value_e = NULL; 322 | } 323 | 324 | switch (ch) 325 | { 326 | case '#': state = S_RESOLV_CONF_SHARP; break; 327 | case '\t': 328 | case ' ': 329 | case CR : 330 | case LF : break; 331 | default : param_b = p; state = S_RESOLV_CONF_PARAM; break; 332 | } 333 | break; 334 | case S_RESOLV_CONF_PARAM: 335 | switch (ch) 336 | { 337 | case '#': state = S_RESOLV_CONF_SHARP; break; 338 | case '\t': 339 | case ' ': param_e = p; state = S_RESOLV_CONF_VALUE_BEGIN; break; 340 | case CR : 341 | case LF : state = S_RESOLV_CONF_BEGIN; break; 342 | } 343 | break; 344 | case S_RESOLV_CONF_VALUE_BEGIN: 345 | switch (ch) 346 | { 347 | case '#': state = S_RESOLV_CONF_SHARP; break; 348 | case '\t': 349 | case ' ': break; 350 | case CR : 351 | case LF : state = S_RESOLV_CONF_BEGIN; break; 352 | default : value_b = p; state = S_RESOLV_CONF_VALUE; break; 353 | } 354 | break; 355 | case S_RESOLV_CONF_VALUE: 356 | switch (ch) 357 | { 358 | case '#': value_e = p; state = S_RESOLV_CONF_SHARP; break; 359 | case '\t': 360 | case ' ': value_e = p; state = S_RESOLV_CONF_VALUE_SPACE; break; 361 | case CR : 362 | case LF : value_e = p; state = S_RESOLV_CONF_BEGIN; break; 363 | } 364 | break; 365 | case S_RESOLV_CONF_VALUE_SPACE: 366 | switch (ch) 367 | { 368 | case '#': state = S_RESOLV_CONF_SHARP; break; 369 | case '\t': 370 | case ' ': break; 371 | case CR : 372 | case LF : state = S_RESOLV_CONF_BEGIN; break; 373 | default : state = S_RESOLV_CONF_VALUE; break; 374 | } 375 | break; 376 | case S_RESOLV_CONF_SHARP: 377 | switch (ch) 378 | { 379 | case CR : 380 | case LF : state = S_RESOLV_CONF_BEGIN; break; 381 | } 382 | break; 383 | } 384 | } 385 | 386 | if (value_e) 387 | { 388 | if (0 == strncmp(param_b, "nameserver", param_e - param_b)) 389 | { 390 | goto found; 391 | } 392 | } 393 | 394 | return "127.0.0.1"; 395 | 396 | found: 397 | 398 | ns = aux_pool_malloc(pool, value_e - value_b + 1); 399 | 400 | if (NULL == ns) 401 | { 402 | aux_umap(&resolv_conf); 403 | return "127.0.0.1"; 404 | } 405 | 406 | memcpy(ns, value_b, value_e - value_b); 407 | ns[value_e - value_b] = 0; 408 | 409 | aux_umap(&resolv_conf); 410 | 411 | return ns; 412 | } 413 | 414 | -------------------------------------------------------------------------------- /ugh/aux/resolver.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_RESOLVER_H__ 2 | #define __AUX_RESOLVER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "memory.h" 8 | #include "string.h" 9 | #include "system.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | int create_name_query(char *p, char *name, size_t size); 16 | int process_response(char *data, size_t size, in_addr_t *addrs, int addrs_len, strp name); 17 | 18 | const char *parse_resolv_conf(aux_pool_t *pool); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif /* __AUX_RESOLVER_H__ */ 25 | -------------------------------------------------------------------------------- /ugh/aux/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_SOCKET_H__ 2 | #define __AUX_SOCKET_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | static inline 16 | int aux_set_nonblk(int s, int value) 17 | { 18 | return ioctl(s, FIONBIO, &value); 19 | } 20 | 21 | static inline 22 | int aux_set_sckopt(int s, int level, int key, int value) 23 | { 24 | return setsockopt(s, level, key, (void *) &value, sizeof(value)); 25 | } 26 | 27 | #if defined(__APPLE__) || defined(__MACH__) 28 | #define AUX_NOSIGNAL SO_NOSIGPIPE 29 | #else 30 | #define AUX_NOSIGNAL MSG_NOSIGNAL 31 | #endif 32 | 33 | static inline 34 | int aux_unix_send(int fd, void *data, size_t size) 35 | { 36 | return send(fd, data, size, AUX_NOSIGNAL); 37 | } 38 | 39 | static inline 40 | int aux_unix_recv(int fd, void *data, size_t size) 41 | { 42 | int rc; 43 | 44 | for (;;) 45 | { 46 | rc = recv(fd, data, size, 0); 47 | if (0 <= rc) break; 48 | 49 | if (EINTR != errno) 50 | { 51 | return -1; 52 | } 53 | } 54 | 55 | return rc; 56 | } 57 | 58 | /* NOTE: If we will rewrite aux_inet_addr to return already accumulated address 59 | * on first bad symbol, we'll need to fix the usage of this function in 60 | * ugh_resolver (where we are trying to aux_inet_addr(hostname) to check if 61 | * it's valid IP address or hostname, which we should resolve) */ 62 | 63 | static inline 64 | in_addr_t aux_inet_addr(const char *data, size_t size) 65 | { 66 | const char *p; 67 | in_addr_t addr; 68 | uintptr_t octet, n; 69 | 70 | addr = 0; 71 | octet = 0; 72 | n = 0; 73 | 74 | for (p = data; p < data + size; ++p) 75 | { 76 | char c = *p; 77 | 78 | if (c >= '0' && c <= '9') 79 | { 80 | octet = octet * 10 + (c - '0'); 81 | continue; 82 | } 83 | 84 | if (c == '.' && octet < 256) 85 | { 86 | addr = (addr << 8) + octet; 87 | octet = 0; 88 | n++; 89 | continue; 90 | } 91 | 92 | return INADDR_NONE; 93 | } 94 | 95 | if (n != 3) 96 | { 97 | return INADDR_NONE; 98 | } 99 | 100 | if (octet < 256) 101 | { 102 | addr = (addr << 8) + octet; 103 | return htonl(addr); 104 | } 105 | 106 | return INADDR_NONE; 107 | } 108 | 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | 113 | #endif /* __AUX_SOCKET_H__ */ 114 | -------------------------------------------------------------------------------- /ugh/aux/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "string.h" 3 | 4 | strt aux_empty_string = { 0, "" }; 5 | 6 | const unsigned char aux_ch2dig_table [UCHAR_MAX + 1] = 7 | { 8 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 10 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 11 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-9 */ 12 | 0xff, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* A-O */ 13 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, /* P-Z */ 14 | 0xff, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* a-o */ 15 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, /* p-z */ 16 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 17 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 18 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 19 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 20 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 21 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 22 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 23 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 24 | }; 25 | 26 | #define SPA AUX_SPACE 27 | #define OCT AUX_OCTAL 28 | #define DEC AUX_DECAL 29 | #define XUP AUX_HEXUP 30 | #define XLO AUX_HEXLO 31 | #define UPP AUX_UPPER 32 | #define LOW AUX_LOWER 33 | 34 | const unsigned char aux_ctypes_table [UCHAR_MAX + 1] = 35 | { 36 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, SPA, SPA, SPA, SPA, SPA, 0x0, 0x0, 37 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 38 | SPA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 39 | OCT, OCT, OCT, OCT, OCT, OCT, OCT, OCT, DEC, DEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 40 | 0x0, XUP, XUP, XUP, XUP, XUP, XUP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, 41 | UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, UPP, 0x0, 0x0, 0x0, 0x0, 0x0, 42 | 0x0, XLO, XLO, XLO, XLO, XLO, XLO, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, 43 | LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, 0x0, 0x0, 0x0, 0x0, 0x0, 44 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 45 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 46 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 47 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 48 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 49 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 50 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 51 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 52 | }; 53 | 54 | char *aux_stristr(const char *s, const char *n) 55 | { 56 | const char *w; 57 | const char *x; 58 | 59 | if (!s || !*s) return NULL; 60 | if (!n || !*n) return (char *) s; 61 | 62 | for (; *s; ++s) 63 | { 64 | if (tolower(*s) == tolower(*n)) 65 | { 66 | for (w = s, x = n; *w && *x; ++w, ++x) 67 | { 68 | if (tolower(*w) != tolower(*x)) break; 69 | } 70 | 71 | if (!*x) return (char *) s; 72 | } 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | char *aux_strnstr(const char *s, const char *n, size_t len) 79 | { 80 | size_t nsz; 81 | const char *es, *en; 82 | const char *w; 83 | const char *x; 84 | 85 | if (!s || !*s) return NULL; 86 | if (!n || !*n) return (char *) s; 87 | 88 | if (len < (nsz = strlen(n))) return NULL; 89 | 90 | es = s + len; 91 | en = n + nsz; 92 | 93 | for (; s < es; ++s) 94 | { 95 | if (*s == *n) 96 | { 97 | for (w = s, x = n; w < es && x < en; ++w, ++x) 98 | { 99 | if (*w != *x) break; 100 | } 101 | 102 | if (x == en) return (char *) s; 103 | } 104 | } 105 | 106 | return NULL; 107 | } 108 | 109 | char *aux_strxstr(const char *s, const char *n, size_t len) 110 | { 111 | const char *w; 112 | const char *x; 113 | const char *es; 114 | const char *en; 115 | size_t nsz; 116 | 117 | if (!s || !*s) return NULL; 118 | if (!n || !*n) return (char *) s; 119 | if (len < (nsz = strlen(n))) return NULL; 120 | 121 | es = s + len; 122 | en = n + nsz; 123 | 124 | for (; s < es; ++s) 125 | { 126 | if (tolower(*s) == tolower(*n)) 127 | { 128 | for (w = s, x = n; w < es && x < en; ++w, ++x) 129 | { 130 | if (tolower(*w) != tolower(*x)) break; 131 | } 132 | 133 | if (x == en) return (char *) s; 134 | } 135 | } 136 | 137 | return NULL; 138 | } 139 | 140 | static unsigned char table_hexval [] = "0123456789ABCDEF"; 141 | 142 | static unsigned char table_urldec [UCHAR_MAX + 1] = 143 | { 144 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0-9 */ 148 | 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a-f */ 149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150 | 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A-F */ 151 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160 | }; 161 | 162 | static uint32_t table_urlenc [] = 163 | { 164 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 165 | /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 166 | 0xfc009fff, /* 1111 1100 0000 0000 1001 1111 1111 1111 */ 167 | /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 168 | 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ 169 | /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 170 | 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ 171 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 172 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 173 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 174 | 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 175 | }; 176 | 177 | size_t aux_urlenc(char* dst, const char* src, size_t sz_src) 178 | { 179 | unsigned char* pdst = (unsigned char *) dst; 180 | unsigned char* psrc = (unsigned char *) src; 181 | unsigned char* esrc = (unsigned char *) src + sz_src; 182 | 183 | while (psrc < esrc) 184 | { 185 | if (table_urlenc[*psrc >> 5] & (1 << (*psrc & 0x1f))) 186 | { 187 | *pdst++ = '%'; 188 | *pdst++ = table_hexval[*psrc >> 4]; 189 | *pdst++ = table_hexval[*psrc & 0xf]; 190 | ++psrc; 191 | } 192 | else 193 | { 194 | *pdst++ = *psrc++; 195 | } 196 | } 197 | 198 | *pdst = 0; 199 | 200 | return pdst - (unsigned char *) dst; 201 | } 202 | 203 | size_t aux_urldec(char* dst, const char* src, size_t sz_src) 204 | { 205 | unsigned char* pdst = (unsigned char *) dst; 206 | const unsigned char* psrc = (const unsigned char *) src; 207 | const unsigned char* esrc = (const unsigned char *) src + sz_src; 208 | 209 | while (psrc < esrc) 210 | { 211 | if (*psrc == '%' && psrc < esrc - 2) 212 | { 213 | *pdst++ = table_urldec[*(psrc+1)] * 0x10 + table_urldec[*(psrc+2)] * 0x01; 214 | psrc += 3; 215 | } 216 | else if (*psrc == '+') 217 | { 218 | *pdst++ = ' '; 219 | ++psrc; 220 | } 221 | else 222 | { 223 | *pdst++ = *psrc++; 224 | } 225 | } 226 | 227 | *pdst = 0; 228 | 229 | return pdst - (unsigned char *) dst; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /ugh/aux/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_STRING_H__ 2 | #define __AUX_STRING_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #if 1 14 | typedef struct { size_t size; char *data; } strt, *strp; 15 | 16 | #define aux_string(s) { sizeof(s) - 1, s } 17 | #define aux_null_string { 0, NULL } 18 | extern strt aux_empty_string; 19 | 20 | #define CR 0x0d 21 | #define LF 0x0a 22 | #define CRLF "\r\n" 23 | 24 | #define QUOTES_NAME(val) #val 25 | #define QUOTES_DATA(val) QUOTES_NAME(val) 26 | #define CONCAT_NAME(a,b) a##b 27 | #define CONCAT_DATA(a,b) CONCAT_NAME(a,b) 28 | 29 | #define aux_clrptr(p) memset((p),0,sizeof(*(p))) 30 | #define aux_clrvec(p,n) memset((p),0,sizeof(*(p))*(n)) 31 | #define aux_clrmem(p,n) memset((p),0,(n)) 32 | 33 | #define aux_memcpy(d,s,n) ((memcpy(d,s,n))) 34 | #define aux_cpymsz(d,s,n) ((memcpy(d,s,n),(n))) 35 | #define aux_cpymem(d,s,n) (((char *)memcpy(d,s,n))+(n)) 36 | 37 | /* rowsof - static allocated arrays only */ 38 | 39 | #define aux_bitsof(x) (sizeof(x) * 8) 40 | #define aux_rowsof(x) (sizeof(x)/sizeof((x)[0])) 41 | #define aux_member(t,n) (((t*)0)->(n)) 42 | #define aux_memberof(t,n,p) (((t*)(((unsigned char *)(p))-offsetof(t,n)))) 43 | #define aux_mebitsof(t,n) (aux_bitsof(aux_member(t,n))) 44 | #define aux_mesizeof(t,n) (sizeof(aux_member(t,n))) 45 | #define aux_metypeof(t,n) (typeof(aux_member(t,n))) 46 | #endif 47 | 48 | /* ----------------- */ 49 | 50 | #define aux_strerror(err) aux_strerror_r(err, (char *) alloca(4096), 4096) 51 | 52 | static inline 53 | const char* aux_strerror_r(int err, char* data, size_t size) 54 | { 55 | #if !defined(__USE_GNU) || defined(__FreeBSD__) 56 | if (0 != strerror_r(err, data, size)) return "XSI strerror_r returned error"; 57 | return data; 58 | #else 59 | return strerror_r(err, data, size); 60 | #endif /* __USE_GNU */ 61 | } 62 | 63 | /* ----------------- */ 64 | 65 | extern const unsigned char aux_ctypes_table [UCHAR_MAX + 1]; 66 | extern const unsigned char aux_ch2dig_table [UCHAR_MAX + 1]; 67 | 68 | #define aux_ctypes(c) aux_ctypes_table[(unsigned char) (c)] 69 | #define aux_ch2dig(c) aux_ch2dig_table[(unsigned char) (c)] 70 | 71 | #define AUX_SPACE 0x01 /* space */ 72 | #define AUX_OCTAL 0x02 /* octal */ 73 | #define AUX_DECAL 0x04 /* digit */ 74 | #define AUX_HEXUP 0x08 /* hexup */ 75 | #define AUX_HEXLO 0x10 /* hexlo */ 76 | #define AUX_UPPER 0x20 /* upper */ 77 | #define AUX_LOWER 0x40 /* lower */ 78 | 79 | #define aux_isspace(c) (aux_ctypes(c) & (AUX_SPACE)) 80 | #define aux_isoctal(c) (aux_ctypes(c) & (AUX_OCTAL)) 81 | #define aux_isdecal(c) (aux_ctypes(c) & (AUX_OCTAL|AUX_DECAL)) 82 | #define aux_ishexit(c) (aux_ctypes(c) & (AUX_OCTAL|AUX_DECAL|AUX_HEXUP|AUX_HEXLO)) 83 | #define aux_ishexup(c) (aux_ctypes(c) & (AUX_HEXUP)) 84 | #define aux_ishexlo(c) (aux_ctypes(c) & (AUX_HEXLO)) 85 | #define aux_isupper(c) (aux_ctypes(c) & (AUX_HEXUP|AUX_UPPER)) 86 | #define aux_islower(c) (aux_ctypes(c) & (AUX_HEXLO|AUX_LOWER)) 87 | #define aux_isalpha(c) (aux_ctypes(c) & (AUX_HEXUP|AUX_HEXLO|AUX_UPPER|AUX_LOWER)) 88 | #define aux_isalnum(c) (aux_ctypes(c) & (AUX_OCTAL|AUX_DECAL|AUX_HEXUP|AUX_HEXLO|AUX_UPPER|AUX_LOWER)) 89 | #define aux_tolower(c) ((unsigned char) (((c) >= 'A' && (c) <= 'Z') ? ((c) | 0x20) : (c))) 90 | #define aux_toupper(c) ((unsigned char) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~0x20) : (c))) 91 | 92 | #define hex_hi(x) "0123456789abcdef" [((unsigned char) (x)) >> 4] 93 | #define hex_lo(x) "0123456789abcdef" [((unsigned char) (x)) & 0xf] 94 | #define HEX_hi(x) "0123456789ABCDEF" [((unsigned char) (x)) >> 4] 95 | #define HEX_lo(x) "0123456789ABCDEF" [((unsigned char) (x)) & 0xf] 96 | #define hex_pk(l,r) ((*l++ = hex_hi(r)), (*l++ = hex_lo(r))) 97 | #define HEX_pk(l,r) ((*l++ = HEX_hi(r)), (*l++ = HEX_lo(r))) 98 | 99 | static inline 100 | int aux_bin2str(char *dest, const char *data, size_t size) 101 | { 102 | for (; size--; ++data) 103 | { 104 | HEX_pk(dest,*data); 105 | } 106 | 107 | return 0; 108 | } 109 | 110 | static inline 111 | int aux_str2bin(char *dest, const char *data, size_t size) 112 | { 113 | unsigned char dig; 114 | 115 | for (;; ++dest) 116 | { 117 | if (!size--) break; 118 | if (16 <= (dig = aux_ch2dig(*data++))) return -1; 119 | *dest |= (dig & 0x0f) << 4; 120 | 121 | if (!size--) break; 122 | if (16 <= (dig = aux_ch2dig(*data++))) return -1; 123 | *dest |= dig & 0x0f; 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | char *aux_stristr(const char *s, const char *n); /* search for [n] in [s] ignoring case */ 130 | char *aux_strnstr(const char *s, const char *n, size_t len); /* search for [n] in first [len] bytes of [s] */ 131 | char *aux_strxstr(const char *s, const char *n, size_t len); /* search for [n] in first [len] bytes of [s] ignoring case */ 132 | 133 | static inline 134 | char *aux_memmem(const char *m, size_t sz_m, const char *n, size_t sz_n) 135 | { 136 | const char *bm, *em, *bn, *en; 137 | 138 | if (!m || !sz_m) return NULL; 139 | if (!n || !sz_n) return (char *) m; 140 | if (sz_m < sz_n) return NULL; 141 | 142 | bm = m; em = bm + sz_m; 143 | bn = n; en = bn + sz_n; 144 | 145 | for (; bm < em; ++bm) 146 | { 147 | if (*bm == *bn) 148 | { 149 | const char *pm, *pn; 150 | 151 | for (pm = bm, pn = bn; pm < em && pn < en; ++pm, ++pn) 152 | { 153 | if (*pm != *pn) break; 154 | } 155 | 156 | if (pn == en) return (char *) bm; 157 | } 158 | } 159 | 160 | return NULL; 161 | } 162 | 163 | /* two functions below will add terminating 0 byte! */ 164 | size_t aux_urlenc(char *dst, const char *src, size_t sz_src); 165 | size_t aux_urldec(char *dst, const char *src, size_t sz_src); 166 | 167 | #ifdef __cplusplus 168 | } /* extern "C" */ 169 | #endif 170 | 171 | #endif /* __AUX_STRING_H__ */ 172 | -------------------------------------------------------------------------------- /ugh/aux/system.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "logger.h" 6 | #include "system.h" 7 | 8 | int aux_fdopen(int fd, const char *path, int flags) 9 | { 10 | int td; 11 | 12 | td = open(path, flags, 0644); 13 | if (0 > td) return -1; 14 | 15 | return aux_fdmove(td, fd); 16 | } 17 | 18 | 19 | int aux_fdmove(int fd, int nd) /* if nd is ebadf or eq-to-fd => fd is returned */ 20 | { 21 | if (0 > nd || fd == nd) 22 | { 23 | return fd; 24 | } 25 | 26 | nd = dup2(fd, nd); 27 | if (0 > nd) return -1; 28 | 29 | if (0 > close(fd)) 30 | { 31 | return -1; 32 | } 33 | 34 | return nd; 35 | } 36 | 37 | /* 38 | * To pass a BSS-constant, you MUST use char[] (NOT char*) here. 39 | * 40 | * const char rw_access [] = "we can change this string"; 41 | * const char *r_access = "we cannot change this string"; 42 | * 43 | * It's not a hackneyed fact of C (and most likely C++), that static "BSS" 44 | * constants like the second one in the example above MAY be put in read-only 45 | * memory segment by compiler (and gcc have been following that recomendation 46 | * from some release), while the first line in the same example shows the 47 | * string of type, which SHOULD (or MUST, I don't care much) provide write 48 | * permissions if programmer needs it. 49 | * 50 | * The fact, that the const keyword in C does nothing, is much more hackneyed, 51 | * though. 52 | */ 53 | int aux_mkpath(char *path) 54 | { 55 | int rc; 56 | char *p = path + 1; 57 | 58 | for (; *p; ++p) 59 | { 60 | if (*p != '/') continue; 61 | 62 | *p = 0; 63 | rc = mkdir(path, 0755); 64 | *p = '/'; 65 | 66 | if (0 > rc && EEXIST != errno) 67 | { 68 | return -1; 69 | } 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | int aux_mkpidf(const char *path) 76 | { 77 | int fd, nb; 78 | char buf [32]; 79 | 80 | fd = open(path, O_CREAT|O_RDWR, 0644); 81 | if (0 > fd) return -1; 82 | 83 | if (0 > flock(fd, LOCK_EX|LOCK_NB)) 84 | { 85 | log_emerg("can't lock pid-file %s", path); 86 | close(fd); 87 | return -1; 88 | } 89 | 90 | struct stat st; 91 | 92 | if (0 > fstat(fd, &st)) 93 | { 94 | log_emerg("can't stat locked pid-file %s", path); 95 | flock(fd, LOCK_UN); 96 | close(fd); 97 | return -1; 98 | } 99 | 100 | if (st.st_size > 0) 101 | { 102 | log_emerg("pid-file %s exists", path); 103 | flock(fd, LOCK_UN); 104 | close(fd); 105 | return -1; 106 | } 107 | 108 | if (0 > (nb = snprintf(buf, 32, "%d\n", (int) getpid()))) 109 | { 110 | flock(fd, LOCK_UN); 111 | close(fd); 112 | return -1; 113 | } 114 | 115 | if (0 > write(fd, buf, nb)) 116 | { 117 | flock(fd, LOCK_UN); 118 | close(fd); 119 | return -1; 120 | } 121 | 122 | if (0 > flock(fd, LOCK_UN)) 123 | { 124 | close(fd); 125 | return -1; 126 | } 127 | 128 | if (0 > close(fd)) 129 | { 130 | return -1; 131 | } 132 | 133 | return 0; 134 | } 135 | 136 | /* 137 | * In fact, open_flags should be defined by both prot_flags and mmap_flags values, 138 | * and the definition of this interdependence doesn't look trivial sometimes. 139 | * 140 | * However, my guess is that almost in all cases file descriptor must be readable, 141 | * e.g. MAP_SHARED + PROT_WRITE require open with O_RDWR (not O_WRONLY). 142 | * 143 | * See, mmap(2). 144 | */ 145 | int aux_mmap(strp area, int prot_flags, int mmap_flags, const char *filename) 146 | { 147 | int fd; 148 | int open_flags; 149 | 150 | if (NULL == filename) /* MAP_ANONYMOUS */ 151 | { 152 | area->data = mmap(NULL, area->size, prot_flags, 153 | mmap_flags|MAP_ANON, -1, 0); 154 | 155 | if (MAP_FAILED == area->data) return -1; 156 | 157 | return 0; 158 | } 159 | 160 | open_flags = (prot_flags & PROT_WRITE) 161 | ? O_RDWR : O_RDONLY; 162 | 163 | fd = open(filename, open_flags); 164 | if (0 > fd) return -1; 165 | 166 | if (0 != area->size) 167 | { 168 | if (0 > ftruncate(fd, area->size)) 169 | { 170 | close(fd); 171 | return -1; 172 | } 173 | } 174 | else 175 | { 176 | struct stat st; 177 | 178 | if (0 > fstat(fd, &st)) 179 | { 180 | close(fd); 181 | return -1; 182 | } 183 | 184 | area->size = st.st_size; 185 | } 186 | 187 | area->data = mmap(NULL, area->size, prot_flags, 188 | mmap_flags, fd, 0); 189 | 190 | if (MAP_FAILED == area->data) 191 | { 192 | close(fd); 193 | return -1; 194 | } 195 | 196 | if (0 > close(fd)) 197 | { 198 | return -1; 199 | } 200 | 201 | return 0; 202 | } 203 | 204 | -------------------------------------------------------------------------------- /ugh/aux/system.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUX_SYSTEM_H__ 2 | #define __AUX_SYSTEM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "string.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | int aux_fdopen(int fd, const char *path, int flags); 15 | int aux_fdmove(int fd, int nd); 16 | 17 | #define aux_fnopen(p,f) open((const char *) p, f, 0644) 18 | #define aux_mkfold(p) mkdir((const char *) p, 0755) 19 | #define aux_rmfold(p) rmdir((const char *) p) 20 | #define aux_rmfile(p) unlink((const char *) p) 21 | #define aux_rlpath(p) realpath((const char *) p, alloca(PATH_MAX)) 22 | 23 | int aux_mkpath(char *path); 24 | int aux_mkpidf(const char *path); 25 | 26 | int aux_mmap(strp area, int prot_flags, int mmap_flags, const char *filename); 27 | 28 | #define aux_umap(area) munmap((area)->data, (area)->size) 29 | #define aux_mmap_file(a,p) aux_mmap((strp) aux_clrptr(a), PROT_READ, MAP_PRIVATE, p) 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* __AUX_SYSTEM_H__ */ 36 | -------------------------------------------------------------------------------- /ugh/channel.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | static 4 | int ugh_channel_del_memory(ugh_channel_t *ch) 5 | { 6 | log_info("channel_id=%.*s del memory", (int) ch->channel_id.size, ch->channel_id.data); 7 | 8 | ev_async_stop(loop, &ch->wev_message); 9 | 10 | if (ch->timeout > 0) 11 | { 12 | ev_timer_stop(loop, &ch->wev_timeout); 13 | } 14 | 15 | JudyLFreeArray(&ch->messages_hash, PJE0); 16 | JudyLFreeArray(&ch->clients_hash, PJE0); 17 | 18 | aux_pool_free(ch->pool); 19 | 20 | return 0; 21 | } 22 | 23 | static 24 | int ugh_channel_process_message(ugh_channel_t *ch) 25 | { 26 | Word_t idx = 0; 27 | int rc; 28 | 29 | for (rc = Judy1First(ch->clients_hash, &idx, PJE0); 0 != rc; 30 | rc = Judy1Next (ch->clients_hash, &idx, PJE0)) 31 | { 32 | Judy1Unset(&ch->clients_hash, idx, PJE0); 33 | ugh_client_t *c = (ugh_client_t *) idx; 34 | 35 | is_main_coro = 0; 36 | coro_transfer(&ctx_main, &c->ctx); 37 | is_main_coro = 1; 38 | } 39 | 40 | /* Judy1FreeArray(&new_clients_hash, PJE0); */ 41 | 42 | if (ch->status == UGH_CHANNEL_DELETED) 43 | { 44 | ugh_channel_del_memory(ch); 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | static 51 | void ugh_channel_wcb_timeout(EV_P_ ev_timer *w, int tev) 52 | { 53 | ugh_channel_t *ch = aux_memberof(ugh_channel_t, wev_timeout, w); 54 | log_warn("channel_id=%.*s received timeout", (int) ch->channel_id.size, ch->channel_id.data); 55 | 56 | /* we can't remove proxy channel waiting for subrequest */ 57 | /* TODO maybe we should force finish of subrequest here, but it's not that easy */ 58 | if (ch->type == UGH_CHANNEL_PROXY && NULL != ch->subreqs_hash) 59 | { 60 | log_warn("channel_id=%.*s could not be removed right now, waiting for subrequests to finish", (int) ch->channel_id.size, ch->channel_id.data); 61 | ev_timer_again(loop, w); 62 | return; 63 | } 64 | 65 | JudyLDel(&ch->s->channels_hash, aux_hash_key(ch->channel_id.data, ch->channel_id.size), PJE0); 66 | 67 | ch->status = UGH_CHANNEL_DELETED; 68 | ugh_channel_process_message(ch); 69 | } 70 | 71 | static 72 | void ugh_channel_wcb_message(EV_P_ ev_async *w, int tev) 73 | { 74 | ugh_channel_t *ch = aux_memberof(ugh_channel_t, wev_message, w); 75 | log_info("channel_id=%.*s received %s", (int) ch->channel_id.size, ch->channel_id.data, ch->status ? "delete" : "message"); 76 | 77 | ugh_channel_process_message(ch); 78 | } 79 | 80 | ugh_channel_t *ugh_channel_add(ugh_server_t *s, strp channel_id, unsigned type, ev_tstamp timeout) 81 | { 82 | /* check if channel already exist and return if it does */ 83 | 84 | void **dest = JudyLIns(&s->channels_hash, aux_hash_key(channel_id->data, channel_id->size), PJE0); 85 | if (PJERR == dest) return NULL; 86 | 87 | if (NULL != *dest) 88 | { 89 | log_warn("channel_id=%.*s trying to add existing channel", (int) channel_id->size, channel_id->data); 90 | return *dest; 91 | } 92 | 93 | /* create new channel */ 94 | 95 | aux_pool_t *pool; 96 | ugh_channel_t *ch; 97 | 98 | pool = aux_pool_init(0); 99 | if (NULL == pool) return NULL; 100 | 101 | ch = (ugh_channel_t *) aux_pool_calloc(pool, sizeof(*ch)); 102 | 103 | if (NULL == ch) 104 | { 105 | aux_pool_free(pool); 106 | return NULL; 107 | } 108 | 109 | ch->s = s; 110 | ch->pool = pool; 111 | 112 | ev_async_init(&ch->wev_message, ugh_channel_wcb_message); 113 | ev_async_start(loop, &ch->wev_message); 114 | 115 | ch->timeout = timeout; 116 | 117 | if (ch->timeout > 0) 118 | { 119 | ev_timer_init(&ch->wev_timeout, ugh_channel_wcb_timeout, 0, ch->timeout); 120 | ev_timer_again(loop, &ch->wev_timeout); 121 | } 122 | 123 | ch->channel_id.size = channel_id->size; 124 | ch->channel_id.data = aux_pool_strdup(ch->pool, channel_id); 125 | if (NULL == ch->channel_id.data) return NULL; 126 | 127 | ch->type = type; 128 | 129 | *dest = ch; 130 | 131 | return ch; 132 | } 133 | 134 | ugh_channel_t *ugh_channel_get(ugh_server_t *s, strp channel_id) 135 | { 136 | void **dest; 137 | 138 | dest = JudyLGet(s->channels_hash, aux_hash_key(channel_id->data, channel_id->size), PJE0); 139 | if (PJERR == dest || NULL == dest) return NULL; 140 | 141 | return *dest; 142 | } 143 | 144 | int ugh_channel_del(ugh_server_t *s, strp channel_id) 145 | { 146 | Word_t idx = aux_hash_key(channel_id->data, channel_id->size); 147 | 148 | void **dest; 149 | 150 | dest = JudyLGet(s->channels_hash, idx, PJE0); 151 | if (PJERR == dest || NULL == dest) return -1; 152 | 153 | ugh_channel_t *ch = *dest; 154 | 155 | /* for now, we disallow removing proxy channels with DELETE requests */ 156 | if (ch->type == UGH_CHANNEL_PROXY) 157 | { 158 | return -1; 159 | } 160 | 161 | ch->status = UGH_CHANNEL_DELETED; 162 | ev_async_send(loop, &ch->wev_message); 163 | 164 | /* 165 | * we remove channel from server list here, but free channel memory only 166 | * after all clients will be served with 410 Gone respone 167 | */ 168 | 169 | JudyLDel(&s->channels_hash, idx, PJE0); 170 | 171 | return 0; 172 | } 173 | 174 | int ugh_channel_add_subreq(ugh_channel_t *ch, ugh_subreq_t *s) 175 | { 176 | Judy1Set(&ch->subreqs_hash, (uintptr_t) s, PJE0); 177 | return 0; 178 | } 179 | 180 | int ugh_channel_add_message(ugh_channel_t *ch, strp body, strp content_type, ugh_subreq_t *r) 181 | { 182 | Word_t etag = -1; 183 | void **dest; 184 | 185 | if (NULL != JudyLLast(ch->messages_hash, &etag, PJE0)) 186 | { 187 | ++etag; 188 | } 189 | else 190 | { 191 | etag = 1; /* first possible etag */ 192 | } 193 | 194 | dest = JudyLIns(&ch->messages_hash, etag, PJE0); 195 | if (PJERR == dest) return -1; 196 | 197 | /* creating message */ 198 | 199 | ugh_channel_message_t *m = aux_pool_malloc(ch->pool, sizeof(*m)); 200 | if (NULL == m) return -1; 201 | 202 | if (NULL != r) 203 | { 204 | m->tag = r->tag; 205 | } 206 | 207 | m->body.data = aux_pool_strdup(ch->pool, body); 208 | m->body.size = body->size; 209 | 210 | m->content_type.data = aux_pool_strdup(ch->pool, content_type); 211 | m->content_type.size = content_type->size; 212 | 213 | *dest = m; 214 | 215 | ev_async_send(loop, &ch->wev_message); 216 | 217 | if (ch->type == UGH_CHANNEL_PROXY && NULL != r) 218 | { 219 | Judy1Unset(&ch->subreqs_hash, (uintptr_t) r, PJE0); 220 | } 221 | 222 | return 0; 223 | } 224 | 225 | static 226 | int ugh_channel_gen_message(ugh_channel_t *ch, ugh_channel_message_t **m, Word_t *etag) 227 | { 228 | void **dest = JudyLNext(ch->messages_hash, etag, PJE0); 229 | if (NULL == dest) return UGH_AGAIN; /* no new messages for this client */ 230 | 231 | *m = *dest; 232 | 233 | /* check if we should remove PROXY channel now */ 234 | 235 | if (ch->type == UGH_CHANNEL_PROXY && NULL == ch->subreqs_hash) 236 | { 237 | /* XXX this behaviour is arguable, cause we can try to use the same 238 | * ugh_channel_t from client, which got last message */ 239 | 240 | Word_t next_etag = *etag; 241 | 242 | if (NULL == (dest = JudyLNext(ch->messages_hash, &next_etag, PJE0))) /* check if next entry doesn't exist */ 243 | { 244 | ch->status = UGH_CHANNEL_DELETED; 245 | ev_async_send(loop, &ch->wev_message); 246 | 247 | JudyLDel(&ch->s->channels_hash, aux_hash_key(ch->channel_id.data, ch->channel_id.size), PJE0); 248 | } 249 | } 250 | 251 | return UGH_OK; 252 | } 253 | 254 | int ugh_channel_get_message(ugh_channel_t *ch, ugh_client_t *c, ugh_channel_message_t **m, unsigned type, Word_t *etag) 255 | { 256 | if (ch->status == UGH_CHANNEL_DELETED) /* XXX do we need this check here? */ 257 | { 258 | return UGH_ERROR; 259 | } 260 | 261 | /* check if the next entry exist */ 262 | 263 | Word_t original_etag = *etag; 264 | 265 | if (UGH_OK == ugh_channel_gen_message(ch, m, etag)) 266 | { 267 | return UGH_OK; 268 | } 269 | 270 | *etag = original_etag; 271 | 272 | /* next entry was not found, we're waiting for it */ 273 | 274 | if (type == UGH_CHANNEL_INTERVAL_POLL) 275 | { 276 | return UGH_AGAIN; 277 | } 278 | 279 | Judy1Set(&ch->clients_hash, (uintptr_t) c, PJE0); 280 | ch->clients_size++; 281 | 282 | for (;;) /* wait until we can generate message for this client */ 283 | { 284 | is_main_coro = 1; 285 | coro_transfer(&c->ctx, &ctx_main); 286 | is_main_coro = 0; 287 | 288 | if (ch->status == UGH_CHANNEL_DELETED) 289 | { 290 | return UGH_ERROR; 291 | } 292 | 293 | if (UGH_OK == ugh_channel_gen_message(ch, m, etag)) 294 | { 295 | break; 296 | } 297 | } 298 | 299 | return UGH_OK; 300 | } 301 | 302 | -------------------------------------------------------------------------------- /ugh/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "aux/resolver.h" 5 | #include "config.h" 6 | #include "module.h" 7 | #include "ugh.h" 8 | 9 | #define UGH_CONFIG_ARGMAX 8 10 | 11 | #define S_NEWARG 0 12 | #define S_NORMAL 1 13 | #define S_IGNORE 2 14 | #define S_QUOTES 3 15 | 16 | int ugh_config_init(ugh_config_t *cfg) 17 | { 18 | cfg->pool = aux_pool_init(0); 19 | if (NULL == cfg->pool) return -1; 20 | 21 | cfg->next_upstream = UGH_UPSTREAM_FT_ERROR 22 | | UGH_UPSTREAM_FT_TIMEOUT 23 | | UGH_UPSTREAM_FT_HTTP_500 24 | | UGH_UPSTREAM_FT_HTTP_502 25 | | UGH_UPSTREAM_FT_HTTP_503 26 | | UGH_UPSTREAM_FT_HTTP_504 27 | | UGH_UPSTREAM_FT_HTTP_404 28 | | UGH_UPSTREAM_FT_HTTP_5XX 29 | | UGH_UPSTREAM_FT_HTTP_4XX 30 | | UGH_UPSTREAM_FT_TIMEOUT_CONNECT 31 | ; 32 | 33 | cfg->log_error = "ugh.log"; 34 | cfg->log_level = "info"; 35 | 36 | cfg->listen_host = "0.0.0.0"; 37 | cfg->listen_port = 80; 38 | 39 | cfg->resolver_host = parse_resolv_conf(cfg->pool); 40 | cfg->resolver_timeout = UGH_CONFIG_RESOLVER_TIMEOUT; 41 | cfg->resolver_cache = 1; 42 | 43 | return 0; 44 | } 45 | 46 | int ugh_config_load(ugh_config_t *cfg, const char *filename) 47 | { 48 | int rc; 49 | strt data; 50 | 51 | rc = aux_mmap_file(&data, filename); 52 | 53 | if (0 != rc) 54 | { 55 | log_emerg("Can't open %s (%d: %s)", filename, errno, aux_strerror(errno)); 56 | return -1; 57 | } 58 | 59 | rc = ugh_config_data(cfg, data.data, data.size); 60 | if (0 != rc) return -1; 61 | 62 | rc = aux_umap(&data); 63 | if (0 != rc) return -1; 64 | 65 | return 0; 66 | } 67 | 68 | int ugh_config_free(ugh_config_t *cfg) 69 | { 70 | JudyLFreeArray(&cfg->upstreams_hash, PJE0); 71 | JudyLFreeArray(&cfg->vars_hash, PJE0); 72 | aux_pool_free(cfg->pool); 73 | 74 | return 0; 75 | } 76 | 77 | int ugh_config_data(ugh_config_t *cfg, char *data, size_t size) 78 | { 79 | cfg->beg = data; 80 | cfg->pos = data; 81 | cfg->end = data + size; 82 | 83 | return ugh_config_parser(cfg, ugh_config_option); 84 | } 85 | 86 | /* NOTE that last argv[] element is not guaranteed to be NULL */ 87 | 88 | int ugh_config_option(ugh_config_t *cfg, int argc, char **argv) 89 | { 90 | ugh_command_t *cmd = ugh_command_get(cfg, argv[0]); 91 | 92 | if (NULL == cmd) 93 | { 94 | log_emerg("Bad directive in config file: %s", argv[0]); 95 | return -1; 96 | } 97 | 98 | return cmd->handle(cfg, argc, argv, cmd); 99 | } 100 | 101 | int ugh_config_optset(ugh_config_t *cfg, int *argc, char **argv) 102 | { 103 | if (*argc < UGH_CONFIG_ARGMAX) 104 | { 105 | char *arg = aux_pool_nalloc(cfg->pool, cfg->pos - cfg->beg + 1); 106 | if (NULL == arg) return -1; 107 | 108 | memcpy(arg, cfg->beg, cfg->pos - cfg->beg); 109 | arg[cfg->pos - cfg->beg] = 0; 110 | 111 | argv[(*argc)++] = arg; 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | int ugh_config_parser(ugh_config_t *cfg, ugh_config_handle_fp handle) 118 | { 119 | int argc = 0; 120 | char *argv [UGH_CONFIG_ARGMAX]; 121 | 122 | int state = S_NEWARG; 123 | 124 | char quote = 0; 125 | 126 | for (cfg->pos = cfg->beg; cfg->pos < cfg->end; ++cfg->pos) 127 | { 128 | char ch = *cfg->pos; 129 | 130 | switch (state) 131 | { 132 | case S_NEWARG: 133 | if (';' == ch || '{' == ch) 134 | { 135 | cfg->beg = cfg->pos + 1; 136 | 137 | if (0 > handle(cfg, argc, argv)) return -1; 138 | argc = 0; 139 | } 140 | else if ('}' == ch) 141 | { 142 | return 0; 143 | } 144 | else if ('#' == ch) 145 | { 146 | state = S_IGNORE; 147 | } 148 | else if ('"' == ch || '\'' == ch) 149 | { 150 | quote = ch; 151 | cfg->beg = cfg->pos + 1; 152 | state = S_QUOTES; 153 | } 154 | else if (!isspace(ch)) 155 | { 156 | cfg->beg = cfg->pos; 157 | state = S_NORMAL; 158 | } 159 | break; 160 | case S_NORMAL: 161 | if (isspace(ch) || ';' == ch || '{' == ch) 162 | { 163 | ugh_config_optset(cfg, &argc, argv); 164 | 165 | if (';' == ch || '{' == ch) 166 | { 167 | cfg->beg = cfg->pos + 1; 168 | 169 | if (0 > handle(cfg, argc, argv)) return -1; 170 | argc = 0; 171 | } 172 | 173 | state = S_NEWARG; 174 | } 175 | break; 176 | case S_IGNORE: 177 | if (CR == ch || LF == ch) 178 | { 179 | state = S_NEWARG; 180 | } 181 | break; 182 | case S_QUOTES: 183 | if (quote == ch) 184 | { 185 | quote = 0; 186 | ugh_config_optset(cfg, &argc, argv); 187 | state = S_NEWARG; 188 | } 189 | break; 190 | } 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | ugh_command_t *ugh_command_get(ugh_config_t *cfg, const char *name) 197 | { 198 | size_t i, j; 199 | 200 | for (i = 0; i < ugh_modules_size; ++i) 201 | { 202 | for (j = 0; ugh_modules[i]->cmds[j].name; ++j) 203 | { 204 | if (0 == strcmp(ugh_modules[i]->cmds[j].name, name)) 205 | { 206 | return &ugh_modules[i]->cmds[j]; 207 | } 208 | } 209 | } 210 | 211 | return NULL; 212 | } 213 | 214 | /* set_*_slot */ 215 | 216 | int ugh_config_set_sint_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 217 | { 218 | char *p = ugh_module_config_get_last(); 219 | int *ep = (int *) (p + cmd->offset); 220 | 221 | *ep = strtol(argv[1], NULL, 10); 222 | 223 | return 0; 224 | } 225 | 226 | int ugh_config_set_uint_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 227 | { 228 | char *p = ugh_module_config_get_last(); 229 | unsigned *ep = (unsigned *) (p + cmd->offset); 230 | 231 | *ep = strtoul(argv[1], NULL, 10); 232 | 233 | return 0; 234 | } 235 | 236 | int ugh_config_set_flag_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 237 | { 238 | char *p = ugh_module_config_get_last(); 239 | unsigned *ep = (unsigned *) (p + cmd->offset); 240 | 241 | *ep = (0 == strcmp("on", argv[1]) ? 1 : 0); 242 | 243 | return 0; 244 | } 245 | 246 | int ugh_config_set_time_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 247 | { 248 | char *p = ugh_module_config_get_last(); 249 | double *ep = (double *) (p + cmd->offset); 250 | 251 | *ep = (double) strtoul(argv[1], &p, 10); 252 | 253 | switch (*p) 254 | { 255 | case 'm': *ep /= 1000; break; 256 | case 'u': *ep /= 1000000; break; 257 | case 'n': *ep /= 1000000000; break; 258 | } 259 | 260 | return 0; 261 | } 262 | 263 | int ugh_config_set_size_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 264 | { 265 | char *p = ugh_module_config_get_last(); 266 | size_t *ep = (size_t *) (p + cmd->offset); 267 | 268 | *ep = (size_t) strtoul(argv[1], &p, 10); 269 | 270 | switch (*p) 271 | { 272 | case 'k': 273 | case 'K': *ep *= 1024; break; 274 | case 'm': 275 | case 'M': *ep *= 1024 * 1024; break; 276 | case 'g': 277 | case 'G': *ep *= 1024 * 1024 * 1024; break; 278 | } 279 | 280 | return 0; 281 | } 282 | 283 | int ugh_config_set_char_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 284 | { 285 | char *p = ugh_module_config_get_last(); 286 | char **ep = (char **) (p + cmd->offset); 287 | 288 | *ep = argv[1]; 289 | 290 | return 0; 291 | } 292 | 293 | int ugh_config_set_strt_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 294 | { 295 | char *p = ugh_module_config_get_last(); 296 | strp ep = (strp) (p + cmd->offset); 297 | 298 | ep->data = argv[1]; 299 | ep->size = strlen(argv[1]); 300 | 301 | return 0; 302 | } 303 | 304 | int ugh_config_set_template_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 305 | { 306 | char *p = ugh_module_config_get_last(); 307 | ugh_template_t *ep = (ugh_template_t *) (p + cmd->offset); 308 | 309 | ugh_template_compile(ep, argv[1], strlen(argv[1]), cfg); 310 | 311 | return 0; 312 | } 313 | 314 | int ugh_config_set_double_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 315 | { 316 | char *p = ugh_module_config_get_last(); 317 | double *ep = (double *) (p + cmd->offset); 318 | 319 | *ep = atof(argv[1]); 320 | 321 | return 0; 322 | } 323 | 324 | -------------------------------------------------------------------------------- /ugh/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __UGH_CONFIG_H__ 2 | #define __UGH_CONFIG_H__ 3 | 4 | #include 5 | #include "aux/memory.h" 6 | #include "aux/hashes.h" 7 | #include "aux/system.h" 8 | #include "aux/string.h" 9 | 10 | #define UGH_CONFIG_LISTEN_BACKLOG 2048 /* TODO move to config */ 11 | #define UGH_CONFIG_SOCKET_TIMEOUT 60.0 /* TODO move to config */ 12 | #define UGH_CONFIG_SUBREQ_TIMEOUT 1.0 13 | #define UGH_CONFIG_SUBREQ_TIMEOUT_CONNECT 1.0 14 | #define UGH_CONFIG_SILENT_TIMEOUT 1.0 /* TODO move to config */ 15 | #define UGH_CONFIG_RESOLVER_TIMEOUT 1.0 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | typedef struct ugh_config 22 | ugh_config_t; 23 | 24 | struct ugh_config 25 | { 26 | aux_pool_t *pool; 27 | 28 | void *vars_hash; /* "var_name" -> var */ 29 | void *upstreams_hash; /* "upstream_name" -> upstream */ 30 | 31 | uint32_t next_upstream; /* TODO one per subrequest OR better - one per upstream */ 32 | 33 | char *beg; 34 | char *pos; 35 | char *end; 36 | 37 | const char *log_error; 38 | const char *log_level; 39 | 40 | const char *listen_host; 41 | unsigned listen_port; 42 | 43 | const char *resolver_host; 44 | double resolver_timeout; 45 | unsigned resolver_cache; 46 | 47 | /* unsigned worker_threads; */ 48 | }; 49 | 50 | int ugh_config_init(ugh_config_t *cfg); /* set default values here */ 51 | int ugh_config_load(ugh_config_t *cfg, const char *filename); 52 | int ugh_config_data(ugh_config_t *cfg, char *data, size_t size); 53 | int ugh_config_free(ugh_config_t *cfg); 54 | 55 | typedef int (*ugh_config_handle_fp)(ugh_config_t *, int, char **); 56 | int ugh_config_parser(ugh_config_t *cfg, ugh_config_handle_fp handle); 57 | int ugh_config_option(ugh_config_t *cfg, int argc, char **argv); 58 | 59 | /* ### ugh_command ### */ 60 | 61 | typedef struct ugh_command 62 | ugh_command_t; 63 | 64 | struct ugh_command 65 | { 66 | const char *name; 67 | int (*handle)(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 68 | 69 | off_t offset; 70 | }; 71 | 72 | ugh_command_t *ugh_command_get(ugh_config_t *cfg, const char *name); 73 | 74 | #define ugh_make_command(name) { #name, ugh_command_##name, 0 } 75 | #define ugh_null_command { NULL, NULL, 0 } 76 | 77 | int ugh_config_set_sint_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 78 | int ugh_config_set_uint_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 79 | int ugh_config_set_flag_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 80 | int ugh_config_set_time_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 81 | int ugh_config_set_size_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 82 | int ugh_config_set_char_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 83 | int ugh_config_set_strt_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 84 | int ugh_config_set_template_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 85 | int ugh_config_set_double_slot(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd); 86 | 87 | #define ugh_make_command_sint(name, type, member) { #name, ugh_config_set_sint_slot, offsetof(type, member) } 88 | #define ugh_make_command_uint(name, type, member) { #name, ugh_config_set_uint_slot, offsetof(type, member) } 89 | #define ugh_make_command_flag(name, type, member) { #name, ugh_config_set_flag_slot, offsetof(type, member) } 90 | #define ugh_make_command_time(name, type, member) { #name, ugh_config_set_time_slot, offsetof(type, member) } 91 | #define ugh_make_command_size(name, type, member) { #name, ugh_config_set_size_slot, offsetof(type, member) } 92 | #define ugh_make_command_char(name, type, member) { #name, ugh_config_set_char_slot, offsetof(type, member) } 93 | #define ugh_make_command_strt(name, type, member) { #name, ugh_config_set_strt_slot, offsetof(type, member) } 94 | #define ugh_make_command_template(name, type, member) { #name, ugh_config_set_template_slot, offsetof(type, member) } 95 | #define ugh_make_command_double(name, type, member) { #name, ugh_config_set_double_slot, offsetof(type, member) } 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif /* __UGH_CONFIG_H__ */ 102 | -------------------------------------------------------------------------------- /ugh/coro/coro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2009 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | */ 40 | 41 | #include "coro.h" 42 | 43 | #include 44 | 45 | /*****************************************************************************/ 46 | /* ucontext/setjmp/asm backends */ 47 | /*****************************************************************************/ 48 | #if CORO_UCONTEXT || CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX || CORO_ASM 49 | 50 | # if CORO_UCONTEXT 51 | # include 52 | # endif 53 | 54 | # if !defined(STACK_ADJUST_PTR) 55 | # if __sgi 56 | /* IRIX is decidedly NON-unix */ 57 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 58 | # define STACK_ADJUST_SIZE(sp,ss) ((ss) - 8) 59 | # elif (__i386__ && CORO_LINUX) || (_M_IX86 && CORO_LOSER) 60 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss)) 61 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 62 | # elif (__amd64__ && CORO_LINUX) || ((_M_AMD64 || _M_IA64) && CORO_LOSER) 63 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 64 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 65 | # else 66 | # define STACK_ADJUST_PTR(sp,ss) (sp) 67 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 68 | # endif 69 | # endif 70 | 71 | # include 72 | 73 | # if CORO_SJLJ 74 | # include 75 | # include 76 | # include 77 | # endif 78 | 79 | static coro_func coro_init_func; 80 | static void *coro_init_arg; 81 | static coro_context *new_coro, *create_coro; 82 | 83 | static void 84 | coro_init (void) 85 | { 86 | volatile coro_func func = coro_init_func; 87 | volatile void *arg = coro_init_arg; 88 | 89 | coro_transfer (new_coro, create_coro); 90 | 91 | func ((void *)arg); 92 | 93 | /* the new coro returned. bad. just abort() for now */ 94 | abort (); 95 | } 96 | 97 | # if CORO_SJLJ 98 | 99 | static volatile int trampoline_done; 100 | 101 | /* trampoline signal handler */ 102 | static void 103 | trampoline (int sig) 104 | { 105 | if (coro_setjmp (new_coro->env)) 106 | coro_init (); /* start it */ 107 | else 108 | trampoline_done = 1; 109 | } 110 | 111 | # endif 112 | 113 | # if CORO_ASM 114 | 115 | asm ( 116 | ".text\n" 117 | ".globl coro_transfer\n" 118 | ".type coro_transfer, @function\n" 119 | "coro_transfer:\n" 120 | /* windows, of course, gives a shit on the amd64 ABI and uses different registers */ 121 | /* http://blogs.msdn.com/freik/archive/2005/03/17/398200.aspx */ 122 | #if __amd64 123 | #define NUM_SAVED 6 124 | "\tpush %rbp\n" 125 | "\tpush %rbx\n" 126 | "\tpush %r12\n" 127 | "\tpush %r13\n" 128 | "\tpush %r14\n" 129 | "\tpush %r15\n" 130 | #if CORO_WIN_TIB 131 | "\tpush %gs:0x0\n" 132 | "\tpush %gs:0x8\n" 133 | "\tpush %gs:0xc\n" 134 | #endif 135 | "\tmov %rsp, (%rdi)\n" 136 | "\tmov (%rsi), %rsp\n" 137 | #if CORO_WIN_TIB 138 | "\tpop %gs:0xc\n" 139 | "\tpop %gs:0x8\n" 140 | "\tpop %gs:0x0\n" 141 | #endif 142 | "\tpop %r15\n" 143 | "\tpop %r14\n" 144 | "\tpop %r13\n" 145 | "\tpop %r12\n" 146 | "\tpop %rbx\n" 147 | "\tpop %rbp\n" 148 | #elif __i386 149 | #define NUM_SAVED 4 150 | "\tpush %ebp\n" 151 | "\tpush %ebx\n" 152 | "\tpush %esi\n" 153 | "\tpush %edi\n" 154 | #if CORO_WIN_TIB 155 | "\tpush %fs:0\n" 156 | "\tpush %fs:4\n" 157 | "\tpush %fs:8\n" 158 | #endif 159 | "\tmov %esp, (%eax)\n" 160 | "\tmov (%edx), %esp\n" 161 | #if CORO_WIN_TIB 162 | "\tpop %fs:8\n" 163 | "\tpop %fs:4\n" 164 | "\tpop %fs:0\n" 165 | #endif 166 | "\tpop %edi\n" 167 | "\tpop %esi\n" 168 | "\tpop %ebx\n" 169 | "\tpop %ebp\n" 170 | #else 171 | #error unsupported architecture 172 | #endif 173 | "\tret\n" 174 | ); 175 | 176 | # endif 177 | 178 | void 179 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize) 180 | { 181 | coro_context nctx; 182 | # if CORO_SJLJ 183 | stack_t ostk, nstk; 184 | struct sigaction osa, nsa; 185 | sigset_t nsig, osig; 186 | # endif 187 | 188 | if (!coro) 189 | return; 190 | 191 | coro_init_func = coro; 192 | coro_init_arg = arg; 193 | 194 | new_coro = ctx; 195 | create_coro = &nctx; 196 | 197 | # if CORO_SJLJ 198 | /* we use SIGUSR2. first block it, then fiddle with it. */ 199 | 200 | sigemptyset (&nsig); 201 | sigaddset (&nsig, SIGUSR2); 202 | sigprocmask (SIG_BLOCK, &nsig, &osig); 203 | 204 | nsa.sa_handler = trampoline; 205 | sigemptyset (&nsa.sa_mask); 206 | nsa.sa_flags = SA_ONSTACK; 207 | 208 | if (sigaction (SIGUSR2, &nsa, &osa)) 209 | { 210 | perror ("sigaction"); 211 | abort (); 212 | } 213 | 214 | /* set the new stack */ 215 | nstk.ss_sp = STACK_ADJUST_PTR (sptr, ssize); /* yes, some platforms (IRIX) get this wrong. */ 216 | nstk.ss_size = STACK_ADJUST_SIZE (sptr, ssize); 217 | nstk.ss_flags = 0; 218 | 219 | if (sigaltstack (&nstk, &ostk) < 0) 220 | { 221 | perror ("sigaltstack"); 222 | abort (); 223 | } 224 | 225 | trampoline_done = 0; 226 | kill (getpid (), SIGUSR2); 227 | sigfillset (&nsig); sigdelset (&nsig, SIGUSR2); 228 | 229 | while (!trampoline_done) 230 | sigsuspend (&nsig); 231 | 232 | sigaltstack (0, &nstk); 233 | nstk.ss_flags = SS_DISABLE; 234 | if (sigaltstack (&nstk, 0) < 0) 235 | perror ("sigaltstack"); 236 | 237 | sigaltstack (0, &nstk); 238 | if (~nstk.ss_flags & SS_DISABLE) 239 | abort (); 240 | 241 | if (~ostk.ss_flags & SS_DISABLE) 242 | sigaltstack (&ostk, 0); 243 | 244 | sigaction (SIGUSR2, &osa, 0); 245 | sigprocmask (SIG_SETMASK, &osig, 0); 246 | 247 | # elif CORO_LOSER 248 | 249 | coro_setjmp (ctx->env); 250 | #if __CYGWIN__ && __i386 251 | ctx->env[8] = (long) coro_init; 252 | ctx->env[7] = (long) ((char *)sptr + ssize) - sizeof (long); 253 | #elif __CYGWIN__ && __x86_64 254 | ctx->env[7] = (long) coro_init; 255 | ctx->env[6] = (long) ((char *)sptr + ssize) - sizeof (long); 256 | #elif defined(__MINGW32__) 257 | ctx->env[5] = (long) coro_init; 258 | ctx->env[4] = (long) ((char *)sptr + ssize) - sizeof (long); 259 | #elif defined(_M_IX86) 260 | ((_JUMP_BUFFER *)&ctx->env)->Eip = (long) coro_init; 261 | ((_JUMP_BUFFER *)&ctx->env)->Esp = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 262 | #elif defined(_M_AMD64) 263 | ((_JUMP_BUFFER *)&ctx->env)->Rip = (__int64) coro_init; 264 | ((_JUMP_BUFFER *)&ctx->env)->Rsp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 265 | #elif defined(_M_IA64) 266 | ((_JUMP_BUFFER *)&ctx->env)->StIIP = (__int64) coro_init; 267 | ((_JUMP_BUFFER *)&ctx->env)->IntSp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 268 | #else 269 | #error "microsoft libc or architecture not supported" 270 | #endif 271 | 272 | # elif CORO_LINUX 273 | 274 | coro_setjmp (ctx->env); 275 | #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (JB_PC) && defined (JB_SP) 276 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 277 | ctx->env[0].__jmpbuf[JB_SP] = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 278 | #elif __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (__mc68000__) 279 | ctx->env[0].__jmpbuf[0].__aregs[0] = (long int)coro_init; 280 | ctx->env[0].__jmpbuf[0].__sp = (int *) ((char *)sptr + ssize) - sizeof (long); 281 | #elif defined (__GNU_LIBRARY__) && defined (__i386__) 282 | ctx->env[0].__jmpbuf[0].__pc = (char *) coro_init; 283 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 284 | #elif defined (__GNU_LIBRARY__) && defined (__amd64__) 285 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 286 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 287 | #else 288 | #error "linux libc or architecture not supported" 289 | #endif 290 | 291 | # elif CORO_IRIX 292 | 293 | coro_setjmp (ctx->env, 0); 294 | ctx->env[JB_PC] = (__uint64_t)coro_init; 295 | ctx->env[JB_SP] = (__uint64_t)STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 296 | 297 | # elif CORO_ASM 298 | 299 | ctx->sp = (void **)(ssize + (char *)sptr); 300 | *--ctx->sp = (void *)abort; /* needed for alignment only */ 301 | *--ctx->sp = (void *)coro_init; 302 | 303 | #if CORO_WIN_TIB 304 | *--ctx->sp = 0; /* ExceptionList */ 305 | *--ctx->sp = (char *)sptr + ssize; /* StackBase */ 306 | *--ctx->sp = sptr; /* StackLimit */ 307 | #endif 308 | 309 | ctx->sp -= NUM_SAVED; 310 | 311 | # elif CORO_UCONTEXT 312 | 313 | getcontext (&(ctx->uc)); 314 | 315 | ctx->uc.uc_link = 0; 316 | ctx->uc.uc_stack.ss_sp = sptr; 317 | ctx->uc.uc_stack.ss_size = (size_t)ssize; 318 | ctx->uc.uc_stack.ss_flags = 0; 319 | 320 | makecontext (&(ctx->uc), (void (*)())coro_init, 0); 321 | 322 | # endif 323 | 324 | coro_transfer (create_coro, new_coro); 325 | } 326 | 327 | /*****************************************************************************/ 328 | /* pthread backend */ 329 | /*****************************************************************************/ 330 | #elif CORO_PTHREAD 331 | 332 | /* this mutex will be locked by the running coroutine */ 333 | pthread_mutex_t coro_mutex = PTHREAD_MUTEX_INITIALIZER; 334 | 335 | struct coro_init_args 336 | { 337 | coro_func func; 338 | void *arg; 339 | coro_context *self, *main; 340 | }; 341 | 342 | static pthread_t null_tid; 343 | 344 | /* I'd so love to cast pthread_mutex_unlock to void (*)(void *)... */ 345 | static void 346 | mutex_unlock_wrapper (void *arg) 347 | { 348 | pthread_mutex_unlock ((pthread_mutex_t *)arg); 349 | } 350 | 351 | static void * 352 | coro_init (void *args_) 353 | { 354 | struct coro_init_args *args = (struct coro_init_args *)args_; 355 | coro_func func = args->func; 356 | void *arg = args->arg; 357 | 358 | pthread_mutex_lock (&coro_mutex); 359 | 360 | /* we try to be good citizens and use deferred cancellation and cleanup handlers */ 361 | pthread_cleanup_push (mutex_unlock_wrapper, &coro_mutex); 362 | coro_transfer (args->self, args->main); 363 | func (arg); 364 | pthread_cleanup_pop (1); 365 | 366 | return 0; 367 | } 368 | 369 | void 370 | coro_transfer (coro_context *prev, coro_context *next) 371 | { 372 | pthread_cond_signal (&next->cv); 373 | pthread_cond_wait (&prev->cv, &coro_mutex); 374 | #if __FreeBSD__ /* freebsd is of course broken and needs manual testcancel calls... yay... */ 375 | pthread_testcancel (); 376 | #endif 377 | } 378 | 379 | void 380 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize) 381 | { 382 | static coro_context nctx; 383 | static int once; 384 | 385 | if (!once) 386 | { 387 | once = 1; 388 | 389 | pthread_mutex_lock (&coro_mutex); 390 | pthread_cond_init (&nctx.cv, 0); 391 | null_tid = pthread_self (); 392 | } 393 | 394 | pthread_cond_init (&ctx->cv, 0); 395 | 396 | if (coro) 397 | { 398 | pthread_attr_t attr; 399 | struct coro_init_args args; 400 | 401 | args.func = coro; 402 | args.arg = arg; 403 | args.self = ctx; 404 | args.main = &nctx; 405 | 406 | pthread_attr_init (&attr); 407 | #if __UCLIBC__ 408 | /* exists, but is borked */ 409 | /*pthread_attr_setstacksize (&attr, (size_t)ssize);*/ 410 | #else 411 | pthread_attr_setstack (&attr, sptr, (size_t)ssize); 412 | #endif 413 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); 414 | pthread_create (&ctx->id, &attr, coro_init, &args); 415 | 416 | coro_transfer (args.main, args.self); 417 | } 418 | else 419 | ctx->id = null_tid; 420 | } 421 | 422 | void 423 | coro_destroy (coro_context *ctx) 424 | { 425 | if (!pthread_equal (ctx->id, null_tid)) 426 | { 427 | pthread_cancel (ctx->id); 428 | pthread_mutex_unlock (&coro_mutex); 429 | pthread_join (ctx->id, 0); 430 | pthread_mutex_lock (&coro_mutex); 431 | } 432 | 433 | pthread_cond_destroy (&ctx->cv); 434 | } 435 | 436 | #else 437 | # error unsupported backend 438 | #endif 439 | 440 | -------------------------------------------------------------------------------- /ugh/coro/coro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2009 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | * 40 | * This coroutine library is very much stripped down. You should either 41 | * build your own process abstraction using it or - better - just use GNU 42 | * Portable Threads, http://www.gnu.org/software/pth/. 43 | * 44 | */ 45 | 46 | /* 47 | * 2006-10-26 Include stddef.h on OS X to work around one of its bugs. 48 | * Reported by Michael_G_Schwern. 49 | * 2006-11-26 Use _setjmp instead of setjmp on GNU/Linux. 50 | * 2007-04-27 Set unwind frame info if gcc 3+ and ELF is detected. 51 | * Use _setjmp instead of setjmp on _XOPEN_SOURCE >= 600. 52 | * 2007-05-02 Add assembly versions for x86 and amd64 (to avoid reliance 53 | * on SIGUSR2 and sigaltstack in Crossfire). 54 | * 2008-01-21 Disable CFI usage on anything but GNU/Linux. 55 | * 2008-03-02 Switched to 2-clause BSD license with GPL exception. 56 | * 2008-04-04 New (but highly unrecommended) pthreads backend. 57 | * 2008-04-24 Reinstate CORO_LOSER (had wrong stack adjustments). 58 | * 2008-10-30 Support assembly method on x86 with and without frame pointer. 59 | * 2008-11-03 Use a global asm statement for CORO_ASM, idea by pippijn. 60 | * 2008-11-05 Hopefully fix misaligned stacks with CORO_ASM/SETJMP. 61 | * 2008-11-07 rbp wasn't saved in CORO_ASM on x86_64. 62 | * introduce coro_destroy, which is a nop except for pthreads. 63 | * speed up CORO_PTHREAD. Do no longer leak threads either. 64 | * coro_create now allows one to create source coro_contexts. 65 | * do not rely on makecontext passing a void * correctly. 66 | * try harder to get _setjmp/_longjmp. 67 | * major code cleanup/restructuring. 68 | * 2008-11-10 the .cfi hacks are no longer needed. 69 | * 2008-11-16 work around a freebsd pthread bug. 70 | * 2008-11-19 define coro_*jmp symbols for easier porting. 71 | * 2009-06-23 tentative win32-backend support for mingw32 (Yasuhiro Matsumoto). 72 | * 2010-12-03 tentative support for uclibc (which lacks all sorts of things). 73 | */ 74 | 75 | #ifndef CORO_H 76 | #define CORO_H 77 | 78 | #if __cplusplus 79 | extern "C" { 80 | #endif 81 | 82 | #define CORO_VERSION 2 83 | 84 | /* 85 | * Changes since API version 1: 86 | * replaced bogus -DCORO_LOOSE with gramatically more correct -DCORO_LOSER 87 | */ 88 | 89 | /* 90 | * This library consists of only three files 91 | * coro.h, coro.c and LICENSE (and optionally README) 92 | * 93 | * It implements what is known as coroutines, in a hopefully 94 | * portable way. At the moment you have to define which kind 95 | * of implementation flavour you want: 96 | * 97 | * -DCORO_UCONTEXT 98 | * 99 | * This flavour uses SUSv2's get/set/swap/makecontext functions that 100 | * unfortunately only newer unices support. 101 | * 102 | * -DCORO_SJLJ 103 | * 104 | * This flavour uses SUSv2's setjmp/longjmp and sigaltstack functions to 105 | * do it's job. Coroutine creation is much slower than UCONTEXT, but 106 | * context switching is often a bit cheaper. It should work on almost 107 | * all unices. 108 | * 109 | * -DCORO_LINUX 110 | * 111 | * Old GNU/Linux systems (<= glibc-2.1) only work with this implementation 112 | * (it is very fast and therefore recommended over other methods, but 113 | * doesn't work with anything newer). 114 | * 115 | * -DCORO_LOSER 116 | * 117 | * Microsoft's highly proprietary platform doesn't support sigaltstack, and 118 | * this automatically selects a suitable workaround for this platform. 119 | * (untested) 120 | * 121 | * -DCORO_IRIX 122 | * 123 | * SGI's version of Microsoft's NT ;) 124 | * 125 | * -DCORO_ASM 126 | * 127 | * Handcoded assembly, known to work only on a few architectures/ABI: 128 | * GCC + x86/IA32 and amd64/x86_64 + GNU/Linux and a few BSDs. 129 | * 130 | * -DCORO_PTHREAD 131 | * 132 | * Use the pthread API. You have to provide and -lpthread. 133 | * This is likely the slowest backend, and it also does not support fork(), 134 | * so avoid it at all costs. 135 | * 136 | * If you define neither of these symbols, coro.h will try to autodetect 137 | * the model. This currently works for CORO_LOSER only. For the other 138 | * alternatives you should check (e.g. using autoconf) and define the 139 | * following symbols: HAVE_UCONTEXT_H / HAVE_SETJMP_H / HAVE_SIGALTSTACK. 140 | */ 141 | 142 | /* 143 | * This is the type for the initialization function of a new coroutine. 144 | */ 145 | typedef void (*coro_func)(void *); 146 | 147 | /* 148 | * A coroutine state is saved in the following structure. Treat it as an 149 | * opaque type. errno and sigmask might be saved, but don't rely on it, 150 | * implement your own switching primitive if you need that. 151 | */ 152 | typedef struct coro_context coro_context; 153 | 154 | /* 155 | * This function creates a new coroutine. Apart from a pointer to an 156 | * uninitialised coro_context, it expects a pointer to the entry function 157 | * and the single pointer value that is given to it as argument. 158 | * 159 | * Allocating/deallocating the stack is your own responsibility. 160 | * 161 | * As a special case, if coro, arg, sptr and ssize are all zero, 162 | * then an "empty" coro_context will be created that is suitable 163 | * as an initial source for coro_transfer. 164 | * 165 | * This function is not reentrant, but putting a mutex around it 166 | * will work. 167 | */ 168 | void coro_create (coro_context *ctx, /* an uninitialised coro_context */ 169 | coro_func coro, /* the coroutine code to be executed */ 170 | void *arg, /* a single pointer passed to the coro */ 171 | void *sptr, /* start of stack area */ 172 | long ssize); /* size of stack area */ 173 | 174 | /* 175 | * The following prototype defines the coroutine switching function. It is 176 | * usually implemented as a macro, so watch out. 177 | * 178 | * This function is thread-safe and reentrant. 179 | */ 180 | #if 0 181 | void coro_transfer (coro_context *prev, coro_context *next); 182 | #endif 183 | 184 | /* 185 | * The following prototype defines the coroutine destroy function. It is 186 | * usually implemented as a macro, so watch out. It also serves 187 | * no purpose unless you want to use the CORO_PTHREAD backend, 188 | * where it is used to clean up the thread. You are responsible 189 | * for freeing the stack and the context itself. 190 | * 191 | * This function is thread-safe and reentrant. 192 | */ 193 | #if 0 194 | void coro_destroy (coro_context *ctx); 195 | #endif 196 | 197 | /* 198 | * That was it. No other user-visible functions are implemented here. 199 | */ 200 | 201 | /*****************************************************************************/ 202 | 203 | #if !defined(CORO_LOSER) && !defined(CORO_UCONTEXT) \ 204 | && !defined(CORO_SJLJ) && !defined(CORO_LINUX) \ 205 | && !defined(CORO_IRIX) && !defined(CORO_ASM) \ 206 | && !defined(CORO_PTHREAD) 207 | # if defined(WINDOWS) 208 | # define CORO_LOSER 1 /* you don't win with windoze */ 209 | # elif defined(__linux) && (defined(__x86) || defined (__amd64)) 210 | # define CORO_ASM 1 211 | # elif defined(HAVE_UCONTEXT_H) 212 | # define CORO_UCONTEXT 1 213 | # elif defined(HAVE_SETJMP_H) && defined(HAVE_SIGALTSTACK) 214 | # define CORO_SJLJ 1 215 | # else 216 | error unknown or unsupported architecture 217 | # endif 218 | #endif 219 | 220 | /*****************************************************************************/ 221 | 222 | #if CORO_UCONTEXT 223 | 224 | # include 225 | 226 | struct coro_context { 227 | ucontext_t uc; 228 | }; 229 | 230 | # define coro_transfer(p,n) swapcontext (&((p)->uc), &((n)->uc)) 231 | # define coro_destroy(ctx) (void *)(ctx) 232 | 233 | #elif CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX 234 | 235 | # if defined(CORO_LINUX) && !defined(_GNU_SOURCE) 236 | # define _GNU_SOURCE /* for linux libc */ 237 | # endif 238 | 239 | # if !CORO_LOSER 240 | # include 241 | # endif 242 | 243 | /* solaris is hopelessly borked, it expands _XOPEN_UNIX to nothing */ 244 | # if __sun 245 | # undef _XOPEN_UNIX 246 | # define _XOPEN_UNIX 1 247 | # endif 248 | 249 | # include 250 | 251 | # if _XOPEN_UNIX > 0 || defined (_setjmp) 252 | # define coro_jmp_buf jmp_buf 253 | # define coro_setjmp(env) _setjmp (env) 254 | # define coro_longjmp(env) _longjmp ((env), 1) 255 | # elif CORO_LOSER 256 | # define coro_jmp_buf jmp_buf 257 | # define coro_setjmp(env) setjmp (env) 258 | # define coro_longjmp(env) longjmp ((env), 1) 259 | # else 260 | # define coro_jmp_buf sigjmp_buf 261 | # define coro_setjmp(env) sigsetjmp (env, 0) 262 | # define coro_longjmp(env) siglongjmp ((env), 1) 263 | # endif 264 | 265 | struct coro_context { 266 | coro_jmp_buf env; 267 | }; 268 | 269 | # define coro_transfer(p,n) do { if (!coro_setjmp ((p)->env)) coro_longjmp ((n)->env); } while (0) 270 | # define coro_destroy(ctx) (void *)(ctx) 271 | 272 | #elif CORO_ASM 273 | 274 | struct coro_context { 275 | void **sp; /* must be at offset 0 */ 276 | }; 277 | 278 | void __attribute__ ((__noinline__, __regparm__(2))) 279 | coro_transfer (coro_context *prev, coro_context *next); 280 | 281 | # define coro_destroy(ctx) (void *)(ctx) 282 | 283 | #elif CORO_PTHREAD 284 | 285 | # include 286 | 287 | extern pthread_mutex_t coro_mutex; 288 | 289 | struct coro_context { 290 | pthread_cond_t cv; 291 | pthread_t id; 292 | }; 293 | 294 | void coro_transfer (coro_context *prev, coro_context *next); 295 | void coro_destroy (coro_context *ctx); 296 | 297 | #endif 298 | 299 | #if __cplusplus 300 | } 301 | #endif 302 | 303 | #endif 304 | 305 | -------------------------------------------------------------------------------- /ugh/coro_ucontext/coro_ucontext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "coro_ucontext.h" 5 | 6 | static coro_func coro_init_func; 7 | static void *coro_init_arg; 8 | static coro_context *new_coro, *create_coro; 9 | 10 | static void 11 | coro_init (void) 12 | { 13 | volatile coro_func func = coro_init_func; 14 | volatile void *arg = coro_init_arg; 15 | 16 | coro_transfer (new_coro, create_coro); 17 | 18 | func ((void *)arg); 19 | 20 | /* the new coro returned. bad. just abort() for now */ 21 | /* abort (); */ 22 | } 23 | 24 | void 25 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize, coro_context *link) 26 | { 27 | coro_context nctx; 28 | 29 | if (!coro) 30 | return; 31 | 32 | coro_init_func = coro; 33 | coro_init_arg = arg; 34 | 35 | new_coro = ctx; 36 | create_coro = &nctx; 37 | 38 | #if 1 39 | getcontext (&(ctx->uc)); 40 | 41 | ctx->uc.uc_link = &(link->uc); 42 | ctx->uc.uc_stack.ss_sp = sptr; 43 | ctx->uc.uc_stack.ss_size = (size_t)ssize; 44 | ctx->uc.uc_stack.ss_flags = 0; 45 | 46 | makecontext (&(ctx->uc), (void (*)())coro_init, 0); 47 | #endif 48 | 49 | coro_transfer (create_coro, new_coro); 50 | } 51 | 52 | #if 0 53 | // ------------------------ 54 | 55 | #include 56 | 57 | coro_context ctx_main; 58 | coro_context ctx_func; 59 | 60 | void func(void *arg) 61 | { 62 | printf("func: switching to main\n"); 63 | coro_transfer(&ctx_func, &ctx_main); 64 | 65 | printf("func: returning\n"); 66 | } 67 | 68 | int main(int argc, char **argv) 69 | { 70 | char stack [4096]; 71 | 72 | coro_create(&ctx_func, func, NULL, stack, 4096, &ctx_main); 73 | 74 | printf("main: switching to func\n"); 75 | coro_transfer(&ctx_main, &ctx_func); 76 | 77 | printf("main: switching to func\n"); 78 | coro_transfer(&ctx_main, &ctx_func); 79 | 80 | printf("main: returned\n"); 81 | 82 | return 0; 83 | } 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /ugh/coro_ucontext/coro_ucontext.h: -------------------------------------------------------------------------------- 1 | #ifndef __CORO_UCONTEXT_H__ 2 | #define __CORO_UCONTEXT_H__ 3 | 4 | typedef void (*coro_func)(void *); 5 | 6 | typedef struct coro_context coro_context; 7 | 8 | void coro_create (coro_context *ctx, /* an uninitialised coro_context */ 9 | coro_func coro, /* the coroutine code to be executed */ 10 | void *arg, /* a single pointer passed to the coro */ 11 | void *sptr, /* start of stack area */ 12 | long ssize, /* size of stack area */ 13 | coro_context *link); 14 | 15 | #include 16 | 17 | struct coro_context { 18 | ucontext_t uc; 19 | }; 20 | 21 | #define coro_transfer(p,n) swapcontext (&((p)->uc), &((n)->uc)) 22 | #define coro_destroy(ctx) (void *)(ctx) 23 | 24 | #endif /* __CORO_UCONTEXT_H__ */ 25 | -------------------------------------------------------------------------------- /ugh/daemon.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | struct ev_loop *loop; 4 | 5 | #if 1 6 | ugh_module_handle_t ugh_module_handles [UGH_MODULE_HANDLES_MAX]; 7 | size_t ugh_module_handles_size = 0; 8 | 9 | int ugh_module_handle_all(ugh_client_t *c) 10 | { 11 | size_t i; 12 | int status = UGH_HTTP_OK; 13 | 14 | for (i = 0; i < ugh_module_handles_size; ++i) 15 | { 16 | if (NULL == ugh_module_handles[i].handle) continue; 17 | 18 | int tmp_status = ugh_module_handles[i].handle(c, ugh_module_handles[i].config, &c->bufs[i]); 19 | 20 | /* XXX UGH_AGAIN means here, that module was not supposed to be called 21 | * OR that the module itself didn't want to change response status 22 | */ 23 | #if 1 24 | if (tmp_status != UGH_AGAIN) 25 | { 26 | status = tmp_status; 27 | } 28 | #endif 29 | } 30 | 31 | return status; 32 | } 33 | #endif 34 | 35 | #if 1 36 | coro_context ctx_main; 37 | unsigned char is_main_coro = 1; 38 | #endif 39 | 40 | void ugh_wcb_silent(EV_P_ ev_timer *w, int tev) 41 | { 42 | if (0 != aux_terminate) 43 | { 44 | ev_timer_stop(EV_A_ w); 45 | ev_break(EV_A_ EVBREAK_ALL); 46 | return; 47 | } 48 | 49 | if (0 != aux_rotatelog) 50 | { 51 | ugh_daemon_t *d; 52 | 53 | aux_rotatelog = 0; 54 | d = aux_memberof(ugh_daemon_t, wev_silent, w); 55 | 56 | if (-1 == log_rotate(d->cfg.log_error)) 57 | { 58 | log_warn("log_rotate(%s) (%d: %s)", d->cfg.log_error, 59 | errno, aux_strerror(errno)); 60 | } 61 | } 62 | } 63 | 64 | int ugh_daemon_exec(const char *cfg_filename, unsigned daemon) 65 | { 66 | int rc; 67 | ugh_daemon_t d; 68 | 69 | aux_clrptr(&d); 70 | 71 | /* XXX this is temporary call for the function to avoid linking 72 | * problems when modules try to use it */ 73 | aux_random_init(); 74 | 75 | loop = ev_default_loop(0); 76 | 77 | /* TODO make it possible to set default values for each module config and 78 | * global config via ugh_make_command macro (by setting all this at 79 | * module_handle_add stage) 80 | */ 81 | rc = ugh_config_init(&d.cfg); 82 | if (0 > rc) return -1; 83 | 84 | rc = ugh_config_load(&d.cfg, cfg_filename); 85 | if (0 > rc) return -1; 86 | 87 | if (daemon) 88 | { 89 | rc = log_create(d.cfg.log_error, log_levels(d.cfg.log_level)); 90 | if (0 > rc) return -1; 91 | } 92 | else 93 | { 94 | log_level = log_levels(d.cfg.log_level); 95 | } 96 | 97 | rc = ugh_resolver_init(&d.resolver, &d.cfg); 98 | if (0 > rc) return -1; 99 | 100 | size_t i; 101 | 102 | for (i = 0; i < ugh_module_handles_size; ++i) 103 | { 104 | if (ugh_module_handles[i].module->init) 105 | { 106 | rc = ugh_module_handles[i].module->init(&d.cfg, ugh_module_handles[i].config); 107 | if (0 > rc) return -1; 108 | } 109 | } 110 | 111 | rc = ugh_server_listen(&d.srv, &d.cfg, &d.resolver); 112 | if (0 > rc) return -1; 113 | 114 | ev_timer_init(&d.wev_silent, ugh_wcb_silent, 0, UGH_CONFIG_SILENT_TIMEOUT); 115 | ev_timer_again(loop, &d.wev_silent); 116 | 117 | ev_run(loop, 0); 118 | 119 | ugh_server_enough(&d.srv); 120 | 121 | for (i = 0; i < ugh_module_handles_size; ++i) 122 | { 123 | if (ugh_module_handles[i].module->free) 124 | { 125 | rc = ugh_module_handles[i].module->free(&d.cfg, ugh_module_handles[i].config); 126 | if (0 > rc) return -1; 127 | } 128 | } 129 | 130 | ugh_resolver_free(&d.resolver); 131 | 132 | ugh_config_free(&d.cfg); 133 | 134 | return 0; 135 | } 136 | 137 | int main(int argc, char **argv) 138 | { 139 | int rc; 140 | aux_getopt_t opt; 141 | 142 | rc = aux_getopt_parse(argc, argv, &opt); 143 | 144 | if (0 > rc || NULL == opt.config) 145 | { 146 | aux_getopt_usage(argc, argv); 147 | return -1; 148 | } 149 | 150 | rc = aux_daemon_load(&opt); 151 | if (0 > rc) return -1; 152 | 153 | rc = ugh_daemon_exec(opt.config, opt.daemon); 154 | if (0 > rc) return -1; 155 | 156 | rc = aux_daemon_stop(&opt); 157 | if (0 > rc) return -1; 158 | 159 | return 0; 160 | } 161 | 162 | -------------------------------------------------------------------------------- /ugh/module.c: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | 3 | extern ugh_module_t ugh_module_core; 4 | extern ugh_module_t ugh_module_import; 5 | extern ugh_module_t ugh_module_map; 6 | extern ugh_module_t ugh_module_set; 7 | extern ugh_module_t ugh_module_return; 8 | extern ugh_module_t ugh_module_add_header; 9 | extern ugh_module_t ugh_module_proxy; 10 | extern ugh_module_t ugh_module_upstream; 11 | extern ugh_module_t ugh_module_push_subscriber; 12 | extern ugh_module_t ugh_module_push_publisher; 13 | extern ugh_module_t ugh_module_push_pass; 14 | 15 | ugh_module_t *ugh_modules [UGH_MODULES_MAX] = { 16 | &ugh_module_core, 17 | &ugh_module_import, 18 | &ugh_module_map, 19 | &ugh_module_set, 20 | &ugh_module_return, 21 | &ugh_module_add_header, 22 | &ugh_module_proxy, 23 | &ugh_module_upstream, 24 | &ugh_module_push_subscriber, 25 | &ugh_module_push_publisher, 26 | &ugh_module_push_pass, 27 | NULL, 28 | NULL, 29 | NULL, 30 | NULL, 31 | NULL 32 | }; 33 | 34 | size_t ugh_modules_size = 11; 35 | 36 | int ugh_module_add(ugh_module_t *m) 37 | { 38 | if (ugh_modules_size >= UGH_MODULES_MAX) 39 | { 40 | return -1; 41 | } 42 | 43 | ugh_modules[ugh_modules_size++] = m; 44 | 45 | return 0; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /ugh/module.h: -------------------------------------------------------------------------------- 1 | #ifndef __UGH_MODULE_H__ 2 | #define __UGH_MODULE_H__ 3 | 4 | #include "config.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct ugh_module 11 | ugh_module_t; 12 | 13 | struct ugh_module 14 | { 15 | ugh_command_t *cmds; 16 | 17 | int (*init)(ugh_config_t *cfg, void *data); /* data is module conf */ 18 | int (*free)(ugh_config_t *cfg, void *data); /* data is module conf */ 19 | }; 20 | 21 | #define UGH_MODULES_MAX 16 22 | 23 | extern ugh_module_t *ugh_modules [UGH_MODULES_MAX]; 24 | extern size_t ugh_modules_size; 25 | 26 | int ugh_module_add(ugh_module_t *m); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif /* __UGH_MODULE_H__ */ 33 | -------------------------------------------------------------------------------- /ugh/module_add_header.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | ugh_template_t key; 6 | ugh_template_t value; 7 | } ugh_module_add_header_conf_t; 8 | 9 | extern ugh_module_t ugh_module_add_header; 10 | 11 | static 12 | int ugh_module_add_header_handle(ugh_client_t *c, void *data, strp body) 13 | { 14 | ugh_module_add_header_conf_t *conf = (ugh_module_add_header_conf_t *) data; 15 | 16 | strp key = ugh_template_execute(&conf->key, c); 17 | strp value = ugh_template_execute(&conf->value, c); 18 | 19 | ugh_client_header_out_set(c, key->data, key->size, value->data, value->size); 20 | 21 | return UGH_AGAIN; 22 | } 23 | 24 | static 25 | int ugh_command_add_header(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 26 | { 27 | ugh_module_add_header_conf_t *conf; 28 | 29 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 30 | if (NULL == conf) return -1; 31 | 32 | ugh_template_compile(&conf->key, argv[1], strlen(argv[1]), cfg); 33 | ugh_template_compile(&conf->value, argv[2], strlen(argv[2]), cfg); 34 | 35 | ugh_module_handle_add(ugh_module_add_header, conf, ugh_module_add_header_handle); 36 | 37 | return 0; 38 | } 39 | 40 | static ugh_command_t ugh_module_add_header_cmds [] = 41 | { 42 | ugh_make_command(add_header), 43 | ugh_null_command 44 | }; 45 | 46 | ugh_module_t ugh_module_add_header = 47 | { 48 | ugh_module_add_header_cmds, 49 | NULL, 50 | NULL 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /ugh/module_core.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | static 4 | int ugh_command_error_log(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 5 | { 6 | cfg->log_error = argv[1]; 7 | cfg->log_level = (3 > argc) ? "error" : argv[2]; 8 | 9 | return 0; 10 | } 11 | 12 | static 13 | int ugh_command_listen(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 14 | { 15 | char *port = strchr(argv[1], ':'); 16 | 17 | if (NULL == port) 18 | { 19 | cfg->listen_host = "0.0.0.0"; 20 | cfg->listen_port = strtoul(argv[1], NULL, 10); 21 | } 22 | else 23 | { 24 | *port++ = 0; 25 | 26 | cfg->listen_host = argv[1]; 27 | cfg->listen_port = strtoul(port, NULL, 10); 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | static 34 | int ugh_command_resolver(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 35 | { 36 | cfg->resolver_host = argv[1]; 37 | 38 | return 0; 39 | } 40 | 41 | static 42 | int ugh_command_resolver_timeout(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 43 | { 44 | char *p; 45 | 46 | cfg->resolver_timeout = (double) strtoul(argv[1], &p, 10); 47 | 48 | switch (*p) 49 | { 50 | case 'm': cfg->resolver_timeout /= 1000; break; 51 | case 'u': cfg->resolver_timeout /= 1000000; break; 52 | case 'n': cfg->resolver_timeout /= 1000000000; break; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | static 59 | int ugh_command_resolver_cache(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 60 | { 61 | cfg->resolver_cache = (0 == strcmp("on", argv[1]) ? 1 : 0); 62 | 63 | return 0; 64 | } 65 | 66 | #if 0 67 | static 68 | int ugh_command_worker_threads(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 69 | { 70 | cfg->worker_threads = strtoul(argv[1], NULL, 10); 71 | 72 | return 0; 73 | } 74 | #endif 75 | 76 | static ugh_command_t ugh_module_core_cmds [] = 77 | { 78 | ugh_make_command(error_log), 79 | ugh_make_command(listen), 80 | ugh_make_command(resolver), 81 | ugh_make_command(resolver_timeout), 82 | ugh_make_command(resolver_cache), 83 | /* ugh_make_command(worker_threads), */ 84 | ugh_null_command 85 | }; 86 | 87 | ugh_module_t ugh_module_core = 88 | { 89 | ugh_module_core_cmds, 90 | NULL, 91 | NULL 92 | }; 93 | 94 | -------------------------------------------------------------------------------- /ugh/module_import.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ugh.h" 3 | 4 | /* TODO dlclose */ 5 | 6 | static 7 | int ugh_command_import(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 8 | { 9 | char name [1024]; 10 | char pbuf [PATH_MAX], *path; 11 | 12 | snprintf(name, 1024, "ugh_module_%s", argv[1]); 13 | 14 | if (2 < argc) 15 | { 16 | path = argv[2]; 17 | } 18 | else 19 | { 20 | snprintf(pbuf, PATH_MAX, UGH_MODULE_PREFIX "%s" UGH_MODULE_SUFFIX, argv[1]); 21 | path = pbuf; 22 | } 23 | 24 | void *handle = dlopen(path, RTLD_NOW); 25 | 26 | if (NULL == handle) 27 | { 28 | log_emerg("dlopen(%s, RTLD_NOW): %s", path, dlerror()); 29 | return -1; 30 | } 31 | 32 | char *dl_err = dlerror(); /* clear error before calling dlsym() */ 33 | 34 | ugh_module_t *module = (ugh_module_t *) dlsym(handle, name); 35 | 36 | if (NULL != (dl_err = dlerror())) 37 | { 38 | log_emerg("dl_err = %s", dl_err); 39 | return -1; 40 | } 41 | 42 | ugh_module_add(module); 43 | 44 | return 0; 45 | } 46 | 47 | static ugh_command_t ugh_module_import_cmds [] = 48 | { 49 | ugh_make_command(import), 50 | ugh_null_command 51 | }; 52 | 53 | ugh_module_t ugh_module_import = 54 | { 55 | ugh_module_import_cmds, 56 | NULL, 57 | NULL 58 | }; 59 | 60 | -------------------------------------------------------------------------------- /ugh/module_map.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | void *hash; 6 | strt key; 7 | strt default_value; 8 | } ugh_module_map_conf_t; 9 | 10 | extern ugh_module_t ugh_module_map; 11 | 12 | /* TODO XXX JudyLFreeArray */ 13 | 14 | static 15 | int ugh_command_map_handle_line(ugh_config_t *cfg, int argc, char **argv) 16 | { 17 | /* msg("%s -> %s", argv[0], (2 > argc) ? "" : argv[1]); */ 18 | 19 | ugh_module_map_conf_t *conf = ugh_module_config_get_last(); 20 | 21 | if (2 > argc) 22 | { 23 | conf->default_value.size = strlen(argv[0]); 24 | conf->default_value.data = argv[0]; 25 | 26 | return 0; 27 | } 28 | 29 | strp value; 30 | 31 | value = (strp) aux_pool_malloc(cfg->pool, sizeof(*value)); 32 | if (NULL == value) return -1; 33 | 34 | value->size = strlen(argv[1]); 35 | value->data = argv[1]; 36 | 37 | void **dest; 38 | 39 | dest = JudyLIns(&conf->hash, aux_hash_key_nt(argv[0]), PJE0); 40 | if (PJERR == dest) return -1; 41 | 42 | *dest = value; 43 | 44 | return 0; 45 | } 46 | 47 | static 48 | strp ugh_variable_map_handle(ugh_client_t *c, const char *name, size_t size, void *data) 49 | { 50 | ugh_module_map_conf_t *conf = data; 51 | strp vv = ugh_get_varvalue(c, conf->key.data, conf->key.size); 52 | 53 | void **dest; 54 | 55 | dest = JudyLGet(conf->hash, aux_hash_key(vv->data, vv->size), PJE0); 56 | if (PJERR == dest || NULL == dest) return &conf->default_value; 57 | 58 | return *dest; 59 | } 60 | 61 | static 62 | int ugh_command_map(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 63 | { 64 | /* msg("map %s -> %s", argv[1], argv[2]); */ 65 | 66 | ugh_module_map_conf_t *conf; 67 | 68 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 69 | if (NULL == conf) return -1; 70 | 71 | conf->hash = NULL; 72 | 73 | conf->key.data = argv[1] + 1; 74 | conf->key.size = strlen(argv[1]) - 1; 75 | 76 | conf->default_value.data = NULL; 77 | conf->default_value.size = 0; 78 | 79 | ugh_module_handle_add(ugh_module_map, conf, NULL); 80 | 81 | /* variable */ 82 | 83 | ugh_variable_t *v = aux_pool_malloc(cfg->pool, sizeof(ugh_variable_t)); 84 | if (NULL == v) return -1; 85 | 86 | v->name.data = ""; 87 | v->name.size = 0; 88 | v->handle = ugh_variable_map_handle; 89 | v->data = conf; 90 | 91 | ugh_idx_variable(cfg, argv[1] + 1, strlen(argv[1]) - 1); 92 | ugh_set_variable(cfg, argv[2] + 1, strlen(argv[2]) - 1, v); 93 | 94 | ugh_config_parser(cfg, ugh_command_map_handle_line); 95 | 96 | return 0; 97 | } 98 | 99 | static ugh_command_t ugh_module_map_cmds [] = { 100 | ugh_make_command(map), 101 | ugh_null_command 102 | }; 103 | 104 | ugh_module_t ugh_module_map = { 105 | ugh_module_map_cmds, 106 | NULL, 107 | NULL 108 | }; 109 | 110 | -------------------------------------------------------------------------------- /ugh/module_proxy.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | ugh_template_t url; 6 | unsigned nowait; 7 | double recv_timeout; 8 | double connect_timeout; 9 | 10 | } ugh_module_proxy_conf_t; 11 | 12 | extern ugh_module_t ugh_module_proxy; 13 | 14 | static 15 | int ugh_module_proxy_handle(ugh_client_t *c, void *data, strp body) 16 | { 17 | ugh_module_proxy_conf_t *conf = data; 18 | 19 | strp tv = ugh_template_execute(&conf->url, c); 20 | 21 | if (conf->nowait) 22 | { 23 | ugh_subreq_t *r = ugh_subreq_add(c, tv->data, tv->size, 0); 24 | ugh_subreq_set_timeout(r, conf->recv_timeout, UGH_TIMEOUT_ONCE); 25 | ugh_subreq_set_timeout_connect(r, conf->connect_timeout); 26 | ugh_subreq_run(r); 27 | } 28 | else 29 | { 30 | ugh_subreq_t *r = ugh_subreq_add(c, tv->data, tv->size, UGH_SUBREQ_WAIT); 31 | ugh_subreq_set_timeout(r, conf->recv_timeout, UGH_TIMEOUT_ONCE); 32 | ugh_subreq_set_timeout_connect(r, conf->connect_timeout); 33 | ugh_subreq_run(r); 34 | ugh_subreq_wait(c); 35 | 36 | body->data = r->body.data; 37 | body->size = r->body.size; 38 | } 39 | 40 | /* TODO we should copy upstream headers */ 41 | 42 | return UGH_HTTP_OK; /* TODO we should return upstream status */ 43 | } 44 | 45 | static 46 | int ugh_module_proxy_init(ugh_config_t *cfg, void *data) 47 | { 48 | ugh_module_proxy_conf_t *conf = data; 49 | 50 | if (conf->recv_timeout == 0) 51 | { 52 | conf->recv_timeout = UGH_CONFIG_SUBREQ_TIMEOUT; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | static 59 | int ugh_command_proxy_pass(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 60 | { 61 | ugh_module_proxy_conf_t *conf; 62 | 63 | conf = aux_pool_calloc(cfg->pool, sizeof(*conf)); 64 | if (NULL == conf) return -1; 65 | 66 | ugh_template_compile(&conf->url, argv[1], strlen(argv[1]), cfg); 67 | 68 | ugh_module_handle_add(ugh_module_proxy, conf, ugh_module_proxy_handle); 69 | 70 | return 0; 71 | } 72 | 73 | static 74 | int ugh_command_proxy_next_upstream(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 75 | { 76 | int i; 77 | 78 | cfg->next_upstream = UGH_UPSTREAM_FT_OFF; /* clear default value */ 79 | 80 | for (i = 1; i < argc; ++i) 81 | { 82 | if (0 == strcmp(argv[i], "error")) 83 | { 84 | cfg->next_upstream |= UGH_UPSTREAM_FT_ERROR; 85 | } 86 | else if (0 == strcmp(argv[i], "timeout")) 87 | { 88 | cfg->next_upstream |= UGH_UPSTREAM_FT_TIMEOUT; 89 | } 90 | else if (0 == strcmp(argv[i], "invalid_header")) 91 | { 92 | cfg->next_upstream |= UGH_UPSTREAM_FT_INVALID_HEADER; 93 | } 94 | else if (0 == strcmp(argv[i], "http_500")) 95 | { 96 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_500; 97 | } 98 | else if (0 == strcmp(argv[i], "http_502")) 99 | { 100 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_502; 101 | } 102 | else if (0 == strcmp(argv[i], "http_503")) 103 | { 104 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_503; 105 | } 106 | else if (0 == strcmp(argv[i], "http_504")) 107 | { 108 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_504; 109 | } 110 | else if (0 == strcmp(argv[i], "http_404")) 111 | { 112 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_404; 113 | } 114 | else if (0 == strcmp(argv[i], "http_5xx")) 115 | { 116 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_5XX; 117 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_500; 118 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_502; 119 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_503; 120 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_504; 121 | } 122 | else if (0 == strcmp(argv[i], "http_4xx")) 123 | { 124 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_4XX; 125 | cfg->next_upstream |= UGH_UPSTREAM_FT_HTTP_404; 126 | } 127 | else if (0 == strcmp(argv[i], "timeout_connect")) 128 | { 129 | cfg->next_upstream |= UGH_UPSTREAM_FT_TIMEOUT_CONNECT; 130 | } 131 | else if (0 == strcmp(argv[i], "off")) 132 | { 133 | cfg->next_upstream = UGH_UPSTREAM_FT_OFF; 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | static ugh_command_t ugh_module_proxy_cmds [] = 141 | { 142 | ugh_make_command(proxy_pass), 143 | ugh_make_command_flag(proxy_nowait, ugh_module_proxy_conf_t, nowait), 144 | ugh_make_command_time(proxy_recv_timeout, ugh_module_proxy_conf_t, recv_timeout), 145 | ugh_make_command_time(proxy_connect_timeout, ugh_module_proxy_conf_t, connect_timeout), 146 | ugh_make_command(proxy_next_upstream), 147 | ugh_null_command 148 | }; 149 | 150 | ugh_module_t ugh_module_proxy = 151 | { 152 | ugh_module_proxy_cmds, 153 | ugh_module_proxy_init, 154 | NULL 155 | }; 156 | 157 | -------------------------------------------------------------------------------- /ugh/module_push_pass.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | ugh_template_t channel_id; 6 | ugh_template_t url; 7 | double recv_timeout; 8 | 9 | #if 1 /* XXX temporary */ 10 | strt location; 11 | #endif 12 | 13 | } ugh_module_push_pass_conf_t; 14 | 15 | extern ugh_module_t ugh_module_push_pass; 16 | 17 | static 18 | int ugh_module_push_pass_handle(ugh_client_t *c, void *data, strp body) 19 | { 20 | ugh_module_push_pass_conf_t *conf = data; 21 | 22 | #if 1 /* XXX temporary */ 23 | if (conf->location.size != c->uri.size || 0 != strncmp(conf->location.data, c->uri.data, c->uri.size)) 24 | { 25 | return UGH_AGAIN; 26 | } 27 | #endif 28 | 29 | /* add channel */ 30 | 31 | strp channel_id = ugh_template_execute(&conf->channel_id, c); 32 | 33 | ugh_channel_t *ch = ugh_channel_add(c->s, channel_id, UGH_CHANNEL_PROXY, 0); /* TODO add configurable timeout for channel lifetime instead of 0 here */ 34 | 35 | if (NULL == ch) 36 | { 37 | return UGH_HTTP_INTERNAL_SERVER_ERROR; 38 | } 39 | 40 | /* perform subrequest */ 41 | 42 | strp url = ugh_template_execute(&conf->url, c); 43 | 44 | ugh_subreq_t *r = ugh_subreq_add(c, url->data, url->size, UGH_SUBREQ_PUSH); 45 | ugh_subreq_set_channel(r, ch, 0); 46 | 47 | if (conf->recv_timeout > 0) /* XXX to remove this if we should support default values per each block in _init function */ 48 | { 49 | ugh_subreq_set_timeout(r, conf->recv_timeout, UGH_TIMEOUT_ONCE); 50 | } 51 | 52 | ugh_subreq_run(r); 53 | 54 | return UGH_HTTP_OK; 55 | } 56 | 57 | static 58 | int ugh_command_push_pass(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 59 | { 60 | ugh_module_push_pass_conf_t *conf; 61 | 62 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 63 | if (NULL == conf) return -1; 64 | 65 | ugh_template_compile(&conf->channel_id, argv[1], strlen(argv[1]), cfg); 66 | ugh_template_compile(&conf->url, argv[2], strlen(argv[2]), cfg); 67 | 68 | ugh_module_handle_add(ugh_module_push_pass, conf, ugh_module_push_pass_handle); 69 | 70 | return 0; 71 | } 72 | 73 | static ugh_command_t ugh_module_push_pass_cmds [] = 74 | { 75 | ugh_make_command(push_pass), 76 | ugh_make_command_strt(push_pass_location, ugh_module_push_pass_conf_t, location), 77 | ugh_make_command_time(push_pass_recv_timeout, ugh_module_push_pass_conf_t, recv_timeout), 78 | ugh_null_command 79 | }; 80 | 81 | ugh_module_t ugh_module_push_pass = 82 | { 83 | ugh_module_push_pass_cmds, 84 | NULL, 85 | NULL 86 | }; 87 | 88 | -------------------------------------------------------------------------------- /ugh/module_push_publisher.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | /* 4 | * TODO: Message storage limits SHOULD be configurable. publisher locations 5 | * SHOULD be configurable to allow foregoing message storage on POST 6 | * requests. 7 | * 8 | * 9 | * TODO: All 200-level responses MUST, in the response body, contain 10 | * information about the applicable channel. This information MAY contain 11 | * the number of stored messages and the number of subscribers' requests 12 | * being long-held prior to this request. The server MAY implement a 13 | * content-negotiation scheme for this information. 14 | */ 15 | 16 | typedef struct 17 | { 18 | ugh_template_t channel_id; 19 | 20 | #if 1 /* XXX temporary */ 21 | strt location; 22 | #endif 23 | 24 | } ugh_module_push_publisher_conf_t; 25 | 26 | extern ugh_module_t ugh_module_push_publisher; 27 | 28 | static 29 | int ugh_module_push_publisher_handle(ugh_client_t *c, void *data, strp body) 30 | { 31 | ugh_module_push_publisher_conf_t *conf = data; 32 | 33 | #if 1 /* XXX temporary */ 34 | if (conf->location.size != c->uri.size || 0 != strncmp(conf->location.data, c->uri.data, c->uri.size)) 35 | { 36 | return UGH_AGAIN; 37 | } 38 | #endif 39 | 40 | strp channel_id = ugh_template_execute(&conf->channel_id, c); 41 | 42 | if (0 == channel_id->size) 43 | { 44 | return UGH_HTTP_BAD_REQUEST; 45 | } 46 | 47 | if (c->method == UGH_HTTP_GET || c->method == UGH_HTTP_HEAD) 48 | { 49 | ugh_channel_t *ch = ugh_channel_get(c->s, channel_id); 50 | 51 | if (NULL == ch) 52 | { 53 | return UGH_HTTP_NOT_FOUND; 54 | } 55 | 56 | return UGH_HTTP_OK; 57 | } 58 | 59 | if (c->method == UGH_HTTP_PUT) 60 | { 61 | ugh_channel_t *ch = ugh_channel_add(c->s, channel_id, UGH_CHANNEL_PERMANENT, 0); 62 | 63 | if (NULL == ch) 64 | { 65 | return UGH_HTTP_INTERNAL_SERVER_ERROR; 66 | } 67 | 68 | return UGH_HTTP_OK; 69 | } 70 | 71 | if (c->method == UGH_HTTP_DELETE) 72 | { 73 | int rc = ugh_channel_del(c->s, channel_id); 74 | 75 | if (0 > rc) 76 | { 77 | return UGH_HTTP_NOT_FOUND; 78 | } 79 | 80 | return UGH_HTTP_OK; 81 | } 82 | 83 | if (c->method == UGH_HTTP_POST) 84 | { 85 | ugh_channel_t *ch = ugh_channel_get(c->s, channel_id); 86 | 87 | if (NULL == ch) 88 | { 89 | return UGH_HTTP_NOT_FOUND; 90 | } 91 | 92 | ugh_header_t *h_content_type = ugh_client_header_get_nt(c, "Content-Type"); 93 | ugh_channel_add_message(ch, &c->body, &h_content_type->value, NULL); 94 | 95 | return ch->clients_size ? UGH_HTTP_CREATED : UGH_HTTP_ACCEPTED; 96 | } 97 | 98 | return UGH_HTTP_BAD_REQUEST; 99 | } 100 | 101 | static 102 | int ugh_command_push_publisher(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 103 | { 104 | ugh_module_push_publisher_conf_t *conf; 105 | 106 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 107 | if (NULL == conf) return -1; 108 | 109 | ugh_template_compile(&conf->channel_id, argv[1], strlen(argv[1]), cfg); 110 | 111 | ugh_module_handle_add(ugh_module_push_publisher, conf, ugh_module_push_publisher_handle); 112 | 113 | return 0; 114 | } 115 | 116 | static ugh_command_t ugh_module_push_publisher_cmds [] = 117 | { 118 | ugh_make_command(push_publisher), 119 | ugh_make_command_strt(push_publisher_location, ugh_module_push_publisher_conf_t, location), 120 | ugh_null_command 121 | }; 122 | 123 | ugh_module_t ugh_module_push_publisher = 124 | { 125 | ugh_module_push_publisher_cmds, 126 | NULL, 127 | NULL 128 | }; 129 | 130 | -------------------------------------------------------------------------------- /ugh/module_push_subscriber.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | ugh_template_t channel_id; 6 | unsigned type:1; 7 | 8 | #if 1 /* XXX temporary */ 9 | strt location; 10 | #endif 11 | 12 | } ugh_module_push_subscriber_conf_t; 13 | 14 | extern ugh_module_t ugh_module_push_subscriber; 15 | 16 | static 17 | int ugh_module_push_subscriber_handle(ugh_client_t *c, void *data, strp body) 18 | { 19 | ugh_module_push_subscriber_conf_t *conf = data; 20 | 21 | #if 1 /* XXX temporary */ 22 | if (conf->location.size != c->uri.size || 0 != strncmp(conf->location.data, c->uri.data, c->uri.size)) 23 | { 24 | return UGH_AGAIN; 25 | } 26 | #endif 27 | 28 | strp channel_id = ugh_template_execute(&conf->channel_id, c); 29 | 30 | if (0 == channel_id->size) 31 | { 32 | return UGH_HTTP_BAD_REQUEST; 33 | } 34 | 35 | ugh_channel_t *ch = ugh_channel_get(c->s, channel_id); 36 | 37 | if (NULL == ch) 38 | { 39 | return UGH_HTTP_GONE; 40 | } 41 | 42 | ugh_header_t *h_if_none_match = ugh_client_header_get_nt(c, "If-None-Match"); 43 | Word_t etag = strtoul(h_if_none_match->value.data, NULL, 10); 44 | 45 | ugh_channel_message_t *m; 46 | 47 | int rc = ugh_channel_get_message(ch, c, &m, conf->type, &etag); 48 | 49 | if (UGH_ERROR == rc) 50 | { 51 | return UGH_HTTP_GONE; 52 | } 53 | 54 | if (UGH_AGAIN == rc) 55 | { 56 | return UGH_HTTP_NOT_MODIFIED; 57 | } 58 | 59 | body->data = aux_pool_strdup(c->pool, &m->body); 60 | body->size = m->body.size; 61 | 62 | char *content_type_data = aux_pool_strdup(c->pool, &m->content_type); 63 | ugh_client_header_out_set(c, "Content-Type", sizeof("Content-Type") - 1, content_type_data, m->content_type.size); 64 | 65 | char *etag_data = aux_pool_nalloc(c->pool, 21); 66 | int etag_size = snprintf(etag_data, 21, "%lu", etag); 67 | ugh_client_header_out_set(c, "Etag", sizeof("Etag") - 1, etag_data, etag_size); 68 | 69 | return UGH_HTTP_OK; 70 | } 71 | 72 | static 73 | int ugh_command_push_subscriber(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 74 | { 75 | ugh_module_push_subscriber_conf_t *conf; 76 | 77 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 78 | if (NULL == conf) return -1; 79 | 80 | ugh_template_compile(&conf->channel_id, argv[1], strlen(argv[1]), cfg); 81 | 82 | if (argc > 2) 83 | { 84 | switch (argv[2][0]) 85 | { 86 | case 'l': 87 | case 'L': 88 | conf->type = UGH_CHANNEL_LONG_POLL; 89 | break; 90 | case 'i': 91 | case 'I': 92 | conf->type = UGH_CHANNEL_INTERVAL_POLL; 93 | break; 94 | default: 95 | return -1; 96 | } 97 | } 98 | 99 | ugh_module_handle_add(ugh_module_push_subscriber, conf, ugh_module_push_subscriber_handle); 100 | 101 | return 0; 102 | } 103 | 104 | static ugh_command_t ugh_module_push_subscriber_cmds [] = 105 | { 106 | ugh_make_command(push_subscriber), 107 | ugh_make_command_strt(push_subscriber_location, ugh_module_push_subscriber_conf_t, location), 108 | ugh_null_command 109 | }; 110 | 111 | ugh_module_t ugh_module_push_subscriber = 112 | { 113 | ugh_module_push_subscriber_cmds, 114 | NULL, 115 | NULL 116 | }; 117 | 118 | -------------------------------------------------------------------------------- /ugh/module_return.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | typedef struct 4 | { 5 | ugh_template_t template; 6 | } ugh_module_return_conf_t; 7 | 8 | extern ugh_module_t ugh_module_return; 9 | 10 | static 11 | int ugh_module_return_handle(ugh_client_t *c, void *data, strp body) 12 | { 13 | ugh_module_return_conf_t *conf = (ugh_module_return_conf_t *) data; 14 | 15 | strp tv = ugh_template_execute(&conf->template, c); 16 | 17 | body->size = tv->size; 18 | body->data = tv->data; 19 | 20 | return UGH_HTTP_OK; 21 | } 22 | 23 | static 24 | int ugh_command_return(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 25 | { 26 | ugh_module_return_conf_t *conf; 27 | 28 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 29 | if (NULL == conf) return -1; 30 | 31 | ugh_template_compile(&conf->template, argv[1], strlen(argv[1]), cfg); 32 | 33 | ugh_module_handle_add(ugh_module_return, conf, ugh_module_return_handle); 34 | 35 | return 0; 36 | } 37 | 38 | static ugh_command_t ugh_module_return_cmds [] = 39 | { 40 | ugh_make_command(return), 41 | ugh_null_command 42 | }; 43 | 44 | ugh_module_t ugh_module_return = 45 | { 46 | ugh_module_return_cmds, 47 | NULL, 48 | NULL 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /ugh/module_set.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | #define UGH_MAX_SET_ELEMENTS 16 4 | 5 | typedef struct 6 | { 7 | strt values [UGH_MAX_SET_ELEMENTS]; 8 | size_t size; 9 | size_t curr; 10 | } ugh_module_set_conf_t; 11 | 12 | extern ugh_module_t ugh_module_set; 13 | 14 | static 15 | int ugh_command_set_handle_line(ugh_config_t *cfg, int argc, char **argv) 16 | { 17 | ugh_module_set_conf_t *conf = ugh_module_config_get_last(); 18 | 19 | if (conf->size < UGH_MAX_SET_ELEMENTS) 20 | { 21 | conf->values[conf->size].size = strlen(argv[0]); 22 | conf->values[conf->size].data = argv[0]; 23 | 24 | conf->size++; 25 | conf->curr = conf->size - 1; /* this is to return first value first */ 26 | } 27 | 28 | return 0; 29 | } 30 | 31 | static 32 | strp ugh_variable_set_handle(ugh_client_t *c, const char *name, size_t size, void *data) 33 | { 34 | ugh_module_set_conf_t *conf = data; 35 | 36 | conf->curr += 1; 37 | conf->curr %= conf->size; 38 | 39 | return &conf->values[conf->curr]; 40 | } 41 | 42 | static 43 | int ugh_command_set(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 44 | { 45 | ugh_module_set_conf_t *conf; 46 | 47 | conf = aux_pool_calloc(cfg->pool, sizeof(*conf)); 48 | if (NULL == conf) return -1; 49 | 50 | ugh_module_handle_add(ugh_module_set, conf, NULL); 51 | 52 | /* variable */ 53 | 54 | ugh_variable_t *v; 55 | 56 | v = aux_pool_malloc(cfg->pool, sizeof(*v)); 57 | if (NULL == v) return -1; 58 | 59 | v->name.data = ""; 60 | v->name.size = 0; 61 | v->handle = ugh_variable_set_handle; 62 | v->data = conf; 63 | 64 | ugh_set_variable(cfg, argv[1] + 1, strlen(argv[1]) - 1, v); 65 | 66 | ugh_config_parser(cfg, ugh_command_set_handle_line); 67 | 68 | return 0; 69 | } 70 | 71 | static ugh_command_t ugh_module_set_cmds [] = { 72 | ugh_make_command(set), 73 | ugh_null_command 74 | }; 75 | 76 | ugh_module_t ugh_module_set = { 77 | ugh_module_set_cmds, 78 | NULL, 79 | NULL 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /ugh/module_upstream.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | extern ugh_module_t ugh_module_upstream; 4 | 5 | static 6 | int ugh_command_upstream_handle_line(ugh_config_t *cfg, int argc, char **argv) 7 | { 8 | ugh_upstream_t *upstream = ugh_module_config_get_last(); 9 | 10 | if (0 == strcmp(argv[0], "server")) 11 | { 12 | if (upstream->values_size < UGH_MAX_UPSTREAM_ELEMENTS) 13 | { 14 | ugh_url_t u; 15 | ugh_parser_url(&u, argv[1], strlen(argv[1])); 16 | 17 | upstream->values[upstream->values_size].host.data = u.host.data; 18 | upstream->values[upstream->values_size].host.size = u.host.size; 19 | upstream->values[upstream->values_size].port = strtoul(u.port.data, NULL, 10); 20 | 21 | int i; 22 | 23 | for (i = 2; i < argc; ++i) 24 | { 25 | char *v = strchr(argv[i], '='); 26 | if (NULL == v) continue; 27 | 28 | ++v; 29 | 30 | if (0 == strncmp(argv[i], "max_fails", sizeof("max_fails") - 1)) 31 | { 32 | upstream->values[upstream->values_size].max_fails = atoi(v); 33 | } 34 | else if (0 == strncmp(argv[i], "fail_timeout", sizeof("fail_timeout") - 1)) 35 | { 36 | upstream->values[upstream->values_size].fail_timeout = atof(v); /* XXX change to a proper type of value */ 37 | } 38 | } 39 | 40 | upstream->values_size++; 41 | upstream->values_curr = upstream->values_size - 1; 42 | } 43 | } 44 | else if (0 == strcmp(argv[0], "backup")) 45 | { 46 | if (upstream->backup_values_size < UGH_MAX_UPSTREAM_ELEMENTS) 47 | { 48 | ugh_url_t u; 49 | ugh_parser_url(&u, argv[1], strlen(argv[1])); 50 | 51 | upstream->backup_values[upstream->backup_values_size].host.data = u.host.data; 52 | upstream->backup_values[upstream->backup_values_size].host.size = u.host.size; 53 | upstream->backup_values[upstream->backup_values_size].port = strtoul(u.port.data, NULL, 10); 54 | 55 | int i; 56 | 57 | for (i = 2; i < argc; ++i) 58 | { 59 | char *v = strchr(argv[i], '='); 60 | if (NULL == v) continue; 61 | 62 | ++v; 63 | 64 | if (0 == strncmp(argv[i], "max_fails", sizeof("max_fails") - 1)) 65 | { 66 | upstream->backup_values[upstream->backup_values_size].max_fails = atoi(v); 67 | } 68 | else if (0 == strncmp(argv[i], "fail_timeout", sizeof("fail_timeout") - 1)) 69 | { 70 | upstream->backup_values[upstream->backup_values_size].fail_timeout = atof(v); /* XXX change to a proper type of value */ 71 | } 72 | } 73 | 74 | upstream->backup_values_size++; 75 | upstream->backup_values_curr = upstream->backup_values_size - 1; 76 | } 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | static 83 | int ugh_command_upstream(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 84 | { 85 | ugh_upstream_t *upstream; 86 | 87 | upstream = ugh_upstream_add(cfg, argv[1], strlen(argv[1])); 88 | 89 | ugh_module_handle_add(ugh_module_upstream, upstream, NULL); 90 | 91 | ugh_config_parser(cfg, ugh_command_upstream_handle_line); 92 | 93 | return 0; 94 | } 95 | 96 | static ugh_command_t ugh_module_upstream_cmds [] = { 97 | ugh_make_command(upstream), 98 | ugh_null_command 99 | }; 100 | 101 | ugh_module_t ugh_module_upstream = { 102 | ugh_module_upstream_cmds, 103 | NULL, 104 | NULL 105 | }; 106 | 107 | -------------------------------------------------------------------------------- /ugh/resolver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "aux/resolver.h" 3 | #include "resolver.h" 4 | 5 | static 6 | void ugh_resolver_rec_call_waiters(ugh_resolver_rec_t *rec, in_addr_t addr) 7 | { 8 | Word_t idx = 0; 9 | int rc; 10 | 11 | for (rc = Judy1First(rec->wait_hash, &idx, PJE0); 0 != rc; 12 | rc = Judy1Next (rec->wait_hash, &idx, PJE0)) 13 | { 14 | ugh_resolver_ctx_t *ctx = (ugh_resolver_ctx_t *) idx; 15 | ctx->handle(ctx->data, addr); 16 | } 17 | 18 | Judy1FreeArray(&rec->wait_hash, PJE0); 19 | } 20 | 21 | static 22 | void ugh_resolver_wcb_send(EV_P_ ev_io *w, int tev) 23 | { 24 | ugh_resolver_rec_t *rec = aux_memberof(ugh_resolver_rec_t, wev_send, w); 25 | 26 | int rc, qlen; 27 | char q [1024]; 28 | 29 | if (0 > (qlen = create_name_query(q, rec->name.data, rec->name.size))) 30 | { 31 | ugh_resolver_rec_call_waiters(rec, INADDR_NONE); 32 | ev_io_stop(loop, &rec->wev_send); 33 | ev_timer_stop(loop, &rec->wev_timeout); 34 | return; 35 | } 36 | 37 | if (0 > (rc = send(w->fd, q, qlen, 0))) 38 | { 39 | ugh_resolver_rec_call_waiters(rec, INADDR_NONE); 40 | ev_io_stop(loop, &rec->wev_send); 41 | ev_timer_stop(loop, &rec->wev_timeout); 42 | return; 43 | } 44 | 45 | ev_io_stop(loop, w); 46 | } 47 | 48 | static 49 | void ugh_resolver_wcb_timeout(EV_P_ ev_timer *w, int tev) 50 | { 51 | ugh_resolver_rec_t *rec = aux_memberof(ugh_resolver_rec_t, wev_timeout, w); 52 | 53 | if (rec->tries < UGH_RESOLVER_MAX_TRIES - 1) 54 | { 55 | log_warn("resolver timeout %.*s, trying again", (int) rec->name.size, rec->name.data); 56 | 57 | rec->tries++; 58 | 59 | ev_io_start(loop, &rec->wev_send); 60 | ev_timer_again(loop, &rec->wev_timeout); 61 | 62 | return; 63 | } 64 | 65 | log_warn("resolver timeout %.*s, out of tries", (int) rec->name.size, rec->name.data); 66 | 67 | ugh_resolver_rec_call_waiters(rec, INADDR_NONE); 68 | 69 | ev_io_stop(loop, &rec->wev_send); 70 | ev_timer_stop(loop, &rec->wev_timeout); 71 | } 72 | 73 | static 74 | void ugh_resolver_wcb_recv(EV_P_ ev_io *w, int tev) 75 | { 76 | ugh_resolver_t *r = aux_memberof(ugh_resolver_t, wev_recv, w); 77 | 78 | char buf [1024]; 79 | int len; 80 | 81 | len = aux_unix_recv(w->fd, buf, 1024); 82 | if (0 > len) return; 83 | 84 | in_addr_t addrs [UGH_RESOLVER_ADDRS_LEN]; 85 | int naddrs = UGH_RESOLVER_ADDRS_LEN; 86 | 87 | strt name; 88 | char name_data [1024]; 89 | name.data = name_data; 90 | 91 | naddrs = process_response(buf, len, addrs, naddrs, &name); 92 | 93 | void **dest; 94 | 95 | dest = JudyLGet(r->name_hash, aux_hash_key(name.data, name.size), PJE0); 96 | if (PJERR == dest || NULL == dest) return; 97 | 98 | ugh_resolver_rec_t *rec = *dest; 99 | 100 | int i; 101 | 102 | rec->naddrs = naddrs; 103 | 104 | for (i = 0; i < naddrs; ++i) 105 | { 106 | rec->addrs[i] = addrs[i]; 107 | } 108 | 109 | ugh_resolver_rec_call_waiters(rec, (0 < naddrs) ? addrs[0] : INADDR_NONE); 110 | ev_timer_stop(loop, &rec->wev_timeout); 111 | } 112 | 113 | int ugh_resolver_init(ugh_resolver_t *r, ugh_config_t *cfg) 114 | { 115 | int sd, rc; 116 | 117 | r->cfg = cfg; 118 | 119 | r->pool = aux_pool_init(0); 120 | if (NULL == r->pool) return -1; 121 | 122 | if (0 > (sd = socket(AF_INET, SOCK_DGRAM, 0))) 123 | { 124 | log_error("socket(AF_INET, SOCK_DGRAM, 0) (%d: %s)", errno, aux_strerror(errno)); 125 | return -1; 126 | } 127 | 128 | if (0 > (rc = aux_set_nonblk(sd, 1))) 129 | { 130 | log_error("aux_set_nonblk(%d, 1) (%d: %s)", sd, errno, aux_strerror(errno)); 131 | close(sd); 132 | return -1; 133 | } 134 | 135 | struct sockaddr_in addr; 136 | addr.sin_family = AF_INET; 137 | addr.sin_addr.s_addr = inet_addr(cfg->resolver_host); 138 | addr.sin_port = htons(53); 139 | 140 | rc = connect(sd, (struct sockaddr *) &addr, sizeof(addr)); 141 | 142 | if (0 > rc) 143 | { 144 | log_error("connect(%d, %s) (%d: %s)", sd, cfg->resolver_host, errno, aux_strerror(errno)); 145 | close(sd); 146 | return -1; 147 | } 148 | 149 | ev_io_init(&r->wev_recv, ugh_resolver_wcb_recv, sd, EV_READ); 150 | ev_io_start(loop, &r->wev_recv); 151 | 152 | return 0; 153 | } 154 | 155 | int ugh_resolver_free(ugh_resolver_t *r) 156 | { 157 | void **vptr; 158 | Word_t idx = 0; 159 | 160 | for (vptr = JudyLFirst(r->name_hash, &idx, PJE0); vptr; vptr = JudyLNext(r->name_hash, &idx, PJE0)) 161 | { 162 | ugh_resolver_rec_t *rec = *vptr; 163 | Judy1FreeArray(&rec->wait_hash, PJE0); 164 | } 165 | 166 | JudyLFreeArray(&r->name_hash, PJE0); 167 | 168 | ev_io_stop(loop, &r->wev_recv); 169 | close(r->wev_recv.fd); 170 | 171 | aux_pool_free(r->pool); 172 | 173 | return 0; 174 | } 175 | 176 | #if 0 177 | 1. try inet_addr 178 | 2. ins rec[name], if rec[name] exists and not expired (call ctx->handle and return) 179 | if rec[name] exists and expired (add query, call ctx->handle and return) 180 | if rec[name] exists and waiting (add ctx to waiters and return) 181 | if rec[name] didnot exist (add query, add ctx to waiters and return) 182 | #endif /* */ 183 | 184 | int ugh_resolver_addq(ugh_resolver_t *r, char *name, size_t size, ugh_resolver_ctx_t *ctx) 185 | { 186 | in_addr_t addr; 187 | 188 | if (INADDR_NONE != (addr = aux_inet_addr(name, size))) 189 | { 190 | return ctx->handle(ctx->data, addr); 191 | } 192 | 193 | void **dest; 194 | 195 | dest = JudyLIns(&r->name_hash, aux_hash_key(name, size), PJE0); 196 | if (PJERR == dest) return ctx->handle(ctx->data, INADDR_NONE); /* emulate couldn't connect error' */ 197 | 198 | ugh_resolver_rec_t *rec = *dest; 199 | 200 | if (NULL != rec) 201 | { 202 | if (0 < rec->naddrs) /* result is not empty */ 203 | { 204 | if (0 == r->cfg->resolver_cache) 205 | { 206 | rec->naddrs = 0; 207 | rec->tries = 0; 208 | 209 | ev_timer_again(loop, &rec->wev_timeout); 210 | ev_io_start(loop, &rec->wev_send); 211 | 212 | Judy1Set(&rec->wait_hash, (uintptr_t) ctx, PJE0); /* add wait */ 213 | 214 | return 0; 215 | } 216 | 217 | rec->current += 1; 218 | rec->current %= rec->naddrs; 219 | 220 | return ctx->handle(ctx->data, rec->addrs[rec->current]); 221 | } 222 | else if (NULL != rec->wait_hash) /* result is empty, but someone is already waiting for result */ 223 | { 224 | Judy1Set(&rec->wait_hash, (uintptr_t) ctx, PJE0); /* add wait */ 225 | } 226 | else /* result is empty and noone waiting for it (which means, that there was a resolve error) */ 227 | { 228 | rec->naddrs = 0; 229 | rec->tries = 0; 230 | 231 | ev_timer_again(loop, &rec->wev_timeout); 232 | ev_io_start(loop, &rec->wev_send); 233 | 234 | Judy1Set(&rec->wait_hash, (uintptr_t) ctx, PJE0); /* add wait */ 235 | 236 | return 0; 237 | } 238 | } 239 | else 240 | { 241 | rec = (ugh_resolver_rec_t *) aux_pool_calloc(r->pool, sizeof(*rec)); 242 | if (NULL == rec) return -1; 243 | 244 | rec->name.data = (char *) aux_pool_nalloc(r->pool, size); 245 | if (NULL == rec->name.data) return -1; 246 | 247 | rec->name.size = aux_cpymsz(rec->name.data, name, size); 248 | 249 | *dest = rec; 250 | 251 | ev_io_init(&rec->wev_send, ugh_resolver_wcb_send, r->wev_recv.fd, EV_WRITE); 252 | ev_timer_init(&rec->wev_timeout, ugh_resolver_wcb_timeout, 0, r->cfg->resolver_timeout); 253 | ev_timer_again(loop, &rec->wev_timeout); 254 | ev_io_start(loop, &rec->wev_send); 255 | 256 | Judy1Set(&rec->wait_hash, (uintptr_t) ctx, PJE0); /* add wait */ 257 | } 258 | 259 | return 0; 260 | } 261 | 262 | -------------------------------------------------------------------------------- /ugh/resolver.h: -------------------------------------------------------------------------------- 1 | #ifndef __UGH_RESOLVER_H__ 2 | #define __UGH_RESOLVER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "aux/hashes.h" 8 | #include "aux/logger.h" 9 | #include "aux/memory.h" 10 | #include "aux/socket.h" 11 | #include "config.h" 12 | 13 | #define UGH_RESOLVER_ADDRS_LEN 8 14 | #define UGH_RESOLVER_MAX_TRIES 2 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct ugh_resolver_ctx 21 | ugh_resolver_ctx_t; 22 | 23 | struct ugh_resolver_ctx 24 | { 25 | int (*handle)(void *data, in_addr_t addr); /* called when resolve is done (or address is already known) */ 26 | void *data; 27 | }; 28 | 29 | typedef struct ugh_resolver_rec 30 | ugh_resolver_rec_t; 31 | 32 | struct ugh_resolver_rec 33 | { 34 | strt name; 35 | int naddrs; 36 | in_addr_t addrs [UGH_RESOLVER_ADDRS_LEN]; 37 | 38 | ev_io wev_send; 39 | ev_timer wev_timeout; 40 | 41 | void *wait_hash; /* Judy (ugh_resolver_ctx_t *) */ 42 | 43 | unsigned tries; 44 | unsigned current; 45 | }; 46 | 47 | typedef struct ugh_resolver 48 | ugh_resolver_t; 49 | 50 | struct ugh_resolver 51 | { 52 | ugh_config_t *cfg; 53 | aux_pool_t *pool; 54 | ev_io wev_recv; 55 | void *name_hash; /* Judy (domain -> ugh_resolver_rec_t *) */ 56 | }; 57 | 58 | int ugh_resolver_init(ugh_resolver_t *r, ugh_config_t *cfg); 59 | int ugh_resolver_free(ugh_resolver_t *r); 60 | int ugh_resolver_addq(ugh_resolver_t *r, char *name, size_t size, ugh_resolver_ctx_t *ctx); 61 | 62 | #if 1 63 | extern struct ev_loop *loop; 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif /* __UGH_RESOLVER_H__ */ 71 | -------------------------------------------------------------------------------- /ugh/server.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | static 4 | void ugh_wcb_accept(EV_P_ ev_io *w, int tev) 5 | { 6 | int sd, rc; 7 | struct sockaddr_in addr; 8 | socklen_t addrlen; 9 | 10 | ugh_server_t *s = aux_memberof(ugh_server_t, wev_accept, w); 11 | 12 | if (EV_ERROR & tev) 13 | { 14 | /* TODO: is errno defined here? */ 15 | log_alert("accept: tev:%d, w->fd:%d", tev, w->fd); 16 | 17 | ugh_server_enough(s); 18 | return; 19 | } 20 | 21 | addrlen = sizeof(addr); 22 | 23 | if (0 > (sd = accept(w->fd, (struct sockaddr *) &addr, &addrlen))) 24 | { 25 | log_error("accept: w->fd:%d (%d: %s)", w->fd, errno, aux_strerror(errno)); 26 | return; 27 | } 28 | 29 | if (0 > (rc = aux_set_nonblk(sd, 1))) 30 | { 31 | log_error("set_nonblk: sd:%d (%d: %s)", sd, errno, aux_strerror(errno)); 32 | return; 33 | } 34 | 35 | ugh_client_add(s, sd, &addr); 36 | } 37 | 38 | int ugh_server_listen(ugh_server_t *s, ugh_config_t *cfg, ugh_resolver_t *resolver) 39 | { 40 | int sd, rc; 41 | 42 | sd = socket(AF_INET, SOCK_STREAM, 0); 43 | if (0 > sd) return -1; 44 | 45 | rc = aux_set_nonblk(sd, 1); 46 | if (0 > rc) return -1; 47 | 48 | rc = aux_set_sckopt(sd, SOL_SOCKET, SO_REUSEADDR, 1); 49 | if (0 > rc) return -1; 50 | 51 | aux_clrptr(s); 52 | 53 | s->cfg = cfg; 54 | s->resolver = resolver; 55 | 56 | s->addr.sin_family = AF_INET; 57 | s->addr.sin_addr.s_addr = inet_addr(cfg->listen_host); 58 | s->addr.sin_port = htons(cfg->listen_port); 59 | 60 | rc = bind(sd, (struct sockaddr *) &s->addr, sizeof(s->addr)); 61 | 62 | if (0 > rc) 63 | { 64 | log_emerg("Can't bind to address %s:%u (%d: %s)", cfg->listen_host, cfg->listen_port, errno, aux_strerror(errno)); 65 | return -1; 66 | } 67 | 68 | /* 69 | * TODO (optional) 70 | * Don't forget about inherited sockets. As an option at least. 71 | * Plus LINGERING_CLOSE and TCP_NOPUSH/TCP_NODELAY double strike. 72 | * Finally, don't forget about SO_RCVBUF, SO_SNDBUF, TCP_DEFER_ACCEPT. 73 | */ 74 | 75 | rc = listen(sd, UGH_CONFIG_LISTEN_BACKLOG); 76 | if (0 > rc) return -1; 77 | 78 | ev_io_init(&s->wev_accept, ugh_wcb_accept, sd, EV_READ); 79 | ev_io_start(loop, &s->wev_accept); 80 | 81 | return 0; 82 | } 83 | 84 | int ugh_server_enough(ugh_server_t *s) 85 | { 86 | ev_io_stop(loop, &s->wev_accept); 87 | 88 | return close(s->wev_accept.fd); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /ugh/status.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | const char *ugh_method_string [UGH_HTTP_MAX] = { 4 | "GET", 5 | "HEAD", 6 | "POST", 7 | "PUT", 8 | "DELETE", 9 | }; 10 | 11 | const char *ugh_version_string [UGH_HTTP_VERSION_MAX] = { 12 | "HTTP/0.9", 13 | "HTTP/1.0", 14 | "HTTP/1.1", 15 | }; 16 | 17 | const char *ugh_status_header [UGH_HTTP_STATUS_MAX] = { 18 | "200 OK", 19 | "201 Created", 20 | "202 Accepted", 21 | "203 Non-Authoritative Information", 22 | "204 No Content", 23 | "205 Reset Content", 24 | "206 Partial Content", 25 | "207 Multi-Status", 26 | "300 Multiple Choices", 27 | "301 Moved Permanently", 28 | "302 Moved Temporarily", 29 | "303 See Other", 30 | "304 Not Modified", 31 | "305 Use Proxy", 32 | "306 unused", 33 | "307 Temporary Redirect", 34 | "400 Bad Request", 35 | "401 Unauthorized", 36 | "402 Payment Required", 37 | "403 Forbidden", 38 | "404 Not Found", 39 | "405 Not Allowed", 40 | "406 Not Acceptable", 41 | "407 Proxy Authentication Required", 42 | "408 Request Time-out", 43 | "409 Conflict", 44 | "410 Gone", 45 | "411 Length Required", 46 | "412 Precondition Failed", 47 | "413 Request Entity Too Large", 48 | "414 Request-URI Too Large", 49 | "415 Unsupported Media Type", 50 | "416 Requested Range Not Satisfiable", 51 | "417 Expectation Failed", 52 | "500 Internal Server Error", 53 | "501 Method Not Implemented", 54 | "502 Bad Gateway", 55 | "503 Service Temporarily Unavailable", 56 | "504 Gateway Time-out", 57 | "505 HTTP Version Not Supported", 58 | "506 Variant Also Negotiates", 59 | "507 Insufficient Storage", 60 | "429 Too Many Requests", 61 | "451 Unavailable For Legal Reasons", 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /ugh/template.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ugh.h" 3 | 4 | #define S_STR 0 5 | #define S_VAR 1 6 | 7 | int ugh_template_compile(ugh_template_t *t, char *data, size_t size, ugh_config_t *cfg) 8 | { 9 | char *b = data; 10 | char *p = data; 11 | char *e = data + size; 12 | 13 | unsigned char state = S_STR; 14 | size_t chunks_size = 1; /* we always have at least one chunk */ 15 | 16 | for (; p < e; ++p) 17 | { 18 | char ch = *p; 19 | 20 | if (S_STR == state) 21 | { 22 | if ('$' == ch) 23 | { 24 | chunks_size++; 25 | state = S_VAR; 26 | } 27 | } 28 | else 29 | { 30 | if (!isalnum(ch) && '_' != ch) 31 | { 32 | chunks_size++; 33 | state = ch != '$' ? S_STR : S_VAR; 34 | } 35 | } 36 | } 37 | 38 | t->chunks_size = chunks_size; 39 | t->chunks = aux_pool_malloc(cfg->pool, sizeof(t->chunks[0]) * t->chunks_size); 40 | if (NULL == t->chunks) return -1; 41 | 42 | b = data; 43 | p = data; 44 | 45 | state = S_STR; 46 | size_t cur_chunk = 0; 47 | 48 | for (; p < e; ++p) 49 | { 50 | char ch = *p; 51 | 52 | if (S_STR == state) 53 | { 54 | if ('$' == ch) 55 | { 56 | t->chunks[cur_chunk].data = b; 57 | t->chunks[cur_chunk].size = p - b; 58 | cur_chunk++; 59 | state = S_VAR; 60 | b = p; 61 | } 62 | } 63 | else 64 | { 65 | if (!isalnum(ch) && '_' != ch) 66 | { 67 | ugh_idx_variable(cfg, b + 1, p - b - 1); 68 | 69 | t->chunks[cur_chunk].data = b; 70 | t->chunks[cur_chunk].size = p - b; 71 | cur_chunk++; 72 | state = ch != '$' ? S_STR : S_VAR; 73 | b = p; 74 | } 75 | } 76 | } 77 | 78 | if (S_VAR == state) 79 | { 80 | ugh_idx_variable(cfg, b + 1, p - b - 1); 81 | } 82 | 83 | t->chunks[cur_chunk].data = b; 84 | t->chunks[cur_chunk].size = p - b; 85 | cur_chunk++; 86 | 87 | return 0; 88 | } 89 | 90 | strp ugh_template_execute(ugh_template_t *t, ugh_client_t *c) 91 | { 92 | if (1 == t->chunks_size) 93 | { 94 | if (t->chunks[0].size > 0 && '$' == t->chunks[0].data[0]) 95 | { 96 | return ugh_get_varvalue(c, t->chunks[0].data + 1, t->chunks[0].size - 1); 97 | } 98 | else 99 | { 100 | return &t->chunks[0]; 101 | } 102 | } 103 | 104 | size_t i; 105 | size_t res_capacity = UGH_TEMPLATE_MAX_LENGTH; 106 | 107 | strp res = aux_pool_malloc(c->pool, sizeof(*res)); 108 | if (NULL == res) return &aux_empty_string; 109 | 110 | res->data = aux_pool_nalloc(c->pool, res_capacity); 111 | if (NULL == res->data) return &aux_empty_string; 112 | 113 | res->size = 0; 114 | 115 | for (i = 0; i < t->chunks_size; ++i) 116 | { 117 | if (t->chunks[i].size > 0 && '$' == t->chunks[i].data[0]) 118 | { 119 | strp vv = ugh_get_varvalue(c, t->chunks[i].data + 1, t->chunks[i].size - 1); 120 | 121 | if (res_capacity < res->size + vv->size) /* TODO make a function for this */ 122 | { 123 | char *old_data = res->data; 124 | 125 | res_capacity = res_capacity < vv->size ? res_capacity + vv->size : res_capacity * 2; 126 | res->data = aux_pool_nalloc(c->pool, res_capacity); 127 | if (NULL == res->data) return &aux_empty_string; 128 | 129 | memcpy(res->data, old_data, res->size); 130 | } 131 | 132 | res->size += aux_cpymsz(res->data + res->size, vv->data, vv->size); 133 | } 134 | else 135 | { 136 | if (res_capacity < res->size + t->chunks[i].size) /* TODO make a function for this */ 137 | { 138 | char *old_data = res->data; 139 | 140 | res_capacity = res_capacity < t->chunks[i].size ? res_capacity + t->chunks[i].size : res_capacity * 2; 141 | res->data = aux_pool_nalloc(c->pool, res_capacity); 142 | if (NULL == res->data) return &aux_empty_string; 143 | 144 | memcpy(res->data, old_data, res->size); 145 | } 146 | 147 | res->size += aux_cpymsz(res->data + res->size, t->chunks[i].data, t->chunks[i].size); 148 | } 149 | } 150 | 151 | return res; 152 | } 153 | 154 | -------------------------------------------------------------------------------- /ugh/ugh.cfg.in: -------------------------------------------------------------------------------- 1 | error_log @CMAKE_INSTALL_PREFIX@/var/log/ugh.log info; 2 | listen 19454; 3 | #resolver 192.168.0.1; 4 | 5 | upstream ADVACTION { 6 | server f10.advaction.ru; 7 | server f11.advaction.ru:80; 8 | backup f10.advaction.ru; 9 | } 10 | 11 | set $round_robin_var { 12 | 127.0.0.1/rr1; 13 | 127.0.0.1/rr2; 14 | 127.0.0.1/rr3; 15 | } 16 | 17 | map $cl_hash_arg_cid $subreq_addr { 18 | 0 127.0.0.1; 19 | 1 127.0.1.1; 20 | 2 127.0.2.1; 21 | 3 127.0.3.1; 22 | 4 127.0.4.1; 23 | 5 127.0.5.1; 24 | 6 127.0.6.1; 25 | 7 127.0.7.1; 26 | 8 127.0.8.1; 27 | 9 127.0.9.1; 28 | 127.1.0.1; 29 | } 30 | 31 | map $cl_hash_arg_cid $subreq_port { 32 | 0 15000; 33 | 1 15001; 34 | 2 15002; 35 | 3 15003; 36 | 4 15004; 37 | 5 15005; 38 | 6 15006; 39 | 7 15007; 40 | 8 15008; 41 | 9 15009; 42 | 15010; 43 | } 44 | 45 | proxy_next_upstream error timeout http_5xx http_4xx; 46 | 47 | proxy_pass ADVACTION; 48 | #proxy_pass 94.79.54.194; 49 | #proxy_pass 94.79.55.206:15000; 50 | #proxy_pass 127.0.0.1:19455; 51 | #proxy_pass $subreq_uri; 52 | #proxy_pass $subreq_uri/h; 53 | #proxy_pass $subreq_uri/haha?a=T&b=P; 54 | #proxy_pass 127.0.0.1:15010; 55 | #proxy_pass 127.0.0.1:1502$cl_hash_arg_cid; 56 | #proxy_pass yandex.ru; 57 | #proxy_pass localhost:$subreq_port; 58 | #proxy_pass $arg_u wait; 59 | #return localhost:$subreq_port=RESPONSE,arg_u=$arg_u; 60 | return ; 61 | return subreq_addr=$subreq_addr,; 62 | return subreq_port=$subreq_port,; 63 | return ; 64 | return $round_robin_var; 65 | #return " 66 | #'END' 67 | #"; 68 | #return ' 69 | #"END SINGLE QUOTES" 70 | #'; 71 | #return ""; 72 | #return $http_host,$http_user_agent; 73 | 74 | -------------------------------------------------------------------------------- /ugh/upstream.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | ugh_upstream_t *ugh_upstream_add(ugh_config_t *cfg, const char *name, size_t size) 4 | { 5 | void **dest; 6 | ugh_upstream_t *u; 7 | 8 | dest = JudyLIns(&cfg->upstreams_hash, aux_hash_key(name, size), PJE0); 9 | if (PJERR == dest) return NULL; 10 | 11 | u = aux_pool_calloc(cfg->pool, sizeof(*u)); 12 | if (NULL == u) return NULL; 13 | 14 | *dest = u; 15 | 16 | return u; 17 | } 18 | 19 | ugh_upstream_t *ugh_upstream_get(ugh_config_t *cfg, const char *name, size_t size) 20 | { 21 | void **dest; 22 | 23 | dest = JudyLGet(cfg->upstreams_hash, aux_hash_key(name, size), PJE0); 24 | if (PJERR == dest || NULL == dest) return NULL; 25 | 26 | return *dest; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ugh/variables.c: -------------------------------------------------------------------------------- 1 | #include "ugh.h" 2 | 3 | strp ugh_variable_var_(ugh_client_t *c, const char *name, size_t size, void *data) 4 | { 5 | return ugh_client_getvar(c, name + 4, size - 4); 6 | } 7 | 8 | strp ugh_variable_arg_(ugh_client_t *c, const char *name, size_t size, void *data) 9 | { 10 | return ugh_client_getarg(c, name + 4, size - 4); 11 | } 12 | 13 | strp ugh_variable_http_(ugh_client_t *c, const char *name, size_t size, void *data) 14 | { 15 | ugh_header_t *h = ugh_client_header_get(c, name + 5, size - 5); 16 | if (NULL == h) return &aux_empty_string; 17 | 18 | return &h->value; 19 | } 20 | 21 | strp ugh_variable_cookie_(ugh_client_t *c, const char *name, size_t size, void *data) 22 | { 23 | return ugh_client_cookie_get(c, name + 7, size - 7); 24 | } 25 | 26 | strp ugh_variable_body_(ugh_client_t *c, const char *name, size_t size, void *data) 27 | { 28 | return ugh_client_body_getarg(c, name + 5, size - 5); 29 | } 30 | 31 | strp ugh_variable_hash_(ugh_client_t *c, const char *name, size_t size, void *data) 32 | { 33 | strp val = ugh_get_varvalue(c, name + 5, size - 5); 34 | uintptr_t key = aux_hash_key(val->data, val->size); 35 | 36 | strp res = aux_pool_malloc(c->pool, sizeof(*res)); 37 | if (NULL == res) return &aux_empty_string; 38 | 39 | res->data = aux_pool_nalloc(c->pool, 32); 40 | if (NULL == res->data) return &aux_empty_string; 41 | 42 | res->size = snprintf(res->data, 32, "%"PRIuPTR, key); 43 | 44 | return res; 45 | } 46 | 47 | strp ugh_variable_c0_(ugh_client_t *c, const char *name, size_t size, void *data) 48 | { 49 | strp val = ugh_get_varvalue(c, name + 3, size - 3); 50 | if (1 > val->size) return &aux_empty_string; 51 | 52 | strp res = aux_pool_malloc(c->pool, sizeof(*res)); 53 | if (NULL == res) return &aux_empty_string; 54 | 55 | res->data = val->data; 56 | res->size = 1; 57 | 58 | return res; 59 | } 60 | 61 | strp ugh_variable_cl_(ugh_client_t *c, const char *name, size_t size, void *data) 62 | { 63 | strp val = ugh_get_varvalue(c, name + 3, size - 3); 64 | if (1 > val->size) return &aux_empty_string; 65 | 66 | strp res = aux_pool_malloc(c->pool, sizeof(*res)); 67 | if (NULL == res) return &aux_empty_string; 68 | 69 | res->data = val->data + val->size - 1; 70 | res->size = 1; 71 | 72 | return res; 73 | } 74 | 75 | ugh_variable_t ugh_variables [] = 76 | { 77 | { aux_string("c0_") , ugh_variable_c0_ , NULL }, 78 | { aux_string("cl_") , ugh_variable_cl_ , NULL }, 79 | { aux_string("hash_") , ugh_variable_hash_ , NULL }, 80 | { aux_string("arg_") , ugh_variable_arg_ , NULL }, 81 | #if 1 82 | { aux_string("var_") , ugh_variable_var_ , NULL }, 83 | #endif 84 | { aux_string("http_") , ugh_variable_http_ , NULL }, 85 | { aux_string("cookie_"), ugh_variable_cookie_, NULL }, 86 | { aux_string("body_") , ugh_variable_body_ , NULL }, 87 | { aux_null_string , NULL , NULL }, 88 | }; 89 | 90 | ugh_variable_t *ugh_set_variable(ugh_config_t *cfg, const char *name, size_t size, ugh_variable_t *var) 91 | { 92 | void **dest = JudyLIns(&cfg->vars_hash, aux_hash_key(name, size), PJE0); 93 | if (PJERR == dest) return NULL; 94 | 95 | *dest = var; 96 | 97 | return var; 98 | } 99 | 100 | ugh_variable_t *ugh_get_variable(ugh_config_t *cfg, const char *name, size_t size) 101 | { 102 | void **dest = JudyLGet(cfg->vars_hash, aux_hash_key(name, size), PJE0); 103 | if (PJERR == dest || NULL == dest) return NULL; 104 | 105 | return *dest; 106 | } 107 | 108 | void ugh_idx_variable(ugh_config_t *cfg, const char *name, size_t size) 109 | { 110 | ugh_variable_t *var; 111 | 112 | for (var = ugh_variables; var->name.data; ++var) 113 | { 114 | if (0 != strncmp(name, var->name.data, var->name.size)) 115 | continue; 116 | 117 | /* TODO return error if some component was not found */ 118 | 119 | ugh_idx_variable(cfg, name + var->name.size, size - var->name.size); 120 | ugh_set_variable(cfg, name, size, var); 121 | 122 | break; 123 | } 124 | } 125 | 126 | strp ugh_get_varvalue(ugh_client_t *c, const char *name, size_t size) 127 | { 128 | ugh_variable_t *var = ugh_get_variable(c->s->cfg, name, size); 129 | if (NULL == var) return &aux_empty_string; 130 | 131 | return var->handle(c, name, size, var->data); 132 | } 133 | 134 | -------------------------------------------------------------------------------- /ugh_example/config.cfg.in: -------------------------------------------------------------------------------- 1 | # vim:set ft=nginx: 2 | error_log @CMAKE_INSTALL_PREFIX@/var/log/ugh_example.log info; 3 | listen 19454; 4 | #resolver 192.168.0.1; 5 | 6 | import example "@CMAKE_INSTALL_PREFIX@/lib/ugh/libugh_example.so"; 7 | 8 | example; 9 | example_session_host localhost; 10 | example_friends_host localhost; 11 | example_wall_host localhost; 12 | example_logger_host localhost; 13 | 14 | -------------------------------------------------------------------------------- /ugh_example/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct 4 | { 5 | ugh_template_t session_host; 6 | ugh_template_t friends_host; 7 | ugh_template_t wall_host; 8 | ugh_template_t logger_host; 9 | } ugh_module_example_conf_t; 10 | 11 | extern ugh_module_t ugh_module_example; 12 | 13 | int ugh_module_example_handle(ugh_client_t *c, void *data, strp body) 14 | { 15 | ugh_module_example_conf_t *conf = data; /* get module conf */ 16 | 17 | strp cookie_sid = ugh_client_cookie_get(c, "sid", 3); 18 | 19 | /* order one session subrequest */ 20 | 21 | strp session_host = ugh_template_execute(&conf->session_host, c); 22 | ugh_subreq_t *r_session = ugh_subreq_add(c, session_host->data, session_host->size, UGH_SUBREQ_WAIT); 23 | ugh_subreq_set_body(r_session, cookie_sid->data, cookie_sid->size); 24 | ugh_subreq_run(r_session); 25 | 26 | /* wait for it (do smth in different coroutine while it is being downloaded) */ 27 | 28 | ugh_subreq_wait(c); 29 | 30 | if (0 == cookie_sid->size) 31 | { 32 | ugh_client_header_out_set(c, "Set-Cookie", sizeof("Set-Cookie") - 1, r_session->body.data, r_session->body.size); 33 | 34 | return UGH_HTTP_OK; 35 | } 36 | 37 | if (0 == r_session->body.size) 38 | { 39 | body->data = "Please login [login form]"; 40 | body->size = sizeof("Please login [login form]") - 1; 41 | 42 | return UGH_HTTP_OK; 43 | } 44 | 45 | /* order friends subrequest */ 46 | 47 | strp friends_host = ugh_template_execute(&conf->friends_host, c); 48 | ugh_subreq_t *r_friends = ugh_subreq_add(c, friends_host->data, friends_host->size, UGH_SUBREQ_WAIT); 49 | ugh_subreq_set_body(r_friends, r_session->body.data, r_session->body.size); 50 | ugh_subreq_run(r_friends); 51 | 52 | /* order wall subrequest */ 53 | 54 | strp wall_host = ugh_template_execute(&conf->wall_host, c); 55 | ugh_subreq_t *r_wall = ugh_subreq_add(c, wall_host->data, wall_host->size, UGH_SUBREQ_WAIT); 56 | ugh_subreq_set_body(r_wall, r_session->body.data, r_session->body.size); 57 | ugh_subreq_run(r_wall); 58 | 59 | /* order logger subrequest, but tell ugh not to wait for its result */ 60 | 61 | strp logger_host = ugh_template_execute(&conf->logger_host, c); 62 | ugh_subreq_t *r_logger = ugh_subreq_add(c, logger_host->data, logger_host->size, 0); 63 | ugh_subreq_set_body(r_logger, r_session->body.data, r_session->body.size); 64 | ugh_subreq_run(r_logger); 65 | 66 | /* wait for two ordered subrequests */ 67 | 68 | ugh_subreq_wait(c); 69 | 70 | /* set body */ 71 | 72 | body->size = 0; 73 | body->data = aux_pool_malloc(c->pool, r_friends->body.size + r_wall->body.size); 74 | if (NULL == body->data) return UGH_HTTP_INTERNAL_SERVER_ERROR; 75 | 76 | body->size += aux_cpymsz(body->data + body->size, r_friends->body.data, r_friends->body.size); 77 | body->size += aux_cpymsz(body->data + body->size, r_wall->body.data, r_wall->body.size); 78 | 79 | /* return 200 OK status */ 80 | 81 | return UGH_HTTP_OK; 82 | } 83 | 84 | int ugh_module_example_init(ugh_config_t *cfg, void *data) 85 | { 86 | ugh_module_example_conf_t *conf = data; 87 | 88 | log_info("ugh_module_example_init (called for each added handle, each time server is starting), conf=%p", &conf); 89 | 90 | return 0; 91 | } 92 | 93 | int ugh_module_example_free(ugh_config_t *cfg, void *data) 94 | { 95 | log_info("ugh_module_example_free (called for each added handle, each time server is stopped)"); 96 | 97 | return 0; 98 | } 99 | 100 | int ugh_command_example(ugh_config_t *cfg, int argc, char **argv, ugh_command_t *cmd) 101 | { 102 | ugh_module_example_conf_t *conf; 103 | 104 | conf = aux_pool_malloc(cfg->pool, sizeof(*conf)); 105 | if (NULL == conf) return -1; 106 | 107 | ugh_module_handle_add(ugh_module_example, conf, ugh_module_example_handle); 108 | 109 | return 0; 110 | } 111 | 112 | static ugh_command_t ugh_module_example_cmds [] = 113 | { 114 | ugh_make_command(example), 115 | ugh_make_command_template(example_session_host, ugh_module_example_conf_t, session_host), 116 | ugh_make_command_template(example_friends_host, ugh_module_example_conf_t, friends_host), 117 | ugh_make_command_template(example_wall_host , ugh_module_example_conf_t, wall_host), 118 | ugh_make_command_template(example_logger_host , ugh_module_example_conf_t, logger_host), 119 | ugh_null_command 120 | }; 121 | 122 | ugh_module_t ugh_module_example = 123 | { 124 | ugh_module_example_cmds, 125 | ugh_module_example_init, 126 | ugh_module_example_free 127 | }; 128 | 129 | --------------------------------------------------------------------------------