#include "uwsgi_rack.h"

extern struct uwsgi_server uwsgi;

extern struct uwsgi_rack ur;
extern struct uwsgi_plugin rack_plugin;

#define uwsgi_rack_api(x, y, z) rb_define_module_function(rb_uwsgi_embedded, x, y, z)

VALUE rack_uwsgi_warning(VALUE *class, VALUE rbmessage) {

	Check_Type(rbmessage, T_STRING);
        char *message = RSTRING_PTR(rbmessage);
	size_t len = RSTRING_LEN(rbmessage);

        if (len > 80) {
                uwsgi_log("- warning message must be max 80 chars, it will be truncated -");
                memcpy(uwsgi.shared->warning_message, message, 80);
                uwsgi.shared->warning_message[80] = 0;
        }
        else {
                memcpy(uwsgi.shared->warning_message, message, len);
                uwsgi.shared->warning_message[len] = 0;
        }

        return Qnil;
}

VALUE rack_uwsgi_log(VALUE *class, VALUE msg) {

	Check_Type(msg, T_STRING);

        uwsgi_log("%s\n", RSTRING_PTR(msg));

        return Qnil;
}

VALUE rack_uwsgi_i_am_the_spooler(VALUE *class) {
#ifdef UWSGI_SPOOLER
        if (uwsgi.i_am_a_spooler) {
                return Qtrue;
        }
#endif

        return Qfalse;
}



VALUE rack_uwsgi_setprocname(VALUE *class, VALUE rbname) {

	Check_Type(rbname, T_STRING);
	char *name = RSTRING_PTR(rbname);
        uwsgi_set_processname(name);

        return Qnil;
}

VALUE rack_uwsgi_mem(VALUE *class) {

        uint64_t rss=0, vsz = 0;
        VALUE ml = rb_ary_new2(2);

        get_memusage(&rss, &vsz);

	rb_ary_store(ml, 0, LONG2NUM(rss));
	rb_ary_store(ml, 1, LONG2NUM(vsz));

        return ml;

}

VALUE rack_uwsgi_request_id(VALUE *class) {
        return ULONG2NUM(uwsgi.workers[uwsgi.mywid].requests);
}

VALUE rack_uwsgi_worker_id(VALUE *class) {
        return INT2NUM(uwsgi.mywid);
}

VALUE rack_uwsgi_mule_id(VALUE *class) {
        return INT2NUM(uwsgi.muleid);
}

VALUE rack_uwsgi_logsize(VALUE *class) {
        return ULONG2NUM(uwsgi.shared->logsize);
}

VALUE rack_uwsgi_mule_msg(int argc, VALUE *argv, VALUE *class) {

        int fd = -1;
        int mule_id = -1;

	if (argc == 0) return Qnil;

	Check_Type(argv[0], T_STRING);

        char *message = RSTRING_PTR(argv[0]);
        size_t message_len = RSTRING_LEN(argv[0]);

        if (uwsgi.mules_cnt < 1) {
                rb_raise(rb_eRuntimeError, "no mule configured");
		return Qnil;
	}

        if (argc == 1) {
                mule_send_msg(uwsgi.shared->mule_queue_pipe[0], message, message_len);
        }
        else {
                if (TYPE(argv[1]) == T_STRING) {
                        struct uwsgi_farm *uf = get_farm_by_name(RSTRING_PTR(argv[1]));
                        if (uf == NULL) {
                                rb_raise(rb_eRuntimeError, "unknown farm");
				return Qnil;
                        }
                        fd = uf->queue_pipe[0];
                }
                else if (TYPE(argv[1]) == T_FIXNUM) {
                        mule_id = NUM2INT(argv[1]);
                        if (mule_id < 0 && mule_id > uwsgi.mules_cnt) {
                                rb_raise(rb_eRuntimeError, "invalid mule number");
				return Qnil;
                        }
                        if (mule_id == 0) {
                                fd = uwsgi.shared->mule_queue_pipe[0];
                        }
                        else {
                                fd = uwsgi.mules[mule_id-1].queue_pipe[0];
                        }
                }
                else {
                        rb_raise(rb_eRuntimeError, "invalid mule");
			return Qnil;
                }

                if (fd > -1) {
                        mule_send_msg(fd, message, message_len);
                }
        }

        return Qnil;

}



