scripts are invoked as normal using the complete URL given in
the command, including any query string.</p>
- <p>An attribute defines the location of the document; the
- inclusion is done for each attribute given to the include
- command. The valid attributes are:</p>
+ <p>An attribute defines the location of the document, and may
+ appear more than once in an include element; an inclusion is
+ done for each attribute given to the include command in turn.
+ The valid attributes are:</p>
<dl>
<dt><code>file</code></dt>
<p>If the specified URL is a CGI program, the program will be
executed and its output inserted in place of the directive in the
parsed file. You may include a query string in a CGI url:</p>
-
+
<example>
<!--#include virtual="/cgi-bin/example.cgi?argument=value" -->
</example>
-
+
<p><code>include virtual</code> should be used in preference
to <code>exec cgi</code> to include the output of CGI programs
into an HTML document.</p>
Without the directive, all subrequests are processed as GET
requests.</p>
+ </dd>
+
+ <dt><code>onerror</code></dt>
+ <dd><p>The value is a (%-encoded) URL-path which is shown should a
+ previous attempt to include a file or virtual attribute failed.
+ To be effective, this attribute must be specified after the
+ file or virtual attributes being covered. If the attempt to
+ include the onerror path fails, or if onerror is not specified, the
+ default error message will be included.</p>
+
+ <example>
+ # Simple example<br />
+ <!--#include virtual="/not-exist.html" onerror="/error.html" -->
+ </example>
+
+ <example>
+ # Dedicated onerror paths<br />
+ <!--#include virtual="/path-a.html" onerror="/error-a.html" virtual="/path-b.html" onerror="/error-b.html" -->
+ </example>
+
</dd>
</dl>
</section> <!-- /include -->
}
/*
- * <!--#include virtual|file="..." [virtual|file="..."] ... -->
+ * <!--#include virtual|file="..." [onerror|virtual|file="..."] ... -->
+ *
+ * Output each file/virtual in turn until one of them returns an error.
+ * On error, ignore all further file/virtual attributes until we reach
+ * an onerror attribute, where we make an attempt to serve the onerror
+ * virtual url. If onerror fails, or no onerror is present, the default
+ * error string is inserted into the stream.
*/
static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
apr_bucket_brigade *bb)
{
request_rec *r = f->r;
+ char *last_error;
if (!ctx->argc) {
ap_log_rerror(APLOG_MARK,
return APR_SUCCESS;
}
+ last_error = NULL;
while (1) {
char *tag = NULL;
char *tag_val = NULL;
break;
}
- if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
+ if (strcmp(tag, "virtual") && strcmp(tag, "file") && strcmp(tag,
+ "onerror")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
"\"%s\" to tag include in %s", tag, r->filename);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
rr = ap_sub_req_lookup_file(newpath, r, f->next);
}
}
- else {
+ else if ((tag[0] == 'v' && !last_error)
+ || (tag[0] == 'o' && last_error)) {
if (r->kept_body) {
rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
}
rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
}
}
+ else {
+ continue;
+ }
if (!error_fmt && rr->status != HTTP_OK) {
error_fmt = "unable to include \"%s\" in parsed file %s";
if (error_fmt) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
- r->filename);
- SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ r->filename);
+ if (last_error) {
+ /* onerror threw an error, give up completely */
+ break;
+ }
+ last_error = error_fmt;
+ }
+ else {
+ last_error = NULL;
}
/* Do *not* destroy the subrequest here; it may have allocated
* r->pool, so that pool must survive as long as this request.
* Yes, this is a memory leak. */
- if (error_fmt) {
- break;
- }
+ }
+
+ if (last_error) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
}
return APR_SUCCESS;