-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've made some cleaning & fixes, updated patches attached.

kaalh

KaalH! a écrit :
> Nicolas Cannasse a écrit :
>>>> I'm not sure I'm following your here.
>>>>
>>>> Why exactly mod_tora for lighty or nginx cannot connect directly to
>>>> the tora host and use the protocol as mod_tora for Apache is doing ?
>>>>
>>> I maybe have a wrong vision of tora.
>>>
>>> You may have think mod_tora "only" as a http server plugin to solve
>>> some memory comsumption issues.
>>>
>>> But for me, programmer and sysadmin (can't choose only one, both are
>>> madly enjoying ),  tora must be a scalable way to deploy neko
>>> application in web farms, so the lighttpd/nginx/... implementation
>>> should  be able to connect to several tora instances, load balance and
>>> failover between them, like it's done with fastcgi on lighttpd and nginx.
>> I can understand that, but IMHO that's just a matter of configuration.
>> Once the tora server is selected (depending on balancing and failover),
>> you should be able to use tora protocol.
> 
>> Or am I missing something else ?
> 
> I don't think so :)
> 
>>> The proxy paradigm make difficult to implement the tora multipart
>>> content scheme, be able to pass raw data to tora without have to wait
>>> for the CGetMultipart code would be a lot easier to implement in
>>> lighttpd, and seems to be required for nginx (
>>> http://marc.info/?l=nginx&m=121425082522327&w=2 )
>> Yes, this is similar to what mod_tora is doing, for similar reasons I
>> guess, which are the necessity to stream multipart/POST data only when
>> requested to do so.
> 
>> I'm not sure however what exactly a nginx/lighty "proxy" (between?) is
>> doing here so you might have to explain it to me first :)
> 
> - lighttpd core read the incoming data to a buffers chain (chunkqueue).
> - this "in" chunkqueue is converted by the tora proxy backend to another
> chunkqueue.
> - data is sent from this chunkqueue to the tora server.
> - lighttpd read the response from tora and pass it to the tora backend
> thru another chunckqueue.
> - the backend convert the data from tora to http to another chunkqueue
> - the lighttpd core send the response to the client
> 
> I haven't played yet with nignx, but It seems to be the same, except
> that nginx does not wait for any state completion, chains of small
> buffers are piped along handler/filters/upstream(aka proxy) modules, so
> you could start to send the response to the client while still reading
> incoming data, looks amazing.
> 
>> It might be possible to modify the tora protocol to let the server parse
>> and send the multipart data without waiting for the CGetMultipart : this
>> should not actually interfere with the client since it's not expecting
>> anything else from the server.
> 
> Thanks, I was on the wrong way :)
> 
> My lighttpd implementation is now a lot more efficient as it do no
> longer need to do tricky things in the proxy layer, I'm still in
> testing, I plan to use it in production environment in approx one month.
> The only missing feature is data flush, but I don't know yet if it's
> possible with lighttpd.
> 
> You need to path the tora server with the attached "tora.diff".
> With CBoundary/CMultipartData/CMultipartDone, multipart data is saved by
> tora in a temporary directory ( TMPDIR environment variable, default set
> to "/tmp" ), and only decoded while calling parse_multipart_data.
> 
> lighttpd patching instructions are the same as previous, updated patch
> attached.
> 
> kaalh
> 
>> Nicolas
> 
> 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI/yAbWRw620CDc80RApt5AJ0R63KlM5u2FZfUkQf8nHuRU9EHcQCfYaTe
OUact/gp9VJ2kiZTbkbswsU=
=znNy
-----END PGP SIGNATURE-----
diff -ru lighttpd-1.5.0/configure.in lighttpd/configure.in
--- lighttpd-1.5.0/configure.in	2007-09-02 21:46:03.000000000 +0200
+++ lighttpd/configure.in	2008-09-29 20:35:05.000000000 +0200
@@ -632,6 +632,7 @@
 	mod_proxy_backend_fastcgi \
 	mod_proxy_backend_scgi \
 	mod_proxy_backend_ajp13 \
+	mod_proxy_backend_tora \
 	mod_rrdtool \
 	mod_secdownload \
 	mod_setenv \
diff -ru lighttpd-1.5.0/src/Makefile.am lighttpd/src/Makefile.am
--- lighttpd-1.5.0/src/Makefile.am	2007-05-08 10:31:21.000000000 +0200
+++ lighttpd/src/Makefile.am	2008-09-29 20:32:10.000000000 +0200
@@ -243,6 +243,10 @@
 mod_proxy_backend_ajp13_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
 mod_proxy_backend_ajp13_la_LIBADD = $(common_libadd) $(PCRE_LIB)
 
