|  | @@ -1559,6 +1559,7 @@ enum {
 | 
	
		
			
				|  |  |  	HIDE_FILES,
 | 
	
		
			
				|  |  |  	REQUEST_TIMEOUT,
 | 
	
		
			
				|  |  |  	KEEP_ALIVE_TIMEOUT,
 | 
	
		
			
				|  |  | +	LINGER_TIMEOUT,
 | 
	
		
			
				|  |  |  	SSL_DO_VERIFY_PEER,
 | 
	
		
			
				|  |  |  	SSL_CA_PATH,
 | 
	
		
			
				|  |  |  	SSL_CA_FILE,
 | 
	
	
		
			
				|  | @@ -1642,6 +1643,7 @@ static struct mg_option config_options[] = {
 | 
	
		
			
				|  |  |      {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
 | 
	
		
			
				|  |  |      {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
 | 
	
		
			
				|  |  |      {"keep_alive_timeout_ms", CONFIG_TYPE_NUMBER, "500"},
 | 
	
		
			
				|  |  | +    {"linger_timeout_ms", CONFIG_TYPE_NUMBER, NULL},
 | 
	
		
			
				|  |  |      {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
 | 
	
		
			
				|  |  |      {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
 | 
	
		
			
				|  |  |      {"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
 | 
	
	
		
			
				|  | @@ -6016,7 +6018,7 @@ open_auth_file(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  			/* Use global passwords file */
 | 
	
		
			
				|  |  |  			if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
 | 
	
		
			
				|  |  |  #ifdef DEBUG
 | 
	
		
			
				|  |  | -                /* Use mg_cry here, since gpass has been configured. */
 | 
	
		
			
				|  |  | +				/* Use mg_cry here, since gpass has been configured. */
 | 
	
		
			
				|  |  |  				mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  			}
 | 
	
	
		
			
				|  | @@ -6037,9 +6039,9 @@ open_auth_file(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
 | 
	
		
			
				|  |  |  #ifdef DEBUG
 | 
	
		
			
				|  |  | -                /* Don't use mg_cry here, but only a trace, since this is 
 | 
	
		
			
				|  |  | -                 * a typical case. It will occur for every directory
 | 
	
		
			
				|  |  | -                 * without a password file. */
 | 
	
		
			
				|  |  | +				/* Don't use mg_cry here, but only a trace, since this is
 | 
	
		
			
				|  |  | +				 * a typical case. It will occur for every directory
 | 
	
		
			
				|  |  | +				 * without a password file. */
 | 
	
		
			
				|  |  |  				DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  			}
 | 
	
	
		
			
				|  | @@ -6061,9 +6063,9 @@ open_auth_file(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
 | 
	
		
			
				|  |  |  #ifdef DEBUG
 | 
	
		
			
				|  |  | -                /* Don't use mg_cry here, but only a trace, since this is 
 | 
	
		
			
				|  |  | -                 * a typical case. It will occur for every directory
 | 
	
		
			
				|  |  | -                 * without a password file. */
 | 
	
		
			
				|  |  | +				/* Don't use mg_cry here, but only a trace, since this is
 | 
	
		
			
				|  |  | +				 * a typical case. It will occur for every directory
 | 
	
		
			
				|  |  | +				 * without a password file. */
 | 
	
		
			
				|  |  |  				DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  			}
 | 
	
	
		
			
				|  | @@ -12331,6 +12333,7 @@ close_socket_gracefully(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  	struct linger linger;
 | 
	
		
			
				|  |  |  	int error_code = 0;
 | 
	
		
			
				|  |  | +	int linger_timeout = -1;
 | 
	
		
			
				|  |  |  	socklen_t opt_len = sizeof(error_code);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (!conn) {
 | 
	
	
		
			
				|  | @@ -12358,11 +12361,14 @@ close_socket_gracefully(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  	} while (n > 0);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#if !defined(__MACH__)
 | 
	
		
			
				|  |  | +	if (conn->ctx->config[LINGER_TIMEOUT]) {
 | 
	
		
			
				|  |  | +		linger_timeout = atoi(conn->ctx->config[LINGER_TIMEOUT]);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/* Set linger option to avoid socket hanging out after close. This
 | 
	
		
			
				|  |  |  	 * prevent ephemeral port exhaust problem under high QPS. */
 | 
	
		
			
				|  |  | -	linger.l_onoff = 1;
 | 
	
		
			
				|  |  | -	linger.l_linger = 1;
 | 
	
		
			
				|  |  | +	linger.l_onoff = (linger_timeout >= 0);
 | 
	
		
			
				|  |  | +	linger.l_linger = (linger_timeout + 999) / 1000;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (getsockopt(conn->client.sock,
 | 
	
		
			
				|  |  |  	               SOL_SOCKET,
 | 
	
	
		
			
				|  | @@ -12379,7 +12385,7 @@ close_socket_gracefully(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  	} else if (error_code == ECONNRESET) {
 | 
	
		
			
				|  |  |  		/* Socket already closed by client/peer, close socket without linger */
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		/* Set linger timeout */
 | 
	
		
			
				|  |  |  		if (setsockopt(conn->client.sock,
 | 
	
		
			
				|  |  |  		               SOL_SOCKET,
 | 
	
	
		
			
				|  | @@ -12392,7 +12398,6 @@ close_socket_gracefully(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  			       strerror(ERRNO));
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* Now we know that our FIN is ACK-ed, safe to close */
 | 
	
		
			
				|  |  |  	closesocket(conn->client.sock);
 | 
	
	
		
			
				|  | @@ -14138,13 +14143,13 @@ mg_start(const struct mg_callbacks *callbacks,
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  	pthread_setspecific(sTlsKey, &tls);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /* Dummy use this function - in some #ifdef combinations it's used, 
 | 
	
		
			
				|  |  | -     * while it's not used in others, but GCC seems to stupid to understand
 | 
	
		
			
				|  |  | -     * #pragma GCC diagnostic ignored "-Wunused-function"
 | 
	
		
			
				|  |  | -     * in cases the function is unused, and it also complains on
 | 
	
		
			
				|  |  | -     * __attribute((unused))__ in cases it is used.
 | 
	
		
			
				|  |  | -     * So dummy use it, to have our peace. */
 | 
	
		
			
				|  |  | -    (void)mg_current_thread_id();
 | 
	
		
			
				|  |  | +	/* Dummy use this function - in some #ifdef combinations it's used,
 | 
	
		
			
				|  |  | +	 * while it's not used in others, but GCC seems to stupid to understand
 | 
	
		
			
				|  |  | +	 * #pragma GCC diagnostic ignored "-Wunused-function"
 | 
	
		
			
				|  |  | +	 * in cases the function is unused, and it also complains on
 | 
	
		
			
				|  |  | +	 * __attribute((unused))__ in cases it is used.
 | 
	
		
			
				|  |  | +	 * So dummy use it, to have our peace. */
 | 
	
		
			
				|  |  | +	(void)mg_current_thread_id();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ok = 0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr);
 | 
	
		
			
				|  |  |  #if !defined(ALTERNATIVE_QUEUE)
 |