int uwsgi_ruby_hash_mule_callback(VALUE key, VALUE val, VALUE arg_array) {
	Check_Type(key, T_SYMBOL);
	ID key_id = SYM2ID(key);
	const char *key_name = rb_id2name(key_id);

	if (!strcmp(key_name, "signals")) {
		rb_ary_store(arg_array, 0, val);
	}
	else if (!strcmp(key_name, "farms")) {
		rb_ary_store(arg_array, 1, val);
	}
	else if (!strcmp(key_name, "timeout")) {
		rb_ary_store(arg_array, 2, val);
	}
	else if (!strcmp(key_name, "buffer_size")) {
		rb_ary_store(arg_array, 3, val);
	}

	return 0;
}

VALUE rack_uwsgi_mule_get_msg(int argc, VALUE *argv, VALUE *class) {

	int manage_signals = 1;
	int manage_farms = 1;
	int timeout = -1;
	size_t buffer_size = 65536;
	ssize_t len = 0;
	char *message;

        if (uwsgi.muleid == 0) {
                rb_raise(rb_eRuntimeError, "you can receive mule messages only in a mule !!!");
		return Qnil;
        }

	if (argc > 0) {
		// 0 = manage_signals
		// 1 = manage_farms
		// 2 = timeout
		// 3 = buffer_size
		VALUE arg_array = rb_ary_new2(4);
		Check_Type(argv[0], T_HASH);
		rb_hash_foreach(argv[0], uwsgi_ruby_hash_mule_callback, arg_array);
		
		if (rb_ary_entry(arg_array, 0) == Qfalse) {
			manage_signals = 0;
		}

		if (rb_ary_entry(arg_array, 1) == Qfalse) {
			manage_farms = 0;
		}

		if (TYPE(rb_ary_entry(arg_array,2)) == T_FIXNUM) {
			timeout = NUM2INT(rb_ary_entry(arg_array,2));
		}

		if (TYPE(rb_ary_entry(arg_array,3)) == T_FIXNUM || TYPE(rb_ary_entry(arg_array,3)) == T_BIGNUM) {
			buffer_size = NUM2ULONG(rb_ary_entry(arg_array,3));
		}
		
	}

        message = uwsgi_malloc(buffer_size);

        len = uwsgi_mule_get_msg(manage_signals, manage_farms, message, buffer_size, timeout) ;

        if (len < 0) {
                free(message);
                return Qnil;
        }

        VALUE msg = rb_str_new(message, len);
        free(message);
        return msg;
}


VALUE rack_uwsgi_lock(int argc, VALUE *argv, VALUE *class) {

        int lock_num = 0;

	if (argc > 0) {
		Check_Type(argv[0], T_FIXNUM);
		lock_num = NUM2INT(argv[0]);
	}

        if (lock_num < 0 || lock_num > uwsgi.locks) {
                rb_raise(rb_eRuntimeError, "Invalid lock number");
		return Qnil;
        }

        uwsgi_lock(uwsgi.user_lock[lock_num]);
	return Qnil;
}

VALUE rack_uwsgi_unlock(int argc, VALUE *argv, VALUE *class) {

        int lock_num = 0;

	if (argc > 0) {
                Check_Type(argv[0], T_FIXNUM);
                lock_num = NUM2INT(argv[0]);
        }

        if (lock_num < 0 || lock_num > uwsgi.locks) {
                rb_raise(rb_eRuntimeError, "Invalid lock number");
                return Qnil;
        }


        uwsgi_unlock(uwsgi.user_lock[lock_num]);
	return Qnil;
}




