Non blocking download of file in flask? - python

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.

Related

how to create raw http responses in flask?

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?

Flask : Server not responding on internal API access [duplicate]

This question already has answers here:
Flask hangs when sending a post request to itself
(2 answers)
Closed 5 years ago.
I was trying develop web application using flask, below is my code,
from sample import APIAccessor
#API
#app.route('/test/triggerSecCall',methods=['GET'])
def triggerMain():
resp = APIAccessor().trigger2()
return Response(json.dumps(resp), mimetype='application/json')
#app.route('/test/seccall',methods=['GET'])
def triggerSub():
resp = {'data':'called second method'}
return Response(json.dumps(resp), mimetype='application/json')
And my trigger method contains the following code,
def trigger2(self):
url = 'http:/127.0.0.1:5000/test/seccall'
response = requests.get(url)
response.raise_for_status()
responseJson = response.json()
if self.track:
print 'Response:::%s' %str(responseJson)
return responseJson
When I hit http://127.0.0.1:5000/test/seccall, I get the expected output. When I hit /test/triggerSecCall, the server stop responding. The request waits forever.
At this stage, I am not able to access any apis from anyother REST clients. When I force stop the server(Ctrl+C) I am getting response in the second REST client.
Why flask is not able to serve to internal service call?
I guess you are using the single threaded development server and not a WSGI setup for production.
Since the server has only one thread is can handle one request at a time. The first request will be executed, resulting in the requests.get(...) which will open a second request that can not be handled until the first request is complete, a dead lock.
The best solution would be to just call triggerSub() to get the result instead of using an HTTP request.

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.

Flask send stream as response

I'm trying to "proxy" my Flask server (i will call it Server#01) with another server(Server#02). It's working well except for one thing : when the Server#01 use send_from_directory(), i don't know how to re-send this file.
My classic "proxy"
result = requests.get(my_path_to_server01)
return Response(stream_with_context(result.iter_content()),
content_type = result.headers['Content-Type'])
With a file a response, it's taking hours... So i tried many things. The one who work is :
result = requests.get(my_path_to_server01, stream=True)
with open('img.png', 'wb') as out_file:
shutil.copyfileobj(result.raw, out_file)
return send_from_directory('./', 'img.png')
I would like to "redirect" my response ("result" variable), or send/copy a stream of my file. Anyways I don't want to use a physical file because it don't seems the proper way in my mind and i can imagine all problems who can happens because of that.
There should not be any problem with your "classic" proxy other than that it should use stream=True, and specify a chunk_size for response.iter_content().
By default chunk_size is 1 byte, so the streaming will be very inefficient and consequently very slow. Trying a larger chunk size, e.g. 10K should yield faster transfers. Here's some code for the proxy.
import requests
from flask import Flask, Response, stream_with_context
app = Flask(__name__)
my_path_to_server01 = 'http://localhost:5000/'
#app.route("/")
def streamed_proxy():
r = requests.get(my_path_to_server01, stream=True)
return Response(r.iter_content(chunk_size=10*1024),
content_type=r.headers['Content-Type'])
if __name__ == "__main__":
app.run(port=1234)
You don't even need to use stream_with_context() here because you don't need access to the request context within the generator returned by iter_content().

File Sharing Site in Python

I wanted to design a simple site where one person can upload a file, and pass off the random webaddress to someone, who can then download it.
At this point, I have a webpage where someone can successfully upload a file which gets stored under /files/ on my webserver.
The python script also generates a unique, random 5 letter code that gets stored in a database identifying the file
I have another page called retrieve, where a person should go, put in the 5 letter code, and it should pop up a filebox asking where to save the file.
My Problem is that: 1) How do I retrieve the file for download? At this point my retrieve script, takes the code, gets the location of the file on my server, but how do I get the brower to start downloading?
2)How do I stop people from directly going to the file? Should I change permissions on the file?
How do you serve the file-upload page, and how do you let your users upload files?
If you are using Python's built-in HTTP server modules you shouldn't have any problems.
Anyway, here's how the file serving part is done using Python's built-in modules (just the basic idea).
Regarding your second question, if you were using these modules in the first place you probably wouldn't have asked it because you'd have to explicitly serve specific files.
import SocketServer
import BaseHTTPServer
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
# The URL the client requested
print self.path
# analyze self.path, map the local file location...
# open the file, load the data
with open('test.py') as f: data = f.read()
# send the headers
self.send_response(200)
self.send_header('Content-type', 'application/octet-stream') # you may change the content type
self.end_headers()
# If the file is not found, send error code 404 instead of 200 and display a message accordingly, as you wish.
# wfile is a file-like object. writing data to it will send it to the client
self.wfile.write(data)
# XXX: Obviously, you might want to send the file in segments instead of loading it as a whole
if __name__ == '__main__':
PORT = 8080 # XXX
try:
server = SocketServer.ThreadingTCPServer(('', 8080), RequestHandler)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
You should send the right HTTP Response, containing the binary data and making the browser react on it.
Try this (I haven't) if you're using Django:
response = HttpResponse()
response['X-Sendfile'] = os.path.join(settings.MEDIA_ROOT, file.file.path)
content_type, encoding = mimetypes.guess_type(file.file.read())
if not content_type:
content_type = 'application/octet-stream'
response['Content-Type'] = content_type
response['Content-Length'] = file.file.size
response['Content-Disposition'] = 'attachment; filename="%s"' % file.file.name
return response
Source: http://www.chicagodjango.com/blog/permission-based-file-serving/

Categories

Resources