예제 #1
0
    def test_json_encoder_adapter(self):
        """Testing JSONEncoderAdapter.encode"""
        encoder = WebAPIEncoder()
        adapter = JSONEncoderAdapter(encoder)

        content = adapter.encode(self.data)
        self.assertEqual(content, json.dumps(self.data))
예제 #2
0
    def test_json_encoder_adapter(self):
        """Testing JSONEncoderAdapter.encode"""
        encoder = WebAPIEncoder()
        adapter = JSONEncoderAdapter(encoder)

        content = adapter.encode(self.data)
        self.assertEqual(content, json.dumps(self.data, sort_keys=True))
예제 #3
0
    def _get_content(self):
        """Returns the API response content in the appropriate format.

        This is an overridden version of HttpResponse._get_content that
        generates the resulting content when requested, rather than
        generating it up-front in the constructor. This is used so that
        the @webapi decorator can set the appropriate API format before
        the content is generated, but after the response is created.
        """
        class MultiEncoder(WebAPIEncoder):
            def __init__(self, encoders):
                self.encoders = encoders

            def encode(self, *args, **kwargs):
                for encoder in self.encoders:
                    result = encoder.encode(*args, **kwargs)

                    if result is not None:
                        return result

                return None

        if not self.content_set:
            adapter = None
            encoder = MultiEncoder(self.encoders)

            # See the note above about the check for text/plain.
            if (self.mimetype == 'text/plain' or
                is_mimetype_a(self.mimetype, 'application/json')):
                adapter = JSONEncoderAdapter(encoder)
            elif is_mimetype_a(self.mimetype, "application/xml"):
                adapter = XMLEncoderAdapter(encoder)
            else:
                assert False

            content = adapter.encode(self.api_data, request=self.request,
                                     **self.encoder_kwargs)

            if self.callback is not None:
                content = "%s(%s);" % (self.callback, content)

            self.content = content
            self.content_set = True

        return super(WebAPIResponse, self).content
예제 #4
0
def dispatch(request, handlers, event, payload):
    """Dispatch the given event and payload to the given handlers."""
    encoder = BasicAPIEncoder()
    adapter = JSONEncoderAdapter(encoder)
    body = adapter.encode(payload, request=request)
    body = body.encode('utf-8')

    headers = {
        'X-ReviewBoard-Event': event,
        'Content-Type': 'application/json',
        'Content-Length': len(body),
    }

    for handler in handlers:
        signer = hmac.new(handler.secret.encode('utf-8'), body)
        headers['X-ReviewBoard-Signature'] = signer.hexdigest()

        logging.info('Dispatching webhook for event %s to %s',
                     event, handler.url)
        urlopen(Request(handler.url, body, headers))
예제 #5
0
def dispatch(request, handlers, event, payload):
    """Dispatch the given event and payload to the given handlers."""
    encoder = BasicAPIEncoder()
    adapter = JSONEncoderAdapter(encoder)
    body = adapter.encode(payload, request=request)
    body = body.encode('utf-8')

    headers = {
        'X-ReviewBoard-Event': event,
        'Content-Type': 'application/json',
        'Content-Length': len(body),
    }

    for handler in handlers:
        signer = hmac.new(handler.secret.encode('utf-8'), body)
        headers['X-ReviewBoard-Signature'] = signer.hexdigest()

        logging.info('Dispatching webhook for event %s to %s', event,
                     handler.url)
        urlopen(Request(handler.url, body, headers))
예제 #6
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given webhook targets."""
    encoder = BasicAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            body = render_custom_content(webhook_target.custom_content,
                                         payload)
        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                if encoding == webhook_target.ENCODING_JSON:
                    adapter = JSONEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_XML:
                    adapter = XMLEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_FORM_DATA:
                    adapter = JSONEncoderAdapter(encoder)
                    body = urlencode({
                        'payload':
                        adapter.encode(payload, request=request),
                    })
                else:
                    logging.error(
                        'Unexpected WebHookTarget encoding "%s" for '
                        'ID %s', encoding, webhook_target.pk)
                    continue

                body = body.encode('utf-8')
                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            'X-ReviewBoard-Event': event,
            'Content-Type': webhook_target.encoding,
            'Content-Length': len(body),
            'User-Agent': 'ReviewBoard-WebHook/%s' % get_package_version(),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body)
            headers['X-Hub-Signature'] = 'sha1=%s' % signer.hexdigest()

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        urlopen(Request(webhook_target.url, body, headers))
예제 #7
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content, payload)
            except Exception as e:
                logging.exception("Could not render WebHook payload: %s", e)
                continue

        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({"payload": adapter.encode(payload, request=request)})
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" ' "for ID %s", encoding, webhook_target.pk
                        )
                        continue
                except Exception as e:
                    logging.exception("Could not encode WebHook payload: %s", e)
                    continue

                body = body.encode("utf-8")
                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            "X-ReviewBoard-Event": event,
            "Content-Type": webhook_target.encoding,
            "Content-Length": len(body),
            "User-Agent": "ReviewBoard-WebHook/%s" % get_package_version(),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode("utf-8"), body, hashlib.sha1)
            headers["X-Hub-Signature"] = "sha1=%s" % signer.hexdigest()

        logging.info("Dispatching webhook for event %s to %s", event, webhook_target.url)
        try:
            urlopen(Request(webhook_target.url, body, headers))
        except Exception as e:
            logging.exception("Could not dispatch WebHook to %s: %s", webhook_target.url, e)
예제 #8
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given webhook targets."""
    encoder = BasicAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            body = render_custom_content(webhook_target.custom_content,
                                         payload)
        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                if encoding == webhook_target.ENCODING_JSON:
                    adapter = JSONEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_XML:
                    adapter = XMLEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_FORM_DATA:
                    adapter = JSONEncoderAdapter(encoder)
                    body = urlencode({
                        'payload': adapter.encode(payload, request=request),
                    })
                else:
                    logging.error('Unexpected WebHookTarget encoding "%s" for '
                                  'ID %s',
                                  encoding, webhook_target.pk)
                    continue

                body = body.encode('utf-8')
                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            'X-ReviewBoard-Event': event,
            'Content-Type': webhook_target.encoding,
            'Content-Length': len(body),
            'User-Agent': 'ReviewBoard-WebHook/%s' % get_package_version(),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body)
            headers['X-Hub-Signature'] = 'sha1=%s' % signer.hexdigest()

        logging.info('Dispatching webhook for event %s to %s',
                     event, webhook_target.url)
        urlopen(Request(webhook_target.url, body, headers))
