Overview
SHA1 Hash: | 6af8fdc230b34ef61f6baab75a370c2647d8e470 |
---|---|
Date: | 2007-12-04 13:05:35 |
User: | drh |
Comment: | Generate CGI replies as separate header and body so that the header can be extended during the construction of the body. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/cgi.c from [1c05503871] to [a795dc49f1].
@@ -57,10 +57,17 @@ #define P(x) cgi_parameter((x),0) #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 ** function. @@ -67,40 +74,80 @@ */ #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 */ @@ -119,12 +166,13 @@ /* ** 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); } /* ** Set the reply status code @@ -220,10 +268,11 @@ /* ** Do a normal HTTP reply */ void cgi_reply(void){ + int total_size; if( iReplyStatus<=0 ){ iReplyStatus = 200; zReplyStatus = "OK"; } @@ -273,19 +322,27 @@ printf( "Content-Type: %s; charset=%s\r\n", zContentType, nl_langinfo(CODESET)); #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")); } /* @@ -611,10 +668,11 @@ */ 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, '&'); } @@ -939,20 +997,20 @@ ** extra formatting capabilities such as %h and %t. */ void cgi_printf(const char *zFormat, ...){ va_list ap; va_start(ap,zFormat); - vxprintf(&cgiContent,zFormat,ap); + vxprintf(pContent,zFormat,ap); va_end(ap); } /* ** 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); } /* ** Send a reply indicating that the HTTP request was malformed @@ -976,11 +1034,11 @@ cgi_printf( "<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); }
Modified src/manifest.c from [9573b5d563] to [53c15e41ed].
@@ -783,11 +783,11 @@ int cid = db_column_int(&q, 0); add_mlink(rid, &m, cid, 0); } db_finalize(&q); db_multi_exec( - "INSERT INTO event(type,mtime,objid,user,comment," + "REPLACE INTO event(type,mtime,objid,user,comment," " bgcolor,brbgcolor,euser,ecomment)" "VALUES('ci',%.17g,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype=1)," "(SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype!=1)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," @@ -845,11 +845,11 @@ if( prior ){ content_deltify(prior, rid, 0); } zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle); db_multi_exec( - "INSERT INTO event(type,mtime,objid,user,comment," + "REPLACE INTO event(type,mtime,objid,user,comment," " bgcolor,brbgcolor,euser,ecomment)" "VALUES('w',%.17g,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype=1)," "(SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype!=1)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," @@ -870,15 +870,15 @@ zTag = mprintf("tkt-%s", m.zTicketUuid); tag_insert(zTag, 1, 0, rid, m.rDate, rid); free(zTag); zComment = mprintf("Changes to ticket [%.10s]", m.zTicketUuid); db_multi_exec( - "INSERT INTO event(type,mtime,objid,user,comment)" + "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('t',%.17g,%d,%Q,%Q)", m.rDate, rid, m.zUser, zComment ); free(zComment); } db_end_transaction(0); manifest_clear(&m); return 1; }
Modified src/style.c from [773050316b] to [80b7efbe0d].
@@ -82,10 +82,11 @@ const char *zLogInOut = "Login"; const char *zHeader = db_get("header", (char*)zDefaultHeader); login_check_credentials(); if( pInterp ) return; + cgi_destination(CGI_HEADER); /* Generate the header up through the main menu */ pInterp = SbS_Create(); SbS_Store(pInterp, "project_name", db_get("project-name","Unnamed Fossil Project"), 0); @@ -97,11 +98,11 @@ SbS_Store(pInterp, "login", g.zLogin, 0); zLogInOut = "Logout"; } SbS_Render(pInterp, zHeader); - /* Generate the main menu and the submenu (if any) */ + /* Generate the main menu */ @ <div class="mainmenu"> @ <a href="%s(g.zBaseURL)/home">Home</a> if( g.okRead ){ @ <a href="%s(g.zBaseURL)/leaves">Leaves</a> @ <a href="%s(g.zBaseURL)/timeline">Timeline</a> @@ -119,12 +120,29 @@ } if( !g.noPswd ){ @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a> } @ </div> + cgi_destination(CGI_BODY); + g.cgiPanic = 1; +} + +/* +** Draw the footer at the bottom of the page. +*/ +void style_footer(void){ + const char *zFooter; + + if( pInterp==0 ) return; + + /* Go back and put the submenu at the top of the page. We delay the + ** creation of the submenu until the end so that we can add elements + ** to the submenu while generating page text. + */ if( nSubmenu>0 ){ int i; + cgi_destination(CGI_HEADER); @ <div class="submenu"> qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); for(i=0; i<nSubmenu; i++){ struct Submenu *p = &aSubmenu[i]; if( p->zLink==0 ){ @@ -132,22 +150,16 @@ }else{ @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a> } } @ </div> - } - @ <div class="content"> - g.cgiPanic = 1; -} + cgi_destination(CGI_BODY); + } -/* -** Draw the footer at the bottom of the page. -*/ -void style_footer(void){ - const char *zFooter; - - if( pInterp==0 ) return; + /* Put the footer at the bottom of the page. + */ + @ <div class="content"> zFooter = db_get("footer", (char*)zDefaultFooter); @ </div> SbS_Render(pInterp, zFooter); SbS_Destroy(pInterp); pInterp = 0;