File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / http-header-glue.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 7 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: #include "base.h"
    4: #include "array.h"
    5: #include "buffer.h"
    6: #include "log.h"
    7: #include "etag.h"
    8: #include "http_chunk.h"
    9: #include "response.h"
   10: #include "stat_cache.h"
   11: 
   12: #include <string.h>
   13: #include <errno.h>
   14: 
   15: #include <time.h>
   16: 
   17: /*
   18:  * This was 'borrowed' from tcpdump.
   19:  *
   20:  *
   21:  * This is fun.
   22:  *
   23:  * In older BSD systems, socket addresses were fixed-length, and
   24:  * "sizeof (struct sockaddr)" gave the size of the structure.
   25:  * All addresses fit within a "struct sockaddr".
   26:  *
   27:  * In newer BSD systems, the socket address is variable-length, and
   28:  * there's an "sa_len" field giving the length of the structure;
   29:  * this allows socket addresses to be longer than 2 bytes of family
   30:  * and 14 bytes of data.
   31:  *
   32:  * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
   33:  * variant of the old BSD scheme (with "struct sockaddr_storage" rather
   34:  * than "struct sockaddr"), and some use the new BSD scheme.
   35:  *
   36:  * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
   37:  * macro that determines the size based on the address family.  Other
   38:  * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
   39:  * but not in the final version).  On the latter systems, we explicitly
   40:  * check the AF_ type to determine the length; we assume that on
   41:  * all those systems we have "struct sockaddr_storage".
   42:  */
   43: 
   44: #ifdef HAVE_IPV6
   45: # ifndef SA_LEN
   46: #  ifdef HAVE_SOCKADDR_SA_LEN
   47: #   define SA_LEN(addr)   ((addr)->sa_len)
   48: #  else /* HAVE_SOCKADDR_SA_LEN */
   49: #   ifdef HAVE_STRUCT_SOCKADDR_STORAGE
   50: static size_t get_sa_len(const struct sockaddr *addr) {
   51: 	switch (addr->sa_family) {
   52: 
   53: #    ifdef AF_INET
   54: 	case AF_INET:
   55: 		return (sizeof (struct sockaddr_in));
   56: #    endif
   57: 
   58: #    ifdef AF_INET6
   59: 	case AF_INET6:
   60: 		return (sizeof (struct sockaddr_in6));
   61: #    endif
   62: 
   63: 	default:
   64: 		return (sizeof (struct sockaddr));
   65: 
   66: 	}
   67: }
   68: #    define SA_LEN(addr)   (get_sa_len(addr))
   69: #   else /* HAVE_SOCKADDR_STORAGE */
   70: #    define SA_LEN(addr)   (sizeof (struct sockaddr))
   71: #   endif /* HAVE_SOCKADDR_STORAGE */
   72: #  endif /* HAVE_SOCKADDR_SA_LEN */
   73: # endif /* SA_LEN */
   74: #endif
   75: 
   76: 
   77: 
   78: 
   79: int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
   80: 	data_string *ds;
   81: 
   82: 	UNUSED(srv);
   83: 
   84: 	if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
   85: 		ds = data_response_init();
   86: 	}
   87: 	buffer_copy_string_len(ds->key, key, keylen);
   88: 	buffer_copy_string_len(ds->value, value, vallen);
   89: 
   90: 	array_insert_unique(con->response.headers, (data_unset *)ds);
   91: 
   92: 	return 0;
   93: }
   94: 
   95: int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
   96: 	data_string *ds;
   97: 
   98: 	UNUSED(srv);
   99: 
  100: 	/* if there already is a key by this name overwrite the value */
  101: 	if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
  102: 		buffer_copy_string(ds->value, value);
  103: 
  104: 		return 0;
  105: 	}
  106: 
  107: 	return response_header_insert(srv, con, key, keylen, value, vallen);
  108: }
  109: 
  110: int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
  111: 	data_string *ds;
  112: 
  113: 	UNUSED(srv);
  114: 
  115: 	/* if there already is a key by this name append the value */
  116: 	if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
  117: 		buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
  118: 		buffer_append_string_len(ds->value, value, vallen);
  119: 		return 0;
  120: 	}
  121: 
  122: 	return response_header_insert(srv, con, key, keylen, value, vallen);
  123: }
  124: 
  125: int http_response_redirect_to_directory(server *srv, connection *con) {
  126: 	buffer *o;
  127: 
  128: 	o = buffer_init();
  129: 
  130: 	buffer_copy_buffer(o, con->uri.scheme);
  131: 	buffer_append_string_len(o, CONST_STR_LEN("://"));
  132: 	if (!buffer_is_empty(con->uri.authority)) {
  133: 		buffer_append_string_buffer(o, con->uri.authority);
  134: 	} else {
  135: 		/* get the name of the currently connected socket */
  136: 		struct hostent *he;
  137: #ifdef HAVE_IPV6
  138: 		char hbuf[256];
  139: #endif
  140: 		sock_addr our_addr;
  141: 		socklen_t our_addr_len;
  142: 
  143: 		our_addr_len = sizeof(our_addr);
  144: 
  145: 		if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
  146: 		    || our_addr_len > (socklen_t)sizeof(our_addr)) {
  147: 			con->http_status = 500;
  148: 
  149: 			log_error_write(srv, __FILE__, __LINE__, "ss",
  150: 					"can't get sockname", strerror(errno));
  151: 
  152: 			buffer_free(o);
  153: 			return 0;
  154: 		}
  155: 
  156: 
  157: 		/* Lookup name: secondly try to get hostname for bind address */
  158: 		switch(our_addr.plain.sa_family) {
  159: #ifdef HAVE_IPV6
  160: 		case AF_INET6:
  161: 			if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
  162: 					     SA_LEN((const struct sockaddr *)&our_addr.ipv6),
  163: 					     hbuf, sizeof(hbuf), NULL, 0, 0)) {
  164: 
  165: 				char dst[INET6_ADDRSTRLEN];
  166: 
  167: 				log_error_write(srv, __FILE__, __LINE__,
  168: 						"SSS", "NOTICE: getnameinfo failed: ",
  169: 						strerror(errno), ", using ip-address instead");
  170: 
  171: 				buffer_append_string_len(o, CONST_STR_LEN("["));
  172: 				buffer_append_string(o,
  173: 						     inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
  174: 							       dst, sizeof(dst)));
  175: 				buffer_append_string_len(o, CONST_STR_LEN("]"));
  176: 			} else {
  177: 				buffer_append_string(o, hbuf);
  178: 			}
  179: 			break;
  180: #endif
  181: 		case AF_INET:
  182: 			if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
  183: 				log_error_write(srv, __FILE__, __LINE__,
  184: 						"SdS", "NOTICE: gethostbyaddr failed: ",
  185: 						h_errno, ", using ip-address instead");
  186: 
  187: 				buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
  188: 			} else {
  189: 				buffer_append_string(o, he->h_name);
  190: 			}
  191: 			break;
  192: 		default:
  193: 			log_error_write(srv, __FILE__, __LINE__,
  194: 					"S", "ERROR: unsupported address-type");
  195: 
  196: 			buffer_free(o);
  197: 			return -1;
  198: 		}
  199: 
  200: 		{
  201: 			unsigned short default_port = 80;
  202: 			if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
  203: 				default_port = 443;
  204: 			}
  205: 			if (default_port != srv->srvconf.port) {
  206: 				buffer_append_string_len(o, CONST_STR_LEN(":"));
  207: 				buffer_append_int(o, srv->srvconf.port);
  208: 			}
  209: 		}
  210: 	}
  211: 	buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
  212: 	buffer_append_string_len(o, CONST_STR_LEN("/"));
  213: 	if (!buffer_string_is_empty(con->uri.query)) {
  214: 		buffer_append_string_len(o, CONST_STR_LEN("?"));
  215: 		buffer_append_string_buffer(o, con->uri.query);
  216: 	}
  217: 
  218: 	response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
  219: 
  220: 	con->http_status = 301;
  221: 	con->file_finished = 1;
  222: 
  223: 	buffer_free(o);
  224: 
  225: 	return 0;
  226: }
  227: 
  228: buffer * strftime_cache_get(server *srv, time_t last_mod) {
  229: 	struct tm *tm;
  230: 	size_t i;
  231: 
  232: 	for (i = 0; i < FILE_CACHE_MAX; i++) {
  233: 		/* found cache-entry */
  234: 		if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
  235: 
  236: 		/* found empty slot */
  237: 		if (srv->mtime_cache[i].mtime == 0) break;
  238: 	}
  239: 
  240: 	if (i == FILE_CACHE_MAX) {
  241: 		i = 0;
  242: 	}
  243: 
  244: 	srv->mtime_cache[i].mtime = last_mod;
  245: 	buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
  246: 	tm = gmtime(&(srv->mtime_cache[i].mtime));
  247: 	buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
  248: 
  249: 	return srv->mtime_cache[i].str;
  250: }
  251: 
  252: 
  253: int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
  254: 	int head_or_get =
  255: 		(  HTTP_METHOD_GET  == con->request.http_method
  256: 		|| HTTP_METHOD_HEAD == con->request.http_method);
  257: 	UNUSED(srv);
  258: 
  259: 	/*
  260: 	 * 14.26 If-None-Match
  261: 	 *    [...]
  262: 	 *    If none of the entity tags match, then the server MAY perform the
  263: 	 *    requested method as if the If-None-Match header field did not exist,
  264: 	 *    but MUST also ignore any If-Modified-Since header field(s) in the
  265: 	 *    request. That is, if no entity tags match, then the server MUST NOT
  266: 	 *    return a 304 (Not Modified) response.
  267: 	 */
  268: 
  269: 	if (con->request.http_if_none_match) {
  270: 		/* use strong etag checking for now: weak comparison must not be used
  271: 		 * for ranged requests
  272: 		 */
  273: 		if (etag_is_equal(con->physical.etag, con->request.http_if_none_match, 0)) {
  274: 			if (head_or_get) {
  275: 				con->http_status = 304;
  276: 				return HANDLER_FINISHED;
  277: 			} else {
  278: 				con->http_status = 412;
  279: 				con->mode = DIRECT;
  280: 				return HANDLER_FINISHED;
  281: 			}
  282: 		}
  283: 	} else if (con->request.http_if_modified_since && head_or_get) {
  284: 		/* last-modified handling */
  285: 		size_t used_len;
  286: 		char *semicolon;
  287: 
  288: 		if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
  289: 			used_len = strlen(con->request.http_if_modified_since);
  290: 		} else {
  291: 			used_len = semicolon - con->request.http_if_modified_since;
  292: 		}
  293: 
  294: 		if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
  295: 			if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
  296: 			return HANDLER_FINISHED;
  297: 		} else {
  298: 			char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
  299: 			time_t t_header, t_file;
  300: 			struct tm tm;
  301: 
  302: 			/* convert to timestamp */
  303: 			if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
  304: 
  305: 			strncpy(buf, con->request.http_if_modified_since, used_len);
  306: 			buf[used_len] = '\0';
  307: 
  308: 			if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
  309: 				/**
  310: 				 * parsing failed, let's get out of here 
  311: 				 */
  312: 				return HANDLER_GO_ON;
  313: 			}
  314: 			tm.tm_isdst = 0;
  315: 			t_header = mktime(&tm);
  316: 
  317: 			strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
  318: 			tm.tm_isdst = 0;
  319: 			t_file = mktime(&tm);
  320: 
  321: 			if (t_file > t_header) return HANDLER_GO_ON;
  322: 
  323: 			con->http_status = 304;
  324: 			return HANDLER_FINISHED;
  325: 		}
  326: 	}
  327: 
  328: 	return HANDLER_GO_ON;
  329: }
  330: 
  331: 
  332: static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce) {
  333: 	int multipart = 0;
  334: 	int error;
  335: 	off_t start, end;
  336: 	const char *s, *minus;
  337: 	char *boundary = "fkj49sn38dcn3";
  338: 	data_string *ds;
  339: 	buffer *content_type = NULL;
  340: 
  341: 	start = 0;
  342: 	end = sce->st.st_size - 1;
  343: 
  344: 	con->response.content_length = 0;
  345: 
  346: 	if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) {
  347: 		content_type = ds->value;
  348: 	}
  349: 
  350: 	for (s = con->request.http_range, error = 0;
  351: 	     !error && *s && NULL != (minus = strchr(s, '-')); ) {
  352: 		char *err;
  353: 		off_t la, le;
  354: 
  355: 		if (s == minus) {
  356: 			/* -<stop> */
  357: 
  358: 			le = strtoll(s, &err, 10);
  359: 
  360: 			if (le == 0) {
  361: 				/* RFC 2616 - 14.35.1 */
  362: 
  363: 				con->http_status = 416;
  364: 				error = 1;
  365: 			} else if (*err == '\0') {
  366: 				/* end */
  367: 				s = err;
  368: 
  369: 				end = sce->st.st_size - 1;
  370: 				start = sce->st.st_size + le;
  371: 			} else if (*err == ',') {
  372: 				multipart = 1;
  373: 				s = err + 1;
  374: 
  375: 				end = sce->st.st_size - 1;
  376: 				start = sce->st.st_size + le;
  377: 			} else {
  378: 				error = 1;
  379: 			}
  380: 
  381: 		} else if (*(minus+1) == '\0' || *(minus+1) == ',') {
  382: 			/* <start>- */
  383: 
  384: 			la = strtoll(s, &err, 10);
  385: 
  386: 			if (err == minus) {
  387: 				/* ok */
  388: 
  389: 				if (*(err + 1) == '\0') {
  390: 					s = err + 1;
  391: 
  392: 					end = sce->st.st_size - 1;
  393: 					start = la;
  394: 
  395: 				} else if (*(err + 1) == ',') {
  396: 					multipart = 1;
  397: 					s = err + 2;
  398: 
  399: 					end = sce->st.st_size - 1;
  400: 					start = la;
  401: 				} else {
  402: 					error = 1;
  403: 				}
  404: 			} else {
  405: 				/* error */
  406: 				error = 1;
  407: 			}
  408: 		} else {
  409: 			/* <start>-<stop> */
  410: 
  411: 			la = strtoll(s, &err, 10);
  412: 
  413: 			if (err == minus) {
  414: 				le = strtoll(minus+1, &err, 10);
  415: 
  416: 				/* RFC 2616 - 14.35.1 */
  417: 				if (la > le) {
  418: 					error = 1;
  419: 				}
  420: 
  421: 				if (*err == '\0') {
  422: 					/* ok, end*/
  423: 					s = err;
  424: 
  425: 					end = le;
  426: 					start = la;
  427: 				} else if (*err == ',') {
  428: 					multipart = 1;
  429: 					s = err + 1;
  430: 
  431: 					end = le;
  432: 					start = la;
  433: 				} else {
  434: 					/* error */
  435: 
  436: 					error = 1;
  437: 				}
  438: 			} else {
  439: 				/* error */
  440: 
  441: 				error = 1;
  442: 			}
  443: 		}
  444: 
  445: 		if (!error) {
  446: 			if (start < 0) start = 0;
  447: 
  448: 			/* RFC 2616 - 14.35.1 */
  449: 			if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
  450: 
  451: 			if (start > sce->st.st_size - 1) {
  452: 				error = 1;
  453: 
  454: 				con->http_status = 416;
  455: 			}
  456: 		}
  457: 
  458: 		if (!error) {
  459: 			if (multipart) {
  460: 				/* write boundary-header */
  461: 				buffer *b = buffer_init();
  462: 
  463: 				buffer_copy_string_len(b, CONST_STR_LEN("\r\n--"));
  464: 				buffer_append_string(b, boundary);
  465: 
  466: 				/* write Content-Range */
  467: 				buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes "));
  468: 				buffer_append_int(b, start);
  469: 				buffer_append_string_len(b, CONST_STR_LEN("-"));
  470: 				buffer_append_int(b, end);
  471: 				buffer_append_string_len(b, CONST_STR_LEN("/"));
  472: 				buffer_append_int(b, sce->st.st_size);
  473: 
  474: 				buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: "));
  475: 				buffer_append_string_buffer(b, content_type);
  476: 
  477: 				/* write END-OF-HEADER */
  478: 				buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
  479: 
  480: 				con->response.content_length += buffer_string_length(b);
  481: 				chunkqueue_append_buffer(con->write_queue, b);
  482: 				buffer_free(b);
  483: 			}
  484: 
  485: 			chunkqueue_append_file(con->write_queue, path, start, end - start + 1);
  486: 			con->response.content_length += end - start + 1;
  487: 		}
  488: 	}
  489: 
  490: 	/* something went wrong */
  491: 	if (error) return -1;
  492: 
  493: 	if (multipart) {
  494: 		/* add boundary end */
  495: 		buffer *b = buffer_init();
  496: 
  497: 		buffer_copy_string_len(b, "\r\n--", 4);
  498: 		buffer_append_string(b, boundary);
  499: 		buffer_append_string_len(b, "--\r\n", 4);
  500: 
  501: 		con->response.content_length += buffer_string_length(b);
  502: 		chunkqueue_append_buffer(con->write_queue, b);
  503: 		buffer_free(b);
  504: 
  505: 		/* set header-fields */
  506: 
  507: 		buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("multipart/byteranges; boundary="));
  508: 		buffer_append_string(srv->tmp_buf, boundary);
  509: 
  510: 		/* overwrite content-type */
  511: 		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(srv->tmp_buf));
  512: 	} else {
  513: 		/* add Content-Range-header */
  514: 
  515: 		buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("bytes "));
  516: 		buffer_append_int(srv->tmp_buf, start);
  517: 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-"));
  518: 		buffer_append_int(srv->tmp_buf, end);
  519: 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
  520: 		buffer_append_int(srv->tmp_buf, sce->st.st_size);
  521: 
  522: 		response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(srv->tmp_buf));
  523: 	}
  524: 
  525: 	/* ok, the file is set-up */
  526: 	return 0;
  527: }
  528: 
  529: 
  530: void http_response_send_file (server *srv, connection *con, buffer *path) {
  531: 	stat_cache_entry *sce = NULL;
  532: 	buffer *mtime = NULL;
  533: 	data_string *ds;
  534: 	int allow_caching = (0 == con->http_status || 200 == con->http_status);
  535: 
  536: 	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
  537: 		con->http_status = (errno == ENOENT) ? 404 : 403;
  538: 
  539: 		log_error_write(srv, __FILE__, __LINE__, "sbsb",
  540: 				"not a regular file:", con->uri.path,
  541: 				"->", path);
  542: 
  543: 		return;
  544: 	}
  545: 
  546: 	/* we only handline regular files */
  547: #ifdef HAVE_LSTAT
  548: 	if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
  549: 		con->http_status = 403;
  550: 
  551: 		if (con->conf.log_request_handling) {
  552: 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
  553: 			log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", path);
  554: 		}
  555: 
  556: 		return;
  557: 	}
  558: #endif
  559: 	if (!S_ISREG(sce->st.st_mode)) {
  560: 		con->http_status = 403;
  561: 
  562: 		if (con->conf.log_file_not_found) {
  563: 			log_error_write(srv, __FILE__, __LINE__, "sbsb",
  564: 					"not a regular file:", con->uri.path,
  565: 					"->", sce->name);
  566: 		}
  567: 
  568: 		return;
  569: 	}
  570: 
  571: 	/* mod_compress might set several data directly, don't overwrite them */
  572: 
  573: 	/* set response content-type, if not set already */
  574: 
  575: 	if (NULL == array_get_element(con->response.headers, "Content-Type")) {
  576: 		if (buffer_string_is_empty(sce->content_type)) {
  577: 			/* we are setting application/octet-stream, but also announce that
  578: 			 * this header field might change in the seconds few requests
  579: 			 *
  580: 			 * This should fix the aggressive caching of FF and the script download
  581: 			 * seen by the first installations
  582: 			 */
  583: 			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
  584: 
  585: 			allow_caching = 0;
  586: 		} else {
  587: 			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
  588: 		}
  589: 	}
  590: 
  591: 	if (con->conf.range_requests) {
  592: 		response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
  593: 	}
  594: 
  595: 	if (allow_caching) {
  596: 		if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
  597: 			if (NULL == array_get_element(con->response.headers, "ETag")) {
  598: 				/* generate e-tag */
  599: 				etag_mutate(con->physical.etag, sce->etag);
  600: 
  601: 				response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
  602: 			}
  603: 		}
  604: 
  605: 		/* prepare header */
  606: 		if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
  607: 			mtime = strftime_cache_get(srv, sce->st.st_mtime);
  608: 			response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
  609: 		} else {
  610: 			mtime = ds->value;
  611: 		}
  612: 
  613: 		if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
  614: 			return;
  615: 		}
  616: 	}
  617: 
  618: 	if (con->request.http_range && con->conf.range_requests
  619: 	    && (200 == con->http_status || 0 == con->http_status)
  620: 	    && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
  621: 		int do_range_request = 1;
  622: 		/* check if we have a conditional GET */
  623: 
  624: 		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
  625: 			/* if the value is the same as our ETag, we do a Range-request,
  626: 			 * otherwise a full 200 */
  627: 
  628: 			if (ds->value->ptr[0] == '"') {
  629: 				/**
  630: 				 * client wants a ETag
  631: 				 */
  632: 				if (!con->physical.etag) {
  633: 					do_range_request = 0;
  634: 				} else if (!buffer_is_equal(ds->value, con->physical.etag)) {
  635: 					do_range_request = 0;
  636: 				}
  637: 			} else if (!mtime) {
  638: 				/**
  639: 				 * we don't have a Last-Modified and can match the If-Range:
  640: 				 *
  641: 				 * sending all
  642: 				 */
  643: 				do_range_request = 0;
  644: 			} else if (!buffer_is_equal(ds->value, mtime)) {
  645: 				do_range_request = 0;
  646: 			}
  647: 		}
  648: 
  649: 		if (do_range_request) {
  650: 			/* content prepared, I'm done */
  651: 			con->file_finished = 1;
  652: 
  653: 			if (0 == http_response_parse_range(srv, con, path, sce)) {
  654: 				con->http_status = 206;
  655: 			}
  656: 			return;
  657: 		}
  658: 	}
  659: 
  660: 	/* if we are still here, prepare body */
  661: 
  662: 	/* we add it here for all requests
  663: 	 * the HEAD request will drop it afterwards again
  664: 	 */
  665: 	if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
  666: 		con->http_status = 200;
  667: 		con->file_finished = 1;
  668: 	} else {
  669: 		con->http_status = 403;
  670: 	}
  671: }
  672: 
  673: void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) {
  674: 	const int status = con->http_status;
  675: 	int valid = 1;
  676: 
  677: 	con->file_started = 1;
  678: 
  679: 	/* reset Content-Length, if set by backend
  680: 	 * Content-Length might later be set to size of X-Sendfile static file,
  681: 	 * determined by open(), fstat() to reduces race conditions if the file
  682: 	 * is modified between stat() (stat_cache_get_entry()) and open(). */
  683: 	if (con->parsed_response & HTTP_CONTENT_LENGTH) {
  684: 		data_string *ds = (data_string *) array_get_element(con->response.headers, "Content-Length");
  685: 		if (ds) buffer_reset(ds->value);
  686: 		con->parsed_response &= ~HTTP_CONTENT_LENGTH;
  687: 		con->response.content_length = -1;
  688: 	}
  689: 
  690: 	buffer_urldecode_path(path);
  691: 	buffer_path_simplify(path, path);
  692: 	if (con->conf.force_lowercase_filenames) {
  693: 		buffer_to_lower(path);
  694: 	}
  695: 
  696: 	/* check that path is under xdocroot(s)
  697: 	 * - xdocroot should have trailing slash appended at config time
  698: 	 * - con->conf.force_lowercase_filenames is not a server-wide setting,
  699: 	 *   and so can not be definitively applied to xdocroot at config time*/
  700: 	if (xdocroot->used) {
  701: 		size_t i, xlen = buffer_string_length(path);
  702: 		for (i = 0; i < xdocroot->used; ++i) {
  703: 			data_string *ds = (data_string *)xdocroot->data[i];
  704: 			size_t dlen = buffer_string_length(ds->value);
  705: 			if (dlen <= xlen
  706: 			    && (!con->conf.force_lowercase_filenames
  707: 				? 0 == memcmp(path->ptr, ds->value->ptr, dlen)
  708: 				: 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) {
  709: 				break;
  710: 			}
  711: 		}
  712: 		if (i == xdocroot->used) {
  713: 			log_error_write(srv, __FILE__, __LINE__, "SBs",
  714: 					"X-Sendfile (", path,
  715: 					") not under configured x-sendfile-docroot(s)");
  716: 			con->http_status = 403;
  717: 			valid = 0;
  718: 		}
  719: 	}
  720: 
  721: 	if (valid) http_response_send_file(srv, con, path);
  722: 
  723: 	if (con->http_status >= 400 && status < 300) {
  724: 		con->mode = DIRECT;
  725: 	} else if (0 != status && 200 != status) {
  726: 		con->http_status = status;
  727: 	}
  728: }
  729: 
  730: void http_response_backend_error (server *srv, connection *con) {
  731: 	UNUSED(srv);
  732: 	if (con->file_started) {
  733: 		/*(response might have been already started, kill the connection)*/
  734: 		/*(mode == DIRECT to avoid later call to http_response_backend_done())*/
  735: 		con->mode = DIRECT;  /*(avoid sending final chunked block)*/
  736: 		con->keep_alive = 0; /*(no keep-alive; final chunked block not sent)*/
  737: 		con->file_finished = 1;
  738: 	} /*(else error status set later by http_response_backend_done())*/
  739: }
  740: 
  741: void http_response_backend_done (server *srv, connection *con) {
  742: 	/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
  743: 	 *  i.e. not called from handle_connection_close or connection_reset
  744: 	 *  hooks, except maybe from errdoc handler, which later resets state)*/
  745: 	switch (con->state) {
  746: 	case CON_STATE_HANDLE_REQUEST:
  747: 	case CON_STATE_READ_POST:
  748: 		if (!con->file_started) {
  749: 			/* Send an error if we haven't sent any data yet */
  750: 			con->http_status = 500;
  751: 			con->mode = DIRECT;
  752: 			break;
  753: 		} /* else fall through */
  754: 	case CON_STATE_WRITE:
  755: 		if (!con->file_finished) {
  756: 			http_chunk_close(srv, con);
  757: 			con->file_finished = 1;
  758: 		}
  759: 	default:
  760: 		break;
  761: 	}
  762: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>