We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cache-control header is filled incorrectly
When a cache-control header is formed a ';' character is used as a delimiter:

https://github.com/buildbot/buildbot/blob/144eb7e82dc261e6506f1f68493446bcb24d77a0/master/buildbot/www/config.py#L120

This is not allowed by [RFC 7234](https://tools.ietf.org/html/rfc7234). The RFC states the following format of the header:
```
Cache-Control = *( "," OWS ) cache-directive *( OWS "," [ OWS
    cache-directive ] )
 cache-directive = token [ "=" ( token / quoted-string ) ]
```

Thus a replace `;` -> `, ` is required.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `master/buildbot/www/config.py`
Content:
```
1 # This file is part of Buildbot.  Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Copyright Buildbot Team Members
15 
16 
17 import json
18 import os
19 import posixpath
20 
21 import jinja2
22 
23 from twisted.internet import defer
24 from twisted.python import log
25 from twisted.web.error import Error
26 
27 from buildbot.interfaces import IConfigured
28 from buildbot.util import unicode2bytes
29 from buildbot.www import resource
30 
31 
32 class IndexResource(resource.Resource):
33     # enable reconfigResource calls
34     needsReconfig = True
35 
36     def __init__(self, master, staticdir):
37         super().__init__(master)
38         loader = jinja2.FileSystemLoader(staticdir)
39         self.jinja = jinja2.Environment(
40             loader=loader, undefined=jinja2.StrictUndefined)
41 
42     def reconfigResource(self, new_config):
43         self.config = new_config.www
44 
45         versions = self.getEnvironmentVersions()
46         vs = self.config.get('versions')
47         if isinstance(vs, list):
48             versions += vs
49         self.config['versions'] = versions
50 
51         self.custom_templates = {}
52         template_dir = self.config.pop('custom_templates_dir', None)
53         if template_dir is not None:
54             template_dir = os.path.join(self.master.basedir, template_dir)
55             self.custom_templates = self.parseCustomTemplateDir(template_dir)
56 
57     def render_GET(self, request):
58         return self.asyncRenderHelper(request, self.renderIndex)
59 
60     def parseCustomTemplateDir(self, template_dir):
61         res = {}
62         allowed_ext = [".html"]
63         try:
64             import pyjade   # pylint: disable=import-outside-toplevel
65             allowed_ext.append(".jade")
66         except ImportError:  # pragma: no cover
67             log.msg("pyjade not installed. Ignoring .jade files from {}".format(template_dir))
68             pyjade = None
69         for root, dirs, files in os.walk(template_dir):
70             if root == template_dir:
71                 template_name = posixpath.join("views", "%s.html")
72             else:
73                 # template_name is a url, so we really want '/'
74                 # root is a os.path, though
75                 template_name = posixpath.join(
76                     os.path.basename(root), "views", "%s.html")
77             for f in files:
78                 fn = os.path.join(root, f)
79                 basename, ext = os.path.splitext(f)
80                 if ext not in allowed_ext:
81                     continue
82                 if ext == ".html":
83                     with open(fn) as f:
84                         html = f.read().strip()
85                 elif ext == ".jade":
86                     with open(fn) as f:
87                         jade = f.read()
88                         parser = pyjade.parser.Parser(jade)
89                         block = parser.parse()
90                         compiler = pyjade.ext.html.Compiler(
91                             block, pretty=False)
92                         html = compiler.compile()
93                 res[template_name % (basename,)] = html
94 
95         return res
96 
97     @staticmethod
98     def getEnvironmentVersions():
99         import sys   # pylint: disable=import-outside-toplevel
100         import twisted   # pylint: disable=import-outside-toplevel
101         from buildbot import version as bbversion   # pylint: disable=import-outside-toplevel
102 
103         pyversion = '.'.join(map(str, sys.version_info[:3]))
104 
105         tx_version_info = (twisted.version.major,
106                            twisted.version.minor,
107                            twisted.version.micro)
108         txversion = '.'.join(map(str, tx_version_info))
109 
110         return [
111             ('Python', pyversion),
112             ('Buildbot', bbversion),
113             ('Twisted', txversion),
114         ]
115 
116     @defer.inlineCallbacks
117     def renderIndex(self, request):
118         config = {}
119         request.setHeader(b"content-type", b'text/html')
120         request.setHeader(b"Cache-Control", b"public;max-age=0")
121 
122         try:
123             yield self.config['auth'].maybeAutoLogin(request)
124         except Error as e:
125             config["on_load_warning"] = e.message
126 
127         user_info = self.master.www.getUserInfos(request)
128         config.update({"user": user_info})
129 
130         config.update(self.config)
131         config['buildbotURL'] = self.master.config.buildbotURL
132         config['title'] = self.master.config.title
133         config['titleURL'] = self.master.config.titleURL
134         config['multiMaster'] = self.master.config.multiMaster
135 
136         # delete things that may contain secrets
137         if 'change_hook_dialects' in config:
138             del config['change_hook_dialects']
139 
140         def toJson(obj):
141             try:
142                 obj = IConfigured(obj).getConfigDict()
143             except TypeError:
144                 # this happens for old style classes (not deriving objects)
145                 pass
146             if isinstance(obj, dict):
147                 return obj
148             # don't leak object memory address
149             obj = obj.__class__.__module__ + "." + obj.__class__.__name__
150             return repr(obj) + " not yet IConfigured"
151 
152         tpl = self.jinja.get_template('index.html')
153         # we use Jinja in order to render some server side dynamic stuff
154         # For example, custom_templates javascript is generated by the
155         # layout.jade jinja template
156         tpl = tpl.render(configjson=json.dumps(config, default=toJson),
157                          custom_templates=self.custom_templates,
158                          config=self.config)
159         return unicode2bytes(tpl, encoding='ascii')
160
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/master/buildbot/www/config.py b/master/buildbot/www/config.py
--- a/master/buildbot/www/config.py
+++ b/master/buildbot/www/config.py
@@ -117,7 +117,7 @@
     def renderIndex(self, request):
         config = {}
         request.setHeader(b"content-type", b'text/html')
-        request.setHeader(b"Cache-Control", b"public;max-age=0")
+        request.setHeader(b"Cache-Control", b"public,max-age=0")
 
         try:
             yield self.config['auth'].maybeAutoLogin(request)

{"golden_diff": "diff --git a/master/buildbot/www/config.py b/master/buildbot/www/config.py\n--- a/master/buildbot/www/config.py\n+++ b/master/buildbot/www/config.py\n@@ -117,7 +117,7 @@\n     def renderIndex(self, request):\n         config = {}\n         request.setHeader(b\"content-type\", b'text/html')\n-        request.setHeader(b\"Cache-Control\", b\"public;max-age=0\")\n+        request.setHeader(b\"Cache-Control\", b\"public,max-age=0\")\n \n         try:\n             yield self.config['auth'].maybeAutoLogin(request)\n", "issue": "Cache-control header is filled incorrectly\nWhen a cache-control header is formed a ';' character is used as a delimiter:\r\n\r\nhttps://github.com/buildbot/buildbot/blob/144eb7e82dc261e6506f1f68493446bcb24d77a0/master/buildbot/www/config.py#L120\r\n\r\nThis is not allowed by [RFC 7234](https://tools.ietf.org/html/rfc7234). The RFC states the following format of the header:\r\n```\r\nCache-Control = *( \",\" OWS ) cache-directive *( OWS \",\" [ OWS\r\n    cache-directive ] )\r\n cache-directive = token [ \"=\" ( token / quoted-string ) ]\r\n```\r\n\r\nThus a replace `;` -> `, ` is required.\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\n\nimport json\nimport os\nimport posixpath\n\nimport jinja2\n\nfrom twisted.internet import defer\nfrom twisted.python import log\nfrom twisted.web.error import Error\n\nfrom buildbot.interfaces import IConfigured\nfrom buildbot.util import unicode2bytes\nfrom buildbot.www import resource\n\n\nclass IndexResource(resource.Resource):\n    # enable reconfigResource calls\n    needsReconfig = True\n\n    def __init__(self, master, staticdir):\n        super().__init__(master)\n        loader = jinja2.FileSystemLoader(staticdir)\n        self.jinja = jinja2.Environment(\n            loader=loader, undefined=jinja2.StrictUndefined)\n\n    def reconfigResource(self, new_config):\n        self.config = new_config.www\n\n        versions = self.getEnvironmentVersions()\n        vs = self.config.get('versions')\n        if isinstance(vs, list):\n            versions += vs\n        self.config['versions'] = versions\n\n        self.custom_templates = {}\n        template_dir = self.config.pop('custom_templates_dir', None)\n        if template_dir is not None:\n            template_dir = os.path.join(self.master.basedir, template_dir)\n            self.custom_templates = self.parseCustomTemplateDir(template_dir)\n\n    def render_GET(self, request):\n        return self.asyncRenderHelper(request, self.renderIndex)\n\n    def parseCustomTemplateDir(self, template_dir):\n        res = {}\n        allowed_ext = [\".html\"]\n        try:\n            import pyjade   # pylint: disable=import-outside-toplevel\n            allowed_ext.append(\".jade\")\n        except ImportError:  # pragma: no cover\n            log.msg(\"pyjade not installed. Ignoring .jade files from {}\".format(template_dir))\n            pyjade = None\n        for root, dirs, files in os.walk(template_dir):\n            if root == template_dir:\n                template_name = posixpath.join(\"views\", \"%s.html\")\n            else:\n                # template_name is a url, so we really want '/'\n                # root is a os.path, though\n                template_name = posixpath.join(\n                    os.path.basename(root), \"views\", \"%s.html\")\n            for f in files:\n                fn = os.path.join(root, f)\n                basename, ext = os.path.splitext(f)\n                if ext not in allowed_ext:\n                    continue\n                if ext == \".html\":\n                    with open(fn) as f:\n                        html = f.read().strip()\n                elif ext == \".jade\":\n                    with open(fn) as f:\n                        jade = f.read()\n                        parser = pyjade.parser.Parser(jade)\n                        block = parser.parse()\n                        compiler = pyjade.ext.html.Compiler(\n                            block, pretty=False)\n                        html = compiler.compile()\n                res[template_name % (basename,)] = html\n\n        return res\n\n    @staticmethod\n    def getEnvironmentVersions():\n        import sys   # pylint: disable=import-outside-toplevel\n        import twisted   # pylint: disable=import-outside-toplevel\n        from buildbot import version as bbversion   # pylint: disable=import-outside-toplevel\n\n        pyversion = '.'.join(map(str, sys.version_info[:3]))\n\n        tx_version_info = (twisted.version.major,\n                           twisted.version.minor,\n                           twisted.version.micro)\n        txversion = '.'.join(map(str, tx_version_info))\n\n        return [\n            ('Python', pyversion),\n            ('Buildbot', bbversion),\n            ('Twisted', txversion),\n        ]\n\n    @defer.inlineCallbacks\n    def renderIndex(self, request):\n        config = {}\n        request.setHeader(b\"content-type\", b'text/html')\n        request.setHeader(b\"Cache-Control\", b\"public;max-age=0\")\n\n        try:\n            yield self.config['auth'].maybeAutoLogin(request)\n        except Error as e:\n            config[\"on_load_warning\"] = e.message\n\n        user_info = self.master.www.getUserInfos(request)\n        config.update({\"user\": user_info})\n\n        config.update(self.config)\n        config['buildbotURL'] = self.master.config.buildbotURL\n        config['title'] = self.master.config.title\n        config['titleURL'] = self.master.config.titleURL\n        config['multiMaster'] = self.master.config.multiMaster\n\n        # delete things that may contain secrets\n        if 'change_hook_dialects' in config:\n            del config['change_hook_dialects']\n\n        def toJson(obj):\n            try:\n                obj = IConfigured(obj).getConfigDict()\n            except TypeError:\n                # this happens for old style classes (not deriving objects)\n                pass\n            if isinstance(obj, dict):\n                return obj\n            # don't leak object memory address\n            obj = obj.__class__.__module__ + \".\" + obj.__class__.__name__\n            return repr(obj) + \" not yet IConfigured\"\n\n        tpl = self.jinja.get_template('index.html')\n        # we use Jinja in order to render some server side dynamic stuff\n        # For example, custom_templates javascript is generated by the\n        # layout.jade jinja template\n        tpl = tpl.render(configjson=json.dumps(config, default=toJson),\n                         custom_templates=self.custom_templates,\n                         config=self.config)\n        return unicode2bytes(tpl, encoding='ascii')\n", "path": "master/buildbot/www/config.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\n\nimport json\nimport os\nimport posixpath\n\nimport jinja2\n\nfrom twisted.internet import defer\nfrom twisted.python import log\nfrom twisted.web.error import Error\n\nfrom buildbot.interfaces import IConfigured\nfrom buildbot.util import unicode2bytes\nfrom buildbot.www import resource\n\n\nclass IndexResource(resource.Resource):\n    # enable reconfigResource calls\n    needsReconfig = True\n\n    def __init__(self, master, staticdir):\n        super().__init__(master)\n        loader = jinja2.FileSystemLoader(staticdir)\n        self.jinja = jinja2.Environment(\n            loader=loader, undefined=jinja2.StrictUndefined)\n\n    def reconfigResource(self, new_config):\n        self.config = new_config.www\n\n        versions = self.getEnvironmentVersions()\n        vs = self.config.get('versions')\n        if isinstance(vs, list):\n            versions += vs\n        self.config['versions'] = versions\n\n        self.custom_templates = {}\n        template_dir = self.config.pop('custom_templates_dir', None)\n        if template_dir is not None:\n            template_dir = os.path.join(self.master.basedir, template_dir)\n            self.custom_templates = self.parseCustomTemplateDir(template_dir)\n\n    def render_GET(self, request):\n        return self.asyncRenderHelper(request, self.renderIndex)\n\n    def parseCustomTemplateDir(self, template_dir):\n        res = {}\n        allowed_ext = [\".html\"]\n        try:\n            import pyjade   # pylint: disable=import-outside-toplevel\n            allowed_ext.append(\".jade\")\n        except ImportError:  # pragma: no cover\n            log.msg(\"pyjade not installed. Ignoring .jade files from {}\".format(template_dir))\n            pyjade = None\n        for root, dirs, files in os.walk(template_dir):\n            if root == template_dir:\n                template_name = posixpath.join(\"views\", \"%s.html\")\n            else:\n                # template_name is a url, so we really want '/'\n                # root is a os.path, though\n                template_name = posixpath.join(\n                    os.path.basename(root), \"views\", \"%s.html\")\n            for f in files:\n                fn = os.path.join(root, f)\n                basename, ext = os.path.splitext(f)\n                if ext not in allowed_ext:\n                    continue\n                if ext == \".html\":\n                    with open(fn) as f:\n                        html = f.read().strip()\n                elif ext == \".jade\":\n                    with open(fn) as f:\n                        jade = f.read()\n                        parser = pyjade.parser.Parser(jade)\n                        block = parser.parse()\n                        compiler = pyjade.ext.html.Compiler(\n                            block, pretty=False)\n                        html = compiler.compile()\n                res[template_name % (basename,)] = html\n\n        return res\n\n    @staticmethod\n    def getEnvironmentVersions():\n        import sys   # pylint: disable=import-outside-toplevel\n        import twisted   # pylint: disable=import-outside-toplevel\n        from buildbot import version as bbversion   # pylint: disable=import-outside-toplevel\n\n        pyversion = '.'.join(map(str, sys.version_info[:3]))\n\n        tx_version_info = (twisted.version.major,\n                           twisted.version.minor,\n                           twisted.version.micro)\n        txversion = '.'.join(map(str, tx_version_info))\n\n        return [\n            ('Python', pyversion),\n            ('Buildbot', bbversion),\n            ('Twisted', txversion),\n        ]\n\n    @defer.inlineCallbacks\n    def renderIndex(self, request):\n        config = {}\n        request.setHeader(b\"content-type\", b'text/html')\n        request.setHeader(b\"Cache-Control\", b\"public,max-age=0\")\n\n        try:\n            yield self.config['auth'].maybeAutoLogin(request)\n        except Error as e:\n            config[\"on_load_warning\"] = e.message\n\n        user_info = self.master.www.getUserInfos(request)\n        config.update({\"user\": user_info})\n\n        config.update(self.config)\n        config['buildbotURL'] = self.master.config.buildbotURL\n        config['title'] = self.master.config.title\n        config['titleURL'] = self.master.config.titleURL\n        config['multiMaster'] = self.master.config.multiMaster\n\n        # delete things that may contain secrets\n        if 'change_hook_dialects' in config:\n            del config['change_hook_dialects']\n\n        def toJson(obj):\n            try:\n                obj = IConfigured(obj).getConfigDict()\n            except TypeError:\n                # this happens for old style classes (not deriving objects)\n                pass\n            if isinstance(obj, dict):\n                return obj\n            # don't leak object memory address\n            obj = obj.__class__.__module__ + \".\" + obj.__class__.__name__\n            return repr(obj) + \" not yet IConfigured\"\n\n        tpl = self.jinja.get_template('index.html')\n        # we use Jinja in order to render some server side dynamic stuff\n        # For example, custom_templates javascript is generated by the\n        # layout.jade jinja template\n        tpl = tpl.render(configjson=json.dumps(config, default=toJson),\n                         custom_templates=self.custom_templates,\n                         config=self.config)\n        return unicode2bytes(tpl, encoding='ascii')\n", "path": "master/buildbot/www/config.py"}]}