I am looking for a method to write a simple proxy in flask for ttyd which is an open-source web terminal(https://github.com/tsl0922/ttyd). The most immediate way is to read client request and relay to ttyd server. However, it fails when the websocket is connecting.
My view function is as follows:
#app.route('/')
#app.route('/auth_token.js')
#app.route('/ws')
def ttyd():
if request.path=='/ws':
url = 'ws://192.168.123.172:7681' + request.path
else:
url = 'http://192.168.123.172:7681' + request.path
method = request.method
data = request.data or request.form or None
cookies = request.cookies
headers = request.headers
with closing(
requests.request(method, url, headers=headers, data=data, cookies=cookies)
) as r:
resp_headers = []
for name, value in r.headers.items():
resp_headers.append((name, value))
return Response(r, status=r.status_code, headers=resp_headers)
As you can see, the view function will handle 3 url requests, the first two succeed with status code 200, the third fails with status code 500. The error code in server side is as follows:
requests.exceptions.InvalidSchema: No connection adapters were found for 'ws://192.168.123.172:7681/ws'
I also check the network in two cases(with/without proxy). The picture 'without proxy' means direct type 'http://192.168.123.172:7681', it succeeds. The picture 'with proxy' means access ttyd server with flask proxy, it fails.
Without proxy
With proxy
Since I am new to flask and websocket, I am confused about the result. The sHTTPe flask proxy can handle any other http request(e.g. access google.com) but fails in WebSocket connection.
Thank you for telling me why and how can I fix it?
According to Websockets in Flask there is a flask-sockets project at https://github.com/heroku-python/flask-sockets to serve a websocket-endpoint in flask. To make the backend websocket connection to the server you can't use requests but websocket-client, see How do I format a websocket request?.
When I had this problem I solved it using the autobahn-python project, see https://github.com/arska/stringreplacingwebsocketproxy/
Cheers,
Aarno
Related
I have encountered an issue, as I have to create a cookie in the backend, which I will later use to send a request from the frontend. Both apps are on the same domain. This is the general idea behind it: https://levelup.gitconnected.com/secure-frontend-authorization-67ae11953723.
Frontend - Sending GET request to Backend
#app.get('/')
async def homepage(request: Request, response_class=HTMLResponse):
keycloak_code = 'sksdkssdk'
data = {'code': keycloak_code}
url_post = 'http://127.0.0.1:8002/keycloak_code'
post_token=requests.get(url=url_post, json = data )
return 'Sent'
if __name__ == '__main__':
uvicorn.run(app, host='local.me.me', port=7999,debug=True)
Backend
#app.get("/keycloak_code")
def get_tokens(response: Response, data: dict):
code = data['code']
print(code)
....
requests.get(url='http://local.me.me:8002/set')
return True
#app.get("/set")
async def createcookie(response: Response):
r=response.set_cookie(key='tokic3', value='helloworld', httponly=True)
return True
if __name__ == '__main__':
uvicorn.run(app, host='local.me.me', port=8002, log_level="debug")
When I open the browser and access http://local.me.me:8002/set, I can see that the cookie is created.
But when I make a GET request from my frontend to backend to the same URL, the request is received—as I can see in the terminal—but the backend does not create the cookie. Does anyone know what I might be doing wrong?
I have tried different implementations from FastAPI docs, but none has similar use cases.
127.0.0.1 and localhost (or local.me.me in your case) are two different domains (and origins). Hence, when making a request you need to use the same domain you used for creating the cookie. For example, if the cookie was created for local.me.me domain, then you should use that domain when sending the request. See related posts here, as well as here and here.
You also seem to have a second FastAPI app (listenning on a different port) acting as your frontend (as you say). If that's what you are trying to do, you would need to use Session Objects in Python requests module, or preferably, use a Client instance from httpx library, in order to persist cookies across requests. The advantage of httpx is that it offers an asynchronous API as well, using the httpx.AsyncClient(). You can find more details and examples in this answer, as well as here and here.
I have a flask server running on localhost:8080. I have set up nginx as a reverse proxy from my domain to localhost:8080 and have set up SSL. The automatic HTTPS works fine, except when Flask returns a 308 and redirects the client due to the strict_slashes rule. It redirects the user to a http URL instead of a https one, causing the page to not load. How do I make strict_slashes return redirects to a HTTPS url instead of a HTTP one.
Since Flask url_for does not respect http or https while redirecting, always use redirect(url_for('func_name', _external=True)) to respect the root URL and Scheme(http or https) used.
Alternatively, I used this in one of such cases to explicitly redirect to HTTPS
response_body = redirect(url_for('.route_function', _external=True))
response = app.make_response(response_body)
response.location = response.location.replace("http://", "https://")
return response
I am doing POC to check if we can connect to an API, for that I use the below code.
# Define the client certificate settings for https connection
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
print(type(context))
context.load_cert_chain(certfile=CERT, keyfile=KEY, password=PW)
# Create a connection to submit HTTP requests
connection = http.client.HTTPSConnection(host, port=443, context=context)
# Use connection to submit a HTTP POST request
connection.request(method="GET", url=request_url, headers=request_headers)
# Print the HTTP response from the IOT service endpoint
response = connection.getresponse()
print(response.status, response.reason)
data = response.read()
print(data)
these two variables (CERT and KEY), i get the secrets via kubernetes files and convert them to strings. Is there alternate ways to load the downloaded secrets into context object instead of using load_cert_chain method (since this one needs files). I know this is not ideal, but since I am doing only POC I just want to see if this is doable.
I built an web application using Python Bottle framework.
I used bottle-websocket plugin for WebSocket communication with clients.
Here is a part of my code.
from bottle import Bottle, request, run
from bottle.ext.websocket import GeventWebSocketServer, websocket
class MyHandler():
...
class MyServer(Bottle):
...
def _serve_websocket(self, ws):
handler = MyHandler()
some_data = request.cookies.get('some_key') # READ SOME DATA FROM HTTP REQUEST
while True:
msg = ws.receive()
handler.do_sth_on(msg, some_data) # USE THE DATA FROM HTTP REQUEST
ws.send(msg)
del(handler)
if __name__ == '__main__':
run(app=MyServer(), server=GeventWebSocketServer, host=HOST, port=PORT)
As the code shows, I need to read some data from the browser (cookies or anything in the HTTP request headers) and use it for WebSocket message processing.
How can I ensure the request is from the same browser session as the one where WebSocket connection comes?
NOTE
As I do not have much knowledge of HTTP and WebSocket, I'd love to here detailed answere as much as possible.
How can I ensure the request is from the same browser session as the one where WebSocket connection comes?
Browser session is a bit abstract since HTTP does not have a concept of sessions. HTTP and RESTful APIs is designed to be stateless, but there is options.
Usually, what you usually want to know is what user the request comes from. This is usually solved by authentication e.g. by using OpenID Connect and let the user send his JWT-token in the Authorization: header, this works for all HTTP requests, including when setting up a Websocket connection.
bottle-oauthlib seem to be a library for authenticating end-users using OAuth2 / OpenID Connect.
Another option is to identify the "browser session" using cookies but this depends on a state somewhere on the server side and is harder to implement on cloud native platforms like e.g. Kubernetes that prefer stateless workloads.
user_info = json.dumps({"bio":biography, "interests":interest_indexes})
headers = {'Content-type':'application/json'}
url = "http://0.0.0.0:5000/users/" + str(user_id)
r = requests.post("http://0.0.0.0:5000/users/1", data=user_info, headers=headers)
I've enabled logging and this is in Flask. If I manually do a POST request to the URL with the correct JSON response body, it works fine. It just says INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): 0.0.0.0
And it just hangs there forever.
Any ideas?
You need to run your flask app with threading enabled.
app.run(threaded=True)
This is not advisable for production of course, just for quick development :)