>Synopsis: one side of the http relay gets changed to tcp relay if first
>query of the persistent connection is empty OPTIONS
>Category: system
>Environment:
System : OpenBSD 6.5
Details : OpenBSD 6.5-current (GENERIC.MP) #11: Mon May 6
17:40:38 MDT 2019
[email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Architecture: OpenBSD.amd64
Machine : amd64
>Description:
When first query of persistent http connection is OPTIONS without body
one direction of the session gets changed to tcp relay. The session
still works but eg appending request headers stops happening.
>How-To-Repeat:
relayd.conf:
log state changes
log connection
table <test> { 4.5.6.7 }
http protocol test_http {
match request header set "X-Forwarded-For" value "$REMOTE_ADDR"
match request url log
}
relay test {
listen on 1.2.3.4 port 80
protocol test_http
forward to <test> port 80
}
test program:
#!/usr/bin/env ruby
require 'net/http/persistent'
uri = URI.parse('http://someserver')
http = Net::HTTP::Persistent.new
req = Net::HTTP::Options.new(uri.path)
req.body = "asdjhakdjahsdkjsad" if ARGV[1]
http.request(uri, req) if ARGV[0]
req = Net::HTTP::Get.new(uri.path)
http.request(uri, req)
http.request(uri, req)
Just 2 GET queries.
$ ./emptyoptions.rb
relayd log: relay test, session 9 (1 active), 0, 1.2.3.4 -> 5.6.7.8:80, done,
[User-Agent: Ruby] [someserver/] GET; [User-Agent: Ruby]
[someserver/] GET;
OPTIONS with some body as the first query.
$ ./emptyoptions.rb 1 1
relayd log: relay test, session 10 (1 active), 0, 1.2.3.4 -> 5.6.7.8:80, done,
[User-Agent: Ruby] [someserver/] OPTIONS; [User-Agent: Ruby]
[someserver/] GET; [User-Agent: Ruby] [someserver/] GET;
OPTIONS without body as the first query.
$ ./emptyoptions.rb 1
relayd log: relay test, session 11 (1 active), 0, 1.2.3.4 -> 5.6.7.8:80, done,
[User-Agent: Ruby] [someserver/] OPTIONS;;;
<code/input/activities to reproduce the problem (multiple lines)>
>Fix:
I'm working on a fix but so far I haven't found one which doesn't
break some regress test. Avoiding following code path in
relay_http.c solves the problem.
/* Single-pass HTTP body */
if (cre->toread < 0) {
cre->toread = TOREAD_UNLIMITED;
bev->readcb = relay_read;
}