Modify

Opened 6 months ago

Closed 6 months ago

#13701 closed defect (fixed)

TypeError: http header key must be a string

Reported by: Massimo Owned by: matobaa
Priority: normal Component: FieldTooltipPlugin
Severity: blocker Keywords:
Cc: Trac Release: 1.4

Description

The current version in PyPI (0.8.3) leads to

2019-11-29 10:14:46,122 Trac[main] ERROR: [10.190.226.173] Internal Server Error: <RequestWithSession "POST '/tracfieldtooltip/tooltip.jsonrpc'">, referrer 'https://project-pb/trac-pp/ticket/155'
Traceback (most recent call last):
  File "/mnt/data/trac/.local/lib64/python2.7/site-packages/trac/web/main.py", line 639, in dispatch_request
    dispatcher.dispatch(req)
  File "/mnt/data/trac/.local/lib64/python2.7/site-packages/trac/web/main.py", line 250, in dispatch
    resp = chosen_handler.process_request(req)
  File "build/bdist.linux-x86_64/egg/tracfieldtooltip/tooltip.py", line 176, in process_request
    req.end_headers()
  File "/mnt/data/trac/.local/lib64/python2.7/site-packages/trac/web/api.py", line 732, in end_headers
    exc_info)
TypeError: http header key must be a string

I double checked with the build from last SVN version TracFieldTooltip-0.8.3-py2.7.egg, same issue. Last working version is FieldTooltip-0.7.3-py2.7.egg

Attachments (0)

Change History (8)

comment:1 Changed 6 months ago by matobaa

Worksforme. (python 2.7.16 64bit on windows 10)

Could you show your "System Information" at "about trac" and /etc/*-release?

comment:3 Changed 6 months ago by Jun Omae

BTW, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag, ETag header should be ETag: W/"<etag_value>" or ETag: "<etag_value>".

I think the value should be enclosed with double quote characters.

source:fieldtooltipplugin/0.12/tracfieldtooltip/tooltip.py@17587:175#L175

-        req.send_header('ETag', "%s" % id(self.pages))
+        req.send_header('ETag', '"%s"' % id(self.pages))
Last edited 6 months ago by Jun Omae (previous) (diff)

comment:4 Changed 6 months ago by matobaa

Resolution: fixed
Status: newclosed

comment:5 Changed 6 months ago by Jun Omae

When If-None-Match header is missing, AttributeError occurs.

Traceback (most recent call last):
  File "/dev/shm/venv/lib/python2.7/site-packages/trac/web/main.py", line 647, in _dispatch_request
    dispatcher.dispatch(req)
  File "/dev/shm/venv/lib/python2.7/site-packages/trac/web/main.py", line 248, in dispatch
    resp = chosen_handler.process_request(req)
  File "/dev/shm/venv/lib/python2.7/site-packages/tracfieldtooltip/tooltip.py", line 164, in process_request
    if req.get_header('If-None-Match').strip('"') == str(id(self.pages)):
AttributeError: 'NoneType' object has no attribute 'strip'

Another issue, the ETag value is not stable if web server is multiprocess (e.g. mod_wsgi, uwsgi, ...).

I don't think id() should be used for ETag value because id() returns the address of the object in memory. See also https://docs.python.org/2/library/functions.html#id. hashlib.sha1() should be used rather than id().

Also, trivial thing, we could use b'header' rather than str('header').

$ for i in $(seq 1 10); do
>   curl -v -o /dev/null -X POST \
>     -H 'Content-Type: application/json' -H 'If-None-Match: X' \
>     -d '{"method":"wiki.getPage"}' \
>     http://127.0.0.1:3000/tracenv/tracfieldtooltip/tooltip.jsonrpc 2>&1 \
>     | grep '^< ETag:'
> done
< ETag: "140066037392280"
< ETag: "140066037441152"
< ETag: "140066037392280"
< ETag: "140066037441152"
< ETag: "140066037392280"
< ETag: "140066037441152"
< ETag: "140066037392280"
< ETag: "140066037441152"
< ETag: "140066037392280"
< ETag: "140066037441152"

Proposed patch:

  • fieldtooltipplugin/0.12/tracfieldtooltip/tooltip.py

    diff --git a/fieldtooltipplugin/0.12/tracfieldtooltip/tooltip.py b/fieldtooltipplugin/0.12/tracfieldtooltip/tooltip.py
    index 5bfee0968..72361d120 100644
    a b from pkg_resources import ResourceManager 
    1212from trac.cache import cached
    1313from trac.core import Component, implements
    1414from trac.util import arity
     15from trac.util.text import to_utf8
    1516from trac.web.api import IRequestHandler, IRequestFilter
    1617from trac.web.chrome import ITemplateProvider, add_script, add_stylesheet, web_context
    1718from trac.wiki.api import IWikiChangeListener, WikiSystem
    1819from trac.wiki.formatter import format_to_html
    1920from trac.wiki.model import WikiPage
     21import binascii
     22import hashlib
    2023import json
    2124
    2225
    class Tooltip(Component): 
    156159    def process_request(self, req):
    157160        def to_html(dom):
    158161            return format_to_html(self.env, web_context(req), dom, False)
     162        def build_etag(pages):
     163            h = hashlib.sha1()
     164            for name in sorted(pages):
     165                h.update(to_utf8(name))
     166                h.update(b'\0')
     167                h.update(to_utf8(pages[name]))
     168                h.update(b'\0')
     169            return '"%s"' % binascii.hexlify(h.digest())
    159170        payload = json.load(req)
    160171        if 'method' not in payload or not payload['method'] == 'wiki.getPage':
    161172            req.send_response(501)  # Method Not Implemented
    162173            req.end_headers()
    163174            return
    164         if req.get_header('If-None-Match').strip('"') == str(id(self.pages)):
     175        etag = build_etag(self.pages)
     176        if req.get_header('If-None-Match') == etag:
    165177            req.send_response(304)  # Not Modified
    166178            req.end_headers()
    167179            return
    class Tooltip(Component): 
    170182            'defaults': {page: to_html(self._default_pages[page]) for page in self._default_pages},
    171183        }, indent=4)
    172184        req.send_response(200)
    173         req.send_header(str('Content-Type'), 'application/json')
    174         req.send_header(str('Content-Length'), len(content))
    175         req.send_header(str('ETag'), '"%s"' % id(self.pages))
     185        req.send_header(b'Content-Type', 'application/json')
     186        req.send_header(b'Content-Length', len(content))
     187        req.send_header(b'ETag', etag)
    176188        req.end_headers()
    177189        req.write(content)
    178190

comment:6 in reply to:  5 Changed 6 months ago by Massimo

Replying to Jun Omae:

When If-None-Match header is missing, AttributeError occurs. ...

If this is still blocking this ticket, please reopen or open a new one.

comment:7 Changed 6 months ago by matobaa

Resolution: fixed
Status: closedreopened

comment:8 Changed 6 months ago by matobaa

Resolution: fixed
Status: reopenedclosed

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain matobaa.
The resolution will be deleted. Next status will be 'reopened'.

Add Comment


E-mail address and name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.