I want to integrate a webhook in my Django application that supports only HTTPS requests. I want to test in my local machine so here I am using ngrok to make my localhost HTTPS i.e. https://c71e-2415-201-138f-ad9d-3005-825a-23c9-c788.ngrok.io/
and my local URL is
http://localhost:8000
I created a URL in my application named call_webhook_internally and related views.py function is
#csrf_exempt
def call_webhook_internally(request):
try:
ng_rok_url = https://c71e-2415-201-138f-ad9d-3005-825a-23c9-c788.ngrok.io/
url = ng_rok_url + "call_webhook/"
headers = {
"Timestamp": str(datetime.utcnow()),
}
payload = json.loads(request.body)
response = requests.request("POST", url, headers=headers, data=payload)
return HttpResponse("success")
except Exception as e:
return HttpResponse("Error")
the upper function is calling and when my request is called to call webhook using python it blocks the main thread and postman goes in an infinite loop, in ngrok terminal I am getting that request is received but in function, my print statement is not printing. I want to call my internal webhook in my existing app views.py function is call_webhook i.e.
def call_webhook_internally(request):
print(request)
return HttpResponse("webhook_called")
After 10-15 minutes getting error i.e.
webhook failed. Error HTTPSConnectionPool(host='c10e-2105-215-138f-ad9d-3005-825a-23c9-c788.ngrok.io',
port=443): Max retries exceeded with url: /call_webhook/ (Caused by SSLError(SSLError("read error:
Error([('SSL routines', 'ssl3_get_record', 'decryption failed or bad record mac')],)",),))
so pls suggest to me some way to do this is am I doing something wrong?
Thanks
Related
Set-up
I use the following Python code to log-in to a Adobe Commerce test environment via the Rest API,
import requests
host = 'https://b2b.test.163.com.pl'
store_code = 'PL_B2B_PL'
# Set the API endpoint for getting a customer token
endpoint = host + "/rest/" + store_code + "/V1/integration/customer/token"
# Set the credentials for the login request
credentials = {
"username": "myusername",
"password": "mypassword"
}
# Send a POST request to the login endpoint with the credentials
response = requests.post(endpoint, json=credentials, headers={"Content-Type": "application/json"})
# Check the response status code to see if the request was successful
if response.status_code == 200:
# Get the customer token from the response
customer_token = response.text
# Use the customer token in the Authorization header of subsequent API requests
headers = {
"Authorization": f"Bearer {customer_token}"
}
else:
# Handle the error response if the request was unsuccessful
print(f"Request failed: {response.text}")
Issue
Running the above code yields the following error,
ConnectionError: HTTPSConnectionPool(host='b2b.test.163.com.pl', port=443): Max retries exceeded with url: /rest/PL_B2B_PL/V1/integration/customer/token (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f7b718ed880>: Failed to establish a new connection: [Errno 60] Operation timed out'))
Question
How do I correctly connect to the test environment?
I think this code should've worked, but clearly I am wrong.
I have a Python script that I would like to run at a set interval using Google Cloud Functions and Google Cloud Scheduler. The script works fine when tested locally, but when I test it in the Google Cloud Functions panel I'm getting a network connection error message for some reason? Do I need to do something special to get the requests library to work when the Python script is a Google Cloud Function?
Python script:
import datetime
from config import config_vars
import requests
APIKEY = config_vars['APIKEY']
NOW = datetime.datetime.now()
LAST = NOW - datetime.timedelta(seconds=config_vars['UPDATE_INTERVAL'])
def getOrders(nextPage = None):
url = "https://api.squarespace.com/1.0/commerce/orders"
if nextPage is None:
params = {
'modifiedAfter': f"{LAST.isoformat()}Z",
'modifiedBefore': f"{NOW.isoformat()}Z"
}
else:
params = { 'cursor': nextPage }
headers = { "Authorization": f"Bearer {SAPIKEY}" }
r = requests.get(url, params=params, headers=headers)
if not r.ok:
logging.error(f"Unable to get orders. Respoonse: {r.text}")
return []
res = r.json()
pagination = res['pagination']
if pagination['hasNextPage']: return res['result'] + getOrders(pagination['nextPageCursor'])
else: return res['result']
def main(data = None, context = None):
"""Triggered from a message on a Cloud Pub/Sub topic.
Args:
data (dict): Event payload.
context (google.cloud.functions.Context): Metadata for the event.
"""
orders = getOrders()
for order in orders:
# do something with each order
pass
if __name__ == '__main__': main()
Error message:
HTTPSConnectionPool(host='api.squarespace.com', port=443): Max retries exceeded with url: /1.0/commerce/orders?modifiedAfter=2020-02-09T23%3A01%3A44.372818Z&modifiedBefore=2020-02-09T23%3A01%3A45.372818Z (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7eedecb76850>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'))
You need to enable billing for your project. You won't be able to make outbound requests to any URL until it is enabled.
The "Billing enabled" answer worked for me initially - However, I was mystified by a later occurrence of this same message on a Function / project where billing was definitely enabled, and in fact I could make some outbound requests, but one in particular was failing. It turned out to be a \n at the end of the URL string I had been sending to the function as a parameter. In my particular case, since I was using PHP to generate the string, a simple trim() call removed the cruft and the function continued to work as expected. Posting just in case it helps someone else, as this had me scratching my head for a bit.
I am trying to use Twilio in pythonanywhere and I know I need a proxy to make it work. My code looks like this:
class ProxiedTwilioHttpClient(HttpClient):
"""
General purpose HTTP Client for interacting with the Twilio API
"""
def request(self, method, url, params=None, data=None, headers=None, auth=None, timeout=None,
allow_redirects=False):
session = Session()
session.verify = get_cert_file()
session.proxies = {
"https" : "https://52.14.161.178:3128"
}
request = Request(method.upper(), url, params=params, data=data, headers=headers, auth=auth)
prepped_request = session.prepare_request(request)
response = session.send(
prepped_request,
allow_redirects=allow_redirects,
timeout=timeout,
)
return Response(int(response.status_code), response.content.decode('utf-8'))
def send_sms(phone, content):
client = Client(api_key, api_secret, account_sid, http_client=ProxiedTwilioHttpClient())
message = client.messages.create(
to=phone,
from_="+19999999999", #of course I use the correct one
body=content)
return(message.sid)
But then it returns the following error:
.virtualenvs/sms/local/lib/python2.7/site-packages/requests/adapters.py",
line 502, in send raise ProxyError(e, request=request) requests.exceptions.ProxyError:
HTTPSConnectionPool(host='api.twilio.com', port=443):
Max retries exceeded with url: /2010-04-01/Accounts/XXXXXXXXX/Messages.json (Caused by ProxyError('Cannot connect to proxy.',
NewConnectionError('<urllib3.connection.Verif iedHTTPSConnection object at 0x7fa41a55e090>:
Failed to establish a new connection: [Errno 111] Connection refused',)))
I am using the following answer that seems to work for others: https://stackoverflow.com/a/43608637/7298530
How can I solve it?
You're specifying that your code should use a proxy at https://52.14.161.178:3128. That won't work on PythonAnywhere, you need to use the proxy that the service provides. To find out what address to use for that, start a Bash console and run
echo $http_proxy
[2018 edit] We now have a specific page on getting twilio to work with the pythonanywhere proxy
There is two tries to get response from "working" django server. Working version is hardcoded and not working while unittesting
# working
# a = requests.post('http://localhost:8000/ImportKeys/',
# data=json.dumps({'user_id': key_obj.email,
#'key': self.restore_pubkey(key_obj.fingerprint)}))
# not working
a = requests.post('http://' + request.get_host() + reverse('import_keys'),data=json.dumps({'user_id': key_obj.email,'key': self.restore_pubkey(key_obj.fingerprint)}))
On that version, that I whant to starts working, I've got this(end stacktrace):
File "/home/PycharmProjects/lib/python3.4/site-packages/requests/sessions.py", line 576, in send
r = adapter.send(request, **kwargs)
File "/home/PycharmProjects/lib/python3.4/site-packages/requests/adapters.py", line 437, in send
raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='testserver', port=80): Max retries exceeded with url: /ImportKeys/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno -2] Name or service not known',))
And yes, I see that it's trying connect to 80 port, and this is bad.
To test your views in the TestCase classes, use django.test.Client, which is designed specifically for that purpose. If you inherit your test cases from django.test.TestCase, it's already available via the self.client attribute.
class YourTestCase(TestCase):
def test_import_keys_posting(self):
data = {
'user_id': key_obj.email,
'key': self.restore_pubkey(key_obj.fingerprint)
}
response = self.client.post(reverse('import_keys'), data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'result': 'ok'})
And if you use Django Rest Framework, consider using its wonderful APIClient, which simplifies API testing even more.
If you need to send requests to the server during tests (in that case this will probably not be from the test code itself but from some mock or from JS code):
Extend LiveServerTestCase instead TestCase. This will launch an actual server during tests.
If you are using request.build_absolute_uri() in your regular code which is being tested, you need to change the test code to update the HTTP request headers accordingly like this:
checkout_url = '{}{}'.format(self.live_server_url, reverse('checkout', kwargs={'pk': article.id}))
parsed_url = parse.urlparse(self.live_server_url)
# add the info on host and port to the http header to make subsequent
# request.build_absolute_uri() calls work
response = self.client.get(checkout_url, SERVER_NAME=parsed_url.hostname, SERVER_PORT=parsed_url.port)
I'm trying to use the Python requests library to send an android .apk file to a API service. I've successfully used requests and this file type to submit to another service but I keep getting a:
ConnectionError(MaxRetryError("HTTPSConnectionPool(host='REDACTED', port=443): Max retries exceeded with url: /upload/app (Caused by : [WinError 10054] An existing connection was forcibly closed by the remote host)",),)
This is the code responsible:
url = "https://website"
files = {'file': open(app, 'rb')}
headers = {'user':'value', 'pass':'value'}
try:
response = requests.post(url, files=files, headers=headers)
jsonResponse = json.loads(response.text)
if 'error' in jsonResponse:
logger.error(jsonResponse['error'])
except Exception as e:
logger.error("Exception when trying to upload app to host")
The response line is throwing the above mentioned exception. I've used these exact same parameters using the Chrome Postman extension to replicate the POST request and it works perfectly. I've used the exact same format of file to upload to another RESTful service as well. The only difference between this request and the one that works is that this one has custom headers attached in order to verify the POST. The API doesn't stipulate this as authentication in the sense of needing to be encoded and the examples both in HTTP and cURL define these values as headers or -H.
Any help would be most appreciated!
So this was indeed a certificates issue. In my case I was able to stay internal to my company and connect to another URL, but the requests library, which is quite amazing, has information on certs at: http://docs.python-requests.org/en/latest/user/advanced/?highlight=certs
For all intents and purposes this is answered but perhaps it will be useful to someone in posterity.