├── .gitmodules ├── CMakeLists.txt ├── DockerExample ├── Dockerfile_ngx_ffunc ├── ffunc_profile ├── profile_service_daemonize.c └── start_ffunc.sh ├── LICENSE ├── README.md ├── Testing └── Temporary │ ├── CTestCostData.txt │ └── LastTest.log ├── cmake_uninstall.cmake.in ├── examples ├── profile_service.c ├── profile_service.cpp └── service_with_spdlog.cpp ├── extra_tips.md ├── ffunc.c ├── ffunc.h ├── images ├── fcgi_func_architecture.png └── simple-flow.png └── payload.txt /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Taymindis/fcgi-function/1a231c95af59193e9d441f050e6b6e9e2953ec2a/.gitmodules -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.2) 2 | if (APPLE) 3 | cmake_policy(SET CMP0042 NEW) 4 | endif() 5 | 6 | 7 | if(UNIX AND NOT APPLE) 8 | set(FCGIFUNC_LINUX TRUE) 9 | else(UNIX AND NOT APPLE) 10 | set(FCGIFUNC_MACOSX TRUE) 11 | set(FCGIFUNC_CMAKE_MACOSX_RPATH 1) 12 | endif (UNIX AND NOT APPLE) 13 | 14 | set(PROJ_FCGIFUNC ffunc) 15 | project(ffunc 16 | VERSION 2.1 17 | LANGUAGES C) 18 | 19 | # Some tweak parameters 20 | SET(PWD ${CMAKE_CURRENT_SOURCE_DIR}) 21 | 22 | IF (NOT DEFINED CMAKE_INSTALL_BINDIR) 23 | SET(CMAKE_INSTALL_BINDIR /usr/local/bin) 24 | ENDIF (NOT DEFINED CMAKE_INSTALL_BINDIR) 25 | 26 | IF (NOT DEFINED CMAKE_INSTALL_LIBDIR) 27 | SET(CMAKE_INSTALL_LIBDIR /usr/local/lib) 28 | ENDIF (NOT DEFINED CMAKE_INSTALL_LIBDIR) 29 | 30 | IF (NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) 31 | SET(CMAKE_INSTALL_INCLUDEDIR /usr/local/include) 32 | ENDIF(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) 33 | 34 | MESSAGE(STATUS "ffunc LIBRARY DIRECTORY located at ${CMAKE_INSTALL_LIBDIR}") 35 | message(STATUS "Current SOURCES DIRECTORY located at ${CMAKE_CURRENT_SOURCE_DIR}") 36 | message(STATUS "Current Binary DIRECTORY located at ${CMAKE_CURRENT_BINARY_DIR}") 37 | 38 | SET(LIBPATH /usr/lib /usr/local/lib) 39 | 40 | include_directories(include /usr/local/include) 41 | 42 | add_definitions(-D_XOPEN_SOURCE) 43 | 44 | 45 | # add_definitions(-DFDYMEMDETECT=true) 46 | 47 | # for static libary find first priority 48 | # find_library(FCGI_LIB NAMES libfcgi.a PATHS ${LIBPATH}) 49 | # for dynamic library find second priority 50 | find_library(FCGI_LIB NAMES fcgi PATHS ${LIBPATH}) 51 | if(FCGI_LIB) 52 | message(STATUS "fcgi found at ${FCGI_LIB}. fcgi support is compiled in.") 53 | add_definitions(-DFCGI=true) 54 | else(FCGI_LIB) 55 | message( FATAL_ERROR "fcgi not found. Please install fcgi") 56 | endif(FCGI_LIB) 57 | 58 | find_library(THREAD_LIB NAMES pthread PATHS ${LIBPATH}) 59 | if(THREAD_LIB) 60 | else(THREAD_LIB) 61 | message( FATAL_ERROR "pthread not found. Please install pthread") 62 | endif(THREAD_LIB) 63 | 64 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wstrict-prototypes -Wmissing-prototypes") 65 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual") 66 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-compare -Iinclude -std=c11 -pedantic") 67 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 68 | 69 | # Not need to export dynamic symbol out, -rdyanmic only for user application 70 | # SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-rdynamic") # LDFLAGS= 71 | # gcc -fPIC -pie -o libffunc.so src/ffunc_core.c src/ffunc_pool.c -Iinclude -Wl,-E -Wl,--whole-archive -lfcgi -latomic -ldl -Wl,--no-whole-archive 72 | 73 | if(UNIX AND NOT APPLE) 74 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pie -Wl,-E") 75 | else(UNIX AND NOT APPLE) 76 | add_definitions(-DFCGIFUNC_MACOSX=true) 77 | endif(UNIX AND NOT APPLE) 78 | 79 | # uninstall target 80 | configure_file( 81 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 82 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 83 | IMMEDIATE @ONLY) 84 | 85 | add_custom_target(uninstall 86 | COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 87 | 88 | SET (SRCS ffunc.c) 89 | SET (HDRS ffunc.h) 90 | 91 | add_library(${PROJ_FCGIFUNC}.static STATIC ${HDRS} ${SRCS}) 92 | set_target_properties(${PROJ_FCGIFUNC}.static PROPERTIES OUTPUT_NAME ${PROJ_FCGIFUNC}) 93 | 94 | add_library(${PROJ_FCGIFUNC} SHARED ${HDRS} ${SRCS}) 95 | 96 | link_directories(/usr/local/lib /usr/lib) 97 | 98 | # target_link_libraries(ffunc ${PROJ_FCGIFUNC}) 99 | # target_link_libraries(ffunc fcgi dl) 100 | 101 | if(UNIX AND NOT APPLE) 102 | # target_link_libraries(${PROJ_FCGIFUNC} ${LIB_DL}) #static link to fcgi 103 | target_link_libraries(${PROJ_FCGIFUNC} fcgi dl pthread) #dynamic link to -ldl 104 | # target_link_libraries(${PROJ_FCGIFUNC}.static ${LIB_DL}) #static link 105 | target_link_libraries(${PROJ_FCGIFUNC}.static fcgi dl pthread) #dynamic link to -ldl 106 | else(UNIX AND NOT APPLE) 107 | # target_link_libraries(${PROJ_FCGIFUNC} ${LIB_DL}) #static link to fcgi 108 | target_link_libraries(${PROJ_FCGIFUNC} fcgi dl pthread) #dynamic link to -ldl 109 | # target_link_libraries(${PROJ_FCGIFUNC}.static ${LIB_DL}) #static link 110 | target_link_libraries(${PROJ_FCGIFUNC}.static fcgi dl pthread) #dynamic link to -ldl 111 | endif(UNIX AND NOT APPLE) 112 | 113 | 114 | install(TARGETS ${PROJ_FCGIFUNC} EXPORT ${PROJ_FCGIFUNC} 115 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 116 | LIBRARY DESTINATION lib${LIB_SUFFIX} 117 | # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 118 | ) 119 | install(TARGETS ${PROJ_FCGIFUNC}.static EXPORT ${PROJ_FCGIFUNC} 120 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 121 | LIBRARY DESTINATION lib${LIB_SUFFIX} 122 | # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 123 | ) 124 | install(FILES ffunc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 125 | 126 | # install(TARGETS ffunc EXPORT ffunc DESTINATION ${CMAKE_INSTALL_BINDIR}) 127 | install(CODE "execute_process(COMMAND ldconfig)") 128 | 129 | # IF (${CMAKE_BUILD_TYPE} MATCHES "Debug") 130 | # add_definitions(-D__DEBUG__) 131 | # SET(CMAKE_C_FLAGS "-Wall -O0 -fPIC ${CMAKE_C_FLAGS}") 132 | # SET(CMAKE_CXX_FLAGS "-Wall -O0 -fPIC ${CMAKE_CXX_FLAGS}") 133 | # ELSE (${CMAKE_BUILD_TYPE} MATCHES "Debug") 134 | # SET(CMAKE_C_FLAGS "-Wall -O2 -fPIC ${CMAKE_C_FLAGS}") 135 | # SET(CMAKE_CXX_FLAGS "-Wall -O2 -fPIC ${CMAKE_CXX_FLAGS}") 136 | # ENDIF (${CMAKE_BUILD_TYPE} MATCHES "Debug") 137 | 138 | -------------------------------------------------------------------------------- /DockerExample/Dockerfile_ngx_ffunc: -------------------------------------------------------------------------------- 1 | #Download base image NGINX 2 | # docker build -t nginx_ffunc -f Dockerfile_ngx_ffunc . 3 | # docker run -d -v ~/fcgi-function/DockerExample:/tmp -p 80:80 --name testffunc nginx_ffunc 4 | FROM nginx 5 | 6 | # Update Software repository, install more libraries if you need it 7 | RUN apt-get update 8 | #RUN ["apt-get", "install", "-y", "vim"] 9 | #RUN ["apt-get", "install", "-y", "procps"] 10 | #RUN ["apt-get", "install", "-y", "net-tools"] 11 | RUN ["apt-get", "install", "-y", "wget"] 12 | RUN ["apt-get", "install", "-y", "build-essential"] 13 | RUN ["apt-get", "install", "-y", "make"] 14 | RUN ["apt-get", "install", "-y", "cmake"] 15 | RUN ["apt-get", "install", "-y", "git"] 16 | 17 | WORKDIR "/opt" 18 | RUN wget https://github.com/FastCGI-Archives/FastCGI.com/raw/master/original_snapshot/fcgi-2.4.1-SNAP-0910052249.tar.gz 19 | RUN tar -zxvf fcgi-2.4.1-SNAP-0910052249.tar.gz 20 | WORKDIR "/opt/fcgi-2.4.1-SNAP-0910052249" 21 | RUN ./configure 22 | RUN make && make install 23 | WORKDIR "/opt" 24 | RUN git clone https://github.com/Taymindis/fcgi-function.git 25 | WORKDIR "/opt/fcgi-function" 26 | 27 | RUN ldconfig 28 | #RUN gcc -I./ examples/profile_service_daemonize.c ffunc.c ffunc.h -lfcgi -pthread -ldl -rdynamic -o ffuncsample 29 | #RUN ./ffuncsample 30 | 31 | #RUN sed -i '/listen[[:space:]]\+80;/a include fcgi_func.conf;' /etc/nginx/conf.d/default.conf 32 | 33 | 34 | # Configure Services 35 | #COPY profile_service_daemonize.c /tmp/profile_service_daemonize.c 36 | #COPY ffunc_profile /etc/nginx/conf.d/ffunc_profile 37 | #COPY start_ffunc.sh /tmp/start_ffunc.sh 38 | #RUN chmod 755 /tmp/start_ffunc.sh 39 | WORKDIR "/tmp" 40 | CMD chmod 755 /tmp/start_ffunc.sh && ./start_ffunc.sh 41 | -------------------------------------------------------------------------------- /DockerExample/ffunc_profile: -------------------------------------------------------------------------------- 1 | location /getProfile { 2 | add_header Allow "GET, POST, HEAD" always; 3 | if ( $request_method !~ ^(GET|HEAD)$ ) { 4 | return 405; 5 | } 6 | include /etc/nginx/fastcgi_params; 7 | fastcgi_param FN_HANDLER getProfile; 8 | fastcgi_pass 127.0.0.1:2005; 9 | } 10 | 11 | location /postProfile { 12 | add_header Allow "GET, POST, HEAD" always; 13 | if ( $request_method !~ ^(POST)$ ) { 14 | return 405; 15 | } 16 | include /etc/nginx/fastcgi_params; 17 | fastcgi_param FN_HANDLER postProfile; 18 | fastcgi_pass 127.0.0.1:2005; 19 | } 20 | -------------------------------------------------------------------------------- /DockerExample/profile_service_daemonize.c: -------------------------------------------------------------------------------- 1 | #include "ffunc.h" 2 | 3 | /*Compile*/ 4 | /*gcc profile_service.c -I../ ../ffunc.c -lfcgi -pthread -ldl -rdynamic*/ 5 | static int req_count = 0; 6 | FFUNC getProfile(ffunc_session_t * session) { 7 | ffunc_write_out(session, "Status: 200 OK\r\n"); 8 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n");/* \r\n\r\n means go to response message*/ 9 | ffunc_write_out(session, "%s\n", "you reach here, welcome to fcgi function container"); 10 | if (session->query_str) { 11 | char *out = ffunc_get_query_param(session, "userId", sizeof("userId") - 1); 12 | if (out) 13 | ffunc_write_out(session, "output= %s\n", out); //cjson undefined because only use it's own file 14 | 15 | out = ffunc_get_query_param(session, "userId2", sizeof("userId2") - 1); 16 | if (out) 17 | ffunc_write_out(session, "output 2= %s\n", out); //cjson undefined because only use it's own file 18 | } 19 | } 20 | 21 | FFUNC postError(ffunc_session_t * session) { 22 | printf("%s\n", "you reach here with post Error test"); 23 | ffunc_write_out(session, "Status: 500 Internal Server Error\r\n"); 24 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n"); 25 | ffunc_write_out(session, "%s\n", "you hitting error"); 26 | } 27 | 28 | FFUNC postProfile(ffunc_session_t * session) { 29 | // not need to free, csession handle it 30 | ffunc_str_t payload; 31 | if( ffunc_read_body(session, &payload) ) { 32 | printf("the sz is = %ld\n", payload.len); 33 | ffunc_write_out(session, "Status: 200 OK\r\n"); 34 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 35 | ffunc_write_out(session, "Query String %s\n", session->query_str?session->query_str:""); 36 | ffunc_write_out(session, "Body %s\n", payload.data); 37 | } 38 | } 39 | 40 | /* if this function given, whatever function not provided in nginx, it will go to this consume to let user handle the function */ 41 | FFUNC ffunc_direct_consume(ffunc_session_t * session) { 42 | // not need to free, csession handle it 43 | ffunc_str_t payload; 44 | if( ffunc_read_body(session, &payload) ) { 45 | printf("the sz is = %ld\n", payload.len); 46 | ffunc_write_out(session, "Status: 200 OK\r\n"); 47 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 48 | ffunc_write_out(session, "Query String %s\n", session->query_str?session->query_str:""); 49 | ffunc_write_out(session, "Body %.*s\n", payload.len, payload.data); 50 | } 51 | } 52 | 53 | int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf) { 54 | ffunc_conf->sock_port = 2005; 55 | ffunc_conf->backlog = 160; 56 | ffunc_conf->max_thread = 64; 57 | ffunc_conf->daemon = 1; 58 | ffunc_parse_function(ffunc_conf, "getProfile", "postError", "postProfile"); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /DockerExample/start_ffunc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cp ffunc_profile /etc/nginx/conf.d/ffunc_profile 4 | 5 | sed -i '/include conf.d\/ffunc_profile;/d ' /etc/nginx/conf.d/default.conf 6 | sed -i '/listen[[:space:]]\+80;/a include conf.d/ffunc_profile;' /etc/nginx/conf.d/default.conf 7 | 8 | gcc -I/opt/fcgi-function /tmp/profile_service_daemonize.c /opt/fcgi-function/ffunc.c -lfcgi -pthread -ldl -rdynamic -o ffuncsample 9 | ./ffuncsample 10 | 11 | nginx -g "daemon off;" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Taymindis Woon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fcgi-function 2 | fcgi Function is a C/CPP language based interface, which build on top of FastCGI with built in functional handler support, it also provided useful collection tools to enhance the facilities. It can be integrated with Nginx Fastcgi module 3 | 4 | ## How it works 5 | ![Image of architecture](/images/fcgi_func_architecture.png) 6 | 7 | 8 | ## Prerequisition Installed 9 | Nginx -- https://www.nginx.com/ 10 | 11 | fcgi library -- https://github.com/FastCGI-Archives/FastCGI.com 12 | 13 | cmake and make 14 | 15 | ### Supported OS: LINUX, MAC OSX, and Windows 16 | 17 | ## Clone the Repository Recursively 18 | git clone --recursive https://github.com/Taymindis/fcgi-function.git 19 | 20 | ## How to Build 21 | 22 | ### Just Include ffunc.h and ffunc.c in your project, simplest way ( applicable to Linux, Darwin and Windows platforms ) 23 | #### Build sample 24 | > gcc profile_service.c ffunc.c ffunc.h -lfcgi -pthread -ldl -rdynamic 25 | 26 | > g++ profile_service.cpp ffunc.c ffunc.h -lfcgi -pthread -ldl -rdynamic 27 | 28 | > g++ -std=c++11 -I/home/xxx/cpplib/spdlog-0.17.0/include -DSPDLOG_FMT_PRINTF service_with_spdlog.cpp ffunc.c ffunc.h -lfcgi -pthread -ldl -rdynamic 29 | 30 | ### when you type 31 | 32 | > ./simple_service 33 | 34 | it will result:- 35 | 36 | ```bash 37 | 38 | Service starting 39 | sock_port=2005, backlog=16 40 | total function = 3 41 | 64 threads in process 42 | Socket on hook 2005 43 | 44 | ``` 45 | 46 | 47 | Running on background by setting `ffunc_conf->daemon = 1 `, see example for mode details 48 | ```c 49 | int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf) { 50 | ffunc_conf->sock_port = 2005; 51 | ffunc_conf->backlog = 160; 52 | ffunc_conf->max_thread = 64; 53 | ffunc_conf->daemon = 1; // run as daemon 54 | ffunc_parse_function(ffunc_conf, "getProfile", "postError", "postProfile"); 55 | return 0; 56 | } 57 | ``` 58 | 59 | ### Running on valgrind (performance will impact) 60 | > valgrind --leak-check=full --show-leak-kinds=all --trace-children=yes ./simple_service 61 | 62 | ### Edit the nginx.conf in your nginx config folder by append in your server block:- 63 | 64 | location /getProfile { 65 | add_header Allow "GET, POST, HEAD" always; 66 | if ( $request_method !~ ^(GET|HEAD)$ ) { 67 | return 405; 68 | } 69 | include /etc/nginx/fastcgi_params; 70 | fastcgi_param FN_HANDLER getProfile; 71 | fastcgi_pass 127.0.0.1:2005; 72 | } 73 | 74 | location /postProfile { 75 | add_header Allow "GET, POST, HEAD" always; 76 | if ( $request_method !~ ^(POST)$ ) { 77 | return 405; 78 | } 79 | include /etc/nginx/fastcgi_params; 80 | fastcgi_param FN_HANDLER postProfile; 81 | fastcgi_pass 127.0.0.1:2005; 82 | } 83 | 84 | ### Edit the httpd.conf in your httpd config folder by append in your block 85 | 86 | ```httpd 87 | 88 | # Timeout values are in seconds 89 | ProxyPass "fcgi://127.0.0.1:2005" connectiontimeout=300 timeout=300 90 | ProxyPassReverse "fcgi://127.0.0.1:2005" 91 | SetEnv FN_HANDLER "getProfile" 92 | 93 | 94 | ``` 95 | 96 | ***You will see the FN_HANDLER is function name mapping with the function inside simple_service code, the fastcgi port 2005 is the service you start with(please look at step 10 for more details.*** 97 | 98 | ### start the nginx/httpd server 99 | 100 | ### Using apache benchmark for get request load test 101 | 102 | > ab -c 100 -n 10000 http://127.0.0.1:80/getProfile 103 | 104 | 105 | #### For post request load test 106 | 107 | > ab -p "payload.txt" -T application/json -c 100 -n 10000 http://127.0.0.1:80/postProfile 108 | 109 | *the payload.txt is inside the root directory* 110 | 111 | 112 | ### shutdown the background instance 113 | 114 | ##### kill the process by using `kill -2 `, *please do not use -9 as it will direct kill the process, unless -2 is not working* 115 | #### for valgrind log, you will get the summary report after `kill -2 ` 116 | 117 | 118 | ### Example of how to build a docker image 119 | 120 | ```bash 121 | cd DockerExample 122 | docker build -t nginx_ffunc -f Dockerfile_ngx_ffunc . 123 | docker run -d -v ~/fcgi-function/DockerExample:/tmp -p 80:80 --name testffunc nginx_ffunc 124 | curl "http://127.0.0.1/getProfile" 125 | ``` 126 | 127 | 128 | ### Logging Recommendation 129 | Due to built in logging mechanism will slow down the process speed, suggested to use third party logging mechanism for your application layer such as:- 130 | 131 | [C++ spdlog](https://github.com/gabime/spdlog) 132 | 133 | [C++ g3log](https://github.com/KjellKod/g3log) 134 | 135 | [C mini-async-log](https://github.com/RafaGago/mini-async-log) 136 | 137 | 138 | ## Please contact minikawoon2017@gmail.com for More End to End tier project examples. 139 | -------------------------------------------------------------------------------- /Testing/Temporary/CTestCostData.txt: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /Testing/Temporary/LastTest.log: -------------------------------------------------------------------------------- 1 | Start testing: Jun 29 10:34 SGT 2 | ---------------------------------------------------------- 3 | End testing: Jun 29 10:34 SGT 4 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif(NOT "${rm_retval}" STREQUAL 0) 18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 20 | endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | endforeach(file) -------------------------------------------------------------------------------- /examples/profile_service.c: -------------------------------------------------------------------------------- 1 | #include "ffunc.h" 2 | // #include 3 | 4 | /*Compile*/ 5 | /*gcc profile_service.c -I../ ../ffunc.c -lfcgi -pthread -ldl -rdynamic*/ 6 | static int req_count = 0; 7 | FFUNC getProfile(ffunc_session_t * session) { 8 | ffunc_write_out(session, "Status: 200 OK\r\n"); 9 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n");/* \r\n\r\n means go to response message*/ 10 | ffunc_write_out(session, "%s\n", "you are here"); 11 | if (session->query_str) { 12 | char *out = ffunc_get_query_param(session, "userId", sizeof("userId") - 1); 13 | if (out) 14 | ffunc_write_out(session, "output= %s\n", out); //cjson undefined because only use it's own file 15 | 16 | out = ffunc_get_query_param(session, "userId2", sizeof("userId2") - 1); 17 | if (out) 18 | ffunc_write_out(session, "output 2= %s\n", out); //cjson undefined because only use it's own file 19 | } 20 | } 21 | 22 | FFUNC postError(ffunc_session_t * session) { 23 | printf("%s\n", "you reach here with post Error test"); 24 | ffunc_write_out(session, "Status: 500 Internal Server Error\r\n"); 25 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n"); 26 | ffunc_write_out(session, "%s\n", "you hitting error"); 27 | } 28 | 29 | FFUNC postProfile(ffunc_session_t * session) { 30 | // not need to free, csession handle it 31 | ffunc_str_t payload; 32 | if( ffunc_read_body(session, &payload) ) { 33 | printf("the sz is = %ld\n", payload.len); 34 | ffunc_write_out(session, "Status: 200 OK\r\n"); 35 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 36 | ffunc_write_out(session, "Query String %s\n", session->query_str?session->query_str:""); 37 | ffunc_write_out(session, "Body %s\n", payload.data); 38 | } 39 | } 40 | 41 | /* if this function given, whatever function not provided in nginx, it will go to this consume to let user handle the function */ 42 | FFUNC ffunc_direct_consume(ffunc_session_t * session) { 43 | // not need to free, csession handle it 44 | ffunc_str_t payload; 45 | if( ffunc_read_body(session, &payload) ) { 46 | printf("the sz is = %ld\n", payload.len); 47 | ffunc_write_out(session, "Status: 200 OK\r\n"); 48 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 49 | ffunc_write_out(session, "Query String %s\n", session->query_str?session->query_str:""); 50 | ffunc_write_out(session, "Body %.*s\n", payload.len, payload.data); 51 | } 52 | } 53 | 54 | int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf) { 55 | ffunc_conf->sock_port = 2005; 56 | ffunc_conf->backlog = 160; 57 | ffunc_conf->max_thread = 64; 58 | // ffunc_conf->daemon = 1; uncomment if you want to run as daemon 59 | ffunc_parse_function(ffunc_conf, "getProfile", "postError", "postProfile"); 60 | return 0; 61 | } 62 | 63 | 64 | // int memcheck(ffunc_session_t * csession) { 65 | // // printf("%s\n", "you reach here"); 66 | // ffunc_write_out("Status: 200 OK\r\n"); 67 | // ffunc_write_out("Content-Type: text/plain\r\n\r\n"); \r\n\r\n means go to response message 68 | // ffunc_write_out("alloc= %d\n", get_total_malloc_count()); 69 | // ffunc_write_out("free= %d\n", get_total_free_count()); 70 | // ffunc_write_out("leak count= %d\n", get_total_malloc_count() - get_total_free_count()); 71 | // return 1; 72 | // } 73 | -------------------------------------------------------------------------------- /examples/profile_service.cpp: -------------------------------------------------------------------------------- 1 | #include "ffunc.h" 2 | #include 3 | #include 4 | /*** 5 | g++ profile_service.cpp -I../ ../ffunc.c -lfcgi -pthread -ldl -rdynamic 6 | **/ 7 | 8 | class MyType 9 | { 10 | public: 11 | MyType() { 12 | printf("%s\n", "ASD"); 13 | } 14 | ~MyType() { 15 | printf("%s\n", "Value been freed"); 16 | } 17 | }; 18 | 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | FFUNC init_logger_in_instance(void); 25 | 26 | FFUNC init_logger_in_instance() { 27 | fprintf(stderr, "%s\n", "start instance"); 28 | fprintf(stderr, "%s\n", "init logging"); 29 | } 30 | 31 | FFUNC getProfile(ffunc_session_t * session) { 32 | ffunc_write_out(session, "Status: 200 OK\r\n"); 33 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n");/* \r\n\r\n means go to response message*/ 34 | ffunc_write_out(session, "%s\n", "you are here"); 35 | 36 | MyType me; 37 | std::vector a; 38 | a.push_back(1); 39 | 40 | if (session->query_str) { 41 | char *out = (char*) ffunc_get_query_param(session, "userId", sizeof("userId")- 1); 42 | if (out) 43 | ffunc_write_out(session, "output= %s\n", out); //cjson undefined because only use it's own file 44 | } 45 | } 46 | 47 | FFUNC postError(ffunc_session_t * session) { 48 | ffunc_write_out(session, "Status: 500 Internal Server Error\r\n"); 49 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n"); 50 | ffunc_write_out(session, "%s\n", "you hitting error"); 51 | } 52 | 53 | 54 | FFUNC postProfile(ffunc_session_t * session) { 55 | // not need to free, session handle it 56 | ffunc_str_t payload; 57 | if(ffunc_read_body(session, &payload) ) { 58 | ffunc_write_out(session, "Status: 200 OK\r\n"); 59 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 60 | ffunc_write_out(session, "Query String %s\n", session->query_str?session->query_str:""); 61 | ffunc_write_out(session, "Body %s\n", payload.data); 62 | } 63 | 64 | } 65 | 66 | 67 | int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf) { 68 | ffunc_conf->sock_port = 2005; 69 | //ffunc_conf->sock_port_str = "localhost:2005"; 70 | #if defined _WIN32 || _WIN64 /*Windows*/ 71 | ffunc_conf->app_init_handler_name = "init_logger_in_instance"; 72 | #else 73 | ffunc_conf->app_init_handler = &init_logger_in_instance; 74 | #endif 75 | ffunc_conf->backlog = 160; 76 | ffunc_conf->max_thread = 64; 77 | ffunc_parse_function(ffunc_conf, "getProfile", "postError", "postProfile"); 78 | return 0; 79 | } 80 | 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /examples/service_with_spdlog.cpp: -------------------------------------------------------------------------------- 1 | #include "ffunc.h" 2 | #include 3 | #include 4 | /*https://github.com/gabime/spdlog*/ 5 | #include 6 | /*** 7 | Using spdlog 8 | g++ -std=c++11 -I/home/xxx/cpplib/spdlog-0.17.0/include -I../ -DSPDLOG_FMT_PRINTF service_with_spdlog.cpp ../ffunc.c -lfcgi -pthread -ldl -rdynamic 9 | **/ 10 | static std::shared_ptr file_logger = 0; 11 | 12 | class MyType 13 | { 14 | public: 15 | MyType() { 16 | file_logger->info("%s\n", "ASD"); 17 | } 18 | ~MyType() { 19 | file_logger->info("%s\n", "Value been freed"); 20 | } 21 | }; 22 | 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | void init_logger_in_instance(void); 29 | 30 | void init_logger_in_instance() { 31 | fprintf(stderr, "%s\n", "start instance"); 32 | fprintf(stderr, "%s\n", "init logging"); 33 | auto rotating = std::make_shared ("simpledump.log", 1024 * 1024 * 5 /*5MB*/, 5); 34 | file_logger = spdlog::create_async("my_logger", rotating, 8192, 35 | spdlog::async_overflow_policy::block_retry, nullptr, std::chrono::milliseconds{5000}/*flush interval*/, nullptr); 36 | 37 | // file_logger->set_pattern("%v"); // Just message 38 | } 39 | 40 | FFUNC getProfile(ffunc_session_t * session) { 41 | file_logger->info("%s\n", "you reach here with get Request"); 42 | ffunc_write_out(session, "Status: 200 OK\r\n"); 43 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n");/* \r\n\r\n means go to response message*/ 44 | ffunc_write_out(session, "%s\n", "you are here"); 45 | 46 | MyType me; 47 | std::vector a; 48 | a.push_back(1); 49 | 50 | if (session->query_str) { 51 | char *out = (char*) ffunc_get_query_param(session, "userId", sizeof("userId")- 1); 52 | if (out) 53 | ffunc_write_out(session, "output= %s\n", out); //cjson undefined because only use it's own file 54 | } 55 | 56 | } 57 | 58 | FFUNC postError(ffunc_session_t * session) { 59 | file_logger->info("%s\n", "you reach here with post Error test"); 60 | ffunc_write_out(session, "Status: 500 Internal Server Error\r\n"); 61 | ffunc_write_out(session, "Content-Type: text/plain\r\n\r\n"); 62 | ffunc_write_out(session, "%s\n", "you hitting error"); 63 | } 64 | 65 | 66 | FFUNC postProfile(ffunc_session_t * session) { 67 | // not need to free, session handle it 68 | ffunc_str_t payload; 69 | if(ffunc_read_body(session, &payload) ) { 70 | file_logger->info("the sz is = %ld", payload.len); 71 | ffunc_write_out(session, "Status: 200 OK\r\n"); 72 | ffunc_write_out(session, "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); 73 | ffunc_write_out(session, "Query String %s\n", session->query_str); 74 | ffunc_write_out(session, "Body %s\n", payload.data); 75 | file_logger->info("%s\n", payload.data); 76 | } 77 | 78 | } 79 | 80 | int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf) { 81 | ffunc_conf->sock_port = 2005; 82 | ffunc_conf->backlog = 160; 83 | ffunc_conf->max_thread = 64; 84 | ffunc_conf->app_init_handler = init_logger_in_instance; 85 | ffunc_parse_function(ffunc_conf, "getProfile", "postError", "postProfile"); 86 | return 0; 87 | } 88 | 89 | #ifdef __cplusplus 90 | } 91 | #endif 92 | -------------------------------------------------------------------------------- /extra_tips.md: -------------------------------------------------------------------------------- 1 | Extra: 2 | 3 | how to redirect background program output to a file 4 | some_cmd > some_file 2>&1 & 5 | 6 | 7 | -valgrind find memory leak 8 | ``` 9 | valgrind --tool=memcheck --leak-check=full ./a.out -p2005 -w200 -q200 10 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./appservice -p2005 -w400 -q400 -d -e 11 | ``` 12 | -valgrind background attach 13 | ``` 14 | nohup bash -c "valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./sample_services -p2005 -w400 -q400 -d -e" > /home/datafeedx11/valgrind_server.log & 15 | ``` 16 | 17 | 18 | -memleax find memory leak 19 | ``` 20 | sudo memleax -e 7 21 | ``` 22 | -------------------------------------------------------------------------------- /ffunc.c: -------------------------------------------------------------------------------- 1 | #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ 2 | #ifndef _GNU_SOURCE 3 | #define _GNU_SOURCE 4 | #endif 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include /* needed to define getpid() */ 12 | #include 13 | // #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "ffunc.h" 22 | 23 | #define _get_param_(KEY) FCGX_GetParam(KEY, request->envp) 24 | #define ffunc_print(...) fprintf (stderr, __VA_ARGS__) 25 | 26 | #define _FFUNC_MASTER_ENV "_ffunc-master_" __TIME__ 27 | #define FFUNC_SETENV setenv 28 | #define FFUNC_GETENV getenv 29 | #define FFUNC_EXECVE execve 30 | #define _FFUNC_MASTER_ " ffunc-master" 31 | #define _FFUNC_WORKER_ " ffunc-worker" 32 | 33 | static void* mem_align(size_t size); 34 | static int ffunc_get_number_of_digit(long long number); 35 | static ffunc_pool* ffunc_recreate_pool(ffunc_pool *curr_p, size_t new_size); 36 | static ffunc_str_t ffunc_proc_name; 37 | 38 | typedef void(*h)(ffunc_session_t*); 39 | 40 | struct ffunc_handler { 41 | char* name; 42 | size_t len; 43 | h handle; 44 | }; 45 | 46 | typedef struct { 47 | int fcgi_func_socket; 48 | pthread_mutex_t accept_mutex; 49 | } ffunc_worker_t; 50 | 51 | static int func_count = 0; 52 | struct ffunc_handler *dyna_funcs = NULL; 53 | 54 | static size_t max_std_input_buffer; 55 | 56 | static int ffunc_init(char** ffunc_nmap_func); 57 | static int ffunc_strpos(const char *haystack, const char *needle); 58 | void *ffunc_thread_worker(void* wrker); 59 | static int hook_socket(int sock_port, char* sock_port_str, int backlog, int max_thread, char** ffunc_nmap_func, int* procpip); 60 | static void ffunc_add_signal_handler(void); 61 | static void handle_request(FCGX_Request *request); 62 | static size_t ffunc_read_body_limit(ffunc_session_t * csession, ffunc_str_t *content); 63 | static size_t ffunc_read_body_nolimit(ffunc_session_t * csession, ffunc_str_t *content); 64 | static void ffunc_default_direct_consume(ffunc_session_t *sess); 65 | static int has_init_signal = 0; 66 | static struct sigaction sa; 67 | static void *usr_req_handle; 68 | // extern declaration 69 | size_t(*ffunc_read_body)(ffunc_session_t *, ffunc_str_t *); 70 | void(*_ffunc_direct_consume)(ffunc_session_t *); 71 | 72 | static int 73 | ffunc_init(char** ffunc_nmap_func) { 74 | int f = 0; 75 | size_t szof_func_name; 76 | usr_req_handle = NULL; 77 | char *error; 78 | usr_req_handle = dlopen(NULL, RTLD_LAZY | RTLD_NOW); 79 | if (!usr_req_handle) { 80 | ffunc_print("%s\n", dlerror()); 81 | ffunc_print("%s\n", "Module not found"); 82 | return 0; 83 | } 84 | 85 | for (func_count = 0; ffunc_nmap_func[func_count]; func_count++); 86 | 87 | dyna_funcs = (struct ffunc_handler*) malloc(func_count * sizeof(struct ffunc_handler)); 88 | for (f = 0; f < func_count; f++) { 89 | // szof_func_name = strlen(ffunc_nmap_func[f]); 90 | dyna_funcs[f].name = ffunc_nmap_func[f]; //(char*) calloc(szof_func_name + 1, sizeof(char)); 91 | 92 | // memcpy(dyna_funcs[f].name, ffunc_nmap_func[f], szof_func_name); 93 | dyna_funcs[f].len = strlen(ffunc_nmap_func[f]); 94 | *(void **)(&dyna_funcs[f].handle) = dlsym(usr_req_handle, ffunc_nmap_func[f]); 95 | 96 | if ((error = dlerror()) != NULL) { 97 | ffunc_print("%s\n", error); 98 | ffunc_print("%s\n", "Have you included -rdynamic in your compiler option, for e.g gcc ... -rdynamic"); 99 | return 0; 100 | } 101 | } 102 | 103 | *(void **)(&_ffunc_direct_consume) = dlsym(usr_req_handle, "ffunc_direct_consume"); 104 | if ((error = dlerror()) != NULL) { 105 | ffunc_print("%s\n", "direct consume disabled"); 106 | _ffunc_direct_consume = &ffunc_default_direct_consume; 107 | } 108 | else { 109 | ffunc_print("%s\n", "direct consume enabled"); 110 | } 111 | return 1; 112 | } 113 | 114 | /**To prevent -std=99 issue**/ 115 | char * 116 | ffunc_strdup(ffunc_session_t * csession, const char *str, size_t len) { 117 | if (len == 0) { 118 | return NULL; 119 | } 120 | char *dupstr = (char*)_ffunc_alloc(&csession->pool, len + 1); 121 | if (dupstr == NULL) { 122 | return NULL; 123 | } 124 | memcpy(dupstr, str, len); 125 | // memset(dupstr, 0, len + 1); 126 | dupstr[len] = (char)0; 127 | return dupstr; 128 | } 129 | 130 | static void 131 | handle_request(FCGX_Request *request) { 132 | char *value, 133 | *qparam, 134 | *query_string; 135 | 136 | ffunc_pool *p = ffunc_create_pool(DEFAULT_BLK_SZ); 137 | ffunc_session_t *_csession_ = (ffunc_session_t*)malloc(sizeof(ffunc_session_t)); 138 | if (p == NULL || _csession_ == NULL) { 139 | fprintf(stderr, "error %s\n", strerror(errno)); 140 | exit(EXIT_FAILURE); 141 | } 142 | _csession_->pool = p; 143 | _csession_->query_str = NULL; 144 | _csession_->request = request; 145 | 146 | if ((value = FCGX_GetParam("FN_HANDLER", request->envp))) { 147 | int i; 148 | size_t vallen = strlen(value); 149 | for (i = 0; i < func_count; i++) { 150 | if (dyna_funcs[i].len == vallen && (strcmp(dyna_funcs[i].name, value) == 0)) { 151 | if ((qparam = _get_param_("QUERY_STRING"))) { 152 | query_string = ffunc_strdup(_csession_, qparam, strlen(qparam)); 153 | if (query_string && query_string[0]) { 154 | _csession_->query_str = query_string;//parse_json_fr_args(query_string); 155 | } 156 | } 157 | dyna_funcs[i].handle(_csession_); 158 | 159 | goto RELEASE_POOL; 160 | } 161 | } 162 | ffunc_default_direct_consume(_csession_); 163 | } 164 | else { 165 | if ((qparam = _get_param_("QUERY_STRING"))) { 166 | query_string = ffunc_strdup(_csession_, qparam, strlen(qparam)); 167 | if (query_string && query_string[0]) { 168 | _csession_->query_str = query_string;//parse_json_fr_args(query_string); 169 | } 170 | } 171 | _ffunc_direct_consume(_csession_); 172 | } 173 | 174 | RELEASE_POOL: 175 | ffunc_destroy_pool(_csession_->pool); 176 | free(_csession_); 177 | } 178 | 179 | static size_t 180 | ffunc_read_body_nolimit(ffunc_session_t * csession, ffunc_str_t *content) { 181 | FCGX_Request *request = csession->request; 182 | char* clenstr = _get_param_("CONTENT_LENGTH"); 183 | FCGX_Stream *in = request->in; 184 | size_t len; 185 | 186 | if (clenstr && ((len = strtol(clenstr, &clenstr, 10)) != 0)) { 187 | content->data = (char*)_ffunc_alloc(&csession->pool, len + 1); 188 | memset(content->data, 0, len + 1); 189 | content->len = FCGX_GetStr(content->data, len, in); 190 | } 191 | else { 192 | content->data = NULL; 193 | content->len = 0; 194 | } 195 | 196 | /* Chew up whats remaining (required by mod_fastcgi) */ 197 | while (FCGX_GetChar(in) != -1); 198 | 199 | return len; 200 | } 201 | 202 | static size_t 203 | ffunc_read_body_limit(ffunc_session_t * csession, ffunc_str_t *content) { 204 | FCGX_Request *request = csession->request; 205 | char* clenstr = _get_param_("CONTENT_LENGTH"); 206 | FCGX_Stream *in = request->in; 207 | size_t len; 208 | 209 | if (clenstr) { 210 | // long int strtol (const char* str, char** endptr, int base); 211 | // endptr -- Reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value. 212 | // This parameter can also be a null pointer, in which case it is not used. 213 | len = strtol(clenstr, &clenstr, 10); 214 | /* Don't read more than the predefined limit */ 215 | if (len > max_std_input_buffer) { len = max_std_input_buffer; } 216 | 217 | content->data = (char*)_ffunc_alloc(&csession->pool, len + 1); 218 | memset(content->data, 0, len + 1); 219 | content->len = FCGX_GetStr(content->data, len, in); 220 | } 221 | /* Don't read if CONTENT_LENGTH is missing or if it can't be parsed */ 222 | else { 223 | content->data = NULL; 224 | content->len = 0; 225 | } 226 | 227 | /* Chew up whats remaining (required by mod_fastcgi) */ 228 | while (FCGX_GetChar(in) != -1); 229 | 230 | return len; 231 | } 232 | 233 | 234 | #define FFUNC_APP_INITIALIZING "INIT" 235 | #define FFUNC_APP_INITIALIZED "DONE" 236 | #define FFUNC_APP_INIT_PIPE_BUF_SIZE sizeof(FFUNC_APP_INITIALIZED) 237 | 238 | /**Main Hook**/ 239 | int 240 | ffunc_hook(ffunc_config_t *conf) { 241 | pid_t child_pid, daemon_pid; 242 | int child_status; 243 | int procpip[2]; 244 | char pipbuf[FFUNC_APP_INIT_PIPE_BUF_SIZE]; 245 | int sock_port = conf->sock_port; 246 | char* sock_port_str = conf->sock_port_str; 247 | int backlog = conf->backlog; 248 | int max_thread = conf->max_thread; 249 | char** ffunc_nmap_func = conf->ffunc_nmap_func; 250 | size_t max_read_buffer = conf->max_read_buffer; 251 | 252 | if (max_read_buffer > 0) { 253 | max_std_input_buffer = max_read_buffer; 254 | ffunc_read_body = &ffunc_read_body_limit; 255 | } 256 | else { 257 | ffunc_read_body = &ffunc_read_body_nolimit; 258 | } 259 | 260 | if (sock_port <= 0 && sock_port_str == NULL) { 261 | ffunc_print("%s\n", "sock_port has no defined..."); 262 | return 1; 263 | } 264 | 265 | if (max_thread <= 0) { 266 | ffunc_print("%s\n", "max_thread has no defined..."); 267 | return 1; 268 | } 269 | 270 | if (backlog <= 0) { 271 | ffunc_print("%s\n", "backlog has no defined..."); 272 | return 1; 273 | } 274 | 275 | if (!ffunc_nmap_func[0]) { 276 | ffunc_print("%s\n", "ffunc_nmap_func has no defined..."); 277 | return 1; 278 | } 279 | 280 | ffunc_print("%s\n", "Service starting"); 281 | if (sock_port) { 282 | ffunc_print("sock_port=%d, backlog=%d\n", sock_port, backlog); 283 | } else if(sock_port_str) { 284 | ffunc_print("sock_port=%s, backlog=%d\n", sock_port_str, backlog); 285 | } 286 | /** Do master pipe before fork **/ 287 | if (pipe(procpip) < 0) 288 | exit(1); 289 | 290 | FFUNC_WORKER_RESTART: 291 | if (conf->daemon) { 292 | daemon_pid = fork(); 293 | 294 | /* An error occurred */ 295 | if (daemon_pid < 0) 296 | exit(EXIT_FAILURE); 297 | 298 | /* Success: Let the parent terminate */ 299 | if (daemon_pid > 0) 300 | exit(EXIT_SUCCESS); 301 | 302 | /* On success: The child process becomes session leader */ 303 | if (setsid() < 0) 304 | exit(EXIT_FAILURE); 305 | } 306 | 307 | if (!has_init_signal) { 308 | ffunc_add_signal_handler(); 309 | } 310 | write(procpip[1], FFUNC_APP_INITIALIZING, FFUNC_APP_INIT_PIPE_BUF_SIZE); 311 | child_pid = fork(); 312 | if (child_pid >= 0) {// fork was successful 313 | if (child_pid == 0) {// child process 314 | char *swap_name = (ffunc_proc_name.data + ffunc_proc_name.len) - (sizeof(_FFUNC_MASTER_) - 1); 315 | memcpy(swap_name, _FFUNC_WORKER_, (sizeof(_FFUNC_MASTER_) - 1)); 316 | if (conf->app_init_handler) { 317 | conf->app_init_handler(); 318 | } 319 | return hook_socket(sock_port, sock_port_str, backlog, max_thread, ffunc_nmap_func, procpip); 320 | } 321 | else { // Parent process 322 | while (1) { 323 | if (waitpid(child_pid, &child_status, WNOHANG) == child_pid) { 324 | has_init_signal = 0; 325 | if (read(procpip[0], pipbuf, FFUNC_APP_INIT_PIPE_BUF_SIZE) > 0) { 326 | if (strncmp(pipbuf, FFUNC_APP_INITIALIZED, FFUNC_APP_INIT_PIPE_BUF_SIZE) == 0) { 327 | goto FFUNC_WORKER_RESTART; 328 | } 329 | else { 330 | printf("%s\n", "Failed while initializing the application... "); 331 | close(procpip[0]); 332 | close(procpip[1]); 333 | return 1; // ERROR 334 | } 335 | } 336 | } 337 | sleep(1); 338 | } 339 | } 340 | } 341 | else {// fork failed 342 | ffunc_print("%s\n", "Fork Child failed.."); 343 | return -1; 344 | } 345 | return 0; 346 | // return hook_socket(sock_port, backlog, max_thread, ffunc_nmap_func, app_init_handler); 347 | } 348 | 349 | void * 350 | ffunc_thread_worker(void* wrker) { 351 | int rc; 352 | ffunc_worker_t *worker_t = (ffunc_worker_t*)wrker; 353 | 354 | FCGX_Request request; 355 | if (FCGX_InitRequest(&request, worker_t->fcgi_func_socket, FCGI_FAIL_ACCEPT_ON_INTR) != 0) { 356 | ffunc_print("%s\n", "Can not init request"); 357 | return NULL; 358 | } 359 | while (1) { 360 | pthread_mutex_lock(&worker_t->accept_mutex); 361 | rc = FCGX_Accept_r(&request); 362 | pthread_mutex_unlock(&worker_t->accept_mutex); 363 | if (rc < 0) { 364 | ffunc_print("%s\n", "Cannot accept new request"); 365 | FCGX_Finish_r(&request); // this will free all the fcgiparams memory and request 366 | continue; 367 | } 368 | handle_request(&request); 369 | FCGX_Finish_r(&request); // this will free all the fcgiparams memory and request 370 | } 371 | pthread_exit(NULL); 372 | } 373 | 374 | static int 375 | hook_socket(int sock_port, char *sock_port_str, int backlog, int max_thread, char** ffunc_nmap_func, int* procpip) { 376 | char pipbuf[FFUNC_APP_INIT_PIPE_BUF_SIZE]; 377 | FCGX_Init(); 378 | if (!ffunc_init(ffunc_nmap_func)) { 379 | exit(1); 380 | } 381 | 382 | char port_str[12]; 383 | ffunc_worker_t *worker_t = (ffunc_worker_t*)malloc(sizeof(ffunc_worker_t)); 384 | if (worker_t == NULL) { 385 | perror("nomem"); 386 | } 387 | 388 | worker_t->accept_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; // this is lock for all threads 389 | int i; 390 | 391 | if (sock_port) { 392 | sprintf(port_str, ":%d", sock_port); 393 | if (backlog) 394 | worker_t->fcgi_func_socket = FCGX_OpenSocket(port_str, backlog); 395 | else 396 | worker_t->fcgi_func_socket = FCGX_OpenSocket(port_str, 50); 397 | } else if(sock_port_str) { 398 | if (backlog) 399 | worker_t->fcgi_func_socket = FCGX_OpenSocket(sock_port_str, backlog); 400 | else 401 | worker_t->fcgi_func_socket = FCGX_OpenSocket(sock_port_str, 50); 402 | } else { 403 | ffunc_print("%s\n", "argument wrong"); 404 | exit(1); 405 | } 406 | 407 | if (worker_t->fcgi_func_socket < 0) { 408 | ffunc_print("%s\n", "unable to open the socket"); 409 | exit(1); 410 | } 411 | 412 | ffunc_print("%d threads \n", max_thread); 413 | 414 | /** Release for success initialized **/ 415 | read(procpip[0], pipbuf, FFUNC_APP_INIT_PIPE_BUF_SIZE); 416 | write(procpip[1], FFUNC_APP_INITIALIZED, FFUNC_APP_INIT_PIPE_BUF_SIZE); 417 | close(procpip[0]); 418 | close(procpip[1]); 419 | 420 | pthread_t pth_workers[max_thread]; 421 | for (i = 0; i < max_thread; i++) { 422 | pthread_create(&pth_workers[i], NULL, ffunc_thread_worker, worker_t); 423 | // pthread_detach(pth_worker); 424 | } 425 | for (i = 0; i < max_thread; i++) { 426 | pthread_join(pth_workers[i], NULL); 427 | } 428 | 429 | ffunc_print("%s\n", "Exiting"); 430 | return EXIT_SUCCESS; 431 | } 432 | 433 | static void 434 | ffunc_signal_backtrace(int sfd) { 435 | size_t i, ptr_size; 436 | void *buffer[10]; 437 | char **strings; 438 | 439 | ptr_size = backtrace(buffer, 1024); 440 | fprintf(stderr, "backtrace() returned %zd addresses\n", ptr_size); 441 | 442 | strings = backtrace_symbols(buffer, ptr_size); 443 | if (strings == NULL) { 444 | fprintf(stderr, "backtrace_symbols= %s", strerror(errno)); 445 | exit(EXIT_FAILURE); 446 | } 447 | 448 | for (i = 0; i < ptr_size; i++) 449 | fprintf(stderr, "%s\n", strings[i]); 450 | 451 | free(strings); 452 | exit(EXIT_FAILURE); 453 | } 454 | 455 | static void 456 | ffunc_add_signal_handler() { 457 | memset(&sa, 0, sizeof(struct sigaction)); 458 | sa.sa_handler = ffunc_signal_backtrace; 459 | sigemptyset(&sa.sa_mask); 460 | 461 | sigaction(SIGABRT, &sa, NULL); 462 | sigaction(SIGFPE, &sa, NULL); 463 | sigaction(SIGILL, &sa, NULL); 464 | sigaction(SIGIOT, &sa, NULL); 465 | sigaction(SIGSEGV, &sa, NULL); 466 | #ifdef SIGBUS 467 | sigaction(SIGBUS, &sa, NULL); 468 | #endif 469 | has_init_signal = 1; 470 | } 471 | 472 | int 473 | main(int argc, char *argv[]) { 474 | ffunc_config_t *conf; 475 | char **new_argv, *env_ffunc_master; 476 | unsigned int i; 477 | int status; 478 | 479 | if (FFUNC_GETENV(_FFUNC_MASTER_ENV)) { 480 | conf = (ffunc_config_t*)calloc(1, sizeof(ffunc_config_t)); 481 | conf->sock_port_str = NULL; 482 | ffunc_proc_name.data = argv[0]; 483 | ffunc_proc_name.len = strlen(argv[0]); 484 | 485 | if ((status = ffunc_main(argc, argv, conf)) == 0) { 486 | return ffunc_hook(conf); 487 | } 488 | else { 489 | fprintf(stderr, "Error status %d, status return is not successful(0) \n", status); 490 | return status; 491 | } 492 | } 493 | else { 494 | FFUNC_SETENV(_FFUNC_MASTER_ENV, argv[0], 1); 495 | new_argv = (char **)calloc(argc + 1, sizeof(char*)); 496 | size_t arg$0sz = strlen(argv[0]); 497 | new_argv[0] = (char *)calloc(arg$0sz + sizeof(_FFUNC_MASTER_), sizeof(char)); 498 | memcpy(new_argv[0], argv[0], arg$0sz); 499 | memcpy(new_argv[0] + arg$0sz, _FFUNC_MASTER_, sizeof(_FFUNC_MASTER_) - 1); 500 | 501 | for (i = 1; i < argc; i++) { 502 | new_argv[i] = argv[i]; 503 | } 504 | 505 | new_argv[i] = NULL; 506 | extern char **environ; 507 | FFUNC_EXECVE(argv[0], new_argv, environ); 508 | } 509 | return 0; 510 | } 511 | 512 | #else 513 | #if defined _WIN32 || _WIN64 /*Windows*/ 514 | #include 515 | #include 516 | #include 517 | #include 518 | #include 519 | #include "ffunc.h" 520 | #include 521 | 522 | 523 | #ifdef UNICODE 524 | #define FFUNC_SPRINTF swprintf 525 | #define FFUNC_PRINTF wprintf 526 | #else 527 | #define FFUNC_SPRINTF sprintf_s 528 | #define FFUNC_PRINTF printf 529 | #endif 530 | #define ffunc_spawn_child _spawnve 531 | #define pipename TEXT("\\\\.\\pipe\\LogPipe") 532 | static TCHAR *fullPipeName = NULL; 533 | #define NAMED_PIPED_BUFF_SIZE 32 534 | #define _get_param_(KEY) FCGX_GetParam(KEY, request->envp) 535 | #define FFUNC_ERROR(errmsg) FFUNC_PRINTF(TEXT("%s - %d\n"), TEXT(errmsg), GetLastError() ) 536 | 537 | int setenv(const char *name, const char *value, int overwrite) { 538 | int errcode = 0; 539 | if (!overwrite) { 540 | size_t envsize = 0; 541 | errcode = getenv_s(&envsize, NULL, 0, name); 542 | if (errcode || envsize) return errcode; 543 | } 544 | return _putenv_s(name, value); 545 | } 546 | char* getenv_secure(char const* _VarName) { 547 | size_t envsize = 0; 548 | char *exec_name = NULL; 549 | if (getenv_s(&envsize, NULL, 0, _VarName) || envsize <= 0) { 550 | return NULL; 551 | } 552 | exec_name = calloc(envsize + 1, sizeof(char)); 553 | if (getenv_s(&envsize, exec_name, envsize, _VarName) || envsize <= 0) { 554 | return NULL; 555 | } 556 | return exec_name; 557 | } 558 | #define _FFUNC_MASTER_ENV "_ffunc-master_" __TIME__ 559 | #define FFUNC_SETENV setenv 560 | #define FFUNC_GETENV(varname) getenv_secure(varname) 561 | #define FFUNC_EXECVE _spawnve 562 | #define _FFUNC_MASTER_ "_ffunc-master" 563 | #define _FFUNC_WORKER_ "_ffunc-worker" 564 | 565 | static void* mem_align(size_t size); 566 | static int ffunc_get_number_of_digit(long long number); 567 | static ffunc_pool* ffunc_recreate_pool(ffunc_pool *curr_p, size_t new_size); 568 | typedef void(*h)(ffunc_session_t*); 569 | typedef void(*inithandler)(void); 570 | static ffunc_str_t ffunc_proc_name; 571 | 572 | struct ffunc_handler { 573 | char* name; 574 | size_t len; 575 | h handle; 576 | }; 577 | 578 | typedef struct { 579 | int fcgi_func_socket; 580 | HANDLE accept_mutex; 581 | } ffunc_worker_t; 582 | 583 | static int func_count = 0; 584 | struct ffunc_handler *dyna_funcs = NULL; 585 | 586 | static size_t max_std_input_buffer; 587 | 588 | static int ffunc_init(char** ffunc_nmap_func, char* init_handler_name); 589 | static int ffunc_strpos(const char *haystack, const char *needle); 590 | unsigned __stdcall ffunc_thread_worker(void *wrker); 591 | static int hook_socket(int sock_port, char* sock_port_str, int backlog, int max_thread, char** ffunc_nmap_func, char* init_handler_name); 592 | static void ffunc_add_signal_handler(void); 593 | static void handle_request(FCGX_Request *request); 594 | static size_t ffunc_read_body_limit(ffunc_session_t * csession, ffunc_str_t *content); 595 | static size_t ffunc_read_body_nolimit(ffunc_session_t * csession, ffunc_str_t *content); 596 | static void ffunc_default_direct_consume(ffunc_session_t *sess); 597 | static int has_init_signal = 0; 598 | static HINSTANCE usr_req_handle; 599 | 600 | // extern declaration 601 | size_t(*ffunc_read_body)(ffunc_session_t *, ffunc_str_t *); 602 | h _ffunc_direct_consume; 603 | 604 | // static void* 605 | // read_t(void *origin) { 606 | // ffunc_session_t *_csession_ = malloc(sizeof(ffunc_session_t)); 607 | // memcpy(_csession_, origin, sizeof(ffunc_session_t)); 608 | // return _csession_; 609 | // } 610 | 611 | // static void 612 | // free_t(void* node) { 613 | // free(node); 614 | // } 615 | static int 616 | ffunc_init(char** ffunc_nmap_func, char* init_handler_name) { 617 | int f; 618 | SIZE_T szof_func_name; 619 | usr_req_handle = GetModuleHandle(NULL); 620 | // Pending FreeLibrary 621 | 622 | if (!usr_req_handle) { 623 | FFUNC_ERROR("GEGetModuleHandle"); 624 | return 0; 625 | } 626 | 627 | for (func_count = 0; ffunc_nmap_func[func_count]; func_count++); 628 | 629 | dyna_funcs = malloc(func_count * sizeof(struct ffunc_handler)); 630 | 631 | for (f = 0; f < func_count; f++) { 632 | szof_func_name = strlen(ffunc_nmap_func[f]); 633 | dyna_funcs[f].name = calloc(szof_func_name + 1, sizeof(char)); 634 | 635 | memcpy(dyna_funcs[f].name, ffunc_nmap_func[f], szof_func_name); 636 | dyna_funcs[f].len = szof_func_name; 637 | 638 | dyna_funcs[f].handle = (h)GetProcAddress(usr_req_handle, ffunc_nmap_func[f]); 639 | if (!dyna_funcs[f].handle) { 640 | FFUNC_ERROR("could not locate the function"); 641 | return EXIT_FAILURE; 642 | } 643 | } 644 | 645 | 646 | 647 | _ffunc_direct_consume = (h)GetProcAddress(usr_req_handle, "ffunc_direct_consume"); 648 | if (!_ffunc_direct_consume) { 649 | FFUNC_PRINTF(TEXT("direct consume is disabled\n")); 650 | _ffunc_direct_consume = &ffunc_default_direct_consume; 651 | } 652 | else { 653 | FFUNC_PRINTF(TEXT("direct consume is enabled\n")); 654 | } 655 | 656 | if (init_handler_name) { 657 | inithandler initHandler = (inithandler)GetProcAddress(usr_req_handle, init_handler_name); 658 | if (initHandler) { 659 | initHandler(); 660 | } 661 | } 662 | 663 | return 1; 664 | } 665 | 666 | /**To prevent -std=99 issue**/ 667 | char * 668 | ffunc_strdup(ffunc_session_t * csession, const char *str, size_t len) { 669 | if (len == 0) { 670 | return NULL; 671 | } 672 | char *dupstr = (char*)_ffunc_alloc(&csession->pool, len + 1); 673 | if (dupstr == NULL) { 674 | return NULL; 675 | } 676 | memcpy(dupstr, str, len); 677 | // memset(dupstr, 0, len + 1); 678 | dupstr[len] = (char)0; 679 | return dupstr; 680 | } 681 | 682 | static void 683 | handle_request(FCGX_Request *request) { 684 | char *value, 685 | *qparam, 686 | *query_string; 687 | 688 | ffunc_pool *p = ffunc_create_pool(DEFAULT_BLK_SZ); 689 | ffunc_session_t *_csession_ = (ffunc_session_t *)malloc(sizeof(ffunc_session_t)); 690 | if (p == NULL || _csession_ == NULL) { 691 | char err_buff[50]; 692 | strerror_s(err_buff, sizeof err_buff, errno); 693 | fprintf(stderr, "error %s\n", err_buff); 694 | exit(EXIT_FAILURE); 695 | } 696 | _csession_->pool = p; 697 | _csession_->query_str = NULL; 698 | _csession_->request = request; 699 | 700 | if ((value = FCGX_GetParam("FN_HANDLER", request->envp))) { 701 | int i; 702 | size_t vallen = strlen(value); 703 | for (i = 0; i < func_count; i++) { 704 | if (dyna_funcs[i].len == vallen && (strcmp(dyna_funcs[i].name, value) == 0)) { 705 | if ((qparam = _get_param_("QUERY_STRING"))) { 706 | query_string = ffunc_strdup(_csession_, qparam, strlen(qparam)); 707 | if (query_string && query_string[0]) { 708 | _csession_->query_str = query_string;//parse_json_fr_args(query_string); 709 | } 710 | } 711 | dyna_funcs[i].handle(_csession_); 712 | 713 | goto RELEASE_POOL; 714 | } 715 | } 716 | ffunc_default_direct_consume(_csession_); 717 | } 718 | else { 719 | if ((qparam = _get_param_("QUERY_STRING"))) { 720 | query_string = ffunc_strdup(_csession_, qparam, strlen(qparam)); 721 | if (query_string && query_string[0]) { 722 | _csession_->query_str = query_string;//parse_json_fr_args(query_string); 723 | } 724 | } 725 | _ffunc_direct_consume(_csession_); 726 | } 727 | 728 | 729 | RELEASE_POOL: 730 | ffunc_destroy_pool(_csession_->pool); 731 | free(_csession_); 732 | } 733 | 734 | static size_t 735 | ffunc_read_body_nolimit(ffunc_session_t * csession, ffunc_str_t *content) { 736 | FCGX_Request *request = csession->request; 737 | char* clenstr = _get_param_("CONTENT_LENGTH"); 738 | FCGX_Stream *in = request->in; 739 | size_t len; 740 | 741 | if (clenstr && ((len = strtol(clenstr, &clenstr, 10)) != 0)) { 742 | content->data = _ffunc_alloc(&csession->pool, len + 1); 743 | memset(content->data, 0, len + 1); 744 | content->len = FCGX_GetStr(content->data, len, in); 745 | } 746 | else { 747 | content->data = NULL; 748 | content->len = 0; 749 | } 750 | 751 | /* Chew up whats remaining (required by mod_fastcgi) */ 752 | while (FCGX_GetChar(in) != -1); 753 | 754 | return len; 755 | } 756 | 757 | static size_t 758 | ffunc_read_body_limit(ffunc_session_t * csession, ffunc_str_t *content) { 759 | FCGX_Request *request = csession->request; 760 | char* clenstr = _get_param_("CONTENT_LENGTH"); 761 | FCGX_Stream *in = request->in; 762 | size_t len; 763 | 764 | if (clenstr) { 765 | // long int strtol (const char* str, char** endptr, int base); 766 | // endptr -- Reference to an object of type char*, whose value is set by the function to the next character in str after the numerical value. 767 | // This parameter can also be a null pointer, in which case it is not used. 768 | len = strtol(clenstr, &clenstr, 10); 769 | /* Don't read more than the predefined limit */ 770 | if (len > max_std_input_buffer) { 771 | len = max_std_input_buffer; 772 | } 773 | 774 | content->data = _ffunc_alloc(&csession->pool, len + 1); 775 | memset(content->data, 0, len + 1); 776 | content->len = FCGX_GetStr(content->data, len, in); 777 | } 778 | /* Don't read if CONTENT_LENGTH is missing or if it can't be parsed */ 779 | else { 780 | content->data = NULL; 781 | content->len = 0; 782 | } 783 | 784 | /* Chew up whats remaining (required by mod_fastcgi) */ 785 | while (FCGX_GetChar(in) != -1); 786 | 787 | return len; 788 | } 789 | 790 | 791 | static void 792 | ffunc_interrupt_handler(intptr_t signal) { 793 | ExitProcess(0); 794 | } 795 | 796 | BOOL WINAPI 797 | console_ctrl_handler(DWORD ctrl) { 798 | switch (ctrl) 799 | { 800 | case CTRL_C_EVENT: 801 | case CTRL_CLOSE_EVENT: 802 | case CTRL_BREAK_EVENT: 803 | case CTRL_LOGOFF_EVENT: 804 | case CTRL_SHUTDOWN_EVENT: 805 | return TRUE; 806 | default: 807 | return FALSE; 808 | } 809 | } 810 | 811 | /**Main Hook**/ 812 | int ffunc_hook(char **argv, ffunc_config_t *conf, int bypassmaster) { 813 | int sock_port = conf->sock_port; 814 | char* sock_port_str = conf->sock_port_str; 815 | int backlog = conf->backlog; 816 | int max_thread = conf->max_thread; 817 | char** ffunc_nmap_func = conf->ffunc_nmap_func; 818 | char* app_init_handler_name = conf->app_init_handler_name; 819 | size_t max_read_buffer = conf->max_read_buffer; 820 | char* exec_name = conf->__exec_name; 821 | int totalPipeLength; 822 | TCHAR *pipenamebuff; 823 | 824 | 825 | if (sock_port <= 0 && sock_port_str == NULL) { 826 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("sock_port has no defined...")); 827 | return 1; 828 | } 829 | 830 | if (max_thread <= 0) { 831 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("max_thread has no defined...")); 832 | return 1; 833 | } 834 | 835 | if (backlog <= 0) { 836 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("backlog has no defined...")); 837 | return 1; 838 | } 839 | 840 | if (!ffunc_nmap_func[0]) { 841 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("ffunc_nmap_func has no defined...")); 842 | return 1; 843 | } 844 | 845 | if (max_read_buffer > 0) { 846 | max_std_input_buffer = max_read_buffer; 847 | ffunc_read_body = &ffunc_read_body_limit; 848 | } 849 | else { 850 | ffunc_read_body = &ffunc_read_body_nolimit; 851 | } 852 | 853 | if (bypassmaster) { 854 | goto CONTINUE_CHILD_PROCESS; 855 | } 856 | else { 857 | goto SPAWN_CHILD_PROC; 858 | } 859 | 860 | SPAWN_CHILD_PROC: 861 | // Setup a console control handler: We stop the server on CTRL-C 862 | SetConsoleCtrlHandler(console_ctrl_handler, TRUE); 863 | signal(SIGINT, ffunc_interrupt_handler); 864 | 865 | SIZE_T i; 866 | char *sockport_str, 867 | *backlog_str, 868 | *max_thread_str, 869 | *max_read_buffer_str; 870 | 871 | DWORD parentPid = GetCurrentProcessId(); 872 | HANDLE pipe; 873 | 874 | if (parentPid < 0) { 875 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("invalid pid")); 876 | return 1; 877 | } 878 | else { 879 | totalPipeLength = ffunc_get_number_of_digit(parentPid) + sizeof(pipename); 880 | pipenamebuff = (TCHAR*)calloc(totalPipeLength, sizeof(TCHAR)); 881 | FFUNC_SPRINTF(pipenamebuff, totalPipeLength, TEXT("%s%d"), pipename, parentPid); 882 | pipe = CreateNamedPipe(pipenamebuff, PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND, PIPE_WAIT, 1, 883 | NAMED_PIPED_BUFF_SIZE, NAMED_PIPED_BUFF_SIZE, 120 * 1000, NULL); 884 | if (pipe == INVALID_HANDLE_VALUE) { 885 | FFUNC_PRINTF(TEXT("Error: %d"), GetLastError()); 886 | } 887 | #ifdef UNICODE 888 | char *toCharPipe = calloc(totalPipeLength + 1, sizeof(char)); 889 | size_t i; 890 | wcstombs_s(&i, toCharPipe, totalPipeLength, pipenamebuff, totalPipeLength); 891 | FFUNC_SETENV("_FFUNC_PID_NAMED_PIPE", toCharPipe, 1); 892 | #else 893 | FFUNC_SETENV("_FFUNC_PID_NAMED_PIPE", pipenamebuff, 1); 894 | #endif 895 | } 896 | 897 | if (sock_port != 0) { 898 | sockport_str = malloc((ffunc_get_number_of_digit(sock_port) + 1) * sizeof(char)); 899 | snprintf(sockport_str, ffunc_get_number_of_digit(sock_port) + 1, "%d", sock_port); 900 | FFUNC_SETENV("_FFUNC_ENV_SOCK_PORT", sockport_str, 1); 901 | } else if (sock_port_str != NULL) { 902 | sockport_str = calloc((strlen(sock_port_str) + 1), sizeof(char)); 903 | memcpy(sockport_str, sock_port_str, strlen(sock_port_str)); 904 | FFUNC_SETENV("_FFUNC_ENV_SOCK_PORT_STR", sockport_str, 1); 905 | } 906 | 907 | if (app_init_handler_name) { 908 | FFUNC_SETENV("_FFUNC_ENV_INIT_HANDLER_NAME", app_init_handler_name, 1); 909 | } 910 | 911 | if (backlog != 0) { 912 | backlog_str = malloc((ffunc_get_number_of_digit(backlog) + 1) * sizeof(char)); 913 | snprintf(backlog_str, ffunc_get_number_of_digit(backlog) + 1, "%d", backlog); 914 | FFUNC_SETENV("_FFUNC_ENV_BACKLOG", backlog_str, 1); 915 | } 916 | 917 | if (max_thread != 0) { 918 | max_thread_str = malloc((ffunc_get_number_of_digit(max_thread) + 1) * sizeof(char)); 919 | snprintf(max_thread_str, ffunc_get_number_of_digit(max_thread) + 1, "%d", max_thread); 920 | FFUNC_SETENV("_FFUNC_ENV_MAX_THREAD", max_thread_str, 1); 921 | } 922 | 923 | 924 | if (max_read_buffer != 0) { 925 | max_read_buffer_str = malloc((ffunc_get_number_of_digit(max_read_buffer) + 1) * sizeof(char)); 926 | snprintf(max_read_buffer_str, ffunc_get_number_of_digit(max_read_buffer) + 1, "%d", max_read_buffer); 927 | FFUNC_SETENV("_FFUNC_ENV_MAX_READ_BUF", max_read_buffer_str, 1); 928 | } 929 | 930 | 931 | SIZE_T func_str_sz = 0; 932 | for (i = 0; ffunc_nmap_func[i]; i++) { 933 | func_str_sz = func_str_sz + strlen(ffunc_nmap_func[i]) + sizeof("\",\" ") - 1; 934 | } 935 | 936 | char *func_str = malloc((func_str_sz + 1) * sizeof(char)); 937 | char* p = func_str; 938 | for (i = 0; ffunc_nmap_func[i]; i++) { 939 | p = (char*)(memcpy(p, ffunc_nmap_func[i], strlen(ffunc_nmap_func[i]))) + strlen(ffunc_nmap_func[i]); 940 | p = (char*)(memcpy(p, "\",\" ", sizeof("\",\" ") - 1)) + (sizeof("\",\" ") - 1); 941 | } 942 | func_str[func_str_sz] = (char)0; 943 | 944 | FFUNC_SETENV("_FFUNC_ENV_FUNC_str", func_str, 1); 945 | // FFUNC_PRINTF("func_str = %s\n", func_str); 946 | 947 | // Change command line to worker 948 | if ((strlen(exec_name) + (sizeof(_FFUNC_WORKER_) - 1)) <= strlen(argv[0])) { 949 | memmove(argv[0] + strlen(exec_name), _FFUNC_WORKER_, (sizeof(_FFUNC_WORKER_) - 1)); 950 | } 951 | extern char **environ; 952 | 953 | FFUNC_WORKER_RESTART: 954 | if (exec_name) { 955 | FFUNC_EXECVE(_P_WAIT, exec_name, argv, environ); 956 | char data[NAMED_PIPED_BUFF_SIZE]; 957 | DWORD numRead; 958 | //ConnectNamedPipe(pipe, NULL); 959 | if (PeekNamedPipe(pipe, NULL, 0, NULL, &numRead, NULL)) { 960 | if (0 != numRead) { 961 | ReadFile(pipe, data, 1024, &numRead, NULL); 962 | goto FFUNC_WORKER_RESTART; 963 | } 964 | } 965 | 966 | CloseHandle(pipe); 967 | 968 | } 969 | else { 970 | FFUNC_ERROR("Error: Exec name not found "); 971 | 972 | } 973 | 974 | FFUNC_ERROR("Error: "); 975 | Sleep(3000); 976 | ExitProcess(0); 977 | 978 | CONTINUE_CHILD_PROCESS: 979 | 980 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("Service starting")); 981 | if (sock_port) { 982 | FFUNC_PRINTF(TEXT("Socket port on hook %d\n"), sock_port); 983 | } 984 | else if(sock_port_str){ 985 | FFUNC_PRINTF(TEXT("Socket port str on hook %s\n"), sock_port_str); 986 | } 987 | FFUNC_PRINTF(TEXT("backlog=%d\n"), backlog); 988 | FFUNC_PRINTF(TEXT("%d threads \n"), max_thread); 989 | fprintf(stderr, "%s\n", "Press Ctrl-C to terminate the process...."); 990 | return hook_socket(sock_port, sock_port_str, backlog, max_thread, ffunc_nmap_func, conf->app_init_handler_name); 991 | } 992 | 993 | unsigned __stdcall 994 | ffunc_thread_worker(void* wrker) { 995 | int rc; 996 | ffunc_worker_t *worker_t = (ffunc_worker_t*)wrker; 997 | 998 | FCGX_Request request; 999 | if (FCGX_InitRequest(&request, worker_t->fcgi_func_socket, FCGI_FAIL_ACCEPT_ON_INTR) != 0) { 1000 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("Can not init request")); 1001 | return 0; 1002 | } 1003 | while (1) { 1004 | WaitForSingleObject(&worker_t->accept_mutex, INFINITE); 1005 | rc = FCGX_Accept_r(&request); 1006 | ReleaseMutex(&worker_t->accept_mutex); 1007 | if (rc < 0) { 1008 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("Cannot accept new request")); 1009 | FCGX_Finish_r(&request); // this will free all the fcgiparams memory and request 1010 | continue; 1011 | } 1012 | handle_request(&request); 1013 | FCGX_Finish_r(&request); // this will free all the fcgiparams memory and request 1014 | } 1015 | _endthread(); 1016 | } 1017 | 1018 | static int 1019 | hook_socket(int sock_port, char* sock_port_str, int backlog, int max_thread, char** ffunc_nmap_func, char* init_handler_name) { 1020 | char *_pipeName; 1021 | // #define MAX_PIPE_SIZE 256 1022 | // TCHAR *pipeName; 1023 | HANDLE pipe; 1024 | if (!(_pipeName = FFUNC_GETENV("_FFUNC_PID_NAMED_PIPE"))) { 1025 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("Failed getting _FFUNC_PID_NAMED_PIPE")); 1026 | return -1; 1027 | } 1028 | 1029 | /* 1030 | #ifdef UNICODE 1031 | size_t szi; 1032 | pipeName = calloc(MAX_PIPE_SIZE, sizeof(TCHAR)); 1033 | int numConverted = mbstowcs_s(&szi, pipeName, MAX_PIPE_SIZE, _pipeName, MAX_PIPE_SIZE); 1034 | #else 1035 | pipeName = _pipeName; 1036 | #endif 1037 | */ 1038 | 1039 | //FFUNC_PRINTF(TEXT("PIPENAME IS %s"), pipeName); 1040 | pipe = CreateFileA(_pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 1041 | if (pipe == INVALID_HANDLE_VALUE) { 1042 | FFUNC_ERROR("Create file FAILED"); 1043 | return -1; 1044 | } 1045 | 1046 | FCGX_Init(); 1047 | if (!ffunc_init(ffunc_nmap_func, init_handler_name)) { 1048 | exit(1); 1049 | } 1050 | 1051 | char port_str[12]; 1052 | ffunc_worker_t *worker_t = malloc(sizeof(ffunc_worker_t)); 1053 | if (worker_t == NULL) { 1054 | perror("nomem"); 1055 | } 1056 | 1057 | worker_t->accept_mutex = CreateMutex(NULL, FALSE, NULL); // this is lock for all threads 1058 | int i; 1059 | 1060 | if (sock_port) { 1061 | sprintf_s(port_str, sizeof port_str, ":%d", sock_port); 1062 | if (backlog) 1063 | worker_t->fcgi_func_socket = FCGX_OpenSocket(port_str, backlog); 1064 | else 1065 | worker_t->fcgi_func_socket = FCGX_OpenSocket(port_str, 50); 1066 | } else if(sock_port_str) { 1067 | if (backlog) 1068 | worker_t->fcgi_func_socket = FCGX_OpenSocket(sock_port_str, backlog); 1069 | else 1070 | worker_t->fcgi_func_socket = FCGX_OpenSocket(sock_port_str, 50); 1071 | } 1072 | else { 1073 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("argument wrong")); 1074 | exit(1); 1075 | } 1076 | 1077 | if (worker_t->fcgi_func_socket < 0) { 1078 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("unable to open the socket")); 1079 | exit(1); 1080 | } 1081 | 1082 | /* Release of successfully initialized */ 1083 | DWORD numWritten; 1084 | WriteFile(pipe, TEXT("DONE"), sizeof(TEXT("DONE")), &numWritten, NULL); 1085 | CloseHandle(pipe); 1086 | 1087 | HANDLE *pth_workers = calloc(max_thread, sizeof(HANDLE)); 1088 | for (i = 0; i < max_thread; i++) { 1089 | unsigned thread_id; 1090 | pth_workers[i] = (HANDLE)_beginthreadex(NULL, 0, ffunc_thread_worker, worker_t, 0, &thread_id); 1091 | } 1092 | for (i = 0; i < max_thread; i++) { 1093 | WaitForSingleObject(pth_workers[i], INFINITE); 1094 | } 1095 | free(pth_workers); 1096 | FFUNC_PRINTF(TEXT("%s\n"), TEXT("Exiting")); 1097 | return EXIT_SUCCESS; 1098 | } 1099 | 1100 | int 1101 | main(int argc, char *argv[]) { 1102 | ffunc_config_t *conf; 1103 | char **new_argv; 1104 | unsigned int i = 1; 1105 | int status; 1106 | char* exec_name; 1107 | char *sockport_str, 1108 | *backlog_str, 1109 | *max_thread_str, 1110 | *max_read_buffer_str, 1111 | *func_str, 1112 | *init_handler_name; 1113 | 1114 | if (((sockport_str = FFUNC_GETENV("_FFUNC_ENV_SOCK_PORT")) || (sockport_str = FFUNC_GETENV("_FFUNC_ENV_SOCK_PORT_STR"))) && 1115 | (backlog_str = FFUNC_GETENV("_FFUNC_ENV_BACKLOG")) && 1116 | (max_thread_str = FFUNC_GETENV("_FFUNC_ENV_MAX_THREAD")) && 1117 | (func_str = FFUNC_GETENV("_FFUNC_ENV_FUNC_str")) 1118 | ) { 1119 | conf = calloc(1, sizeof(ffunc_config_t)); 1120 | 1121 | if(FFUNC_GETENV("_FFUNC_ENV_SOCK_PORT")) { 1122 | conf->sock_port = atoi(sockport_str); 1123 | } else if(FFUNC_GETENV("_FFUNC_ENV_SOCK_PORT_STR")) { 1124 | conf->sock_port_str = sockport_str; 1125 | } 1126 | 1127 | if ( (init_handler_name = FFUNC_GETENV("_FFUNC_ENV_INIT_HANDLER_NAME")) ) { 1128 | conf->app_init_handler_name = init_handler_name; 1129 | } 1130 | 1131 | conf->backlog = atoi(backlog_str); 1132 | conf->max_thread = atoi(max_thread_str); 1133 | if ((max_read_buffer_str = FFUNC_GETENV("_FFUNC_ENV_MAX_READ_BUF"))) { 1134 | conf->max_read_buffer = (size_t)atol(max_read_buffer_str); 1135 | } 1136 | int func_count = 0, index = 0; 1137 | char* p = func_str; 1138 | while ((p = strstr(p, "\",\" "))) { 1139 | func_count++; 1140 | p += sizeof("\",\" ") - 1; 1141 | } 1142 | if (func_count > 0) { 1143 | conf->ffunc_nmap_func = (char**)calloc(func_count + 1, sizeof(char*)); 1144 | p = func_str; 1145 | while ((strstr(p, "\",\" "))) { 1146 | conf->ffunc_nmap_func[index++] = p; 1147 | p = strstr(p, "\",\" "); 1148 | *p = (char)0; // char terminate for previos function 1149 | p += sizeof("\",\" ") - 1; 1150 | } 1151 | conf->ffunc_nmap_func[index++] = NULL; 1152 | /*for (index = 0; conf->ffunc_nmap_func[index]; index++) { 1153 | printf("conf->ffunc_nmap_func %s\n", conf->ffunc_nmap_func[index]); 1154 | }*/ 1155 | } 1156 | else { 1157 | FFUNC_ERROR("Function has no defined, [ conf->ffunc_nmap_func ] "); 1158 | return -1; 1159 | } 1160 | return ffunc_hook(argv, conf, 1); 1161 | } 1162 | else if ((exec_name = FFUNC_GETENV(_FFUNC_MASTER_ENV))) { 1163 | conf = calloc(1, sizeof(ffunc_config_t)); 1164 | conf->sock_port_str = NULL; 1165 | conf->__exec_name = exec_name; 1166 | ffunc_proc_name.data = argv[0]; 1167 | ffunc_proc_name.len = strlen(argv[0]); 1168 | 1169 | if ((status = ffunc_main(argc, argv, conf)) == 0) { 1170 | return ffunc_hook(argv, conf, 0); 1171 | } 1172 | else { 1173 | fprintf(stderr, "Error status %d, status return is not successful(0) \n", status); 1174 | return status; 1175 | } 1176 | } 1177 | else { 1178 | FFUNC_SETENV(_FFUNC_MASTER_ENV, argv[0], 1); 1179 | new_argv = calloc(argc + 1, sizeof(char*)); 1180 | size_t arg$0sz = strlen(argv[0]); 1181 | new_argv[0] = calloc(arg$0sz + sizeof(_FFUNC_MASTER_), sizeof(char)); 1182 | memcpy(new_argv[0], argv[0], arg$0sz); 1183 | memcpy(new_argv[0] + arg$0sz, _FFUNC_MASTER_, sizeof(_FFUNC_MASTER_) - 1); 1184 | 1185 | while (argv[i]) { 1186 | new_argv[i] = argv[i++]; 1187 | } 1188 | 1189 | new_argv[i] = NULL; 1190 | extern char **environ; 1191 | FFUNC_EXECVE(_P_NOWAIT, argv[0], new_argv, environ); 1192 | } 1193 | return 0; 1194 | } 1195 | #endif 1196 | #endif 1197 | 1198 | static void 1199 | ffunc_default_direct_consume(ffunc_session_t *sess) { 1200 | ffunc_write_out(sess, "Content-Type: text/plain\r\n\r\n"); 1201 | ffunc_write_out(sess, "%s\n", "FN_HANDLER not found, ffunc_direct_consume not found"); 1202 | ffunc_write_out(sess, "%s\n", "Please provide your FN_HANDLER in your config file, else provide \"ffunc_direct_consume(ffunc_session_t *sess){}\" to customize your inputs"); 1203 | ffunc_write_out(sess, "%s\n", "For e.g nginx.conf fastcgi_param FN_HANDLER getProfile"); 1204 | } 1205 | 1206 | static int 1207 | ffunc_get_number_of_digit(long long number) { 1208 | int count = 0; 1209 | while (number != 0) 1210 | { 1211 | number /= 10; 1212 | ++count; 1213 | } 1214 | return count; 1215 | } 1216 | 1217 | static int 1218 | ffunc_strpos(const char *haystack, const char *needle) 1219 | { 1220 | char *p = strstr((char*)haystack, needle); 1221 | if (p) 1222 | return p - haystack; 1223 | return -1; // Not found = -1. 1224 | } 1225 | 1226 | void* 1227 | ffunc_get_query_param(ffunc_session_t * csession, const char *key, size_t len) { 1228 | int pos; 1229 | char *qs = csession->query_str; 1230 | if (key && *key && qs && *qs) { 1231 | do { 1232 | if ((pos = ffunc_strpos(qs, key)) < 0) return NULL; 1233 | 1234 | if (pos == 0 || qs[pos - 1] == '&') { 1235 | qs = (char*)qs + pos + len; 1236 | if (*qs++ == '=') { 1237 | char *src = qs, 1238 | *ret; 1239 | size_t sz = 0; 1240 | while (*qs && *qs++ != '&')sz++; 1241 | 1242 | ret = (char *)_ffunc_alloc(&csession->pool, sz + 1); 1243 | memset(ret, 0, sz + 1); 1244 | return memcpy(ret, src, sz); 1245 | } 1246 | else while (*qs && *qs++ != '&'); 1247 | } 1248 | else while (*qs && *qs++ != '&'); 1249 | } while (*qs); 1250 | } 1251 | return NULL; 1252 | } 1253 | 1254 | void 1255 | ffunc_destroy_pool(ffunc_pool *p) { 1256 | if (p) { 1257 | if (p->prev) { 1258 | ffunc_destroy_pool(p->prev); 1259 | } 1260 | free(p); 1261 | } 1262 | } 1263 | 1264 | size_t 1265 | ffunc_mem_left(ffunc_pool *p) { 1266 | return (uintptr_t)p->end - (uintptr_t)p->next; 1267 | } 1268 | 1269 | size_t 1270 | ffunc_mem_used(ffunc_pool *p) { 1271 | return (uintptr_t)p->next - (uintptr_t)&p[1]; 1272 | } 1273 | 1274 | size_t 1275 | ffunc_blk_size(ffunc_pool *p) { 1276 | return (uintptr_t)p->end - (uintptr_t)(void*)&p[1]; 1277 | } 1278 | 1279 | static void* 1280 | mem_align(size_t size) //alignment => 16 1281 | { 1282 | void *p; 1283 | #ifdef POSIX_MEMALIGN 1284 | int err; 1285 | err = posix_memalign(&p, ALIGNMENT, size); 1286 | if (err) { 1287 | // FFUNC_PRINTF("posix_memalign(%uz, %uz) failed \n", ALIGNMENT, size); 1288 | p = NULL; 1289 | } 1290 | // FFUNC_PRINTF("posix_memalign: %p:%uz @%uz \n", p, size, ALIGNMENT); 1291 | #else 1292 | // FFUNC_PRINTF("%s\n", "Using Malloc"); 1293 | p = malloc(size); 1294 | 1295 | #endif 1296 | return p; 1297 | } 1298 | 1299 | 1300 | ffunc_pool* 1301 | ffunc_create_pool(size_t size) { 1302 | ffunc_pool * p = (ffunc_pool*)mem_align(size + sizeof(ffunc_pool)); 1303 | p->next = (void*)&p[1]; //p + sizeof(ffunc_pool) 1304 | p->end = (void*)((uintptr_t)p->next + size); 1305 | p->prev = NULL; 1306 | return p; // p->memblk = //mem_align( 16, DEFAULT_BLK_SZ); 1307 | } 1308 | 1309 | static ffunc_pool* 1310 | ffunc_recreate_pool(ffunc_pool *curr_p, size_t new_size) { 1311 | ffunc_pool *newp = NULL; 1312 | if (curr_p) { 1313 | newp = (ffunc_pool*)ffunc_create_pool(new_size); 1314 | newp->prev = curr_p; 1315 | } 1316 | return newp; 1317 | } 1318 | 1319 | /* 1 true, 0 false */ 1320 | static int 1321 | ffunc_get_fit_pool(ffunc_pool **p, size_t fit_size) { 1322 | ffunc_pool *curr_pool = *p, *prev_pool; 1323 | while ((prev_pool = curr_pool->prev)) { 1324 | if (ffunc_mem_left(prev_pool) >= fit_size) { 1325 | *p = prev_pool; 1326 | return 1; 1327 | } 1328 | curr_pool = curr_pool->prev; 1329 | } 1330 | return 0; 1331 | } 1332 | 1333 | void * 1334 | _ffunc_alloc(ffunc_pool **p, size_t size) { 1335 | ffunc_pool *curr_p = *p; 1336 | if (ffunc_mem_left(curr_p) < size) { 1337 | if (!(ffunc_get_fit_pool(&curr_p, size))) { 1338 | size_t curr_blk_sz = ffunc_blk_size(curr_p); 1339 | size_t new_size = curr_blk_sz * 2; 1340 | 1341 | while (new_size < size) { 1342 | new_size *= 2; 1343 | } 1344 | 1345 | *p = curr_p = ffunc_recreate_pool(curr_p, new_size); 1346 | } 1347 | } 1348 | void *mem = (void*)curr_p->next; 1349 | curr_p->next = (void*)((uintptr_t)curr_p->next + size); // alloc new memory 1350 | // memset(curr_p->next, 0, 1); // split for next 1351 | 1352 | return mem; 1353 | } 1354 | 1355 | /* For Pool Testing purpose */ 1356 | // int main() 1357 | // { 1358 | // ffunc_pool *thisp = ffunc_create_pool(DEFAULT_BLK_SZ); 1359 | 1360 | // ffunc_print("thisp Address = %p\n", thisp); 1361 | // ffunc_print("thisp->next Address = %p\n", thisp->next); 1362 | // char* s = (char*)_ffunc_alloc(&thisp, DEFAULT_BLK_SZ); 1363 | // char* s2 = (char*)_ffunc_alloc(&thisp, DEFAULT_BLK_SZ); 1364 | // char* s3 = (char*)_ffunc_alloc(&thisp, DEFAULT_BLK_SZ); 1365 | // ffunc_pool *thatp = ffunc_create_pool(DEFAULT_BLK_SZ); 1366 | // char* s4 = (char*)_ffunc_alloc(&thisp, DEFAULT_BLK_SZ); 1367 | // char* s5 = (char*)_ffunc_alloc(&thisp, DEFAULT_BLK_SZ); 1368 | 1369 | // char* s6 = (char*)_ffunc_alloc(&thatp, DEFAULT_BLK_SZ); 1370 | // char* s7 = (char*)_ffunc_alloc(&thatp, DEFAULT_BLK_SZ); 1371 | 1372 | // ffunc_print("thisp Address = %p\n", thisp); 1373 | // ffunc_print("thisp->next Address = %p\n", thisp->next); 1374 | 1375 | // // ffunc_print("thatp Address = %u\n", thatp); 1376 | // // ffunc_print("thatp->next Address = %u\n", thatp->next); 1377 | 1378 | 1379 | // ffunc_destroy_pool(thatp); 1380 | // ffunc_destroy_pool(thisp); 1381 | 1382 | // return (0); 1383 | // } 1384 | -------------------------------------------------------------------------------- /ffunc.h: -------------------------------------------------------------------------------- 1 | #ifndef __ffunc_h__ 2 | #define __ffunc_h__ 3 | #ifndef _GNU_SOURCE 4 | #define _GNU_SOURCE 5 | #endif 6 | #ifdef __cplusplus 7 | #include 8 | extern "C" { 9 | #endif 10 | 11 | #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #define FFUNC void 22 | #else 23 | #define FFUNC extern __declspec(dllexport) void 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | #ifndef __APPLE__ 30 | #include 31 | #endif 32 | #define DEFAULT_BLK_SZ 2048 33 | #define ALIGNMENT 16 34 | 35 | typedef struct pool { 36 | void *next, 37 | *end; 38 | struct pool *prev; 39 | } ffunc_pool; 40 | 41 | typedef struct { 42 | ffunc_pool *pool; 43 | char *query_str; 44 | FCGX_Request *request; 45 | } ffunc_session_t; 46 | 47 | typedef struct { 48 | char *data; 49 | size_t len; 50 | } ffunc_str_t; 51 | 52 | typedef struct { 53 | int sock_port; 54 | char* sock_port_str; 55 | int backlog; 56 | int max_thread; 57 | char** ffunc_nmap_func; 58 | size_t max_read_buffer; 59 | 60 | #if defined __GNUC__ || defined __CYGWIN__ || defined __MINGW32__ || defined __APPLE__ 61 | void(*app_init_handler)(void); 62 | int daemon; 63 | #else 64 | #if defined _WIN32 || _WIN64 /*Windows*/ 65 | char* app_init_handler_name; 66 | #endif 67 | #endif 68 | 69 | /* DO NOT USE THE VARIABLE BELOW */ 70 | char* __exec_name; 71 | } ffunc_config_t; 72 | 73 | extern ffunc_pool* ffunc_create_pool(size_t size); 74 | extern void ffunc_destroy_pool(ffunc_pool *p); 75 | extern void * _ffunc_alloc(ffunc_pool **p, size_t size); 76 | extern size_t ffunc_mem_left(ffunc_pool *p); 77 | extern size_t ffunc_mem_used(ffunc_pool *p); 78 | extern size_t ffunc_blk_size(ffunc_pool *p); 79 | 80 | extern int ffunc_main(int argc, char *argv[], ffunc_config_t *ffunc_conf); 81 | /* direct consume will be trigger if not function given, it let user self handling the request */ 82 | FFUNC ffunc_direct_consume(ffunc_session_t *); 83 | extern char *ffunc_strdup(ffunc_session_t * csession, const char *str, size_t len); 84 | extern size_t(*ffunc_read_body)(ffunc_session_t *, ffunc_str_t *); 85 | extern void* ffunc_get_query_param(ffunc_session_t * csession, const char *key, size_t len); 86 | 87 | #define ffunc_parse_function(ffconf_, ...) do { \ 88 | const char *_ffunc_nmap_func[] = {__VA_ARGS__, NULL}; \ 89 | unsigned int i=0, j; \ 90 | while(_ffunc_nmap_func[i])i++; \ 91 | (ffconf_)->ffunc_nmap_func = (char**) calloc(i+1, sizeof(char*) ); \ 92 | for(j=0;jffunc_nmap_func[j] = (char*) (x ? memcpy(x, _ffunc_nmap_func[j], len) : NULL); \ 96 | } \ 97 | (ffconf_)->ffunc_nmap_func[i] = NULL; \ 98 | } while(0) 99 | 100 | #define ffunc_write_out(_csession, ...) FCGX_FPrintF(_csession->request->out, __VA_ARGS__) 101 | #define ffunc_get_fcgi_param(_csession, constkey) FCGX_GetParam(constkey, _csession->request->envp) 102 | #define ffunc_alloc(ffunc_session_t, sz) _ffunc_alloc(&ffunc_session_t->pool, sz) 103 | 104 | #define ffunc_write_http_ok_status(_csession) ffunc_write_out(_csession, "Status: 200 OK\r\n") 105 | #define ffunc_write_http_not_found_status(_csession) ffunc_write_out(_csession, "Status: 404 Not Found\r\n") 106 | #define ffunc_write_http_internal_error_status(_csession) ffunc_write_out(_csession, "Status: 500 Internal Server Error\r\n") 107 | #define ffunc_write_http_no_content_status(_csession) ffunc_write_out(_csession, "Status: 204 No Content\r\n") 108 | #define ffunc_write_textplain_header(_csession) ffunc_write_out(_csession, "Content-Type: text/plain\r\n\r\n") 109 | #define ffunc_write_default_header(_csession) ffunc_write_out(_csession, "Content-Type: text/plain\r\n\r\n") 110 | #define ffunc_write_jsonp_header(_csession) ffunc_write_out(_csession, "Content-Type: application/javascript\r\n\r\n") 111 | #define ffunc_write_json_header(_csession) ffunc_write_out(_csession, "Content-Type: application/json\r\n\r\n") 112 | #define ffunc_write_xwwwformurlenc_header(_csession) ffunc_write_out(_csession, "Content-Type: application/x-www-form-urlencoded\r\n\r\n") 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /images/fcgi_func_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Taymindis/fcgi-function/1a231c95af59193e9d441f050e6b6e9e2953ec2a/images/fcgi_func_architecture.png -------------------------------------------------------------------------------- /images/simple-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Taymindis/fcgi-function/1a231c95af59193e9d441f050e6b6e9e2953ec2a/images/simple-flow.png -------------------------------------------------------------------------------- /payload.txt: -------------------------------------------------------------------------------- 1 | {"items": 2 | [ 3 | { 4 | "name": "Enjoy the C Handler Module!!", 5 | "index": "X", 6 | "optional": "0" 7 | }, 8 | { 9 | "name": "status", 10 | "index": "X", 11 | "optional": "0" 12 | } 13 | ]} --------------------------------------------------------------------------------