VALUE rack_uwsgi_cache_set(VALUE *class, VALUE rbkey, VALUE rbvalue) {


	Check_Type(rbkey, T_STRING);
	Check_Type(rbvalue, T_STRING);

        char *key = RSTRING_PTR(rbkey);
	uint64_t keylen = RSTRING_LEN(rbkey);
        char *value = RSTRING_PTR(rbvalue);
	uint64_t vallen = RSTRING_LEN(rbvalue);

        uint64_t expires = 0;

        if (vallen > uwsgi.cache_blocksize) {
                rb_raise(rb_eRuntimeError, "uWSGI cache items size must be < %llu, requested %llu bytes", (unsigned long long)uwsgi.cache_blocksize, (unsigned long long) vallen);
		return Qnil;
        }

        uwsgi_wlock(uwsgi.cache_lock);
        if (uwsgi_cache_set(key, keylen, value, vallen, expires, 0)) {
        	uwsgi_rwunlock(uwsgi.cache_lock);
		return Qnil;
        }

        uwsgi_rwunlock(uwsgi.cache_lock);
        return Qtrue;

}
VALUE rack_uwsgi_cache_update(VALUE *class, VALUE rbkey, VALUE rbvalue) {


	Check_Type(rbkey, T_STRING);
	Check_Type(rbvalue, T_STRING);

        char *key = RSTRING_PTR(rbkey);
	uint64_t keylen = RSTRING_LEN(rbkey);
        char *value = RSTRING_PTR(rbvalue);
	uint64_t vallen = RSTRING_LEN(rbvalue);

        uint64_t expires = 0;

        if (vallen > uwsgi.cache_blocksize) {
                rb_raise(rb_eRuntimeError, "uWSGI cache items size must be < %llu, requested %llu bytes", (unsigned long long)uwsgi.cache_blocksize, (unsigned long long) vallen);
		return Qnil;
        }

        uwsgi_wlock(uwsgi.cache_lock);
        if (uwsgi_cache_set(key, keylen, value, vallen, expires, UWSGI_CACHE_FLAG_UPDATE)) {
        	uwsgi_rwunlock(uwsgi.cache_lock);
		return Qnil;
        }

        uwsgi_rwunlock(uwsgi.cache_lock);
        return Qtrue;

}

VALUE rack_uwsgi_cache_set_exc(VALUE *class, VALUE rbkey, VALUE rbvalue) {
	VALUE ret;
	ret = rack_uwsgi_cache_set(class, rbkey, rbvalue);
	if (ret == Qnil) {
		rb_raise(rb_eRuntimeError, "unable to set value in uWSGI cache");
	}	
	return ret;
}

VALUE rack_uwsgi_cache_update_exc(VALUE *class, VALUE rbkey, VALUE rbvalue) {
	VALUE ret;
	ret = rack_uwsgi_cache_update(class, rbkey, rbvalue);
	if (ret == Qnil) {
		rb_raise(rb_eRuntimeError, "unable to update value in uWSGI cache");
	}	
	return ret;
}





VALUE rack_uwsgi_cache_del(VALUE *class, VALUE rbkey) {

	Check_Type(rbkey, T_STRING);

        char *key = RSTRING_PTR(rbkey);
	size_t keylen = RSTRING_LEN(rbkey);
	
        uwsgi_wlock(uwsgi.cache_lock);
        if (uwsgi_cache_del(key, keylen, 0)) {
        	uwsgi_rwunlock(uwsgi.cache_lock);
		return Qfalse;
        }

        uwsgi_rwunlock(uwsgi.cache_lock);
        return Qtrue;

}


VALUE rack_uwsgi_cache_exists(VALUE *class, VALUE rbkey) {

	Check_Type(rbkey, T_STRING);

        char *key = RSTRING_PTR(rbkey);
	size_t keylen = RSTRING_LEN(rbkey);

        if (uwsgi_cache_exists(key, keylen)) {
                return Qtrue;
        }

        return Qfalse;

}


VALUE rack_uwsgi_cache_get(VALUE *class, VALUE rbkey) {

	Check_Type(rbkey, T_STRING);

        char *key = RSTRING_PTR(rbkey);
	size_t keylen = RSTRING_LEN(rbkey);

        uint64_t valsize;
        char *value = NULL;

        uwsgi_rlock(uwsgi.cache_lock);
        value = uwsgi_cache_get(key, keylen, &valsize);
        if (!value) {
        	uwsgi_rwunlock(uwsgi.cache_lock);
        	return Qnil;
        }
        VALUE res = rb_str_new(value, valsize);
        uwsgi_rwunlock(uwsgi.cache_lock);
        return res;

}
VALUE rack_uwsgi_cache_get_exc(VALUE *class, VALUE rbkey) {
	VALUE ret;
	ret = rack_uwsgi_cache_get(class, rbkey);
	if (ret == Qnil) {
		rb_raise(rb_eRuntimeError, "unable to get value from uWSGI cache");
	}	
	return ret;
}




