Diff
Not logged in

Differences From:

File src/cgi.c part of check-in [fb358ca492] - Progress toward getting ticketing working. We can enter a new ticket and display it. Cannot yet edit a ticket. by drh on 2007-11-24 19:33:46. Also file src/cgi.c part of check-in [d0305b305a] - Merged mainline into my branch to get the newest application. by aku on 2007-12-05 08:07:46. [view]

To:

File src/cgi.c part of check-in [6af8fdc230] - Generate CGI replies as separate header and body so that the header can be extended during the construction of the body. by drh on 2007-12-04 13:05:35. Also file src/cgi.c part of check-in [b312f5ff5b] - Merge in some changes to the CGI reply generator that we made back in early December but got lost on an abandoned branch. Distributed version control is nice, but it also leaves open the real danger of losing changes this way. We need to work on interface features to minimize the risk of losing changes like this, and to identify lost changes quickly. by drh on 2008-02-03 02:41:50. [view]

@@ -58,8 +58,15 @@
 #define PD(x,y)     cgi_parameter((x),(y))
 #define QP(x)       quotable_string(cgi_parameter((x),0))
 #define QPD(x,y)    quotable_string(cgi_parameter((x),(y)))
 
+
+/*
+** Destinations for output text.
+*/
+#define CGI_HEADER   0
+#define CGI_BODY     1
+
 #endif /* INTERFACE */
 
 /*
 ** Provide a reliable implementation of a caseless string comparison
@@ -68,38 +75,78 @@
 #define stricmp sqlite3StrICmp
 extern int sqlite3StrICmp(const char*, const char*);
 
 /*
-** The body of the HTTP reply text is stored here.
-*/
-static Blob cgiContent = BLOB_INITIALIZER;
+** The HTTP reply is generated in two pieces: the header and the body.
+** These pieces are generated separately because they are not necessary
+** produced in order.  Parts of the header might be built after all or
+** part of the body.  The header and body are accumulated in separate
+** Blob structures then output sequentially once everything has been
+** built.
+**
+** The cgi_destination() interface switch between the buffers.
+*/
+static Blob *pContent;
+static Blob cgiContent[2] = { BLOB_INITIALIZER, BLOB_INITIALIZER };
+
+/*
+** Set the destination buffer into which to accumulate CGI content.
+*/
+void cgi_destination(int dest){
+  switch( dest ){
+    case CGI_HEADER: {
+      pContent = &cgiContent[0];
+      break;
+    }
+    case CGI_BODY: {
+      pContent = &cgiContent[1];
+      break;
+    }
+    default: {
+      cgi_panic("bad destination");
+    }
+  }
+}
 
 /*
 ** Append reply content to what already exists.
 */
 void cgi_append_content(const char *zData, int nAmt){
-  blob_append(&cgiContent, zData, nAmt);
+  blob_append(pContent, zData, nAmt);
 }
 
 /*
 ** Reset the HTTP reply text to be an empty string.
 */
 void cgi_reset_content(void){
-  blob_reset(&cgiContent);
+  blob_reset(&cgiContent[0]);
+  blob_reset(&cgiContent[1]);
 }
 
 /*
 ** Return a pointer to the CGI output blob.
 */
 Blob *cgi_output_blob(void){
-  return &cgiContent;
+  return pContent;
+}
+
+/*
+** Combine the header and body of the CGI into a single string.
+*/
+static void cgi_combine_header_and_body(void){
+  int size = blob_size(&cgiContent[1]);
+  if( size>0 ){
+    blob_append(&cgiContent[0], blob_buffer(&cgiContent[1]), size);
+    blob_reset(&cgiContent[1]);
+  }
 }
 
 /*
 ** Return a pointer to the HTTP reply text.
 */
 char *cgi_extract_content(int *pnAmt){
-  return blob_buffer(&cgiContent);
+  cgi_combine_header_and_body();
+  return blob_buffer(&cgiContent[0]);
 }
 
 /*
 ** Additional information used to form the HTTP reply
@@ -120,10 +167,11 @@
 /*
 ** Set the reply content to the specified BLOB.
 */
 void cgi_set_content(Blob *pNewContent){
-  blob_reset(&cgiContent);
-  cgiContent = *pNewContent;
+  cgi_reset_content();
+  cgi_destination(CGI_HEADER);
+  cgiContent[0] = *pNewContent;
   blob_zero(pNewContent);
 }
 
 /*
@@ -221,8 +269,9 @@
 /*
 ** Do a normal HTTP reply
 */
 void cgi_reply(void){
+  int total_size;
   if( iReplyStatus<=0 ){
     iReplyStatus = 200;
     zReplyStatus = "OK";
   }
@@ -274,17 +323,25 @@
 #else
   printf( "Content-Type: %s; charset=ISO-8859-1\r\n", zContentType);
 #endif
   if( strcmp(zContentType,"application/x-fossil")==0 ){
-    blob_compress(&cgiContent, &cgiContent);
+    cgi_combine_header_and_body();
+    blob_compress(&cgiContent[0], &cgiContent[0]);
   }
 
   if( iReplyStatus != 304 ) {
-    printf( "Content-Length: %d\r\n", blob_size(&cgiContent) );
+    total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
+    printf( "Content-Length: %d\r\n", total_size);
   }
   printf("\r\n");
-  if( blob_size(&cgiContent)>0 && iReplyStatus != 304 ){
-    fwrite(blob_buffer(&cgiContent), 1, blob_size(&cgiContent), stdout);
+  if( total_size>0 && iReplyStatus != 304 ){
+    int i, size;
+    for(i=0; i<2; i++){
+      size = blob_size(&cgiContent[i]);
+      if( size>0 ){
+        fwrite(blob_buffer(&cgiContent[i]), 1, size, stdout);
+      }
+    }
   }
   CGIDEBUG(("DONE\n"));
 }
 
@@ -612,8 +669,9 @@
 void cgi_init(void){
   char *z;
   const char *zType;
   int len;
+  cgi_destination(CGI_BODY);
   z = (char*)P("QUERY_STRING");
   if( z ){
     z = mprintf("%s",z);
     add_param_list(z, '&');
@@ -940,9 +998,9 @@
 */
 void cgi_printf(const char *zFormat, ...){
   va_list ap;
   va_start(ap,zFormat);
-  vxprintf(&cgiContent,zFormat,ap);
+  vxprintf(pContent,zFormat,ap);
   va_end(ap);
 }
 
 /*
@@ -949,9 +1007,9 @@
 ** This routine works like "vprintf" except that it has the
 ** extra formatting capabilities such as %h and %t.
 */
 void cgi_vprintf(const char *zFormat, va_list ap){
-  vxprintf(&cgiContent,zFormat,ap);
+  vxprintf(pContent,zFormat,ap);
 }
 
 
 /*
@@ -977,9 +1035,9 @@
     "<html><body><h1>Internal Server Error</h1>\n"
     "<plaintext>"
   );
   va_start(ap, zFormat);
-  vxprintf(&cgiContent,zFormat,ap);
+  vxprintf(pContent,zFormat,ap);
   va_end(ap);
   cgi_reply();
   exit(1);
 }