-
Notifications
You must be signed in to change notification settings - Fork 246
Description
Hello,
I'm building a testing-tool on top of isahc
and couldn't figure out some odd behaviour around the TCP connection as detailed here and here.
I finally narrowed it to down to the TCP socket not having the option TCP_NODELAY
set on it even though the Rust program explicity sets this. I used strace
and knetstat to verify this. I'm able to reproduce the issue locally with the following simple example:
Setup:
OS: Debian 4.19.171-2
OpenSSL 1.1.1d
Cargo.toml:
[dependencies]
curl = "0.4.34"
Rust program:
use curl::easy::Easy;
fn main() {
let mut easy = Easy::new();
easy.verbose(true);
easy.tcp_nodelay(true).unwrap();
easy.url("https://www.example.org/").unwrap();
easy.perform().unwrap();
println!("{}", easy.response_code().unwrap());
}
Curl command(for comparison):
# curl --version
curl 7.64.0 (x86_64-pc-linux-gnu) libcurl/7.64.0 OpenSSL/1.1.1d zlib/1.2.11 libidn2/2.0.5 libpsl/0.20.2 (+libidn2/2.0.5) libssh2/1.8.0 nghttp2/1.36.0 librtmp/2.3
Release-Date: 2019-02-06
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
#curl -v https://www.example.org/
Note: curl
has TCP_NODELAY
on by default now
Differences between cargo run
and curl
command
stdout
cargo run
* Trying 93.184.216.34:443...
* Connected to www.example.org (93.184.216.34) port 443 (#0)
curl
* Trying 93.184.216.34... * TCP_NODELAY set * Connected to www.example.org (93.184.216.34) port 443 (#0)
knetstat
cargo run
0 523 10.140.0.2:48106 93.184.216.34:443 ESTB ># SO_REUSEADDR=0,SO_REUSEPORT=0,SO_KEEPALIVE=0,TCP_NODELAY=0,TCP_DEFER_ACCEPT=0
curl
0 523 10.140.0.2:48190 93.184.216.34:443 ESTB ># SO_REUSEADDR=0,SO_REUSEPORT=0,SO_KEEPALIVE=1,TCP_KEEPIDLE=60,TCP_KEEPINTVL=60,TCP_NODELAY=1,TCP_DEFER_ACCEPT=0
strace
cargo run
# strace -f -tt cargo run 2>&1 | grep sock
11:09:49.017727 socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 5
11:09:49.144278 getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
11:09:49.145171 getsockname(5, {sa_family=AF_INET, sin_port=htons(48204), sin_addr=inet_addr("10.140.0.2")}, [256->16]) = 0
curl
# strace -f -tt curl -v https://www.example.org/ 2>&1 | grep sock 11:07:42.346046 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 11:07:42.346623 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0 11:07:42.347039 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 11:07:42.347091 setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0 11:07:42.347301 setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0 11:07:42.473227 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 11:07:42.473664 getsockname(3, {sa_family=AF_INET, sin_port=htons(48198), sin_addr=inet_addr("10.140.0.2")}, [128->16]) = 0
The curl-rust
implementation here and here look the same as every other option and I've double-checked that the code 121
is correct. I'm not sure why the options are not getting set on the socket though.
Is my usage of easy.tcp_nodelay(true)
incorrect in the sample program?
Can you please try to reproduce this issue too?
/cc @sagebind (sort of related to sagebind/isahc#303)
Activity
sagebind commentedon Mar 2, 2021
I am not able to naively reproduce using your exact sample code on my current machine:
I stepped through the code briefly and verified that libcurl was setting
tcp_nodelay
internally in response to theEasy::tcp_nodelay
call. I can look into this deeper tonight.tailrecur commentedon Mar 2, 2021
@sagebind My gut feel is that I'm doing something wrong locally since this would be a bigger bug otherwise that would have been noticed earlier.
The only difference I can see in your example is that you are using
IPv6
. Would it be possible to forceIpv4
usage?I'll try to repeat this on a fresh server too.
tailrecur commentedon Mar 2, 2021
I tried this on a fresh VM instance with the following spec:
I still get the behaviour described above. I'm trying to figure out how to force Debian to use IPv6 resolution.
@sagebind which OS flavour are you on?
sagebind commentedon Mar 2, 2021
My test was run on macOS 10.15.7. With libcurl 7.64.1.
tailrecur commentedon Mar 2, 2021
@sagebind Thanks for the details!
I see the following behaviour on macOS 10.15.7 and libcurl 7.64.1.
With system curl
Cargo.toml:
TCP_DELAY is set correctly
with static-curl
Cargo.toml:
TCP_DELAY is not set.
I see no difference in output from
cargo tree
for both configurations.otool
confirms that one uses systemlibcurl
while the other does not.Can you also try this with the
static-curl
feature turned on please?sagebind commentedon Mar 4, 2021
@tailrecur Great discovery! I ran your example with
static-curl
enabled and was able to reproduce. I stepped through the program with a debugger and confirmed thatCURLOPT_TCP_NODELAY
is still getting set, but curl was ignoring it. The reason why is revealed when you get to the definition of the internaltcpnodelay
function:https://github.com/curl/curl/blob/2f33be817cbce6ad7a36f27dd7ada9219f13584c/lib/connect.c#L1055-L1074
However, the
TCP_NODELAY
C macro is not defined for some reason in our current build process forstatic-curl
, likely some missing headers, so libcurl just silently does nothing. I will look into this.sagebind commentedon Mar 4, 2021
It is also worth noting that looking for the
TCP_NODELAY set
debug line is not sufficient to know whetherTCP_NODELAY
is working or not because it was removed in curl 7.69.0:curl/curl@1b6cfb9
tailrecur commentedon Mar 4, 2021
@sagebind Thanks for the confirmation. Nice to know I wasn't dreaming this 😄
Nice to know. I was not too confident about relying on the debug logs in the original bug report and so wanted to independently confirm it with
strace
andknetstat
just in case.Thanks for the links to the curl code. I'll need to brush up on my C first to be able to tackle that.
Fix TCP_NODELAY not defined for static builds on Unix
Fix TCP_NODELAY not defined for static builds on Unix (#381)
Auto merge of #178 - rust-lang:dependabot/cargo/curl-0.4.35, r=JohnTitor