Sidebar

x

Implement a web shell interface using Flask

flask

Recently, I am doing the PyOne back-end management program. When I am doing the upload file function, I want to show how the upload progress is more appropriate. I want to think about it, or do I think what data is displayed in the background. How to display the front-end is actually lazy! But this lazy is not so good to steal, because to display the results of the background run directly in the foreground, you need a pseudo-shell environment display, search for a few days to see two ways:


  1. Timing ajax to get results. This method has many drawbacks. First, the data acquisition is not timely, and the effect on the display is also very bad. Second, the ajax needs to generate files in the background to save the data. Under various drawbacks, this method is passed.
  2. Data is pushed by the server through websocket. This method is actually the most suitable, the server only needs to push the background data to the front end. However, because I tried to use websocket in another project, but the websocket is not enough, the effect is not good, so I gave up the websocket.

EventSource Introduction

Server push means that the server actively sends a message (response) to the client. In the HTTP protocol implementation of the application layer, the "request-response" is a round trip, and its starting point comes from the client, so the simple server-side push function cannot be implemented on the application layer. Currently there are several solutions for server push:

  1. Client long polling
  2. Websocket bidirectional connection
  3. Iframe permanent frame

Although long-wheel training can avoid server overload caused by short-wheel training, after the server returns data, the client still needs to initiate the next long-wheel training request and wait for the server to respond. This still requires the underlying connection establishment and the server processing logic needs. Corresponding processing, not logically simple process server push;

The websocket connection is the most powerful, but it has requirements for the server version. This method should be used on servers that can use the websocket protocol.

The iframe permanent frame is an iframe page embedded in the page to receive data. The page outputs relevant information by the server. For example, the server keeps writing similar script tags and data to the iframe to implement another form. Server push. However, the technique of permanent frames will cause the loading bar of the main page to always be in the "loading" state, and the experience is very poor.

The HTML5 specification provides a server-side event EventSource. After the browser creates an EventSource connection on the premise of implementing the specification, the browser can receive the message sent by the server. These messages need to follow a certain format, for the front-end developer. Just listen to the corresponding event in the browser.

Compared with the implementations mentioned in the above 3, the implementation of the EventSource stream is very simple for the client developers, and the compatibility is good except for the IE browser (IE, Edge); It can be compatible with old browsers, without upgrading to other protocols, and can meet the needs in a simple server-side push scenario. In the scenario where the browser and the server need strong interaction, websocket is still the best choice.

Make a fake shell with EventSource
Since the Python flask is used here as the server, the following code is flask.
Installation dependency package
The main dependencies are 4:

  1. Flask:web frame
  2. Shelljob: subprocess package for easier operation
  3. Eventlet: asynchronous framework
  4. Gunicorn: running the framework

pip install flask shelljob eventlet gunicorn
 

Server: server.py

# -*- coding=utf-8 -*-
from flask import Flask,Response,send_file
from shelljob import proc

import eventlet
eventlet.monkey_patch()

app = Flask(__name__)

@app.route( '/stream' )
def stream():
    g = proc.Group()
    p = g.run( [ "bash", "-c", "for ((i=0;i<10;i=i+1)); do echo $i; sleep 1; done" ] )

    def read_process():
        while g.is_pending():
            lines = g.readlines()
            for proc, line in lines:
                yield "data:" + line + "\n\n"
        yield "data:end\n\n"

    return Response( read_process(), mimetype= 'text/event-stream' )

@app.route('/page')
def get_page():
    return send_file('page.html')

if __name__ == "__main__":
    app.run(debug=True) 

 Front page: page.html

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <h1>Output</h1>
    <div id="output"></div>
    <script>
    var source = new EventSource("/stream");
    source.onmessage = function(event) {
        if(event.data=='end'){
            source.close();
        }
        else{
        document.getElementById("output").innerHTML += event.data + "<br/>";
        }
    }
    </script>
</body>

</html>
 

Server.py and page.html are placed in the same directory, run:

gunicorn -k eventlet -b 0:8000 server:app
 
Install CloudLinux OS on CentOS 7
How to upload a picture url's content by form POST...
 

Comments

No comments made yet. Be the first to submit a comment
Already Registered? Login Here
Guest
Sunday, 18 November 2018