parent
0d936dff7f
commit
01d3a3b5df
9 changed files with 513 additions and 5 deletions
@ -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) |
@ -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; |
||||
} |
@ -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 |
@ -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); |
||||
} |
||||
} |
Loading…
Reference in new issue