⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connection.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Connections management *//* $Id: connection.c,v 1.210.4.6 2005/05/01 21:15:33 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "document/document.h"#include "encoding/encoding.h"#include "intl/gettext/libintl.h"#include "lowlevel/connect.h"#include "lowlevel/dns.h"#include "lowlevel/select.h"#include "protocol/protocol.h"#include "protocol/proxy.h"#include "protocol/uri.h"#include "sched/connection.h"#include "sched/session.h"#include "util/error.h"#include "util/memory.h"#include "util/object.h"#include "util/string.h"#include "util/ttime.h"#include "util/types.h"struct keepalive_connection {	LIST_HEAD(struct keepalive_connection);	/* XXX: This is just the URI of the connection that registered the	 * keepalive connection so only rely on the protocol, user, password,	 * host and port part. */	struct uri *uri;	/* Function called when the keepalive has timed out or is deleted */	void (*done)(struct connection *);	ttime timeout;	ttime add_time;	unsigned int protocol_family:1; /* 0 == PF_INET, 1 == PF_INET6 */	int socket;};static unsigned int connection_id = 0;static int active_connections = 0;static int keepalive_timeout = -1;/* TODO: queue probably shouldn't be exported; ideally we should probably * separate it to an own module and define operations on it (especially * foreach_queue or so). Ok ok, that's nothing important and I'm not even * sure I would really like it ;-). --pasky */INIT_LIST_HEAD(queue);static INIT_LIST_HEAD(host_connections);static INIT_LIST_HEAD(keepalive_connections);/* Prototypes */static void notify_connection_callbacks(struct connection *conn);static void check_keepalive_connections(void);static /* inline */ enum connection_priorityget_priority(struct connection *conn){	enum connection_priority priority;	for (priority = 0; priority < PRIORITIES; priority++)		if (conn->pri[priority])			break;	assertm(priority != PRIORITIES, "Connection has no owner");	/* Recovery path ;-). (XXX?) */	return priority;}longconnect_info(int type){	long info = 0;	struct connection *conn;	struct keepalive_connection *keep_conn;	switch (type) {		case INFO_FILES:			foreach (conn, queue) info++;			break;		case INFO_CONNECTING:			foreach (conn, queue)				info += is_in_connecting_state(conn->state);			break;		case INFO_TRANSFER:			foreach (conn, queue)				info += is_in_transfering_state(conn->state);			break;		case INFO_KEEP:			foreach (keep_conn, keepalive_connections) info++;			break;	}	return info;}static inline intconnection_disappeared(struct connection *conn){	struct connection *c;	foreach (c, queue)		if (conn == c && conn->id == c->id)			return 0;	return 1;}/* Host connection management: *//* Used to keep track on the number of connections to any given host. When * trying to setup a new connection the list is searched to see if the maximum * number of connection has been reached. If that is the case we try to suspend * an already established connection. *//* Some connections (like file://) that do not involve hosts are not maintained * in the list. */struct host_connection {	LIST_HEAD(struct host_connection);	/* XXX: This is just the URI of the connection that registered the	 * host connection so only rely on the host part. */	struct uri *uri;	struct object object;};static struct host_connection *get_host_connection(struct connection *conn){	struct host_connection *host_conn;	if (!conn->uri->host) return NULL;	foreach (host_conn, host_connections)		if (compare_uri(host_conn->uri, conn->uri, URI_HOST))			return host_conn;	return NULL;}/* Returns if the connection was successfully added. *//* Don't add hostnameless host connections but they're valid. */static intadd_host_connection(struct connection *conn){	struct host_connection *host_conn = get_host_connection(conn);	if (!host_conn && conn->uri->host) {		host_conn = mem_calloc(1, sizeof(*host_conn));		if (!host_conn) return 0;		host_conn->uri = get_uri_reference(conn->uri);		object_nolock(host_conn, "host_connection");		add_to_list(host_connections, host_conn);	}	if (host_conn) object_lock(host_conn);	return 1;}/* Decrements and free()s the host connection if it is the last 'refcount'. */static voiddone_host_connection(struct connection *conn){	struct host_connection *host_conn = get_host_connection(conn);	if (!host_conn) return;	object_unlock(host_conn);	if (is_object_used(host_conn)) return;	del_from_list(host_conn);	done_uri(host_conn->uri);	mem_free(host_conn);}static void sort_queue();#ifdef CONFIG_DEBUGstatic voidcheck_queue_bugs(void){	struct connection *conn;	enum connection_priority prev_priority = 0;	int cc = 0;	foreach (conn, queue) {		enum connection_priority priority = get_priority(conn);		cc += conn->running;		assertm(priority >= prev_priority, "queue is not sorted");		assertm(is_in_progress_state(conn->state),			"interrupted connection on queue (conn %s, state %d)",			struri(conn->uri), conn->state);		prev_priority = priority;	}	assertm(cc == active_connections,		"bad number of active connections (counted %d, stored %d)",		cc, active_connections);}#else#define check_queue_bugs()#endifstatic struct connection *init_connection(struct uri *uri, struct uri *proxied_uri, struct uri *referrer,		int start, enum cache_mode cache_mode,		enum connection_priority priority){	struct connection *conn = mem_calloc(1, sizeof(*conn));	if (!conn) return NULL;	assert(proxied_uri->protocol != PROTOCOL_PROXY);	/* load_uri() gets the URI from get_proxy() which grabs a reference for	 * us. */	conn->uri = uri;	conn->proxied_uri = proxied_uri;	conn->id = connection_id++;	conn->pri[priority] = 1;	conn->cache_mode = cache_mode;	conn->socket.fd = conn->data_socket.fd = -1;	conn->content_encoding = ENCODING_NONE;	conn->stream_pipes[0] = conn->stream_pipes[1] = -1;	conn->cgi_pipes[0] = conn->cgi_pipes[1] = -1;	init_list(conn->downloads);	conn->est_length = -1;	conn->progress.start = start;	conn->progress.timer = -1;	conn->timer = -1;	if (referrer) {		/* Don't set referrer when it is the file protocol and the URI		 * being loaded is not. This means CGI scripts will have it		 * available while preventing information about the local		 * system from being leaked to external servers. */		if (referrer->protocol != PROTOCOL_FILE		    || uri->protocol == PROTOCOL_FILE)			conn->referrer = get_uri_reference(referrer);	}	return conn;}static void stat_timer(struct connection *conn);static voidupdate_progress(struct connection *conn){	struct progress *progress = &conn->progress;	ttime a = get_time() - progress->last_time;	progress->loaded = conn->received;	progress->size = conn->est_length;	progress->pos = conn->from;	if (progress->size < progress->pos && progress->size != -1)		progress->size = conn->from;	progress->dis_b += a;	while (progress->dis_b >= SPD_DISP_TIME * CURRENT_SPD_SEC) {		progress->cur_loaded -= progress->data_in_secs[0];		memmove(progress->data_in_secs, progress->data_in_secs + 1,			sizeof(*progress->data_in_secs) * (CURRENT_SPD_SEC - 1));		progress->data_in_secs[CURRENT_SPD_SEC - 1] = 0;		progress->dis_b -= SPD_DISP_TIME;	}	progress->data_in_secs[CURRENT_SPD_SEC - 1] += progress->loaded - progress->last_loaded;	progress->cur_loaded += progress->loaded - progress->last_loaded;	progress->last_loaded = progress->loaded;	progress->last_time += a;	progress->elapsed += a;	progress->timer = install_timer(SPD_DISP_TIME, (void (*)(void *)) stat_timer, conn);}static voidstat_timer(struct connection *conn){	update_progress(conn);	notify_connection_callbacks(conn);}voidset_connection_state(struct connection *conn, enum connection_state state){	struct download *download;	struct progress *progress = &conn->progress;	if (is_in_result_state(conn->state) && is_in_progress_state(state))		conn->prev_error = conn->state;	conn->state = state;	if (conn->state == S_TRANS) {		if (progress->timer == -1) {			if (!progress->valid) {				int tmp = progress->start;				int tmp2 = progress->seek;				memset(progress, 0, sizeof(*progress));				progress->start = tmp;				progress->seek = tmp2;				progress->valid = 1;			}			progress->last_time = get_time();			progress->last_loaded = progress->loaded;			update_progress(conn);			if (connection_disappeared(conn))				return;		}	} else if (progress->timer != -1) {		kill_timer(progress->timer);		progress->timer = -1;	}	foreach (download, conn->downloads) {		download->state = state;		download->prev_error = conn->prev_error;	}	if (is_in_progress_state(state)) notify_connection_callbacks(conn);}static voidfree_connection_data(struct connection *conn){	assertm(conn->running, "connection already suspended");	/* XXX: Recovery path? Originally, there was none. I think we'll get	 * at least active_connections underflows along the way. --pasky */	conn->running = 0;	if (conn->socket.fd != -1)		clear_handlers(conn->socket.fd);	if (conn->data_socket.fd != -1)		clear_handlers(conn->data_socket.fd);	close_socket(NULL, &conn->data_socket);	/* XXX: See also protocol/http/http.c:decompress_shutdown(). */	if (conn->stream) {		close_encoded(conn->stream);		conn->stream = NULL;	}	if (conn->stream_pipes[1] >= 0)		close(conn->stream_pipes[1]);	conn->stream_pipes[0] = conn->stream_pipes[1] = -1;	if (conn->cgi_pipes[0] >= 0)		close(conn->cgi_pipes[0]);	if (conn->cgi_pipes[1] >= 0)		close(conn->cgi_pipes[1]);	conn->cgi_pipes[0] = conn->cgi_pipes[1] = -1;	if (conn->dnsquery) {		kill_dns_request(&conn->dnsquery);	}	if (conn->conn_info) {		/* No callbacks should be made */		conn->conn_info->done = NULL;		done_connection_info(conn);	}	mem_free_set(&conn->buffer, NULL);	mem_free_set(&conn->info, NULL);	if (conn->timer != -1) {		kill_timer(conn->timer);		conn->timer = -1;	}	active_connections--;	assertm(active_connections >= 0, "active connections underflow");	if_assert_failed active_connections = 0;	if (conn->state != S_WAIT)		done_host_connection(conn);}voidnotify_connection_callbacks(struct connection *conn){	enum connection_state state = conn->state;	struct download *download, *next;	foreachsafe (download, next, conn->downloads) {		download->cached = conn->cached;		if (download->callback)			download->callback(download, download->data);		if (is_in_progress_state(state) && connection_disappeared(conn))			return;	}}static voiddone_connection(struct connection *conn){	if (!is_in_result_state(conn->state))		set_connection_state(conn, S_INTERNAL);	del_from_list(conn);	notify_connection_callbacks(conn);	if (conn->referrer) done_uri(conn->referrer);	done_uri(conn->uri);	done_uri(conn->proxied_uri);	mem_free(conn);	check_queue_bugs();}static inline void add_to_queue(struct connection *conn);/* Returns zero if no callback was done and the keepalive connection should be * deleted or non-zero if the keepalive connection should not be deleted. */static intdo_keepalive_connection_callback(struct keepalive_connection *keep_conn){	struct uri *proxied_uri = get_proxied_uri(keep_conn->uri);	struct uri *proxy_uri   = get_proxy_uri(keep_conn->uri, NULL);	if (proxied_uri && proxy_uri) {		struct connection *conn;		conn = init_connection(proxy_uri, proxied_uri, NULL, 0,				       CACHE_MODE_NEVER, PRI_CANCEL);		if (conn) {			void (*done)(struct connection *) = keep_conn->done;			add_to_queue(conn);			/* Get the keepalive info and let it clean up */			if (!has_keepalive_connection(conn)			    || !add_host_connection(conn)) {				free_connection_data(conn);				done_connection(conn);				return 0;			}			active_connections++;			conn->running = 1;			done(conn);			return 1;		}	}	if (proxied_uri) done_uri(proxied_uri);	if (proxy_uri) done_uri(proxy_uri);	return 0;}static inline voiddone_keepalive_connection(struct keepalive_connection *keep_conn){	if (keep_conn->done && do_keepalive_connection_callback(keep_conn))		return;	del_from_list(keep_conn);	if (keep_conn->socket != -1) close(keep_conn->socket);	done_uri(keep_conn->uri);	mem_free(keep_conn);}static struct keepalive_connection *init_keepalive_connection(struct connection *conn, ttime timeout,			  void (*done)(struct connection *)){	struct keepalive_connection *keep_conn;	struct uri *uri = conn->uri;	assert(uri->host);	if_assert_failed return NULL;	keep_conn = mem_calloc(1, sizeof(*keep_conn));	if (!keep_conn) return NULL;	keep_conn->uri = get_uri_reference(uri);	keep_conn->done = done;	keep_conn->protocol_family = conn->protocol_family;	keep_conn->socket = conn->socket.fd;	keep_conn->timeout = timeout;	keep_conn->add_time = get_time();	return keep_conn;}static struct keepalive_connection *get_keepalive_connection(struct connection *conn){	struct keepalive_connection *keep_conn;	if (!conn->uri->host) return NULL;	foreach (keep_conn, keepalive_connections)		if (compare_uri(keep_conn->uri, conn->uri, URI_KEEPALIVE))			return keep_conn;	return NULL;}inthas_keepalive_connection(struct connection *conn){	struct keepalive_connection *keep_conn = get_keepalive_connection(conn);	if (!keep_conn) return 0;	conn->socket.fd = keep_conn->socket;	conn->protocol_family = keep_conn->protocol_family;	/* Mark that the socket should not be closed and the callback should be	 * ignored. */	keep_conn->socket = -1;	keep_conn->done = NULL;	done_keepalive_connection(keep_conn);	return 1;}voidadd_keepalive_connection(struct connection *conn, ttime timeout,			 void (*done)(struct connection *)){	struct keepalive_connection *keep_conn;	assertm(conn->socket.fd != -1, "keepalive connection not connected");	if_assert_failed goto done;	keep_conn = init_keepalive_connection(conn, timeout, done);	if (keep_conn) {		add_to_list(keepalive_connections, keep_conn);	} else if (done) {		/* It will take just a little more time */		done(conn);		return;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -