fastjs/fastjs.cc

247 lines
6.1 KiB
C++

/*
* fastjs - A v8-based JavaScript FastCGI server
*
* Copyright 2009 - R. Tyler Ballance <tyler@monkeypox.org>
*/
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <v8.h>
#include "fastjs.h"
extern "C" {
extern char **environ;
#include <fcgi_config.h>
#include <fcgiapp.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <glib.h>
}
using namespace v8; // *puke*
/* Functions that will be exposed into the JavaScript environment */
Handle<Value> FastJS_Write(const Arguments& args);
Handle<Value> FastJS_Source(const Arguments& args);
Handle<Value> FastJS_Log(const Arguments& args);
/* Unfortunately we need a few global pointers to our current streams for the JavaScript callbacks */
FCGX_Stream *_in_stream = NULL;
FCGX_Stream *_out_stream = NULL;
FCGX_Stream *_error_stream = NULL;
void *_jQuery = NULL; /* Global jQuery buffer to prevent needing to re-read the file per-request */
static void *read_file_contents(const char *filepath)
{
struct stat attributes;
int fd = 0;
int rc = 0;
rc = stat(filepath, &attributes);
if (rc != 0) {
/* I should do something with the error here */
return NULL;
}
void *buffer = malloc(sizeof(char) * (attributes.st_size + 1));
bzero(buffer, (attributes.st_size + 1));
fd = open(filepath, 0);
if (fd == 0) {
/* I should do something with the error here */
close(fd);
return NULL;
}
rc = read(fd, buffer, attributes.st_size);
close(fd);
return buffer;
}
static void log_exception(FILE *stream, TryCatch *context)
{
HandleScope scope;
String::Utf8Value exception(context->Exception());
Handle<Message> message = context->Message();
if (message.IsEmpty()) {
/* V8 didn't pass any extra fancy information back */
fprintf(stream, *exception);
return;
}
String::Utf8Value filename(message->GetScriptResourceName());
int line = message->GetLineNumber();
fprintf(stream, "%s:%i: %s\n", *filename, line, *exception);
String::Utf8Value offender(message->GetSourceLine());
fprintf(stream, "\t%s\n", *offender);
return;
}
static int exec_javascript(const char *script)
{
HandleScope scope;
TryCatch exception_ctx;
Handle<Script> compiled = Script::Compile(String::New(script));
if (compiled.IsEmpty()) {
log_exception(stderr, &exception_ctx);
return FASTJS_COMPILE_ERROR;
}
Handle<Value> result = compiled->Run();
if (result.IsEmpty()) {
log_exception(stderr, &exception_ctx);
return FASTJS_EXECUTION_ERROR;
}
return FASTJS_SUCCESS;
}
static void parse_environment(Handle<ObjectTemplate> _node, char **env)
{
for (; *env != NULL; env++) {
gchar **splitted = g_strsplit((const gchar *)(*env), "=", 2);
if (splitted == NULL)
continue;
if ( (splitted[0] == NULL) || (splitted[1] == NULL) ) {
g_strfreev(splitted);
continue;
}
_node->Set(String::New((const char *)(splitted[0])), String::New((const char *)(splitted[1])));
g_strfreev(splitted);
}
}
static void req_handle(FCGX_Stream *out, char **environment)
{
if (_jQuery == NULL) {
/* FAIL! */
FCGX_FPrintF(out, "FAILED TO PROPERLY LOAD JQUERY!");
return;
}
HandleScope scope;
Handle<ObjectTemplate> _global = ObjectTemplate::New();
Handle<ObjectTemplate> _fastjs = ObjectTemplate::New();
Handle<ObjectTemplate> _env = ObjectTemplate::New();
Handle<ObjectTemplate> _fcgi_env = ObjectTemplate::New();
parse_environment(_env, environment);
parse_environment(_fcgi_env, environ);
_fastjs->Set(String::New("write"), FunctionTemplate::New(FastJS_Write));
_fastjs->Set(String::New("source"), FunctionTemplate::New(FastJS_Source));
_fastjs->Set(String::New("log"), FunctionTemplate::New(FastJS_Log));
_fastjs->Set(String::New("env"), _env);
_fastjs->Set(String::New("fcgi_env"), _fcgi_env);
_global->Set(String::New("fastjs"), _fastjs);
Persistent<Context> ctx = Context::New(NULL, _global);
Context::Scope ctx_scope(ctx);
int rc = 0;
rc = exec_javascript(JQUERY_COMPAT);
rc = exec_javascript( (const char *)(_jQuery) );
void *index = read_file_contents("pages/index.fjs");
Handle<String> source = String::New( (const char *)(index) );
TryCatch ex;
Handle<Script> script = Script::Compile(source);
if (script.IsEmpty()) {
Handle<Value> exception = ex.Exception();
String::AsciiValue ex_str(exception);
fprintf(stderr, *ex_str);
fprintf(stderr, "\n\n");
}
Handle<Value> results = script->Run();
ctx.Dispose();
/*
String::AsciiValue rc_str(rc);
fprintf(stderr, *rc_str);
*/
}
int main ()
{
FCGX_Stream *in, *out, *err;
FCGX_ParamArray envp;
unsigned int count = 0;
_jQuery = read_file_contents(JQUERY_FILE);
while (FCGX_Accept(&in, &out, &err, &envp) >= 0) {
_in_stream = in;
_error_stream = err;
_out_stream = out;
char *contentLength = FCGX_GetParam("CONTENT_LENGTH", envp);
unsigned int len = 0;
FCGX_FPrintF(out, "Content-type: text/html\r\nX-FastJS-Request: %d\r\n"
"X-FastJS-Process: %d\r\nX-FastJS-Engine: V8\r\n\r\n", ++count, getpid());
if (contentLength != NULL)
len = strtol(contentLength, NULL, 10);
req_handle(out, envp);
_error_stream = NULL;
_in_stream = NULL;
_out_stream = NULL;
} /* while */
if (_jQuery)
free(_jQuery);
return 0;
}
Handle<Value> FastJS_Write(const Arguments& args)
{
for (int i = 0; i < args.Length(); ++i) {
String::Utf8Value str(args[i]);
FCGX_FPrintF(_out_stream, *str);
}
FCGX_FPrintF(_out_stream, "\n");
return Undefined();
}
Handle<Value> FastJS_Source(const Arguments& args) {
for (int i = 0; i < args.Length(); ++i) {
HandleScope scope;
String::Utf8Value file(args[i]);
void *sourcebuf = read_file_contents(*file);
if (sourcebuf == NULL)
return ThrowException(String::New("Error loading the file!"));
int rc = exec_javascript((const char *)(sourcebuf));
if (rc != FASTJS_SUCCESS)
return ThrowException(String::New("Error executing the file!"));
}
return Undefined();
}
Handle<Value> FastJS_Log(const Arguments& args) {
for (int i =0; i < args.Length(); ++i) {
HandleScope scope;
String::Utf8Value line(args[i]);
FCGX_FPrintF(_error_stream, "FastJS_Log>> %s\n", *line);
}
return Undefined();
}