+lib_LTLIBRARIES += mod_proxy_backend_tora.la
+mod_proxy_backend_tora_la_SOURCES = mod_proxy_backend_tora.c
+mod_proxy_backend_tora_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
+mod_proxy_backend_tora_la_LIBADD = $(common_libadd) $(PCRE_LIB)
 
 lib_LTLIBRARIES += mod_ssi.la
 mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c diff -Pru lighttpd-1.5.0/src/mod_proxy_backend_tora.c lighttpd/src/mod_proxy_backend_tora.c
diff -Pru lighttpd-1.5.0/src/plugin.c lighttpd/src/plugin.c
--- lighttpd-1.5.0/src/plugin.c	2007-05-08 12:35:14.000000000 +0200
+++ lighttpd/src/plugin.c	2008-10-06 16:48:35.000000000 +0200
@@ -134,6 +134,7 @@
 PLUGIN_STATIC(mod_proxy_backend_fastcgi);
 PLUGIN_STATIC(mod_proxy_backend_http);
 PLUGIN_STATIC(mod_proxy_backend_scgi);
+PLUGIN_STATIC(mod_proxy_backend_tora);
 PLUGIN_STATIC(mod_proxy_core);
 PLUGIN_STATIC(mod_redirect);
 PLUGIN_STATIC(mod_rewrite);
