Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

git-svn-id: svn://cherokee-project.com/cherokee/trunk@2969 5dc97367-9…

…7f1-0310-9951-d761b3857238
  • Loading branch information...
commit 8cdea13548ba4099cd4aa78c5a17018635b96421 1 parent 36e2e20
@alobbs alobbs authored
View
6 ChangeLog
@@ -1,5 +1,11 @@
2009-03-12 Alvaro Lopez Ortega <alvaro@octality.com>
+ * cherokee/header.c, cherokee/header.h, cherokee/handler_fcgi.c,
+ cherokee/post.c, cherokee/post.h, cherokee/handler_cgi.c,
+ cherokee/connection.c, admin/pyscgi.py, qa/213-Post-Chunked1.py,
+ qa/Makefile.am, qa/util.py, qa/214-Post-Chunked2.py: Implements
+ "Transfer-Encoding: chunked" POSTs.
+
* cherokee/handler_cgi_base.c: Fixes the SCRIPT_NAME generation
when a CGI is inside a user directory (/~user).
View
4 admin/pyscgi.py
@@ -84,8 +84,8 @@ def __read_netstring_size (self):
c = self.__safe_read(1)
if c == ':':
break
- elif not c:
- raise IOError, 'Malformed netstring'
+ elif len(c) == 0:
+ raise IOError, 'Empty Length. Closed?'
size += c
return long(size)
View
32 cherokee/connection.c
@@ -1598,17 +1598,37 @@ post_init (cherokee_connection_t *conn)
cuint_t info_len = 0;
CHEROKEE_TEMP(buf, 64);
- /* Get the header "Content-Length" content
+ /* RFC 2616:
+ *
+ * If a message is received with both a Transfer-Encoding
+ * header field and a Content-Length header field, the latter
+ * MUST be ignored.
+ */
+
+ /* Check "Transfer-Encoding"
+ */
+ ret = cherokee_header_get_known (&conn->header, header_transfer_encoding, &info, &info_len);
+ if (ret == ret_ok) {
+ if (strncasecmp (info, "chunked", MIN(info_len, 7)) == 0) {
+ cherokee_post_set_encoding (&conn->post, post_enc_chunked);
+ return ret_ok;
+ }
+ }
+
+ /* Check "Content-Length"
*/
ret = cherokee_header_get_known (&conn->header, header_content_length, &info, &info_len);
- if (ret != ret_ok) {
+ if (unlikely (ret != ret_ok)) {
conn->error_code = http_length_required;
return ret_error;
}
/* Parse the POST length
*/
- if ((info_len == 0) || (info_len >= buf_size) || (info == NULL)) {
+ if (unlikely ((info == NULL) ||
+ (info_len == 0) ||
+ (info_len >= buf_size)))
+ {
conn->error_code = http_bad_request;
return ret_error;
}
@@ -1616,10 +1636,10 @@ post_init (cherokee_connection_t *conn)
memcpy (buf, info, info_len);
buf[info_len] = '\0';
- /* Check: Post length > 0
+ /* Check: Post length >= 0
*/
- post_len = (off_t) atol(buf);
- if (post_len < 0) {
+ post_len = (off_t) atoll(buf);
+ if (unlikely (post_len < 0)) {
conn->error_code = http_bad_request;
return ret_error;
}
View
17 cherokee/handler_cgi.c
@@ -347,12 +347,13 @@ cherokee_handler_cgi_add_env_pair (cherokee_handler_cgi_base_t *cgi_base,
}
static ret_t
-add_environment (cherokee_handler_cgi_t *cgi, cherokee_connection_t *conn)
+add_environment (cherokee_handler_cgi_t *cgi,
+ cherokee_connection_t *conn)
{
ret_t ret;
- char *length;
- cuint_t length_len;
+ off_t post_len;
cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(cgi);
+ cherokee_buffer_t *tmp = THREAD_TMP_BUF2(CONN_THREAD(conn));
ret = cherokee_handler_cgi_base_build_envp (HDL_CGI_BASE(cgi), conn);
if (unlikely (ret != ret_ok))
@@ -360,9 +361,13 @@ add_environment (cherokee_handler_cgi_t *cgi, cherokee_connection_t *conn)
/* CONTENT_LENGTH
*/
- ret = cherokee_header_get_known (&conn->header, header_content_length, &length, &length_len);
- if (ret == ret_ok)
- set_env (cgi_base, "CONTENT_LENGTH", length, length_len);
+ if (http_method_with_input (conn->header.method)) {
+ cherokee_post_get_len (&conn->post, &post_len);
+
+ cherokee_buffer_clean (tmp);
+ cherokee_buffer_add_ullong10 (tmp, post_len);
+ set_env (cgi_base, "CONTENT_LENGTH", tmp->buf, tmp->len);
+ }
/* SCRIPT_FILENAME
*/
View
9 cherokee/handler_fcgi.c
@@ -391,16 +391,19 @@ set_env_pair (cherokee_handler_cgi_base_t *cgi_base,
static ret_t
add_extra_fcgi_env (cherokee_handler_fcgi_t *hdl, cuint_t *last_header_offset)
{
- ret_t ret;
+ off_t post_len = 0;
cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(hdl);
cherokee_buffer_t buffer = CHEROKEE_BUF_INIT;
cherokee_connection_t *conn = HANDLER_CONN(hdl);
/* CONTENT_LENGTH
*/
- ret = cherokee_header_copy_known (&conn->header, header_content_length, &buffer);
- if (ret == ret_ok)
+ if (http_method_with_input (conn->header.method)) {
+ cherokee_post_get_len (&conn->post, &post_len);
+
+ cherokee_buffer_add_ullong10 (&buffer, post_len);
set_env (cgi_base, "CONTENT_LENGTH", buffer.buf, buffer.len);
+ }
/* Add PATH_TRANSLATED only it there is pathinfo
*/
View
6 cherokee/header.c
@@ -1011,6 +1011,12 @@ cherokee_header_parse (cherokee_header_t *hdr, cherokee_buffer_t *buffer, cherok
} else
goto unknown;
break;
+ case 'T':
+ if (header_equals ("Transfer-Encoding", header_range, begin, header_len)) {
+ ret = add_known_header (hdr, header_transfer_encoding, val_offs, val_len);
+ } else
+ goto unknown;
+ break;
case 'U':
if (header_equals ("Upgrade", header_upgrade, begin, header_len)) {
ret = add_known_header (hdr, header_upgrade, val_offs, val_len);
View
1  cherokee/header.h
@@ -56,6 +56,7 @@ typedef enum {
header_location,
header_range,
header_referer,
+ header_transfer_encoding,
header_upgrade,
header_user_agent,
header_x_forwarded_for,
View
187 cherokee/post.c
@@ -37,11 +37,16 @@
ret_t
cherokee_post_init (cherokee_post_t *post)
{
- post->type = post_undefined;
- post->size = 0;
- post->received = 0;
- post->tmp_file_fd = -1;
- post->walk_offset = 0;
+ post->type = post_undefined;
+ post->encoding = post_enc_regular;
+
+ post->size = 0;
+ post->received = 0;
+ post->tmp_file_fd = -1;
+ post->walk_offset = 0;
+
+ post->chunked_last = false;
+ post->chunked_processed = 0;
cherokee_buffer_init (&post->info);
cherokee_buffer_init (&post->tmp_file);
@@ -55,10 +60,16 @@ cherokee_post_mrproper (cherokee_post_t *post)
{
int re;
- post->type = post_undefined;
- post->size = 0;
- post->received = 0;
- post->walk_offset = 0;
+ post->type = post_undefined;
+ post->encoding = post_enc_regular;
+
+ post->size = 0;
+ post->received = 0;
+ post->walk_offset = 0;
+
+ post->chunked_last = false;
+ post->chunked_processed = 0;
+
if (post->tmp_file_fd != -1) {
close (post->tmp_file_fd);
@@ -107,6 +118,19 @@ cherokee_post_set_len (cherokee_post_t *post, off_t len)
}
+ret_t
+cherokee_post_set_encoding (cherokee_post_t *post,
+ cherokee_post_encoding_t enc)
+{
+ TRACE(ENTRIES, "Setting encoding: %s\n",
+ (enc == post_enc_regular) ? "regular" : "chunked");
+
+ post->type = post_in_memory;
+ post->encoding = enc;
+ return ret_ok;
+}
+
+
int
cherokee_post_is_empty (cherokee_post_t *post)
{
@@ -117,7 +141,15 @@ cherokee_post_is_empty (cherokee_post_t *post)
int
cherokee_post_got_all (cherokee_post_t *post)
{
- return (post->received >= post->size);
+ switch(post->encoding) {
+ case post_enc_regular:
+ return (post->received >= post->size);
+ case post_enc_chunked:
+ return post->chunked_last;
+ }
+
+ SHOULDNT_HAPPEN;
+ return 0;
}
ret_t
@@ -139,14 +171,141 @@ cherokee_post_append (cherokee_post_t *post, const char *str, size_t len)
}
+static ret_t
+process_chunk (cherokee_post_t *post,
+ off_t start_at,
+ off_t *processed)
+{
+ char *begin;
+ char *p;
+ char *end;
+ off_t content_size;
+
+ begin = post->info.buf + start_at;
+ p = begin;
+
+ TRACE (ENTRIES, "Post info buffer len=%d, starting at="FMT_OFFSET"\n",
+ post->info.len, start_at);
+
+ while (true) {
+ end = post->info.buf + post->info.len;
+
+ /* Iterate through the number */
+ while ((p < end) &&
+ (((*p >= '0') && (*p <= '9')) ||
+ ((*p >= 'a') && (*p <= 'f')) ||
+ ((*p >= 'A') && (*p <= 'F'))))
+ p++;
+
+ if (unlikely (p+2 > end))
+ return ret_ok;
+
+ /* Check the CRLF after the length */
+ if (p[0] != CHR_CR)
+ return ret_error;
+ if (p[1] != CHR_LF)
+ return ret_error;
+
+ /* Read the length */
+ content_size = (off_t) strtoul (begin, &p, 16);
+ p += 2;
+
+ if (unlikely (content_size < 0))
+ return ret_error;
+
+ /* Check if there's enough info */
+ if (content_size == 0)
+ post->chunked_last = true;
+
+ else if (p + content_size + 2 > end) {
+ TRACE (ENTRIES, "Unfinished chunk(len="FMT_OFFSET"), has=%d, processed="FMT_OFFSET"\n",
+ content_size, (int)(end-p), *processed);
+ return ret_ok;
+ }
+
+ TRACE (ENTRIES, "Processing chunk len=%d\n", content_size);
+
+ /* Remove the prefix */
+ cherokee_buffer_remove_chunk (&post->info, begin - post->info.buf, p-begin);
+
+ if (post->chunked_last) {
+ TRACE(ENTRIES, "Last chunk: %s\n", "exiting");
+ break;
+ } else {
+ /* Clean the trailing CRLF */
+ p = post->info.buf + start_at + content_size;
+ cherokee_buffer_remove_chunk (&post->info, p - post->info.buf, 2);
+ }
+
+ /* Next iteration */
+ begin = p;
+ *processed = (*processed + content_size);
+ }
+
+ return ret_ok;
+}
+
+
ret_t
cherokee_post_commit_buf (cherokee_post_t *post, size_t size)
{
+ ret_t ret;
ssize_t written;
+ off_t processed = 0;
+
+ if (unlikely (size <= 0))
+ return ret_ok;
+
+ /* Chunked post
+ */
+ if (post->encoding == post_enc_chunked) {
+ /* Process the chunks */
+ ret = process_chunk (post, post->chunked_processed, &processed);
+ if (unlikely (ret != ret_ok)) {
+ return ret_error;
+ }
+
+ if (processed <= 0) {
+ return ret_ok;
+ }
+
+ /* Store the processed info */
+ switch (post->type) {
+ case post_undefined:
+ SHOULDNT_HAPPEN;
+ return ret_error;
+
+ case post_in_memory:
+ post->received += processed;
+ post->chunked_processed += processed;
+ break;
+
+ case post_in_tmp_file:
+ if (post->tmp_file_fd == -1)
+ return ret_error;
+
+ written = write (post->tmp_file_fd, post->info.buf, processed);
+ if (unlikely (written < 0))
+ return ret_error;
+
+ cherokee_buffer_move_to_begin (&post->info, (cuint_t)written);
+
+ post->chunked_processed -= written;
+ post->received += written;
+
+ break;
+ }
+
+ if (post->chunked_last) {
+ TRACE(ENTRIES, "Got it all: len=%d\n", post->received);
+ post->size = post->received;
+ }
- if (size <= 0)
return ret_ok;
+ }
+ /* Plain Post
+ */
switch (post->type) {
case post_undefined:
return ret_error;
@@ -156,11 +315,12 @@ cherokee_post_commit_buf (cherokee_post_t *post, size_t size)
return ret_ok;
case post_in_tmp_file:
- if (post->tmp_file_fd == -1)
+ if (unlikely (post->tmp_file_fd == -1))
return ret_error;
written = write (post->tmp_file_fd, post->info.buf, post->info.len);
- if (written < 0) return ret_error;
+ if (unlikely (written < 0))
+ return ret_error;
cherokee_buffer_move_to_begin (&post->info, (cuint_t)written);
post->received += written;
@@ -168,6 +328,7 @@ cherokee_post_commit_buf (cherokee_post_t *post, size_t size)
return ret_ok;
}
+ SHOULDNT_HAPPEN;
return ret_error;
}
View
27 cherokee/post.h
@@ -39,16 +39,28 @@ typedef enum {
post_in_tmp_file
} cherokee_post_type_t;
+typedef enum {
+ post_enc_regular,
+ post_enc_chunked
+} cherokee_post_encoding_t;
+
typedef struct {
- cherokee_post_type_t type;
- off_t size;
- off_t received;
- off_t walk_offset;
+ cherokee_post_type_t type;
+ cherokee_post_encoding_t encoding;
+
+ cherokee_boolean_t chunked_last;
+ off_t chunked_processed;
+
+ cherokee_boolean_t got_last_chunk;
+
+ off_t size;
+ off_t received;
+ off_t walk_offset;
- cherokee_buffer_t info;
+ cherokee_buffer_t info;
- cherokee_buffer_t tmp_file;
- int tmp_file_fd;
+ cherokee_buffer_t tmp_file;
+ int tmp_file_fd;
} cherokee_post_t;
#define POST(x) ((cherokee_post_t *)(x))
@@ -66,6 +78,7 @@ ret_t cherokee_post_get_len (cherokee_post_t *post, off_t *len);
ret_t cherokee_post_append (cherokee_post_t *post, const char *str, size_t len);
ret_t cherokee_post_commit_buf (cherokee_post_t *post, size_t len);
+ret_t cherokee_post_set_encoding (cherokee_post_t *post, cherokee_post_encoding_t enc);
ret_t cherokee_post_walk_reset (cherokee_post_t *post);
ret_t cherokee_post_walk_finished (cherokee_post_t *post);
View
32 qa/213-Post-Chunked1.py
@@ -0,0 +1,32 @@
+import random
+import string
+from base import *
+from util import *
+
+FILE = "Chunked_post_tiny.php"
+MAGIC = 'Alvaro_alobbs.com'
+
+SCRIPT = """<?php
+ echo "Testing Chunked encoded POSTs\n";
+ echo $_POST['var'] . "\n";
+ echo "The end\n";
+?>"""
+
+class Test (TestBase):
+ def __init__ (self):
+ TestBase.__init__ (self, __file__)
+ self.name = "POST Chunked: Tiny"
+
+ self.request = "POST /%s HTTP/1.0\r\n" % (FILE) +\
+ "Content-type: application/x-www-form-urlencoded\r\n" +\
+ "Content-length: 0\r\n" +\
+ "Transfer-Encoding: chunked\r\n"
+ self.expected_error = 200
+ self.expected_content = [MAGIC]
+ self.post = hex(len(MAGIC)+4)[2:] + "\r\nvar="+MAGIC + "\r\n0\r\n"
+
+ def Prepare (self, www):
+ self.WriteFile (www, FILE, 0444, SCRIPT)
+
+ def Precondition (self):
+ return os.path.exists (look_for_php())
View
35 qa/214-Post-Chunked2.py
@@ -0,0 +1,35 @@
+import random
+import string
+from base import *
+from util import *
+
+FILE = "Chunked_post1.php"
+POST_LENGTH = (100*1024)+35
+MAGIC = letters_random (POST_LENGTH)
+
+SCRIPT = """<?php
+ echo "Testing Chunked encoded POSTs\n";
+ echo $_POST['var'] . "\n";
+ echo "The end\n";
+?>"""
+
+class Test (TestBase):
+ def __init__ (self):
+ TestBase.__init__ (self, __file__)
+ self.name = "POST Chunked: ~100k"
+
+ self.request = "POST /%s HTTP/1.0\r\n" % (FILE) +\
+ "Content-type: application/x-www-form-urlencoded\r\n" +\
+ "Content-length: 0\r\n" +\
+ "Transfer-Encoding: chunked\r\n"
+ self.expected_error = 200
+
+ def Prepare (self, www):
+ tmpfile = self.WriteTemp (MAGIC)
+ self.WriteFile (www, FILE, 0444, SCRIPT)
+
+ self.post = chunk_encode("var=" + MAGIC)
+ self.expected_content = "file:%s" % (tmpfile)
+
+ def Precondition (self):
+ return os.path.exists (look_for_php())
View
4 qa/Makefile.am
@@ -217,7 +217,9 @@ run-tests.py \
209-ScriptAlias_DRoot.py \
210-Userdir_ScriptName.py \
211-EmptyGif.py \
-212-DirList-Hidden.py
+212-DirList-Hidden.py \
+213-Post-Chunked1.py \
+214-Post-Chunked2.py
test:
View
20 qa/util.py
@@ -1,4 +1,4 @@
-import os, sys, time, random, fcntl, socket
+import os, sys, time, random, fcntl, socket, math
from conf import *
@@ -261,3 +261,21 @@ def ip_cmp(x,y):
def get_forwarded_http_header (header):
return 'HTTP_' + header.upper().replace('-','_')
+
+def chunk_encode (txt, size=None, pieces=None):
+ out = ''
+
+ if not pieces:
+ pieces = random.randint (1, 10)
+ if not size:
+ size = int (math.ceil (len(txt)/float(pieces)))
+
+ for n in range(pieces):
+ sub = txt[(n*size):(n+1)*size]
+ out += hex(len(sub))[2:]
+ out += '\r\n'
+ out += sub
+ out += '\r\n'
+
+ out += '0\r\n'
+ return out
Please sign in to comment.
Something went wrong with that request. Please try again.