VALUE rack_uwsgi_add_cron(VALUE *class, VALUE rbsignum, VALUE rbmin, VALUE rbhour, VALUE rbday, VALUE rbmon, VALUE rbweek) {

	Check_Type(rbsignum, T_FIXNUM);
	Check_Type(rbmin, T_FIXNUM);
	Check_Type(rbhour, T_FIXNUM);
	Check_Type(rbday, T_FIXNUM);
	Check_Type(rbmon, T_FIXNUM);
	Check_Type(rbweek, T_FIXNUM);

        uint8_t uwsgi_signal = NUM2INT(rbsignum);
        int minute = NUM2INT(rbmin);
        int hour = NUM2INT(rbhour);
        int day = NUM2INT(rbday);
        int month = NUM2INT(rbmon);
        int week = NUM2INT(rbweek);

        if (uwsgi_signal_add_cron(uwsgi_signal, minute, hour, day, month, week)) {
                rb_raise(rb_eRuntimeError, "unable to add cron");
                return Qnil;
        }

        return Qtrue;
}



VALUE rack_uwsgi_add_timer(VALUE *class, VALUE rbsignum, VALUE secs) {

	Check_Type(rbsignum, T_FIXNUM);
	Check_Type(secs, T_FIXNUM);

        uint8_t uwsgi_signal = NUM2INT(rbsignum);
        int seconds = NUM2INT(secs);

        if (uwsgi_add_timer(uwsgi_signal, seconds)) {
                rb_raise(rb_eRuntimeError, "unable to add timer");
                return Qnil;
        }

        return Qtrue;
}



VALUE rack_uwsgi_add_rb_timer(VALUE *class, VALUE rbsignum, VALUE secs) {

	Check_Type(rbsignum, T_FIXNUM);
	Check_Type(secs, T_FIXNUM);

        uint8_t uwsgi_signal = NUM2INT(rbsignum);
        int seconds = NUM2INT(secs);


        if (uwsgi_signal_add_rb_timer(uwsgi_signal, seconds, 0)) {
                rb_raise(rb_eRuntimeError, "unable to add rb_timer");
                return Qnil;
        }

        return Qtrue;
}



VALUE rack_uwsgi_add_file_monitor(VALUE *class, VALUE rbsignum, VALUE rbfilename) {

	Check_Type(rbsignum, T_FIXNUM);
	Check_Type(rbfilename, T_STRING);

        uint8_t uwsgi_signal = NUM2INT(rbsignum);
        char *filename = RSTRING_PTR(rbfilename);

        if (uwsgi_add_file_monitor(uwsgi_signal, filename)) {
                rb_raise(rb_eRuntimeError, "unable to add file monitor");
                return Qnil;
        }

        return Qtrue;
}


#ifdef UWSGI_ASYNC
VALUE uwsgi_ruby_wait_fd_read(VALUE *class, VALUE arg1, VALUE arg2) {

	Check_Type(arg1, T_FIXNUM);
	Check_Type(arg2, T_FIXNUM);


        struct wsgi_request *wsgi_req = current_wsgi_req();

        int fd = NUM2INT(arg1);
        int timeout = NUM2INT(arg2);

        if (fd >= 0) {
                async_add_fd_read(wsgi_req, fd, timeout);
        }

        return Qtrue;
}

VALUE uwsgi_ruby_wait_fd_write(VALUE *class, VALUE arg1, VALUE arg2) {

	Check_Type(arg1, T_FIXNUM);
	Check_Type(arg2, T_FIXNUM);

        struct wsgi_request *wsgi_req = current_wsgi_req();

        int fd = NUM2INT(arg1);
        int timeout = NUM2INT(arg2);

        if (fd >= 0) {
                async_add_fd_write(wsgi_req, fd, timeout);
        }

        return Qtrue;
}
#endif



VALUE uwsgi_ruby_async_connect(VALUE *class, VALUE arg) {

	Check_Type(arg, T_STRING);

        int fd = uwsgi_connect(RSTRING_PTR(arg), 0, 1);

        return INT2FIX(fd);
}


#ifdef UWSGI_ASYNC
VALUE uwsgi_ruby_async_sleep(VALUE *class, VALUE arg) {

	Check_Type(arg, T_FIXNUM);

        struct wsgi_request *wsgi_req = current_wsgi_req();
        int timeout = NUM2INT(arg);

        if (timeout >= 0) {
                async_add_timeout(wsgi_req, timeout);
        }

        return Qtrue;
}
#endif