diff -Pru lighttpd-1.5.0/src/mod_proxy_backend_tora.c lighttpd/src/mod_proxy_backend_tora.c
--- lighttpd-1.5.0/src/mod_proxy_backend_tora.c	1970-01-01 01:00:00.000000000 +0100
+++ lighttpd/src/mod_proxy_backend_tora.c	2008-10-22 14:29:55.000000000 +0200
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2008, KaalH! <[EMAIL PROTECTED]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mod_proxy_core.h"
+#include "mod_proxy_core_protocol.h"
+#include "configfile.h"
+#include "buffer.h"
+#include "log.h"
+#include "inet_ntop_cache.h"
+
+#define CORE_PLUGIN "mod_proxy_core"
+
+#define TORA_HEADER_LEN 4
+
+#define BUFSIZE (1 << 16)
+
+#define PARSE_HEADER(start,cursor) \
+        cursor = start; \
+        if( *cursor == '"' ) { \
+                start++; \
+                cursor++; \
+                while( *cursor != '"' && *cursor != 0 ) \
+                        cursor++; \
+        } else { \
+                while( *cursor != 0 && *cursor != '\r' && *cursor != '\n' && *cursor != '\t' ) \
+                        cursor++; \
+        }
+
+typedef struct {
+	PLUGIN_DATA;
+	proxy_protocol *protocol;
+} protocol_plugin_data;
+
+typedef struct {
+	size_t   len;
+	off_t    offset;
+	int      type;
+} tora_packet;
+
+/**
+ * The tora protocol decoder will use this struct for storing state variables
+ * used in decoding the stream
+ */
+typedef struct {
+	buffer        *buf;      /* holds raw header bytes or used to buffer STDERR */
+	off_t         offset;    /* parse offset into buffer. */
+	tora_packet  packet;    /* parsed info about current packet. */
+	size_t        chunk_len; /* chunk length */
+	size_t	requested_bytes; /* backend requested we send this many bytes of the request content. */
+	buffer        *hdkey;      /* temporary holds header key */
+} tora_state_data;
+
+typedef enum {
+        CODE_FILE = 1,
+        CODE_URI,
+        CODE_CLIENT_IP,
+        CODE_GET_PARAMS,
+        CODE_POST_DATA,
+        CODE_HEADER_KEY,
+        CODE_HEADER_VALUE,
+        CODE_HEADER_ADD_VALUE,
+        CODE_PARAM_KEY,
+        CODE_PARAM_VALUE,
+        CODE_HOST_NAME,
+        CODE_HTTP_METHOD,
+        CODE_EXECUTE,
+        CODE_ERROR,
+        CODE_PRINT,
+        CODE_LOG,
+        CODE_FLUSH,
+        CODE_REDIRECT,
+        CODE_RETURNCODE,
+        CODE_QUERY_MULTIPART,
+        CODE_PART_FILENAME,
+        CODE_PART_KEY,
+        CODE_PART_DATA,
+        CODE_PART_DONE,
+        CODE_TEST_CONNECT,
+        CODE_BOUNDARY,
+        CODE_MULTIPART_DATA
+} proto_code;
+
+typedef struct {
+	unsigned char type;
+	unsigned char lengthB0;
+	unsigned char lengthB1;
+	unsigned char lengthB2;
+} TORA_Header;
+
+
+void tora_encode_header(buffer *b, proto_code code,int len ) {
+	unsigned char h[4];
+        h[0] = (unsigned char)code;
+        h[1] = (unsigned char)len;
+        h[2] = (unsigned char)(len >> 8);
+        h[3] = (unsigned char)(len >> 16);
+	buffer_append_string_len(b,(char*)h,4);
+}
+void tora_encode_size(buffer *b, proto_code code, const char *str, int len ) {
+	tora_encode_header(b,code,len);
+	buffer_append_string_len(b,str,len);
+}
+void tora_encode(buffer *b, proto_code code, const char *str ) {
+	tora_encode_size(b,code,str,(int)strlen(str));
+}
+
+static char *memfind( char *mem, int mlen, const char *v ) {
+        char *found;
+        int len = (int)strlen(v);
+        if( len == 0 )
+                return mem;
+        while( (found = memchr(mem,*v,mlen)) != NULL ) {
+                if( (int)(found - mem) + len > mlen )
+                        break;
+                if( memcmp(found,v,len) == 0 )
+                        return found;
+                mlen -= (int)(found - mem + 1);
+                mem = found + 1;
+        }
+        return NULL;
+}
+
+int url_decode( const char *bin, int len, char *bout ) {
+        int pin = 0;
+        int pout = 0;
+        while( len-- > 0 ) {
+                char c = bin[pin++];
+                if( c == '+' )
+                        c = ' ';
+                else if( c == '%' ) {
+                        int p1, p2;
+                        if( len < 2 )
+                                break;
+                        p1 = bin[pin++];
+                        p2 = bin[pin++];
+                        len -= 2;
+                        if( p1 >= '0' && p1 <= '9' )
+                                p1 -= '0';
+                        else if( p1 >= 'a' && p1 <= 'f' )
+                                p1 -= 'a' - 10;
+                        else if( p1 >= 'A' && p1 <= 'F' )
+                                p1 -= 'A' - 10;
+                        else
+                                continue;
+                        if( p2 >= '0' && p2 <= '9' )
+                                p2 -= '0';
+                        else if( p2 >= 'a' && p2 <= 'f' )
+                                p2 -= 'a' - 10;
+                        else if( p2 >= 'A' && p2 <= 'F' )
+                                p2 -= 'A' - 10;
+                        else
+                                continue;
+                        c = (char)((unsigned char)((p1 << 4) + p2));
+                }
+                bout[pout++] = c;
+        }
+        bout[pout] = 0;
+        return pout;
+}
+
+#define DEFAULT_SIZE    256
+
+static void tora_param_encode( buffer *b, proto_code code, const char *str, int len ) {
+        char tmp[DEFAULT_SIZE];
+        char *buf = NULL;
+        int size;
+        if( len >= DEFAULT_SIZE )
+                buf = malloc(len+1);
+        size = url_decode(str,len,buf?buf:tmp);
+        tora_encode_size(b,code,buf?buf:tmp,size);
+        if( buf )
+                free(buf);
+}
+
+void tora_parse_params(buffer *b, buffer *r) {
+	char *aand, *aeq, *asep;
+	char *args = BUF_STR(b);
+
+        while( TRUE ) {
+                aand = strchr(args,'&');
+                if( aand == NULL ) {
+                        asep = strchr(args,';');
+                        aand = asep;
+                } else {
+                        asep = strchr(args,';');
+                        if( asep != NULL && asep < aand )
+                                aand = asep;
+                }
+                if( aand != NULL )
+                        *aand = 0;
+                aeq = strchr(args,'=');
+                if( aeq != NULL ) {
+                        *aeq = 0;
+                        tora_param_encode(r,CODE_PARAM_KEY,args,(int)(aeq-args));
+                        tora_param_encode(r,CODE_PARAM_VALUE,aeq+1,(int)strlen(aeq+1));
+                        *aeq = '=';
+                }
+                if( aand == NULL )
+                        break;
+                *aand = (aand == asep)?';':'&';
+                args = aand+1;
+        }
+}
+
+tora_state_data *tora_state_data_init(void) {
+	tora_state_data *data;
+
+	data = calloc(1, sizeof(*data));
+	data->buf = buffer_init();
+	data->hdkey = buffer_init();
+	return data;
+}
+
+PROXY_CONNECTION_FUNC(proxy_tora_init) {
+
+	UNUSED(srv);
+
+	if(!proxy_con->protocol_data) {
+		proxy_con->protocol_data = tora_state_data_init();
+	}
+	return 1;
+}
+
+void tora_state_data_free(tora_state_data *data) {
+	buffer_free(data->buf);
+	buffer_free(data->hdkey);
+	free(data);
+}
+
+void tora_state_data_reset(tora_state_data *data) {
+	buffer_reset(data->buf);
+	data->packet.len = 0;
+	data->packet.offset = 0;
+	data->packet.type = 0;
+	data->offset = 0;
+	data->chunk_len = 0;
+}
+
+PROXY_CONNECTION_FUNC(proxy_tora_cleanup) {
+
+	UNUSED(srv);
+
+	if(proxy_con->protocol_data) {
+		tora_state_data_free((tora_state_data *)proxy_con->protocol_data);
+		proxy_con->protocol_data = NULL;
+	}
+	return 1;
+}
+
+/*
+ * copy len bytes from chunk-chain into buffer
+ */
+static int proxy_tora_fill_buffer(tora_state_data *data, chunkqueue *in, size_t len) {
+	off_t we_have = 0, we_need = len;
+	chunk *c;
+
+	for (c = in->first; c && we_need > 0; c = c->next) {
+		if(c->mem->used == 0) continue;
+		we_have = c->mem->used - c->offset - 1;
+		if (we_have == 0) continue;
+		if (we_have > we_need) we_have = we_need;
+		buffer_append_string_len(data->buf, c->mem->ptr + c->offset, we_have);
+		data->packet.offset += we_have;
+		c->offset += we_have;
+		in->bytes_out += we_have;
+		we_need -= we_have;
+	}
+	return we_need;
+}
+
+PROXY_STREAM_DECODER_FUNC(proxy_tora_stream_decoder_internal) {
+	proxy_connection *proxy_con = sess->proxy_con;
+	chunkqueue *in = proxy_con->recv;
+	tora_state_data *data = (tora_state_data *)proxy_con->protocol_data;
+	size_t we_have = 0, we_need = 0;
+	TORA_Header *header;
+	handler_t rc = HANDLER_GO_ON;
+	char buf[32];
+
+	UNUSED(srv);
+
+	if ((in->bytes_in == in->bytes_out) && in->is_closed) {
+		out->is_closed = 1;
+
+
+		TRACE("%ld / %ld -> %d",
+				in->bytes_in, in->bytes_out,
+				in->is_closed);
+
+		ERROR("looks like the tora backend (%s) terminated before it sent a FIN packet", BUF_STR(sess->request_uri));
+
+		return HANDLER_FINISHED;
+	}
+
+	/* no data ? */
+	if (!in->first) return HANDLER_GO_ON;
+
+	if(data->packet.type == 0) {
+		we_need = (TORA_HEADER_LEN - data->packet.offset);
+		we_need = proxy_tora_fill_buffer(data, in, we_need);
+		if(we_need > 0) {
+			/* we need more data to parse the header. */
+			chunkqueue_remove_finished_chunks(in);
+			return HANDLER_GO_ON;
+		}
+		header = (TORA_Header *)(data->buf->ptr);
+		data->packet.type = header->type;
+		data->packet.len = ((header->lengthB0 | header->lengthB1 << 8) | header->lengthB2 << 16);
+		data->packet.offset = 0;
+
+		/* Finished parsing raw header bytes. */
+		buffer_reset(data->buf);
+	}
+
+	/* proccess the packet's contents. */
+	we_need = data->packet.len - data->packet.offset;
+
+	/* for most packet types copy the content into the data buffer */
+	if (data->packet.type != CODE_PRINT) {
+		if(we_need > 0) {
+			/* copy tora packet contents to buffer */
+			we_need = proxy_tora_fill_buffer(data, in, we_need);
+			/* make sure we have the full tora packet content. */
+			if(we_need > 0) {
+				chunkqueue_remove_finished_chunks(in);
+				return HANDLER_GO_ON;
+			}
+		}
+	}
+
+	switch(data->packet.type) {
+		case CODE_EXECUTE:
+			LI_ltostr(buf, out->bytes_in);
+			array_append_key_value(sess->resp->headers, CONST_STR_LEN("Content-Length"), buf, strlen(buf));
+			if(sess->resp->status == -1) {
+				sess->resp->status = 200;
+			}
+			if(array_get_element(sess->resp->headers, CONST_STR_LEN("Content-Type")) == NULL) {
+				array_append_key_value(sess->resp->headers, CONST_STR_LEN("Content-Type"),CONST_STR_LEN("text/html"));
+			}
+			sess->have_response_headers = 1;
+			sess->is_request_finished = 1;
+			rc = HANDLER_FINISHED;
+			break;
+		case CODE_HEADER_KEY:
+			buffer_copy_string_buffer(data->hdkey,data->buf);
+			break;
+		case CODE_HEADER_VALUE:
+			array_append_key_value(sess->resp->headers, CONST_BUF_LEN(data->hdkey), CONST_BUF_LEN(data->buf));
+			break;
+		case CODE_HEADER_ADD_VALUE:
+			array_append_key_value(sess->resp->headers, CONST_BUF_LEN(data->hdkey), CONST_BUF_LEN(data->buf));
+			break;
+		case CODE_PRINT:
+			while(we_need > 0 && in->first != NULL) {
+				we_have = chunkqueue_steal_chunks_len(out, in->first, we_need);
+				we_need -= we_have;
+				in->bytes_out += we_have;
+				out->bytes_in += we_have;
+				chunkqueue_remove_finished_chunks(in);
+			}
+			break;
+		case CODE_LOG:
+			TRACE("[mod_tora] %s", BUF_STR(data->buf));
+			break;
+		case CODE_ERROR:
+			TRACE("[mod_tora] Error: %s", BUF_STR(data->buf));
+			rc = HANDLER_ERROR;
+			break;
+		case CODE_FLUSH:
+			/* TODO: how does lighty flush output ??? */
+			TRACE("[mod_tora] Notice: %s","flush not implemented");
+			break;
+		case CODE_REDIRECT:
+			array_append_key_value(sess->resp->headers, CONST_STR_LEN("Location"), CONST_BUF_LEN(data->buf));
+			sess->resp->status = 301;
+			sess->have_response_headers = 1;
+			break;
+		case CODE_RETURNCODE:
+			sess->resp->status = atoi(BUF_STR(data->buf));
+			break;
+		case CODE_QUERY_MULTIPART:
+			TRACE("[mod_tora] Warning: %s","You must used a patched tora server.");
+			break;
+		default:
+			TRACE("[mod_tora] %s","Unexpected code");
+			rc = HANDLER_ERROR;
+			break;
+	}
+
+	if(we_need == 0) {
+		/* packet finished, reset state for next packet */
+		tora_state_data_reset(data);
+	}
+
+	chunkqueue_remove_finished_chunks(in);
+
+	return rc;
+}
+
+PROXY_STREAM_DECODER_FUNC(proxy_tora_stream_decoder) {
+	proxy_connection *proxy_con = sess->proxy_con;
+	chunkqueue *in = proxy_con->recv;
+	int res;
+
+	if(out->is_closed) return 1;
+	/* decode the whole packet stream */
+	do {
+		/* decode the packet */
+		res = proxy_tora_stream_decoder_internal(srv, sess, out);
+	} while (in->first && res == HANDLER_GO_ON);
+	return res;
+}
+
+
+/**
+ * transform the content-stream into a valid TORA content-stream
+ *
+ * as we don't apply chunked-encoding here, pass it on AS IS
+ */
+PROXY_STREAM_ENCODER_FUNC(proxy_tora_stream_encoder) {
+	proxy_connection *proxy_con = sess->proxy_con;
+	connection *con = sess->remote_con;
+	chunkqueue *out = proxy_con->send;
+	handler_t rc = HANDLER_GO_ON;
+	size_t we_have = 0;
+	buffer *b, *bhm = buffer_init();
+	chunk *c;
+
+	UNUSED(srv);
+
+	/* output queue closed, can't encode any more data. */
+	if(out->is_closed) return HANDLER_FINISHED;
+
+	b = chunkqueue_get_append_buffer(out);
+
+	buffer_copy_string(bhm,get_http_method_name(con->request.http_method));
+
+    if (buffer_is_empty(con->uri.query)) {
+    	char *qstr = malloc(con->request.orig_uri->used + 1);
+    	if (NULL != (qstr = strchr(con->request.orig_uri->ptr, '?'))) {
+			/** extract query string from request.uri */
+    		buffer_copy_string(con->uri.query, qstr + 1);
+    	}
+    	free(qstr);
+	}
+	if (strcmp(bhm->ptr,"POST") == 0) {
+
+		unsigned int is_multipart = 0;
+		data_string *ds;
+
+        ds =  (data_string *) array_get_element(con->request.headers, CONST_STR_LEN("Content-Type"));
+		if(strstr(BUF_STR(ds->value),"multipart/form-data"))
+			is_multipart = 1;
+
+		if(is_multipart == 0) {
+			if(chunkqueue_length(in)> BUFSIZE) {
+				ERROR("[mod_tora] %s","Maximum POST data exceeded. Try using multipart encoding");
+			} else {
+
+				chunkqueue *tmp = chunkqueue_init();
+				buffer *r = buffer_init();
+
+				we_have = chunkqueue_steal_all_chunks(tmp, in);
+				in->bytes_out += we_have;
+				tmp->bytes_in += we_have;
+
+				for (c = tmp->first; c; c = c->next) {
+					buffer_append_string(r,c->mem->ptr + c->offset);
+				}
+
+				chunkqueue_free(tmp);
+
+				tora_encode(b,CODE_HTTP_METHOD, bhm->ptr);
+				tora_encode(b,CODE_POST_DATA, BUF_STR(r));
+				tora_parse_params(r,b);
+
+				buffer_free(r);
+				out->bytes_in += b->used-1;
+				rc = HANDLER_FINISHED;
+			}
+			tora_encode_size(b,CODE_EXECUTE, NULL,0);
+			out->bytes_in += b->used -1;
+
+		} else {
+
+			int len = 0;
+			char *boundstr = NULL;
+			unsigned int boundstr_len;
+			const char *boundary, *bend;
+
+			if( (boundary = strstr(BUF_STR(ds->value),"boundary=")) == NULL ) {
+				return HANDLER_ERROR;
+			}
+			boundary += 9;
+			PARSE_HEADER(boundary,bend);
+			len = (int)(bend - boundary);
+			boundstr_len = len + 2;
+
+			boundstr = (char*)malloc(boundstr_len + 1);
+			boundstr[0] = '-';
+			boundstr[1] = '-';
+			boundstr[boundstr_len] = 0;
+			memcpy(boundstr+2,boundary,len);
+			tora_encode(b,CODE_BOUNDARY, boundstr);
+			free(boundstr);
+			out->bytes_in += b->used -1;
+
+			do {
+				b = chunkqueue_get_append_buffer(out);
+				we_have = chunkqueue_steal_chunks_len(out, in->first,BUFSIZE);
+				if(we_have == 0) break;
+				tora_encode_header(b,CODE_MULTIPART_DATA,we_have);
+				out->bytes_in += b->used -1 + we_have;
+				in->bytes_out += we_have;
+			} while(in->first);
+			b = chunkqueue_get_append_buffer(out);
+			tora_encode_size(b,CODE_EXECUTE, NULL,0);
+			out->bytes_in += b->used -1;
+			rc = HANDLER_FINISHED;
+		}
+	} else {
+	    if (!buffer_is_empty(con->uri.query)) {
+			tora_encode(b,CODE_GET_PARAMS, BUF_STR(con->uri.query));
+			tora_parse_params(con->uri.query,b);
+		}
+		tora_encode(b,CODE_HTTP_METHOD, bhm->ptr);
+		tora_encode_size(b,CODE_EXECUTE, NULL,0);
+		out->bytes_in += b->used -1;
+		rc = HANDLER_FINISHED;
+	}
+
+	buffer_free(bhm);
+	//out->is_closed = 1;
+	return rc;
+}
+
+/**
+ * generate a HTTP/1.1 proxy request from the set of request-headers
+ *
+ */
+PROXY_STREAM_ENCODER_FUNC(proxy_tora_encode_request_headers) {
+	proxy_connection *proxy_con = sess->proxy_con;
+	chunkqueue *out = proxy_con->send;
+	connection *con = sess->remote_con;
+	buffer *b;
+	unsigned int i;
+
+	//UNUSED(srv);
+	UNUSED(in);
+
+	b = chunkqueue_get_append_buffer(out);
+
+	tora_encode(b,CODE_FILE,BUF_STR(con->physical.path));
+
+	if (buffer_is_empty(con->request.pathinfo)) {
+		const char *qstr = memfind(con->request.orig_uri->ptr,con->request.orig_uri->used,"?");
+		if(qstr == NULL) {
+			tora_encode(b,CODE_URI,BUF_STR(con->request.orig_uri));
+		} else {
+			tora_encode_size(b,CODE_URI,BUF_STR(con->request.orig_uri),qstr - con->request.orig_uri->ptr);
+		}
+	} else {
+		tora_encode(b,CODE_URI,BUF_STR(con->request.pathinfo));
+	}
+
+	tora_encode(b,CODE_HOST_NAME,BUF_STR(con->server_name));
+	tora_encode(b,CODE_CLIENT_IP, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
+
+	for (i = 0; i < con->request.headers->used; i++) {
+        data_string *ds;
+        ds = (data_string *)con->request.headers->data[i];
+
+		if (buffer_is_empty(ds->value) || buffer_is_empty(ds->key)) continue;
+		tora_encode(b,CODE_HEADER_KEY,BUF_STR(ds->key));
+		tora_encode(b,CODE_HEADER_VALUE,BUF_STR(ds->value));
+	}
+
+	out->bytes_in += b->used - 1;
+
+	return HANDLER_FINISHED;
+}
+
+INIT_FUNC(mod_proxy_backend_tora_init) {
+	mod_proxy_core_plugin_data *core_data;
+	protocol_plugin_data *p;
+
+	/* get the plugin_data of the core-plugin */
+	core_data = plugin_get_config(srv, CORE_PLUGIN);
+	if(!core_data) return NULL;
+
+	p = calloc(1, sizeof(*p));
+
+	/* define protocol handler callbacks */
+	p->protocol = (core_data->proxy_register_protocol)("tora");
+
+	p->protocol->proxy_stream_init = proxy_tora_init;
+	p->protocol->proxy_stream_cleanup = proxy_tora_cleanup;
+	p->protocol->proxy_stream_decoder = proxy_tora_stream_decoder;
+	p->protocol->proxy_stream_encoder = proxy_tora_stream_encoder;
+	p->protocol->proxy_encode_request_headers = proxy_tora_encode_request_headers;
+
+	return p;
+}
+
+FREE_FUNC(mod_proxy_backend_tora_free) {
+	protocol_plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+LI_EXPORT int mod_proxy_backend_tora_plugin_init(plugin *p) {
+	data_string *ds;
+
+	p->version      = LIGHTTPD_VERSION_ID;
+	p->name         = buffer_init_string("mod_proxy_backend_tora");
+
+	p->init         = mod_proxy_backend_tora_init;
+	p->cleanup      = mod_proxy_backend_tora_free;
+
+	p->data         = NULL;
+
+	ds = data_string_init();
+	buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN));
+	array_insert_unique(p->required_plugins, (data_unset *)ds);
+
+	return 0;
+}
+
+
Index: Client.hx
===================================================================
RCS file: /cvsroot/neko/libs/mod_tora/server/Client.hx,v
retrieving revision 1.7
diff -u -r1.7 Client.hx
--- Client.hx	7 Oct 2008 10:18:19 -0000	1.7
+++ Client.hx	22 Oct 2008 12:41:47 -0000
@@ -41,6 +41,8 @@
 	CPartData;
 	CPartDone;
 	CTestConnect;
