I've solved the performance problems w/my athttpd file upload patch
without modifying
my code much. The post data is now, effectively, only scanned once.
There are also various other changes in this patch, but I'll split
them out once CVS HEAD
has accepted the other major patches in the pipeline.
--
Øyvind Harboe
http://www.zylin.com - eCos ARM & FPGA developer kit
### Eclipse Workspace Patch 1.0
#P ecos
Index: net/athttpd/current/include/forms.h
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/include/forms.h,v
retrieving revision 1.1
diff -u -r1.1 forms.h
--- net/athttpd/current/include/forms.h 18 Jul 2006 16:37:24 -0000 1.1
+++ net/athttpd/current/include/forms.h 13 Dec 2007 10:10:26 -0000
@@ -73,6 +73,8 @@
// Prototypes.
char *cyg_httpd_store_form_data(char*);
+void
+cyg_httpd_fetch_form_data(char *name, char *buffer, int len, int *actual);
void cyg_httpd_handle_method_POST(void);
cyg_int8 cyg_httpd_from_hex (cyg_int8);
char *cyg_httpd_find_form_variable(char*);
Index: net/athttpd/current/include/http.h
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/include/http.h,v
retrieving revision 1.2
diff -u -r1.2 http.h
--- net/athttpd/current/include/http.h 27 Nov 2006 15:41:56 -0000 1.2
+++ net/athttpd/current/include/http.h 13 Dec 2007 10:10:26 -0000
@@ -102,6 +102,7 @@
#define CYG_HTTPD_MODE_SEND_HEADER_ONLY 0x0004
#define CYG_HTTPD_MODE_NO_CACHE 0x0008
#define CYG_HTTPD_MODE_FORM_DATA 0x0010
+#define CYG_HTTPD_MODE_MULTIPART_FORM_DATA 0x0020
// This must be generated at random...
#define CYG_HTTPD_MD5_AUTH_NAME "MD5"
@@ -175,6 +176,17 @@
// data (it might come in more than one frame) and must be visible to
// handlers and cgi scripts.
char *post_data;
+ // the boundary string used for multipart/form-data
+ char *boundary;
+
+ // skipping to a boundary can take a *long* time.
+ // we cache N such skips to save time
+ int boundarySkipCacheNum;
+ struct
+ {
+ char *start;
+ char *next;
+ } boundaryCache[16]; // skipping to skip
// This pointer points to the information about the domain that needs
// to be authenticated. It is only used by the function that builds the
Index: net/athttpd/current/include/cgi.h
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/include/cgi.h,v
retrieving revision 1.1
diff -u -r1.1 cgi.h
--- net/athttpd/current/include/cgi.h 18 Jul 2006 16:37:24 -0000 1.1
+++ net/athttpd/current/include/cgi.h 13 Dec 2007 10:10:26 -0000
@@ -55,7 +55,8 @@
#ifndef __CGI_H__
#define __CGI_H__
-cyg_int32 cyg_httpd_exec_cgi(void);
+int cyg_httpd_exec_cgi(void);
void cyg_httpd_init_tcl_interpreter(void);
+
#endif
Index: net/athttpd/current/src/socket.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/socket.c,v
retrieving revision 1.4
diff -u -r1.4 socket.c
--- net/athttpd/current/src/socket.c 27 Nov 2006 15:41:56 -0000 1.4
+++ net/athttpd/current/src/socket.c 13 Dec 2007 10:10:29 -0000
@@ -210,7 +210,8 @@
void
cyg_httpd_process_request(cyg_int32 index)
{
- httpstate.client_index = index;
+ diag_printf("Start request");
+ httpstate.client_index = index;
cyg_int32 descr = httpstate.sockets[index].descriptor;
// By placing a terminating '\0' not only we have a safe stopper point
@@ -240,6 +241,7 @@
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
printf("EOF received on descriptor: %d. Closing it.\n", descr);
#endif
+ diag_printf("Stop request");
return;
}
@@ -253,6 +255,7 @@
diag_printf("ERROR reading from socket. read() returned: %d\n",
httpstate.inbuffer_len);
#endif
+ diag_printf("Stop request 2");
return;
}
@@ -278,6 +281,8 @@
// close() without first asking the opinion of the client, and
// running the risk of stray data lingering around.
shutdown(descr, SHUT_WR);
+
+ diag_printf("Stop request 3");
}
void
Index: net/athttpd/current/src/forms.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/forms.c,v
retrieving revision 1.4
diff -u -r1.4 forms.c
--- net/athttpd/current/src/forms.c 14 Nov 2007 14:39:13 -0000 1.4
+++ net/athttpd/current/src/forms.c 13 Dec 2007 10:10:27 -0000
@@ -83,44 +83,75 @@
: c - 'a' + 10;
}
+/* Scan through the entire string, but only store as much as the buffer can
hold.
+ * That way a string that is too long does stop subsequent variables from
+ * being read
+ *
+ * buflen - length of buffer, including space for \0. must be >= 1
+ * actual - actual length of string excluding \0
+ */
char*
-cyg_httpd_store_form_variable(char *query, cyg_httpd_fvars_table_entry *entry)
+cyg_httpd_store_form_variable(char *query, char *q, int buflen, int * actual)
{
char *p = query;
- char *q = entry->buf;
int len = 0;
-
- while (len < (entry->buflen - 1))
+ char c=0;
+ int gotChar;
+
+ for (;;)
+ {
+ int done = (len >= (buflen - 1));
switch(*p)
{
case '%':
p++;
+ gotChar=0;
if (*p)
- *q = cyg_httpd_from_hex(*p++) * 16;
+ {
+ c = cyg_httpd_from_hex(*p++) * 16;
+ gotChar=1;
+ }
if (*p)
- *q = (*q + cyg_httpd_from_hex(*p++));
- q++;
+ {
+ c = (c + cyg_httpd_from_hex(*p++));
+ gotChar=1;
+ }
+ if (!done&&gotChar)
+ {
+ *q++=c;
+ }
len++;
break;
case '+':
- *q++ = ' ';
+ if (!done)
+ {
+ *q++=' ';
+ }
p++;
len++;
break;
+ case 0: // Hmmm... I'm not quite sure how, in previous versions, the
\0 sentinel was caught...
case '&':
case ' ':
- *q++ = '\0';
- return p;
- default:
- *q++ = *p++;
+ goto doneLoop;
+ default:
+ c=*p++;
+ if (!done)
+ {
+ *q++ = c;
+ }
len++;
+ break;
}
- *q = '\0';
- while ((*p != ' ') && (*p != '&'))
- p++;
- return p;
+ }
+doneLoop:
+ *q = '\0';
+ *actual=len;
+ return p;
}
+
+
// We'll try to parse the data from the form, and store it in the variables
// that have been defined by the user in the 'form_variable_table'.
char*
@@ -164,7 +195,8 @@
}
// Found the variable, store the name.
- p = cyg_httpd_store_form_variable(++p2, entry);
+ int actual;
+ p = cyg_httpd_store_form_variable(++p2, entry->buf, entry->buflen,
&actual);
#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
diag_printf("Stored form variable: %s Value: %s\n",
entry->name,
@@ -176,6 +208,344 @@
return p;
}
+/* If the string contains *all* of prefix, return true.
+ * \n in prefix can mean \n or \r\n in 'p'.
+ */
+static char *contains(char *p, char *prefix, int len)
+{
+ int i;
+ for (i=0; i<strlen(prefix); i++)
+ {
+ if (len<=0)
+ {
+// diag_printf("mismatch\n");
+ return NULL;
+ }
+// diag_printf("p=%02xprefix=%02x\n", p[i], prefix[i]);
+// diag_printf("p=%cprefix=%c\n", p[i], prefix[i]);
+ if (*p!=prefix[i])
+ {
+ if
((len>=2)&&(prefix[i]=='\n')&&(*p=='\r')&&(p[1]=='\n'))
+ {
+ p+=2;
+ len-=2;
+ continue;
+ }
+// diag_printf("mismatch\n");
+ return NULL;
+ }
+ p++;
+ }
+// diag_printf("match\n");
+ return p;
+}
+
+/* if we're looking at this text, skip it, otherwise return NULL.
+ * If p==NULL, return NULL.
+ * If the function returns NULL, then *len is untouched
+ */
+static char *skip(char *p, int *len, char *expect)
+{
+// diag_printf("skip p=%08x %s %d\n", p, expect, *len);
+ if (p==NULL)
+ return NULL;
+// int i;
+// for (i=0; i<strlen(expect); i++)
+// {
+// diag_printf("%c", p[i]);
+// }
+// diag_printf("strncmp starting\n");
+ char *after;
+ after=contains(p, expect, *len);
+ if (after!=NULL)
+ {
+ // skip boundary
+ int l=after-p;
+ p+=l;
+ *len-=l;
+ return p;
+ }
+ return NULL;
+}
+
+static char *skipToLineEnd(char *p, int *len)
+{
+ if (p==NULL)
+ return NULL;
+ int i;
+ // Special case to improve performance. This is the code
+ // that will skip *large* amounts of data
+ //
+ char *found=(char *)memchr(p, (int)'\n', *len);
+#if 0
+ for (i=0; i<*len; i++)
+ {
+ if (p[i]=='\n')
+ {
+ break;
+ }
+ }
+#endif
+ if (found==NULL)
+ {
+ return NULL;
+ }
+ i=found-p;
+ // Found \n. Do we need to back up one?
+ if ((i>0)&&(p[i-1]=='\r'))
+ {
+ i--;
+ }
+ // found string.
+ *len-=i;
+ return p+i;
+}
+
+// Same as skip() except that we skip forwards to the expected string
+static char *skipTo(char *p, int *len, char *expect)
+{
+// diag_printf("skipTo p=%08x %s\n", p, expect);
+ if (p==NULL)
+ return NULL;
+
+ int i;
+ int strLen=strlen(expect);
+ for (i=0; i<*len; i++)
+ {
+ int j;
+ char *cmp=p;
+ for (j=0; j<strLen; j++)
+ {
+ if (cmp[i+j]!=expect[j])
+ {
+ if
(((*len-i)>=2)&&(expect[j]=='\n')&&(cmp[i+j]=='\r')&&(cmp[i+j+1]=='\n'))
+ {
+ // interpret \r\n as line end
+ cmp++;
+ continue;
+ }
+ break;
+ }
+ }
+ if (j==strLen)
+ {
+ // found string.
+ *len-=i;
+ return p+i;
+ }
+ }
+ // did not find string
+ return NULL;
+}
+
+// Returns pointer to the beginning of a boundary,
+// i.e. the byte *after* the last char in
+// the value
+static char *skipUntilBoundary(char *p, int *origLen)
+{
+// diag_printf("skipUntilBoundary\n");
+ if (p==NULL)
+ return NULL;
+
+ // do we need to search??
+ char *start=p;
+ int i;
+ for (i=0; i<httpstate.boundarySkipCacheNum; i++)
+ {
+ if (httpstate.boundaryCache[i].start==start)
+ {
+ return httpstate.boundaryCache[i].next;
+ }
+ }
+
+ int len=*origLen;
+ for (;;)
+ {
+ p=skipToLineEnd(p, &len);
+ // We're now at the beginning of the boundary
+ char *t=p;
+ int l=len;
+ p=skip(p, &len, "\n");
+ if (p==NULL)
+ return NULL;
+ char *p2;
+ p2=skip(p, &len, "--");
+ if (p2!=NULL)
+ {
+ if (contains(p2, httpstate.boundary, len))
+ {
+ // We back up a bit
+ *origLen=l;
+
+ // can we fit this into the cache?
+ if
(httpstate.boundarySkipCacheNum<(sizeof(httpstate.boundaryCache)/sizeof(*httpstate.boundaryCache)))
+ {
+
httpstate.boundaryCache[httpstate.boundarySkipCacheNum].start=start;
+
httpstate.boundaryCache[httpstate.boundarySkipCacheNum].next=t;
+ httpstate.boundarySkipCacheNum++;
+ }
+
+ return t;
+ }
+ // Not the boundary, continue
+ }
+ }
+ return NULL;
+}
+
+// Example(From Firefox):
+//
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_filename"
+//
+//test
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_filecontent"; filename="abc.txt"
+//Content-Type: text/plain
+//
+//file line 1
+//file line 2
+//file line 3
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_action"
+//
+//Upload
+//-----------------------------107852697226440--
+
+#define VERBOSE(a)
+
+// Skip to variable and fish out contents
+static void
+cyg_httpd_fetch_multipart_form_data(char *name, char *buffer, int bufferlen,
int *actual)
+{
+ char *p=httpstate.post_data;
+ int len=httpstate.content_len;
+
+ if (httpstate.boundary==NULL)
+ {
+ return;
+ }
+
+// diag_printf("2YYYY %d\n", len);
+// diag_printf("2YYYY\n");
+
+// diag_printf("2XXXXXX\nFinding %s\n", name);
+
+ for (;;)
+ {
+ p=skipTo(p, &len, "--");
+ p=skip(p, &len, "--");
+ p=skip(p, &len, httpstate.boundary);
+ p=skip(p, &len, "\n");
+ p=skip(p, &len, "Content-Disposition: form-data; name=\"");
+ char *start=p;
+ p=skipTo(p, &len, "\"");
+ if (p==NULL)
+ return;
+ char *end=p;
+ p=skip(p, &len, "\"");
+ VERBOSE(
+ int i;
+ for (i=0; i<end-start; i++)
+ {
+ diag_printf("%c", start[i]);
+ }
+ )
+ if (contains(start, name, end-start))
+ {
+ // Found variable!
+ // Ignore rest of line and all "Content-xxx" headers and
return the raw data.
+ p=skipToLineEnd(p, &len);
+ p=skip(p, &len, "\n");
+ while (contains(p, "Content-", len))
+ {
+ p=skipToLineEnd(p, &len);
+ p=skip(p, &len, "\n");
+ }
+ // blank line before content
+ p=skipToLineEnd(p, &len);
+ p=skip(p, &len, "\n");
+ start=p;
+ for (;;)
+ {
+ p=skipUntilBoundary(p, &len);
+ if (p==NULL)
+ return;
+ // Found end!
+ *actual=p-start;
+ int t;
+ t=bufferlen-1; // always leave space for a 0.
+ if (t>*actual)
+ t=*actual;
+ memcpy(buffer, start, t);
+ buffer[t]=0; // always poke a 0 afterwards. Note that
the actual data can also contain 0's.
+// diag_printf("tttt\n%s\n\nFound
name=%s\nvalue=\"%s\"\n", p, name, buffer);
+ return;
+ }
+ } else
+ {
+ // Skip this variable
+ p=skipUntilBoundary(p, &len);
+ if (p==NULL)
+ return;
+ }
+ }
+}
+
+// Find form variable, copy it to the buffer and return the actual length of
the variable
+//
+// If a variable is not found(or there are no post data) an empty string
+// is returned.
+void
+cyg_httpd_fetch_form_data(char *name, char *buffer, int len, int *actual)
+{
+ char *p2;
+
+ char *p=httpstate.post_data;
+
+
+ buffer[0]=0;
+ *actual=0;
+
+ if (!p) /* No form data? just return after clearing variables */
+ return;
+
+ if (httpstate.mode & CYG_HTTPD_MODE_MULTIPART_FORM_DATA)
+ {
+ cyg_httpd_fetch_multipart_form_data(name, buffer, len, actual);
+ return;
+ }
+
+
+ while (*p && *p != ' ')
+ {
+ if (!(p2 = strchr(p, '=')))
+ return; /* Malformed post? */
+ int var_length = (cyg_int32)p2 - (cyg_int32)p;
+
+ if (strncmp((const char*)p, name, var_length))
+ {
+ // No such variable. Run through the data.
+ while ((*p != '&') && (*p && *p != ' '))
+ p++;
+ if(*p == '&')
+ p++;
+ continue;
+ }
+
+ // Found the variable, store the name.
+ p = cyg_httpd_store_form_variable(++p2, buffer, len, actual);
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+ diag_printf("Stored form variable: %s Value: %s\n",
+ name,
+ buffer);
+#endif
+
+ return;
+ }
+}
+
+
char*
cyg_httpd_find_form_variable(char *p)
{
@@ -204,6 +574,7 @@
// The content data is only valid during a POST.
httpstate.post_data = (char*)malloc(httpstate.content_len + 1);
+ httpstate.boundarySkipCacheNum=0; // flush boundary cache
CYG_ASSERT(httpstate.post_data != NULL, "Cannot malloc POST buffer");
if (httpstate.post_data == NULL)
{
@@ -266,10 +637,12 @@
{
// Here we'll look for extension to the file. We'll call the cgi
// handler only if the extension is '.o'.
- cyg_httpd_exec_cgi();
- free(httpstate.post_data);
- httpstate.post_data = NULL;
- return;
+ if (cyg_httpd_exec_cgi())
+ {
+ free(httpstate.post_data);
+ httpstate.post_data = NULL;
+ return;
+ }
}
#endif
Index: net/athttpd/current/src/cgi.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/cgi.c,v
retrieving revision 1.3
diff -u -r1.3 cgi.c
--- net/athttpd/current/src/cgi.c 27 Nov 2006 15:41:56 -0000 1.3
+++ net/athttpd/current/src/cgi.c 13 Dec 2007 10:10:27 -0000
@@ -157,15 +157,96 @@
}
#endif
+/* We're not going to write ad-hoc CGI scripts without good old printf! */
+void cyg_httpd_fprintf(char *format, ...)
+{
+ char *buffer = NULL;
+ int size = 1;
+ int n=-1;
+ va_list ap;
+ va_start(ap, format);
+ diag_vprintf(format, ap);
+ va_end(ap);
+
+
+ /* process format string */
+ for (;;)
+ {
+ buffer=malloc(size);
+ if (buffer==NULL)
+ {
+ return;
+ }
+
+ va_start(ap, format);
+ n = vsnprintf(buffer, size, format, ap);
+ va_end(ap);
+ if (n>0 && n < (size-1))
+ break;
+ size=size*2+1;
+ free(buffer);
+ }
+
+ if (n > 0)
+ {
+ cyg_httpd_write_chunked(buffer, strlen(buffer));
+ } else
+ {
+ /* vsnprintf failed */
+ }
+
+ if (buffer)
+ free(buffer);
+}
+
+
//
=============================================================================
// tcl CGI Support
//
=============================================================================
#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
+
+
+/* Error message has to go to HTTP to make debugging less of a torturous
process */
+void cyg_httpd_exec_cgi_tcl_error(Jim_Interp *interp)
+{
+ int len, i;
+
+ cyg_httpd_start_chunked("html");
+ cyg_httpd_fprintf("<html><body>\n");
+
+ cyg_httpd_fprintf("Runtime error, file \"%s\", line %d:<br>" ,
+ interp->errorFileName, interp->errorLine);
+ cyg_httpd_fprintf(" %s<br>" ,
+ Jim_GetString(interp->result, NULL));
+ Jim_ListLength(interp, interp->stackTrace, &len);
+ for (i = 0; i < len; i+= 3) {
+ Jim_Obj *objPtr;
+ const char *proc, *file, *line;
+
+ Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
+ proc = Jim_GetString(objPtr, NULL);
+ Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
+ JIM_NONE);
+ file = Jim_GetString(objPtr, NULL);
+ Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
+ JIM_NONE);
+ line = Jim_GetString(objPtr, NULL);
+ cyg_httpd_fprintf("In procedure '%s' called at file \"%s\", line
%s<br>" ,
+ proc, file, line);
+ }
+ cyg_httpd_fprintf("</html></body>\n");
+
+ cyg_httpd_end_chunked();
+}
+
+
int Jim_AioInit(Jim_Interp *);
cyg_int32
cyg_httpd_exec_cgi_tcl(char *file_name)
{
- char tcl_cmd[CYG_HTTPD_MAXPATH];
+ char tcl_cmd[CYG_HTTPD_MAXPATH+128];
+ // we need to wrap the code into a proc to make sure that all
+ // variables go out of scope to clean up e.g. file handles
sprintf(tcl_cmd, "source %s", file_name);
// Make sure that tcl sees the internal variables including the post_data.
@@ -179,12 +260,15 @@
entry++;
}
- if (httpstate.post_data != NULL)
- Jim_SetVariableStrWithStr(httpstate.jim_interp,
- "post_data",
- httpstate.post_data);
-
- Jim_Eval(httpstate.jim_interp, tcl_cmd);
+ int err;
+ err=Jim_Eval(httpstate.jim_interp, tcl_cmd);
+ if (err!=JIM_OK)
+ {
+ cyg_httpd_exec_cgi_tcl_error(httpstate.jim_interp);
+ }
+
+ // Here we do an explicit garbage collect to clean up e.g. file handles
+ Jim_Collect(httpstate.jim_interp);
return 0;
}
@@ -217,10 +301,58 @@
cyg_httpd_end_chunked();
return JIM_OK;
}
-
+
+
+
+int
+cyg_httpd_Jim_Command_formfetch(Jim_Interp *interp,
+ int argc,
+ Jim_Obj *const *argv)
+{
+ char *name = (char*)Jim_GetString(argv[1], NULL);
+
+ // Find length
+ char buf;
+ int actual;
+ cyg_httpd_fetch_form_data(name, &buf, 1, &actual);
+
+ int len=actual+1;
+ char *destBuffer=malloc(len);
+ if (destBuffer==NULL)
+ return JIM_ERR;
+
+ cyg_httpd_fetch_form_data(name, destBuffer, len, &actual);
+
+ Jim_Obj *objPtr;
+ objPtr = Jim_NewStringObj(interp, destBuffer, actual);
+ Jim_SetResult(interp, objPtr);
+ free(destBuffer);
+ return JIM_OK;
+}
+
+int
+cyg_httpd_Jim_Command_post_data(Jim_Interp *interp,
+ int argc,
+ Jim_Obj *const *argv)
+{
+ Jim_Obj *objPtr;
+ objPtr = Jim_NewStringObj(interp, httpstate.post_data,
httpstate.content_len);
+ Jim_SetResult(interp, objPtr);
+ return JIM_OK;
+}
+
+
+// If the application wants to add commands, it will need to do so after the
+// interpreter has been initialized. However, the initialization of the http
+// server is asynchronous. This function is safe to invoke *before* the http
+// server is started.
void
cyg_httpd_init_tcl_interpreter(void)
{
+ static bool initDone=false;
+ if (initDone)
+ return;
+ initDone=true;
// Start the TCL interpreter.
Jim_InitEmbedded();
httpstate.jim_interp = Jim_CreateInterp();
@@ -243,6 +375,16 @@
cyg_httpd_Jim_Command_endchunked,
NULL,
NULL);
+ Jim_CreateCommand(httpstate.jim_interp,
+ "formfetch",
+ cyg_httpd_Jim_Command_formfetch,
+ NULL,
+ NULL);
+ Jim_CreateCommand(httpstate.jim_interp,
+ "post_data",
+ cyg_httpd_Jim_Command_post_data,
+ NULL,
+ NULL);
}
#endif
@@ -251,7 +393,9 @@
//
=============================================================================
#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL ) || \
defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER)
-cyg_int32
+
+// Return 1 if the request was handled by cgi
+int
cyg_httpd_exec_cgi(void)
{
char file_name[CYG_HTTPD_MAXPATH];
@@ -261,32 +405,25 @@
strcat(file_name, "/");
strcat(file_name, httpstate.url);
cyg_httpd_cleanup_filename(file_name);
-
- char *extension = rindex(httpstate.url, '.');
- if (extension == NULL)
- {
- // File in CGI request _must_ have an extension.
- cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
- return 0;
- }
+ char *extension = rindex(httpstate.url, '.');
+
#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER
if ( strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_OBJLOADER_EXTENSION) == 0)
{
// Load a cgibin via OBJLOADER.
- cyg_int32 rc = cyg_httpd_exec_cgi_objloader(file_name);
- return rc;
+ cyg_httpd_exec_cgi_objloader(file_name);
+ return 1;
}
#endif
#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
if ( strcmp(extension, CYG_HTTPD_DEFAULT_CGIBIN_TCL_EXTENSION) == 0)
{
// Load a cgibin via the TCL interpreter.
- cyg_int32 rc = cyg_httpd_exec_cgi_tcl(file_name);
- return rc;
+ cyg_httpd_exec_cgi_tcl(file_name);
+ return 1;
}
#endif
- cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
return 0;
}
#endif
Index: net/athttpd/current/src/http.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/http.c,v
retrieving revision 1.4
diff -u -r1.4 http.c
--- net/athttpd/current/src/http.c 14 Nov 2007 14:39:13 -0000 1.4
+++ net/athttpd/current/src/http.c 13 Dec 2007 10:10:28 -0000
@@ -715,8 +715,10 @@
CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR,
strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
{
- cyg_httpd_exec_cgi();
- return;
+ if (cyg_httpd_exec_cgi())
+ {
+ return;
+ }
}
// If the OBJLOADER package is not loaded, then the request for a library
// will likely generate a 404.
@@ -851,7 +853,7 @@
{
// The deafult for HTTP 1.1 is keep-alive connections, unless specifically
// closed by the far end.
- httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA);
+ httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA |
CYG_HTTPD_MODE_MULTIPART_FORM_DATA);
httpstate.modified_since = -1;
httpstate.content_len = 0;
while ((*p != '\r') && (*p != '\n') & (*p != '\0'))
@@ -892,14 +894,51 @@
}
else if (strncasecmp(p, "Content-Type: ", 14) == 0)
{
- p = strchr(p, ':') + 2;
- if (p)
- // In the case of a POST request, this is the total length of
- // the payload, which might be spread across several frames.
- if (strncasecmp(p,
- "application/x-www-form-urlencoded",
- 33) == 0)
- httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+ p = p + strlen("Content-Type: ");
+ // In the case of a POST request, this is the total length of
+ // the payload, which might be spread across several frames.
+ if (strncasecmp(p,
+ "application/x-www-form-urlencoded",
+ strlen("application/x-www-form-urlencoded")) == 0)
+ httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+ if (strncasecmp(p,
+ "multipart/form-data",
+ strlen("multipart/form-data")) == 0)
+ {
+ httpstate.mode |= CYG_HTTPD_MODE_MULTIPART_FORM_DATA;
+ p+=strlen("multipart/form-data");
+ }
+
+ // Fish out boundary
+ // Content-type: multipart/form-data, boundary=AaB03x
+ if (httpstate.boundary!=NULL)
+ free(httpstate.boundary);
+ httpstate.boundary=NULL; // no boundary by default
+
+ while ((*p == ' ')||(*p == ';'))
+ {
+ p++;
+ }
+ if (strncmp(p, "boundary", strlen("boundary"))==0)
+ {
+ p+=strlen("boundary");
+ if (*p=='=')
+ {
+ p++;
+
+ char *start=p;
+ while
((*p!='\n')&&(*p!='\r')&&(*p!=';')&&(*p!=' '))
+ {
+ p++;
+ }
+ httpstate.boundary=malloc(p-start+1);
+ if (httpstate.boundary!=NULL)
+ {
+ memcpy(httpstate.boundary, start,
p-start);
+ httpstate.boundary[p-start]=0;
+ }
+ }
+ }
while (*p++ != '\n');
}
else if (strncasecmp("Host:", p, 5) == 0)
@@ -941,28 +980,28 @@
}
else if (strncasecmp(p, "Digest", 6) == 0)
{
- p += 6;
- while (*p == ' ')
- p++;
+ p += 6;
+ while (*p == ' ')
+ p++;
while ((*p != '\r') && (*p != '\n'))
- {
- if (strncasecmp(p, "realm=", 6) == 0)
+ {
+ if (strncasecmp(p, "realm=", 6) == 0)
p = cyg_httpd_digest_skip(p + 6);
- else if (strncasecmp(p, "username=", 9) == 0)
+ else if (strncasecmp(p, "username=", 9) == 0)
p = cyg_httpd_digest_skip(p + 9);
else if (strncasecmp(p, "nonce=", 6) == 0)
p = cyg_httpd_digest_skip(p + 6);
- else if (strncasecmp(p, "response=", 9) == 0)
+ else if (strncasecmp(p, "response=", 9) == 0)
p = cyg_httpd_digest_data(cyg_httpd_md5_response,
p + 9);
- else if (strncasecmp(p, "cnonce=", 7) == 0)
+ else if (strncasecmp(p, "cnonce=", 7) == 0)
p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7);
- else if (strncasecmp(p, "qop=", 4) == 0)
+ else if (strncasecmp(p, "qop=", 4) == 0)
p = cyg_httpd_digest_skip(p + 4);
- else if (strncasecmp(p, "nc=", 3) == 0)
+ else if (strncasecmp(p, "nc=", 3) == 0)
p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount,
p + 3);
- else if (strncasecmp(p, "algorithm=", 10) == 0)
+ else if (strncasecmp(p, "algorithm=", 10) == 0)
p = cyg_httpd_digest_skip(p + 10);
else if (strncasecmp(p, "opaque=", 7) == 0)
p = cyg_httpd_digest_skip(p + 7);
Index: net/athttpd/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/ChangeLog,v
retrieving revision 1.9
diff -u -r1.9 ChangeLog
--- net/athttpd/current/ChangeLog 14 Nov 2007 14:39:13 -0000 1.9
+++ net/athttpd/current/ChangeLog 13 Dec 2007 10:10:26 -0000
@@ -1,3 +1,7 @@
+2007-12-13 Oyvind Harboe <[EMAIL PROTECTED]>
+
+ * tbd. Waiting for some other patches to go in...
+
2007-11-12 Oyvind Harboe <[EMAIL PROTECTED]>
2007-11-12 Jonathan Larmour <[EMAIL PROTECTED]>