VALUE uwsgi_ruby_masterpid(VALUE *class) {

        if (uwsgi.master_process) {
                return INT2NUM(uwsgi.workers[0].pid);
        }
        return INT2NUM(0);
}

VALUE uwsgi_ruby_suspend(VALUE *class) {

        struct wsgi_request *wsgi_req = current_wsgi_req();

        uwsgi.schedule_to_main(wsgi_req);

        return Qtrue;

}


VALUE uwsgi_ruby_signal_wait(int argc, VALUE *argv, VALUE *class) {

        struct wsgi_request *wsgi_req = current_wsgi_req();
        int wait_for_specific_signal = 0;
        uint8_t uwsgi_signal = 0;
        int received_signal;

        wsgi_req->signal_received = -1;

        if (argc > 0) {
		Check_Type(argv[0], T_FIXNUM);
                uwsgi_signal = NUM2INT(argv[0]);
                wait_for_specific_signal = 1;
        }

        if (wait_for_specific_signal) {
                received_signal = uwsgi_signal_wait(uwsgi_signal);
        }
        else {
                received_signal = uwsgi_signal_wait(-1);
        }

	if (received_signal < 0) {
		rb_raise(rb_eRuntimeError, "unable to call rpc function");
	}
	else {
        	wsgi_req->signal_received = received_signal;
	}

        return Qnil;
}

VALUE uwsgi_ruby_signal_received(VALUE *class) {

        struct wsgi_request *wsgi_req = current_wsgi_req();

        return INT2NUM(wsgi_req->signal_received);
}


VALUE uwsgi_ruby_signal_registered(VALUE *class, VALUE signum) {

	Check_Type(signum, T_FIXNUM);

        uint8_t uwsgi_signal = NUM2INT(signum);

        if (uwsgi_signal_registered(uwsgi_signal)) {
                return Qtrue;
        }

        return Qfalse;
}

VALUE uwsgi_ruby_do_rpc(int argc, VALUE *rpc_argv, VALUE *class) {

	char *node = NULL, *func;
        uint16_t size = 0;

        char *argv[256];
        uint16_t argvs[256];

        int i;


        // TODO better error reporting
        if (argc < 2)
                goto clear;


        VALUE rpc_node = rpc_argv[0];

        if (TYPE(rpc_node) == T_STRING) {
                node = RSTRING_PTR(rpc_node);
        }


        VALUE rpc_func = rpc_argv[1];

        if (TYPE(rpc_func) != T_STRING)
                goto clear;

        func = RSTRING_PTR(rpc_func);

        for (i = 0; i < (argc - 2); i++) {
                VALUE rpc_str = rpc_argv[i + 2];
                if (TYPE(rpc_str) != T_STRING)
                        goto clear;
                argv[i] = RSTRING_PTR(rpc_str);
                argvs[i] = RSTRING_LEN(rpc_str);
        }

	// response must always be freed
        char *response = uwsgi_do_rpc(node, func, argc - 2, argv, argvs, &size);

        if (size > 0) {
                VALUE ret = rb_str_new(response, size);
                free(response);
                return ret;
        }
	free(response);

clear:

        rb_raise(rb_eRuntimeError, "unable to call rpc function");
        return Qnil;
}

VALUE uwsgi_ruby_register_rpc(int argc, VALUE *argv, VALUE *class) {

        int rb_argc = 0;

        if (argc < 2) goto clear;
        if (argc > 2) {
		Check_Type(argv[2], T_FIXNUM);
                rb_argc = NUM2INT(argv[2]);
        }

	Check_Type(argv[0], T_STRING);
        char *name = RSTRING_PTR(argv[0]);
        void *func = (void *) argv[1];


        if (uwsgi_register_rpc(name, rack_plugin.modifier1, rb_argc, func)) {
clear:
                rb_raise(rb_eRuntimeError, "unable to register rpc function");
                return Qnil;
        }
        rb_gc_register_address(&argv[1]);
        rb_ary_push(ur.rpc_protector, argv[1]);

        return Qtrue;
}

