#include #include #include #include #include #include "squirrel.h" #include "sqstdblobimpl.h" SQ_OPT_STRING_STRLEN(); /* Lua System: Internet Server Application: FastCGI */ #define FCGI_VERSION 1 #define FCGI_HEADER_LEN 8 #define FCGI_END_REQUEST_LEN 8 #define FCGI_RESPONDER 1 #define FCGI_KEEP_CONN 1 #define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 #define FCGI_NULL_REQUEST_ID ~0U #define FCGI_MAX_CONTENT_LEN 0xFFFF static const char * fcgi_decode_begin_request (HSQUIRRELVM v, const unsigned char *cp, const size_t size) { int role, keep_conn; if (size != 8) return "bad content"; role = (*cp++ << 8); role |= *cp++; keep_conn = *cp & FCGI_KEEP_CONN; if (role != FCGI_RESPONDER) return "bad role"; sq_pushbool(v, keep_conn); sq_setfield(v, -2, "keep_conn"); return NULL; } static const char * fcgi_decode_params (HSQUIRRELVM v, const unsigned char *cp, const size_t size) { const unsigned char *endp = cp + size; while (cp < endp) { unsigned int name_len, value_len; name_len = *cp++; if (name_len & 0x80) { if (cp + 3 >= endp) return "bad content"; name_len ^= 0x80; name_len = (name_len << 8) | *cp++; name_len = (name_len << 8) | *cp++; name_len = (name_len << 8) | *cp++; } if (cp >= endp) return "bad content"; value_len = *cp++; if (value_len & 0x80) { if (cp + 3 >= endp) return "bad content"; value_len ^= 0x80; value_len = (value_len << 8) | *cp++; value_len = (value_len << 8) | *cp++; value_len = (value_len << 8) | *cp++; } if (cp + name_len + value_len > endp) return "bad content"; sq_pushstring(v, (const char *) cp, name_len); cp += name_len; sq_pushstring(v, (const char *) cp, value_len); cp += value_len; sq_rawset(v, -3); } return NULL; } static const char * fcgi_decode_stdin (HSQUIRRELVM v, const unsigned char *cp, const size_t size) { int concat = 1; sq_getfield(v, -1, "stdin"); if (sq_isnil(v, -1)) { sq_pop(v, 1); concat = 0; } sq_pushstring(v, (const char *) cp, size); if (concat) sq_concat(v, 2); sq_setfield(L, -2, "stdin"); return NULL; } /* * Arguments: ..., channel (table) */ static int fcgi_decode_record (HSQUIRRELVM v, const unsigned char *cp, const size_t size, unsigned int *ready_request_id) { unsigned int version, type, request_id, content_len, padding_len, len; int is_ready = 0; const char *err = NULL; version = *cp++; type = *cp++; request_id = (*cp++ << 8); request_id |= *cp++; content_len = (*cp++ << 8); content_len |= *cp++; padding_len = *cp++; cp++; /* reserved */ len = FCGI_HEADER_LEN + content_len + padding_len; if (len > size) return 0; sq_rawgeti(v, -1, request_id); if (!sq_istable(v, -1)) { sq_pop(v, 1); sq_newtable(v); sq_pushvalue(v, -1); sq_rawseti(v, -3, request_id); sq_pushinteger(v, request_id); sq_setfield(v, -2, "id"); sq_getfield(v, -2, "request_meta"); /* metatable from channel */ sq_setmetatable(v, -2); sq_pushvalue(v, -2); /* channel */ sq_setfield(v, -2, "channel"); } if (version != FCGI_VERSION) err = "bad version"; else { switch (type) { case FCGI_BEGIN_REQUEST: err = fcgi_decode_begin_request(v, cp, content_len); break; case FCGI_ABORT_REQUEST: break; case FCGI_PARAMS: if (content_len) err = fcgi_decode_params(v, cp, content_len); break; case FCGI_STDIN: if (content_len) err = fcgi_decode_stdin(v, cp, content_len); else is_ready = 1; break; default: err = "bad type"; } } if (err) { sq_pushstring(v, err, -1); sq_setfield(v, -2, "error"); /* lua_pushinteger(L, version); lua_setfield(L, -2, "version"); lua_pushinteger(L, type); lua_setfield(L, -2, "type"); lua_pushlstring(L, cp, content_len); lua_setfield(L, -2, "content"); */ is_ready = 1; } if (is_ready) { if (*ready_request_id != FCGI_NULL_REQUEST_ID) sq_pushinteger(v, *ready_request_id); else sq_pushnil(v); sq_setfield(v, -2, "next_ready"); *ready_request_id = request_id; } sq_pop(v, 1); return len; } /* * Arguments: {string | membuf_udata}, channel (table) * Returns: [request_id (number)] */ static SQRESULT sq_fastcgi_decode (HSQUIRRELVM v) { SQ_FUNC_VARS(v); struct sys_buffer sb; const char *cp; unsigned int ready_request_id = FCGI_NULL_REQUEST_ID; if (!sys_buffer_read_init(v, 1, &sb)) return 0; sq_checktype(v, 2, LUA_TTABLE); cp = sb.ptr.r; while (sb.size >= FCGI_HEADER_LEN) { const int len = fcgi_decode_record(v, (const unsigned char *) cp, sb.size, &ready_request_id); if (!len) break; cp += len; sb.size -= len; } sys_buffer_read_next(&sb, cp - sb.ptr.r); if (ready_request_id != FCGI_NULL_REQUEST_ID) { sq_pushinteger(v, ready_request_id); return 1; } return 0; } /* * Arguments: membuf_udata, request_id (number), prev_offset (number), string * Returns: offset (number) */ static SQRESULT sq_fastcgi_encode (HSQUIRRELVM v) { SQ_FUNC_VARS(v); SQBlob *blob = NULL; SQObjectType ptype = sq_gettype(v, 2); if(ptype == OT_INSTANCE){ if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&blob,(SQUserPointer)SQBlob::SQBlob_TAG))) return sq_throwerror(v,_SC("invalid type tag")); if(!blob || !blob->IsValid()) return sq_throwerror(v,_SC("the blob is invalid")); } SQ_GET_INTEGER(v, 3, request_id); SQ_GET_INTEGER(v, 4, prev_offset); SQ_GET_STRING(v, 5, s); unsigned char *cp; unsigned char *lenp; SQInteger prev_len = 0, prev_padding_len = 0; SQInteger data_len, padding_len; if (s_size > FCGI_MAX_CONTENT_LEN) { return sq_throwerror(v,_SC("too long")); } sys_buffer_write_init(v, 1, &sb, NULL, 0); if (prev_offset) { cp = (unsigned char *) sb.ptr.w - prev_offset; if (cp[-1] == (unsigned char) request_id && cp[-2] == (unsigned char) (request_id >> 8)) { prev_len = (cp[0] << 8) | cp[1]; prev_padding_len = cp[2]; } else { prev_offset = 0; } } if (!len) { data_len = 2 * FCGI_HEADER_LEN + FCGI_END_REQUEST_LEN; prev_offset = 0; } else { data_len = len; if (!prev_offset || (prev_len + len) > FCGI_MAX_CONTENT_LEN) { data_len += FCGI_HEADER_LEN; prev_offset = 0; } else { data_len -= prev_padding_len; } } if (data_len <= 0) { padding_len = prev_padding_len - len; /* fits to previous padding */ data_len = 0; } else { padding_len = 8 - data_len % 8; if (padding_len == 8) padding_len = 0; data_len += padding_len; if (!sys_buffer_write_next(v, &sb, NULL, data_len)) return 0; } /* buffer may be re-allocated */ cp = (unsigned char *) sb.ptr.w; if (prev_offset) { lenp = cp - prev_offset; cp -= prev_padding_len; memcpy(cp, s, len); /* concat to previous record */ cp += len; len += prev_len; } else { *cp++ = FCGI_VERSION; *cp++ = FCGI_STDOUT; *cp++ = (unsigned char) (request_id >> 8); *cp++ = (unsigned char) request_id; lenp = cp++; /* content_len */ cp++; cp++; /* padding_len */ *cp++ = 0; /* reserved */ if (len) { memcpy(cp, s, len); cp += len; } else { *cp++ = FCGI_VERSION; *cp++ = FCGI_END_REQUEST; *cp++ = (unsigned char) (request_id >> 8); *cp++ = (unsigned char) request_id; *cp++ = 0; /* content_len */ *cp++ = FCGI_END_REQUEST_LEN; *cp++ = 0; /* padding_len */ *cp++ = 0; /* reserved */ memset(cp, 0, FCGI_END_REQUEST_LEN); } } lenp[0] = (unsigned char) (len >> 8); lenp[1] = (unsigned char) len; lenp[2] = (unsigned char) padding_len; if (padding_len) { memset(cp, 0, padding_len); cp += padding_len; } sys_buffer_write_done(v, &sb, NULL, data_len); sq_pushinteger(v, cp - lenp); return 1; } #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_fastcgi_##name,nparams,tycheck} static SQRegFunction sq_fastcgi_methods[] = { _DECL_FUNC(encode, -1, _SC("x")), _DECL_FUNC(decode, 1, _SC("x")), {0,0} }; #undef _DECL_FUNC #ifdef __cplusplus extern "C" { #endif SQRESULT sqext_register_fastcgi(HSQUIRRELVM v) { //add a namespace sqfastcgi sq_pushstring(v, sq_fastcgi_TAG,-1); sq_newclass(v, SQFalse); sq_insert_reg_funcs(v, sq_fastcgi_methods); sq_newslot(v,-3,SQTrue); //add sqfastcgi table to the root table return SQ_OK; } #ifdef __cplusplus } #endif