# Buggy code
def start(address=None, port=5000, ssl_crt=None, ssl_key=None):
    """
    Api to listen for webhooks to send to the reactor.

    Implement the webhook behavior in an engine.
    :py:class:`rest_cherrypy Webhook docs <salt.netapi.rest_cherrypy.app.Webhook>`

    Unlike the rest_cherrypy Webhook, this is only an unauthenticated webhook
    endpoint.  If an authenticated webhook endpoint is needed, use the salt-api
    webhook which runs on the master and authenticates through eauth.

    .. note: This is really meant to be used on the minion, because salt-api
             needs to be run on the master for use with eauth.

    .. warning:: Unauthenticated endpoint

        This engine sends webhook calls to the event stream.  If the engine is
        running on a minion with `file_client: local` the event is sent to the
        minion event stream.  Otherwise it is sent to the master event stream.

    Example Config

    .. code-block:: yaml

        engines:
          - webhook: {}

    .. code-block:: yaml

        engines:
          - webhook:
              port: 8000
              address: 10.128.1.145
              ssl_crt: /etc/pki/tls/certs/localhost.crt
              ssl_key: /etc/pki/tls/certs/localhost.key

    .. note: For making an unsigned key, use the following command
             `salt-call --local tls.create_self_signed_cert`
    """
    if __opts__.get("__role") == "master":
        fire_master = salt.utils.event.get_master_event(
            __opts__, __opts__["sock_dir"]
        ).fire_event
    else:
        fire_master = None

    def fire(tag, msg):
        """
        How to fire the event
        """
        if fire_master:
            fire_master(msg, tag)
        else:
            __salt__["event.send"](tag, msg)

    class WebHook(tornado.web.RequestHandler):  # pylint: disable=abstract-method
        def post(self, tag):  # pylint: disable=arguments-differ
            body = self.request.body
            headers = self.request.headers
            payload = {"headers": headers, "body": body}
            fire("salt/engines/hook/" + tag, payload)

    application = tornado.web.Application(
        [
            (r"/(.*)", WebHook),
        ]
    )
    ssl_options = None
    if all([ssl_crt, ssl_key]):
        ssl_options = {"certfile": ssl_crt, "keyfile": ssl_key}
    io_loop = tornado.ioloop.IOLoop(make_current=False)
    http_server = tornado.httpserver.HTTPServer(
        application, ssl_options=ssl_options, io_loop=io_loop
    )
    http_server.listen(port, address=address)
    io_loop.start()


# Traceback (TypeError)
2018-01-21 21:52:25,664 [tornado.application][ERROR   ][31253] Uncaught exception POST /prometheus/alertmanager (127.0.0.1)
HTTPServerRequest(protocol='http', host='localhost:8000', method='POST', uri='/prometheus/alertmanager', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Host': 'localhost:8000', 'User-Agent': 'Alertmanager/0.9.1', 'Content-Type': 'application/json', 'Content-Length': '850', 'Accept-Encoding': 'gzip'})
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/tornado/web.py", line 1509, in _execute
result = method(*self.path_args, **self.path_kwargs)
File "/usr/lib/python2.7/site-packages/salt/engines/webhook.py", line 74, in post
fire('salt/engines/hook/' + tag, payload)
File "/usr/lib/python2.7/site-packages/salt/engines/webhook.py", line 65, in fire
fire_master(msg, tag)
File "/usr/lib/python2.7/site-packages/salt/utils/event.py", line 714, in fire_event
dump_data = self.serial.dumps(data)
File "/usr/lib/python2.7/site-packages/salt/payload.py", line 181, in dumps
return msgpack.dumps(msg, use_bin_type=use_bin_type)
File "/usr/lib/python2.7/site-packages/msgpack/__init__.py", line 47, in packb
return Packer(**kwargs).pack(o)
File "msgpack/_packer.pyx", line 274, in msgpack._packer.Packer.pack
File "msgpack/_packer.pyx", line 276, in msgpack._packer.Packer.pack
File "msgpack/_packer.pyx", line 224, in msgpack._packer.Packer._pack
File "msgpack/_packer.pyx", line 271, in msgpack._packer.Packer._pack
TypeError: can't serialize <tornado.httputil.HTTPHeaders object at 0x6005353be490>
2018-01-21 21:52:25,665 [tornado.access   ][ERROR   ][31253] 500 POST /prometheus/alertmanager (127.0.0.1) 1.90ms

# Fixed code
def start(address=None, port=5000, ssl_crt=None, ssl_key=None):
    """
    Api to listen for webhooks to send to the reactor.

    Implement the webhook behavior in an engine.
    :py:class:`rest_cherrypy Webhook docs <salt.netapi.rest_cherrypy.app.Webhook>`

    Unlike the rest_cherrypy Webhook, this is only an unauthenticated webhook
    endpoint.  If an authenticated webhook endpoint is needed, use the salt-api
    webhook which runs on the master and authenticates through eauth.

    .. note: This is really meant to be used on the minion, because salt-api
             needs to be run on the master for use with eauth.

    .. warning:: Unauthenticated endpoint

        This engine sends webhook calls to the event stream.  If the engine is
        running on a minion with `file_client: local` the event is sent to the
        minion event stream.  Otherwise it is sent to the master event stream.

    Example Config

    .. code-block:: yaml

        engines:
          - webhook: {}

    .. code-block:: yaml

        engines:
          - webhook:
              port: 8000
              address: 10.128.1.145
              ssl_crt: /etc/pki/tls/certs/localhost.crt
              ssl_key: /etc/pki/tls/certs/localhost.key

    .. note: For making an unsigned key, use the following command
             `salt-call --local tls.create_self_signed_cert`
    """
    if __opts__.get("__role") == "master":
        fire_master = salt.utils.event.get_master_event(
            __opts__, __opts__["sock_dir"]
        ).fire_event
    else:
        fire_master = None

    def fire(tag, msg):
        """
        How to fire the event
        """
        if fire_master:
            fire_master(msg, tag)
        else:
            __salt__["event.send"](tag, msg)

    class WebHook(tornado.web.RequestHandler):  # pylint: disable=abstract-method
        def post(self, tag):  # pylint: disable=arguments-differ
            body = self.request.body
            headers = self.request.headers
            payload = {
                "headers": headers if isinstance(headers, dict) else dict(headers),
                "body": body,
            }
            fire("salt/engines/hook/" + tag, payload)

    application = tornado.web.Application(
        [
            (r"/(.*)", WebHook),
        ]
    )
    ssl_options = None
    if all([ssl_crt, ssl_key]):
        ssl_options = {"certfile": ssl_crt, "keyfile": ssl_key}
    io_loop = tornado.ioloop.IOLoop(make_current=False)
    http_server = tornado.httpserver.HTTPServer(
        application, ssl_options=ssl_options, io_loop=io_loop
    )
    http_server.listen(port, address=address)
    io_loop.start()