VALUE uwsgi_ruby_register_signal(VALUE *class, VALUE signum, VALUE sigkind, VALUE rbhandler) {

	Check_Type(signum, T_FIXNUM);
	Check_Type(sigkind, T_STRING);

        uint8_t uwsgi_signal = NUM2INT(signum);
        char *signal_kind = RSTRING_PTR(sigkind);

        if (uwsgi_register_signal(uwsgi_signal, signal_kind, (void *) rbhandler, rack_plugin.modifier1)) {
                rb_raise(rb_eRuntimeError, "unable to register signal %d", uwsgi_signal);
                return Qnil;
        }

        rb_gc_register_address(&rbhandler);
        rb_ary_push(ur.signals_protector, rbhandler);

        return Qtrue;
}


VALUE uwsgi_ruby_signal(int argc, VALUE *argv, VALUE *class) {

	if (argc < 1) {
		rb_raise(rb_eRuntimeError, "you have to specify a signum");
                return Qnil;
	}

	Check_Type(argv[0], T_FIXNUM);

        uint8_t uwsgi_signal = NUM2INT(argv[0]);

	if (argc > 1) {
		Check_Type(argv[1], T_STRING);
		char *remote = RSTRING_PTR(argv[1]);

		int ret = uwsgi_remote_signal_send(remote, uwsgi_signal);
                if (ret == 1) return Qtrue;
                if (ret == -1) {
                        rb_raise(rb_eRuntimeError, "unable to deliver signal %d to node %s", uwsgi_signal, remote);
			return Qnil;
		}
                if (ret == 0) {
                        rb_raise(rb_eRuntimeError, "node %s rejected signal %d", remote, uwsgi_signal);	
			return Qnil;
		}
	}
	else {
		uwsgi_signal_send(uwsgi.signal_socket, uwsgi_signal);
	}

        return Qtrue;
}

int rack_uwsgi_build_spool(VALUE rbkey, VALUE rbval, VALUE argv) {
	char **sa = (char **) argv;

	char *cur_buf = sa[0];
	char *watermark = sa[1];

	if (TYPE(rbkey) != T_STRING || TYPE(rbval) != T_STRING) {
		rb_raise(rb_eRuntimeError, "spool hash must contains only strings");
		return ST_STOP;
	}

	char *key = RSTRING_PTR(rbkey); uint16_t keylen = RSTRING_LEN(rbkey);
	char *val = RSTRING_PTR(rbval); uint16_t vallen = RSTRING_LEN(rbval);

	if (cur_buf + (2+keylen+2+vallen) > watermark) {
		rb_raise(rb_eRuntimeError, "spool hash size can be no more than 64K");
		return ST_STOP;
	}

	*cur_buf++ = (uint8_t) (keylen & 0xff);
        *cur_buf++ = (uint8_t) ((keylen >> 8) & 0xff);
	memcpy(cur_buf, key, keylen); cur_buf += keylen;

	*cur_buf++ = (uint8_t) (vallen & 0xff);
        *cur_buf++ = (uint8_t) ((vallen >> 8) & 0xff);
	memcpy(cur_buf, val, vallen); cur_buf += vallen;

	// fix the ptr
	sa[0] = cur_buf;

	return ST_CONTINUE;
}


