4923: webhooks send duplicate Content-Length headers under python3

nuxi

What version are you running?

4.0 RC2 (on Python 3.8)

I believe its running from the official docker images.

What's the URL of the page containing the problem?

Not entirely sure what to put here since its not really a page, I guess:

/admin/db/notifications/webhooktarget/

What steps will reproduce the problem?

  1. Run under python3 (the official docker image seems to repro)
  2. Set up a webhook
  3. Trigger the webhook (ie: open a review)

What is the expected output? What do you see instead?

Expected: an HTTP request sent from point A to point B

Actual: Here is the raw HTTP request that I captured in a pcap:

POST /generic-webhook-trigger/invoke?token=SECRET&jobQuietPeriod=0 HTTP/1.1
Accept-Encoding: identity
Content-Type: application/x-www-form-urlencoded
Content-Length: 2656
Host: jenkins
User-Agent: Python-urllib/3.8
X-Reviewboard-Event: review_request_published
Content-Type: application/json
Content-Length: 2656
User-Agent: ReviewBoard-WebHook/4.0rc2
Connection: close

As you can see there are duplicate Content-Type headers with different values and duplicate Content-Length headers. The latter makes Apache and Nginx reject the HTTP request.

Here is what Apache 2.4.46 logged:

Fri Apr 16 21:25:22.156941 2021] [core:debug] [pid 10720:tid 140064923318016] protocol.c(1388): [client 172.18.30.79:47508] AH10242: client sent invalid Content-Length (2656, 2656): /generic-webhook-trigger/invoke
[Fri Apr 16 21:25:22.156988 2021] [headers:debug] [pid 10720:tid 140064923318016] mod_headers.c(899): AH01503: headers: ap_headers_error_filter()
0065411356416] mod_headers.c(899): AH01503: headers: ap_headers_error_filter()

Here is the log from reviewboard:

Traceback (most recent call last):
  File "/venv/lib/python3.8/site-packages/reviewboard/notifications/webhooks.py", line 398, in dispatch_webhook_event
    urlopen(Request(url, body, headers))
  File "/usr/lib/python3.8/urllib/request.py", line 222, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib/python3.8/urllib/request.py", line 531, in open
    response = meth(req, response)
  File "/usr/lib/python3.8/urllib/request.py", line 640, in http_response
    response = self.parent.error(
  File "/usr/lib/python3.8/urllib/request.py", line 569, in error
    return self._call_chain(*args)
  File "/usr/lib/python3.8/urllib/request.py", line 502, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.8/urllib/request.py", line 649, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 400: Bad Request

What operating system are you using? What browser?

I'm running Debian 11 + Firefox 87, but I don't think thats relevant ;)

Please provide any additional information below.

Here is where the headers are set up:

https://github.com/reviewboard/reviewboard/blob/release-4.0rc2/reviewboard/notifications/webhooks.py#L368

The code has been present since RB 2.5.8:

https://github.com/reviewboard/reviewboard/commit/975b8e56d928100b6ff9e11a86399435440222e7

The code works in python 2.7 but results in duplicate headers under python 3.6, 3.8, and 3.9.