Unable to view files in a browser with python http server - python

I am creating a python httpserver for a folder on remote machine using command :
python -m SimpleHTTPServer 9999
But I am unable to view a file in the browser using this. As soon as click on the link the file gets downloaded. Is there a way to create a server so that i can view the files in my browser only.

To make the browser open files inline instead of downloading them, you have to serve the files with the appropriate http Content headers.
What makes the content load inline in the browser tab, instead of as a download, is the header Content-Disposition: inline.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
To add these headers, you you can subclass the default SimpleHTTPRequestHandler with a custom one.
This is how it can be done using python 3. You have to modify the imports and maybe some other parts if you have to use python 2.
Put it in a executable script file which you can call myserver.py and run it like so: ./myserver.py 9999
#!/usr/bin/env python3
from http.server import SimpleHTTPRequestHandler, test
import argparse
class InlineHandler(SimpleHTTPRequestHandler):
def end_headers(self):
mimetype = self.guess_type(self.path)
is_file = not self.path.endswith('/')
# This part adds extra headers for some file types.
if is_file and mimetype in ['text/plain', 'application/octet-stream']:
self.send_header('Content-Type', 'text/plain')
self.send_header('Content-Disposition', 'inline')
super().end_headers()
# The following is based on the standard library implementation
# https://github.com/python/cpython/blob/3.6/Lib/http/server.py#L1195
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
test(InlineHandler, port=args.port, bind=args.bind)

Serving files like foo.sh should work fine using the SimpleHTTPServer. Using curl as the client, I get a HTTP response like this:
$ curl -v http://localhost:9999/so.sh
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9999 (#0)
> GET /so.sh HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:9999
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/2.7.6
< Date: Thu, 02 Jun 2016 07:28:57 GMT
< Content-type: text/x-sh
< Content-Length: 11
< Last-Modified: Thu, 02 Jun 2016 07:27:41 GMT
<
echo "foo"
* Closing connection 0
You see the header line
Content-type: text/x-sh
which is correct for the file foo.sh. The mapping from the file extensions sh to text/x-sh happens in /etc/mime.types on GNU/Linux systems. My browser
Chromium 50.0.2661.102
is able to display the file inline.
Summary: As long as you serve known files and your browser can display them inline, everything should work.

Related

HTTP request with browser "redirects" request without 302, but with Python Request library it 404's

I'm trying to fetch a page made with react with Python's request.get that returns 404.
import requests
page=requests.get("https://example.com/foo", allow_redirects=True)
print(page.status_code)
results in 404. I see that requests support HTTP 1.1 only.
With curl the URL returns 404 but then the server responds with a different page anyways. The server is using HTTP2. Here are some hints from curl -vv that seem relevant:
$ curl -v https://example.com/foo
* Trying 10.0.0.1
* TCP_NODELAY set
* Connected to example.com (10.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
[snip]
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
> GET /foo HTTP/2
> Host: example.com
> User-Agent: curl/7.61.1
> Accept: */*
[snip]
< HTTP/2 404
< date: Fri, 08 Apr 2022 08:42:34 GMT
< content-type: text/html; charset=utf-8
< cache-control: public, max-age=0, s-maxage=300
< etag: W/"a568501bae2318d9d0ca13a89359638e"
< last-modified: Fri, 10 Sep 2021 17:30:40 UTC
< strict-transport-security: max-age=315360000; includeSubdomains; preload
< vary: Accept-Encoding
< x-content-type-options: nosniff
< cf-cache-status: MISS
[snip some cloudflare stuff]
Then finally followed by the content of https://example.com/bar,
The headers of the response seem to indicate it "offers" http 1.1, so how do I ask for it it with the request library?
Searching, I see httpx as a http2 compliant library but their examples have snippets of async but assuming background knowledge of the same. Do I have to use httpx for http2 on Python 3.8 or greater? Is there a way to do this without async/await?
You can use httpx for http/2, you have a specific part of the doc explaining how to activate it, you need at least python 3.6, the doc suggest the use of async by it's examples.
pip install httpx[http2]
From the doc:
client = httpx.AsyncClient(http2=True)
but you can use it with the classic Client:
import httpx
client = httpx.Client(http2=True)
if __name__ == "__main__":
resp = client.get('https://example.com/foo')
print(resp.content)

how to get html file into code in python?

how to get html file into python code using socket. I was able to implement using the requests library. However, it needs to be rewritten to sockets. I don’t understand how. The implementation code through requests will be below. I will also leave pathetic attempts to implement via a socket using Google. However, the decision is not at all correct. ! (Help implement using sockets.
import requests
reg_get = requests.get("https://stackoverflow.blog/")
text = reg_get.text
print(text)
import socket
request = b"GET / HTTP/1.1\nHost: https://stackoverflow.blog/\n\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("https://stackoverflow.blog/", 80))
s.send(request)
result = s.recv(10000)
while (len(result) > 0):
print(result)
result = s.recv(10000)
After seeing the comments and listening to you. I have rewritten the following code. However, I never got the html. And I received information about the site. How do I get html structure in python
import socket
import ssl
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
request = "GET /r/AccidentalRenaissance/comments/8ciibe/mr_fluffies_betrayal/ HTTP/1.1\r\nHost: www.reddit.com\r\n\r\n"
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
s = context.wrap_socket(sock, server_hostname = "www.reddit.com")
s.connect(("www.reddit.com", 443))
s.sendall(request.encode())
contest = s.recv(1024).decode()
s.close()
print(contest)
result
HTTP/1.1 200 OK
Connection: keep-alive
Cache-control: private, s-maxage=0, max-age=0, must-revalidate, no-store
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Accept-Ranges: bytes
Date: Sun, 03 Oct 2021 03:34:25 GMT
Via: 1.1 varnish
Vary: Accept-Encoding, Accept-Encoding
A URL is composed of a protocol, a hostname, an optional port, and an optional path. In the URL http://stackoverflow.blog/ , https is the protocol, stackoverflow.blog is the hostname, and no port or path is provided. For http, the port defaults to 80 and the path defaults to /. When using sockets, first establish a connection to the host at the port using connect then send an HTTP command to retrieve the page on the path. The HTTP command to retrieve the page is "GET /" and receive the response from the server.
Note that I used http instead of https because https adds security set up and negotiation to the above that occurs once the connect is done but before the "GET /" is done. It is quite complicated and a good reason to use Requests instead of trying to implement it yourself. If you don't want to use Requests but don't want to go down to the level of sockets, take a look at urllib3

How exactly should I properly interrupt HTTP upload requests?

I developed a server app which, among others, handles uploading of large chunks of data. When the upload request starts, before even receiving the chunk, the server app performs a few checks in order for the client to abort the operation if something goes wrong, instead of finding that there is an issue only after he sends gigabytes of data.
When playing with the server app using curl, I discovered a strange behavior.
curl starts the request, being ready to stream the data.
The server responds immediately with a HTTP 403 to signal a problem and provides a JSON response with the details of the problem.
curl fails with exit code 18 and the following output:
curl: (18) transfer closed with 30 bytes remaining to read
When enabling verbose output, here's what I see:
$ curl -X PUT --limit-rate 2M http://127.0.0.1/blob -F files[]=#/tmp/tmp75hw30vc -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> PUT /blob HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 2439352842
> Content-Type: multipart/form-data; boundary=------------------------32c442f4cf8abe0c
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 403 FORBIDDEN
< Server: nginx/1.10.3
< Date: Sat, 29 Sep 2018 22:03:16 GMT
< Content-Type: application/json
< Content-Length: 30
< Connection: keep-alive
* HTTP error before end of send, stop sending
<
* transfer closed with 30 bytes remaining to read
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with 30 bytes remaining to read
On server side, the code (using Flask) is the following:
def receive_blob():
if _can_upload():
return flask.jsonify({"error": "already-uploading"}), 403
...
I'm not sure I understand if the problem is related to my way to use Flask, or to curl options I'm using.
What should I do to avoid this situation, i.e. to make curl display the JSON error message returned by the server?
The question is not a duplicate of How to handle "100 continue" HTTP message? since mine explicitly asks how to make curl display the JSON error message. The linked question invites to add --fail which would lead instead to the output “curl: (22) The requested URL returned error: 403 FORBIDDEN”

Reading text from website using sockets Python

I am trying to go to http://www.py4inf.com/code/romeo.txt, read the contents of romeo.txt and print them back out, am using python 3.6.1.
import socket
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('www.py4inf.com', 80))
mysock.send('GET http://www.py4inf.com/code/romeo.txt HTTP/1.0\n\n'.encode("utf8"))
while True:
data = mysock.recv(512)
if ( len(data) < 1 ) :
break
print (data.decode("utf8"))
mysock.close()
instead of the contents of the page it prints out
TTP/1.1 404 Not Found
Server: nginx
Date: Wed, 21 Jun 2017 03:00:15 GMT
Content-Type: text/html
Content-Length: 162
Connection: close
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html
Why is this? Thanks in advance
In theory, the Host header is only mandatory from HTTP 1.1 onwards, but it appears that particular server requires the Host header to be present, even for HTTP 1.0. I'm not sure if that's the default behaviour of Nginx, or whether the server admin's explicitly configured it that way.
In any case, try changing your request to the following:
mysock.send('GET http://www.py4inf.com/code/romeo.txt HTTP/1.0\nHost: www.py4inf.com\n\n'.encode("utf8"))
I can understand your confusion - IMHO, it should be returning 400 not 404 if it is insisting on the Host header being provided (since it's a client request issue, not a matter of the resource not existing).

Non blocking CherryPy does not receive anything

I am trying to run cherrypy with cherrypy.engine.start instead of cherrypy.quickstart. That's because I want to run cherrypy in non blocking state to start and stop a web server within my functional tests with py.test.
This works fine:
cherrypy.quickstart(WebServerTest(None), config=testconf)
The response to a curl is:
curl --head http://127.0.0.1:1026/index HTTP/1.1 200 OK
Date: Thu, 08 Aug 2013 12:54:37 GMT
Content-Length: 0
Content-Type: text/html;charset=utf-8
Server: CherryPy/3.2.2
But it's blocking the rest of the script to execute.
However this does not work:
testconf = path.join(path.dirname(__file__), 'webservertest.conf')
web_server = WebServerTest(None)
cherrypy.tree.mount(web_server, "", config=testconf)
cherrypy.engine.start()
time.sleep(60)
cherrypy.engine.stop()
The response to a curl is:
curl --head http://127.0.0.1:1026/index
curl: (7) couldn't connect to host
Adding cherrypy.engine.block() aftet cherrypy.engine.start does not solve the problem.
So how can I make it work with cherrypy.engine.start()?
The webservertest.conf config file is:
[global]
server.socket_host = "127.0.0.1"
server.socket_port = 1026
server.thread_pool = 10
You also need to pass the conf to cherrypy.config.update(conf). This is for global config (including your server host and port), whereas the tree.mount call only sets config for that particular app. Read the source code of quickstart to see all the gory details.

Categories