how to create raw http responses in flask? - python

I am working on a software that has a TCP Server that replies to requests done in a proprietary protocol.
Obviously the implementation relies on a socket listening on a fixed port and on analyzing and managing raw request and response.
I should add to this service the possibility to manage http requests.
I started using flask with the idea to let it manage templates rendering and responses creation, but I am a bit struck on the second part:
Righ now I managed to make this work with something like this:
with open(template_file) as f:
template = f.read()
app = flask.Flask('my app') # create a context to render Response
with app.app_context():
context = {'title': 'mytitle',
'other_info':'.....',}
rendered = flask.render_template_string(template, **context)
response = flask.make_response(rendered)
answer = f'''HTTP/1.0 200 OK\nContent-Type: text/html\n\n {rendered} \n\n'''
sock.sendall(answer.encode())
sock.close()
In this case make_response creates a Response instance where you can get the rendered html code but going from Response to the raw http is my problem.
To solve this i have added manually a header, but I think that there is a better way in flask to do this but can't figure out.
To make the question more general: how can coexist flask web application with others? Where is the point on which I have to take control of the process?

Related

How can one intercept an HTTP request, stop the request, and redirect the user to another site?

I am working on a url redirector application, in Python. The idea of this application is simple: when a user performs an http request to a certain domain (e.g. to https://google.com), the app stops the request and performs another http request (e.g. to https://github.com), thereby redirecting the user to the second page.
Unfortunately, I have looked through SO and I haven't found any question that addresses this issue directly:
Intercept and filter HTTP request doesn't redirect
Sending a HTTP request using python doesn't either
Admittedly, I only have some fabricated pseudocode to demonstrate what I wish to do, but it may prove useful:
import requests
original_site = "https://google.com"
redirect_site = "https://github.com"
def redirect_request():
if requests.get(original_site) == True:
requests.kill(request.original_site.id)
requests.get(redirect_site)
I greatly appreciate any suggestions.
EDIT - Here is a clarification of what I mean:
The user runs my python script, which I will call foobar.py, as follows:
python foobar.py
Then the user opens their web browser, and enters the url https://google.com, and as the script is running, the user will not actually visit https://google.com, but be redirected to https://github.com
One option is if you are trying to build a lightweight web app using python where you can mess with HTTP redirects, you can use Flask to accept a GET on a route and then simply do a redirect.
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/')
def index():
return redirect("https://www.google.com")
But to answer your question more directly, you don't "stop" the initial request. You simply find a way to handle it and serve back a response that you specify.

Responding to an http request with JSON in Python

I have several Python scripts that are used from the CLI. Now I have been asked if I could provide a web API to perform some of the work normally done from the CLI. I would like to respond with JSON formatted data. I know little about JSON or API's.
How do I retrieve the query data from the http request?
How to a create the HTTP headers for the response from my script?
Given the following URL, how can I respond with a JSON reply of the name "Steve"?
http://myserver.com/api.py?query=who
I already have a functioning Apache web server running, and can serve up HTML and CGI scripts. It's simply coding the Python script that I need some help with. I would prefer not to use a framework, like Flask.
A simple code example would be appreciated.
The following is the Python code that I've come up with, but I know it's not the correct way to do this, and it doesn't use JSON:
#!/usr/local/bin/python3.7
import cgi # CGI module
# Set page headers and start page
print("Content-type: text/plain")
print("X-Content-Type-Options: nosniff\x0d\x0a\x0d\x0a")
# Form defaults
query_arg = None
# Get form values, if any
form = cgi.FieldStorage()
# Get the rest of the values if the form was submitted
if "query" in form.keys():
query_arg = form["query"].value
if query_arg == "who":
print("Steve", end="", flush=True)
You are trying to build a request handler with core python code. which is able to handle http request, In my opinion its not good idea as there are multiple securty scenarios attached with it, also in cross server request its bit difficult to handle all request and request scenarios . I will suggest to use Flask which is very lightweight and this will give an pre-setup of routing mechanism to handle all kind of request in very less code below is the snippet to generate http json response hope it helps
import sys
import flask
import random, string
from flask import jsonify
class Utils:
def make_response(self):
response = jsonify({
'message': 'success',
})
response.status_code = 200
return response

In Django, how to re-package a Request.response as a Django response?

I have a Django App that accepts messages from a remote device as a POST message.
This fits well with Django's framework! I used the generic View class (from django.views import View) and defined my own POST function.
But the remote device requires a special reply that I cannot generate in Django (yet). So, I use the Requests library to re-create the POST message and send it up to the manufacturer's cloud server.
That server processes the data, and responds with the special message in the body. Idealy, the entire HTML response message should go back to the remote device. If it does not get a valid reply, it will re-send the message. Which would be annoying!
I've been googling, but am having trouble getting a clear picture on how to either:
(a): Reply back in Django with the Requests.response object without any edits.
(b): Build a Django response and send it back.
Actually, I think I do know how to do (b), but its work. I would rather do (a) if its possible.
Thanks in Advance!
Rich.
Thanks for the comments and questions!
The perils of late night programming: you might over-think something, or miss the obvious. I was so focused on finding a way to return the request.response without any changes/edits I did not even sketch out what option (b) would be.
Well, it turns out its pretty simple:
s = Session()
# Populate POST to cloud with data from remote device request:
req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()
timeout = 10
retries = 3
while retries > 0:
try:
logger.debug("POST data to remote host")
resp = s.send(prepped, timeout=timeout)
break
except:
logger.debug("remote host connection failed, retry")
retries -= 1
logger.debug("retries left: %d", retries)
time.sleep(.3)
if retries == 0:
pass # There isn't anything I can do if this fails repeatedly...
# Build reply to remote device:
r = HttpResponse(resp.content,
content_type = resp.headers['Content-Type'],
status = resp.status_code,
reason = resp.reason,
)
r['Server'] = resp.headers['Server']
r['Connection'] = resp.headers['Connection']
logger.debug("Returning Server response to remote device")
return r
The Session "s" allows one to use "prepped" and "send", which allows one to monkey with the request object before its sent, and to re-try the send. I think at least some of it can be removed in a refactor; making this process even simpler.
There are 3 HTTP object at play here:
"req" is the POST I send up to the cloud server to get back a special (encrypted) reply.
"resp" is the reply back from the cloud server. The body (.content) contains the special reply.
"r" is the Django HTTP response I need to send back to the remote device that started this ball rolling by POSTing data to my view.
Its pretty simple to populate the response with the data, and set headers to the values returned by the cloud server.
I know this works because the remote device does not POST the same data twice! If there was a mistake anyplace in this process, it would re-send the same data over and over. I copied the While/try loop from a Socket repeater module. I don't know if that is really applicable to HTTP. I have been testing this on live hardware for over 48 hours and so far it has never failed. Timeouts are a question mark too, in that I know the remote device and cloud server have strict limits. So if there is an error in my "repeater", re-trying may not work if the process takes too long. It might be better to just discard/give up on the current POST. And wait for the remote device to re-try. Sorry, refactoring out loud...

Can't parse request data in Flask when running over SSL

I have a Flask based app running which had a path responding to a POST command. The incoming data was json so I used the get_json() method to parse the data.
I have now changed the server to run nginx and uwsgi as I now use SSL. All paths in the app work (GET) but the POST based path no longer sees the incoming data as python and fails. The data is visible in request.data but the get_json method fails.
#school_app.route('/school/queries', methods=['POST'])
def school_queries():
req = request.get_json(silent=True, force=True)
command_name = req["result"]["parameters"]["command-name"]
I have also tried to push the request.data through json.loads but this fails as well.
req = json.loads(request.data)
I'm assuming that the server changes have impacted the data but I can't see why it can no longer be parsed as json.
So, the following code works...
data = request.data
req = json.loads(data)
command_name = req["result"]["parameters"]["command-name"]
Still not sure why the get_json method stopped working after the switch to SSL/nginx/uwsgi but at least it works.

Non blocking download of file in flask?

I am making a drop box like service using openstack. I am making a web interface using flask. User gets the object data in content of get request. I am sending data to the user iteratively. But my Flask app stops until the whole object is dowloaded. How could I make it non blocking?
#Returns the json content
r = swift_account.getObject(container_name, object_name)
filename = r.headers['X-Object-Meta-Orig-Filename']
#Make a generator so that all the content are not stored at once in memory
def generate():
for chunk in r.iter_content():
yield chunk
response = make_response(Response(stream_with_context(generate())))
response.headers['Content-Disposition'] = 'attachment; filename=' + filename
return response
Whether flask runs as blocking or non-blocking depends how you run it. When you say it blocks, how are you running it?
Unless it's being run by something with async capabilities like gunicorn with async or at least a threading model for multiple requests like apache with mod_wsgi then it won't be able to respond to more than one request at a time.

Categories

Resources