Handle HEAD request method using gunicorn and django - python

Is there a way to force gunicorn to pass the handling of "HEAD" request method to a drf application?
Currently, I have a view which looks similar to the code below :-
#api_view(["POST", "GET", "DELETE", "PUT", "PATCH", "HEAD"])
#renderer_classes([ProxyRender])
def my_proxy_view(request, path=""):
return proxy_dispatch(
urljoin(settings.PROXY["ENDPOINT"], path),
request,
)
However, when sending a "HEAD" request to the endpoint it seems like gunicorn or Django is the one handling the response, not my view as invoking my view should yield a head result of the service behind.
$ curl -I "http://localhost:8000/proxy/some-endpoint"
HTTP/1.1 200 OK
Server: gunicorn
Date: Sat, 20 Aug 2022 15:29:57 GMT
Connection: close
Content-Type: application/json
Allow: HEAD, POST, PUT, DELETE, GET, PATCH, OPTIONS
X-Frame-Options: DENY
Content-Length: 106
Vary: Cookie
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin

Related

Python. Cannot make a request to simple site api. Flask and requests

I am trying to create simple API for my site. I created the route with flask:
#api.route('/api/rate&message_id=<message_id>&performer=<performer_login>', methods=['POST'])
def api_rate_msg(message_id, performer_login):
print("RATE API ", message_id, ' ', performer_id)
return 400
print(...) function don't execute...
I use flask-socketio to communicate between client and server.
I send json from client and process it with:
#socket.on('rate')
def handle_rate(data):
print(data)
payload = {'message_id':data['message_id'], 'performer':data['performer']}
r = requests.post('/api/rate', params=payload)
print (r.status_code)
Note, that data variable is sending from client and is correct(I've checked it).
print(r.status_code) don't exec too...
Where I'm wrong? Please, sorry for my bad english :(
This api function must increase rate of message, which stored in mongodb, if interesting.
Don't put &message_id=<message_id>&performer=<performer_login> in your route string. Instead, get these arguments from request.args.
Try it:
from flask import request
...
#api.route('/api/rate', methods=['POST'])
def api_rate_msg():
print(request.args)
return ''
I've tested it with httpie:
$ http -v POST :5000/api/rate message_id==123 performer_login==foo
POST /api/rate?message_id=123&performer_login=foo HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:5000
User-Agent: HTTPie/0.9.8
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 02 Apr 2017 13:54:40 GMT
Server: Werkzeug/0.11.11 Python/2.7.13
And from flask's log:
ImmutableMultiDict([('message_id', u'123'), ('performer_login', u'foo')])
127.0.0.1 - - [02/Apr/2017 22:54:40] "POST /api/rate?message_id=123&performer_login=foo HTTP/1.1" 200 -
Remove the below part from your api route
&message_id=<message_id>&performer=<performer_login
This is not required in POST request. It helps in GET requests. API call in request is not matching the route definition and therefore you have the current problem

Token Authentication Django Rest Framework HTTPie

Hello I am trying to test Token Authentication i have implemented with DRF using httpie as per the tutorial in this following link
The following command:
http GET 127.0.0.1:8000/api/projects/ 'Authorization: Token b453919a139448c5891eadeb14bf1080a2624b03'
yields the following error.
usage: http [--json] [--form] [--pretty {all,colors,format,none}]
[--style STYLE] [--print WHAT] [--headers] [--body] [--verbose]
[--all] [--history-print WHAT] [--stream] [--output FILE]
[--download] [--continue]
[--session SESSION_NAME_OR_PATH | --session-read-only SESSION_NAME_OR_PATH]
[--auth USER[:PASS]] [--auth-type {basic,digest}]
[--proxy PROTOCOL:PROXY_URL] [--follow]
[--max-redirects MAX_REDIRECTS] [--timeout SECONDS]
[--check-status] [--verify VERIFY]
[--ssl {ssl2.3,ssl3,tls1,tls1.1,tls1.2}] [--cert CERT]
[--cert-key CERT_KEY] [--ignore-stdin] [--help] [--version]
[--traceback] [--default-scheme DEFAULT_SCHEME] [--debug]
[METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]http: error: argument REQUEST_ITEM: "Token" is not a valid value
So i decided to differ from the tutorial and made my request like this
http GET 127.0.0.1:8000/api/projects/ 'Authorization:b453919a139448c5891eadeb14bf1080a2624b03'
The following message was returned
HTTP/1.0 401 Unauthorized
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Date: Thu, 03 Nov 2016 09:52:05 GMT
Server: WSGIServer/0.1 Python/2.7.10
Vary: Accept
WWW-Authenticate: Token
X-Frame-Options: SAMEORIGIN
{
"detail": "Authentication credentials were not provided."
}
Any assistance offered would be great. I am running on local machine at home.
The solution is simple as is as follows . Use double quotes in the place of single quotes contrary to what the DRF Documentation says
For curl use the command below
curl -H "Authorization: Token b453919a139448c5891eadeb14bf1080a2624b03" http://127.0.0.1:8000/api/projects/
For HTTPie use
http GET http://127.0.0.1:8000/api/projects/ "Authorization: Token b453919a139448c5891eadeb14bf1080a2624b03"
Note that Double quotes are used contrary to single quotes in the documentation.
Contrary to Paul Nyondo's experience, for me the issue is not single quotes / double quotes (both are fine when using bash as shell), but the space between Authorization: and Token.
This fails:
» http GET http://service:8000/api/v1/envs/ 'Authorization: Token 3ea4d8306c6702dcefabb4ea49cfb052f15af85c'
http: error: InvalidHeader: Invalid return character or leading space in header: Authorization
This works (with double quotes):
» http GET http://service:8000/api/v1/envs/ "Authorization:Token 3ea4d8306c6702dcefabb4ea49cfb052f15af85c"
HTTP/1.1 200 OK
Allow: GET, HEAD, OPTIONS
Content-Length: 90
Content-Type: application/json
And this also works (with single quotes):
» http GET http://svc.userv.dgvmetro:8000/api/v1/envs/ 'Authorization:Token 3ea4d8306c6702dcefabb4ea49cfb052f15af85c'
HTTP/1.1 200 OK
Allow: GET, HEAD, OPTIONS
Content-Length: 90
Content-Type: application/json

Apache sending Transfer-Encoding: chunked when deflate module is enabled

I have a simple web.py code like below, deployed with mod_wsgi in apache.
import web
urls = (
'/', 'index'
)
class index:
def GET(self):
content = 'hello'
web.header('Content-length', len(content))
return content
app = web.application(urls, globals())
application = app.wsgifunc()
This website runs well, except one minor issue. When mod_deflate is turn on, the response is chunked, even it has a very small response body.
Response Header
HTTP/1.1 200 OK
Date: Wed, 20 May 2015 20:14:12 GMT
Server: Apache/2.4.7 (Ubuntu)
Vary: Accept-Encoding
Content-Encoding: gzip
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html
When mod_deflate is turn off, Content-Length header is back.
HTTP/1.1 200 OK
Date: Wed, 20 May 2015 20:30:09 GMT
Server: Apache/2.4.7 (Ubuntu)
Content-Length: 5
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
I've searched around and someone said reduce the DeflateBufferSize will help, but this response's size is only 5, far from it's default value: 8096, so I don't think it interferes with this issue.
And someone said apache send chunked response because it doesn't know the response's size before begin to send the response to client, but in my code, I do set Content-Length.
I've also tried Flask and Apache/2.2.15 (CentOS), same result.
How do I set content-length when deflate module is enabled? and I don't like to gzip content in python.
The response Content-Length has to reflect the final length of the data sent after the compression has been done, not the original length. Thus mod_deflate has to remove the original Content-Length header and use chunked transfer encoding. The only way it could otherwise know the content length to be able to send the Content-Length before sending the compressed data, would be to buffer up the complete compressed response in memory or into a file and then calculate the length. Buffering all the compressed content isn't practical and in part defeats the point of compressing the data as the response is streamed.
If you don't want mod_deflate enabled for the whole site, then only enable it for certain URL prefixes by scoping it within a Location block.

Bottle framework with a tunnel doesn't receive GET arguments

I'm trying to make a simple python script which returns an argument from GET request. The issue is that it does not receive any arguments and returns blank body. There is one peculiar thing, though. In order to test GET requests I use requestmaker.com, hurl.it and apikitchen.com. While requestmaker and apikitchen return an empty body, hurl.it actually returns the required parameter.
I have tried Bottle, Flask and Tornado with the same results. I'm using ngrok for tunneling but I've also tried forwardhq.com.
The code (with bottle framework):
import bottle
from bottle import route, run, request, response
bottle.debug(True)
#route('/')
def home():
return "Great Scott!"
#route('/valley')
def thevalley():
theflux = request.query.flux
return theflux
run(host='0.0.0.0', port=8515, reloader=True)
ngrok status:
Tunnel Status online
Version 1.6/1.5
Forwarding http://88mph.ngrok.com -> 127.0.0.1:8515
The results I get from GET requests:
requestmaker.com
Request Headers Sent:
GET /valley HTTP/1.1
Host: 88mph.ngrok.com
Accept: */*
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
Response Headers:
HTTP/1.1 200 OK
Server: nginx/1.4.3
Date: Sun, 09 Feb 2014 13:51:20 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: keep-alive
bottle:
127.0.0.1 - - [09/Feb/2014 13:51:20] "GET /valley HTTP/1.1" 200 0
hurl.it
Request:
Accept: */*
Accept-Encoding: gzip, deflate, compress
User-Agent: runscope/0.1
Response:
Connection: keep-alive
Content-Length: 5
Content-Type: text/html; charset=UTF-8
Date: Sun, 09 Feb 2014 14:02:55 GMT
Server: nginx/1.4.3
Body:
121gw
bottle:
127.0.0.1 - - [09/Feb/2014 14:02:55] "GET /valley?flux=121gw HTTP/1.1" 200 5
https://88mph.ngrok.com/valley?flux=121gw
Finally, just entering the URL into the address bar works as well, I get "121gw".
bottle:
127.0.0.1 - - [09/Feb/2014 14:05:46] "GET /valley?flux=121gw HTTP/1.1" 200 5
End
Every request maker can connect to server (200 OK) and even return "Great Scott" when accessing root. However, only webbrowser and hurl return the argument. Any ideas what is at fault here?

HTTP 303 (SeeOther): GET Works, POST Fails

I am trying to perform a simple action:
POST to a URL
Return HTTP 303 (SeeOther)
GET from new URL
From what I can tell, this is a pretty standard practice:
http://en.wikipedia.org/wiki/Post/Redirect/Get
Also, it would seem that SeeOther is designed to work this way:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
I'm using web.py as my server-side controller, but I suspect that it's not the issue. If I GET, SeeOther works flawlessly as expected. If I POST to the same URL, the browser fails to redirect or load anything at all.
Thinking it was a browser issue, I tried both IE9 and Google Chrome (v23 ish). Both have the same issue.
Thinking web.py might be serving the page incorrectly, or generating a bad URL, I used telnet to examine the headers. I found this:
HTTP GET (this works in the browser):
GET /Users/1 HTTP/1.1
HOST: domain.com
HTTP/1.1 303 See Other
Date: Mon, 24 Dec 2012 18:07:55 GMT
Server: Apache/2
Cache-control: no-cache
Location: http://domain.com/Users
Content-Length: 0
Content-Type: text/html
HTTP POST (this does not work in the browser):
POST /Users/1 HTTP/1.1
HOST: domain.com
HTTP/1.1 303 See Other
Date: Mon, 24 Dec 2012 18:12:35 GMT
Server: Apache/2
Cache-control: no-cache
Location: http://domain.com/Users
Content-Length: 0
Content-Type: text/html
Another thing that could be throwing a wrench in the works:
I'm using mod-rewrite so that the user-visible domain.com/Users/1 is actually domain.com/control.py/Users/1
There may be more information/troubleshooting that I have, but I'm drawing a blank right now.
The Question:
Why does this work with a GET request, but not a POST request? Am I missing a response header somewhere?
EDIT:
Using IE9 Developer Tools and Chrome's Inspector, it looks like the 303 isn't coming back to the browser after a POST. However, I can see the 303 come in when I do a GET request.
However, after looking more closely at Chrome's Inspector, I saw the ability to log every request (don't clear w/ each page call). This allowed me to see that for some reason, my POST request looks like it's failing. Again - GET works just fine.
It's entirely possible that this isn't your issue, but since you don't have your code posted I'll take a shot (just in case).
Since you're using web.py, do you have the POST method defined on your object?
i.e.
urls = (
'/page', 'page'
)
class page:
def POST(self):
# Do something
def GET(self):
# Do something else

Categories