You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Mike Abbott <mj...@trudge.engr.sgi.com> on 1999/09/02 22:40:31 UTC
[PATCH] 10x performance increase patch #8
Apache Developers,
Here is my eighth patch for increasing Apache's performance. This one
adds a static-content cache called the QSC. Complete documentation is
included.
The base for this patch is Apache/1.3.6 + 10x performance increase
patches #1 through #7. If there is sufficient interest and time I'll
port all my patches to the latest Apache release.
Note that this patch includes two new files, htdocs/manual/qsc.html and
src/main/qsc.c. When I apply the patch to a pure 1.3.6 tree my version
of the patch command puts these files in the current directory rather
than where they belong. I hope yours works!
As always, I welcome your review, questions, and feedback.
--
Michael J. Abbott mja@sgi.com
========================================================================
diff -Naur apache_1.3.6+01-07/conf/httpd.conf-dist apache_1.3.6+01-08/conf/httpd.conf-dist
--- apache_1.3.6+01-07/conf/httpd.conf-dist Tue Jul 20 22:19:58 1999
+++ apache_1.3.6+01-08/conf/httpd.conf-dist Thu Sep 2 10:12:04 1999
@@ -210,6 +210,16 @@
#
#ExtendedStatus On
+#
+# QSC: Enable the Quick Shortcut Cache. The QSC caches response
+# headers and data together for very fast response to requests for
+# static content. It is tightly integrated with and requires the
+# mmap_static module.
+#
+<IfModule mod_mmap_static.c>
+ QSC on
+</IfModule>
+
### Section 2: 'Main' server configuration
#
# The directives in this section set up the values used by the 'main'
diff -Naur apache_1.3.6+01-07/htdocs/manual/index.html apache_1.3.6+01-08/htdocs/manual/index.html
--- apache_1.3.6+01-07/htdocs/manual/index.html Thu Jul 8 16:08:43 1999
+++ apache_1.3.6+01-08/htdocs/manual/index.html Thu Sep 2 10:12:18 1999
@@ -47,6 +47,7 @@
<LI><A HREF="misc/API.html">The Apache API</A>
<LI><A HREF="suexec.html">Using SetUserID Execution for CGI</A>
<LI><A HREF="64-bit.html">Guide to 64-bit Apache</A>
+<LI><A HREF="qsc.html">The Quick Shortcut Cache</A>
</UL>
<H3><A NAME="oth">Other Notes</A></H3>
diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/core.html apache_1.3.6+01-08/htdocs/manual/mod/core.html
--- apache_1.3.6+01-07/htdocs/manual/mod/core.html Tue Jul 20 22:26:34 1999
+++ apache_1.3.6+01-08/htdocs/manual/mod/core.html Thu Sep 2 10:12:58 1999
@@ -75,6 +75,7 @@
<LI><A HREF="#options">Options</A>
<LI><A HREF="#pidfile">PidFile</A>
<LI><A HREF="#port">Port</A>
+<LI><A HREF="#qsc">QSC</A>
<LI><A HREF="#require">require</A>
<LI><A HREF="#resourceconfig">ResourceConfig</A>
<LI><A HREF="#rlimitcpu">RLimitCPU</A>
@@ -2349,6 +2350,34 @@
not to set <A HREF="#user">User</A> to root. If you run the server as
root whilst handling connections, your site may be open to a major security
attack.<P><HR>
+
+<H2><A NAME="qsc">QSC directive</A></H2>
+<!--%plaintext <?INDEX {\tt QSC} directive> -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> QSC <EM>on|off</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE><IfModule mod_mmap_static.c> QSC on </IfModule></CODE> <EM>else QSC off</EM><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> core<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> The QSC is only available in Apache
+1.3.X and later.
+<P>
+This directive enables or disables the <A HREF="../qsc.html">Quick Shortcut Cache</A>.
+<P>
+<HR>
<H2><A NAME="require">require directive</A></H2>
<!--%plaintext <?INDEX {\tt require} directive> -->
diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/directives.html apache_1.3.6+01-08/htdocs/manual/mod/directives.html
--- apache_1.3.6+01-07/htdocs/manual/mod/directives.html Tue Jul 20 22:27:13 1999
+++ apache_1.3.6+01-08/htdocs/manual/mod/directives.html Thu Sep 2 10:13:06 1999
@@ -167,6 +167,7 @@
<LI><A HREF="mod_proxy.html#proxyremote">ProxyRemote</A>
<LI><A HREF="mod_proxy.html#proxyrequests">ProxyRequests</A>
<LI><A HREF="mod_proxy.html#proxyvia">ProxyVia</A>
+<LI><A HREF="core.html#qsc">QSC</A>
<LI><A HREF="mod_autoindex.html#readmename">ReadmeName</A>
<LI><A HREF="mod_alias.html#redirect">Redirect</A>
<LI><A HREF="mod_alias.html#redirectmatch">RedirectMatch</A>
diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/mod_mmap_static.html apache_1.3.6+01-08/htdocs/manual/mod/mod_mmap_static.html
--- apache_1.3.6+01-07/htdocs/manual/mod/mod_mmap_static.html Thu Jul 8 12:05:38 1999
+++ apache_1.3.6+01-08/htdocs/manual/mod/mod_mmap_static.html Thu Sep 2 11:40:28 1999
@@ -31,6 +31,12 @@
</PRE>
</P>
+ <P>
+ Use of this module allows use of the Quick Shortcut (or Static-content)
+ Cache (QSC) for very fast static-content serving.
+ See the <A HREF="../qsc.html">QSC documentation</A> for details.
+ </P>
+
<H2>Summary</H2>
<P>
This is an <STRONG>experimental</STRONG> module and should be used with
diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/mod_status.html apache_1.3.6+01-08/htdocs/manual/mod/mod_status.html
--- apache_1.3.6+01-07/htdocs/manual/mod/mod_status.html Mon Mar 22 16:17:41 1999
+++ apache_1.3.6+01-08/htdocs/manual/mod/mod_status.html Thu Sep 2 10:13:26 1999
@@ -47,6 +47,8 @@
<LI>The current percentage CPU used by each child and in total by
Apache (*)
<LI>The current hosts and requests being processed (*)
+<LI><A HREF="../qsc.html#status">QSC statistics</A>, if the QSC is
+present and enabled.
</UL>
A compile-time option must be used to display the details marked "(*)" as
diff -Naur apache_1.3.6+01-07/htdocs/manual/qsc.html apache_1.3.6+01-08/htdocs/manual/qsc.html
--- apache_1.3.6+01-07/htdocs/manual/qsc.html
+++ apache_1.3.6+01-08/htdocs/manual/qsc.html Thu Sep 2 10:10:40 1999
@@ -0,0 +1,705 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+ <META NAME="Generator" CONTENT="Cosmo Create 1.0.3">
+ <TITLE>The Quick Shortcut Cache (QSC)</TITLE>
+</HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY ALINK="#ff0000" VLINK="#000080" LINK="#0000ff" BGCOLOR="#ffffff"
+ TEXT="#000000">
+<P>
+<DIV ALIGN="CENTER"><IMG SRC="images/sub.gif"
+ ALT="[APACHE DOCUMENTATION]"> </P>
+<H3>
+Apache HTTP Server Version 1.3 </H3>
+<P>
+</DIV></P>
+<CENTER><H1 ALIGN="CENTER">
+The Quick Shortcut Cache (QSC)</H1>
+</CENTER><CENTER><H2 ALIGN="CENTER">
+Also known as the Quick Static-content Cache</H2>
+</CENTER><CENTER><P ALIGN="CENTER">
+Mike Abbott - <A HREF="mailto:mja@sgi.com">mja@sgi.com</A></P>
+</CENTER><HR>
+<P>
+Apache/1.3.X and beyond support a very fast cache of static content and
+HTTP response headers known as the Quick Shortcut (or Static-content)
+Cache, or QSC. The QSC is meant for sites that serve lots of data as-is
+from disk, such as images, unparsed HTML, and plain text. Sites that
+serve mostly dynamically-generated content, such as CGI output or
+on-disk content with headers or footers generated on the fly, probably
+should not use the QSC.</P>
+<H2>
+Contents</H2>
+<UL>
+ <LI>
+ <A HREF="#primer">QSC Primer</A>
+ <LI>
+ <A HREF="#using">Using the QSC</A>
+ <LI>
+ <A HREF="#status">Monitoring the QSC</A>
+ <LI>
+ <A HREF="#advanced">Advanced Options</A>
+</UL>
+<H2>
+<A NAME="primer">QSC Primer</A></H2>
+<P>
+Normally Apache processes an HTTP request by following a long list of <A
+ HREF="misc/API.html#HMR">rules</A>, such as converting the URI to a
+file name, authenticating the request, generating HTTP headers for the
+response, sending the response, and logging information about the
+transaction. Apache performs all these steps (more or less) for every
+request, even if it has handled the same request previously. This
+memory-less behavior is required for steps such as authentication but
+is unnecessary for URI-to-file translation and HTTP response header
+generation when the response consists of static content. The QSC adds
+memory to Apache, allowing it to shortcut the processing of
+previously-seen requests for static content.</P>
+<H3>
+Operation</H3>
+<P>
+After Apache reads an HTTP request and locates the appropriate <A
+ HREF="vhosts/index.html">virtual host</A> context in which to handle
+the request, it checks whether the QSC can respond to the request --
+whether the request is <A HREF="#cachable">cachable</A> and whether the
+URI and virtual host match a cached entry. If so, the QSC bypasses all
+unnecessary processing and sends the previously-generated HTTP response
+quickly, then Apache logs the transaction and moves on to the next
+request.</P>
+<P>
+When the QSC cannot respond quickly, Apache continues processing the
+request normally. When such normal processing results in the <A
+ HREF="mod/mod_mmap_static.html">mmap_static module</A> sending the
+HTTP response, that module tries to insert the request and response
+into the QSC -- which succeeds only if the request and response are <A
+ HREF="#cachable">cachable</A> and the cache isn't <A HREF="#limit">full</A>.
+Finally, as with the cached response, Apache logs the transaction and
+moves on to the next request. Note that the QSC caches both the
+response headers and the response body.</P>
+<H3>
+Interaction with the mmap_static Module</H3>
+<P>
+Only the mmap_static module inserts entries into the QSC, for a number
+of reasons:</P>
+<UL>
+ <LI>
+ The mmap_static module keeps file contents mapped into memory so
+ sending the response body is simple and quick. (QSC entries point to
+ the memory-mapped file contents held by the mmap_static module; the QSC
+ sends this data as the HTTP response body.)
+ <LI>
+ The mmap_static module responds only to requests for static content.
+ <LI>
+ The mmap_static module responds only to requests for
+ explicitly-designated files. The administrator has full control.
+ <LI>
+ Both the QSC and the mmap_static module reset themselves upon server
+ restarts.
+ <LI>
+ Both the QSC and the mmap_static module share a sole purpose: to
+ increase performance.
+</UL>
+<H3>
+<A NAME="vhost">Virtual Hosts</A></H3>
+<P>
+When looking for a cache entry to satisfy a request, the QSC matches
+the virtual host as well as the URI because different virtual hosts can
+map the same URI to different files.</P>
+<H3>
+<A NAME="resheaders">Response Headers</A></H3>
+<P>
+Each QSC entry contains two nearly identical sets of HTTP response
+headers, one for keep-alive connections (the headers contain a <CODE>Connection:
+keep-alive</CODE> header) and one for non-keep-alive connections (the
+headers contain a <CODE>Connection: close</CODE> header). Caching both
+versions allows the QSC to respond quickly regardless of the nature of
+the connection and without having to generate the HTTP headers for each
+request -- a key ingredient for quick response.</P>
+<P>
+Furthermore, the QSC aligns both sets of response headers on a certain
+memory boundary and pads them out to a certain length. Generally this
+alignment is the secondary cache line size of the system on which
+Apache runs. When asked to send data out on a network, operating
+systems typically align misaligned data by copying it. The QSC
+pre-aligns the headers (and the mmap_static module automatically aligns
+the body due to the nature of memory-mapping files) and pads the
+headers (by adding spaces to the <CODE>Server</CODE> header value) to
+eliminate this overhead. You can <A HREF="#align">adjust or disable the
+header alignment</A> manually.</P>
+<H2>
+<A NAME="using">Using the QSC</A></H2>
+<P>
+This section describes how to compile and enable QSC support in your
+Apache server.</P>
+<P>
+The QSC requires the <A HREF="mod/mod_mmap_static.html">mmap_static
+module</A>. In the standard Apache distribution neither the QSC nor the
+mmap_static module are compiled into the server. You must add them both
+to the compilation configuration to use the QSC:</P>
+<PRE>
+ $ CFLAGS=-DUSE_QSC configure --enable-module=mmap_static
+ $ make
+</PRE>
+<P>
+(The QSC is an enhancement to core Apache, while mmap_static is an
+Apache <A HREF="mod/index.html">module</A>.) There are also advanced
+options to controlling QSC behavior, described <A HREF="#advanced">below</A>.</P>
+<P>
+In addition, a run-time configuration directive, <A
+ HREF="mod/core.html#qsc"><CODE>QSC</CODE></A>, enables and disables
+the QSC. By default the <CODE>QSC</CODE> is <CODE>off</CODE> (disabled)
+but this snippet from the standard httpd.conf file enables it when the
+mmap_static module is also compiled into the server:</P>
+<PRE>
+ <<A HREF="mod/core.html#ifmodule">IfModule</A> mod_mmap_static.c>
+ QSC on
+ </IfModule>
+</PRE>
+<P>
+In other words, compiling both the QSC and the mmap_static module
+automatically enables the QSC. The <CODE>QSC</CODE> directive exists to
+allow you to disable it.</P>
+<P>
+You must also configure the mmap_static module by adding an <A
+ HREF="mod/mod_mmap_static.html#mmapfile">mmapfile</A> directive for
+each file you want cached.</P>
+<P>
+Once configured the QSC will operate automatically.</P>
+<H3>
+Shared Memory</H3>
+<P>
+All of the data the QSC stores is in shared memory (memory accessible
+to all of Apache's subprocesses) so that the cache is not duplicated
+for each Apache child process. Systems that do not support anonymous
+shared memory (that define neither <CODE>HAVE_MMAP</CODE> nor <CODE>HAVE_SHMGET</CODE>)
+cannot use the QSC.</P>
+<H3>
+Atomic Compare-and-Swap</H3>
+<P>
+The QSC requires one piece of functionality that is completely new to
+Apache and so has not had the benefit of years of multi-platform
+porting: a way to compare and swap (<I>cas</I>) two values atomically
+(i.e., in a thread-safe manner). All of the QSC's internal data
+structures are stored in shared memory so every update to that data
+must be done in a way that is guaranteed to be safe and correct for all
+the child processes. If your attempt to compile the QSC fails with the
+error "need atomic compare-and-swap function," you must port
+the function <CODE>qsc_cas()</CODE> to your system.</P>
+<H2>
+<A NAME="status">Monitoring the QSC</A></H2>
+<P>
+The QSC gathers statistics about its operation. You can view these
+statistics on the page that the <A HREF="mod/mod_status.html">status
+module</A> generates in response to requests of the form:</P>
+<PRE>
+ http://your.server.name/server-status
+</PRE>
+<P>
+This section has examples and explanations of this information.</P>
+<P>
+This is what the status page looks like when the QSC is disabled:</P>
+<PRE>
+ Quick Shortcut Cache (QSC) Status:
+ QSC disabled
+</PRE>
+<P>
+There may be an explanation why the QSC is disabled in the server's
+error log. The next example shows the statistics from a freshly-started
+server with the QSC enabled:</P>
+<PRE>
+ Quick Shortcut Cache (QSC) Status:
+ hit ratio 0/1 (0.00%)
+ uncachable 1/1 (100.00%)
+ uncachable misses 1/1 (100.00%)
+ uncachable requests 0/1 (0.00%)
+ uncachable responses 0/1 (0.00%)
+ resets 1
+ Hash table
+ failed insertions 0
+ entries 0
+ duplicate entries 0
+ bucket use 0/32768 (0.00%)
+ hash effectiveness 0/0 (0.00%)
+ longest chain 0
+ avg. chain 0.0
+ avg. nonempty chain 0.0
+ Chain length histogram:
+ 1 2 3 4 5+
+ 0 0 0 0 0
+ Memory use (in bytes)
+ table + misc 131104
+ entries 0
+ URIs 0
+ headers 0
+ total 135264/5000000 (2.71%)
+ mapped file data 0
+ mapped file vaddrs 0 (0 16384-byte pages)
+</PRE>
+<P>
+It's pretty clear that the cache is empty at this point. The next
+example shows the statistics from the same server after running for a
+while:</P>
+<PRE>
+ Quick Shortcut Cache (QSC) Status:
+ hit ratio 2104749/2112853 (99.62%)
+ uncachable 40/2112853 (0.00%)
+ uncachable misses 40/8104 (0.49%)
+ uncachable requests 0/2112853 (0.00%)
+ uncachable responses 0/2112853 (0.00%)
+ resets 1
+ Hash table
+ failed insertions 0
+ entries 8064
+ duplicate entries 0
+ bucket use 7923/32768 (24.18%)
+ hash effectiveness 7923/8064 (98.25%)
+ longest chain 2
+ avg. chain 0.2
+ avg. nonempty chain 1.0
+ Chain length histogram:
+ 1 2 3 4 5+
+ 7782 141 0 0 0
+ Memory use (in bytes)
+ table + misc 131104
+ entries 322560
+ URIs 246024
+ headers 4128768
+ total 4844640/5000000 (96.89%)
+ mapped file data 1146761280
+ mapped file vaddrs 1229455360 (75040 16384-byte pages)
+</PRE>
+<P>
+The QSC computes some of the statistics (such as the hash chain
+lengths, histogram, and memory use) only when requested, and computing
+them frequently may interfere with normal server operation. You can
+view a condensed statistics page that skips the computation by
+appending <CODE>?qsc=quick</CODE> to your request, like this:</P>
+<PRE>
+ http://your.server.name/server-status?qsc=quick
+</PRE>
+<P>
+which produces this output:</P>
+<PRE>
+ Quick Shortcut Cache (QSC) Status:
+ hit ratio 2104749/2112854 (99.62%)
+ uncachable requests 0/2112854 (0.00%)
+ uncachable responses 0/2112854 (0.00%)
+ resets 1
+ Hash table
+ failed insertions 0
+</PRE>
+<P>
+Alternatively, you can view detailed QSC information by appending <CODE>?qsc=full</CODE>,
+like this:</P>
+<PRE>
+ http://your.server.name/server-status?qsc=full
+</PRE>
+<P>
+which produces this output (with a large portion omitted for brevity):</P>
+<PRE>
+ Quick Shortcut Cache (QSC) Status:
+ hit ratio 2104749/2112855 (99.62%)
+ uncachable 42/2112855 (0.00%)
+ uncachable misses 42/8106 (0.52%)
+ uncachable requests 0/2112855 (0.00%)
+ uncachable responses 0/2112855 (0.00%)
+ resets 1
+ Hash table
+ failed insertions 0
+ entries 8064
+ duplicate entries 0
+ bucket use 7923/32768 (24.18%)
+ hash effectiveness 7923/8064 (98.25%)
+ longest chain 2
+ avg. chain 0.2
+ avg. nonempty chain 1.0
+ Chain length histogram:
+ 1 2 3 4 5+
+ 7782 141 0 0 0
+ Memory use (in bytes)
+ table + misc 131104
+ entries 322560
+ URIs 246024
+ headers 4128768
+ total 4844640/5000000 (96.89%)
+ mapped file data 1146761280
+ mapped file vaddrs 1229455360 (75040 16384-byte pages)
+ Full entry info
+ server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name
+ main * /spec/file_set/dir115/class0_0 @ 37 -> 256;256 + 102 /a/htdocs/spec/file_set/dir115/class0_0
+ main * /spec/file_set/dir115/class0_1 @ 38 -> 256;256 + 204 /a/htdocs/spec/file_set/dir115/class0_1
+ main * /spec/file_set/dir115/class0_2 @ 39 -> 256;256 + 306 /a/htdocs/spec/file_set/dir115/class0_2
+ main * /spec/file_set/dir115/class0_3 @ 40 -> 256;256 + 408 /a/htdocs/spec/file_set/dir115/class0_3
+ <EM>... thousands of lines elided for brevity ...</EM>
+ main * /spec/file_set/dir214/class3_8 @ 32752 -> 256;256 + 921600 /a/htdocs/spec/file_set/dir214/class3_8
+</PRE>
+<H3>
+What Do the Statistics Mean?</H3>
+<P>
+This section explains the final example above in great detail.</P>
+<PRE>
+ hit ratio 2104749/2112855 (99.62%)
+</PRE>
+<P>
+The <EM>hit ratio</EM> is the ratio of the number of requests
+successfully served by the QSC to the total number of requests made to
+the server. The number in parentheses is the ratio expressed as a
+percentage. In this case there were 2,112,855 total requests, 2,104,749
+or 99.62% of which were cache hits -- the QSC responded to the requests
+quickly -- and 8,106 or 0.38% were cache misses -- Apache processed the
+requests without assistance from the QSC.</P>
+<PRE>
+ uncachable 42/2112855 (0.00%)
+ uncachable misses 42/8106 (0.52%)
+ uncachable requests 0/2112855 (0.00%)
+ uncachable responses 0/2112855 (0.00%)
+</PRE>
+<P>
+These explain the cache misses. Of the 2,112,855 total requests, 42
+were <EM>uncachable</EM> meaning that not only did they miss (were not
+in) the cache but also the QSC could not enter them into its cache for
+some reason. In this case, all 42 uncachable requests were <EM>uncachable
+misses</EM> meaning some handler other than the mmap_static module's
+handled the request. For instance, all server-status requests are
+handled by the status module and so are uncachable misses. (You can see
+the number of uncachable misses increasing by one for each example
+above.) Other reasons requests may be uncachable are <EM>uncachable
+requests</EM> and <EM>uncachable responses</EM>.</P>
+<P>
+<A NAME="cachable">The QSC caches</A> responses to HTTP requests only
+when both the request and the response meet certain criteria. To be
+cachable a request must:</P>
+<UL>
+ <LI>
+ be an HTTP GET request, <U>and</U>
+ <LI>
+ not have been marked uncachable by any module (e.g., <CODE>r->no_cache
+ == 0</CODE>), <U>and</U>
+ <LI>
+ not contain any of the following headers:
+ <UL>
+ <LI>
+ Authorization: ...
+ <LI>
+ Cache-Control: ...
+ <LI>
+ If-Modified-Since: ...
+ <LI>
+ If-None-Match: ...
+ <LI>
+ If-Range: ...
+ <LI>
+ Pragma: no-cache
+ <LI>
+ Range: ...
+ </UL>
+</UL>
+<P>
+and its response must:</P>
+<UL>
+ <LI>
+ have status 200 OK, <U>and</U>
+ <LI>
+ not be chunked, <U>and</U>
+ <LI>
+ not have been marked uncachable by any module (e.g., <CODE>r->no_cache
+ == 0</CODE>), <U>and</U>
+ <LI>
+ not contain any of the following headers:
+ <UL>
+ <LI>
+ Cache-Control: ...
+ <LI>
+ Content-Range: ...
+ <LI>
+ ETag: W/... (that is, strong etags are cachable, weaks ones are not)
+ <LI>
+ Expires: ...
+ <LI>
+ Pragma: no-cache
+ <LI>
+ Vary: ...
+ </UL>
+</UL>
+<P>
+For example, pressing the "Reload" button on some popular
+browsers causes them to issue requests with a <CODE>Pragma: no-cache</CODE>
+ and/or <CODE>Cache-control</CODE> header which are meant to bypass
+caching mechanisms such as the QSC.</P>
+<PRE>
+ resets 1
+</PRE>
+<P>
+The QSC counts the number of times it has been reset. The counter
+starts at zero but Apache's normal startup procedures cause one reset.
+Each time the server is restarted the QSC clears its cache completely,
+zeros-out all the statistics except this counter, and increments this
+counter. Thus, the statistics displayed are since the last reset, not
+since the server was started.</P>
+<PRE>
+ Hash table
+ failed insertions 0
+</PRE>
+<P>
+This is the number of times the QSC tried to insert a new entry into
+its cache and failed. Failure can occur when, for example, the QSC has
+consumed all the memory it is allowed to use (i.e., the cache is full).
+If you see a large number of failed insertions, consider increasing
+your QSC's <A HREF="#limit">cache size</A>.</P>
+<PRE>
+ entries 8064
+ duplicate entries 0
+</PRE>
+<P>
+This shows you how many entries are in the cache, and how many of those
+entries are duplicates of one another. Duplicate entries are harmless
+aside from wasting a little memory.</P>
+<PRE>
+ bucket use 7923/32768 (24.18%)
+ hash effectiveness 7923/8064 (98.25%)
+ longest chain 2
+ avg. chain 0.2
+ avg. nonempty chain 1.0
+ Chain length histogram:
+ 1 2 3 4 5+
+ 7782 141 0 0 0
+</PRE>
+<P>
+The above information describes the effectiveness of the QSC's hash
+algorithm. This particular instance has 32,768 cache buckets of which
+7,923 or 24.18% have at least one entry (the rest are empty). The
+effectiveness of the hash function is the ratio of the number of
+buckets over which entries are spread to the number of entries, in this
+case 7,923 to 8,064 or 98.25% effective. Higher effectiveness means
+shorter hash chains which are faster when looking up entries. The
+longest hash chain has only two entries which is very good. If your
+server shows a low hash efficiency and long hash chains, consider
+increasing your QSC's <A HREF="#limit">number of hash buckets</A>. The
+average (arithmetic mean) chain length is just 0.2 entries per bucket,
+including empty buckets, and the average chain length of non-empty
+buckets is 1.0 which is excellent. The histogram displays the number of
+hash buckets having chains with one, two, three, four, and five-or-more
+entries. You can control the <A HREF="#debug">number of histogram bins</A>.</P>
+<PRE>
+ Memory use (in bytes)
+ table + misc 131104
+</PRE>
+<P>
+The QSC carefully manages the amount of memory it uses and this part of
+the report explains where all the bytes are going. This line accounts
+for the empty hash table -- the size of which is directly related to
+the number of hash buckets -- and other data structures necessary for
+the QSC's operation such as the statistics counters. In this example
+there are 32,768 hash buckets each of which is four bytes in size so
+the whole table consumes 131,072 bytes. The remaining 32 bytes (for a
+total of 131,104) are for the statistics counters and other overhead.</P>
+<PRE>
+ entries 322560
+</PRE>
+<P>
+This line accounts for the memory used for the hash entry data
+structures. In this example each entry consumes 40 bytes and there are
+8,064 of them for a total of 322,560 bytes.</P>
+<PRE>
+ URIs 246024
+</PRE>
+<P>
+Each hash entry maps a URI and virtual host to HTTP response headers
+and data. This counts the amount of memory consumed by remembering
+those URIs. The average cached URI length in this example is 246,024
+bytes divided by 8,064 entries or about 31 bytes.</P>
+<PRE>
+ headers 4128768
+</PRE>
+<P>
+This is the number of bytes consumed by remembering the HTTP response
+headers for each cached entry. This is approximately double the number
+of bytes of header information sent in response to a cached entry
+because the QSC keeps <A HREF="#resheaders">two sets of headers</A>,
+one for keep-alive connections and one for non-keep-alive connections.
+The average number of bytes of cached headers is 4,128,768 bytes
+divided by 8,064 entries or exactly 512 bytes (two sets of 256-byte
+headers per entry). This number is so tidy because of <A HREF="#align">header
+padding and alignment</A>.</P>
+<PRE>
+ total 4844640/5000000 (96.89%)
+</PRE>
+<P>
+This line displays the total amount of memory that the QSC is using and
+the maximum amount to which it limits itself. In this case the cache is
+pretty close to full. You can <A HREF="#limit">control the maximum
+cache size</A>.</P>
+<PRE>
+ mapped file data 1146761280
+ mapped file vaddrs 1229455360 (75040 16384-byte pages)
+</PRE>
+<P>
+The QSC itself manages only the hash table, the URI strings, and the
+headers. The mmap_static module manages the cache of memory-mapped file
+contents. These two lines count the number of bytes of response body
+data (in this case 1,146,761,280 bytes for an average file size of
+142,208 bytes) and the number of bytes of virtual memory consumed
+(1,229,455,360 bytes). The latter is larger because memory-mapping a
+file whose size is not an exact multiple of the machine's page size
+wastes the space between the end of the file and the end of the page.
+In this case the machine's page size is 16 KB and 75,040 pages are used
+to map the file contents.</P>
+<PRE>
+ Full entry info
+ server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name
+ main * /spec/file_set/dir115/class0_0 @ 37 -> 256;256 + 102 /a/htdocs/spec/file_set/dir115/class0_0
+ main * /spec/file_set/dir115/class0_1 @ 38 -> 256;256 + 204 /a/htdocs/spec/file_set/dir115/class0_1
+ main * /spec/file_set/dir115/class0_2 @ 39 -> 256;256 + 306 /a/htdocs/spec/file_set/dir115/class0_2
+ main * /spec/file_set/dir115/class0_3 @ 40 -> 256;256 + 408 /a/htdocs/spec/file_set/dir115/class0_3
+ <EM>... thousands of lines elided for brevity ...</EM>
+ main * /spec/file_set/dir214/class3_8 @ 32752 -> 256;256 + 921600 /a/htdocs/spec/file_set/dir214/class3_8
+</PRE>
+<P>
+This final section, available using the <CODE>?qsc=full</CODE>
+ server-status extension, lists all of the information known about each
+cache entry. There were as many lines as there are cache entries so
+most of them were omitted for brevity. The following information is
+printed for each entry, as the list's header notes:</P>
+<TABLE BORDER="1">
+ <TR>
+ <TD>server</TD>
+ <TD>The file name and line number where the virtual server was
+ defined (for lack of better virtual host identification), or <CODE>main</CODE>
+ for the main server.</TD>
+ </TR>
+ <TR>
+ <TD>URI</TD>
+ <TD>The URI for which the response is cached.</TD>
+ </TR>
+ <TR>
+ <TD>hash-bucket</TD>
+ <TD>The ordinal of the bucket into which the URI hashes.</TD>
+ </TR>
+ <TR>
+ <TD>keep-alive-header-bytes</TD>
+ <TD>The number of bytes of HTTP response header cached for a
+ keep-alive response.</TD>
+ </TR>
+ <TR>
+ <TD>non-keep-alive-header-bytes</TD>
+ <TD>The number of bytes of HTTP response header cached for a
+ non-keep-alive (i.e., Connection: close) response.</TD>
+ </TR>
+ <TR>
+ <TD>body-bytes</TD>
+ <TD>The number of bytes of HTTP response body.</TD>
+ </TR>
+ <TR>
+ <TD>file-name</TD>
+ <TD>The name of the cached file, available only when the QSC is
+ compiled with <CODE>QSC_DEBUG</CODE> enabled. Without <CODE>QSC_DEBUG</CODE>
+ <CODE>n/a</CODE> is displayed ("not available").</TD>
+ </TR>
+</TABLE>
+<H2>
+<A NAME="advanced">Advanced Options</A></H2>
+<P>
+All of the following are compile-time options. The first two must be
+defined manually. The rest are defined in the QSC source code.</P>
+<TABLE BORDER="1" WIDTH="100%">
+ <TR>
+ <TD WIDTH="25%"><CODE>USE_QSC</CODE></TD>
+ <TD><P>
+ <A HREF="#using">Compiles the QSC into Apache</A>.</P>
+ Default: <CODE>USE_QSC</CODE> is not defined so the QSC is
+ disabled.</TD>
+ </TR>
+</TABLE>
+<H3>
+<A NAME="debug">Debugging</A> / Additional Information</H3>
+<TABLE BORDER="1" WIDTH="100%">
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_DEBUG</CODE></TD>
+ <TD><P>
+ Enables internal consistency checks.</P>
+ <P>
+ Makes the QSC keep track of the name of the file mapped by the
+ mmap_static module for each entry. The file name is displayed on the <A
+ HREF="#status">full status page</A>.</P>
+ Default: <CODE>QSC_DEBUG</CODE> is not defined so debugging
+ is disabled.</TD>
+ </TR>
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_HIST_SIZE</CODE></TD>
+ <TD><P>
+ Sets the number of hash bucket histogram bins the <A
+ HREF="#status">status page</A> displays.</P>
+ Default: 5.</TD>
+ </TR>
+</TABLE>
+<H3>
+<A NAME="limit">Size Restriction</A></H3>
+<TABLE BORDER="1" WIDTH="100%">
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_MAX_SIZE</CODE></TD>
+ <TD><P>
+ Sets the maximum number of bytes of memory the QSC will consume. Note
+ that this does not include mapped file data.</P>
+ Default: 4194304 (4 MB).</TD>
+ </TR>
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_HASH_SIZE</CODE></TD>
+ <TD><P>
+ Sets the number of hash buckets.</P>
+ Default: 128.</TD>
+ </TR>
+</TABLE>
+<H3>
+<A NAME="align">Header Alignment</A></H3>
+<TABLE BORDER="1" WIDTH="100%">
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_HEADER_GRAIN</CODE></TD>
+ <TD><P>
+ Sets both the virtual address alignment boundary of cached HTTP
+ response headers and the number of bytes to which the headers are
+ padded. For best performance make this equal to the size of the largest
+ cache line size on the system on which Apache runs. Must be a power of
+ two. The special value 0 disables alignment and padding.</P>
+ Default: system dependent (typically 32 or 128).</TD>
+ </TR>
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_GRAIN</CODE></TD>
+ <TD><P>
+ Sets both the virtual address alignment boundary of internal memory
+ allocations and the number of bytes to which the allocations are
+ padded. Must be a power of two at least as large as the larger of a
+ pointer and a long. Same idea as <CODE>CLICK_SZ</CODE> in
+ Apache's own memory management subsystem.</P>
+ Default: system dependent (typically 4 or 8).</TD>
+ </TR>
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_MAX_ALLOC</CODE></TD>
+ <TD><P>
+ Sets the maximum single-allocation size in bytes. Internal memory
+ allocations larger than this will fail and the request/response will
+ not be cached.</P>
+ Default: 512.</TD>
+ </TR>
+ <TR>
+ <TD WIDTH="25%"><CODE>QSC_RED_ZONE</CODE></TD>
+ <TD><P>
+ Sets the number of bytes of safety margin required between the two
+ internal memory allocation zones. Should be a small multiple of <CODE>QSC_MAX_ALLOC</CODE>
+ for safety.</P>
+ Default: 4096.</TD>
+ </TR>
+</TABLE>
+<H2>
+See also</H2>
+<P>
+<A HREF="misc/API.html">Apache API</A>, <A
+ HREF="mod/mod_mmap_static.html">mmap_static module</A>, <A
+ HREF="mod/mod_status.html">status module</A>, <A
+ HREF="vhosts/index.html">virtual hosts</A></P>
+<HR>
+<CENTER><H3 ALIGN="CENTER">
+Apache HTTP Server Version 1.3 </H3>
+</CENTER><P>
+<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> </P>
+</BODY>
+</HTML>
diff -Naur apache_1.3.6+01-07/htdocs/manual/vhosts/index.html apache_1.3.6+01-08/htdocs/manual/vhosts/index.html
--- apache_1.3.6+01-07/htdocs/manual/vhosts/index.html Mon Mar 22 16:17:46 1999
+++ apache_1.3.6+01-08/htdocs/manual/vhosts/index.html Thu Sep 2 10:13:33 1999
@@ -48,6 +48,7 @@
<LI><A HREF="details.html">In-Depth Discussion of Virtual Host Matching</A>
<LI><A HREF="fd-limits.html">File Descriptor Limits</A>
<LI><A HREF="mass.html">Dynamically Configured Mass Virtual Hosting with mod_rewrite</A>
+<LI><A HREF="../qsc.html#vhost">QSC Interaction with Virtual Hosts</A>
</UL>
<H2>Configuration directives</H2>
diff -Naur apache_1.3.6+01-07/src/include/ap_config.h apache_1.3.6+01-08/src/include/ap_config.h
--- apache_1.3.6+01-07/src/include/ap_config.h Tue Jul 20 22:58:29 1999
+++ apache_1.3.6+01-08/src/include/ap_config.h Thu Sep 2 10:14:17 1999
@@ -1028,7 +1028,7 @@
int setrlimit(int, struct rlimit *);
#endif
#endif
-#ifdef USE_MMAP_SCOREBOARD
+#ifdef HAVE_MMAP
#if !defined(OS2) && !defined(WIN32)
/* This file is not needed for OS/2 */
#include <sys/mman.h>
@@ -1038,6 +1038,12 @@
#define MAP_ANON MAP_ANONYMOUS
#endif
+#ifdef HAVE_SHMGET
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
#if defined(USE_MMAP_FILES) && (defined(NO_MMAP) || !defined(HAVE_MMAP))
#undef USE_MMAP_FILES
#endif
@@ -1202,6 +1208,14 @@
*/
#if defined(__WCOREDUMP) && !defined(WCOREDUMP)
#define WCOREDUMP __WCOREDUMP
+#endif
+
+/*
+ * Maximum number of bytes to write from an mmap'ed region per write().
+ * See ap_send_mmap() and qsc_process_request().
+ */
+#ifndef MMAP_SEGMENT_SIZE
+#define MMAP_SEGMENT_SIZE 32768
#endif
/*
diff -Naur apache_1.3.6+01-07/src/include/buff.h apache_1.3.6+01-08/src/include/buff.h
--- apache_1.3.6+01-07/src/include/buff.h Thu Jul 15 14:05:15 1999
+++ apache_1.3.6+01-08/src/include/buff.h Thu Sep 2 10:14:23 1999
@@ -175,6 +175,7 @@
API_EXPORT(int) ap_blookc(char *buff, BUFF *fb);
API_EXPORT(int) ap_bskiplf(BUFF *fb);
API_EXPORT(int) ap_bwrite(BUFF *fb, const void *buf, int nbyte);
+API_EXPORT(int) ap_bwritev(BUFF *fb, struct iovec *, int);
API_EXPORT(int) ap_bflush(BUFF *fb);
API_EXPORT(int) ap_bputs(const char *x, BUFF *fb);
API_EXPORT(int) ap_bvputs(BUFF *fb,...);
diff -Naur apache_1.3.6+01-07/src/include/http_conf_globals.h apache_1.3.6+01-08/src/include/http_conf_globals.h
--- apache_1.3.6+01-07/src/include/http_conf_globals.h Tue Jul 20 22:30:01 1999
+++ apache_1.3.6+01-08/src/include/http_conf_globals.h Thu Sep 2 10:14:28 1999
@@ -86,6 +86,9 @@
extern int ap_listenbacklog;
extern int ap_dump_settings;
extern API_VAR_EXPORT int ap_extended_status;
+#ifdef USE_QSC
+extern int ap_qsc_enabled;
+#endif
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
extern int ap_single_listen;
diff -Naur apache_1.3.6+01-07/src/include/http_main.h apache_1.3.6+01-08/src/include/http_main.h
--- apache_1.3.6+01-07/src/include/http_main.h Fri Jan 1 11:04:40 1999
+++ apache_1.3.6+01-08/src/include/http_main.h Thu Sep 2 10:14:33 1999
@@ -127,6 +127,13 @@
unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x);
API_EXPORT(int) ap_check_alarm(void);
+#ifdef HAVE_MMAP
+API_EXPORT(caddr_t) ap_map_anonymous(pool *p, caddr_t addr, size_t size);
+#endif
+#ifdef HAVE_SHMGET
+API_EXPORT(caddr_t) ap_share_anonymous(pool *p, caddr_t addr, size_t size);
+#endif
+
#ifndef NO_OTHER_CHILD
/*
* register an other_child -- a child which the main loop keeps track of
diff -Naur apache_1.3.6+01-07/src/include/http_protocol.h apache_1.3.6+01-08/src/include/http_protocol.h
--- apache_1.3.6+01-07/src/include/http_protocol.h Thu Jul 8 12:12:57 1999
+++ apache_1.3.6+01-08/src/include/http_protocol.h Thu Sep 2 10:14:39 1999
@@ -82,10 +82,12 @@
* ap_send_http_header().
*/
API_EXPORT(void) ap_basic_http_header(request_rec *r);
+API_EXPORT(void) ap_tee_basic_http_header(request_rec *r, char *, size_t *);
/* Send the Status-Line and header fields for HTTP response */
API_EXPORT(void) ap_send_http_header(request_rec *l);
+API_EXPORT(void) ap_tee_http_header(request_rec *l, char *, size_t *);
/* Send the response to special method requests */
@@ -139,6 +141,8 @@
API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
size_t length);
+
+API_EXPORT(size_t) ap_send_iovec(request_rec *, struct iovec *, int);
/* Hmmm... could macrofy these for now, and maybe forever, though the
* definitions of the macros would get a whole lot hairier.
diff -Naur apache_1.3.6+01-07/src/include/httpd.h apache_1.3.6+01-08/src/include/httpd.h
--- apache_1.3.6+01-07/src/include/httpd.h Tue Jul 20 22:31:15 1999
+++ apache_1.3.6+01-08/src/include/httpd.h Thu Sep 2 10:14:46 1999
@@ -68,6 +68,7 @@
/* Mike Abbott - mja@sgi.com */
#ifdef SPEED_DAEMON /* that's me, a real speed demon */
+# define USE_QSC /* enable the Quick Shortcut Cache */
# define FAST_TIME /* eliminate redundant calls to time() */
# define BUFFERED_LOGS /* buffer log messages */
# define NO_GRACEFUL /* eliminate per-request signal manipulation */
@@ -1147,6 +1148,15 @@
#undef strtoul
#endif
#define strtoul strtoul_is_not_a_portable_function_use_strtol_instead
+
+#ifdef USE_QSC
+/* QSC - Quick Shortcut Cache */
+extern void qsc_init(pool *);
+extern int qsc_process_request(request_rec *);
+extern void qsc_insert_request(request_rec *, const char *, size_t, void *,
+ size_t, const char *);
+extern void qsc_status(request_rec *, const char *);
+#endif
/*
* 64-bit porting aides. On 64-bit Irix, strlen() returns size_t which
diff -Naur apache_1.3.6+01-07/src/include/scoreboard.h apache_1.3.6+01-08/src/include/scoreboard.h
--- apache_1.3.6+01-07/src/include/scoreboard.h Tue Jul 20 22:32:03 1999
+++ apache_1.3.6+01-08/src/include/scoreboard.h Thu Sep 2 10:14:49 1999
@@ -163,6 +163,12 @@
int cpu; /* cpu to which it is bound */
struct sockaddr_in single_addr; /* if ap_single_listen, listen addr */
#endif
+#ifdef USE_QSC
+ ap_atomic qsc_nucreq; /* uncachable requests */
+ ap_atomic qsc_nucres; /* uncachable responses */
+ ap_atomic qsc_nhits; /* successful qsc lookups */
+ ap_atomic qsc_nmisses; /* unsuccessful qsc lookups */
+#endif
} short_score;
typedef struct {
diff -Naur apache_1.3.6+01-07/src/main/Makefile.tmpl apache_1.3.6+01-08/src/main/Makefile.tmpl
--- apache_1.3.6+01-07/src/main/Makefile.tmpl Mon Jul 19 16:24:16 1999
+++ apache_1.3.6+01-08/src/main/Makefile.tmpl Thu Sep 2 10:14:56 1999
@@ -11,7 +11,7 @@
http_config.o http_core.o http_log.o \
http_main.o http_protocol.o http_request.o http_vhost.o \
util.o util_date.o util_script.o util_uri.o util_md5.o \
- rfc1413.o
+ rfc1413.o qsc.o
.c.o:
$(CC) -c $(INCLUDES) $(CFLAGS) $<
@@ -139,6 +139,14 @@
$(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \
$(INCDIR)/http_log.h $(INCDIR)/http_vhost.h \
$(INCDIR)/http_protocol.h
+qsc.o: qsc.c $(INCDIR)/httpd.h $(INCDIR)/ap_types.h \
+ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
+ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h $(OSDIR)/os-inline.c \
+ $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \
+ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \
+ $(INCDIR)/http_log.h $(INCDIR)/http_config.h \
+ $(INCDIR)/http_conf_globals.h $(INCDIR)/http_protocol.h \
+ $(INCDIR)/http_main.h $(INCDIR)/scoreboard.h
rfc1413.o: rfc1413.c $(INCDIR)/httpd.h $(INCDIR)/ap_types.h \
$(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
$(INCDIR)/ap_config_auto.h $(OSDIR)/os.h $(OSDIR)/os-inline.c \
diff -Naur apache_1.3.6+01-07/src/main/buff.c apache_1.3.6+01-08/src/main/buff.c
--- apache_1.3.6+01-07/src/main/buff.c Thu Jul 15 14:05:15 1999
+++ apache_1.3.6+01-08/src/main/buff.c Thu Sep 2 10:17:06 1999
@@ -1029,43 +1029,93 @@
*/
static int writev_it_all(BUFF *fb, struct iovec *vec, int nvec)
{
- int i, rv;
+ return (ap_bwritev(fb, vec, nvec) >= 0) ? 0 : -1;
+}
+#endif
+
+/*
+ * Write data using writev() if available, write() otherwise.
+ * Returns number of bytes written or -1 on error.
+ * Note that it may modify iov;
+ */
+API_EXPORT(int)
+ap_bwritev(BUFF *fb, struct iovec *iov, int iovcnt)
+{
+ int rval;
+
+ if ((fb->flags & (B_EOUT | B_WRERR | B_WR)) == B_WR) {
+#ifndef NO_WRITEV
+ int i, len;
+
+ rval = 0;
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++)
+ len += iov[i].iov_len;
+
+ while (len > 0) {
+ int w;
+
+ w = (iovcnt > 1) ? (int) writev(fb->fd, iov, iovcnt) :
+ (int) write(fb->fd, iov->iov_base, iov->iov_len);
+ if (w >= 0) {
+ rval += w;
+ fb->bytes_sent += w;
+
+ len -= w;
+ if (len == 0)
+ break; /* shortcut the common case */
- /* while it's nice an easy to build the vector and crud, it's painful
- * to deal with a partial writev()
- */
- i = 0;
- while (i < nvec) {
- do
- rv = (int) writev(fb->fd, &vec[i], nvec - i);
- while (rv == -1 && (errno == EINTR || errno == EAGAIN)
- && !(fb->flags & B_EOUT));
- if (rv == -1) {
- if (errno != EINTR && errno != EAGAIN) {
+ while (w > iov[0].iov_len) {
+ w -= iov[0].iov_len;
+ iov++;
+ iovcnt--;
+ }
+ iov[0].iov_base = (char *) iov[0].iov_base + w;
+ iov[0].iov_len -= w;
+ } else if (errno != EINTR && errno != EAGAIN) {
doerror(fb, B_WR);
+ rval = -1;
+ break;
}
- return -1;
- }
- fb->bytes_sent += rv;
- /* recalculate vec to deal with partial writes */
- while (rv > 0) {
- if (rv < vec[i].iov_len) {
- vec[i].iov_base = (char *) vec[i].iov_base + rv;
- vec[i].iov_len -= rv;
- rv = 0;
+
+ if (fb->flags & B_EOUT) { /* set asynchronously */
+ rval = -1;
+ break;
}
- else {
- rv -= vec[i].iov_len;
- ++i;
+ }
+#else
+ int i;
+
+ rval = 0;
+ for (i = 0; i < iovcnt; i++) {
+ while (iov[i].iov_len > 0) {
+ int w;
+
+ w = write(fb->fd, iov[i].iov_base, iov[i].iov_len);
+ if (w >= 0) {
+ rval += w;
+ fb->bytes_sent += w;
+ iov[i].iov_base = (char *) iov[i].iov_base + w;
+ iov[i].iov_len -= w;
+ } else if (errno != EINTR && errno != EAGAIN) {
+ doerror(fb, B_WR);
+ rval = -1;
+ break;
+ }
+
+ if (fb->flags & B_EOUT) { /* set asynchronously */
+ rval = -1;
+ break;
+ }
}
}
- if (fb->flags & B_EOUT)
- return -1;
- }
- /* if we got here, we wrote it all */
- return 0;
-}
#endif
+ } else
+ rval = -1;
+
+ return rval;
+}
/* A wrapper for buff_write which deals with error conditions and
* bytes_sent. Also handles non-blocking writes.
diff -Naur apache_1.3.6+01-07/src/main/http_config.c apache_1.3.6+01-08/src/main/http_config.c
--- apache_1.3.6+01-07/src/main/http_config.c Tue Jul 20 22:33:40 1999
+++ apache_1.3.6+01-08/src/main/http_config.c Thu Sep 2 10:17:11 1999
@@ -1399,6 +1399,9 @@
ap_listeners = NULL;
ap_listenbacklog = DEFAULT_LISTENBACKLOG;
ap_extended_status = 0;
+#ifdef USE_QSC
+ ap_qsc_enabled = 0;
+#endif
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
ap_single_listen = 0;
#endif
diff -Naur apache_1.3.6+01-07/src/main/http_core.c apache_1.3.6+01-08/src/main/http_core.c
--- apache_1.3.6+01-07/src/main/http_core.c Tue Jul 20 23:29:49 1999
+++ apache_1.3.6+01-08/src/main/http_core.c Thu Sep 2 10:17:24 1999
@@ -2698,7 +2698,7 @@
return NULL;
}
-#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+#if defined(USE_QSC) || defined(SINGLE_LISTEN_UNSERIALIZED_ACCEPT)
static int
set_binary(const char *how, int *var)
{
@@ -2720,6 +2720,20 @@
#endif
/*ARGSUSED1*/
+static const char *set_qsc_enabled(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err;
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+#ifdef USE_QSC
+ if (err == NULL && !set_binary(arg, &ap_qsc_enabled))
+ err = "Unknown QSC control; try \"on\" or \"off\"";
+#endif
+
+ return err;
+}
+
+/*ARGSUSED1*/
static const char *set_single_listen(cmd_parms *cmd, void *dummy, char *arg)
{
const char *err;
@@ -2973,6 +2987,13 @@
(void*)XtOffsetOf(core_dir_config, limit_req_body),
OR_ALL, TAKE1,
"Limit (in bytes) on maximum size of request message body" },
+{ "QSC", set_qsc_enabled, NULL, RSRC_CONF, TAKE1,
+#ifdef USE_QSC
+ "Enable/disable Quick Shortcut Cache"
+#else
+ "Ignored because !USE_QSC"
+#endif
+},
{ "SingleListen", set_single_listen, NULL, RSRC_CONF, TAKE1,
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
"Force each child to serve a single Listen address"
diff -Naur apache_1.3.6+01-07/src/main/http_main.c apache_1.3.6+01-08/src/main/http_main.c
--- apache_1.3.6+01-07/src/main/http_main.c Tue Jul 20 22:40:59 1999
+++ apache_1.3.6+01-08/src/main/http_main.c Thu Sep 2 10:18:50 1999
@@ -104,11 +104,6 @@
#include "scoreboard.h"
#include "multithread.h"
#include <sys/stat.h>
-#ifdef USE_SHMGET_SCOREBOARD
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#endif
#ifdef SecureWare
#include <sys/security.h>
#include <sys/audit.h>
@@ -249,6 +244,9 @@
int ap_listenbacklog;
int ap_dump_settings = 0;
API_VAR_EXPORT int ap_extended_status = 0;
+#ifdef USE_QSC
+int ap_qsc_enabled;
+#endif
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
int ap_single_listen;
#endif
@@ -1608,6 +1606,146 @@
}
#endif
+#ifdef HAVE_MMAP
+/*ARGSUSED*/
+caddr_t
+ap_map_anonymous(pool *p, caddr_t addr, size_t size)
+{
+ caddr_t m = (caddr_t) (ap_ptr) -1;
+
+#ifdef MAP_ANON
+/* BSD style */
+#ifdef CONVEXOS11
+ {
+ unsigned int len = size;
+
+ m = mmap(addr, &len, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,
+ NOFD, 0);
+ if (m == (caddr_t) (ap_ptr) -1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+ }
+ }
+#elif defined(MAP_TMPFILE)
+ {
+ char *mfile = ap_pstrdup(p, "/tmp/apache_shmem_XXXX");
+ int fd = mkstemp(mfile);
+ if (fd >= 0) {
+ m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) (ap_ptr) -1) {
+ perror(mfile);
+ fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile);
+ }
+ close(fd);
+ } else {
+ perror(mfile);
+ fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile);
+ }
+ unlink(mfile);
+ }
+#else
+ m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+ if (m == (caddr_t) (ap_ptr) -1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+ }
+#endif
+#else
+/* Sun style */
+ int fd = open("/dev/zero", O_RDWR);
+ if (fd >= 0) {
+ m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) (ap_ptr) -1) {
+ perror("/dev/zero");
+ fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+ }
+ close(fd);
+ } else {
+ perror("/dev/zero");
+ fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0);
+ }
+#endif
+
+ return m;
+}
+#endif
+
+#ifdef HAVE_SHMGET
+/*ARGSUSED*/
+caddr_t
+ap_share_anonymous(pool *p, caddr_t addr, size_t size)
+{
+ caddr_t m = (caddr_t) (ap_ptr) -1;
+ int shmid;
+
+ shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_R | SHM_W);
+ if (shmid >= 0) {
+#ifdef MOVEBREAK
+ void *obrk;
+#endif
+
+ ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, server_conf,
+ "created shared memory segment #%d", shmid);
+
+#ifdef MOVEBREAK
+ /*
+ * Some SysV systems place the shared segment WAY too close
+ * to the dynamic memory break point (sbrk(0)). This severely
+ * limits the use of malloc/sbrk in the program since sbrk will
+ * refuse to move past that point.
+ *
+ * To get around this, we move the break point "way up there",
+ * attach the segment and then move break back down. Ugly
+ */
+ if ((obrk = sbrk(MOVEBREAK)) == (void *) (ap_ptr) -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not move break");
+ }
+#endif
+
+ m = shmat(shmid, addr, 0);
+ if (m != (caddr_t) (ap_ptr) -1) {
+ struct shmid_ds shmbuf;
+
+ if (shmctl(shmid, IPC_STAT, &shmbuf) == 0) {
+ shmbuf.shm_perm.uid = ap_user_id;
+ shmbuf.shm_perm.gid = ap_group_id;
+ if (shmctl(shmid, IPC_SET, &shmbuf) < 0)
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl(%d, IPC_SET, uid %d gid %d)", shmid,
+ ap_user_id, ap_group_id);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl(%d, IPC_STAT)", shmid);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "shmat(%d, %p)",
+ shmid, addr);
+
+ if (shmctl(shmid, IPC_RMID, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "shmctl(%d, IPC_RMID)", shmid);
+
+#ifdef MOVEBREAK
+ if (obrk != (void *) (ap_ptr) -1 && sbrk(-(MOVEBREAK)) == (void *) (ap_ptr) -1)
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not restore break");
+#endif
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmget(IPC_PRIVATE, %ld)", (long) size);
+#ifdef LINUX
+ if (errno == ENOSYS)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Your kernel was built without CONFIG_SYSVIPC\n"
+ "%s: Please consult the Apache FAQ for details",
+ ap_server_argv0);
+#endif
+ }
+
+ return m;
+}
+#endif
+
/*****************************************************************
*
* Dealing with the scoreboard... a lot of these variables are global
@@ -1815,8 +1953,6 @@
{
caddr_t m;
-#if defined(MAP_ANON)
-/* BSD style */
#ifdef CONVEXOS11
/*
* 9-Aug-97 - Jeff Venters (venters@convex.hp.com)
@@ -1828,59 +1964,14 @@
* Also, the length requires a pointer as the actual length is
* returned (rounded up to a page boundary).
*/
- {
- unsigned len = SCOREBOARD_SIZE;
-
- m = mmap((caddr_t) 0xC0000000, &len,
- PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0);
- }
-#elif defined(MAP_TMPFILE)
- {
- char mfile[] = "/tmp/apache_shmem_XXXX";
- int fd = mkstemp(mfile);
- if (fd == -1) {
- perror("open");
- fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile);
- exit(APEXIT_INIT);
- }
- m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (m == (caddr_t) - 1) {
- perror("mmap");
- fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile);
- exit(APEXIT_INIT);
- }
- close(fd);
- unlink(mfile);
- }
+ m = ap_map_anonymous(p, 0xC0000000, SCOREBOARD_SIZE);
#else
- m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
- PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+ m = ap_map_anonymous(p, 0, SCOREBOARD_SIZE);
#endif
- if (m == (caddr_t) - 1) {
- perror("mmap");
- fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
- exit(APEXIT_INIT);
- }
-#else
-/* Sun style */
- int fd;
- fd = open("/dev/zero", O_RDWR);
- if (fd == -1) {
- perror("open");
- fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0);
+ if (m == (caddr_t) (ap_ptr) -1)
exit(APEXIT_INIT);
- }
- m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (m == (caddr_t) - 1) {
- perror("mmap");
- fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0);
- exit(APEXIT_INIT);
- }
- close(fd);
-#endif
+
ap_scoreboard_image = (scoreboard *) m;
ap_scoreboard_image->global.running_generation = 0;
}
@@ -1891,91 +1982,15 @@
}
#elif defined(USE_SHMGET_SCOREBOARD)
-static key_t shmkey = IPC_PRIVATE;
-static int shmid = -1;
-
-/*ARGSUSED*/
static void setup_shared_mem(pool *p)
{
- struct shmid_ds shmbuf;
-#ifdef MOVEBREAK
- char *obrk;
-#endif
-
- if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) {
-#ifdef LINUX
- if (errno == ENOSYS) {
- ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
- "Your kernel was built without CONFIG_SYSVIPC\n"
- "%s: Please consult the Apache FAQ for details",
- ap_server_argv0);
- }
-#endif
- ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
- "could not call shmget");
- exit(APEXIT_INIT);
- }
-
- ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
- "created shared memory segment #%d", shmid);
-
-#ifdef MOVEBREAK
- /*
- * Some SysV systems place the shared segment WAY too close
- * to the dynamic memory break point (sbrk(0)). This severely
- * limits the use of malloc/sbrk in the program since sbrk will
- * refuse to move past that point.
- *
- * To get around this, we move the break point "way up there",
- * attach the segment and then move break back down. Ugly
- */
- if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
- ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
- "sbrk() could not move break");
- }
-#endif
+ caddr_t m;
-#define BADSHMAT ((scoreboard *) (ap_ptr) (-1))
- if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error");
- /*
- * We exit below, after we try to remove the segment
- */
- }
- else { /* only worry about permissions if we attached the segment */
- if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
- "shmctl() could not stat segment #%d", shmid);
- }
- else {
- shmbuf.shm_perm.uid = ap_user_id;
- shmbuf.shm_perm.gid = ap_group_id;
- if (shmctl(shmid, IPC_SET, &shmbuf) != 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
- "shmctl() could not set segment #%d", shmid);
- }
- }
- }
- /*
- * We must avoid leaving segments in the kernel's
- * (small) tables.
- */
- if (shmctl(shmid, IPC_RMID, NULL) != 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
- "shmctl: IPC_RMID: could not remove shared memory segment #%d",
- shmid);
- }
- if (ap_scoreboard_image == BADSHMAT) /* now bailout */
+ m = ap_share_anonymous(p, 0, SCOREBOARD_SIZE);
+ if (m == (caddr_t) (ap_ptr) -1)
exit(APEXIT_INIT);
-#ifdef MOVEBREAK
- if (obrk == (char *) -1)
- return; /* nothing else to do */
- if (sbrk(-(MOVEBREAK)) == (char *) -1) {
- ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
- "sbrk() could not move break back");
- }
-#endif
+ ap_scoreboard_image = (scoreboard *) m;
ap_scoreboard_image->global.running_generation = 0;
}
@@ -3524,6 +3539,26 @@
#ifdef NO_RELIABLE_PIPED_LOGS
printf(" -D NO_RELIABLE_PIPED_LOGS\n");
#endif
+#ifdef USE_QSC
+ printf(" -D USE_QSC\n");
+# ifdef QSC_HASH_SIZE
+ printf(" -D QSC_HASH_SIZE=%d\n", QSC_HASH_SIZE);
+# endif
+# ifdef QSC_MAX_SIZE
+ printf(" -D QSC_MAX_SIZE=%lu\n", (unsigned long) QSC_MAX_SIZE);
+# endif
+# ifdef QSC_HEADER_GRAIN
+ printf(" -D QSC_HEADER_GRAIN=%d\n", QSC_HEADER_GRAIN);
+# endif
+# if !defined(QSC_HEADER_GRAIN) || QSC_HEADER_GRAIN > 0
+# ifdef QSC_MAX_ALLOC
+ printf(" -D QSC_MAX_ALLOC=%d\n", QSC_MAX_ALLOC);
+# endif
+# ifdef QSC_RED_ZONE
+ printf(" -D QSC_RED_ZONE=%d\n", QSC_RED_ZONE);
+# endif
+# endif
+#endif
#ifdef FAST_TIME
printf(" -D FAST_TIME\n");
#endif
@@ -4760,6 +4795,10 @@
exit(0);
}
+#ifdef USE_QSC
+ qsc_init(pconf);
+#endif
+
child_timeouts = !ap_standalone || one_process;
if (ap_standalone) {
@@ -6138,6 +6177,11 @@
if (!child && !ap_dump_settings && !install) {
ap_log_pid(pconf, ap_pid_fname);
}
+
+#ifdef USE_QSC
+ qsc_init(pconf);
+#endif
+
ap_set_version();
ap_init_modules(pconf, server_conf);
ap_suexec_enabled = init_suexec();
diff -Naur apache_1.3.6+01-07/src/main/http_protocol.c apache_1.3.6+01-08/src/main/http_protocol.c
--- apache_1.3.6+01-07/src/main/http_protocol.c Tue Jul 20 16:29:55 1999
+++ apache_1.3.6+01-08/src/main/http_protocol.c Thu Sep 2 10:26:41 1999
@@ -1530,10 +1530,18 @@
API_EXPORT(void) ap_basic_http_header(request_rec *r)
{
+ ap_tee_basic_http_header(r, NULL, NULL);
+}
+
+API_EXPORT(void) ap_tee_basic_http_header(request_rec *r,
+ char *basic_header_copy, size_t *basic_header_copy_remain)
+{
char *protocol;
#ifdef CHARSET_EBCDIC
int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
#endif /*CHARSET_EBCDIC*/
+ const char *cp;
+ int len;
if (r->assbackwards)
return;
@@ -1560,10 +1568,44 @@
/* Output the HTTP/1.x Status-Line and the Date and Server fields */
+ /*
+ * The whole basic_header_copy* thing is a hack to include the basic
+ * http headers in the QSC entry for mod_mmap_static. Everything
+ * else uses the headers_out table, but noooo, this function has to
+ * emit headers directly, and since they may be flushed at any point
+ * we must copy them aside for later insertion into the QSC.
+ */
ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL);
-
- ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time));
- ap_send_header_field(r, "Server", ap_get_server_version());
+ if (basic_header_copy) {
+ len = ap_snprintf(basic_header_copy, *basic_header_copy_remain,
+ "%s %s\015\012", protocol, r->status_line);
+ basic_header_copy += len;
+ *basic_header_copy_remain -= len;
+ }
+
+ cp = ap_gm_timestr_822(r->pool, r->request_time);
+ ap_send_header_field(r, "Date", cp);
+ if (basic_header_copy) {
+ len = ap_snprintf(basic_header_copy, *basic_header_copy_remain,
+ "Date: %s\015\012", cp);
+ basic_header_copy += len;
+ *basic_header_copy_remain -= len;
+ }
+
+ cp = ap_get_server_version();
+ ap_send_header_field(r, "Server", cp);
+ if (basic_header_copy) {
+ len = ap_snprintf(basic_header_copy, *basic_header_copy_remain,
+ "Server: %s\015\012", cp);
+ basic_header_copy += len;
+ *basic_header_copy_remain -= len;
+ }
+ /*
+ * NOTE: if you change the basic header such that the Server
+ * response-header is not at the end or its value is other than
+ * ap_get_server_version(), update the most-likely-place probe in
+ * qsc_insert_request().
+ */
ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
ap_table_unset(r->headers_out, "Server");
@@ -1696,6 +1738,12 @@
API_EXPORT(void) ap_send_http_header(request_rec *r)
{
+ ap_tee_http_header(r, NULL, NULL);
+}
+
+API_EXPORT(void) ap_tee_http_header(request_rec *r, char *basic_header_copy,
+ size_t *basic_header_copy_remain)
+{
int i;
const ap_int32 zero = 0;
#ifdef CHARSET_EBCDIC
@@ -1720,7 +1768,7 @@
ap_hard_timeout("send headers", r);
- ap_basic_http_header(r);
+ ap_tee_basic_http_header(r, basic_header_copy, basic_header_copy_remain);
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
@@ -2320,9 +2368,6 @@
* To take advantage of zero-copy TCP under Solaris 2.6 this should be a
* multiple of 16k. (And you need a SunATM2.0 network card.)
*/
-#ifndef MMAP_SEGMENT_SIZE
-#define MMAP_SEGMENT_SIZE 32768
-#endif
/* send data from an in-memory buffer */
API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
@@ -2368,6 +2413,36 @@
ap_kill_timeout(r);
SET_BYTES_SENT(r);
return total_bytes_sent;
+}
+
+API_EXPORT(size_t)
+ap_send_iovec(request_rec *r, struct iovec *iov, int iovcnt)
+{
+ size_t sent;
+
+ sent = 0;
+ ap_soft_timeout("send iovec", r);
+ if (!r->connection->aborted) {
+ int w;
+
+ /*
+ * No need to loop. ap_bwritev writes it all unless there was
+ * an error.
+ */
+ w = ap_bwritev(r->connection->client, iov, iovcnt);
+ if (w > 0)
+ sent += w;
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before send iovec completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ }
+ ap_kill_timeout(r);
+ SET_BYTES_SENT(r);
+
+ return sent;
}
API_EXPORT(int) ap_rputc(int c, request_rec *r)
diff -Naur apache_1.3.6+01-07/src/main/http_request.c apache_1.3.6+01-08/src/main/http_request.c
--- apache_1.3.6+01-07/src/main/http_request.c Thu Jul 15 10:30:03 1999
+++ apache_1.3.6+01-08/src/main/http_request.c Thu Sep 2 11:40:06 1999
@@ -1225,7 +1225,10 @@
if (ap_extended_status)
ap_time_process_request(r->connection->child_num, START_PREQUEST);
- process_request_internal(r);
+#ifdef USE_QSC
+ if (!qsc_process_request(r))
+#endif
+ process_request_internal(r);
old_stat = ap_update_child_status(r->connection->child_num,
SERVER_BUSY_LOG, r);
diff -Naur apache_1.3.6+01-07/src/main/qsc.c apache_1.3.6+01-08/src/main/qsc.c
--- apache_1.3.6+01-07/src/main/qsc.c
+++ apache_1.3.6+01-08/src/main/qsc.c Thu Sep 2 12:48:21 1999
@@ -0,0 +1,1336 @@
+/*
+ * Copyright (c) 1999 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED 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 THE APACHE GROUP OR
+ * ITS CONTRIBUTORS 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.
+ * ====================================================================
+ *
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * QSC - Quick Shortcut Cache (or Quick Static-content Cache).
+ * Tightly integrated with mod_mmap_static.
+ * Mike Abbott - mja@sgi.com
+ *
+ * Cache the response to a request for a static page, so that subsequent
+ * requests for the same page will bypass all the computation that is
+ * identical every time that page is requested. Caches the HTTP headers
+ * and body together for quick response with a single writev(). Hashes
+ * URIs for fast lookup. See htdocs/manual/qsc.html.
+ */
+
+#include "httpd.h"
+
+#ifdef USE_QSC
+
+#include "http_log.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "scoreboard.h"
+
+/*
+ * Number of hash buckets. Larger values shorten chain lengths and
+ * increase performance. Default value is arbitrary.
+ */
+#ifndef QSC_HASH_SIZE
+#define QSC_HASH_SIZE 128
+#endif
+
+/*
+ * Number of qsc_status() hash chain length histogram buckets. Default
+ * value is arbitrary.
+ */
+#ifndef QSC_HIST_SIZE
+#define QSC_HIST_SIZE 5
+#endif
+
+/*
+ * Total size of shared data, in bytes. The QSC will not use more than
+ * this much memory. Default value is arbitrary.
+ */
+#ifndef QSC_MAX_SIZE
+#define QSC_MAX_SIZE (4 * 1024 * 1024)
+#endif
+
+/*
+ * Granularity of memory allocation in bytes. Must be power of two at
+ * least as large as max(sizeof (long), sizeof (void *)).
+ */
+#ifndef QSC_GRAIN
+union qsc_granule {
+ size_t s;
+ void *c;
+ ap_atomic a;
+};
+#define QSC_GRAIN sizeof (union qsc_granule)
+#endif
+
+/*
+ * Granularity of headers, for processor cache alignment, or 0 for none.
+ * The Server response header is padded with spaces to force alignment.
+ */
+#ifndef QSC_HEADER_GRAIN
+#define QSC_HEADER_GRAIN CACHE_ALIGNMENT
+#endif
+
+#if QSC_HEADER_GRAIN > 0
+/*
+ * Maximum single allocation by qsc_malloc() or qsc_hmalloc(), in bytes,
+ * to avoid overlap race.
+ */
+# ifndef QSC_MAX_ALLOC
+# define QSC_MAX_ALLOC 512
+# endif
+
+/*
+ * Minimum separation of upward-growing nonaligned allocations and
+ * downward-growing aligned allocations, in bytes, to avoid overlap
+ * race. Should be a small multiple of QSC_MAX_ALLOC for safety.
+ */
+# ifndef QSC_RED_ZONE
+# define QSC_RED_ZONE 4096
+# endif
+#endif
+
+#ifdef QSC_DEBUG
+#define QSC_ASSERT(x) ap_assert(x)
+#else
+#define QSC_ASSERT(x) /* nothing */
+#endif
+
+/* Round x up to the next a */
+#define QSC_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+
+/*
+ * Atomic compare-and-swap function. Returns nonzero on success.
+ */
+#ifdef IRIX
+# if _MIPS_SIM == _ABIO32
+# if IRIX < 65
+/*
+ * SGI's ucode (o32 ABI) compilers lack an intrinsic so use _r4k_cas
+ * from libc. This won't work if you're using an R3000 or earlier,
+ * which lack load-linked/store-conditional instructions; you'll have to
+ * use uscas() after calling usinit().
+ */
+extern int _r4k_cas(unsigned long *, unsigned long, unsigned long);
+# define qsc_cas(p, oldval, newval) _r4k_cas(p, oldval, newval)
+# else
+/*
+ * Starting with Irix 6.5 _r4k_cas is hidden in libc so we can't use it.
+ * Either build -n32 or -64, use uscas() after calling usinit(), or
+ * don't use the QSC.
+ */
+# error "build -n32 or -64 on Irix 6.5 and above"
+# endif
+# else
+/*
+ * Use SGI's MIPSpro (n32 and 64-bit ABIs) compiler intrinsic
+ * __compare_and_swap().
+ */
+# define qsc_cas(p, oldval, newval) __compare_and_swap(p, oldval, newval)
+# endif
+#elif defined(LINUX)
+/*
+ * Use asm statement copied from Linux libpthread's __compare_and_swap().
+ */
+static ap_inline int
+qsc_cas(ap_atomic *p, ap_atomic oldval, ap_atomic newval)
+{
+ char ret;
+ ap_atomic readval;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
+ : "=q" (ret), "=m" (*p), "=a" (readval)
+ : "r" (newval), "m" (*p), "a" (oldval));
+ return ret;
+}
+#else
+# error "need atomic compare-and-swap function"
+#endif
+
+/* Hash table entry */
+struct qsc_entry {
+ struct qsc_entry *next; /* in hash bucket */
+ char *uri; /* URI of cache entry */
+ const server_rec *server; /* vhost serving entry */
+ void *kaheaders; /* keep-alive HTTP response headers */
+ size_t nkaheaderbytes; /* length of kaheaders */
+ void *ccheaders; /* non-ka HTTP response headers */
+ size_t nccheaderbytes; /* length of ccheaders */
+ void *body; /* mmap_static response body */
+ size_t nbodybytes; /* length of body */
+#ifdef QSC_DEBUG
+ const char *filename; /* name of mapped file */
+#endif
+};
+
+/* Counters */
+struct qsc_stats {
+ ap_atomic nfailures; /* number of failed insertions */
+ ap_atomic nucreq; /* number of uncachable requests */
+ ap_atomic nucres; /* number of uncachable responses */
+ ap_atomic nhits; /* number of successful hash table lookups */
+ ap_atomic nmisses; /* number of unsuccessful hash table lookups */
+ ap_atomic nresets; /* number of resets */
+};
+
+/* All QSC data */
+struct qsc {
+ struct qsc_stats stats;
+ struct qsc_entry *hash_table[QSC_HASH_SIZE];
+ ap_atomic alloc_head;
+#if QSC_HEADER_GRAIN > 0
+ ap_atomic alloc_aligned;
+#endif
+};
+
+static struct qsc *qsc; /* root of qsc data shared by all */
+
+/*
+ * Add v to *p atomically and return the original value of *p.
+ */
+static ap_inline ap_atomic
+qsc_atomic_add(ap_atomic *p, ap_atomic v)
+{
+ ap_atomic r;
+
+ do
+ r = *p;
+ while (!qsc_cas(p, r, r + v));
+
+ return r;
+}
+
+/*
+ * Atomically insert the new entry into the hash bucket at head and return
+ * the old value of *head.
+ */
+static ap_inline struct qsc_entry *
+qsc_atomic_insert(struct qsc_entry **head, struct qsc_entry *new)
+{
+ do
+ new->next = *head;
+ while (!qsc_cas((ap_atomic *) head, (ap_atomic) new->next,
+ (ap_atomic) new));
+
+ return new->next;
+}
+
+/*
+ * Initialize or reset the shared-memory allocator.
+ */
+static void
+qsc_init_malloc(void)
+{
+ qsc->alloc_head = QSC_ALIGN((ap_atomic) (qsc + 1), QSC_GRAIN);
+#if QSC_HEADER_GRAIN > 0
+ qsc->alloc_aligned = ((ap_atomic) qsc + QSC_MAX_SIZE) &
+ ~(QSC_HEADER_GRAIN - 1);
+ if (qsc->alloc_aligned < qsc->alloc_head)
+ qsc->alloc_aligned = qsc->alloc_head;
+#endif
+}
+
+/*
+ * Allocate nbytes from shared memory.
+ */
+static void *
+qsc_malloc(size_t nbytes)
+{
+ ap_atomic r, n;
+
+ nbytes = QSC_ALIGN(nbytes, QSC_GRAIN);
+#if QSC_HEADER_GRAIN > 0
+ QSC_ASSERT(qsc->alloc_head <= qsc->alloc_aligned);
+ if (nbytes <= QSC_MAX_ALLOC) {
+#endif
+ do {
+ r = qsc->alloc_head;
+ n = r + nbytes;
+ if (n >
+#if QSC_HEADER_GRAIN > 0
+ qsc->alloc_aligned - QSC_RED_ZONE
+#else
+ (ap_atomic) qsc + QSC_MAX_SIZE
+#endif
+ ) {
+ r = 0;
+ break;
+ }
+ } while (!qsc_cas(&qsc->alloc_head, r, n));
+#if QSC_HEADER_GRAIN > 0
+ } else
+ r = 0;
+#endif
+
+ return (void *) r;
+}
+
+#if QSC_HEADER_GRAIN > 0
+/*
+ * Allocate nbytes from shared memory, aligned to a QSC_HEADER_GRAIN
+ * virtual address. nbytes must be similarly aligned.
+ */
+static void *
+qsc_hmalloc(size_t nbytes)
+{
+ ap_atomic n;
+
+ QSC_ASSERT((qsc->alloc_aligned & (QSC_HEADER_GRAIN - 1)) == 0);
+ QSC_ASSERT((nbytes & (QSC_HEADER_GRAIN - 1)) == 0);
+ QSC_ASSERT(qsc->alloc_head <= qsc->alloc_aligned);
+
+ if (nbytes <= QSC_MAX_ALLOC) {
+ ap_atomic r;
+
+ do {
+ r = qsc->alloc_aligned;
+ n = r - nbytes;
+ if (n < qsc->alloc_head + QSC_RED_ZONE) {
+ n = 0;
+ break;
+ }
+ } while (!qsc_cas(&qsc->alloc_aligned, r, n));
+ } else
+ n = 0;
+
+ return (void *) n;
+}
+#endif
+
+/*
+ * Free vp to shared memory.
+ */
+/*ARGSUSED0*/
+static void
+qsc_free(void *vp)
+{
+ /* no-op */
+}
+
+/*
+ * Return the number of bytes of shared memory in use.
+ */
+static ap_atomic
+qsc_inuse(void)
+{
+ return qsc->alloc_head - (ap_atomic) qsc
+#if QSC_HEADER_GRAIN > 0
+ + ((ap_atomic) qsc + QSC_MAX_SIZE) - qsc->alloc_aligned + QSC_RED_ZONE
+#endif
+ ;
+}
+
+/*
+ * Hash the URI into an integer.
+ */
+static unsigned long
+qsc_hash_uri(const char *uri)
+{
+ unsigned long hash = 0;
+
+ while (*uri)
+ hash = (hash << 4) - hash + *uri++; /* hash = hash * 15 + *uri++ */
+
+ return hash;
+}
+
+/*
+ * Return the hash table entry for the URI and vhost, or NULL if none.
+ */
+static struct qsc_entry *
+qsc_lookup_uri(const char *uri, const server_rec *server)
+{
+ struct qsc_entry *ep;
+
+ ep = qsc->hash_table[qsc_hash_uri(uri) % QSC_HASH_SIZE];
+ while (ep && (ep->server != server || strcmp(ep->uri, uri)))
+ ep = ep->next;
+
+ /* printf("%d: qsc_lookup_uri(%s, 0x%08x) -> 0x%08x\n", getpid(), uri, server, ep); */
+
+ return ep;
+}
+
+/*
+ * Insert a new hash table entry for the URI and vhost and return it, or
+ * NULL if not possible.
+ */
+static struct qsc_entry *
+qsc_insert_uri(const char *uri, const server_rec *server)
+{
+ struct qsc_entry *nep;
+
+ nep = (struct qsc_entry *) qsc_malloc(sizeof *nep);
+ if (nep) {
+ size_t nb;
+
+ nep->next = NULL;
+
+ nb = strlen(uri) + 1;
+ nep->uri = (char *) qsc_malloc(nb);
+ if (nep->uri) {
+ memcpy(nep->uri, uri, nb);
+ nep->server = server;
+
+ /*
+ * insert it even if some other thread already did;
+ * suffer an occasional duplicate entry rather than
+ * have to spin-lock the table
+ */
+ qsc_atomic_insert(&qsc->hash_table[qsc_hash_uri(uri) %
+ QSC_HASH_SIZE], nep);
+ } else {
+ qsc_free(nep);
+ nep = NULL;
+ }
+ }
+
+ /* printf("%d: qsc_insert_uri(%s, 0x%08x) -> 0x%08x\n", getpid(), uri, server, nep); */
+
+ return nep;
+}
+
+/*
+ * Clear out the shared QSC global data. Does not destroy it, just
+ * clears it.
+ */
+/*ARGSUSED0*/
+static void
+qsc_cleanup(void *junk)
+{
+ ap_atomic n;
+
+ /*
+ * No locking here because this is only ever called while the server
+ * is single-threaded (single-processed, whatever).
+ */
+
+ n = qsc->stats.nresets;
+ memset(qsc, 0, sizeof *qsc);
+ qsc->stats.nresets = n + 1;
+
+ qsc_init_malloc();
+}
+
+/*
+ * Increment the insertion-failure counter and log an error if this is
+ * the first failure.
+ */
+static void
+qsc_failure(void)
+{
+ if (qsc_atomic_add(&qsc->stats.nfailures, 1) == 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL,
+ "QSC is full; compile with larger QSC_MAX_SIZE and/or QSC_MAX_ALLOC to cache more");
+}
+
+/*
+ * Update a stats counter from a scoreboard counter.
+ */
+static void
+qsc_counter_sync(ap_atomic *score_counter, ap_atomic *stats_counter)
+{
+ ap_atomic n;
+
+ do
+ n = *score_counter;
+ while (n && !qsc_cas(score_counter, n, 0));
+
+ if (n)
+ qsc_atomic_add(stats_counter, n);
+}
+
+/*
+ * Update the QSC stats counters from the individual scoreboard server
+ * counters.
+ */
+static void
+qsc_sync_from_scoreboard(void)
+{
+ int i;
+
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nucreq,
+ &qsc->stats.nucreq);
+ qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nucres,
+ &qsc->stats.nucres);
+ qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nhits,
+ &qsc->stats.nhits);
+ qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nmisses,
+ &qsc->stats.nmisses);
+
+ /*
+ * This really only works with shared memory scoreboards.
+ * To make this work with a file-based scoreboard:
+ * - make qsc_counter_sync() return nonzero when it updates
+ * - if any call above returns nonzero, call put_scoreboard_info
+ * - but first make put_scoreboard_info global
+ * - call ap_sync_scoreboard_image() once before loop
+ * - pray two processes don't call this function at the same time
+ */
+ }
+}
+
+/*
+ * HTTP/1.1 header fields, from RFC 2068, and their effects on QSC operation.
+ *
+ * Header Field Name Type Cachability
+ * ----------------- ---- ------------
+ * Accept request ignore, but should obey
+ * Accept-Charset request ignore, but should obey
+ * Accept-Encoding request ignore, but should obey
+ * Accept-Language request ignore, but should obey
+ * Accept-Ranges response ignore
+ * Age response ignore
+ * Allow entity ignore
+ * Alternates response ignore
+ * Authorization request uncachable by choice (could handle)
+ * Cache-Control general uncachable
+ * Connection general keep separate ka & !ka headers cached
+ * Content-Base entity ignore
+ * Content-Encoding entity ignore
+ * Content-Language entity ignore
+ * Content-Length entity ignore
+ * Content-Location entity ignore
+ * Content-MD5 entity ignore
+ * Content-Range entity uncachable by choice (could handle)
+ * Content-Type entity ignore
+ * Content-Version entity ignore
+ * Date general ignore
+ * Derived-From entity ignore
+ * ETag entity uncachable if weak, otherwise ignore
+ * Expires entity uncachable? (see 14.21)
+ * From request ignore
+ * Host request secondary cache key? (see 14.23)
+ * If-Modified-Since request uncachable by choice (could handle)
+ * If-Match request ignore: cached etags don't change
+ * If-None-Match request uncachable by choice (could handle)
+ * If-Range request uncachable by choice (could handle)
+ * If-Unmodified-Since request ignore: cached dates don't change
+ * Keep-Alive connection ignore
+ * Last-Modified entity ignore
+ * Link entity ignore
+ * Location response ignore
+ * Max-Forwards request ignore
+ * Pragma general uncachable if "no-cache", otherwise ignore
+ * Proxy-Authenticate response ignore
+ * Proxy-Authorization request ignore
+ * Public response ignore
+ * Range request uncachable by choice
+ * Referer request ignore
+ * Retry-After response ignore
+ * Server response ignore
+ * Transfer-Encoding general ignore
+ * Upgrade general ignore
+ * User-Agent request ignore
+ * Vary response uncachable
+ * Via general ignore
+ * Warning response ignore
+ * WWW-Authenticate response ignore
+ */
+
+/*
+ * Return nonzero if the request is cachable, zero otherwise.
+ */
+static int
+qsc_request_is_cachable(const request_rec *r)
+{
+ int cachable;
+ const array_header *ap;
+ int i;
+
+ /*
+ * Requests are cachable if they:
+ * - are GET, and
+ * - are not explicitly marked no_cache,
+ * unless they contain one of the following headers:
+ * Authorization: ...
+ * QSC does not support authorization.
+ * Cache-Control: ...
+ * Most values limit cachability so assume all do, for speed.
+ * If-Modified-Since: ...
+ * If-None-Match: ...
+ * Both require extra processing.
+ * If-Range: ...
+ * QSC does not support ranges yet.
+ * Pragma: no-cache
+ * Explicit.
+ * Range: ...
+ * QSC does not support ranges yet.
+ */
+
+ cachable = r->method_number == M_GET && !r->header_only && !r->no_cache;
+ ap = ap_table_elts(r->headers_in);
+ for (i = 0; cachable && i < ap->nelts; i++) {
+ const table_entry *tep = &((table_entry *) ap->elts)[i];
+
+ switch (ap_tolower(tep->key[0])) {
+ case 'a':
+ if (!strcasecmp(tep->key, "authorization"))
+ cachable = 0;
+ break;
+ case 'c':
+ if (!strcasecmp(tep->key, "cache-control"))
+ cachable = 0;
+ break;
+ case 'i':
+ if (!strcasecmp(tep->key, "if-modified-since") ||
+ !strcasecmp(tep->key, "if-none-match") ||
+ !strcasecmp(tep->key, "if-range"))
+ cachable = 0;
+ break;
+ case 'p':
+ if (!strcasecmp(tep->key, "pragma") &&
+ !strcasecmp(tep->val, "no-cache"))
+ cachable = 0;
+ break;
+ case 'r':
+ if (!strcasecmp(tep->key, "range"))
+ cachable = 0;
+ break;
+ }
+ }
+
+ return cachable;
+}
+
+/*
+ * Return nonzero if the response is cachable, zero otherwise.
+ */
+static int
+qsc_response_is_cachable(const request_rec *r)
+{
+ int cachable;
+ const array_header *ap;
+ int i;
+
+ /*
+ * Responses are cachable if they:
+ * - have status 200 OK, and
+ * - are not chunked, and
+ * - are not explicitly marked no_cache,
+ * unless they contain one of the following headers:
+ * Cache-Control: ...
+ * Most values limit cachability so assume all do, for speed.
+ * Content-Range: ...
+ * QSC does not support ranges yet.
+ * ETag: W/... (that is, strong etags are cachable, weaks ones are not)
+ * Cache only strong entity-tags. Weak etags will change to
+ * strong soon enough (see ap_set_etag()).
+ * Expires: ...
+ * QSC does not support removal or update of cache entries.
+ * Pragma: no-cache
+ * Explicit.
+ * Vary: ...
+ * Response depends on values other than URI, and QSC uses only
+ * the URI to look up cache entries.
+ */
+
+ cachable = r->status == HTTP_OK && !r->chunked && !r->no_cache;
+ ap = ap_table_elts(r->headers_out);
+ for (i = 0; cachable && i < ap->nelts; i++) {
+ const table_entry *tep = &((table_entry *) ap->elts)[i];
+
+ switch (ap_tolower(tep->key[0])) {
+ case 'c':
+ if (!strcasecmp(tep->key, "cache-control") ||
+ !strcasecmp(tep->key, "content-range"))
+ cachable = 0;
+ break;
+ case 'e':
+ if ((!strcasecmp(tep->key, "etag") &&
+ !strncasecmp(tep->val, "w/", 2)) ||
+ !strcasecmp(tep->key, "expires"))
+ cachable = 0;
+ break;
+ case 'p':
+ if (!strcasecmp(tep->key, "pragma") &&
+ !strcasecmp(tep->val, "no-cache"))
+ cachable = 0;
+ break;
+ case 'v':
+ if (!strcasecmp(tep->key, "vary"))
+ cachable = 0;
+ break;
+ }
+ }
+
+ return cachable;
+}
+
+/*
+ * Return nonzero if the request uses keep-alive. Also sets
+ * r->connection->keepalive.
+ */
+static int
+qsc_set_keepalive(request_rec *r)
+{
+ conn_rec *c;
+ const server_rec *s;
+ const char *ch;
+ int ka;
+
+ /*
+ * This performs the same function as ap_set_keepalive() but we can
+ * make some simplifying assumptions which speed up the decision.
+ * Specifically, we know:
+ * r->status == HTTP_OK
+ * !r->header_only
+ * r->headers_out contains a Content-Length header
+ * r->headers_out does not contain a Connection header
+ * r->subprocess_env does not contain a nokeepalive header
+ * Also we do not send a Keep-Alive header back, since it's optional
+ * anyway and the value changes every time if s->keep_alive_max > 0
+ * (making it hard to cache and optimize).
+ */
+ c = r->connection;
+ s = r->server;
+ ch = ap_table_get(r->headers_in, "Connection");
+ if (c->keepalive >= 0 &&
+ s->keep_alive &&
+ s->keep_alive_timeout > 0 &&
+ (s->keep_alive_max == 0 || s->keep_alive_max > c->keepalives) &&
+ !ap_find_token(r->pool, ch, "close") &&
+ (r->proto_num >= HTTP_VERSION(1,1) ||
+ ap_find_token(r->pool, ch, "keep-alive"))) {
+ ka = 1;
+ c->keepalives++;
+ } else
+ ka = 0;
+
+ c->keepalive = ka;
+ return ka;
+}
+
+/*
+ * Initialize the QSC. The given pool must match the one
+ * mod_mmap_static registers its cleanup upon, since we share pointers
+ * with it which it unmaps during its cleanup.
+ */
+void
+qsc_init(pool *p)
+{
+ if (ap_qsc_enabled) {
+ if (ap_find_linked_module("mod_mmap_static.c")) {
+#if defined(IRIX) && _MIPS_SIM != _ABIO32
+/* disable warning about constant controlling expression */
+#pragma set woff 1209
+#endif
+ if (QSC_MAX_SIZE >= sizeof *qsc) {
+#if defined(IRIX) && _MIPS_SIM != _ABIO32
+#pragma reset woff 1209
+#endif
+ caddr_t m;
+
+ /*
+ * Use the same mechanism as the scoreboard, except it
+ * must be shared memory of some sort (not a file).
+ */
+#ifdef USE_SHMGET_SCOREBOARD
+ m = ap_share_anonymous(p, 0, QSC_MAX_SIZE);
+#else
+ m = ap_map_anonymous(p, 0, QSC_MAX_SIZE);
+#endif
+ if (m != (caddr_t) (ap_ptr) -1) {
+ qsc = (struct qsc *) m;
+ qsc_init_malloc();
+ ap_register_cleanup(p, NULL, qsc_cleanup, ap_null_cleanup);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL,
+ "cannot obtain %d bytes of anonymous shared memory; QSC disabled",
+ QSC_MAX_SIZE);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL,
+ "QSC_MAX_SIZE too small; QSC disabled");
+ } else
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL,
+ "mmap_static module not configured; QSC disabled");
+ }
+}
+
+/*
+ * Try to shortcut the request through the QSC. Returns nonzero if
+ * successful, zero if the server should continue normal processing.
+ */
+int
+qsc_process_request(request_rec *r)
+{
+ int handled = 0;
+
+ if (qsc) {
+ short_score *sp;
+
+ sp = &ap_scoreboard_image->servers[r->connection->child_num];
+ if (qsc_request_is_cachable(r)) {
+ const struct qsc_entry *ep;
+
+ /*
+ * Used to increment shared stats counters directly but on
+ * large systems that can cause cache line thrashing.
+ * qsc_atomic_add(ep ? &qsc->stats.nhits : &qsc->stats.nmisses, 1);
+ * Instead increment nonshared scoreboard server counters
+ * and rely on qsc_sync_from_scoreboard() to update the
+ * shared stats counters.
+ */
+
+ QSC_ASSERT(r->prev == NULL); /* r is original request */
+ ep = qsc_lookup_uri(r->unparsed_uri, r->server);
+ if (ep) {
+ struct iovec iovec[2];
+
+ qsc_atomic_add(&sp->qsc_nhits, 1);
+
+ /*
+ * The set of headers sent depends on the connection
+ * status, keep-alive (ka) vs. single-shot (cc, for
+ * connection: close).
+ */
+ if (qsc_set_keepalive(r)) {
+ iovec[0].iov_base = ep->kaheaders;
+ iovec[0].iov_len = ep->nkaheaderbytes;
+ } else {
+ iovec[0].iov_base = ep->ccheaders;
+ iovec[0].iov_len = ep->nccheaderbytes;
+ }
+#if QSC_HEADER_GRAIN > 0
+ QSC_ASSERT((iovec[0].iov_len & (QSC_HEADER_GRAIN - 1)) == 0);
+#endif
+ iovec[1].iov_base = ep->body;
+ iovec[1].iov_len = ep->nbodybytes;
+
+ if (iovec[1].iov_len <= MMAP_SEGMENT_SIZE) {
+ /* nice and small, write all at once */
+ ap_send_iovec(r, iovec, 2);
+ } else {
+ /*
+ * bigger than we'd like; segment it WITHOUT using
+ * ap_bwrite() which copies data around and
+ * unnecessarily handles chunked encoding.
+ * ap_send_iovec() is streamlined for us.
+ */
+ iovec[1].iov_len = MMAP_SEGMENT_SIZE;
+ if (ap_send_iovec(r, iovec, 2) > 0) {
+ size_t nw = MMAP_SEGMENT_SIZE;
+ do {
+ iovec[1].iov_base = (char *) ep->body + nw;
+ iovec[1].iov_len = ep->nbodybytes - nw;
+ if (iovec[1].iov_len > MMAP_SEGMENT_SIZE)
+ iovec[1].iov_len = MMAP_SEGMENT_SIZE;
+ } while (ap_send_iovec(r, &iovec[1], 1) > 0 &&
+ (nw += iovec[1].iov_len) < ep->nbodybytes);
+ }
+ }
+
+ handled = 1;
+ } else
+ qsc_atomic_add(&sp->qsc_nmisses, 1);
+ } else
+ qsc_atomic_add(&sp->qsc_nucreq, 1);
+ }
+
+ return handled;
+}
+
+/*
+ * Insert an entry into the QSC. The given basic header, headers from
+ * the request, and given body are all cached together indexed by the
+ * original request's URI.
+ */
+void
+qsc_insert_request(request_rec *r, const char *basic_header,
+ size_t basic_header_len, void *body, size_t nbodybytes,
+ const char *filename)
+{
+ if (qsc && qsc_request_is_cachable(r) && qsc_response_is_cachable(r)) {
+ array_header *ap;
+ struct header_size {
+ size_t keylen;
+ size_t vallen;
+ } *header_sizes;
+ int i;
+ size_t nheaderbytes;
+#if QSC_HEADER_GRAIN > 0
+ size_t akalen, acclen;
+ const char *pp, *padpoint;
+#endif
+ char *kaheaders, *ccheaders;
+ static const char katail[] = "Connection: keep-alive\r\n\r\n";
+ static const char cctail[] = "Connection: close\r\n\r\n";
+ enum {
+ kalength = 26, /* strlen(katail) */
+ cclength = 21 /* strlen(cctail) */
+ };
+
+ ap = ap_table_elts(r->headers_out);
+ header_sizes = (struct header_size *) ap_palloc(r->pool,
+ ap->nelts * sizeof *header_sizes);
+
+#if QSC_HEADER_GRAIN > 0
+ /*
+ * Find where we will insert padding if necessary: append to
+ * the Server header value. Probe the most likely place first
+ * (where we think ap_tee_basic_http_header() put it) and
+ * failing that, scan for it.
+ */
+ if (basic_header_len >= 12 &&
+ !strncmp(basic_header + basic_header_len -
+ strlen(ap_get_server_version()) - 12, "\r\nServer: ", 10))
+ padpoint = basic_header + basic_header_len - 2;
+ else {
+ padpoint = NULL;
+ for (pp = basic_header + basic_header_len; --pp >= basic_header; ) {
+ if (*pp == '\015')
+ padpoint = pp;
+ if ((*pp == 'S' || *pp == 's') &&
+ pp + 8 < basic_header + basic_header_len &&
+ !strncasecmp(pp, "server: ", 8))
+ break;
+ }
+ if (pp < basic_header)
+ padpoint = NULL;
+ }
+#endif
+
+ /* measure lengths of strings and total length of linearized headers */
+ nheaderbytes = basic_header_len;
+ for (i = 0; i < ap->nelts; i++) {
+ table_entry *tep;
+ int h;
+
+ tep = &((table_entry *) ap->elts)[i];
+ h = 1;
+ switch (ap_tolower(tep->key[0])) {
+ case 'c':
+ /* the Connection header is handled separately */
+ if (!strcasecmp(tep->key, "connection"))
+ h = 0;
+ break;
+ case 'k':
+ /* don't cache the Keep-Alive header */
+ if (!strcasecmp(tep->key, "keep-alive"))
+ h = 0;
+ break;
+ }
+
+ if (h) {
+ header_sizes[i].keylen = strlen(tep->key);
+ header_sizes[i].vallen = strlen(tep->val);
+
+ /* +4 for ": " and "\r\n" */
+ nheaderbytes += header_sizes[i].keylen +
+ header_sizes[i].vallen + 4;
+ } else
+ header_sizes[i].keylen = 0;
+ }
+
+ /* create ka & cc linearized headers using above info */
+#if QSC_HEADER_GRAIN > 0
+ /* round up to QSC_HEADER_GRAIN */
+ akalen = QSC_ALIGN(nheaderbytes + kalength, QSC_HEADER_GRAIN);
+ acclen = QSC_ALIGN(nheaderbytes + cclength, QSC_HEADER_GRAIN);
+ /* align that to QSC_HEADER_GRAIN vaddr */
+ kaheaders = (char *) qsc_hmalloc(akalen);
+ ccheaders = (char *) qsc_hmalloc(acclen);
+#else
+ kaheaders = (char *) qsc_malloc(nheaderbytes + kalength);
+ ccheaders = (char *) qsc_malloc(nheaderbytes + cclength);
+#endif
+ if (kaheaders && ccheaders) {
+#if QSC_HEADER_GRAIN > 0
+ size_t npad;
+#endif
+ char *hp;
+ request_rec *or;
+ struct qsc_entry *ep;
+
+ /* linearize ka headers first */
+#if QSC_HEADER_GRAIN > 0
+ npad = akalen - (nheaderbytes + kalength);
+ if (padpoint && npad > 0) {
+ size_t padoff = padpoint - basic_header;
+
+ /* copy up to padpoint */
+ memcpy(kaheaders, basic_header, padoff);
+ hp = kaheaders + padoff;
+
+ /* pad with spaces */
+ memset(hp, ' ', npad);
+ hp += npad;
+
+ /* copy the rest */
+ npad = basic_header_len - padoff;
+ memcpy(hp, basic_header + padoff, npad);
+ hp += npad;
+ } else {
+#endif
+ memcpy(kaheaders, basic_header, basic_header_len);
+ hp = kaheaders + basic_header_len;
+#if QSC_HEADER_GRAIN > 0
+ }
+#endif
+ for (i = 0; i < ap->nelts; i++) {
+ if (header_sizes[i].keylen > 0) {
+ table_entry *tep = &((table_entry *) ap->elts)[i];
+ memcpy(hp, tep->key, header_sizes[i].keylen);
+ hp += header_sizes[i].keylen + 2;
+ hp[-2] = ':';
+ hp[-1] = ' ';
+ memcpy(hp, tep->val, header_sizes[i].vallen);
+ hp += header_sizes[i].vallen + 2;
+ hp[-2] = '\r';
+ hp[-1] = '\n';
+ }
+ }
+ memcpy(hp, katail, kalength);
+#if QSC_HEADER_GRAIN > 0
+ QSC_ASSERT(((ap_ptr) kaheaders & (QSC_HEADER_GRAIN - 1)) == 0);
+ QSC_ASSERT(((ap_ptr) (hp + kalength) & (QSC_HEADER_GRAIN - 1)) == 0 ||
+ padpoint == NULL);
+#endif
+
+ /* now linearize the cc headers */
+#if QSC_HEADER_GRAIN > 0
+ npad = acclen - (nheaderbytes + cclength);
+ if (padpoint && npad > 0) {
+ size_t padoff = padpoint - basic_header;
+
+ /* copy up to padpoint */
+ memcpy(ccheaders, basic_header, padoff);
+ hp = ccheaders + padoff;
+
+ /* pad with spaces */
+ memset(hp, ' ', npad);
+ hp += npad;
+
+ /* copy the rest */
+ npad = basic_header_len - padoff;
+ memcpy(hp, basic_header + padoff, npad);
+ hp += npad;
+ } else {
+ memcpy(ccheaders, basic_header, basic_header_len);
+ hp = ccheaders + basic_header_len;
+ }
+ for (i = 0; i < ap->nelts; i++) {
+ if (header_sizes[i].keylen > 0) {
+ table_entry *tep = &((table_entry *) ap->elts)[i];
+ memcpy(hp, tep->key, header_sizes[i].keylen);
+ hp += header_sizes[i].keylen + 2;
+ hp[-2] = ':';
+ hp[-1] = ' ';
+ memcpy(hp, tep->val, header_sizes[i].vallen);
+ hp += header_sizes[i].vallen + 2;
+ hp[-2] = '\r';
+ hp[-1] = '\n';
+ }
+ }
+ memcpy(hp, cctail, cclength);
+ QSC_ASSERT(((ap_ptr) ccheaders & (QSC_HEADER_GRAIN - 1)) == 0);
+ QSC_ASSERT(((ap_ptr) (hp + cclength) & (QSC_HEADER_GRAIN - 1)) == 0 ||
+ padpoint == NULL);
+#else
+ /* ka & cc headers differ only in the tail portion */
+ QSC_ASSERT(hp - kaheaders == nheaderbytes);
+ memcpy(ccheaders, kaheaders, nheaderbytes);
+ memcpy(ccheaders + nheaderbytes, cctail, cclength);
+#endif
+
+ /*
+ * insert headers and body into cache, indexed by original
+ * uri and current vhost
+ */
+ for (or = r; or->prev; or = or->prev)
+ ;
+ ep = qsc_insert_uri(or->uri, r->server);
+ if (ep) {
+ ep->kaheaders = kaheaders;
+ ep->nkaheaderbytes =
+#if QSC_HEADER_GRAIN > 0
+ padpoint ? akalen :
+#endif
+ nheaderbytes + kalength;
+ ep->ccheaders = ccheaders;
+ ep->nccheaderbytes =
+#if QSC_HEADER_GRAIN > 0
+ padpoint ? acclen :
+#endif
+ nheaderbytes + cclength;
+ ep->body = body;
+ ep->nbodybytes = nbodybytes;
+#ifdef QSC_DEBUG
+ ep->filename = filename;
+#endif
+ } else {
+ qsc_free(ccheaders);
+ qsc_free(kaheaders);
+ qsc_failure();
+ }
+ } else {
+ qsc_failure();
+ if (ccheaders)
+ qsc_free(ccheaders);
+ if (kaheaders)
+ qsc_free(kaheaders);
+ }
+ } else if (qsc)
+ qsc_atomic_add(&ap_scoreboard_image->servers[r->connection->child_num].qsc_nucres, 1);
+}
+
+/*
+ * Send a QSC report card.
+ * Options are:
+ * full show detailed info about each entry
+ * quick don't compute anything, just dump counters
+ */
+void
+qsc_status(request_rec *r, const char *option)
+{
+ ap_rputs("<pre>Quick Shortcut Cache (QSC) Status:\n", r);
+
+ if (qsc) {
+ static const char fmt[] = " %-20s %s\n";
+ char buf[128];
+ struct qsc_stats stats;
+ int i, full, quick, nbuckets, nentries, longest_chain, ndupent;
+ ap_atomic nlookups, nuribytes, nheaderbytes, nbodybytes, nbodyvas;
+ int histogram[QSC_HIST_SIZE], pagesize;
+ struct qsc_entry *ep;
+
+ full = 0;
+ quick = 0;
+ if (option) {
+ if (!strcasecmp(option, "full"))
+ full = 1;
+ else if (!strcasecmp(option, "quick"))
+ quick = 1;
+ else
+ ap_rprintf(r, "<strong>Unknown QSC status option \"%s\"</strong>\n",
+ option);
+ }
+
+ qsc_sync_from_scoreboard();
+
+ stats = qsc->stats; /* take a more or less consistent snapshot */
+ nbuckets = 0;
+ nentries = 0;
+ longest_chain = 0;
+ nuribytes = 0;
+ nheaderbytes = 0;
+ nbodybytes = 0;
+ nbodyvas = 0;
+ ndupent = 0;
+ memset(histogram, 0, sizeof histogram);
+ pagesize = getpagesize();
+ if (pagesize <= 0 || (pagesize & (pagesize - 1)) != 0)
+ pagesize = 4096;
+
+ if (!quick) {
+ for (i = 0; i < QSC_HASH_SIZE; i++) {
+ ep = qsc->hash_table[i];
+ if (ep) {
+ int cl;
+
+ nbuckets++;
+
+ cl = 0;
+ do {
+ struct qsc_entry *dp;
+
+ cl++;
+ nentries++;
+ nuribytes += strlen(ep->uri) + 1;
+ nheaderbytes += ep->nkaheaderbytes + ep->nccheaderbytes;
+ nbodybytes += ep->nbodybytes;
+ nbodyvas += QSC_ALIGN(ep->nbodybytes, pagesize);
+
+ for (dp = ep->next; dp; dp = dp->next)
+ if (ep->server == dp->server &&
+ !strcmp(ep->uri, dp->uri))
+ ndupent++;
+
+ ep = ep->next;
+ } while (ep);
+
+ if (cl > longest_chain)
+ longest_chain = cl;
+ histogram[((cl <= QSC_HIST_SIZE) ? cl : QSC_HIST_SIZE) - 1]++;
+ }
+ }
+ }
+
+ /*
+ * ap_vformatter() botches some conversions (like %.2f) so use
+ * sprintf() then ap_rprintf()
+ */
+
+ nlookups = stats.nucreq + stats.nucres + stats.nhits + stats.nmisses;
+ sprintf(buf, "%ld/%ld (%.2f%%)",
+ (long) stats.nhits, (long) nlookups,
+ nlookups ? (double) stats.nhits * 100.0 / (double) nlookups : 0);
+ ap_rprintf(r, fmt, "hit ratio", buf);
+ if (!quick) {
+ ap_atomic uncachable;
+
+ uncachable = stats.nucreq + stats.nucres + stats.nmisses -
+ nentries;
+ sprintf(buf, "%ld/%ld (%.2f%%)",
+ (long) uncachable, (long) nlookups,
+ nlookups ? (double) uncachable * 100.0 / (double) nlookups : 0);
+ ap_rprintf(r, fmt, "uncachable", buf);
+
+ uncachable = stats.nmisses - nentries;
+ sprintf(buf, "%ld/%ld (%.2f%%)",
+ (long) uncachable, (long) stats.nmisses,
+ stats.nmisses ? (double) uncachable * 100.0 / (double) stats.nmisses : 0);
+ ap_rprintf(r, fmt, "uncachable misses", buf);
+ }
+ sprintf(buf, "%ld/%ld (%.2f%%)",
+ (long) stats.nucreq, (long) nlookups,
+ nlookups ? (double) stats.nucreq * 100.0 / (double) nlookups : 0);
+ ap_rprintf(r, fmt, "uncachable requests", buf);
+ sprintf(buf, "%ld/%ld (%.2f%%)",
+ (long) stats.nucres, (long) nlookups,
+ nlookups ? (double) stats.nucres * 100.0 / (double) nlookups : 0);
+ ap_rprintf(r, fmt, "uncachable responses", buf);
+ sprintf(buf, "%ld", (long) stats.nresets);
+ ap_rprintf(r, fmt, "resets", buf);
+
+ ap_rputs("<em>Hash table</em>\n", r);
+ sprintf(buf, "%ld", (long) stats.nfailures);
+ ap_rprintf(r, fmt, "failed insertions", buf);
+ if (!quick) {
+ ap_atomic inuse;
+
+ sprintf(buf, "%d", nentries);
+ ap_rprintf(r, fmt, "entries", buf);
+ sprintf(buf, "%d", ndupent);
+ ap_rprintf(r, fmt, "duplicate entries", buf);
+ sprintf(buf, "%d/%d (%.2f%%)", nbuckets, QSC_HASH_SIZE,
+ (double) nbuckets * 100.0 / (double) QSC_HASH_SIZE);
+ ap_rprintf(r, fmt, "bucket use", buf);
+ sprintf(buf, "%d/%d (%.2f%%)", nbuckets, nentries,
+ nentries ? (double) nbuckets * 100.0 / (double) nentries : 0);
+ ap_rprintf(r, fmt, "hash effectiveness", buf);
+ sprintf(buf, "%d", longest_chain);
+ ap_rprintf(r, fmt, "longest chain", buf);
+ sprintf(buf, "%.1f", (double) nentries / QSC_HASH_SIZE);
+ ap_rprintf(r, fmt, "avg. chain", buf);
+ sprintf(buf, "%.1f", nbuckets ?
+ (double) nentries / (double) nbuckets : 0);
+ ap_rprintf(r, fmt, "avg. nonempty chain", buf);
+
+ ap_rputs(" Chain length histogram:\n ", r);
+ for (i = 1; i < QSC_HIST_SIZE; i++)
+ ap_rprintf(r, "%5d ", i);
+ ap_rprintf(r, "%5d+\n ", i);
+ for (i = 1; i <= QSC_HIST_SIZE; i++)
+ ap_rprintf(r, "%5d ", histogram[i - 1]);
+ ap_rputc('\n', r);
+
+ ap_rputs("<em>Memory use</em> (in bytes)\n", r);
+ sprintf(buf, "%ld", (long) sizeof *qsc);
+ ap_rprintf(r, fmt, "table + misc", buf);
+ sprintf(buf, "%ld", (long) nentries * sizeof (struct qsc_entry));
+ ap_rprintf(r, fmt, "entries", buf);
+ sprintf(buf, "%lu", (unsigned long) nuribytes);
+ ap_rprintf(r, fmt, "URIs", buf);
+ sprintf(buf, "%lu", (unsigned long) nheaderbytes);
+ ap_rprintf(r, fmt, "headers", buf);
+ inuse = qsc_inuse();
+ sprintf(buf, "%ld/%ld (%.2f%%)", (long) inuse, (long) QSC_MAX_SIZE,
+ (double) inuse * 100.0 / (double) QSC_MAX_SIZE);
+ ap_rprintf(r, fmt, "total", buf);
+ sprintf(buf, "%lu", (unsigned long) nbodybytes);
+ ap_rprintf(r, fmt, "mapped file data", buf);
+ sprintf(buf, "%lu (%lu %d-byte pages)", (unsigned long) nbodyvas,
+ (unsigned long) (nbodyvas / pagesize), pagesize);
+ ap_rprintf(r, fmt, "mapped file vaddrs", buf);
+ }
+
+ if (full) {
+ ap_rputs("<em>Full entry info</em>\n", r);
+#ifndef QSC_DEBUG
+ ap_rputs("<strong>File names available only -DQSC_DEBUG</strong>\n", r);
+#endif
+ ap_rputs(" <em>server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name</em>\n", r);
+
+ for (i = 0; i < QSC_HASH_SIZE; i++) {
+ for (ep = qsc->hash_table[i]; ep; ep = ep->next) {
+ if (ep->server->defn_name)
+ sprintf(buf, "%s:%u", ep->server->defn_name,
+ ep->server->defn_line_number);
+ else
+ strcpy(buf, "main");
+
+ ap_rprintf(r, " %s * %s @ %d -> %lu;%lu + %lu ",
+ buf, ep->uri, i,
+ (unsigned long) ep->nkaheaderbytes,
+ (unsigned long) ep->nccheaderbytes,
+ (unsigned long) ep->nbodybytes);
+#ifdef QSC_DEBUG
+ ap_rputs(ep->filename, r);
+#else
+ ap_rwrite("n/a", 3, r);
+#endif
+ ap_rputc('\n', r);
+ }
+ }
+ }
+ } else
+ ap_rputs("QSC disabled\n", r);
+
+ ap_rputs("</pre>\n", r);
+}
+
+#endif
diff -Naur apache_1.3.6+01-07/src/modules/experimental/mod_mmap_static.c apache_1.3.6+01-08/src/modules/experimental/mod_mmap_static.c
--- apache_1.3.6+01-07/src/modules/experimental/mod_mmap_static.c Mon Jul 19 16:43:26 1999
+++ apache_1.3.6+01-08/src/modules/experimental/mod_mmap_static.c Thu Sep 2 10:37:38 1999
@@ -322,6 +322,10 @@
a_file **pmatch;
a_file *match;
int rangestatus, errstatus;
+#ifdef USE_QSC
+ char basic_header[1024];
+ size_t basic_header_len;
+#endif
/* we don't handle anything but GET */
if (r->method_number != M_GET) return DECLINED;
@@ -370,7 +374,13 @@
}
rangestatus = ap_set_byterange(r);
+
+#ifdef USE_QSC
+ basic_header_len = sizeof basic_header;
+ ap_tee_http_header(r, basic_header, &basic_header_len);
+#else
ap_send_http_header(r);
+#endif
if (!r->header_only) {
if (!rangestatus) {
@@ -383,6 +393,17 @@
}
}
}
+
+#ifdef USE_QSC
+ if (basic_header_len > 0) {
+ /* convert from bytes remaining to bytes used */
+ basic_header_len = sizeof basic_header - basic_header_len;
+
+ qsc_insert_request(r, basic_header, basic_header_len,
+ match->mm, match->finfo.st_size, match->filename);
+ }
+#endif
+
return OK;
}
diff -Naur apache_1.3.6+01-07/src/modules/standard/mod_status.c apache_1.3.6+01-08/src/modules/standard/mod_status.c
--- apache_1.3.6+01-07/src/modules/standard/mod_status.c Tue Jul 20 22:44:40 1999
+++ apache_1.3.6+01-08/src/modules/standard/mod_status.c Thu Sep 2 10:38:04 1999
@@ -74,6 +74,9 @@
* /server-status?refresh - Returns page with 1 second refresh
* /server-status?refresh=6 - Returns page with refresh every 6 seconds
* /server-status?auto - Returns page with data for automatic parsing
+#ifdef USE_QSC
+ * /server-status?qsc=... - Returns page with extra QSC status info
+#endif
*
* Mark Cox, mark@ukweb.com, November 1995
*
@@ -209,6 +212,7 @@
#define STAT_OPT_REFRESH 0
#define STAT_OPT_NOTABLE 1
#define STAT_OPT_AUTO 2
+#define STAT_OPT_QSC 3
struct stat_opt {
int id;
@@ -221,6 +225,9 @@
{STAT_OPT_REFRESH, "refresh", "Refresh"},
{STAT_OPT_NOTABLE, "notable", NULL},
{STAT_OPT_AUTO, "auto", NULL},
+#ifdef USE_QSC
+ {STAT_OPT_QSC, "qsc", NULL},
+#endif
{STAT_OPT_END, NULL, NULL}
};
@@ -260,6 +267,9 @@
#endif
clock_t tu, ts, tcu, tcs;
server_rec *vhost;
+#ifdef USE_QSC
+ const char *qscp = NULL;
+#endif
tu = ts = tcu = tcs = 0;
@@ -299,6 +309,12 @@
r->content_type = "text/plain";
short_report = 1;
break;
+#ifdef USE_QSC
+ case STAT_OPT_QSC:
+ if (loc[strlen(status_options[i].form_data_str)] == '=')
+ qscp = &loc[strlen(status_options[i].form_data_str) + 1];
+ break;
+#endif
}
}
i++;
@@ -748,6 +764,11 @@
ap_rputs("you need to use the <code>ExtendedStatus On</code> directive. \n", r);
}
+
+#ifdef USE_QSC
+ ap_rputs("<hr>", r);
+ qsc_status(r, qscp);
+#endif
if (!short_report) {
ap_rputs(ap_psignature("<HR>\n",r), r);