예제 #9
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)

                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue

        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload':
                            adapter.encode(payload, request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" '
                            'for ID %s', encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event':
            event.encode('utf-8'),
            b'Content-Type':
            webhook_target.encoding.encode('utf-8'),
            b'Content-Length':
            len(body),
            b'User-Agent':
            ('ReviewBoard-WebHook/%s' % get_package_version()).encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        try:
            url = webhook_target.url.encode('utf-8')
            urlopen(Request(url, body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)
예제 #10
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets.

    Args:
        request (django.http.HttpRequest):
            The HTTP request from the client.

        webhook_targets (list of
                         reviewboard.notifications.models.WebHookTarget):
            The list of WebHook targets containing endpoint URLs to dispatch
            to.

        event (unicode):
            The name of the event being dispatched.

        payload (dict):
            The payload data to encode for the WebHook payload.

    Raises:
        ValueError:
            There was an error with the payload format. Details are in the
            log and the exception message.
    """
    encoder = BasicAPIEncoder()
    bodies = {}

    raw_norm_payload = None
    json_norm_payload = None

    for webhook_target in webhook_targets:
        use_custom_content = webhook_target.use_custom_content
        encoding = webhook_target.encoding

        # See how we need to handle normalizing this payload. If we need
        # something JSON-safe, then we need to go the more aggressive route
        # and normalize keys to strings.
        if raw_norm_payload is None or json_norm_payload is None:
            try:
                if (raw_norm_payload is None
                        and (use_custom_content
                             or encoding == webhook_target.ENCODING_XML)):
                    # This payload's going to be provided for XML and custom
                    # templates. We don't want to alter the keys at all.
                    raw_norm_payload = normalize_webhook_payload(
                        payload=payload, request=request)
                elif (json_norm_payload is None and not use_custom_content
                      and encoding in (webhook_target.ENCODING_JSON,
                                       webhook_target.ENCODING_FORM_DATA)):
                    # This payload's going to be provided for JSON or
                    # form-data. We want to normalize all keys to strings.
                    json_norm_payload = normalize_webhook_payload(
                        payload=payload, request=request, use_string_keys=True)
            except TypeError as e:
                logging.exception(
                    'WebHook payload passed to '
                    'dispatch_webhook_event containing invalid '
                    'data types: %s', e)

                raise ValueError(six.text_type(e))

        if use_custom_content:
            try:
                assert raw_norm_payload is not None
                body = render_custom_content(webhook_target.custom_content,
                                             raw_norm_payload)
                body = force_bytes(body)
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue
        else:
            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        assert json_norm_payload is not None
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(json_norm_payload,
                                              request=request)
                    elif encoding == webhook_target.ENCODING_XML:
                        assert raw_norm_payload is not None
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(raw_norm_payload,
                                              request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        assert json_norm_payload is not None
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload':
                            adapter.encode(json_norm_payload, request=request),
                        })
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" '
                            'for ID %s', encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                body = force_bytes(body)
                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event':
            event.encode('utf-8'),
            b'Content-Type':
            webhook_target.encoding.encode('utf-8'),
            b'Content-Length':
            len(body),
            b'User-Agent':
            ('ReviewBoard-WebHook/%s' % get_package_version()).encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)

        try:
            url = webhook_target.url
            url_parts = urlsplit(url)

            if url_parts.username or url_parts.password:
                credentials, netloc = url_parts.netloc.split('@', 1)
                url = urlunsplit((url_parts.scheme, netloc, url_parts.path,
                                  url_parts.query, url_parts.fragment))
                headers[b'Authorization'] = \
                     b'Basic %s' % b64encode(credentials.encode('utf-8'))

            urlopen(Request(url.encode('utf-8'), body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)

            if isinstance(e, HTTPError):
                logging.info('Error response from %s: %s %s\n%s',
                             webhook_target.url, e.code, e.reason, e.read())
예제 #11
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)

                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue

        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload':
                            adapter.encode(payload, request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" '
                            'for ID %s', encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event':
            event.encode('utf-8'),
            b'Content-Type':
            webhook_target.encoding.encode('utf-8'),
            b'Content-Length':
            len(body),
            b'User-Agent':
            ('ReviewBoard-WebHook/%s' % get_package_version()).encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        try:
            url = webhook_target.url
            url_parts = urlsplit(url)

            if url_parts.username or url_parts.password:
                netloc = url_parts.netloc.split('@', 1)[1]
                url = urlunsplit((url_parts.scheme, netloc, url_parts.path,
                                  url_parts.params, url_parts.query))

                password_mgr = HTTPPasswordMgrWithDefaultRealm()
                password_mgr.add_password(None, url, url_parts.username,
                                          url_parts.password)
                handler = HTTPBasicAuthHandler(password_mgr)
                opener = build_opener(handler)
            else:
                opener = build_opener()

            opener.open(Request(url.encode('utf-8'), body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)
예제 #12
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets.

    Args:
        request (django.http.HttpRequest):
            The HTTP request from the client.

        webhook_targets (list of
                         reviewboard.notifications.models.WebHookTarget):
            The list of WebHook targets containing endpoint URLs to dispatch
            to.

        event (unicode):
            The name of the event being dispatched.

        payload (dict):
            The payload data to encode for the WebHook payload.

    Raises:
        ValueError:
            There was an error with the payload format. Details are in the
            log and the exception message.
    """
    try:
        payload = normalize_webhook_payload(payload, request)
    except TypeError as e:
        logging.exception('WebHook payload passed to dispatch_webhook_event '
                          'containing invalid data types: %s',
                          e)

        raise ValueError(six.text_type(e))

    encoder = BasicAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)
                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue
        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload': adapter.encode(payload,
                                                      request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error('Unexpected WebHookTarget encoding "%s" '
                                      'for ID %s',
                                      encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event': event.encode('utf-8'),
            b'Content-Type': webhook_target.encoding.encode('utf-8'),
            b'Content-Length': len(body),
            b'User-Agent':
                ('ReviewBoard-WebHook/%s' % get_package_version())
                .encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s',
                     event, webhook_target.url)
        try:
            url = webhook_target.url
            url_parts = urlsplit(url)

            if url_parts.username or url_parts.password:
                netloc = url_parts.netloc.split('@', 1)[1]
                url = urlunsplit(
                    (url_parts.scheme, netloc, url_parts.path,
                     url_parts.params, url_parts.query))

                password_mgr = HTTPPasswordMgrWithDefaultRealm()
                password_mgr.add_password(
                    None, url, url_parts.username, url_parts.password)
                handler = HTTPBasicAuthHandler(password_mgr)
                opener = build_opener(handler)
            else:
                opener = build_opener()

            opener.open(Request(url.encode('utf-8'), body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)
예제 #13
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets.

    Args:
        request (django.http.HttpRequest):
            The HTTP request from the client.

        webhook_targets (list of
                         reviewboard.notifications.models.WebHookTarget):
            The list of WebHook targets containing endpoint URLs to dispatch
            to.

        event (unicode):
            The name of the event being dispatched.

        payload (dict):
            The payload data to encode for the WebHook payload.

    Raises:
        ValueError:
            There was an error with the payload format. Details are in the
            log and the exception message.
    """
    try:
        payload = normalize_webhook_payload(payload, request)
    except TypeError as e:
        logging.exception('WebHook payload passed to dispatch_webhook_event '
                          'containing invalid data types: %s',
                          e)

        raise ValueError(six.text_type(e))

    encoder = BasicAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)
                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue
        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload': adapter.encode(payload,
                                                      request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error('Unexpected WebHookTarget encoding "%s" '
                                      'for ID %s',
                                      encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event': event.encode('utf-8'),
            b'Content-Type': webhook_target.encoding.encode('utf-8'),
            b'Content-Length': len(body),
            b'User-Agent':
                ('ReviewBoard-WebHook/%s' % get_package_version())
                .encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s',
                     event, webhook_target.url)
        try:
            url = webhook_target.url
            url_parts = urlsplit(url)

            if url_parts.username or url_parts.password:
                netloc = url_parts.netloc.split('@', 1)[1]
                url = urlunsplit(
                    (url_parts.scheme, netloc, url_parts.path,
                     url_parts.params, url_parts.query))

                password_mgr = HTTPPasswordMgrWithDefaultRealm()
                password_mgr.add_password(
                    None, url, url_parts.username, url_parts.password)
                handler = HTTPBasicAuthHandler(password_mgr)
                opener = build_opener(handler)
            else:
                opener = build_opener()

            opener.open(Request(url.encode('utf-8'), body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)