Flask change the server header - python

I've made a simple flask application:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
host:google.be
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.9.6 Python/2.7.6
Date: Mon, 08 Dec 2014 19:15:43 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
Connection closed by foreign host.
One of the things I would like the change is the server header which at the moment is set as Werkzeug/0.9.6 Python/2.7.6 to something my own chosing. But I can't seem to find anything in the documentation on how to do this.

You can use Flask's make_response method to add or modify headers.
from flask import make_response
#app.route('/index')
def index():
resp = make_response("Hello, World!")
resp.headers['server'] = 'ASD'
return resp

#bcarroll's answer works but it will bypass other processes defined in original process_response method such as set session cookie.
To avoid the above:
class localFlask(Flask):
def process_response(self, response):
#Every response will be processed here first
response.headers['server'] = SERVER_NAME
super(localFlask, self).process_response(response)
return(response)

You can change the Server header for every response by overriding the Flask.process_response() method.
from flask import Flask
from flask import Response
SERVER_NAME = 'Custom Flask Web Server v0.1.0'
class localFlask(Flask):
def process_response(self, response):
#Every response will be processed here first
response.headers['server'] = SERVER_NAME
return(response)
app = localFlask(__name__)
#app.route('/')
def index():
return('<h2>INDEX</h2>')
#app.route('/test')
def test():
return('<h2>This is a test</h2>')
http://flask.pocoo.org/docs/0.12/api/#flask.Flask.process_response

Overriding Server header in code does not work if You use production server like gunicorn. The better way is to use proxy server behind gunicorn and there change Server header.

TL;DR - overwrite /python3.8/http/server.py send_response method. Comment the server header addition line.
Why?
Adding/Manipulating headers in flask (in any way that mentioned above) will fire the response with the configured headers from flask to the web server but the WSGI logic (which happens independently, after & before flask logic) will be the last one to modify those values if any.
In your case(Werkzeug) some headers are hard-coded in python http module which werkzeug depending on. The server header is one of them.

Easy way:
#app.after_request
def changeserver(response):
response.headers['server'] = SERVER_NAME
return response

Related

Having trouble calling a get request using Falcon framework

I'm following Falcon tutorial for Python.
Everything worked fine until this part:
The response I'm getting when trying this command http localhost:8000/images is:
HTTP/1.1 500 Internal Server Error
Content-Length: 110
Content-Type: text/plain
Date: Sat, 01 Dec 2018 15:50:26 GMT
Server: waitress
Internal Server Error
The server encountered an unexpected internal server error
(generated by waitress)
I read it's a problem in the code but I can't find it, it's exactly as in the tutorial, app.py file:
import falcon
from images import Resource
api = application = falcon.API()
images = Resource()
api.add_route('/images', images)`
images.py:
import json
import falcon
class Resource(object):
def on_get(self, req, resp):
doc = {
'images': [
{
'href': '/images/1eaf6ef1-7f2d-4ecc-a8d5-6e8adba7cc0e.png'
}
]
}
# Create a JSON representation of the resource
resp.body = json.dumps(doc, ensure_ascii=False)
# The following line can be omitted because 200 is the default
# status returned by the framework, but it is included here to
# illustrate how this may be overridden as needed.
resp.status = falcon.HTTP_200
Also, I have an empty file named __init__.py and all the files are in the same folder, C:\look\look.
P.S.
I tried to add an HTTP requests scratch file (using PyCharm IDE) but there is no option to add that kind of a file (after I press Ctrl + Shift + Alt + Insert). I couldn't find how to fix this anywhere.
I see that the question is pretty old, but I found a solution. Just run the server with command:
waitress-serve --port=8000 --call look.app:get_app
since we get the app from calling the function get_app()

POST to Flask from espduino timing out

I'm playing with an esp8266 providing WiFi to an Arduino with this library. I have it set up properly to POST to Pushover as well as requestbin, and between the debug output and the interface from these tools, I can verify that the request data is being POSTed properly, and the esp8266 / arduino unit's response status code properly shows 200.
I wanted to test the reliability of the setup, so I figured I'd turn to Flask. I put in a for loop to POST 100 requests from the espduino to a Flask app running at 0.0.0.0:5000. The POST contains a test string (trying to roughly gauge simple data integrity, make sure the string comes through uncorrupted) as well as the number of the loop that is being sent (e.g. 0 on the first loop ... 99 on the last). Flask keeps track of these requests and updates its output to show which requests came through properly and which did not.
The setup is working great except for one thing: the module is timing out after each request. Each POST appears to work, Flask updates the output appropriately, but the espduino takes its full 5 seconds (default timeout) for each request and says it got a response code of 0.
So to reiterate the situation: my espduino setup properly POSTs and gets a 200 response from requestb.in and pushover.net, but with my local Flask server POSTs and then times out.
WHYT:
Make sure all form data is read by Flask -> no difference
Serve with gunicorn instead of the built-in Flask server -> no difference
Change response content type explicitly to "text/html" -> no difference
Change response content type to "application/json" -> no difference
Add ; charset=utf-8 to content type -> no difference
Change Flask api endpoint from index / to /api -> no difference
Change port -> no difference
Verify Firewall is off (OS X 10.10.5 hosting the Flask app) -> no difference
nc -l 5000 and check the incoming post from the module (looks good)
Post from Postman and curl to the Flask app to inspect response (and compare with responses from Requestb.in / sites that work)
Removed special chars from the test_string
Change WSGIRequestHandler.protocol_version to HTTP/1.1 -> no difference
I've spent a couple days working on this and not made much progress. The biggest breakthrough I had today was that I can successfully post and get 200 responses if I put my gunicorn / Flask setup behind nginx, with no changes to the espduino code, so I'm sure there's something that Flask is or isn't doing (I was concerned it might be the espduino's processing of an IP address vs domain name, but I think this rules that out).
Summary of the setups I've tried:
POST to requestb.in -> POST works, 200 response, no timeout
POST to pushover.net -> POST works, 200 response, no timeout
POST to local Flask server -> POST works, no response, times out
GET to local Flask server -> no response, times out (have not tested if GETs data)
POST to local gunicorn serving Flask app -> POST works, no response, times out
POST to local nginx serving gunicorn / Flask -> POST works, 200 response, no timeout
POST to local gunicorn serving httpbin -> no response, times out
POST to local cherrypy server -> no response, times out
Current code:
from flask import Flask, request, make_response
from datetime import datetime
app = Flask(__name__)
ESPDUINO_IP = 'XXX.XXX.XXX.XXX'
INCOMING_TEST_STRING = 'This is my espduino test string!'
incoming_requests = []
with open('results.txt', 'w') as f:
f.write("Test run start: {}\n".format(datetime.now()))
#app.route('/api', methods=['GET', 'POST'])
def count_requests():
if request.method == 'POST':
form = request.form
incoming_ip = request.remote_addr
if incoming_ip == ESPDUINO_IP and form['test_string'] == INCOMING_TEST_STRING:
test_num = int(form['test_num'])
incoming_requests.append(test_num)
msg = "All is peachy!"
with open('results.txt', 'a') as f:
f.write("{:02d}: {}\n".format(test_num, datetime.now()))
else:
msg = "Hey, you're not the espduino!<br>"
msg += str(len(incoming_requests)) + " requests so far.<br>"
missing = set(range(100)) - set(incoming_requests)
msg += "Missing: {}<br>".format(', '.join(map(str, missing)) if missing else "None!")
msg += '<br>'.join(map(str, incoming_requests))
resp = make_response('{"this": "that"}')
resp.headers['Content-Type'] = "application/json"
return resp
# return "<html><body>{}</body></html>".format(msg)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
Here's what the POST from the espduino looks like:
$ nc -l 5000
POST /api HTTP/1.1
Host: XXX.XXX.XXX.XXX
Content-Length: 55
Connection: close
Content-Type: application/x-www-form-urlencoded
User-Agent: ESPDRUINO#tuanpm
test_string=This is my espduino test string!&test_num=0
As compared to curl -X POST -d 'test_string=This is my espduino test string!&test_num=0' localhost:5000/api:
$ nc -l 5000
POST /api HTTP/1.1
Host: localhost:5000
User-Agent: curl/7.43.0
Accept: */*
Content-Length: 55
Content-Type: application/x-www-form-urlencoded
test_string=This is my espduino test string!&test_num=0
Would love to hear any ideas on what may be going on. I'm wondering if this might be a WSGI problem?
Update Aug 31, 2015:
I still haven't figured this out, but it's definitely not a problem specific to Flask. As I noted above, I've also replicated the timeout with CherryPy, as well as with python3 -m http.server --bind 0.0.0.0 5000 (and changing the espduino code to GET /), as well as with ruby -run -e httpd. I still don't understand why nginx, requestbin, etc. serve it with no problems.
In response to #Miguel's comment about the HOST header not having the port, I'm working on forking and building the firmware to change this, but in the meantime I hard-coded the client host and port into a little HTTP server script with no luck.
from http.server import BaseHTTPRequestHandler, HTTPServer
class MyServer(BaseHTTPRequestHandler):
# protocol_version = 'HTTP/1.1'
# close_connection = True
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
self._set_headers()
self.wfile.write(b"<html><body><h1>hi!</h1></body></html>")
def do_HEAD(self):
self._set_headers()
def do_POST(self):
self.client_address = ("192.168.0.4", 5000)
self._set_headers()
self.wfile.write(b"<html><body><h1>POST!</h1></body></html>")
# import pdb; pdb.set_trace()
def run(server_class=HTTPServer, handler_class=MyServer, port=5000):
server_address = ('0.0.0.0', port)
httpd = server_class(server_address, handler_class)
print('Starting httpd...')
httpd.serve_forever()
if __name__ == "__main__":
run()
Looking through tcpdump to see if I can find any difference between the working (nginx) and nonworking network data. Haven't found anything so far, but I'm also new to the tool.
Update Sep 08, 2015
Still haven't figured this out, but it looks like the tcpdump is significantly different between the nginx and Python servers. Here is an example POST and response -- I have replaced the IPs with ESPDUINO_IP and OSX_IP for clarity, and cleaned up the surrounding ACK calls and such. I need to look into why the Python response is getting interrupted by that odd line -- I examined 10+ consecutive POST / Response pairs, and every one of the Python responses was interrupted like that (in exactly the same place), and none of the nginx responses were, so I wonder if that might be the problem. (Also, as you can see, during this round of testing I had changed the response body to text instead of JSON -- no change in results.)
nginx (works)
POST /api HTTP/1.1
Host: OSX_IP
Content-Length: 29
Connection: close
Content-Type: application/x-www-form-urlencoded; charset=utf-8
User-Agent: espduino#n8henrie
test_string=simple&test_num=0
09:16:04.079291 IP OSX_IP.commplex-main > ESPDUINO_IP.49146: Flags [P.], seq 1:183, ack 211, win 65535, length 182
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Mon, 31 Aug 2015 15:16:04 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 26
Connection: close
<html><body></body></html>
Flask (times out)
POST /api HTTP/1.1
Host: OSX_IP
Content-Length: 29
Connection: close
Content-Type: application/x-www-form-urlencoded; charset=utf-8
User-Agent: espduino#n8henrie
test_string=simple&test_num=3
09:00:19.424086 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [P.], seq 1:18, ack 211, win 65535, length 17
HTTP/1.0 200 OK
09:00:36.382125 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [FP.], seq 18:181, ack 211, win 65535, length 163
E....F#.#..,...e.......#...k..S.P.......Content-Type: text/html; charset=utf-8
Content-Length: 26
Server: Werkzeug/0.10.4 Python/3.4.3
Date: Mon, 31 Aug 2015 15:00:36 GMT
<html><body></body></html>
It looks to me like Python is breaking the response into two halves for some reason, e.g. one part of length 17 and another of length 163, as compared to nginx's single response of length 182.
Update Sep 10, 2015
Interestingly, everything works as expected if I run it through mitmproxy -- even directly to the Flask app without nginx or gunicorn. As soon as I remove mitmproxy, it's back to the timeouts as per above.
Still haven't fixed the problem, but I think I may have figured out what's causing it. Not a Flask problem after all.
Unfortunately this seems to be a bug with the esp_bridge library whose firmware espduino uses in the esp8266. Pardon the likely incorrect terminology, but from what I can tell for some reason it doesn't seem to be joining TCP packets. The servers that produce an HTTP response that is split into separate TCP packets (e.g. Flask) are failing, while tcpdump can verify that nginx and mitmproxy are joining the split TCP packets and returning a response in a single packet, which is why they are working.
https://github.com/tuanpmt/esp_bridge/issues/10
Update 20160128
I revisited this issue today and found a workaround. While the ideal solution would be to fix esp_bridge to reassemble multi-packet reponses, as long as the response is quite small one can force Flask to write the responses in a single packet.
from werkzeug.serving import WSGIRequestHandler
# Your Flask code here...
if __name__ == "__main__":
WSGIRequestHandler.wbufsize = -1
app.run()

Python Flask getting 404 Not Found Error

I am designing this simple website using Flask. But I am getting a 404 error for my hello method. Whenever I click on the button "rate artists," it's giving me the error, but templates for home.html, hello.html, and artist.html are all correct. I do not know what part of my code is wrong. Any help?
#app.route("/",methods=['GET','POST'])
def login():
if request.method=="GET":
return render_template("home.html")
if request.method=="POST":
if (request.form["button"]=="login"):
return render_template("hello.html",name=request.form["name"])
#app.route("/hello",methods=['GET','POST'])
def hello():
if request.method=="GET":
if(request.form["button"]=="rate artists"):
return render_template("artist.html")
I'm having the same problem.
I'm doing some experimental CORS tests between backbone and Flask as an endpoint in a different url
Are you using SERVER_NAME in your Config object or when you use the app.run ?
I put the SERVER_NAME but did not specify the port,
class Config(object):
DEBUG = True
TESTING = True
STATIC_FOLDER = STATIC_FOLDER
STATIC_URL = STATIC_URL
NAVIGATION_JS = '/static/js/navigation.js'
SESSION_COOKIE_SECURE = True
REDIS_HOST_SESSION = 'localhost'
REDIS_PORT_SESSION = 6379
REDIS_DB_SESSION = 2
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 3
SERVER_NAME = 'api.jvazquez.com.ar'
Server name doesn't has the port
I also was getting 404 for any of the routes I had defined (if you are interested, I'm using blueprints)
When I send a request to my flask app using curl, I was getting 404, here is the output
I was getting 404 for all of the blueprints that I had defined
jvazquez#aldebaran:~$ curl -i http://api.jvazquez.com.ar:5000/alive/
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Set-Cookie: session=94c2c120-34c0-4a00-b094-7b5623d438ff; Domain=.api.jvazquez.com.ar; HttpOnly; Path=/
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Wed, 25 Dec 2013 11:21:16 GMT
So, check if you have defined the config variable SERVER_NAME and it has the port
For whatever reason I encountered this when I set the SERVER_NAME to 127.0.0.1 instead of localhost. I don't know why that was important with my setup, but after changing it to localhost everything started working again.

logging errors with flask

I'm trying to log an error in a decorator function using app.logger.error(''), but it just doesn't work. In addition I cant debug this well and I can only see the response from the http client:
(I'm using nginx+uwsgi+flask)
HTTP/1.1 502 Bad Gateway
Server: nginx
Date: Sun, 12 Aug 2012 15:45:09 GMT
Content-Type: text/html
Content-Length: 14
Connection: keep-alive
Everything works great with out the line: app.logger.error('panic !!!')
def mydecorator():
def decorator(f):
def wrapped_function(*args, **kwargs):
try:
ip = Mytable.query.filter_by(ip=request.remote_addr).first()
except:
app.logger.error('panic !!!')
else:
dootherthing()
resp = make_response(f(*args, **kwargs))
h = resp.headers
h['add-this-header'] = ":)"
return resp
return update_wrapper(wrapped_function, f)
return decorator
It seems that it is out of context or something.
in fact, the decorator wasnt able to detect the app instance out of context, i solve this using current_app:
1st. Import the method: from flask import current_app
2nd. append any app class to current_app: current_app.logger.error('panic !!!')
info # http://flask.pocoo.org/docs/api/#flask.current_app
"Points to the application handling the request. This is useful for
extensions that want to support multiple applications running side by
side. This is powered by the application context and not by the
request context, so you can change the value of this proxy by using
the app_context() method."
Is app defined anywhere in the script that you've posted?
Also, to help with debugging, when testing you should consider using the run() method with debug mode.
app.run(debug=True)

Creating a web server with Flask that outputs something available for a GET request

I have a Python script that outputs a piece of text in a string. I am attempting to make that piece of text available online so that I can pull it down to an Arduino Microcontroller. In other words, the work flow goes like this: Text source > Python > ??? > Arduino > Final output.
I've used the sample Flask code from Heroku to begin to experiment with getting this functionality working. Their code for Flask follows:
import os
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return 'Hello World!'
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
When I attempt to do an HTTP get request on my heroku app, it gives me a 404. I suspect this is because this script doesn't really output anything. For example, when I use this Processing app from the Processing.org website to do a GET request:
import processing.net.*;
Client c;
String data;
void setup() {
size(200, 200);
background(50);
fill(200);
c = new Client(this, "http://freezing-stream-5123.herokuapp.com/", 80); // Connect to server on port 80
c.write("GET / HTTP/1.1\n"); // Use the HTTP "GET" command to ask for a Web page
c.write("Host: my_domain_name.com\n\n"); // Be polite and say who we are
}
void draw() {
if (c.available() > 0) { // If there's incoming data from the client...
data = c.readString(); // ...then grab it and print it
println(data);
}
}
What is returned is this:
HTTP/1.1 200 OK
Date: Sat, 31 Mar 2012 22:27:10 GMT
Server: Apache
Cache-control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Pragma: no-cache
Content-Length: 968
Connection: close
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><meta http- equiv="refresh" content="0;url=http://earthlink-help.com/main?
InterceptSource=0&ClientLocation=us&ParticipantID=xj6e3468k634hy3945zg3zkhfn7zfgf6&FailureMode =1&SearchQuery=&FailedURI=http%3A%2F%2Fmy_domain_name.com%2F&AddInType=4&Version=2.1.8-1.90base&Referer=&Implementation=0"/><script type="text/javascript">url="http://earthlink-help.com/main?InterceptSource=0&ClientLocation=us&ParticipantID=xj6e3468k634hy3945zg3zkhfn7zfgf6&FailureMode=1&SearchQuery=&FailedURI=http%3A%2F%2Fmy_domain_name.com%2F&AddInType=4&Version=2.1.8-1.90base&Referer=&Implementation=0";if(top.location!=location){var w=window,d=document,e=d.documentElement,b=d.body,x=w.innerWidth||e.clientWidth||b.clientWidth,y=w.innerHeight||e.clientHeight||b.clientHeight;url+="&w="+x+"&h="+y;}window.
location.replace(url);</script></head><body></body></html>
AKA: Nothing is there. "Hello world" does show up when I use curl to pull the webpage, but I don't know if that means anything.
So my question is: can anyone point me towards something that will stick my string in something that I can retrieve it from? I realize this is probably a stupid question, but I'm totally lost in a sea of web servers etc and would appreciate some guidance.
Thanks!
"Hello world" does show up when I use curl to pull the webpage, but I don't know if that means anything.
It probably means it's working and the problem is possibly in the Processing code.
It's not very clear if your problem is just with getting the basic Flask app running.
It looks like part of the problem is that you are not returning a response object, look at the API docs. There is a Response object that you should be able to just fill with text, either json or just text. If you plan on using the URL endpoint look at using the function 'jsonify' from Flask.

Categories