📄 protocol.c
字号:
/* Copyright 2001-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * http_protocol.c --- routines which directly communicate with the client. * * Code originally by Rob McCool; much redone by Robert S. Thau * and the Apache Software Foundation. */#include "apr.h"#include "apr_strings.h"#include "apr_buckets.h"#include "apr_lib.h"#include "apr_signal.h"#include "apr_strmatch.h"#define APR_WANT_STDIO /* for sscanf */#define APR_WANT_STRFUNC#define APR_WANT_MEMFUNC#include "apr_want.h"#define CORE_PRIVATE#include "util_filter.h"#include "ap_config.h"#include "httpd.h"#include "http_config.h"#include "http_core.h"#include "http_protocol.h"#include "http_main.h"#include "http_request.h"#include "http_vhost.h"#include "http_log.h" /* For errors detected in basic auth common * support code... */#include "mod_core.h"#include "util_charset.h"#include "util_ebcdic.h"#include "scoreboard.h"#if APR_HAVE_STDARG_H#include <stdarg.h>#endif#if APR_HAVE_UNISTD_H#include <unistd.h>#endifAPR_HOOK_STRUCT( APR_HOOK_LINK(post_read_request) APR_HOOK_LINK(log_transaction) APR_HOOK_LINK(http_method) APR_HOOK_LINK(default_port))AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL;/* Patterns to match in ap_make_content_type() */static const char *needcset[] = { "text/plain", "text/html", NULL};static const apr_strmatch_pattern **needcset_patterns;static const apr_strmatch_pattern *charset_pattern;AP_DECLARE(void) ap_setup_make_content_type(apr_pool_t *pool){ int i; for (i = 0; needcset[i]; i++) { continue; } needcset_patterns = (const apr_strmatch_pattern **) apr_palloc(pool, (i + 1) * sizeof(apr_strmatch_pattern *)); for (i = 0; needcset[i]; i++) { needcset_patterns[i] = apr_strmatch_precompile(pool, needcset[i], 0); } needcset_patterns[i] = NULL; charset_pattern = apr_strmatch_precompile(pool, "charset=", 0);}/* * Builds the content-type that should be sent to the client from the * content-type specified. The following rules are followed: * - if type is NULL, type is set to ap_default_type(r) * - if charset adding is disabled, stop processing and return type. * - then, if there are no parameters on type, add the default charset * - return type */AP_DECLARE(const char *)ap_make_content_type(request_rec *r, const char *type){ const apr_strmatch_pattern **pcset; core_dir_config *conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); apr_size_t type_len; if (!type) { type = ap_default_type(r); } if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) { return type; } type_len = strlen(type); if (apr_strmatch(charset_pattern, type, type_len) != NULL) { /* already has parameter, do nothing */ /* XXX we don't check the validity */ ; } else { /* see if it makes sense to add the charset. At present, * we only add it if the Content-type is one of needcset[] */ for (pcset = needcset_patterns; *pcset ; pcset++) { if (apr_strmatch(*pcset, type, type_len) != NULL) { struct iovec concat[3]; concat[0].iov_base = (void *)type; concat[0].iov_len = type_len; concat[1].iov_base = (void *)"; charset="; concat[1].iov_len = sizeof("; charset=") - 1; concat[2].iov_base = (void *)(conf->add_default_charset_name); concat[2].iov_len = strlen(conf->add_default_charset_name); type = apr_pstrcatv(r->pool, concat, 3, NULL); break; } } } return type;}AP_DECLARE(void) ap_set_content_length(request_rec *r, apr_off_t clength){ r->clength = clength; apr_table_setn(r->headers_out, "Content-Length", apr_off_t_toa(r->pool, clength));}/* * Return the latest rational time from a request/mtime (modification time) * pair. We return the mtime unless it's in the future, in which case we * return the current time. We use the request time as a reference in order * to limit the number of calls to time(). We don't check for futurosity * unless the mtime is at least as new as the reference. */AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime){ apr_time_t now; /* For all static responses, it's almost certain that the file was * last modified before the beginning of the request. So there's * no reason to call time(NULL) again. But if the response has been * created on demand, then it might be newer than the time the request * started. In this event we really have to call time(NULL) again * so that we can give the clients the most accurate Last-Modified. If we * were given a time in the future, we return the current time - the * Last-Modified can't be in the future. */ now = (mtime < r->request_time) ? r->request_time : apr_time_now(); return (mtime > now) ? now : mtime;}/* Min # of bytes to allocate when reading a request line */#define MIN_LINE_ALLOC 80/* Get a line of protocol input, including any continuation lines * caused by MIME folding (or broken clients) if fold != 0, and place it * in the buffer s, of size n bytes, without the ending newline. * * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool. * * Returns APR_SUCCESS if there are no problems and sets *read to be * the full length of s. * * APR_ENOSPC is returned if there is not enough buffer space. * Other errors may be returned on other errors. * * The LF is *not* returned in the buffer. Therefore, a *read of 0 * indicates that an empty line was read. * * Notes: Because the buffer uses 1 char for NUL, the most we can return is * (n - 1) actual characters. * * If no LF is detected on the last line due to a dropped connection * or a full buffer, that's considered an error. */AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_size_t *read, request_rec *r, int fold, apr_bucket_brigade *bb){ apr_status_t rv; apr_bucket *e; apr_size_t bytes_handled = 0, current_alloc = 0; char *pos, *last_char = *s; int do_alloc = (*s == NULL), saw_eos = 0; for (;;) { apr_brigade_cleanup(bb); rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); if (rv != APR_SUCCESS) { return rv; } /* Something horribly wrong happened. Someone didn't block! */ if (APR_BRIGADE_EMPTY(bb)) { return APR_EGENERAL; } APR_BRIGADE_FOREACH(e, bb) { const char *str; apr_size_t len; /* If we see an EOS, don't bother doing anything more. */ if (APR_BUCKET_IS_EOS(e)) { saw_eos = 1; break; } rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return rv; } if (len == 0) { /* no use attempting a zero-byte alloc (hurts when * using --with-efence --enable-pool-debug) or * doing any of the other logic either */ continue; } /* Would this overrun our buffer? If so, we'll die. */ if (n < bytes_handled + len) { *read = bytes_handled; if (*s) { /* ensure this string is NUL terminated */ if (bytes_handled > 0) { (*s)[bytes_handled-1] = '\0'; } else { (*s)[0] = '\0'; } } return APR_ENOSPC; } /* Do we have to handle the allocation ourselves? */ if (do_alloc) { /* We'll assume the common case where one bucket is enough. */ if (!*s) { current_alloc = len; if (current_alloc < MIN_LINE_ALLOC) { current_alloc = MIN_LINE_ALLOC; } *s = apr_palloc(r->pool, current_alloc); } else if (bytes_handled + len > current_alloc) { /* Increase the buffer size */ apr_size_t new_size = current_alloc * 2; char *new_buffer; if (bytes_handled + len > new_size) { new_size = (bytes_handled + len) * 2; } new_buffer = apr_palloc(r->pool, new_size); /* Copy what we already had. */ memcpy(new_buffer, *s, bytes_handled); current_alloc = new_size; *s = new_buffer; } } /* Just copy the rest of the data to the end of the old buffer. */ pos = *s + bytes_handled; memcpy(pos, str, len); last_char = pos + len - 1; /* We've now processed that new data - update accordingly. */ bytes_handled += len; } /* If we got a full line of input, stop reading */ if (last_char && (*last_char == APR_ASCII_LF)) { break; } } /* Now NUL-terminate the string at the end of the line; * if the last-but-one character is a CR, terminate there */ if (last_char > *s && last_char[-1] == APR_ASCII_CR) { last_char--; } *last_char = '\0'; bytes_handled = last_char - *s; /* If we're folding, we have more work to do. * * Note that if an EOS was seen, we know we can't have another line. */ if (fold && bytes_handled && !saw_eos) { for (;;) { const char *str; apr_size_t len; char c; /* Clear the temp brigade for this filter read. */ apr_brigade_cleanup(bb); /* We only care about the first byte. */ rv = ap_get_brigade(r->input_filters, bb, AP_MODE_SPECULATIVE, APR_BLOCK_READ, 1); if (rv != APR_SUCCESS) { return rv; } if (APR_BRIGADE_EMPTY(bb)) { break; } e = APR_BRIGADE_FIRST(bb); /* If we see an EOS, don't bother doing anything more. */ if (APR_BUCKET_IS_EOS(e)) { break; } rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { apr_brigade_cleanup(bb); return rv; } /* Found one, so call ourselves again to get the next line. * * FIXME: If the folding line is completely blank, should we * stop folding? Does that require also looking at the next * char? */ /* When we call destroy, the buckets are deleted, so save that * one character we need. This simplifies our execution paths * at the cost of one character read. */ c = *str; if (c == APR_ASCII_BLANK || c == APR_ASCII_TAB) { /* Do we have enough space? We may be full now. */ if (bytes_handled >= n) { *read = n; /* ensure this string is terminated */ (*s)[n-1] = '\0'; return APR_ENOSPC; } else { apr_size_t next_size, next_len; char *tmp; /* If we're doing the allocations for them, we have to * give ourselves a NULL and copy it on return. */ if (do_alloc) { tmp = NULL; } else { /* We're null terminated. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -