I'm trying to set up a simple reverse proxy with Falcon in Python.
I have:
import falcon
import requests
class ReverseProxyResource:
def on_get(self, req, resp, text=None):
print("GET")
if(text):
destination = "[destination_url]/" + text
else:
destination = "[destination_url]"
result = requests.get(destination)
resp.body = result.text
resp.status = result.status_code
def on_post(self, req, resp, text=None):
print("POST")
if(text):
destination = "[destination_url]/" + text
else:
destination = "[destination_url]"
result = requests.post(destination, data=req.bounded_stream.read())
resp.body = result.text
resp.status = result.status_code
proxy_api = application = falcon.API()
proxy_api.req_options.auto_parse_form_urlencoded = True
proxy_api.add_route('/{text}', ReverseProxyResource())
proxy_api.add_route('/', ReverseProxyResource())
Get requests to the proxy are returned correctly.
However, Post requests are only returned a 404 error from the api. The "POST" print statement is not shown, indicating on_post isn't called at all. (The post requests only included Header Content-Type: application/json and a simple JSON body, which work correctly when called directly against the destination url)
EDIT: Interestingly enough, if I change GET call in postman to POST (ie: no body, headers, or anything else added) on_post() is called when I hit the endpoint. So it seems like an issue where post requests that contain a body are being automtically 404'ed without calling on_post()
Try adding user agent and content type before making the post call
headers = {"Content-Type": "text/plain", "User-Agent": "PostmanRuntime/7.30.0"}
result = requests.post(url = destination, data=req.bounded_stream.read(), headers=headers)
below code works for me
import falcon
import requests
class ReverseProxyResource:
def on_get(self, req, resp, text=None):
print("GET")
if(text):
destination = "https://cat-fact.herokuapp.com/" + text
else:
destination = "https://cat-fact.herokuapp.com/facts/"
result = requests.get(destination)
resp.body = result.text
resp.status = result.status_code
def on_post(self, req, resp, text=None):
print("POST")
if(text):
destination = "https://dummy.restapiexample.com/api/v1/"+ text
else:
destination = "https://dummy.restapiexample.com/api/v1/create"
headers = {"Content-Type": "text/plain", "User-Agent": "PostmanRuntime/7.30.0"}
result = requests.post(url = destination, data=req.bounded_stream.read(), headers=headers)
resp.text = result.text
resp.status = result.status_code
proxy_api = application = falcon.App()
proxy_api.req_options.auto_parse_form_urlencoded = True
proxy_api.add_route('/{text}', ReverseProxyResource())
proxy_api.add_route('/', ReverseProxyResource())
Related
How do I create a request to rentry.co in order to create pastes?
I've tried to solve this in Python but I get the following response:
403 reason: Forbidden ...
I tried changing the URL and adding my cookie.
My code looks currently as follows.
import requests
text = "Hello World!"
data = {"text":text}
r = requests.post("https://rentry.co/api", data=data)
print(f"status code: {r.status_code}")
print(f"reason: {r.reason}") ```
try this
#!/usr/bin/env python3
import http.cookiejar
import sys
import urllib.parse
import urllib.request
from http.cookies import SimpleCookie
from json import loads as json_loads
_headers = {"Referer": 'https://rentry.co'}
class UrllibClient:
"""Simple HTTP Session Client, keeps cookies."""
def __init__(self):
self.cookie_jar = http.cookiejar.CookieJar()
self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookie_jar))
urllib.request.install_opener(self.opener)
def get(self, url, headers={}):
request = urllib.request.Request(url, headers=headers)
return self._request(request)
def post(self, url, data=None, headers={}):
postdata = urllib.parse.urlencode(data).encode()
request = urllib.request.Request(url, postdata, headers)
return self._request(request)
def _request(self, request):
response = self.opener.open(request)
response.status_code = response.getcode()
response.data = response.read().decode('utf-8')
return response
def new(url, edit_code, text):
client, cookie = UrllibClient(), SimpleCookie()
cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie'])
csrftoken = cookie['csrftoken'].value
payload = {
'csrfmiddlewaretoken': csrftoken,
'url': url,
'edit_code': edit_code,
'text': text
}
return json_loads(client.post('https://rentry.co/api/new', payload, headers=_headers).data)
def get_rentry_link(text):
url, edit_code = '', ''
response = new(url, edit_code, text)
if response['status'] != '200':
print('error: {}'.format(response['content']))
try:
for i in response['errors'].split('.'):
i and print(i)
sys.exit(1)
except:
sys.exit(1)
else:
pastebin_link = response['url']
print('Url: {}\nEdit code: {}'.format(response['url'], response['edit_code']))
return pastebin_link
if __name__ == '__main__':
link_list = ['https://stackoverflow.com/', 'https://www.youtube.com/', 'https://www.google.com/']
pastebin_link = get_rentry_link('\n'.join(map(str, link_list)))
I am requesting a user image from an api but the image only shows up if I'm using the chrome extension ModHeader which takes the authorization header and url pattern. In my code I am passing the header info so I'm not sure why it doesn't display.
#app.route('/cardholder/<name>', methods=['GET', 'POST'])
def cardholder(name):
response = requests.get("https://commandcentre-api-us.security.gallagher.cloud/api/cardholders", verify=False, headers=Headers)
r = json.loads(response.text)
data = {}
for x in r['results']:
data[x["firstName"]] = x["id"]
chid = data[''+name+'']
url = ("https://commandcentre-api-us.security.gallagher.cloud/api/cardholders/" + chid)
ch = requests.get(url, verify=False, headers=Headers)
chl = json.loads(ch.text)
try:
chimage = chl["#DL"]["href"]
except:
chimage = "../static/img/noimg.png"
return render_template('cardholder.html', name=name, chid=chid, chl=chl, chimage=chimage)
[data image][1]I want to send email to user after this function calling so in first function i m creating customer on razor pay and after fetching customer id i m passing to other function and on.
so in this function get_virtual_account() i am getting all the response from API provided by razor-pay and i need to send that response to user who created the account so how can i do that i am not able to send this response in email.
def create_razor_customer(data):
logger.info("Inside create_razor_customer")
headers = {'Content-Type': 'application/json',}
data=json.dumps(data)
response = requests.post(settings.API_RAZORPAY+'/customers', headers=headers, data=data, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
logger.info(json.loads(response.content))
json_response = response.json()
customer_id = json_response['id']
logger.info(customer_id)
get_razor_customer(customer_id)
return response
def get_razor_customer(customer_id):
logger.info("Inside get_razor_customer")
headers = {'Content-Type': 'application/json',}
response = requests.get(settings.API_RAZORPAY+'/customers/'+customer_id, headers=headers, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
logger.info(json.loads(response.content))
create_razor_virtual_account(customer_id)
return response
def create_razor_virtual_account(customer_id):
logger.info("Inside create_razor_virtual_account")
headers = {'Content-Type': 'application/json',}
data = {"receivers": {"types": ["bank_account"]},"description": "razorpay","customer_id": customer_id,"close_by": 1761615838,"notes": {"reference_key": "reference_value"}}
data=json.dumps(data)
response = requests.post(settings.API_RAZORPAY+'/virtual_accounts', headers=headers, data=data, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
json_response = response.json()
virtual_id = json_response['id']
logger.info(virtual_id)
logger.info(json_response)
return response
def get_virtual_account(virtual_id):
logger.info("Inside get_virtual_account")
logger.info(virtual_id)
headers = {'Content-Type': 'application/json',}
response = requests.get(settings.API_RAZORPAY+'/virtual_accounts/'+virtual_id,headers=headers, auth=(settings.API_RAZORPAY_KEY, settings.API_RAZORPAY_SECRET))
json_response = response.json()
logger.info(json_response)
send_account_details()
return response
def send_account_details():
logger.info('Inside send_account_details')
send_mail('Account Details', 'Details for Razorpay account', settings.EMAIL_HOST_USER, ['abhishek#byond.travel',])
logger.info("sent")
return "sent"
[1]: https://i.stack.imgur.com/proIH.png
Suppose your response JSON is
res_data={"duration":1201,"number":6,"result":"FAILURE","url":"http://localhost:8080/job/git_checkout/6/"}
Then you need to pass this into send_account_details function.
def send_account_details(res_data):
import json
logger.info('Inside send_account_details')
body = "JSON Response is : " + json.dumps(res_data) + "\n\n" + "Details for Razorpay account"
send_mail('Account Details', body, settings.EMAIL_HOST_USER, ['abhishek#byond.travel',])
logger.info("sent")
return "sent"
Using json.dumps(res_data) you can add your JSON to body of email.
i make my requests class, the code is as below:
class RunMethod:
def post_main(self, url, data, header=None):
res = None
if header != None:
res = requests.post(url=url, data=data, headers=header)
else:
res = requests.post(url=url, data=data)
return res.json()
def get_main(self, url, data=None,header=None):
res = None
if header != None:
res = requests.get(url=url,data=data,headers=header,verify=False)
else:
res = requests.get(url=url,data=data,verify=False)
return res.json()
def run_main(self, method,url,data=None,headers=None):
res = None
if method == 'get':
res = self.get_main(url, data, headers)
else:
res = self.post_main(url, data, headers)
and i capture an api from charles and test it in postman, it returns 200. i export python code from postman and it is like this:
import requests
url = "https://stargate.ar.elenet.me/minimart.service/intelligent/invoke"
querystring = {"traceId": "1000000294010",
"shelfCode": "lu8ssMgCpgq00FDYdpX76Q..", "tracedAt": "1545641563164"}
payload = ""
headers = {
'X-STARGATE-ACCESS-TOKEN': "d7594351-0663-43a8-ad55-180c8b29db82",
'Cookie': "SID=NTVMAu8FKskyj06ln8J9uhS45fgcRNk1V3jQ; USERID=2228440841",
'Authorization': "ElemeAPI token",
'cache-control': "no-cache"
}
response = requests.request(
"GET", url, data=payload, headers=headers, params=querystring)
print(response.text)
it works, and i changed my class, put the data like this:
url = "https://stargate.ar.elenet.me/minimart.service/intelligent/invoke?traceId=1000000294010&shelfCode=lu8ssMgCpgq00FDYdpX76Q..&tracedAt=1545641563164"
headers = {
'X-STARGATE-ACCESS-TOKEN': "d7594351-0663-43a8-ad55-180c8b29db82",
'Cookie': "SID=NTVMAu8FKskyj06ln8J9uhS45fgcRNk1V3jQ; USERID=2228440841",
'Authorization': "ElemeAPI token",
'cache-control': "no-cache",
'Content-Type':'application/json'
}
exam = RunMethod()
res = exam.run_main('get', url, headers)
i just put querystring into url, but it returns 401.
i don't know where it's wrong. could anybody help me? thanks a lot!
Update your code in get_main method as per below code.
res = requests.get(url=url,params=data,headers=header,verify=False)
Here we are calling instantiated RunMethod class and calling run_main method.
exam = RunMethod()
res = exam.run_main('get', url, querystring, headers)
The run_main accepts 4 arguments, exam.run_main('get', url, headers) provides only 3 of them (method='get', url=url, data=headers, headers=None).
I would recommend to use named arguments when skipping some of the optional ones:
exam.run_main('get', url, headers=headers)
I am following this tutorial for batching http requests with ASP.NET 4.5. I have the sample working, and now I need to write a client application in Python.
This code creates, and sends a batch request to the web api:
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();
//Create a request to query for customers
HttpRequestMessage queryCustomersRequest = new HttpRequestMessage(HttpMethod.Get, serviceUrl + "/Customers");
//Create a message to add a customer
HttpRequestMessage addCustomerRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "/Customers");
addCustomerRequest.Content = new ObjectContent<Customer>(addedCustomer, formatter);
//Create a message to update a customer
HttpRequestMessage updateCustomerRequest = new HttpRequestMessage(HttpMethod.Put, string.Format(serviceUrl + "/Customers/{0}", updatedCustomer.Id));
updateCustomerRequest.Content = new ObjectContent<Customer>(updatedCustomer, formatter);
//Create a message to remove a customer.
HttpRequestMessage removeCustomerRequest = new HttpRequestMessage(HttpMethod.Delete, string.Format(serviceUrl + "/Customers/{0}", removedCustomer.Id));
//Create the different parts of the multipart content
HttpMessageContent queryContent = new HttpMessageContent(queryCustomersRequest);
HttpMessageContent addCustomerContent = new HttpMessageContent(addCustomerRequest);
HttpMessageContent updateCustomerContent = new HttpMessageContent(updateCustomerRequest);
HttpMessageContent removeCustomerContent = new HttpMessageContent(removeCustomerRequest);
//Create the multipart/mixed message content
MultipartContent content = new MultipartContent("mixed", "batch_" + Guid.NewGuid().ToString());
content.Add(queryContent);
content.Add(addCustomerContent);
content.Add(updateCustomerContent);
content.Add(removeCustomerContent);
//Create the request to the batch service
HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl + "/batch");
//Associate the content with the message
batchRequest.Content = content;
//Send the message
HttpResponseMessage batchResponse = await client.SendAsync(batchRequest);
This is my attempt in Python but it doesn't work:
def build_request( url, type, headers, data = {}):
#Build appropriate request
if type == "get":
request = Request('GET', url, headers=headers)
elif type == "post":
request = Request('POST', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
elif type == "delete":
request = Request('DELETE', url, headers = {'Content-Type':'application/json'})
elif type == "put":
request = Request('PUT', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
elif type == "patch":
request = Request('PATCH', url, data = json.dumps(data), headers = {'Content-Type':'application/json'})
prepared_request = request.prepare()
return prepared_request
#Get customers
get_customers = build_request( url + "/Customers", "get", headers)
#Add a customer
add_customer = build_request( url + "/Customers", "post", data=added_customer, headers=headers)
#update a customer
update_customer = build_request( url + "/Customers/{0}".format(updated_customer["Id"]), "put", data=updated_customer, headers=headers)
#Remove a customer
remove_customer = build_request( url + "/Customers/{0}".format(removed_customer["Id"]), "delete", headers=headers)
request_list = [get_customers,add_customer,update_customer, remove_customer]
batch_request = requests.Request('POST',url + "/batch", data=request_list)
s = Session()
batch_request.prepare()
resp = s.send(batch_request)
The request should look like this:
POST http://localhost:12345/api/batch HTTP/1.1
Content-Type: multipart/mixed; boundary="batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e"
Host: localhost:12345
Content-Length: 857
Expect: 100-continue
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
GET /api/WebCustomers HTTP/1.1
Host: localhost:13245
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
POST /api/WebCustomers HTTP/1.1
Host: localhost:13245
Content-Type: application/json; charset=utf-8
{"Id":129,"Name":"Name4752cbf0-e365-43c3-aa8d-1bbc8429dbf8"}
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
PUT /api/WebCustomers/1 HTTP/1.1
Host: localhost:13245
Content-Type: application/json; charset=utf-8
{"Id":1,"Name":"Peter"}
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e
Content-Type: application/http; msgtype=request
DELETE /api/WebCustomers/2 HTTP/1.1
Host: localhost:13245
--batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e--
I've managed to translate the first part of the C# sample into Python using the following code:
class BatchRequest:
'Class for sending several HTTP requests in a single request'
def __init__(self, url, uuid):
self.batch_url = batch_url
self.uuid = uuid
self.data = ""
self.headers = {"Content-Type" : "multipart/mixed; boundary=\"batch_{0}\"".format(uuid)}
#Build sub-request depending on Method and Data supplied
def add_request(self, method, url, data={}):
if method == "GET":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\n\r\n\r\n".format(uuid, method, url)
#If no data, have alternative option
elif method == "POST" or method == "PUT":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\nContent-Type: application/json; charset=utf-8\r\n\r\n{3}\r\n".format(uuid, method, url, json.dumps(data))
elif method == "DELETE":
self.data += "--batch_{0}\r\nContent-Type: application/http; msgtype=request\r\n\r\n{1} {2} HTTP/1.1\r\nHost: localhost:65200\r\n\r\n\r\n".format(uuid, method, url)
def execute_request(self):
#Add the "closing" boundary
self.data += "--batch_{0}--\r\n".format(uuid)
result = requests.post(self.batch_url, data=self.data, headers=self.headers)
return result
if __name__ == '__main__':
url = "http://localhost:65200/api"
batch_url = "{0}/batch".format(url)
uuid = uuid.uuid4()
br = BatchRequest("http://localhost:65200/api", uuid)
#Get customers
br.add_request("GET", "/api/Customers")
#Create a new customer
new_customer = {"Id" : 10, "Name" : "Name 10"};
br.add_request("POST", "/api/Customers", new_customer)
#Update the name of the first customer in the list
update_customer = customer_list[0]
update_customer["Name"] = "Peter"
br.add_request("PUT", "/api/Customers/{0}".format(update_customer["Id"]), update_customer)
#Remove the second customer in the list
br.add_request("DELETE", "/api/Customers/{0}".format(customer_list[1]["Id"]))
result = br.execute_request()
All that remains is to figure out how to parse the response(s) from the server.