VALUE rack_uwsgi_send_spool(VALUE *class, VALUE args) {

        char spool_filename[1024];
        struct wsgi_request *wsgi_req = current_wsgi_req();
        char *priority = NULL;
        long numprio = 0;
        time_t at = 0;
        char *body = NULL;
        size_t body_len= 0;

	Check_Type(args, T_HASH);

	// priority
#ifdef RUBY19
       VALUE rbprio = rb_hash_lookup(args, rb_str_new2("priority"));
#else
       VALUE rbprio = rb_hash_aref(args, rb_str_new2("priority"));
#endif
        if (TYPE(rbprio) == T_FIXNUM) {
        	numprio = NUM2INT(rbprio);
             	rb_hash_delete(args, rb_str_new2("priority")); 
        }

	// at
#ifdef RUBY19
       VALUE rbat = rb_hash_lookup(args, rb_str_new2("at"));
#else
       VALUE rbat = rb_hash_aref(args, rb_str_new2("at"));
#endif
        if (TYPE(rbat) == T_FIXNUM) {
        	at = NUM2INT(rbat);
             	rb_hash_delete(args, rb_str_new2("at")); 
        }

	// body
#ifdef RUBY19
       VALUE rbbody = rb_hash_lookup(args, rb_str_new2("body"));
#else
       VALUE rbbody = rb_hash_aref(args, rb_str_new2("body"));
#endif
        if (TYPE(rbbody) == T_STRING) {
        	body = RSTRING_PTR(rbbody);
		body_len = RSTRING_LEN(rbbody);
             	rb_hash_delete(args, rb_str_new2("body")); 
        }

	char *spool_buffer = uwsgi_malloc(UMAX16);
	char *argv[2];
	argv[0] = spool_buffer;
	argv[1] = spool_buffer + UMAX16 ;

	rb_hash_foreach(args, rack_uwsgi_build_spool, (VALUE) argv); 

        if (numprio) {
                priority = uwsgi_num2str(numprio);
        }

        int ret = spool_request(uwsgi.spoolers, spool_filename, uwsgi.workers[0].requests + 1, wsgi_req->async_id, spool_buffer, argv[0] - spool_buffer, priority, at, body, body_len);

        if (priority) {
                free(priority);
        }

        free(spool_buffer);

        if (ret > 0) {
                char *slash = uwsgi_get_last_char(spool_filename, '/');
                if (slash) {
                        return rb_str_new2(slash+1);
                }
		return rb_str_new2(spool_filename);
        }

        rb_raise(rb_eRuntimeError, "unable to spool job");
	return Qnil;

}