+	CBoundary;
+	CMultipartData;
 }
 
 class Client {
@@ -66,6 +68,8 @@
 	public var httpMethod : String;
 	public var headersSent : Bool;
 	public var outputHeaders : List<{ code : Code, str : String }>;
+	public var boundary : String;
+	public var filenames : List<String>;
 
 	var key : String;
 
@@ -76,6 +80,7 @@
 		headers = new Array();
 		outputHeaders = new List();
 		params = new Array();
+		filenames = new List();
 	}
 
 	public function sendHeaders() {
@@ -129,6 +134,8 @@
 		case CHttpMethod: httpMethod = data;
 		case CExecute: execute = true; return true;
 		case CTestConnect: execute = false; return true;
+		case CBoundary: boundary = data;
+		case CMultipartData: storeTempData(data);
 		case CError: throw data;
 		default: throw "Unexpected "+Std.string(code);
 		}
@@ -144,4 +151,17 @@
 		return h + u;
 	}
 
+	function storeTempData(data:String)
+	{
+		var filename = Tora.tmpdir + "/tora-"+haxe.Md5.encode(ip+Std.random(10000000));
+		try {
+			var fo = neko.io.File.write(filename,true);
+			fo.writeString(data);
+			fo.close();
+			filenames.add(filename);
+		} catch(e:Dynamic) {
+			try neko.FileSystem.deleteFile(filename) catch(e:Dynamic) {};
+			neko.Lib.rethrow(e);
+		}
+	}
 }
