def index(request, uri): """ Proxies render requests to graphite-web, as configured in graphite.conf """ base = CONFIG.get('graphiteweb', 'base') if request.method in ('GET', 'HEAD'): query = _inject_default_arguments(request.GET) url = urljoin(base, uri + ('?' + query) if query else '') req = Request(url) elif request.method == 'POST': data = _inject_default_arguments(request.POST) url = urljoin(base, uri) req = Request(url, data) else: return HttpResponseNotAllowed(['GET', 'POST', 'HEAD']) LOGGER.debug("proxying request to %r", url) proxy = urlopen(req) headers = proxy.info() content_type = headers.getheader('Content-Type', 'text/html') if request.method == 'HEAD': response = HttpResponse(content_type=content_type) response['Content-Length'] = headers.getheader('Content-Length', '0') else: response = HttpResponse(proxy.read(), content_type=content_type) response['X-Where-Am-I'] = request.get_full_path() return response
def get_file_http(self, url, path, revision): logging.info('Fetching file from %s' % url) try: request = URLRequest(url) if self.username: auth_string = base64.b64encode('%s:%s' % (self.username, self.password)) request.add_header('Authorization', 'Basic %s' % auth_string) return urlopen(request).read() except HTTPError as e: if e.code == 404: logging.error('404') raise FileNotFoundError(path, revision) else: msg = "HTTP error code %d when fetching file from %s: %s" % \ (e.code, url, e) logging.error(msg) raise SCMError(msg) except Exception as e: msg = "Unexpected error fetching file from %s: %s" % (url, e) logging.error(msg) raise SCMError(msg)
def _send_web_request(self, url, payload, attempts=1): """Send out a web request and retry on failure. TODO: Currently this is a blocking operation. Devising a way to send these requests without blocking would be benificial. Args: url (unicode): The URL to send the request to. payload (unicode): The JSON-encoded payload to send. attempts (int): The number of retry attempts left. """ request = Request(url) arguments = urlencode({ 'payload': payload, }) # The addition of data automatically converts request to a POST. request.add_data(arguments) while attempts: try: return urlopen(request) except URLError: attempts -= 1 logging.warning('Sending WebHook Request failed: %s ' % url)
def get_file_http(self, url, path, revision): """Return the contents of a file from an HTTP(S) URL. This is a convenience for looking up the contents of files that are referenced in diffs through an HTTP(S) request. Authentication is performed using the username and password provided (if any). Args: url (unicode): The URL to fetch the file contents from. path (unicode): The path of the file, as referenced in the diff. revision (Revision): The revision of the file, as referenced in the diff. Returns: bytes: The contents of the file. Raises: reviewboard.scmtools.errors.FileNotFoundError: The file could not be found. reviewboard.scmtools.errors.SCMError: Unexpected error in fetching the file. This may be an unexpected HTTP status code. """ logging.info('Fetching file from %s' % url) try: request = URLRequest(url) if self.username: auth_string = base64.b64encode('%s:%s' % (self.username, self.password)) request.add_header('Authorization', 'Basic %s' % auth_string) return urlopen(request).read() except HTTPError as e: if e.code == 404: logging.error('404') raise FileNotFoundError(path, revision) else: msg = "HTTP error code %d when fetching file from %s: %s" % \ (e.code, url, e) logging.error(msg) raise SCMError(msg) except Exception as e: msg = "Unexpected error fetching file from %s: %s" % (url, e) logging.error(msg) raise SCMError(msg)
def _build_request(self, url, body=None, headers={}, username=None, password=None): r = URLRequest(url, body, headers) if username is not None and password is not None: auth_key = username + ':' + password r.add_header(HTTPBasicAuthHandler.auth_header, 'Basic %s' % base64.b64encode(auth_key.encode('utf-8'))) return r
def _get_request(self, request_url, request_method, **params): """ Return a Request object that has the GET parameters attached to the url or the POST data attached to the object. """ if request_method == 'GET': if params: request_url += '&%s' % urlencode(params) request = Request(request_url) elif request_method == 'POST': request = Request(request_url, urlencode(params, doseq=1)) return request
def _build_request(self, url, body=None, headers={}, username=None, password=None): r = URLRequest(url, body, headers) if username is not None and password is not None: auth_key = username + ':' + password r.add_header( HTTPBasicAuthHandler.auth_header, 'Basic %s' % base64.b64encode(auth_key.encode('utf-8'))) return r
def index(request, uri): """ Proxies render requests to graphite-web, as configured in graphite.conf """ base = CONFIG.get('graphiteweb', 'base') if request.method in ('GET', 'HEAD'): query = _inject_default_arguments(request.GET) url = urljoin(base, uri + ('?' + query) if query else '') req = Request(url) data = None elif request.method == 'POST': data = _inject_default_arguments(request.POST).encode('utf-8') url = urljoin(base, uri) req = Request(url, data) else: return HttpResponseNotAllowed(['GET', 'POST', 'HEAD']) _logger.debug("proxying request to %r", url) try: proxy = urlopen(req) except HTTPError as error: status = error.code headers = error.hdrs output = error.fp.read() _logger.error( "%s error on graphite render request: " "%r with arguments: %r", status, url, data, ) else: status = proxy.getcode() headers = proxy.info() output = proxy.read() content_type = headers.get('Content-Type', 'text/html') if request.method == 'HEAD': response = HttpResponse(content_type=content_type, status=status) response['Content-Length'] = headers.get('Content-Length', '0') else: response = HttpResponse(output, content_type=content_type, status=status) response['X-Where-Am-I'] = request.get_full_path() return response
def _get_response(url, xml_body): """Takes and returns an ElementTree xml document.""" req = Request(url, ElementTree.tostring(xml_body, encoding='utf-8')) response = urlopen(req) ret = ElementTree.fromstring(response.read()) response.close() return ret
def send(self, address, alert, language='en'): """Send a message to Slack""" if self._is_still_backing_off_for(address.address): raise DispatcherException( "Refusing to send Slack alert until backoff period has expired" ) params = { 'text': alert.messages.get(language=language, type='sms').message, 'username': self.username, 'channel': self.channel, 'icon_emoji': self.emoji } payload = json.dumps(params) if isinstance(payload, six.text_type): payload = payload.encode("utf-8") request = Request(address.address, payload, {'Content-Type': 'application/json'}) try: urlopen(request) except HTTPError as error: if error.code == HTTP_TOO_MANY_REQUESTS: self._register_failure_for(address.address) raise DispatcherException( "Slack complained there were too many requests; need to back off" ) else: raise
def notify(self, text, fields): """Send a webhook notification to Slack. Args: text (unicode): The text to send. fields (list of dict): A list of fields to include in the notification. """ payload = { 'username': self.settings['notify_username'], 'icon_url': 'http://images.reviewboard.org/rbslack/logo.png', 'attachments': [ { 'color': '#efcc96', 'fallback': text, 'fields': fields, }, ], } channel = self.settings['channel'] if channel: payload['channel'] = channel try: urlopen(Request(self.settings['webhook_url'], json.dumps(payload))) except Exception as e: logging.error('Failed to send notification to slack.com: %s', e, exc_info=True)
def raw_metric_query(query): """Runs a query for metric information against Graphite's REST API. :param query: A search string, e.g. "nav.devices.some-gw_example_org.*" :returns: A list of matching metrics, each represented by a dict. """ base = CONFIG.get("graphiteweb", "base") url = urljoin(base, "/metrics/find") query = urlencode({'query': query}) url = "%s?%s" % (url, query) req = Request(url) try: response_data = urlopen(req).read().decode('utf-8') return json.loads(response_data) except URLError as err: raise errors.GraphiteUnreachableError( "{0} is unreachable".format(base), err) except ValueError: # response could not be decoded return [] finally: try: response.close() except NameError: pass
def get_metric_data(target, start="-5min", end="now"): """ Retrieves raw datapoints from a graphite target for a given period of time. :param target: A metric path string or a list of multiple metric paths :param start: A start time specification that Graphite will accept. :param end: An end time specification that Graphite will accept. :returns: A raw, response from Graphite. Normally a list of dicts that represent the names and datapoints of each matched target, like so:: [{'target': 'x', 'datapoints': [(value, timestamp), ...]}] """ if not target: return [] # no point in wasting time on http requests for no data base = CONFIG.get("graphiteweb", "base") url = urljoin(base, "/render/") # What does Graphite accept of formats? Lets check if the parameters are # datetime objects and try to force a format then if isinstance(start, datetime): start = start.strftime('%H:%M%Y%m%d') if isinstance(end, datetime): end = end.strftime('%H:%M%Y%m%d') query = { 'target': target, 'from': start, 'until': end, 'format': 'json', } query = urlencode(query, True) _logger.debug("get_metric_data%r", (target, start, end)) req = Request(url, data=query.encode('utf-8')) try: response = urlopen(req) json_data = json.load(codecs.getreader('utf-8')(response)) _logger.debug("get_metric_data: returning %d results", len(json_data)) return json_data except HTTPError as err: _logger.error("Got a 500 error from graphite-web when fetching %s" "with data %s", err.url, query) _logger.error("Graphite output: %s", err.fp.read()) raise errors.GraphiteUnreachableError( "{0} is unreachable".format(base), err) except URLError as err: raise errors.GraphiteUnreachableError( "{0} is unreachable".format(base), err) except ValueError: # response could not be decoded return [] finally: try: response.close() except NameError: pass
def create_idonethis_request(request_path, api_token, json_payload=None): """Create a urllib request for the I Done This API. Args: request_path (unicode): The API request path, relative to the base API URL. api_token (unicode): The user's API token for authorization. json_payload (unicode, optional): JSON payload for a POST request. If this is omitted, the request will be a GET. Returns: urllib2.Request: The I Done This API request with the provided details. """ url = '%s/%s' % (IDONETHIS_API_BASE_URL, request_path) headers = { 'Authorization': 'Token %s' % api_token, } if json_payload: headers['Content-Type'] = 'application/json' return Request(url, json_payload, headers)
def find_embed(self, url, max_width=None): # Find provider endpoint = self._get_endpoint(url) if endpoint is None: raise EmbedNotFoundException # Work out params params = self.options.copy() params['url'] = url params['format'] = 'json' if max_width: params['maxwidth'] = max_width # Perform request request = Request(endpoint + '?' + urlencode(params)) request.add_header('User-agent', 'Mozilla/5.0') try: r = urllib_request.urlopen(request) except URLError: raise EmbedNotFoundException oembed = json.loads(r.read().decode('utf-8')) # Convert photos into HTML if oembed['type'] == 'photo': html = '<img src="%s" />' % (oembed['url'], ) else: html = oembed.get('html') # Return embed as a dict return { 'title': oembed['title'] if 'title' in oembed else '', 'author_name': oembed['author_name'] if 'author_name' in oembed else '', 'provider_name': oembed['provider_name'] if 'provider_name' in oembed else '', 'type': oembed['type'], 'thumbnail_url': oembed.get('thumbnail_url'), 'width': oembed.get('width'), 'height': oembed.get('height'), 'html': html, }
def __init__(self, url, body='', headers=None, method='GET'): """Initialize the URLRequest. Args: url (unicode): The URL to make the request against. body (unicode or bytes): The content of the request. headers (dict, optional): Additional headers to attach to the request. method (unicode, optional): The request method. If not provided, it defaults to a ``GET`` request. """ BaseURLRequest.__init__(self, url, body, headers or {}) self.method = method
def __init__(self, url, body='', headers=None, method='GET'): """Initialize the URLRequest. Args: url (unicode): The URL to make the request against. body (unicode or bytes): The content of the request. headers (dict, optional): Additional headers to attach to the request. method (unicode, optional): The request method. If not provided, it defaults to a ``GET`` request. """ # Request is an old-style class and therefore we cannot use super(). BaseURLRequest.__init__(self, url, body, headers or {}) self.method = method
def send(self, address, alert, language='en'): """Send a message to Slack""" params = { 'text': alert.messages.get(language=language, type='sms').message, 'username': self.username, 'channel': self.channel, 'icon_emoji': self.emoji } request = Request(address.address, json.dumps(params), {'Content-Type': 'application/json'}) urlopen(request)
def send(self, address, alert, language='en'): """Send a message to Slack""" params = { 'text': alert.messages.get(language=language, type='sms').message, 'username': self.username, 'channel': self.channel, 'icon_emoji': self.emoji } payload = json.dumps(params) if isinstance(payload, six.text_type): payload = payload.encode("utf-8") request = Request(address.address, payload, {'Content-Type': 'application/json'}) urlopen(request)
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))
def oembed(url, max_width=None): # Find provider provider = get_oembed_provider(url) if provider is None: raise EmbedNotFoundException # Work out params params = {"url": url, "format": "json"} if max_width: params["maxwidth"] = max_width # Perform request request = Request(provider + "?" + urlencode(params)) request.add_header("User-agent", "Mozilla/5.0") try: r = urllib_request.urlopen(request) except URLError: raise EmbedNotFoundException oembed = json.loads(r.read().decode("utf-8")) # Convert photos into HTML if oembed["type"] == "photo": html = '<img src="%s" />' % (oembed["url"],) else: html = oembed.get("html") # Return embed as a dict return { "title": oembed["title"] if "title" in oembed else "", "author_name": oembed["author_name"] if "author_name" in oembed else "", "provider_name": oembed["provider_name"] if "provider_name" in oembed else "", "type": oembed["type"], "thumbnail_url": oembed.get("thumbnail_url"), "width": oembed.get("width"), "height": oembed.get("height"), "html": html, }
def _visit(self, url): if self._is_seen(url): return req = Request(_quote_url(url), headers={'Accept': 'text/html'}) resp = urlopen(req, timeout=TIMEOUT) content_type = resp.info()['Content-type'] if 'html' in content_type.lower(): content = resp.read() self._queue_links_from(content, url) else: content = None page = Page(url, resp.getcode(), content_type, content) self._add_seen(page) return page
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))
def check_recaptcha(request, context, push_messages): is_valid = True if not app_settings.USE_GOOGLE_RECAPTCHA: return is_valid ''' Begin reCAPTCHA validation ''' recaptcha_response = request.POST.get('g-recaptcha-response') url = 'https://www.google.com/recaptcha/api/siteverify' values = { 'secret': app_settings.GOOGLE_RECAPTCHA_SECRET_KEY, 'response': recaptcha_response } data = urlencode(values).encode('utf-8') req = Request(url, data) response = urlopen(req) result = json.load(response) ''' End reCAPTCHA validation ''' if not result['success']: is_valid = False error_message = _('Invalid reCAPTCHA.') if push_messages: messages.error(request, error_message) return is_valid
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())
def notify(self, title, title_link, fallback_text, local_site, review_request, event_name=None, fields={}, pre_text=None, body=None, color=None, thumb_url=None, image_url=None): """Send a webhook notification to Slack. This will post the given message to any Slacks/channels configured to receive it. Args: title (unicode): The title for the message. title_link (unicode): The link for the title of the message. fallback_text (unicode): The non-rich fallback text to display in the chat, for use in IRC and other services. fields (dict): The fields comprising the rich message to display in chat. local_site (reviewboard.site.models.LocalSite): The Local Site for the review request or review emitting the message. Only integration configurations matching this Local Site will be processed. review_request (reviewboard.reviews.models.ReviewRequest): The review request the notification is bound to. event_name (unicode): The name of the event triggering this notification. pre_text (unicode, optional): Text to display before the rest of the message. body (unicode, optional): The body of the message. color (unicode, optional): A Slack color string or RGB hex value for the message. thumb_url (unicode, optional): URL of an image to show on the side of the message. image_url (unicode, optional): URL of an image to show in the message. """ if not color: color = self.DEFAULT_COLOR attachment_payload = { 'color': color or self.DEFAULT_COLOR, 'fallback': fallback_text, 'title': title, 'title_link': title_link, 'text': body, 'pretext': pre_text, } if fields: attachment_payload['fields'] = fields if thumb_url: attachment_payload['thumb_url'] = thumb_url if image_url: attachment_payload['image_url'] = image_url common_payload = { 'icon_url': self.LOGO_URL, 'attachments': [attachment_payload], } # Send a notification to any configured Slack channels. for config in self.get_configs(local_site): if not config.match_conditions(form_cls=self.config_form_cls, review_request=review_request): continue payload = dict({ 'username': config.get('notify_username'), }, **common_payload) channel = config.get('channel') if channel: payload['channel'] = channel webhook_url = config.get('webhook_url') logging.debug('Sending Slack notification for event "%s", ' 'review_request ID %d to channel "%s", ' 'webhook URL %s', event_name, review_request.pk, channel, webhook_url) try: urlopen(Request(webhook_url, json.dumps(payload))) except Exception as e: logging.error('Failed to send notification to Slack: %s', e, exc_info=True)
def get_file_http(self, url, path, revision, mime_type=None): """Return the contents of a file from an HTTP(S) URL. This is a convenience for looking up the contents of files that are referenced in diffs through an HTTP(S) request. Authentication is performed using the username and password provided (if any). Args: url (unicode): The URL to fetch the file contents from. path (unicode): The path of the file, as referenced in the diff. revision (Revision): The revision of the file, as referenced in the diff. mime_type (unicode): The expected content type of the file. If not specified, this will default to accept everything. Returns: bytes: The contents of the file if content type matched, otherwise None. Raises: reviewboard.scmtools.errors.FileNotFoundError: The file could not be found. reviewboard.scmtools.errors.SCMError: Unexpected error in fetching the file. This may be an unexpected HTTP status code. """ logging.info('Fetching file from %s' % url) try: request = URLRequest(url) if self.username: credentials = '%s:%s' % (self.username, self.password) auth_string = \ force_text(base64.b64encode(credentials.encode('utf-8'))) request.add_header(force_str('Authorization'), force_str('Basic %s' % auth_string)) response = urlopen(request) if (mime_type is None or response.info()['Content-Type'] == mime_type): return force_bytes(response.read()) return None except HTTPError as e: if e.code == 404: raise FileNotFoundError(path, revision) else: msg = "HTTP error code %d when fetching file from %s: %s" % \ (e.code, url, e) logging.error(msg) raise SCMError(msg) except Exception as e: msg = "Unexpected error fetching file from %s: %s" % (url, e) logging.error(msg) raise SCMError(msg)
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)
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)
def __init__(self, url, body='', headers={}, method='GET'): BaseURLRequest.__init__(self, url, body, headers) self.method = method
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)