├── .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 | 
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 | ]}
--------------------------------------------------------------------------------