net/connect.lua
author Kim Alvefur <zash@zash.se>
Sun, 24 Mar 2024 21:32:00 +0100
changeset 13468 2dbc169aae6a
parent 12978 ba409c67353b
permissions -rw-r--r--
util.startup: Abort before initialization of logging when started as root Prevents creation of log files owned by the root user which could be inaccessible once started correctly.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
12978
ba409c67353b net: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12643
diff changeset
     1
local server = require "prosody.net.server";
ba409c67353b net: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12643
diff changeset
     2
local log = require "prosody.util.logger".init("net.connect");
ba409c67353b net: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12643
diff changeset
     3
local new_id = require "prosody.util.id".short;
ba409c67353b net: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12643
diff changeset
     4
local timer = require "prosody.util.timer";
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     5
10489
913276ba0c47 net.connect: Mention RFC 6724 regression
Kim Alvefur <zash@zash.se>
parents: 10488
diff changeset
     6
-- FIXME RFC 6724
10456
fa11070c2cd7 net.connect: Add some TODO comments
Kim Alvefur <zash@zash.se>
parents: 10116
diff changeset
     7
-- FIXME Error propagation from resolvers doesn't work
10488
b13a31cea7d9 net.connect: Add some TODOs and FIXMEs
Kim Alvefur <zash@zash.se>
parents: 10456
diff changeset
     8
-- FIXME #1428 Reuse DNS resolver object between service and basic resolver
b13a31cea7d9 net.connect: Add some TODOs and FIXMEs
Kim Alvefur <zash@zash.se>
parents: 10456
diff changeset
     9
-- FIXME #1429 Close DNS resolver object when done
10456
fa11070c2cd7 net.connect: Add some TODO comments
Kim Alvefur <zash@zash.se>
parents: 10116
diff changeset
    10
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    11
local pending_connection_methods = {};
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    12
local pending_connection_mt = {
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    13
	__name = "pending_connection";
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    14
	__index = pending_connection_methods;
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    15
	__tostring = function (p)
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    16
		return "<pending connection "..p.id.." to "..tostring(p.target_resolver.hostname)..">";
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    17
	end;
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    18
};
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    19
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    20
function pending_connection_methods:log(level, message, ...)
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    21
	log(level, "[pending connection %s] "..message, self.id, ...);
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    22
end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    23
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    24
-- pending_connections_map[conn] = pending_connection
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    25
local pending_connections_map = {};
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    26
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    27
local pending_connection_listeners = {};
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    28
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    29
local function attempt_connection(p)
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    30
	p:log("debug", "Checking for targets...");
12416
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    31
	p.target_resolver:next(function (conn_type, ip, port, extra, more_targets_available)
8550
5e9c87376891 net.connect: Handle case when resolver runs out of targets
Matthew Wild <mwild1@gmail.com>
parents: 8549
diff changeset
    32
		if not conn_type then
5e9c87376891 net.connect: Handle case when resolver runs out of targets
Matthew Wild <mwild1@gmail.com>
parents: 8549
diff changeset
    33
			-- No more targets to try
11905
26406ce35e20 net.connect: Propagate last error message from resolvers
Kim Alvefur <zash@zash.se>
parents: 10949
diff changeset
    34
			p:log("debug", "No more connection targets to try", p.target_resolver.last_error);
12429
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    35
			if next(p.conns) == nil then
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    36
				p:log("debug", "No more targets, no pending connections. Connection failed.");
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    37
				if p.listeners.onfail then
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    38
					p.listeners.onfail(p.data, p.last_error or p.target_resolver.last_error or "unable to resolve service");
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    39
				end
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    40
			else
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    41
				p:log("debug", "One or more connection attempts are still pending. Waiting for now.");
8550
5e9c87376891 net.connect: Handle case when resolver runs out of targets
Matthew Wild <mwild1@gmail.com>
parents: 8549
diff changeset
    42
			end
5e9c87376891 net.connect: Handle case when resolver runs out of targets
Matthew Wild <mwild1@gmail.com>
parents: 8549
diff changeset
    43
			return;
5e9c87376891 net.connect: Handle case when resolver runs out of targets
Matthew Wild <mwild1@gmail.com>
parents: 8549
diff changeset
    44
		end
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    45
		p:log("debug", "Next target to try is %s:%d", ip, port);
12209
a2e6605303fa net.connect: Allow passing TLS context from resolver
Kim Alvefur <zash@zash.se>
parents: 11907
diff changeset
    46
		local conn, err = server.addclient(ip, port, pending_connection_listeners, p.options.pattern or "*a",
a2e6605303fa net.connect: Allow passing TLS context from resolver
Kim Alvefur <zash@zash.se>
parents: 11907
diff changeset
    47
			extra and extra.sslctx or p.options.sslctx, conn_type, extra);
8551
162f75ac2693 net.connect: Handle immediate failures of server.addclient
Matthew Wild <mwild1@gmail.com>
parents: 8550
diff changeset
    48
		if not conn then
10116
b327f2870382 net.*: Remove tostring call from logging
Kim Alvefur <zash@zash.se>
parents: 9390
diff changeset
    49
			log("debug", "Connection attempt failed immediately: %s", err);
8551
162f75ac2693 net.connect: Handle immediate failures of server.addclient
Matthew Wild <mwild1@gmail.com>
parents: 8550
diff changeset
    50
			p.last_error = err or "unknown reason";
162f75ac2693 net.connect: Handle immediate failures of server.addclient
Matthew Wild <mwild1@gmail.com>
parents: 8550
diff changeset
    51
			return attempt_connection(p);
162f75ac2693 net.connect: Handle immediate failures of server.addclient
Matthew Wild <mwild1@gmail.com>
parents: 8550
diff changeset
    52
		end
12415
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    53
		p.conns[conn] = true;
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    54
		pending_connections_map[conn] = p;
12416
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    55
		if more_targets_available then
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    56
			timer.add_task(0.250, function ()
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    57
				if not p.connected then
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    58
					p:log("debug", "Still not connected, making parallel connection attempt...");
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    59
					attempt_connection(p);
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    60
				end
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    61
			end);
18a3a6218100 net.connect: When more targets are immediately available, try them after a delay
Matthew Wild <mwild1@gmail.com>
parents: 12415
diff changeset
    62
		end
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    63
	end);
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    64
end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    65
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    66
function pending_connection_listeners.onconnect(conn)
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    67
	local p = pending_connections_map[conn];
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    68
	if not p then
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    69
		log("warn", "Successful connection, but unexpected! Closing.");
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    70
		conn:close();
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    71
		return;
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    72
	end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    73
	pending_connections_map[conn] = nil;
