📄 connection.c
字号:
} else { close(conn->socket.fd); }done: free_connection_data(conn); done_connection(conn); register_bottom_half((void (*)(void *)) check_queue, NULL);}static voidkeepalive_timer(void *x){ keepalive_timeout = -1; check_keepalive_connections();}voidcheck_keepalive_connections(void){ struct keepalive_connection *keep_conn, *next; ttime ct = get_time(); int p = 0; if (keepalive_timeout != -1) { kill_timer(keepalive_timeout); keepalive_timeout = -1; } foreachsafe (keep_conn, next, keepalive_connections) { if (can_read(keep_conn->socket) || ct - keep_conn->add_time > keep_conn->timeout) { done_keepalive_connection(keep_conn); } else { p++; } } for (; p > MAX_KEEPALIVE_CONNECTIONS; p--) { assertm(!list_empty(keepalive_connections), "keepalive list empty"); if_assert_failed return; done_keepalive_connection(keepalive_connections.prev); } if (!list_empty(keepalive_connections)) keepalive_timeout = install_timer(KEEPALIVE_CHECK_TIME, keepalive_timer, NULL);}static inline voidabort_all_keepalive_connections(void){ while (!list_empty(keepalive_connections)) done_keepalive_connection(keepalive_connections.next); check_keepalive_connections();}static inline voidadd_to_queue(struct connection *conn){ struct connection *c; enum connection_priority priority = get_priority(conn); foreach (c, queue) if (get_priority(c) > priority) break; add_at_pos(c->prev, conn);}static voidsort_queue(void){ int swp; do { struct connection *conn; swp = 0; foreach (conn, queue) { if (!list_has_next(queue, conn)) break; if (get_priority(conn->next) < get_priority(conn)) { struct connection *c = conn->next; del_from_list(conn); add_at_pos(c, conn); swp = 1; } } } while (swp);}static voidinterrupt_connection(struct connection *conn){ close_socket(conn, &conn->socket); free_connection_data(conn);}static inline voidsuspend_connection(struct connection *conn){ interrupt_connection(conn); set_connection_state(conn, S_WAIT);}static voidrun_connection(struct connection *conn){ protocol_handler *func = get_protocol_handler(conn->uri->protocol); assert(func); assertm(!conn->running, "connection already running"); if_assert_failed return; if (!add_host_connection(conn)) { set_connection_state(conn, S_OUT_OF_MEM); done_connection(conn); return; } active_connections++; conn->running = 1; func(conn);}voidretry_connection(struct connection *conn){ int max_tries = get_opt_int("connection.retries"); interrupt_connection(conn); if (conn->uri->post || !max_tries || ++conn->tries >= max_tries) { /* notify_connection_callbacks(conn); */ done_connection(conn); register_bottom_half((void (*)(void *)) check_queue, NULL); } else { conn->prev_error = conn->state; run_connection(conn); }}voidabort_connection(struct connection *conn){ if (conn->running) interrupt_connection(conn); /* notify_connection_callbacks(conn); */ done_connection(conn); register_bottom_half((void (*)(void *)) check_queue, NULL);}/* Set certain state on a connection and then abort the connection. */voidabort_conn_with_state(struct connection *conn, enum connection_state state){ set_connection_state(conn, state); abort_connection(conn);}/* Set certain state on a connection and then retry the connection. */voidretry_conn_with_state(struct connection *conn, enum connection_state state){ set_connection_state(conn, state); retry_connection(conn);}static inttry_to_suspend_connection(struct connection *conn, struct uri *uri){ enum connection_priority priority = get_priority(conn); struct connection *c; foreachback (c, queue) { if (get_priority(c) <= priority) return -1; if (c->state == S_WAIT) continue; if (c->uri->post && get_priority(c) < PRI_CANCEL) continue; if (uri && !compare_uri(uri, c->uri, URI_HOST)) continue; suspend_connection(c); return 0; } return -1;}static inline inttry_connection(struct connection *conn, int max_conns_to_host, int max_conns){ struct host_connection *host_conn = get_host_connection(conn); if (host_conn && get_object_refcount(host_conn) >= max_conns_to_host) return try_to_suspend_connection(conn, host_conn->uri) ? 0 : -1; if (active_connections >= max_conns) return try_to_suspend_connection(conn, NULL) ? 0 : -1; run_connection(conn); return 1;}voidcheck_queue(void){ struct connection *conn; int max_conns_to_host = get_opt_int("connection.max_connections_to_host"); int max_conns = get_opt_int("connection.max_connections");again: conn = queue.next; check_queue_bugs(); check_keepalive_connections(); while (conn != (struct connection *) &queue) { struct connection *c; enum connection_priority pri = get_priority(conn); for (c = conn; c != (struct connection *) &queue && get_priority(c) == pri;) { struct connection *cc = c; c = c->next; if (cc->state == S_WAIT && get_keepalive_connection(cc) && try_connection(cc, max_conns_to_host, max_conns)) goto again; } for (c = conn; c != (struct connection *) &queue && get_priority(c) == pri;) { struct connection *cc = c; c = c->next; if (cc->state == S_WAIT && try_connection(cc, max_conns_to_host, max_conns)) goto again; } conn = c; }again2: foreachback (conn, queue) { if (get_priority(conn) < PRI_CANCEL) break; if (conn->state == S_WAIT) { set_connection_state(conn, S_INTERRUPTED); done_connection(conn); goto again2; } } check_queue_bugs();}intload_uri(struct uri *uri, struct uri *referrer, struct download *download, enum connection_priority pri, enum cache_mode cache_mode, int start){ struct cache_entry *cached; struct connection *conn; struct uri *proxy_uri, *proxied_uri; enum connection_state connection_state = S_OK; if (download) { download->conn = NULL; download->cached = NULL; download->pri = pri; download->state = S_OUT_OF_MEM; download->prev_error = 0; }#ifdef CONFIG_DEBUG foreach (conn, queue) { struct download *assigned; foreach (assigned, conn->downloads) { assertm(assigned != download, "Download assigned to '%s'", struri(conn->uri)); if_assert_failed { download->state = S_INTERNAL; if (download->callback) download->callback(download, download->data); return 0; } /* No recovery path should be necessary. */ } }#endif cached = get_validated_cache_entry(uri, cache_mode); if (cached) { if (download) { download->cached = cached; download->state = S_OK; /* XXX: * This doesn't work since sometimes |download->progress| * is undefined and contains random memory locations. * It's not supposed to point on anything here since * |download| has no connection attached. * Downloads resuming will probably break in some * cases without this, though. * FIXME: Needs more investigation. --pasky */ /* if (download->progress) download->progress->start = start; */ if (download->callback) download->callback(download, download->data); } return 0; } proxied_uri = get_proxied_uri(uri); proxy_uri = get_proxy_uri(uri, &connection_state); if (!proxy_uri || !proxied_uri || (get_protocol_need_slash_after_host(proxy_uri->protocol) && !proxy_uri->hostlen)) { if (download) { if (connection_state == S_OK) { connection_state = proxy_uri && proxied_uri ? S_BAD_URL : S_OUT_OF_MEM; } download->state = connection_state; download->callback(download, download->data); } if (proxy_uri) done_uri(proxy_uri); if (proxied_uri) done_uri(proxied_uri); return -1; } foreach (conn, queue) { if (conn->detached || !compare_uri(conn->uri, proxy_uri, 0)) continue; done_uri(proxy_uri); done_uri(proxied_uri); if (get_priority(conn) > pri) { del_from_list(conn); conn->pri[pri]++; add_to_queue(conn); register_bottom_half((void (*)(void *)) check_queue, NULL); } else { conn->pri[pri]++; } if (download) { download->progress = &conn->progress; download->conn = conn; download->cached = conn->cached; add_to_list(conn->downloads, download); /* This is likely to call download->callback() now! */ set_connection_state(conn, conn->state); } check_queue_bugs(); return 0; } conn = init_connection(proxy_uri, proxied_uri, referrer, start, cache_mode, pri); if (!conn) { if (download) { download->state = S_OUT_OF_MEM; download->callback(download, download->data); } if (proxy_uri) done_uri(proxy_uri); if (proxied_uri) done_uri(proxied_uri); return -1; } if (cache_mode < CACHE_MODE_FORCE_RELOAD && cached && !list_empty(cached->frag) && !((struct fragment *) cached->frag.next)->offset) conn->from = ((struct fragment *) cached->frag.next)->length; if (download) { download->progress = &conn->progress; download->conn = conn; download->cached = NULL; add_to_list(conn->downloads, download); } add_to_queue(conn); set_connection_state(conn, S_WAIT); check_queue_bugs(); register_bottom_half((void (*)(void *)) check_queue, NULL); return 0;}/* FIXME: one object in more connections */voidchange_connection(struct download *old, struct download *new, int newpri, int interrupt){ struct connection *conn; assert(old); if_assert_failed return; if (is_in_result_state(old->state)) { if (new) { new->cached = old->cached; new->state = old->state; new->prev_error = old->prev_error; if (new->callback) new->callback(new, new->data); } return; } check_queue_bugs(); conn = old->conn; conn->pri[old->pri]--; assertm(conn->pri[old->pri] >= 0, "priority counter underflow"); if_assert_failed conn->pri[old->pri] = 0; conn->pri[newpri]++; del_from_list(old); old->state = S_INTERRUPTED; if (new) { new->progress = &conn->progress; add_to_list(conn->downloads, new); new->state = conn->state; new->prev_error = conn->prev_error; new->pri = newpri; new->conn = conn; new->cached = conn->cached; } else if (conn->detached || interrupt) { abort_conn_with_state(conn, S_INTERRUPTED); } sort_queue(); check_queue_bugs(); register_bottom_half((void (*)(void *)) check_queue, NULL);}/* This will remove 'pos' bytes from the start of the cache for the specified * connection, if the cached object is already too big. */voiddetach_connection(struct download *download, int pos){ struct connection *conn = download->conn; if (is_in_result_state(download->state)) return; if (!conn->detached) { int total_len; int i, total_pri = 0; if (!conn->cached) return; total_len = (conn->est_length == -1) ? conn->from : conn->est_length; if (total_len < (get_opt_long("document.cache.memory.size") * MAX_CACHED_OBJECT_PERCENT / 100)) { /* This whole thing will fit to the memory anyway, so * there's no problem in detaching the connection. */ return; } for (i = 0; i < PRI_CANCEL; i++) total_pri += conn->pri[i]; assertm(total_pri, "detaching free connection"); /* No recovery path should be necessary...? */ /* Pre-clean cache. */ shrink_format_cache(0); if (total_pri != 1 || is_object_used(conn->cached)) { /* We're too important, or someone uses our cache * entry. */ return; } /* DBG("detached"); */ /* We aren't valid cache entry anymore. */ conn->cached->valid = 0; conn->detached = 1; } /* Strip the entry. */ free_entry_to(conn->cached, pos);}static voidconnection_timeout(struct connection *conn){ conn->timer = -1; set_connection_state(conn, S_TIMEOUT); if (conn->dnsquery) { abort_connection(conn); } else if (conn->conn_info) { dns_found(conn, 0); /* jump to next addr */ if (conn->conn_info) set_connection_timeout(conn); } else { retry_connection(conn); }}/* Huh, using two timers? Is this to account for changes of c->unrestartable * or can it be reduced? --jonas */static voidconnection_timeout_1(struct connection *conn){ conn->timer = install_timer((conn->unrestartable ? get_opt_int("connection.unrestartable_receive_timeout") : get_opt_int("connection.receive_timeout")) * 500, (void (*)(void *)) connection_timeout, conn);}voidset_connection_timeout(struct connection *conn){ if (conn->timer != -1) kill_timer(conn->timer); conn->timer = install_timer((conn->unrestartable ? get_opt_int("connection.unrestartable_receive_timeout") : get_opt_int("connection.receive_timeout")) * 500, (void (*)(void *)) connection_timeout_1, conn);}voidabort_all_connections(void){ while (!list_empty(queue)) { abort_conn_with_state(queue.next, S_INTERRUPTED); } abort_all_keepalive_connections();}voidabort_background_connections(void){ struct connection *conn; foreach (conn, queue) { if (get_priority(conn) >= PRI_CANCEL) { conn = conn->prev; abort_conn_with_state(conn->next, S_INTERRUPTED); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -