Abort a request after checking response headers - python

I have a script that requests a URL via urllib.request's urlopen and then gets it's info().
I don't want to proceed with the request after I've got these headers so I'm currently just leaving it as it is and forgetting about it, but this seems like I'm leaving the connection open and perhaps the server is sending more that just gets ignored.
How can I abort the request properly?
#!/usr/bin/python3
import urllib.request
response = urllib.request.urlopen('http://google.co.uk')
headers = dict(response.info())
print(headers)
# now finished with response, abort???
# ... more stuff

I think what you want is a HEAD request. Something like
>>> import httplib
>>> c = httplib.HTTPConnection("www.google.co.uk")
>>> c.request("HEAD", "/index.html")
>>> r = c.getresponse()
>>> r.getheaders()
[('x-xss-protection', '1; mode=block'), ('transfer-encoding', 'chunked'), ('set-cookie', 'PREF=ID=7867b0a5641d5f7b:FF=0:TM=1363882090:LM=1363882090:S=EXLl2JgBqzMKODcq; expires=Sat, 21-Mar-2015 16:08:10 GMT; path=/; domain=.google.co.uk, NID=67=qElAph6eqHyYKbh995ivP4B-21YRDRED4-uRXx0AvC3vLpv0SF1LkdsI2k6Hg1IhsatrVVqWf2slcMCaQsAZwZ89YfU0F1iPVBdt9PC2FItff31oRJ3gvhJVTQLa_RAt; expires=Fri, 20-Sep-2013 16:08:10 GMT; path=/; domain=.google.co.uk; HttpOnly'), ('expires', '-1'), ('server', 'gws'), ('cache-control', 'private, max-age=0'), ('date', 'Thu, 21 Mar 2013 16:08:10 GMT'), ('p3p', 'CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."'), ('content-type', 'text/html; charset=ISO-8859-1'), ('x-frame-options', 'SAMEORIGIN')]
>>>
From w3.org
The HEAD method is identical to GET except that the server MUST NOT
return a message-body in the response. The metainformation contained
in the HTTP headers in response to a HEAD request SHOULD be identical
to the information sent in response to a GET request. This method can
be used for obtaining metainformation about the entity implied by the
request without transferring the entity-body itself. This method is
often used for testing hypertext links for validity, accessibility,
and recent modification.
The response to a HEAD request MAY be cacheable in the sense that the
information contained in the response MAY be used to update a
previously cached entity from that resource. If the new field values
indicate that the cached entity differs from the current entity (as
would be indicated by a change in Content-Length, Content-MD5, ETag or
Last-Modified), then the cache MUST treat the cache entry as stale.

Related

Response content shorter than Content-Length

I'm doing a FastAPI app, with a function that authenticates to a CouchDB instance. In order to request Couchdb, I use the (yet unmaintained) library python-couchdb.
Here is the relevant portion of code that illustrates my issue:
from fastapi import FastAPI
from couchdb import http
resource = http.Resource(url, http.Session())
from couchdb import Unauthorized
from pydantic import BaseModel
app = FastAPI()
class RegisteredUser(BaseModel):
email: str
password: str
#app.post("/login")
async def log_user(user: RegisteredUser):
# some email format verifications here
# ...
try:
status, headers, _ = resource.post_json('_session', {
'name': user.email,
'password': user.password,
})
except Exception as e:
if isinstance(e, Unauthorized):
return 403
else:
return 500
# tests
print(headers)
The headers look like:
Cache-Control: must-revalidate
Content-Length: 54
Content-Type: application/json
Date: Sat, 08 Aug 2020 19:19:49 GMT
Server: CouchDB/3.1.0 (Erlang OTP/22)
Set-Cookie: AuthSession=am9zZWJvdmVAam9zZWJvdmUuY29tOjVGMkVGQUQ2Op-UUD22VvdxYzbMNp92e30Er_z0; Version=1; Expires=Sat, 08-Aug-2020 20:59:50 GMT; Max-Age=6000; Path=/; HttpOnly
At this point (if no error raised), I'd like to send back to the (browser) client the cookie that CouchDb provides. Something like:
...
# tests
print(headers)
if status == 200 and 'Set-Cookie' in headers:
return JSONResponse(content=True, headers=headers)
else:
return status
I'm not used to sessions-cookie and I'm not sure if I should send back the full headers or just the headers['Set-Cookie'] part
Whatever I do, I end up with the same error message
RuntimeError: Response content shorter than Content-Length
Would you mine to explain to me what the error is saying, and how I can solve my case? Ty!
Found this SO thread but no clue neither FastAPI middleware peeking into responses
Here is one solution:
if status == 200 and 'Set-Cookie' in headers:
return JSONResponse(content=True, headers={
'Set-Cookie' : headers['Set-Cookie']
})
else:
return status
Ty #HernánAlarcón

Django REST Framework - cookies not being set via Response.set_cookie() call...?

I have the following standard Response setup in my DRF application:
response = Response(data=response, status=status.HTTP_200_OK)
I'm then attempting to add a split JWT header.payload and signature to the response headers with a response.set_cookie() call, as follows:
max_age = 365 * 24 * 60 * 60
expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=max_age)
response.set_cookie(
key='JWT_ACCESS_HEADER_PAYLOAD',
value=header_payload,
httponly=False,
expires=expires.strftime("%a, %d-%b-%Y %H:%M:%S UTC"),
max_age=max_age
)
response.set_cookie(
key='JWT_ACCESS_SIGNATURE',
value=signature,
httponly=True,
expires=expires.strftime("%a, %d-%b-%Y %H:%M:%S UTC"),
max_age=max_age
)
return response
I can't see anything wrong with what I have done thus far:
Yet, for some reason, the only cookies set on the client side are as follows:
What have I done wrong here??
The actual output in the headers seem to be a valid SetCookie value:
JWT_ACCESS_HEADER_PAYLOAD=aSasas; expires=Sat, 03-Apr-2021 10:24:31 GMT; Max-Age=31536000; Path=/
JWT_ACCESS_SIGNATURE=asaSasaS; expires=Sat, 03-Apr-2021 10:24:31 GMT; HttpOnly; Max-Age=31536000; Path=
N.B. Running on localhost...if that helps?
So, this seems like a very trivial solution because it is just that.
I was using axios ... without sending { withCredentials: true }, with the requests.
The cookies were being set - because, well, they were. It's just to see them I needed to refresh the browser.

How to get response object from a Requests GET call when it throws an exception

How can I get the response object back from a request when it throws an exception?
Essentially, I'm trying to make a request via a proxy and the proxy is giving back a 302 code (this is what I want). However, after some debugging, I discovered that inside the requests library, it expects a 200 code from the proxy and will throw an exception otherwise.
Here is the command I'm executing:
session.get(url=url, headers=req_headers, verify=False, allow_redirects=True, timeout=30)
This gives me a (Caused by ProxyError('Cannot connect to proxy.', OSError('Tunnel connection failed: 302 Object Moved')). This is the correct behavior I'm expecting from my proxy but I need the response object because the response object has useful info that I need in my logic and right now I'm only able to get the error msg. Is there a way I can still get the response object without having to modify the requests library?
Thanks for your help!
Edit:
def _tunnel(self):
connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self._tunnel_host,
self._tunnel_port)
connect_bytes = connect_str.encode("ascii")
self.send(connect_bytes)
for header, value in self._tunnel_headers.items():
header_str = "%s: %s\r\n" % (header, value)
header_bytes = header_str.encode("latin-1")
self.send(header_bytes)
self.send(b'\r\n')
response = self.response_class(self.sock, method=self._method)
(version, code, message) = response._read_status()
if code != http.HTTPStatus.OK:
self.close()
raise OSError("Tunnel connection failed: %d %s" % (code,
message.strip()))
I've debugged it such that I know that the code is throwing the OSError and it only gives the status code and the message. What I want to do is also pass in the response to that exception but at the same time I don't want to change the library because I'm hoping there's a way to do this without changing the lib
It looks to me from the source code for the ProxyError exception object in Requests like that object should have the request in it. I know you said you think it doesn't, but this shows that it is at least putting a response field in there, even if it were to end up null.
I would have maybe put this in comments, but you can't format code there. This sure seems though like it would give you what you want:
class RequestException(IOError):
"""There was an ambiguous exception that occurred while handling your
request.
"""
def __init__(self, *args, **kwargs):
"""Initialize RequestException with `request` and `response` objects."""
response = kwargs.pop('response', None)
self.response = response
self.request = kwargs.pop('request', None)
if (response is not None and not self.request and
hasattr(response, 'request')):
self.request = self.response.request
super(RequestException, self).__init__(*args, **kwargs)
class ConnectionError(RequestException):
"""A Connection error occurred."""
class ProxyError(ConnectionError):
"""A proxy error occurred."""
So seeing this code, it seems like something like this would work:
try:
...
session.get(url=url, headers=req_headers, verify=False, allow_redirects=True, timeout=30)
...
except ProxyError as ex:
the_response = ex.response
.. do something with the response ..
I think you may be able to use Request's history functionality to access that object, and Request's standard APIs to go from there:
https://2.python-requests.org//en/latest/user/quickstart/#redirection-and-history
By default Requests will perform location redirection for all verbs
except HEAD.
We can use the history property of the Response object to track
redirection.
The Response.history list contains the Response objects that were
created in order to complete the request. The list is sorted from the
oldest to the most recent response.
r = requests.get('http://github.com/')
r.url 'https://github.com/'
r.status_code 200
r.history [<Response [301]>]
Will headers then not give you what you need?
https://2.python-requests.org//en/latest/user/advanced/#advanced
>>> r.headers
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}

How to change request headers

How do I set request headers? I am downloading image from instagram and I want to change its filename, get the file size. These many request headers are there wiki
This is what I have done until now
import requests
#app.route('/try')
def trial():
img = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
imgData = requests.get(img)
return imgData # this gives me error: Response object is not callable
Edit : I want to set the content-disposition header . Another question : when I set the headers, then how would I give the image to the client side?
I read on Internet that to send the file to client side I should set the request header. But how do I send the file is my another question? Sorry for asking another question in the same
The query which you asked has nothing to do with Flask. Flask is a web framework. Requests is a HTTP library which will help solve your issue
You just need to create a dict with your headers (key: value pairs where the key is the name of the header and the value is, well, the value of the pair) and pass that dict to the headers parameter on the .get or .post method.
headers = {'Content-Type': 'text/plain'}
r = requests.get('http://example.com', headers=headers)
If you wish to check the header values set when the request was sent then you can simple do this:
print r.headers
print r.headers['Content-Type'] # Output - 'text/html'
resp = requests.get('https://igcdn-photos-e-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-15/e35/12093691_1082288621781484_1524190206_n.jpg')
resp.headers
And you will get the response like this ..
{'content-length': '73921', 'last-modified': 'Fri, 30 Oct 2015 15:18:29 GMT', 'connection': 'keep-alive', 'cache-control': 'max-age=1209600', 'date': 'Wed, 18 Nov 2015 08:44:15 GMT', 'access-control-allow-origin': '*', 'content-type': 'image/jpeg', 'timing-allow-origin': '*'}
You can get headers... check out the content-length. And you will not gonna get the file name I believe because content-disposition will not be there. So try something else to get the file name.

LinkedIn out of network API calls returning "unauthorized" error

I'm trying to access out-of-network profiles using the LinkedIn API, following the instructions from here.
I'm writing my code in Python.
As I understand it, I just add an extra header to my HTTPSConnection request:
normal call:
connection.request(method, relative_url, body = body, headers={'Authorization': OAuth_header})
out-of-network call:
// these values were extracted from the http-header I received
name = x-li-auth-token
value = name:R8Y4
connection.request(method, relative_url, body = body, headers={'Authorization': OAuth_header, name: value})
When I do the out-of-network call, I get an error:
error:
status: 401
timestamp: 1330027911625
request-id: VHUSL0J7TL
error-code: 0
message: [unauthorized]. OAU:k1tofeoqr4id|2dc38f4e-73d1-4d31-9330-dd82aca89616|*01|*01:1330027911:CRc7YYYQRe2VS6woJpGX+qYVa/Q=
I've been testing this on both my own profile and an actual out-of-network profile, no change in error.
From various API requests, the "value" changes slightly, and I have tried all variants:
"name:R8Y4"
"search-name:R8Y4"
"OUT_OF_NETWORK:R8Y4"
I'm guessing it has to do with the HTTP headers, but I have no idea what is wrong.
Please help! Thank you.
I'm not sure why your call is failing. Here's the sequence using the tested oauth2 library in python, including the entire HTTP conversation.
First, do the search:
http://api.linkedin.com/v1/people-search:(people:(distance,id,first-name,last-name,headline,api-standard-profile-request))
Parameters (I'm using OAuth in the parameters for this call)
oauth_body_hash=2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D
oauth_nonce=41358038
oauth_timestamp=1330098205
oauth_consumer_key=xxx
oauth_signature_method=HMAC-SHA1
facet=network%2CO
oauth_version=1.0
oauth_token=xxx
keywords=Schneider+Electric
oauth_signature=xxx
Response includes:
<person>
<distance>-1</distance>
<id>UBAQYFeiHo</id>
<first-name></first-name>
<last-name>Private</last-name>
<headline>Assistant Engineer at Schneider Electric</headline>
<api-standard-profile-request>
<url>http://api.linkedin.com/v1/people/UBAQYFeiHo</url>
<headers total="1">
<http-header>
<name>x-li-auth-token</name>
<value>OUT_OF_NETWORK:wHti</value>
</http-header>
</headers>
</api-standard-profile-request>
</person>
Second call, to get the profile:
http://api.linkedin.com/v1/people/UBAQYFeiHo:(id,first-name,last-name)
Request headers:
Host: api.linkedin.com
x-li-auth-token: OUT_OF_NETWORK:wHti
accept-encoding: gzip, deflate
user-agent: Python-httplib2/$Rev$
Response:
{'status': '200', 'content-length': '158', 'content-location': u'http://api.linkedin.com/v1/people/UBAQYFeiHo:(id,first-name,last-name)?oauth_body_hash=2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D&oauth_nonce=27886786&oauth_timestamp=1330098212&oauth_consumer_key=xxx&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=xxx&oauth_signature=xxx', 'transfer-encoding': 'chunked', 'vary': '*', 'server': 'Apache-Coyote/1.1', '-content-encoding': 'gzip', 'date': 'Fri, 24 Feb 2012 15:43:34 GMT', 'x-li-request-id': 'N368G241EA', 'x-li-format': 'xml', 'content-type': 'text/xml;charset=UTF-8'}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<id>UBAQYFeiHo</id>
<first-name></first-name>
<last-name>Private</last-name>
</person>
The python code to make the second part work with the oauth2 library is:
import oauth2 as oauth
import time
url = "http://api.linkedin.com/v1/people/UBAQYFeiHo:(id,first-name,last-name)"
consumer = oauth.Consumer(
key="xxx",
secret="xxx")
token = oauth.Token(
key="xxx",
secret="xxx")
client = oauth.Client(consumer, token)
resp, content = client.request(url, headers={'x-li-auth-token':'OUT_OF_NETWORK:wHti'})
print resp
print content

Categories

Resources