]> granicus.if.org Git - apache/blob - modules/http/http_protocol.c
core: Avoid some unexpected connection closes by telling the client
[apache] / modules / http / http_protocol.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * http_protocol.c --- routines which directly communicate with the client.
19  *
20  * Code originally by Rob McCool; much redone by Robert S. Thau
21  * and the Apache Software Foundation.
22  */
23
24 #include "apr.h"
25 #include "apr_strings.h"
26 #include "apr_buckets.h"
27 #include "apr_lib.h"
28 #include "apr_signal.h"
29
30 #define APR_WANT_STDIO          /* for sscanf */
31 #define APR_WANT_STRFUNC
32 #define APR_WANT_MEMFUNC
33 #include "apr_want.h"
34
35 #define CORE_PRIVATE
36 #include "util_filter.h"
37 #include "ap_config.h"
38 #include "httpd.h"
39 #include "http_config.h"
40 #include "http_core.h"
41 #include "http_protocol.h"
42 #include "http_main.h"
43 #include "http_request.h"
44 #include "http_vhost.h"
45 #include "http_log.h"           /* For errors detected in basic auth common
46                                  * support code... */
47 #include "apr_date.h"           /* For apr_date_parse_http and APR_DATE_BAD */
48 #include "util_charset.h"
49 #include "util_ebcdic.h"
50 #include "util_time.h"
51 #include "ap_mpm.h"
52
53 #include "mod_core.h"
54
55 #if APR_HAVE_STDARG_H
56 #include <stdarg.h>
57 #endif
58 #if APR_HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61
62 /* New Apache routine to map status codes into array indicies
63  *  e.g.  100 -> 0,  101 -> 1,  200 -> 2 ...
64  * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
65  * and must be listed in order.
66  */
67
68 #ifdef UTS21
69 /* The second const triggers an assembler bug on UTS 2.1.
70  * Another workaround is to move some code out of this file into another,
71  *   but this is easier.  Dave Dykstra, 3/31/99
72  */
73 static const char * status_lines[RESPONSE_CODES] =
74 #else
75 static const char * const status_lines[RESPONSE_CODES] =
76 #endif
77 {
78     "100 Continue",
79     "101 Switching Protocols",
80     "102 Processing",
81 #define LEVEL_200  3
82     "200 OK",
83     "201 Created",
84     "202 Accepted",
85     "203 Non-Authoritative Information",
86     "204 No Content",
87     "205 Reset Content",
88     "206 Partial Content",
89     "207 Multi-Status",
90 #define LEVEL_300 11
91     "300 Multiple Choices",
92     "301 Moved Permanently",
93     "302 Found",
94     "303 See Other",
95     "304 Not Modified",
96     "305 Use Proxy",
97     "306 unused",
98     "307 Temporary Redirect",
99 #define LEVEL_400 19
100     "400 Bad Request",
101     "401 Authorization Required",
102     "402 Payment Required",
103     "403 Forbidden",
104     "404 Not Found",
105     "405 Method Not Allowed",
106     "406 Not Acceptable",
107     "407 Proxy Authentication Required",
108     "408 Request Time-out",
109     "409 Conflict",
110     "410 Gone",
111     "411 Length Required",
112     "412 Precondition Failed",
113     "413 Request Entity Too Large",
114     "414 Request-URI Too Large",
115     "415 Unsupported Media Type",
116     "416 Requested Range Not Satisfiable",
117     "417 Expectation Failed",
118     "418 unused",
119     "419 unused",
120     "420 unused",
121     "421 unused",
122     "422 Unprocessable Entity",
123     "423 Locked",
124     "424 Failed Dependency",
125     /* This is a hack, but it is required for ap_index_of_response
126      * to work with 426.
127      */
128     "425 No code",
129     "426 Upgrade Required",
130 #define LEVEL_500 46
131     "500 Internal Server Error",
132     "501 Method Not Implemented",
133     "502 Bad Gateway",
134     "503 Service Temporarily Unavailable",
135     "504 Gateway Time-out",
136     "505 HTTP Version Not Supported",
137     "506 Variant Also Negotiates",
138     "507 Insufficient Storage",
139     "508 unused",
140     "509 unused",
141     "510 Not Extended"
142 };
143
144 APR_HOOK_STRUCT(
145     APR_HOOK_LINK(insert_error_filter)
146 )
147
148 AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
149
150 /* The index of the first bit field that is used to index into a limit
151  * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
152  */
153 #define METHOD_NUMBER_FIRST (M_INVALID + 1)
154
155 /* The max method number. Method numbers are used to shift bitmasks,
156  * so this cannot exceed 63, and all bits high is equal to -1, which is a
157  * special flag, so the last bit used has index 62.
158  */
159 #define METHOD_NUMBER_LAST  62
160
161
162 AP_DECLARE(int) ap_set_keepalive(request_rec *r)
163 {
164     int ka_sent = 0;
165     int wimpy = ap_find_token(r->pool,
166                               apr_table_get(r->headers_out, "Connection"),
167                               "close");
168     const char *conn = apr_table_get(r->headers_in, "Connection");
169
170     /* The following convoluted conditional determines whether or not
171      * the current connection should remain persistent after this response
172      * (a.k.a. HTTP Keep-Alive) and whether or not the output message
173      * body should use the HTTP/1.1 chunked transfer-coding.  In English,
174      *
175      *   IF  we have not marked this connection as errored;
176      *   and the response body has a defined length due to the status code
177      *       being 304 or 204, the request method being HEAD, already
178      *       having defined Content-Length or Transfer-Encoding: chunked, or
179      *       the request version being HTTP/1.1 and thus capable of being set
180      *       as chunked [we know the (r->chunked = 1) side-effect is ugly];
181      *   and the server configuration enables keep-alive;
182      *   and the server configuration has a reasonable inter-request timeout;
183      *   and there is no maximum # requests or the max hasn't been reached;
184      *   and the response status does not require a close;
185      *   and the response generator has not already indicated close;
186      *   and the client did not request non-persistence (Connection: close);
187      *   and    we haven't been configured to ignore the buggy twit
188      *       or they're a buggy twit coming through a HTTP/1.1 proxy
189      *   and    the client is requesting an HTTP/1.0-style keep-alive
190      *       or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
191      *   and this MPM process is not already exiting
192      *   THEN we can be persistent, which requires more headers be output.
193      *
194      * Note that the condition evaluation order is extremely important.
195      */
196     if ((r->connection->keepalive != AP_CONN_CLOSE)
197         && ((r->status == HTTP_NOT_MODIFIED)
198             || (r->status == HTTP_NO_CONTENT)
199             || r->header_only
200             || apr_table_get(r->headers_out, "Content-Length")
201             || ap_find_last_token(r->pool,
202                                   apr_table_get(r->headers_out,
203                                                 "Transfer-Encoding"),
204                                   "chunked")
205             || ((r->proto_num >= HTTP_VERSION(1,1))
206                 && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
207         && r->server->keep_alive
208         && (r->server->keep_alive_timeout > 0)
209         && ((r->server->keep_alive_max == 0)
210             || (r->server->keep_alive_max > r->connection->keepalives))
211         && !ap_status_drops_connection(r->status)
212         && !wimpy
213         && !ap_find_token(r->pool, conn, "close")
214         && (!apr_table_get(r->subprocess_env, "nokeepalive")
215             || apr_table_get(r->headers_in, "Via"))
216         && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
217             || (r->proto_num >= HTTP_VERSION(1,1)))
218         && !ap_graceful_stop_signalled()) {
219         int left = r->server->keep_alive_max - r->connection->keepalives;
220
221         r->connection->keepalive = AP_CONN_KEEPALIVE;
222         r->connection->keepalives++;
223
224         /* If they sent a Keep-Alive token, send one back */
225         if (ka_sent) {
226             if (r->server->keep_alive_max) {
227                 apr_table_setn(r->headers_out, "Keep-Alive",
228                        apr_psprintf(r->pool, "timeout=%d, max=%d",
229                             (int)apr_time_sec(r->server->keep_alive_timeout),
230                             left));
231             }
232             else {
233                 apr_table_setn(r->headers_out, "Keep-Alive",
234                       apr_psprintf(r->pool, "timeout=%d",
235                             (int)apr_time_sec(r->server->keep_alive_timeout)));
236             }
237             apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
238         }
239
240         return 1;
241     }
242
243     /* Otherwise, we need to indicate that we will be closing this
244      * connection immediately after the current response.
245      *
246      * We only really need to send "close" to HTTP/1.1 clients, but we
247      * always send it anyway, because a broken proxy may identify itself
248      * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
249      * to a HTTP/1.1 client. Better safe than sorry.
250      */
251     if (!wimpy) {
252         apr_table_mergen(r->headers_out, "Connection", "close");
253     }
254
255     r->connection->keepalive = AP_CONN_CLOSE;
256
257     return 0;
258 }
259
260 AP_DECLARE(int) ap_meets_conditions(request_rec *r)
261 {
262     const char *etag;
263     const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
264     apr_time_t tmp_time;
265     apr_int64_t mtime;
266     int not_modified = 0;
267
268     /* Check for conditional requests --- note that we only want to do
269      * this if we are successful so far and we are not processing a
270      * subrequest or an ErrorDocument.
271      *
272      * The order of the checks is important, since ETag checks are supposed
273      * to be more accurate than checks relative to the modification time.
274      * However, not all documents are guaranteed to *have* ETags, and some
275      * might have Last-Modified values w/o ETags, so this gets a little
276      * complicated.
277      */
278
279     if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
280         return OK;
281     }
282
283     etag = apr_table_get(r->headers_out, "ETag");
284
285     /* All of our comparisons must be in seconds, because that's the
286      * highest time resolution the HTTP specification allows.
287      */
288     /* XXX: we should define a "time unset" constant */
289     tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
290     mtime =  apr_time_sec(tmp_time);
291
292     /* If an If-Match request-header field was given
293      * AND the field value is not "*" (meaning match anything)
294      * AND if our strong ETag does not match any entity tag in that field,
295      *     respond with a status of 412 (Precondition Failed).
296      */
297     if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
298         if (if_match[0] != '*'
299             && (etag == NULL || etag[0] == 'W'
300                 || !ap_find_list_item(r->pool, if_match, etag))) {
301             return HTTP_PRECONDITION_FAILED;
302         }
303     }
304     else {
305         /* Else if a valid If-Unmodified-Since request-header field was given
306          * AND the requested resource has been modified since the time
307          * specified in this field, then the server MUST
308          *     respond with a status of 412 (Precondition Failed).
309          */
310         if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
311         if (if_unmodified != NULL) {
312             apr_time_t ius = apr_date_parse_http(if_unmodified);
313
314             if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
315                 return HTTP_PRECONDITION_FAILED;
316             }
317         }
318     }
319
320     /* If an If-None-Match request-header field was given
321      * AND the field value is "*" (meaning match anything)
322      *     OR our ETag matches any of the entity tags in that field, fail.
323      *
324      * If the request method was GET or HEAD, failure means the server
325      *    SHOULD respond with a 304 (Not Modified) response.
326      * For all other request methods, failure means the server MUST
327      *    respond with a status of 412 (Precondition Failed).
328      *
329      * GET or HEAD allow weak etag comparison, all other methods require
330      * strong comparison.  We can only use weak if it's not a range request.
331      */
332     if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
333     if (if_nonematch != NULL) {
334         if (r->method_number == M_GET) {
335             if (if_nonematch[0] == '*') {
336                 not_modified = 1;
337             }
338             else if (etag != NULL) {
339                 if (apr_table_get(r->headers_in, "Range")) {
340                     not_modified = etag[0] != 'W'
341                                    && ap_find_list_item(r->pool,
342                                                         if_nonematch, etag);
343                 }
344                 else {
345                     not_modified = ap_find_list_item(r->pool,
346                                                      if_nonematch, etag);
347                 }
348             }
349         }
350         else if (if_nonematch[0] == '*'
351                  || (etag != NULL
352                      && ap_find_list_item(r->pool, if_nonematch, etag))) {
353             return HTTP_PRECONDITION_FAILED;
354         }
355     }
356
357     /* If a valid If-Modified-Since request-header field was given
358      * AND it is a GET or HEAD request
359      * AND the requested resource has not been modified since the time
360      * specified in this field, then the server MUST
361      *    respond with a status of 304 (Not Modified).
362      * A date later than the server's current request time is invalid.
363      */
364     if (r->method_number == M_GET
365         && (not_modified || !if_nonematch)
366         && (if_modified_since =
367               apr_table_get(r->headers_in,
368                             "If-Modified-Since")) != NULL) {
369         apr_time_t ims_time;
370         apr_int64_t ims, reqtime;
371
372         ims_time = apr_date_parse_http(if_modified_since);
373         ims = apr_time_sec(ims_time);
374         reqtime = apr_time_sec(r->request_time);
375
376         not_modified = ims >= mtime && ims <= reqtime;
377     }
378
379     if (not_modified) {
380         return HTTP_NOT_MODIFIED;
381     }
382
383     return OK;
384 }
385
386 /**
387  * Singleton registry of additional methods. This maps new method names
388  * such as "MYGET" to methnums, which are int offsets into bitmasks.
389  *
390  * This follows the same technique as standard M_GET, M_POST, etc. These
391  * are dynamically assigned when modules are loaded and <Limit GET MYGET>
392  * directives are processed.
393  */
394 static apr_hash_t *methods_registry = NULL;
395 static int cur_method_number = METHOD_NUMBER_FIRST;
396
397 /* internal function to register one method/number pair */
398 static void register_one_method(apr_pool_t *p, const char *methname,
399                                 int methnum)
400 {
401     int *pnum = apr_palloc(p, sizeof(*pnum));
402
403     *pnum = methnum;
404     apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
405 }
406
407 /* This internal function is used to clear the method registry
408  * and reset the cur_method_number counter.
409  */
410 static apr_status_t ap_method_registry_destroy(void *notused)
411 {
412     methods_registry = NULL;
413     cur_method_number = METHOD_NUMBER_FIRST;
414     return APR_SUCCESS;
415 }
416
417 AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
418 {
419     methods_registry = apr_hash_make(p);
420     apr_pool_cleanup_register(p, NULL,
421                               ap_method_registry_destroy,
422                               apr_pool_cleanup_null);
423
424     /* put all the standard methods into the registry hash to ease the
425        mapping operations between name and number */
426     register_one_method(p, "GET", M_GET);
427     register_one_method(p, "PUT", M_PUT);
428     register_one_method(p, "POST", M_POST);
429     register_one_method(p, "DELETE", M_DELETE);
430     register_one_method(p, "CONNECT", M_CONNECT);
431     register_one_method(p, "OPTIONS", M_OPTIONS);
432     register_one_method(p, "TRACE", M_TRACE);
433     register_one_method(p, "PATCH", M_PATCH);
434     register_one_method(p, "PROPFIND", M_PROPFIND);
435     register_one_method(p, "PROPPATCH", M_PROPPATCH);
436     register_one_method(p, "MKCOL", M_MKCOL);
437     register_one_method(p, "COPY", M_COPY);
438     register_one_method(p, "MOVE", M_MOVE);
439     register_one_method(p, "LOCK", M_LOCK);
440     register_one_method(p, "UNLOCK", M_UNLOCK);
441     register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
442     register_one_method(p, "CHECKOUT", M_CHECKOUT);
443     register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
444     register_one_method(p, "CHECKIN", M_CHECKIN);
445     register_one_method(p, "UPDATE", M_UPDATE);
446     register_one_method(p, "LABEL", M_LABEL);
447     register_one_method(p, "REPORT", M_REPORT);
448     register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
449     register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
450     register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
451     register_one_method(p, "MERGE", M_MERGE);
452 }
453
454 AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
455 {
456     int *methnum;
457
458     if (methods_registry == NULL) {
459         ap_method_registry_init(p);
460     }
461
462     if (methname == NULL) {
463         return M_INVALID;
464     }
465
466     /* Check if the method was previously registered.  If it was
467      * return the associated method number.
468      */
469     methnum = (int *)apr_hash_get(methods_registry, methname,
470                                   APR_HASH_KEY_STRING);
471     if (methnum != NULL)
472         return *methnum;
473
474     if (cur_method_number > METHOD_NUMBER_LAST) {
475         /* The method registry  has run out of dynamically
476          * assignable method numbers. Log this and return M_INVALID.
477          */
478         ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p,
479                       "Maximum new request methods %d reached while "
480                       "registering method %s.",
481                       METHOD_NUMBER_LAST, methname);
482         return M_INVALID;
483     }
484
485     register_one_method(p, methname, cur_method_number);
486     return cur_method_number++;
487 }
488
489 #define UNKNOWN_METHOD (-1)
490
491 static int lookup_builtin_method(const char *method, apr_size_t len)
492 {
493     /* Note: the following code was generated by the "shilka" tool from
494        the "cocom" parsing/compilation toolkit. It is an optimized lookup
495        based on analysis of the input keywords. Postprocessing was done
496        on the shilka output, but the basic structure and analysis is
497        from there. Should new HTTP methods be added, then manual insertion
498        into this code is fine, or simply re-running the shilka tool on
499        the appropriate input. */
500
501     /* Note: it is also quite reasonable to just use our method_registry,
502        but I'm assuming (probably incorrectly) we want more speed here
503        (based on the optimizations the previous code was doing). */
504
505     switch (len)
506     {
507     case 3:
508         switch (method[0])
509         {
510         case 'P':
511             return (method[1] == 'U'
512                     && method[2] == 'T'
513                     ? M_PUT : UNKNOWN_METHOD);
514         case 'G':
515             return (method[1] == 'E'
516                     && method[2] == 'T'
517                     ? M_GET : UNKNOWN_METHOD);
518         default:
519             return UNKNOWN_METHOD;
520         }
521
522     case 4:
523         switch (method[0])
524         {
525         case 'H':
526             return (method[1] == 'E'
527                     && method[2] == 'A'
528                     && method[3] == 'D'
529                     ? M_GET : UNKNOWN_METHOD);
530         case 'P':
531             return (method[1] == 'O'
532                     && method[2] == 'S'
533                     && method[3] == 'T'
534                     ? M_POST : UNKNOWN_METHOD);
535         case 'M':
536             return (method[1] == 'O'
537                     && method[2] == 'V'
538                     && method[3] == 'E'
539                     ? M_MOVE : UNKNOWN_METHOD);
540         case 'L':
541             return (method[1] == 'O'
542                     && method[2] == 'C'
543                     && method[3] == 'K'
544                     ? M_LOCK : UNKNOWN_METHOD);
545         case 'C':
546             return (method[1] == 'O'
547                     && method[2] == 'P'
548                     && method[3] == 'Y'
549                     ? M_COPY : UNKNOWN_METHOD);
550         default:
551             return UNKNOWN_METHOD;
552         }
553
554     case 5:
555         switch (method[2])
556         {
557         case 'T':
558             return (memcmp(method, "PATCH", 5) == 0
559                     ? M_PATCH : UNKNOWN_METHOD);
560         case 'R':
561             return (memcmp(method, "MERGE", 5) == 0
562                     ? M_MERGE : UNKNOWN_METHOD);
563         case 'C':
564             return (memcmp(method, "MKCOL", 5) == 0
565                     ? M_MKCOL : UNKNOWN_METHOD);
566         case 'B':
567             return (memcmp(method, "LABEL", 5) == 0
568                     ? M_LABEL : UNKNOWN_METHOD);
569         case 'A':
570             return (memcmp(method, "TRACE", 5) == 0
571                     ? M_TRACE : UNKNOWN_METHOD);
572         default:
573             return UNKNOWN_METHOD;
574         }
575
576     case 6:
577         switch (method[0])
578         {
579         case 'U':
580             switch (method[5])
581             {
582             case 'K':
583                 return (memcmp(method, "UNLOCK", 6) == 0
584                         ? M_UNLOCK : UNKNOWN_METHOD);
585             case 'E':
586                 return (memcmp(method, "UPDATE", 6) == 0
587                         ? M_UPDATE : UNKNOWN_METHOD);
588             default:
589                 return UNKNOWN_METHOD;
590             }
591         case 'R':
592             return (memcmp(method, "REPORT", 6) == 0
593                     ? M_REPORT : UNKNOWN_METHOD);
594         case 'D':
595             return (memcmp(method, "DELETE", 6) == 0
596                     ? M_DELETE : UNKNOWN_METHOD);
597         default:
598             return UNKNOWN_METHOD;
599         }
600
601     case 7:
602         switch (method[1])
603         {
604         case 'P':
605             return (memcmp(method, "OPTIONS", 7) == 0
606                     ? M_OPTIONS : UNKNOWN_METHOD);
607         case 'O':
608             return (memcmp(method, "CONNECT", 7) == 0
609                     ? M_CONNECT : UNKNOWN_METHOD);
610         case 'H':
611             return (memcmp(method, "CHECKIN", 7) == 0
612                     ? M_CHECKIN : UNKNOWN_METHOD);
613         default:
614             return UNKNOWN_METHOD;
615         }
616
617     case 8:
618         switch (method[0])
619         {
620         case 'P':
621             return (memcmp(method, "PROPFIND", 8) == 0
622                     ? M_PROPFIND : UNKNOWN_METHOD);
623         case 'C':
624             return (memcmp(method, "CHECKOUT", 8) == 0
625                     ? M_CHECKOUT : UNKNOWN_METHOD);
626         default:
627             return UNKNOWN_METHOD;
628         }
629
630     case 9:
631         return (memcmp(method, "PROPPATCH", 9) == 0
632                 ? M_PROPPATCH : UNKNOWN_METHOD);
633
634     case 10:
635         switch (method[0])
636         {
637         case 'U':
638             return (memcmp(method, "UNCHECKOUT", 10) == 0
639                     ? M_UNCHECKOUT : UNKNOWN_METHOD);
640         case 'M':
641             return (memcmp(method, "MKACTIVITY", 10) == 0
642                     ? M_MKACTIVITY : UNKNOWN_METHOD);
643         default:
644             return UNKNOWN_METHOD;
645         }
646
647     case 11:
648         return (memcmp(method, "MKWORKSPACE", 11) == 0
649                 ? M_MKWORKSPACE : UNKNOWN_METHOD);
650
651     case 15:
652         return (memcmp(method, "VERSION-CONTROL", 15) == 0
653                 ? M_VERSION_CONTROL : UNKNOWN_METHOD);
654
655     case 16:
656         return (memcmp(method, "BASELINE-CONTROL", 16) == 0
657                 ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
658
659     default:
660         return UNKNOWN_METHOD;
661     }
662
663     /* NOTREACHED */
664 }
665
666 /* Get the method number associated with the given string, assumed to
667  * contain an HTTP method.  Returns M_INVALID if not recognized.
668  *
669  * This is the first step toward placing method names in a configurable
670  * list.  Hopefully it (and other routines) can eventually be moved to
671  * something like a mod_http_methods.c, complete with config stuff.
672  */
673 AP_DECLARE(int) ap_method_number_of(const char *method)
674 {
675     int len = strlen(method);
676     int which = lookup_builtin_method(method, len);
677
678     if (which != UNKNOWN_METHOD)
679         return which;
680
681     /* check if the method has been dynamically registered */
682     if (methods_registry != NULL) {
683         int *methnum = apr_hash_get(methods_registry, method, len);
684
685         if (methnum != NULL) {
686             return *methnum;
687         }
688     }
689
690     return M_INVALID;
691 }
692
693 /*
694  * Turn a known method number into a name.
695  */
696 AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
697 {
698     apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
699
700     /* scan through the hash table, looking for a value that matches
701        the provided method number. */
702     for (; hi; hi = apr_hash_next(hi)) {
703         const void *key;
704         void *val;
705
706         apr_hash_this(hi, &key, NULL, &val);
707         if (*(int *)val == methnum)
708             return key;
709     }
710
711     /* it wasn't found in the hash */
712     return NULL;
713 }
714
715 /* The index is found by its offset from the x00 code of each level.
716  * Although this is fast, it will need to be replaced if some nutcase
717  * decides to define a high-numbered code before the lower numbers.
718  * If that sad event occurs, replace the code below with a linear search
719  * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
720  */
721 AP_DECLARE(int) ap_index_of_response(int status)
722 {
723     static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
724     LEVEL_500, RESPONSE_CODES};
725     int i, pos;
726
727     if (status < 100) {               /* Below 100 is illegal for HTTP status */
728         return LEVEL_500;
729     }
730
731     for (i = 0; i < 5; i++) {
732         status -= 100;
733         if (status < 100) {
734             pos = (status + shortcut[i]);
735             if (pos < shortcut[i + 1]) {
736                 return pos;
737             }
738             else {
739                 return LEVEL_500;            /* status unknown (falls in gap) */
740             }
741         }
742     }
743     return LEVEL_500;                         /* 600 or above is also illegal */
744 }
745
746 AP_DECLARE(const char *) ap_get_status_line(int status)
747 {
748     return status_lines[ap_index_of_response(status)];
749 }
750
751 /* Build the Allow field-value from the request handler method mask.
752  * Note that we always allow TRACE, since it is handled below.
753  */
754 static char *make_allow(request_rec *r)
755 {
756     char *list;
757     apr_int64_t mask;
758     apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
759     apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
760     /* For TRACE below */
761     core_server_config *conf =
762         ap_get_module_config(r->server->module_config, &core_module);
763
764     mask = r->allowed_methods->method_mask;
765
766     for (; hi; hi = apr_hash_next(hi)) {
767         const void *key;
768         void *val;
769
770         apr_hash_this(hi, &key, NULL, &val);
771         if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
772             *(const char **)apr_array_push(allow) = key;
773
774             /* the M_GET method actually refers to two methods */
775             if (*(int *)val == M_GET)
776                 *(const char **)apr_array_push(allow) = "HEAD";
777         }
778     }
779
780     /* TRACE is tested on a per-server basis */
781     if (conf->trace_enable != AP_TRACE_DISABLE)
782         *(const char **)apr_array_push(allow) = "TRACE";
783
784     list = apr_array_pstrcat(r->pool, allow, ',');
785
786     /* ### this is rather annoying. we should enforce registration of
787        ### these methods */
788     if ((mask & (AP_METHOD_BIT << M_INVALID))
789         && (r->allowed_methods->method_list != NULL)
790         && (r->allowed_methods->method_list->nelts != 0)) {
791         int i;
792         char **xmethod = (char **) r->allowed_methods->method_list->elts;
793
794         /*
795          * Append all of the elements of r->allowed_methods->method_list
796          */
797         for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
798             list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL);
799         }
800     }
801
802     return list;
803 }
804
805 AP_DECLARE(int) ap_send_http_options(request_rec *r)
806 {
807     if (r->assbackwards) {
808         return DECLINED;
809     }
810
811     apr_table_setn(r->headers_out, "Allow", make_allow(r));
812
813     /* the request finalization will send an EOS, which will flush all
814      * the headers out (including the Allow header)
815      */
816
817     return OK;
818 }
819
820 AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
821 {
822     if (!ct) {
823         r->content_type = NULL;
824     }
825     else if (!r->content_type || strcmp(r->content_type, ct)) {
826         r->content_type = ct;
827
828         /* Insert filters requested by the AddOutputFiltersByType
829          * configuration directive. Content-type filters must be
830          * inserted after the content handlers have run because
831          * only then, do we reliably know the content-type.
832          */
833         ap_add_output_filters_by_type(r);
834     }
835 }
836
837 static const char *add_optional_notes(request_rec *r,
838                                       const char *prefix,
839                                       const char *key,
840                                       const char *suffix)
841 {
842     const char *notes, *result;
843
844     if ((notes = apr_table_get(r->notes, key)) == NULL) {
845         result = apr_pstrcat(r->pool, prefix, suffix, NULL);
846     }
847     else {
848         result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
849     }
850
851     return result;
852 }
853
854 /* construct and return the default error message for a given
855  * HTTP defined error code
856  */
857 static const char *get_canned_error_string(int status,
858                                            request_rec *r,
859                                            const char *location)
860 {
861     apr_pool_t *p = r->pool;
862     const char *error_notes, *h1, *s1;
863
864     switch (status) {
865     case HTTP_MOVED_PERMANENTLY:
866     case HTTP_MOVED_TEMPORARILY:
867     case HTTP_TEMPORARY_REDIRECT:
868         return(apr_pstrcat(p,
869                            "<p>The document has moved <a href=\"",
870                            ap_escape_html(r->pool, location),
871                            "\">here</a>.</p>\n",
872                            NULL));
873     case HTTP_SEE_OTHER:
874         return(apr_pstrcat(p,
875                            "<p>The answer to your request is located "
876                            "<a href=\"",
877                            ap_escape_html(r->pool, location),
878                            "\">here</a>.</p>\n",
879                            NULL));
880     case HTTP_USE_PROXY:
881         return(apr_pstrcat(p,
882                            "<p>This resource is only accessible "
883                            "through the proxy\n",
884                            ap_escape_html(r->pool, location),
885                            "<br />\nYou will need to configure "
886                            "your client to use that proxy.</p>\n",
887                            NULL));
888     case HTTP_PROXY_AUTHENTICATION_REQUIRED:
889     case HTTP_UNAUTHORIZED:
890         return("<p>This server could not verify that you\n"
891                "are authorized to access the document\n"
892                "requested.  Either you supplied the wrong\n"
893                "credentials (e.g., bad password), or your\n"
894                "browser doesn't understand how to supply\n"
895                "the credentials required.</p>\n");
896     case HTTP_BAD_REQUEST:
897         return(add_optional_notes(r,
898                                   "<p>Your browser sent a request that "
899                                   "this server could not understand.<br />\n",
900                                   "error-notes",
901                                   "</p>\n"));
902     case HTTP_FORBIDDEN:
903         return(apr_pstrcat(p,
904                            "<p>You don't have permission to access ",
905                            ap_escape_html(r->pool, r->uri),
906                            "\non this server.</p>\n",
907                            NULL));
908     case HTTP_NOT_FOUND:
909         return(apr_pstrcat(p,
910                            "<p>The requested URL ",
911                            ap_escape_html(r->pool, r->uri),
912                            " was not found on this server.</p>\n",
913                            NULL));
914     case HTTP_METHOD_NOT_ALLOWED:
915         return(apr_pstrcat(p,
916                            "<p>The requested method ", r->method,
917                            " is not allowed for the URL ",
918                            ap_escape_html(r->pool, r->uri),
919                            ".</p>\n",
920                            NULL));
921     case HTTP_NOT_ACCEPTABLE:
922         s1 = apr_pstrcat(p,
923                          "<p>An appropriate representation of the "
924                          "requested resource ",
925                          ap_escape_html(r->pool, r->uri),
926                          " could not be found on this server.</p>\n",
927                          NULL);
928         return(add_optional_notes(r, s1, "variant-list", ""));
929     case HTTP_MULTIPLE_CHOICES:
930         return(add_optional_notes(r, "", "variant-list", ""));
931     case HTTP_LENGTH_REQUIRED:
932         s1 = apr_pstrcat(p,
933                          "<p>A request of the requested method ",
934                          r->method,
935                          " requires a valid Content-length.<br />\n",
936                          NULL);
937         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
938     case HTTP_PRECONDITION_FAILED:
939         return(apr_pstrcat(p,
940                            "<p>The precondition on the request "
941                            "for the URL ",
942                            ap_escape_html(r->pool, r->uri),
943                            " evaluated to false.</p>\n",
944                            NULL));
945     case HTTP_NOT_IMPLEMENTED:
946         s1 = apr_pstrcat(p,
947                          "<p>",
948                          ap_escape_html(r->pool, r->method), " to ",
949                          ap_escape_html(r->pool, r->uri),
950                          " not supported.<br />\n",
951                          NULL);
952         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
953     case HTTP_BAD_GATEWAY:
954         s1 = "<p>The proxy server received an invalid" CRLF
955             "response from an upstream server.<br />" CRLF;
956         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
957     case HTTP_VARIANT_ALSO_VARIES:
958         return(apr_pstrcat(p,
959                            "<p>A variant for the requested "
960                            "resource\n<pre>\n",
961                            ap_escape_html(r->pool, r->uri),
962                            "\n</pre>\nis itself a negotiable resource. "
963                            "This indicates a configuration error.</p>\n",
964                            NULL));
965     case HTTP_REQUEST_TIME_OUT:
966         return("<p>Server timeout waiting for the HTTP request from the client.</p>\n");
967     case HTTP_GONE:
968         return(apr_pstrcat(p,
969                            "<p>The requested resource<br />",
970                            ap_escape_html(r->pool, r->uri),
971                            "<br />\nis no longer available on this server "
972                            "and there is no forwarding address.\n"
973                            "Please remove all references to this "
974                            "resource.</p>\n",
975                            NULL));
976     case HTTP_REQUEST_ENTITY_TOO_LARGE:
977         return(apr_pstrcat(p,
978                            "The requested resource<br />",
979                            ap_escape_html(r->pool, r->uri), "<br />\n",
980                            "does not allow request data with ",
981                            r->method,
982                            " requests, or the amount of data provided in\n"
983                            "the request exceeds the capacity limit.\n",
984                            NULL));
985     case HTTP_REQUEST_URI_TOO_LARGE:
986         s1 = "<p>The requested URL's length exceeds the capacity\n"
987              "limit for this server.<br />\n";
988         return(add_optional_notes(r, s1, "error-notes", "</p>\n"));
989     case HTTP_UNSUPPORTED_MEDIA_TYPE:
990         return("<p>The supplied request data is not in a format\n"
991                "acceptable for processing by this resource.</p>\n");
992     case HTTP_RANGE_NOT_SATISFIABLE:
993         return("<p>None of the range-specifier values in the Range\n"
994                "request-header field overlap the current extent\n"
995                "of the selected resource.</p>\n");
996     case HTTP_EXPECTATION_FAILED:
997         return(apr_pstrcat(p,
998                            "<p>The expectation given in the Expect "
999                            "request-header"
1000                            "\nfield could not be met by this server.</p>\n"
1001                            "<p>The client sent<pre>\n    Expect: ",
1002                            ap_escape_html(r->pool, apr_table_get(r->headers_in, "Expect")),
1003                            "\n</pre>\n"
1004                            "but we only allow the 100-continue "
1005                            "expectation.</p>\n",
1006                            NULL));
1007     case HTTP_UNPROCESSABLE_ENTITY:
1008         return("<p>The server understands the media type of the\n"
1009                "request entity, but was unable to process the\n"
1010                "contained instructions.</p>\n");
1011     case HTTP_LOCKED:
1012         return("<p>The requested resource is currently locked.\n"
1013                "The lock must be released or proper identification\n"
1014                "given before the method can be applied.</p>\n");
1015     case HTTP_FAILED_DEPENDENCY:
1016         return("<p>The method could not be performed on the resource\n"
1017                "because the requested action depended on another\n"
1018                "action and that other action failed.</p>\n");
1019     case HTTP_UPGRADE_REQUIRED:
1020         return("<p>The requested resource can only be retrieved\n"
1021                "using SSL.  The server is willing to upgrade the current\n"
1022                "connection to SSL, but your client doesn't support it.\n"
1023                "Either upgrade your client, or try requesting the page\n"
1024                "using https://\n");
1025     case HTTP_INSUFFICIENT_STORAGE:
1026         return("<p>The method could not be performed on the resource\n"
1027                "because the server is unable to store the\n"
1028                "representation needed to successfully complete the\n"
1029                "request.  There is insufficient free space left in\n"
1030                "your storage allocation.</p>\n");
1031     case HTTP_SERVICE_UNAVAILABLE:
1032         return("<p>The server is temporarily unable to service your\n"
1033                "request due to maintenance downtime or capacity\n"
1034                "problems. Please try again later.</p>\n");
1035     case HTTP_GATEWAY_TIME_OUT:
1036         return("<p>The proxy server did not receive a timely response\n"
1037                "from the upstream server.</p>\n");
1038     case HTTP_NOT_EXTENDED:
1039         return("<p>A mandatory extension policy in the request is not\n"
1040                "accepted by the server for this resource.</p>\n");
1041     default:                    /* HTTP_INTERNAL_SERVER_ERROR */
1042         /*
1043          * This comparison to expose error-notes could be modified to
1044          * use a configuration directive and export based on that
1045          * directive.  For now "*" is used to designate an error-notes
1046          * that is totally safe for any user to see (ie lacks paths,
1047          * database passwords, etc.)
1048          */
1049         if (((error_notes = apr_table_get(r->notes,
1050                                           "error-notes")) != NULL)
1051             && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
1052             && (strcmp(h1, "*") == 0)) {
1053             return(apr_pstrcat(p, error_notes, "<p />\n", NULL));
1054         }
1055         else {
1056             return(apr_pstrcat(p,
1057                                "<p>The server encountered an internal "
1058                                "error or\n"
1059                                "misconfiguration and was unable to complete\n"
1060                                "your request.</p>\n"
1061                                "<p>Please contact the server "
1062                                "administrator,\n ",
1063                                ap_escape_html(r->pool,
1064                                               r->server->server_admin),
1065                                " and inform them of the time the "
1066                                "error occurred,\n"
1067                                "and anything you might have done that "
1068                                "may have\n"
1069                                "caused the error.</p>\n"
1070                                "<p>More information about this error "
1071                                "may be available\n"
1072                                "in the server error log.</p>\n",
1073                                NULL));
1074         }
1075         /*
1076          * It would be nice to give the user the information they need to
1077          * fix the problem directly since many users don't have access to
1078          * the error_log (think University sites) even though they can easily
1079          * get this error by misconfiguring an htaccess file.  However, the
1080          * e error notes tend to include the real file pathname in this case,
1081          * which some people consider to be a breach of privacy.  Until we
1082          * can figure out a way to remove the pathname, leave this commented.
1083          *
1084          * if ((error_notes = apr_table_get(r->notes,
1085          *                                  "error-notes")) != NULL) {
1086          *     return(apr_pstrcat(p, error_notes, "<p />\n", NULL);
1087          * }
1088          * else {
1089          *     return "";
1090          * }
1091          */
1092     }
1093 }
1094
1095 /* We should have named this send_canned_response, since it is used for any
1096  * response that can be generated by the server from the request record.
1097  * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
1098  * and 5xx (server error) messages that have not been redirected to another
1099  * handler via the ErrorDocument feature.
1100  */
1101 AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
1102 {
1103     int status = r->status;
1104     int idx = ap_index_of_response(status);
1105     char *custom_response;
1106     const char *location = apr_table_get(r->headers_out, "Location");
1107
1108     /* At this point, we are starting the response over, so we have to reset
1109      * this value.
1110      */
1111     r->eos_sent = 0;
1112
1113     /* and we need to get rid of any RESOURCE filters that might be lurking
1114      * around, thinking they are in the middle of the original request
1115      */
1116
1117     r->output_filters = r->proto_output_filters;
1118
1119     ap_run_insert_error_filter(r);
1120
1121     /*
1122      * It's possible that the Location field might be in r->err_headers_out
1123      * instead of r->headers_out; use the latter if possible, else the
1124      * former.
1125      */
1126     if (location == NULL) {
1127         location = apr_table_get(r->err_headers_out, "Location");
1128     }
1129     /* We need to special-case the handling of 204 and 304 responses,
1130      * since they have specific HTTP requirements and do not include a
1131      * message body.  Note that being assbackwards here is not an option.
1132      */
1133     if (status == HTTP_NOT_MODIFIED) {
1134         ap_finalize_request_protocol(r);
1135         return;
1136     }
1137
1138     if (status == HTTP_NO_CONTENT) {
1139         ap_finalize_request_protocol(r);
1140         return;
1141     }
1142
1143     if (!r->assbackwards) {
1144         apr_table_t *tmp = r->headers_out;
1145
1146         /* For all HTTP/1.x responses for which we generate the message,
1147          * we need to avoid inheriting the "normal status" header fields
1148          * that may have been set by the request handler before the
1149          * error or redirect, except for Location on external redirects.
1150          */
1151         r->headers_out = r->err_headers_out;
1152         r->err_headers_out = tmp;
1153         apr_table_clear(r->err_headers_out);
1154
1155         if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
1156             if ((location != NULL) && *location) {
1157                 apr_table_setn(r->headers_out, "Location", location);
1158             }
1159             else {
1160                 location = "";   /* avoids coredump when printing, below */
1161             }
1162         }
1163
1164         r->content_languages = NULL;
1165         r->content_encoding = NULL;
1166         r->clength = 0;
1167
1168         if (apr_table_get(r->subprocess_env,
1169                           "suppress-error-charset") != NULL) {
1170             core_request_config *request_conf =
1171                         ap_get_module_config(r->request_config, &core_module);
1172             request_conf->suppress_charset = 1; /* avoid adding default
1173                                                  * charset later
1174                                                  */
1175             ap_set_content_type(r, "text/html");
1176         }
1177         else {
1178             ap_set_content_type(r, "text/html; charset=iso-8859-1");
1179         }
1180
1181         if ((status == HTTP_METHOD_NOT_ALLOWED)
1182             || (status == HTTP_NOT_IMPLEMENTED)) {
1183             apr_table_setn(r->headers_out, "Allow", make_allow(r));
1184         }
1185
1186         if (r->header_only) {
1187             ap_finalize_request_protocol(r);
1188             return;
1189         }
1190     }
1191
1192     if ((custom_response = ap_response_code_string(r, idx))) {
1193         /*
1194          * We have a custom response output. This should only be
1195          * a text-string to write back. But if the ErrorDocument
1196          * was a local redirect and the requested resource failed
1197          * for any reason, the custom_response will still hold the
1198          * redirect URL. We don't really want to output this URL
1199          * as a text message, so first check the custom response
1200          * string to ensure that it is a text-string (using the
1201          * same test used in ap_die(), i.e. does it start with a ").
1202          *
1203          * If it's not a text string, we've got a recursive error or
1204          * an external redirect.  If it's a recursive error, ap_die passes
1205          * us the second error code so we can write both, and has already
1206          * backed up to the original error.  If it's an external redirect,
1207          * it hasn't happened yet; we may never know if it fails.
1208          */
1209         if (custom_response[0] == '\"') {
1210             ap_rputs(custom_response + 1, r);
1211             ap_finalize_request_protocol(r);
1212             return;
1213         }
1214     }
1215     {
1216         const char *title = status_lines[idx];
1217         const char *h1;
1218
1219         /* Accept a status_line set by a module, but only if it begins
1220          * with the 3 digit status code
1221          */
1222         if (r->status_line != NULL
1223             && strlen(r->status_line) > 4       /* long enough */
1224             && apr_isdigit(r->status_line[0])
1225             && apr_isdigit(r->status_line[1])
1226             && apr_isdigit(r->status_line[2])
1227             && apr_isspace(r->status_line[3])
1228             && apr_isalnum(r->status_line[4])) {
1229             title = r->status_line;
1230         }
1231
1232         /* folks decided they didn't want the error code in the H1 text */
1233         h1 = &title[4];
1234
1235         /* can't count on a charset filter being in place here,
1236          * so do ebcdic->ascii translation explicitly (if needed)
1237          */
1238
1239         ap_rvputs_proto_in_ascii(r,
1240                   DOCTYPE_HTML_2_0
1241                   "<html><head>\n<title>", title,
1242                   "</title>\n</head><body>\n<h1>", h1, "</h1>\n",
1243                   NULL);
1244
1245         ap_rvputs_proto_in_ascii(r,
1246                                  get_canned_error_string(status, r, location),
1247                                  NULL);
1248
1249         if (recursive_error) {
1250             ap_rvputs_proto_in_ascii(r, "<p>Additionally, a ",
1251                       status_lines[ap_index_of_response(recursive_error)],
1252                       "\nerror was encountered while trying to use an "
1253                       "ErrorDocument to handle the request.</p>\n", NULL);
1254         }
1255         ap_rvputs_proto_in_ascii(r, ap_psignature("<hr>\n", r), NULL);
1256         ap_rvputs_proto_in_ascii(r, "</body></html>\n", NULL);
1257     }
1258     ap_finalize_request_protocol(r);
1259 }
1260
1261 /*
1262  * Create a new method list with the specified number of preallocated
1263  * extension slots.
1264  */
1265 AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
1266 {
1267     ap_method_list_t *ml;
1268
1269     ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
1270     ml->method_mask = 0;
1271     ml->method_list = apr_array_make(p, nelts, sizeof(char *));
1272     return ml;
1273 }
1274
1275 /*
1276  * Make a copy of a method list (primarily for subrequests that may
1277  * subsequently change it; don't want them changing the parent's, too!).
1278  */
1279 AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
1280                                      ap_method_list_t *src)
1281 {
1282     int i;
1283     char **imethods;
1284     char **omethods;
1285
1286     dest->method_mask = src->method_mask;
1287     imethods = (char **) src->method_list->elts;
1288     for (i = 0; i < src->method_list->nelts; ++i) {
1289         omethods = (char **) apr_array_push(dest->method_list);
1290         *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
1291     }
1292 }
1293
1294 /*
1295  * Return true if the specified HTTP method is in the provided
1296  * method list.
1297  */
1298 AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
1299 {
1300     int methnum;
1301     int i;
1302     char **methods;
1303
1304     /*
1305      * If it's one of our known methods, use the shortcut and check the
1306      * bitmask.
1307      */
1308     methnum = ap_method_number_of(method);
1309     if (methnum != M_INVALID) {
1310         return !!(l->method_mask & (AP_METHOD_BIT << methnum));
1311     }
1312     /*
1313      * Otherwise, see if the method name is in the array or string names
1314      */
1315     if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
1316         return 0;
1317     }
1318     methods = (char **)l->method_list->elts;
1319     for (i = 0; i < l->method_list->nelts; ++i) {
1320         if (strcmp(method, methods[i]) == 0) {
1321             return 1;
1322         }
1323     }
1324     return 0;
1325 }
1326
1327 /*
1328  * Add the specified method to a method list (if it isn't already there).
1329  */
1330 AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
1331 {
1332     int methnum;
1333     int i;
1334     const char **xmethod;
1335     char **methods;
1336
1337     /*
1338      * If it's one of our known methods, use the shortcut and use the
1339      * bitmask.
1340      */
1341     methnum = ap_method_number_of(method);
1342     l->method_mask |= (AP_METHOD_BIT << methnum);
1343     if (methnum != M_INVALID) {
1344         return;
1345     }
1346     /*
1347      * Otherwise, see if the method name is in the array of string names.
1348      */
1349     if (l->method_list->nelts != 0) {
1350         methods = (char **)l->method_list->elts;
1351         for (i = 0; i < l->method_list->nelts; ++i) {
1352             if (strcmp(method, methods[i]) == 0) {
1353                 return;
1354             }
1355         }
1356     }
1357     xmethod = (const char **) apr_array_push(l->method_list);
1358     *xmethod = method;
1359 }
1360
1361 /*
1362  * Remove the specified method from a method list.
1363  */
1364 AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
1365                                        const char *method)
1366 {
1367     int methnum;
1368     char **methods;
1369
1370     /*
1371      * If it's a known methods, either builtin or registered
1372      * by a module, use the bitmask.
1373      */
1374     methnum = ap_method_number_of(method);
1375     l->method_mask |= ~(AP_METHOD_BIT << methnum);
1376     if (methnum != M_INVALID) {
1377         return;
1378     }
1379     /*
1380      * Otherwise, see if the method name is in the array of string names.
1381      */
1382     if (l->method_list->nelts != 0) {
1383         register int i, j, k;
1384         methods = (char **)l->method_list->elts;
1385         for (i = 0; i < l->method_list->nelts; ) {
1386             if (strcmp(method, methods[i]) == 0) {
1387                 for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
1388                     methods[j] = methods[k];
1389                 }
1390                 --l->method_list->nelts;
1391             }
1392             else {
1393                 ++i;
1394             }
1395         }
1396     }
1397 }
1398
1399 /*
1400  * Reset a method list to be completely empty.
1401  */
1402 AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
1403 {
1404     l->method_mask = 0;
1405     l->method_list->nelts = 0;
1406 }
1407