Skip to content

Commit

Permalink
Add server_tokens configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
mkgs committed Nov 29, 2018
1 parent ee7af12 commit 14a0cfc
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 5 deletions.
17 changes: 17 additions & 0 deletions docs/source/settings.rst
Expand Up @@ -1095,6 +1095,23 @@ The variables are passed to the the PasteDeploy entrypoint. Example::

.. versionadded:: 19.7

.. _server-tokens:

server_tokens
~~~~~~~~~~~~~

* ``--server-tokens``
* ``true``

Enable or disable emitting gunicorn version in the ``Server`` http response header.

This setting may also be used to emit a custom ``Server`` header value
or disable the header entirely.

A value of ``true`` will result in the default behavior. ``false`` will disable
emittance of gunicorn version. A string value will emit itself as a custom ``Server``
header value, and an empty string will disable the ``Server`` response header.

Server Socket
-------------

Expand Down
31 changes: 30 additions & 1 deletion gunicorn/config.py
Expand Up @@ -17,7 +17,7 @@
import sys
import textwrap

from gunicorn import __version__, util
from gunicorn import __version__, util, SERVER_SOFTWARE
from gunicorn.errors import ConfigError
from gunicorn.reloader import reloader_engines

Expand Down Expand Up @@ -221,6 +221,17 @@ def paste_global_conf(self):

return global_conf

@property
def server_tokens(self):
s = self.settings['server_tokens'].get()
if s == 'true':
return SERVER_SOFTWARE

if s == 'false':
return 'gunicorn'

return s


class SettingMeta(type):
def __new__(cls, name, bases, attrs):
Expand Down Expand Up @@ -1988,3 +1999,21 @@ class PasteGlobalConf(Setting):
.. versionadded:: 19.7
"""

class ServerTokens(Setting):
name = "server_tokens"
section = "Server Mechanics"
cli = ["--server-tokens"]
validator = validate_string
default = 'true'
desc = """\
Enable or disable emitting gunicorn version in the ``Server`` http response header.
This setting may also be used to emit a custom ``Server`` header value
or disable the header entirely.
A value of ``true`` will result in the default behavior. ``false`` will disable
emittance of gunicorn version. A string value will emit itself as a custom ``Server``
header value, and an empty string will disable the ``Server`` response header.
"""

4 changes: 2 additions & 2 deletions gunicorn/http/wsgi.py
Expand Up @@ -194,7 +194,6 @@ class Response(object):
def __init__(self, req, sock, cfg):
self.req = req
self.sock = sock
self.version = SERVER_SOFTWARE
self.status = None
self.chunked = False
self.must_close = False
Expand Down Expand Up @@ -300,10 +299,11 @@ def default_headers(self):
headers = [
"HTTP/%s.%s %s\r\n" % (self.req.version[0],
self.req.version[1], self.status),
"Server: %s\r\n" % self.version,
"Date: %s\r\n" % util.http_date(),
"Connection: %s\r\n" % connection
]
if self.cfg and self.cfg.server_tokens:
headers.append("Server: %s\r\n" % self.cfg.server_tokens)
if self.chunked:
headers.append("Transfer-Encoding: chunked\r\n")
return headers
Expand Down
13 changes: 12 additions & 1 deletion tests/test_config.py
Expand Up @@ -8,7 +8,7 @@

import pytest

from gunicorn import config
from gunicorn import config, SERVER_SOFTWARE
from gunicorn.app.base import Application
from gunicorn.errors import ConfigError
from gunicorn.workers.sync import SyncWorker
Expand Down Expand Up @@ -435,3 +435,14 @@ def test_bind_fd():
with AltArgs(["prog_name", "-b", "fd://42"]):
app = NoConfigApp()
assert app.cfg.bind == ["fd://42"]


def test_server_tokens():
c = config.Config()
assert c.server_tokens == SERVER_SOFTWARE
c.set('server_tokens', 'false')
assert c.server_tokens == 'gunicorn'
c.set('server_tokens', 'server software')
assert c.server_tokens == 'server software'
c.set('server_tokens', '')
assert c.server_tokens == ''
41 changes: 40 additions & 1 deletion tests/test_http.py
Expand Up @@ -4,7 +4,7 @@
import t
import pytest

from gunicorn import util
from gunicorn import util, config, SERVER_SOFTWARE
from gunicorn.http.body import Body, LengthReader, EOFReader
from gunicorn.http.wsgi import Response
from gunicorn.http.unreader import Unreader, IterUnreader, SocketUnreader
Expand Down Expand Up @@ -226,3 +226,42 @@ def test_eof_reader_read_invalid_size():
reader.read([100])
with pytest.raises(ValueError):
reader.read(-100)


def test_http_server_header():
""" tests whether the http server header is set correctly """

def get_server_header(headers):
for header in headers:
if header[:8] == 'Server: ':
return header[8:].strip()
return None

mocked_socket = mock.MagicMock()
mocked_socket.sendall = mock.MagicMock()

mocked_request = mock.MagicMock()
c = config.Config()

# default server header
response = Response(mocked_request, mocked_socket, c)
headers = response.default_headers()
assert get_server_header(headers) == SERVER_SOFTWARE

# server header w/o version number
c.set('server_tokens', 'false')
response = Response(mocked_request, mocked_socket, c)
headers = response.default_headers()
assert get_server_header(headers) == 'gunicorn'

# custom server header
c.set('server_tokens', 'server software')
response = Response(mocked_request, mocked_socket, c)
headers = response.default_headers()
assert get_server_header(headers) == 'server software'

# disabled server header
c.set('server_tokens', '')
response = Response(mocked_request, mocked_socket, c)
headers = response.default_headers()
assert get_server_header(headers) is None

0 comments on commit 14a0cfc

Please sign in to comment.