aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Bühler <stbuehler@web.de>2008-09-20 23:29:37 +0200
committerStefan Bühler <stbuehler@web.de>2008-09-20 23:29:37 +0200
commit01d3a3b5df78a9944bae76d7faf3d04fe0164a19 (patch)
tree4dda982bca23959eaf1e043e1cf97f35857879de
parent0d936dff7f1387c9d6e6df763447d29fca61d748 (diff)
downloadfcgi-debug-01d3a3b5df78a9944bae76d7faf3d04fe0164a19.tar.gz
fcgi-debug-01d3a3b5df78a9944bae76d7faf3d04fe0164a19.zip
Add fastcgi parser
-rw-r--r--cmake/FindRagel.cmake42
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/connection.c19
-rw-r--r--src/debug-fastcgi.c55
-rw-r--r--src/debug-fastcgi.h69
-rw-r--r--src/fcgi-debug.h18
-rw-r--r--src/log.c10
-rw-r--r--src/parse-fastcgi.rl277
-rw-r--r--src/tools.c22
9 files changed, 513 insertions, 5 deletions
diff --git a/cmake/FindRagel.cmake b/cmake/FindRagel.cmake
new file mode 100644
index 0000000..d175bbc
--- /dev/null
+++ b/cmake/FindRagel.cmake
@@ -0,0 +1,42 @@
+
+IF(NOT RAGEL_EXECUTABLE)
+ MESSAGE(STATUS "Looking for ragel")
+ FIND_PROGRAM(RAGEL_EXECUTABLE ragel)
+ IF(RAGEL_EXECUTABLE)
+ EXECUTE_PROCESS(COMMAND "${RAGEL_EXECUTABLE}" -v OUTPUT_VARIABLE _version)
+ STRING(REGEX MATCH "[0-9.]+" RAGEL_VERSION ${_version})
+ SET(RAGEL_FOUND TRUE)
+ ENDIF(RAGEL_EXECUTABLE)
+ELSE(NOT RAGEL_EXECUTABLE)
+ EXECUTE_PROCESS(COMMAND "${RAGEL_EXECUTABLE}" -v OUTPUT_VARIABLE _version)
+ STRING(REGEX MATCH "[0-9.]+" RAGEL_VERSION ${_version})
+ SET(RAGEL_FOUND TRUE)
+ENDIF(NOT RAGEL_EXECUTABLE)
+
+IF(RAGEL_FOUND)
+ IF (NOT Ragel_FIND_QUIETLY)
+ MESSAGE(STATUS "Found ragel: ${RAGEL_EXECUTABLE} (${RAGEL_VERSION})")
+ ENDIF (NOT Ragel_FIND_QUIETLY)
+
+ IF(NOT RAGEL_FLAGS)
+ SET(RAGEL_FLAGS "-T1")
+ ENDIF(NOT RAGEL_FLAGS)
+
+ MACRO(RAGEL_PARSER SRCFILE)
+ GET_FILENAME_COMPONENT(SRCBASE "${SRCFILE}" NAME_WE)
+ SET(OUTFILE "${CMAKE_CURRENT_BINARY_DIR}/${SRCBASE}.c")
+ SET(INFILE "${CMAKE_CURRENT_SOURCE_DIR}/${SRCFILE}")
+ ADD_CUSTOM_COMMAND(OUTPUT ${OUTFILE}
+ COMMAND "${RAGEL_EXECUTABLE}"
+ ARGS -C ${RAGEL_FLAGS} -o "${OUTFILE}" "${INFILE}"
+ DEPENDS "${INFILE}"
+ COMMENT "Generating ${SRCBASE}.c from ${SRCFILE}"
+ )
+ ENDMACRO(RAGEL_PARSER)
+
+ELSE(RAGEL_FOUND)
+
+ IF(Ragel_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "Could not find ragel")
+ ENDIF(Ragel_FIND_REQUIRED)
+ENDIF(RAGEL_FOUND)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c20eeab..f658851 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,6 +9,7 @@ INCLUDE(CPack)
INCLUDE(FindPkgConfig)
INCLUDE(LighttpdMacros)
+FIND_PACKAGE(Ragel REQUIRED)
cmake_policy(SET CMP0005 OLD)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES)
@@ -42,12 +43,17 @@ ADD_DEFINITIONS(-DHAVE_CONFIG_H)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+RAGEL_PARSER(parse-fastcgi.rl)
+
ADD_EXECUTABLE(fcgi-debug
fcgi-debug.c
connection.c
tools.c
stream.c
log.c
+
+ debug-fastcgi.c
+ parse-fastcgi.c
)
ADD_TARGET_PROPERTIES(fcgi-debug LINK_FLAGS ${EV_LDFLAGS})
diff --git a/src/connection.c b/src/connection.c
index b1ac684..0292aba 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -2,6 +2,14 @@
static void connection_close(connection *con) {
stream_close(con->srv, &con->s_server, &con->s_client);
+ if (con->df_server) {
+ con->df_server(con->ctx_server);
+ con->df_server = NULL;
+ }
+ if (con->df_client) {
+ con->df_client(con->ctx_client);
+ con->df_client = NULL;
+ }
g_slice_free(connection, con);
}
@@ -48,8 +56,11 @@ static void fd_server_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
connection_close(con);
return;
}
- log_raw("raw from server", con->con_id, g_string_set_const(&s, readbuf, len));
+// log_raw("raw", TRUE, con->con_id, g_string_set_const(&s, readbuf, len));
stream_append(srv, &con->s_client, readbuf, len);
+ if (con->da_server) {
+ con->da_server(con->ctx_server, readbuf, len);
+ }
}
if (revents & EV_WRITE) {
if (-1 == stream_write(srv, &con->s_server)) {
@@ -74,8 +85,11 @@ static void fd_client_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
connection_close(con);
return;
}
- log_raw("raw from client", con->con_id, g_string_set_const(&s, readbuf, len));
+// log_raw("raw", FALSE, con->con_id, g_string_set_const(&s, readbuf, len));
stream_append(srv, &con->s_server, readbuf, len);
+ if (con->da_client) {
+ con->da_client(con->ctx_client, readbuf, len);
+ }
}
if (revents & EV_WRITE) {
if (-1 == stream_write(srv, &con->s_client)) {
@@ -104,5 +118,6 @@ void connection_new(server *srv, int fd_server) {
con->client_connected = FALSE;
g_print("new connection (%u)\n", con->con_id);
stream_init(srv, &con->s_server, &con->s_client, fd_server, fd_client, fd_server_cb, fd_client_cb, con);
+ setup_debug_fastcgi(con);
connection_connect(con);
}
diff --git a/src/debug-fastcgi.c b/src/debug-fastcgi.c
new file mode 100644
index 0000000..68433f3
--- /dev/null
+++ b/src/debug-fastcgi.c
@@ -0,0 +1,55 @@
+#include "debug-fastcgi.h"
+
+#define SWITCH_HANDLE(x) case x: return #x
+const char* fcgi_type2string(enum FCGI_Type val) {
+ switch (val) {
+ SWITCH_HANDLE(FCGI_BEGIN_REQUEST);
+ SWITCH_HANDLE(FCGI_ABORT_REQUEST);
+ SWITCH_HANDLE(FCGI_END_REQUEST);
+ SWITCH_HANDLE(FCGI_PARAMS);
+ SWITCH_HANDLE(FCGI_STDIN);
+ SWITCH_HANDLE(FCGI_STDOUT);
+ SWITCH_HANDLE(FCGI_STDERR);
+ SWITCH_HANDLE(FCGI_DATA);
+ SWITCH_HANDLE(FCGI_GET_VALUES);
+ SWITCH_HANDLE(FCGI_GET_VALUES_RESULT);
+ SWITCH_HANDLE(FCGI_UNKNOWN_TYPE);
+ default: return "<unknown>";
+ }
+}
+
+const char* fcgi_flags2string(guint8 flags) {
+ switch (flags) {
+ case 0: return "none";
+ case 1: return "FCGI_KEEP_CONN";
+ default: return "<unknown>";
+ }
+}
+
+const char* fcgi_role2string(enum FCGI_Role role) {
+ switch (role) {
+ SWITCH_HANDLE(FCGI_RESPONDER);
+ SWITCH_HANDLE(FCGI_AUTHORIZER);
+ SWITCH_HANDLE(FCGI_FILTER);
+ default: return "<unknown>";
+ }
+}
+
+const char* fcgi_protocol_status2string(enum FCGI_ProtocolStatus val) {
+ switch (val) {
+ SWITCH_HANDLE(FCGI_REQUEST_COMPLETE);
+ SWITCH_HANDLE(FCGI_CANT_MPX_CONN);
+ SWITCH_HANDLE(FCGI_OVERLOADED);
+ SWITCH_HANDLE(FCGI_UNKNOWN_ROLE);
+ default: return "<unknown>";
+ }
+}
+
+void setup_debug_fastcgi(connection *con) {
+ con->ctx_server = fcgi_context_new(TRUE, con->con_id);
+ con->da_server = fcgi_context_append;
+ con->df_server = fcgi_context_free;
+ con->ctx_client = fcgi_context_new(FALSE, con->con_id);
+ con->da_client = fcgi_context_append;
+ con->df_client = fcgi_context_free;
+}
diff --git a/src/debug-fastcgi.h b/src/debug-fastcgi.h
new file mode 100644
index 0000000..f2f2a48
--- /dev/null
+++ b/src/debug-fastcgi.h
@@ -0,0 +1,69 @@
+#ifndef FCGI_DEBUG_DEBUG_FASTCGI_H
+#define FCGI_DEBUG_DEBUG_FASTCGI_H
+
+struct fcgi_context;
+typedef struct fcgi_context fcgi_context;
+
+#include "fcgi-debug.h"
+
+struct fcgi_context {
+ GString *buffer;
+ GString *s_params;
+ gboolean error;
+
+ struct {
+ guint8 version;
+ guint8 type;
+ guint16 requestID;
+ guint16 contentLength;
+ guint8 paddingLength;
+ } FCGI_Record;
+
+ gboolean from_server;
+ guint con_id;
+};
+
+#define FCGI_HEADER_LEN 8
+
+enum FCGI_Type {
+ FCGI_BEGIN_REQUEST = 1,
+ FCGI_ABORT_REQUEST = 2,
+ FCGI_END_REQUEST = 3,
+ FCGI_PARAMS = 4,
+ FCGI_STDIN = 5,
+ FCGI_STDOUT = 6,
+ FCGI_STDERR = 7,
+ FCGI_DATA = 8,
+ FCGI_GET_VALUES = 9,
+ FCGI_GET_VALUES_RESULT = 10,
+ FCGI_UNKNOWN_TYPE = 11
+};
+#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
+
+enum FCGI_Flags {
+ FCGI_KEEP_CONN = 1
+};
+
+enum FCGI_Role {
+ FCGI_RESPONDER = 1,
+ FCGI_AUTHORIZER = 2,
+ FCGI_FILTER = 3
+};
+
+enum FCGI_ProtocolStatus {
+ FCGI_REQUEST_COMPLETE = 0,
+ FCGI_CANT_MPX_CONN = 1,
+ FCGI_OVERLOADED = 2,
+ FCGI_UNKNOWN_ROLE = 3
+};
+
+fcgi_context* fcgi_context_new(gboolean from_server, guint con_id);
+void fcgi_context_free(gpointer _ctx);
+void fcgi_context_append(gpointer _ctx, const gchar* buf, gssize buflen);
+
+const char* fcgi_type2string(enum FCGI_Type val);
+const char* fcgi_flags2string(guint8 flags);
+const char* fcgi_role2string(enum FCGI_Role role);
+const char* fcgi_protocol_status2string(enum FCGI_ProtocolStatus val);
+
+#endif
diff --git a/src/fcgi-debug.h b/src/fcgi-debug.h
index 0a71aff..1082e35 100644
--- a/src/fcgi-debug.h
+++ b/src/fcgi-debug.h
@@ -1,3 +1,5 @@
+#ifndef FCGI_DEBUG_H
+#define FCGI_DEBUG_H
#include <ev.h>
#include <glib.h>
@@ -61,10 +63,17 @@ struct stream {
GString *buffer;
};
+typedef void (*DebugAppend)(gpointer ctx, const gchar *buf, gssize buflen);
+typedef void (*DebugFree)(gpointer ctx);
+
struct connection {
server *srv;
guint con_id;
stream s_server, s_client;
+ gpointer ctx_server, ctx_client;
+ DebugAppend da_server, da_client;
+ DebugFree df_server, df_client;
+
gboolean client_connected;
};
@@ -79,6 +88,7 @@ void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events);
void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events);
GString* g_string_set_const(GString* s, const gchar *data, gsize len);
+GString* g_string_escape(GString *data);
/* connection.c */
void connection_new(server *srv, int fd_server);
@@ -96,4 +106,10 @@ void stream_append(server *srv, stream *s, char *buf, gssize bufsize);
gssize stream_write(server *srv, stream *s);
/* log.c */
-void log_raw(const gchar *head, guint con_id, GString *data);
+void log_raw(const gchar *head, gboolean from_server, guint con_id, GString *data);
+void log_raw_split(const gchar *head, gboolean from_server, guint con_id, GString *data);
+
+/* debug-fastcgi.c */
+void setup_debug_fastcgi(connection *con);
+
+#endif \ No newline at end of file
diff --git a/src/log.c b/src/log.c
index 349521f..e95c17c 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1,6 +1,6 @@
#include "fcgi-debug.h"
-void log_raw(const gchar *head, guint con_id, GString *data) {
+void log_raw_split(const gchar *head, gboolean from_server, guint con_id, GString *data) {
const gchar *start = data->str, *end = start+data->len, *i;
GString *line = g_string_sized_new(0);
for ( ; start < end; ) {
@@ -30,8 +30,14 @@ void log_raw(const gchar *head, guint con_id, GString *data) {
g_string_append_c(line, c);
}
}
- g_print("%s (%u): %s\n", head, con_id, line->str);
+ g_print("%s from %s (%u): %s\n", head, from_server ? "server" : "client", con_id, line->str);
start = i;
}
g_string_free(line, TRUE);
}
+
+void log_raw(const gchar *head, gboolean from_server, guint con_id, GString *data) {
+ GString *line = g_string_escape(data);
+ g_print("%s from %s (%u): %s\n", head, from_server ? "server" : "client", con_id, line->str);
+ g_string_free(line, TRUE);
+}
diff --git a/src/parse-fastcgi.rl b/src/parse-fastcgi.rl
new file mode 100644
index 0000000..c9f8ab4
--- /dev/null
+++ b/src/parse-fastcgi.rl
@@ -0,0 +1,277 @@
+
+#include "debug-fastcgi.h"
+
+fcgi_context* fcgi_context_new(gboolean from_server, guint con_id) {
+ fcgi_context *ctx = g_slice_new0(fcgi_context);
+ ctx->buffer = g_string_sized_new(0);
+ ctx->from_server = from_server;
+ ctx->con_id = con_id;
+
+ ctx->s_params = g_string_sized_new(0);
+
+ return ctx;
+}
+
+void fcgi_context_free(gpointer _ctx) {
+ fcgi_context* ctx = (fcgi_context*) _ctx;
+ g_string_free(ctx->buffer, TRUE);
+ g_string_free(ctx->s_params, TRUE);
+ g_slice_free(fcgi_context, ctx);
+}
+
+#define USE_STREAM(s, p, pe) do {\
+ g_string_append_len(ctx->s, (gchar*) p, pe - p); \
+ p = (guint8*) ctx->s->str; \
+ pe = p + ctx->s->len; \
+} while(0)
+
+#define EAT_STREAM(s, p) do {\
+ g_string_erase(ctx->s, 0, p - (guint8*) ctx->s->str); \
+ p = (guint8*) ctx->s->str; \
+ pe = p + ctx->s->len; \
+} while(0)
+
+static gboolean get_key_value_pair_len(guint8 **_p, guint8 *pe, guint *_len1, guint *_len2) {
+ guint8 *p = *_p;
+ guint len1, len2;
+ /* need at least 2 bytes */
+ if (p + 2 >= pe) return FALSE;
+ len1 = *p++;
+ if (len1 & 0x80) {
+ /* need at least 3+1 bytes */
+ if (p + 4 >= pe) return FALSE;
+ len1 = ((len1 & 0x7F) << 24) | (p[0] << 16) | (p[1] << 8) | p[2];
+ p += 3;
+ }
+ len2 = *p++;
+ if (len2 & 0x80) {
+ /* need at least 3 bytes */
+ if (p + 3 >= pe) return FALSE;
+ len2 = ((len2 & 0x7F) << 24) | (p[0] << 16) | (p[1] << 8) | p[2];
+ p += 3;
+ }
+ *_len1 = len1;
+ *_len2 = len2;
+ *_p = p;
+ return TRUE;
+}
+
+void fcgi_packet_parse(fcgi_context *ctx, guint8 *p, guint8 *pe) {
+ guint8 *eof = pe;
+ GString tmp1, tmp2;
+ UNUSED(eof);
+
+ switch (ctx->FCGI_Record.type) {
+ case FCGI_BEGIN_REQUEST: {
+ guint role;
+ guint8 flags;
+ if (ctx->FCGI_Record.contentLength != 8) {
+ g_print("wrong FCGI_BEGIN_REQUEST size from %s (%u): %u\n",
+ ctx->from_server ? "server" : "client", ctx->con_id, (guint) ctx->FCGI_Record.contentLength);
+ return;
+ }
+ role = (p[0] << 8) | p[1];
+ flags = p[2];
+ g_print("begin request from %s (%u): role: %s, flags: %s\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ fcgi_role2string(role),
+ fcgi_flags2string(flags)
+ );
+ break;
+ }
+ case FCGI_ABORT_REQUEST: {
+ if (ctx->FCGI_Record.contentLength) {
+ g_print("wrong FCGI_ABORT_REQUEST size from %s (%u): %u\n",
+ ctx->from_server ? "server" : "client", ctx->con_id, (guint) ctx->FCGI_Record.contentLength);
+ return;
+ }
+ g_print("abort request from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ break;
+ }
+ case FCGI_END_REQUEST: {
+ guint appStatus;
+ guint8 protocolStatus;
+ if (ctx->FCGI_Record.contentLength != 8) {
+ g_print("wrong FCGI_END_REQUEST size from %s (%u): %u\n",
+ ctx->from_server ? "server" : "client", ctx->con_id, (guint) ctx->FCGI_Record.contentLength);
+ return;
+ }
+ appStatus = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ protocolStatus = p[4];
+ g_print("end request from %s (%u): applicationStatus: %u, protocolStatus: %s\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ appStatus,
+ fcgi_protocol_status2string(protocolStatus)
+ );
+ break;
+ }
+ case FCGI_PARAMS: {
+ guint len1, len2;
+ GString *s1, *s2;
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("params end from %s (%u)%s\n",
+ ctx->from_server ? "server" : "client", ctx->con_id, ctx->s_params->len ? " (unexpected)" : "");
+ return;
+ }
+ USE_STREAM(s_params, p, pe);
+ while (get_key_value_pair_len(&p, pe, &len1, &len2)) {
+ if (p + len1 + len2 > pe) {
+ return;
+ }
+ s1 = g_string_escape(g_string_set_const(&tmp1, (gchar*) p, len1));
+ s2 = g_string_escape(g_string_set_const(&tmp2, (gchar*) p+len1, len2));
+ g_print("param from %s (%u): '%s' = '%s'\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ s1->str, s2->str);
+ g_string_free(s1, TRUE);
+ g_string_free(s2, TRUE);
+ p += len1 + len2;
+ EAT_STREAM(s_params, p);
+ }
+ break;
+ }
+ case FCGI_STDIN:
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("stdin closed from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ log_raw_split("stdin", ctx->from_server, ctx->con_id, g_string_set_const(&tmp1, (gchar*) p, pe - p));
+ break;
+ case FCGI_STDOUT:
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("stdout closed from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ log_raw_split("stdout", ctx->from_server, ctx->con_id, g_string_set_const(&tmp1, (gchar*) p, pe - p));
+ break;
+ case FCGI_STDERR:
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("stderr closed from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ log_raw_split("stderr", ctx->from_server, ctx->con_id, g_string_set_const(&tmp1, (gchar*) p, pe - p));
+ break;
+ case FCGI_DATA:
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("data closed from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ log_raw_split("data", ctx->from_server, ctx->con_id, g_string_set_const(&tmp1, (gchar*) p, pe - p));
+ break;
+
+ case FCGI_GET_VALUES: {
+ guint len1, len2;
+ GString *s1, *s2;
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("empty get values from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ while (get_key_value_pair_len(&p, pe, &len1, &len2)) {
+ if (p + len1 + len2 > pe) {
+ return;
+ }
+ s1 = g_string_escape(g_string_set_const(&tmp1, (gchar*) p, len1));
+ s2 = g_string_escape(g_string_set_const(&tmp2, (gchar*) p+len1, len2));
+ if (len2) {
+ g_print("get values from %s (%u): '%s' = '%s'?\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ s1->str, s2->str);
+ } else {
+ g_print("get values from %s (%u): '%s'\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ s1->str);
+ }
+ g_string_free(s1, TRUE);
+ g_string_free(s2, TRUE);
+ p += len1 + len2;
+ }
+ if (p != pe) {
+ g_print("unexpected end of get values from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ }
+ break;
+ }
+
+ case FCGI_GET_VALUES_RESULT: {
+ guint len1, len2;
+ GString *s1, *s2;
+ if (!ctx->FCGI_Record.contentLength) {
+ g_print("empty get values result from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ return;
+ }
+ while (get_key_value_pair_len(&p, pe, &len1, &len2)) {
+ if (p + len1 + len2 > pe) {
+ return;
+ }
+ s1 = g_string_escape(g_string_set_const(&tmp1, (gchar*) p, len1));
+ s2 = g_string_escape(g_string_set_const(&tmp2, (gchar*) p+len1, len2));
+ g_print("get values result from %s (%u): '%s' = '%s'\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ s1->str, s2->str);
+ g_string_free(s1, TRUE);
+ g_string_free(s2, TRUE);
+ p += len1 + len2;
+ }
+ if (p != pe) {
+ g_print("unexpected end of get values result from %s (%u)\n",
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ }
+ break;
+ }
+
+ case FCGI_UNKNOWN_TYPE:
+ if (ctx->FCGI_Record.contentLength != 8) {
+ g_print("wrong FCGI_UNKNOWN_TYPE size from %s (%u): %u\n",
+ ctx->from_server ? "server" : "client", ctx->con_id, (guint) ctx->FCGI_Record.contentLength);
+ return;
+ }
+ g_print("unknown type %u from %s (%u)\n", (guint) p[0],
+ ctx->from_server ? "server" : "client", ctx->con_id);
+ break;
+
+ default:
+ g_print("packet from %s (%u): type: %s, id: 0x%x, contentLength: 0x%x\n",
+ ctx->from_server ? "server" : "client", ctx->con_id,
+ fcgi_type2string(ctx->FCGI_Record.type),
+ (guint) ctx->FCGI_Record.requestID,
+ (guint) ctx->FCGI_Record.contentLength
+ );
+ log_raw("packet data", ctx->from_server, ctx->con_id, g_string_set_const(&tmp1, (gchar*) p, pe - p));
+ break;
+ }
+}
+
+void fcgi_context_append(gpointer _ctx, const gchar* buf, gssize buflen) {
+ fcgi_context *ctx = (fcgi_context*) _ctx;
+ guint8* data;
+ guint total_len;
+
+ if (ctx->error) return;
+ g_string_append_len(ctx->buffer, buf, buflen);
+
+ for (;;) {
+ data = (guint8*) ctx->buffer->str;
+
+ if (ctx->buffer->len < FCGI_HEADER_LEN) return;
+
+ ctx->FCGI_Record.version = data[0];
+ ctx->FCGI_Record.type = data[1];
+ ctx->FCGI_Record.requestID = (data[2] << 8) | (data[3]);
+ ctx->FCGI_Record.contentLength = (data[4] << 8) | (data[5]);
+ ctx->FCGI_Record.paddingLength = data[6];
+ /* ignore data[7] */
+
+ total_len = FCGI_HEADER_LEN + ctx->FCGI_Record.contentLength + ctx->FCGI_Record.paddingLength;
+ if (ctx->buffer->len < total_len) return;
+
+ fcgi_packet_parse(ctx, (guint8*) (FCGI_HEADER_LEN + ctx->buffer->str), (guint8*) (ctx->FCGI_Record.contentLength + FCGI_HEADER_LEN + ctx->buffer->str));
+ g_string_erase(ctx->buffer, 0, total_len);
+ }
+}
diff --git a/src/tools.c b/src/tools.c
index d1f17ae..8aeb94b 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -55,3 +55,25 @@ GString* g_string_set_const(GString* s, const gchar *data, gsize len) {
s->len = len;
return s;
}
+
+GString* g_string_escape(GString *data) {
+ GString *s = g_string_sized_new(0);
+ const gchar *start = data->str, *end = start+data->len, *i;
+ for (i = start; i < end; i++) {
+ guchar c = *i;
+ if (c == '\n') {
+ g_string_append_len(s, "\\n", 2);
+ } else if (c == '\r') {
+ g_string_append_len(s, "\\r", 2);
+ } else if (c < 0x20 || c >= 0x80) {
+ static char hex[5] = "\\x00";
+ hex[3] = ((c & 0xF) < 10) ? '0' + (c & 0xF) : 'A' + (c & 0xF) - 10;
+ c /= 16;
+ hex[2] = ((c & 0xF) < 10) ? '0' + (c & 0xF) : 'A' + (c & 0xF) - 10;
+ g_string_append_len(s, hex, 4);
+ } else {
+ g_string_append_c(s, c);
+ }
+ }
+ return s;
+}