12415
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    74
	if p.connected then
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    75
		-- We already succeeded in connecting
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    76
		p.conns[conn] = nil;
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    77
		conn:close();
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    78
		return;
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    79
	end
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    80
	p.connected = true;
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    81
	p:log("debug", "Successfully connected");
9390
33e52f727f0f net.connect: Fix passing request table to new listener
Kim Alvefur <zash@zash.se>
parents: 9389
diff changeset
    82
	conn:setlistener(p.listeners, p.data);
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    83
	return p.listeners.onconnect(conn);
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    84
end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    85
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    86
function pending_connection_listeners.ondisconnect(conn, reason)
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    87
	local p = pending_connections_map[conn];
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    88
	if not p then
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    89
		log("warn", "Failed connection, but unexpected!");
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    90
		return;
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    91
	end
12415
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
    92
	p.conns[conn] = nil;
12472
353836684009 net.connect: Fix accumulation of connection attempt references
Kim Alvefur <zash@zash.se>
parents: 12430
diff changeset
    93
	pending_connections_map[conn] = nil;
8549
d66916dc318a net.connect: Track last connection error
Matthew Wild <mwild1@gmail.com>
parents: 8539
diff changeset
    94
	p.last_error = reason or "unknown reason";
d66916dc318a net.connect: Track last connection error
Matthew Wild <mwild1@gmail.com>
parents: 8539
diff changeset
    95
	p:log("debug", "Connection attempt failed: %s", p.last_error);
12430
7a3da1acace1 net.connect: Improve logging on connection attempt failure
Matthew Wild <mwild1@gmail.com>
parents: 12429
diff changeset
    96
	if p.connected then
7a3da1acace1 net.connect: Improve logging on connection attempt failure
Matthew Wild <mwild1@gmail.com>
parents: 12429
diff changeset
    97
		p:log("debug", "Connection already established, ignoring failure");
7a3da1acace1 net.connect: Improve logging on connection attempt failure
Matthew Wild <mwild1@gmail.com>
parents: 12429
diff changeset
    98
	elseif next(p.conns) == nil then
12429
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
    99
		p:log("debug", "No pending connection attempts, and not yet connected");
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
   100
		attempt_connection(p);
12430
7a3da1acace1 net.connect: Improve logging on connection attempt failure
Matthew Wild <mwild1@gmail.com>
parents: 12429
diff changeset
   101
	else
7a3da1acace1 net.connect: Improve logging on connection attempt failure
Matthew Wild <mwild1@gmail.com>
parents: 12429
diff changeset
   102
		p:log("debug", "Other attempts are still pending, ignoring failure");
12429
eabcc3ae22e9 net.connect: Improve handling of failure when attempts are still pending
Matthew Wild <mwild1@gmail.com>
parents: 12416
diff changeset
   103
	end
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   104
end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   105
10627
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   106
local function connect(target_resolver, listeners, options, data)
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   107
	local p = setmetatable({
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   108
		id = new_id();
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   109
		target_resolver = target_resolver;
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   110
		listeners = assert(listeners);
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   111
		options = options or {};
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   112
		data = data;
12415
e132a4279914 net.connect: Support for multiple pending connection attempts
Matthew Wild <mwild1@gmail.com>
parents: 12209
diff changeset
   113
		conns = {};
10627
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   114
	}, pending_connection_mt);
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   115
10627
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   116
	p:log("debug", "Starting connection process");
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   117
	attempt_connection(p);
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   118
end
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   119
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   120
return {
10627
f51c88baeb8a Backed out changeset 44ef46e1a951 (not optimal API)
Matthew Wild <mwild1@gmail.com>
parents: 10616
diff changeset
   121
	connect = connect;
8534
601681acea73 net.connect: New API for outgoing connections, based on 'service resolvers'
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   122
};