Index: ModNekoApi.hx
===================================================================
RCS file: /cvsroot/neko/libs/mod_tora/server/ModNekoApi.hx,v
retrieving revision 1.9
diff -u -r1.9 ModNekoApi.hx
--- ModNekoApi.hx	10 Oct 2008 17:01:00 -0000	1.9
+++ ModNekoApi.hx	22 Oct 2008 12:41:47 -0000
@@ -15,6 +15,7 @@
 /*																			*/
 /* ************************************************************************ */
 import neko.NativeString;
+import neko.io.File;
 import Client.Code;
 
 class ModNekoApi {
@@ -132,6 +133,10 @@
 	}
 
 	function parse_multipart_data( onPart : NativeString -> NativeString -> Void, onData : NativeString -> Int -> Int -> Void ) {
+		if(client.boundary != null) {
+			parse_raw_multipart_data(onPart,onData);
+			return;
+		}
 		var bufsize = 1 << 16;
 		client.sendMessage(CQueryMultipart,Std.string(bufsize));
 		var filename = null;
@@ -170,6 +175,69 @@
 			neko.Lib.rethrow(error.r);
 	}
 
+	function get_next_temp_file_contents ():String {
+		var filename = null;
+		var buffer = null;
+		try {
+			filename = client.filenames.pop();
+			if(filename != null) {
+				buffer = neko.io.File.getContent(filename);
+				try neko.FileSystem.deleteFile(filename) catch(e:Dynamic) {};
+			}
+		} catch(e:Dynamic) {
+			try neko.FileSystem.deleteFile(filename) catch(e:Dynamic) {};
+			for(f in client.filenames)
+				try neko.FileSystem.deleteFile(f) catch(e:Dynamic) {};
+			client.filenames.clear();
+			neko.Lib.rethrow(e);
+		}
+		return buffer;
+	}
+
+	function parse_raw_multipart_data( onPart : NativeString -> NativeString -> Void, onData : NativeString -> Int -> Int -> Void ) {
+		var bufsize = 1 << 16;
+		var buffer = "";
+		var name = null;
+		while( true ) {
+			var partOffset = 0;
+			var bodyOffset = 0;
+			if( name == null) {
+				partOffset = buffer.indexOf(client.boundary+"\r\n");
+				if(partOffset == -1) {
+					buffer += get_next_temp_file_contents();
+					continue;
+				} 
+				partOffset += client.boundary.length + 2;
+				bodyOffset = buffer.indexOf("\r\n\r\n",partOffset);
+				if(bodyOffset == -1) {
+					buffer += get_next_temp_file_contents();
+					continue;
+				}
+				bodyOffset += 4;
+				var header = buffer.substr(partOffset,bodyOffset-partOffset);
+				var snpos = header.indexOf('name="') + 6;
+				var enpos = header.indexOf('"',snpos);
+				name = header.substr(snpos,enpos-snpos);
+				var sfpos = header.indexOf('filename="');
+				if(sfpos == -1) {
+					onPart( neko.Lib.haxeToNeko(name), null );
+				} else {
+					sfpos += 10;
+					var efpos = header.indexOf('"',sfpos);
+					onPart( neko.Lib.haxeToNeko(name), neko.Lib.haxeToNeko(header.substr(sfpos,efpos-sfpos)) );
+				}
+			} else {
+				buffer += get_next_temp_file_contents();
+			}
+			var partLen = buffer.indexOf(client.boundary,bodyOffset);
+			var len = (partLen == -1) ? buffer.length - bodyOffset : partLen - bodyOffset - 2;
+			onData( neko.Lib.haxeToNeko(buffer.substr(bodyOffset,len)),0,len);
+			buffer = buffer.substr(bodyOffset+len);
+			if(buffer == "\r\n"+client.boundary+"--\r\n") break;
+			if(partLen != -1) name = null;
+		}
+	}
+
 	function cgi_flush() {
 		client.sendHeaders();
 		client.sendMessage(CFlush,"");
@@ -210,4 +278,4 @@
 		return v;
 	}
 
-}
\ No newline at end of file
+}
Index: Tora.hx
===================================================================
RCS file: /cvsroot/neko/libs/mod_tora/server/Tora.hx,v
retrieving revision 1.34
diff -u -r1.34 Tora.hx
--- Tora.hx	16 Oct 2008 07:00:41 -0000	1.34
+++ Tora.hx	22 Oct 2008 12:41:47 -0000
@@ -342,6 +342,7 @@
 	}
 
 	public static var inst : Tora;
+	public static var tmpdir : String;
 
 	static function main() {
 		var args = neko.Sys.args();
@@ -353,10 +354,12 @@
 		if( nthreads == null ) nthreads = "32";
 		var port = Std.parseInt(port);
 		var nthreads = Std.parseInt(nthreads);
+		tmpdir = neko.Sys.getEnv('TMPDIR');
+		if(tmpdir == null) tmpdir = "/tmp";
 		inst = new Tora();
 		log("Starting Tora server on "+host+":"+port+" with "+nthreads+" threads");
 		inst.init(nthreads);
 		inst.run(host,port);
 	}
 
-}
\ No newline at end of file
+}
-- 
Neko : One VM to run them all
(http://nekovm.org)

Reply via email to