void uwsgi_rack_init_api() {

	VALUE rb_uwsgi_embedded = rb_define_module("UWSGI");
        uwsgi_rack_api("suspend", uwsgi_ruby_suspend, 0);
        uwsgi_rack_api("masterpid", uwsgi_ruby_masterpid, 0);
#ifdef UWSGI_ASYNC
        uwsgi_rack_api("async_sleep", uwsgi_ruby_async_sleep, 1);
        uwsgi_rack_api("wait_fd_read", uwsgi_ruby_wait_fd_read, 2);
        uwsgi_rack_api("wait_fd_write", uwsgi_ruby_wait_fd_write, 2);
#endif
        uwsgi_rack_api("async_connect", uwsgi_ruby_async_connect, 1);
        uwsgi_rack_api("signal", uwsgi_ruby_signal, -1);
        uwsgi_rack_api("register_signal", uwsgi_ruby_register_signal, 3);
        uwsgi_rack_api("register_rpc", uwsgi_ruby_register_rpc, -1);
        uwsgi_rack_api("signal_registered", uwsgi_ruby_signal_registered, 1);
        uwsgi_rack_api("signal_wait", uwsgi_ruby_signal_wait, -1);
        uwsgi_rack_api("signal_received", uwsgi_ruby_signal_received, 0);
        uwsgi_rack_api("add_cron", rack_uwsgi_add_cron, 6);
        uwsgi_rack_api("add_timer", rack_uwsgi_add_timer, 2);
        uwsgi_rack_api("add_rb_timer", rack_uwsgi_add_rb_timer, 2);
        uwsgi_rack_api("add_file_monitor", rack_uwsgi_add_file_monitor, 2);

        uwsgi_rack_api("setprocname", rack_uwsgi_setprocname, 1);
        uwsgi_rack_api("mem", rack_uwsgi_mem, 0);

        uwsgi_rack_api("lock", rack_uwsgi_lock, -1);
        uwsgi_rack_api("unlock", rack_uwsgi_unlock, -1);

        uwsgi_rack_api("mule_get_msg", rack_uwsgi_mule_get_msg, -1);
        uwsgi_rack_api("mule_msg", rack_uwsgi_mule_msg, -1);

        uwsgi_rack_api("request_id", rack_uwsgi_request_id, 0);
        uwsgi_rack_api("worker_id", rack_uwsgi_worker_id, 0);
        uwsgi_rack_api("mule_id", rack_uwsgi_mule_id, 0);

        uwsgi_rack_api("i_am_the_spooler", rack_uwsgi_i_am_the_spooler, 0);
        uwsgi_rack_api("send_to_spooler", rack_uwsgi_send_spool, 1);
        uwsgi_rack_api("spool", rack_uwsgi_send_spool, 1);

        uwsgi_rack_api("log", rack_uwsgi_log, 1);
        uwsgi_rack_api("logsize", rack_uwsgi_logsize, 0);

        uwsgi_rack_api("set_warning_message", rack_uwsgi_warning, 1);

        uwsgi_rack_api("rpc", uwsgi_ruby_do_rpc, -1);

	

	if (uwsgi.cache_max_items > 0) {
        	uwsgi_rack_api("cache_get", rack_uwsgi_cache_get, 1);
        	uwsgi_rack_api("cache_get!", rack_uwsgi_cache_get_exc, 1);
        	uwsgi_rack_api("cache_exists", rack_uwsgi_cache_exists, 1);
        	uwsgi_rack_api("cache_exists?", rack_uwsgi_cache_exists, 1);
        	uwsgi_rack_api("cache_del", rack_uwsgi_cache_del, 1);
        	uwsgi_rack_api("cache_set", rack_uwsgi_cache_set, 2);
        	uwsgi_rack_api("cache_set!", rack_uwsgi_cache_set_exc, 2);
        	uwsgi_rack_api("cache_update", rack_uwsgi_cache_update, 2);
        	uwsgi_rack_api("cache_update!", rack_uwsgi_cache_update_exc, 2);
	}

        VALUE uwsgi_rb_opt_hash = rb_hash_new();
        int i;
        for (i = 0; i < uwsgi.exported_opts_cnt; i++) {
                VALUE rb_uwsgi_opt_key = rb_str_new2(uwsgi.exported_opts[i]->key);
                if ( rb_funcall(uwsgi_rb_opt_hash, rb_intern("has_key?"), 1, rb_uwsgi_opt_key) == Qtrue) {
                        VALUE rb_uwsgi_opt_item = rb_hash_aref(uwsgi_rb_opt_hash, rb_uwsgi_opt_key);
                        if (TYPE(rb_uwsgi_opt_item) == T_ARRAY) {
                                if (uwsgi.exported_opts[i]->value == NULL) {
                                        rb_ary_push(rb_uwsgi_opt_item, Qtrue);
                                }
                                else {
                                        rb_ary_push(rb_uwsgi_opt_item, rb_str_new2(uwsgi.exported_opts[i]->value));
                                }
                        }
                        else {
                                VALUE rb_uwsgi_opt_list = rb_ary_new();
                                rb_ary_push(rb_uwsgi_opt_list, rb_uwsgi_opt_item);
                                if (uwsgi.exported_opts[i]->value == NULL) {
                                        rb_ary_push(rb_uwsgi_opt_list, Qtrue);
                                }
                                else {
                                        rb_ary_push(rb_uwsgi_opt_list, rb_str_new2(uwsgi.exported_opts[i]->value));
                                }

                                rb_hash_aset(uwsgi_rb_opt_hash, rb_uwsgi_opt_key, rb_uwsgi_opt_list);
                        }
                }
                else {
                        if (uwsgi.exported_opts[i]->value == NULL) {
                                rb_hash_aset(uwsgi_rb_opt_hash, rb_uwsgi_opt_key, Qtrue);
                        }
                        else {
                                rb_hash_aset(uwsgi_rb_opt_hash, rb_uwsgi_opt_key, rb_str_new2(uwsgi.exported_opts[i]->value));
                        }
                }
        }

        rb_const_set(rb_uwsgi_embedded, rb_intern("OPT"), uwsgi_rb_opt_hash);

        rb_const_set(rb_uwsgi_embedded, rb_intern("SPOOL_OK"), INT2NUM(-2));
        rb_const_set(rb_uwsgi_embedded, rb_intern("SPOOL_IGNORE"), INT2NUM(0));
        rb_const_set(rb_uwsgi_embedded, rb_intern("SPOOL_RETRY"), INT2NUM(-1));

        rb_const_set(rb_uwsgi_embedded, rb_intern("VERSION"), rb_str_new2(UWSGI_VERSION));
        rb_const_set(rb_uwsgi_embedded, rb_intern("HOSTNAME"), rb_str_new(uwsgi.hostname, uwsgi.hostname_len));
	if (uwsgi.pidfile) {
        	rb_const_set(rb_uwsgi_embedded, rb_intern("PIDFILE"), rb_str_new2(uwsgi.pidfile));
	}

        rb_const_set(rb_uwsgi_embedded, rb_intern("NUMPROC"), INT2NUM(uwsgi.numproc));
	

}
