get server address from bottle - python

Is it possible to get the IP Address of the server programmaticaly in a bottle request?
I need to return a link to a file on the server inside a request and need to know the IP. Bottle will be started on a server with different IPs and all of these IPs will be serve requests.
currently i looks like this:
from bottle import *
import json
#get('/file')
def getAFileLink():
# some logic here for the right filename to return
# server runs now on e.g. 10.0.0.1 and 10.10.0.1
# every client should see the IP from the server in the right subnet
return json.dumps({'url': 'http://127.0.0.1:1337/some/file.abc'})
#route('/some/<filename>')
def getStaticFile(filename):
return static_file(filename, root="/srv/static/files")
if __name__ == "__main__":
run(host='0.0.0.0', port=1337)

If your servers aren't behind a load balancer, just use the Host HTTP header.
#route('/file')
def getAFileLink():
host = bottle.request.get_header('host')
return {'url': 'http://{}/some/file.abc'.format(host)}

Give a try to bottle.request.url (docs).
In case you need only scheme and hostname, use urlparse to get it.

you can use:
from bottle import request
urlparts = request.urlparts
print urlparts.scheme
print urlparts.netloc
docs

Why are you running your server on the same ip as the link that you return?
import socket
socket.gethostbyname(socket.gethostname())

Related

How do I make a Python server public?

Using this code:
from http import server
class Serv(server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
server.HTTPServer(('host', 80), Serv).serve_forever()
I have tried using my public IP, which didn't work, my private IP, which only worked from the same network, and localhost, which is my PC only. How can I change the host so when someone connects to my IP, it connects to my website (from my code)? Do I need my router to redirect to my PC? I know host can be my private IP or localhost, are there any other hosts I can use?
Edit: I saw an answer in another question that used Flask, I'd like to not use any dependencies as of now.
If you want to make it work from anywhere,
your application should listen on IP address 0.0.0.0
which means (in short) any IPv4 address at all :
from http import server
class Serv(server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b'Hello, world!')
server.HTTPServer(('', 80), Serv).serve_forever()
If you are in a recent linux/GNU based OS, you may check listening ports with :
ss -ltpn
Note that after that modification, you will depend of any proxy/firewall between you and your server to get your application response.

I can't connect to my Bottle server on external devices

I'm trying to get a Bottle server in Python to work. Here is my code:
from bottle import route, run, template
from socket import gethostname, gethostbyname
from time import sleep
ip = str(gethostbyname(gethostname()))
#route('/')
def index():
return 'Hello World!'
run(host=ip, port=1234)
I run this, and on my computer where I'm running it, I navigate to http://127.0.1.1:1234/, and my website shows up, with Hello World!.
However, if I try to connect to it on my phone or my sister's Chromebook, it says that the website refused to connect.
I have tried replacing str(gethostbyname(gethostname())) with '0.0.0.0' and 'localhost', but none have worked.
Get rid of this line; it's not necessary:
ip = str(gethostbyname(gethostname()))
Make your run line look like this:
run(host='0.0.0.0', port=1234)
The address 0.0.0.0 means "listen on all addresses".
Lastly, figure out the network address of the host on which your app is running. Then other devices on the same network should be able to connect to <that ip address>:1234. Devices not on the same network would only be able to connect to the service if you had a publicly routeable address (or if you arranged to forward the appropriate port from a router that has a public address).
You'll want to make sure the system on which your app is running doesn't have firewall rules that would prevent an otherwise successful connection.

Add https redirect in Python3 Mockserver

we have this mockserver that is now serving https:// requests, and if we remove the ssl wrapping (ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True), the server only serves http:// requests. Is there any way where we can make this server to support both requests. Our objective is when the server receives an http:// request, it will automatically convert it as https:// and process the request.
Thanks in advance for the support
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl
class Mock(BaseHTTPRequestHandler):
-------------------
-------------------
def main():
global hostname, port
hostname = "127.0.0.1"
port = 8000
myServer = HTTPServer((hostname, port), Mock)
myServer.socket = ssl.wrap_socket(myServer.socket,keyfile='key.pem',certfile= 'cert.pem', server_side=True)
myServer.serve_forever()
if __name__ =="__main__":
main()
If the HTTP and HTTPS servers need different functionality, then it makes sense to make them two different instances. Why not make a second HTTPServer that is only HTTP that simply returns a 302 status with the Location header pointing to the HTTPS mock server (but using the same path).

How to get CherryPy to listen only on a specific host

I have a flask app that I want to deploy using CherryPy's built in server. I chose CherryPy so that the app can be deployed without having to reverse proxy (ie. nginx in front).
I'm having trouble getting CherryPy to listen for requests on just a single hostname.
Say I'm serving 2 sites: test1.com and test2.com (and have them set in my hosts file to point back to localhost).
My /etc/hosts file:
127.0.0.1 test1.com test2.com
CherryPy is serving test1.com, test2.com doesn't have anything serving it.
My cherrypy file is as follows:
import cherrypy
from my_test_flask_app import app
if __name__ == '__main__':
cherrypy.tree.graft(app, "/")
cherrypy.server.unsubscribe()
server = cherrypy._cpserver.Server()
server.socket_host = "test1.com"
server.socket_port = 8030
server.thread_pool = 30
server.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
Set up this way, I go to test1.com:8030 on my browser and it works as expected.
But when I go to test2.com:8030, the same app is served. I expected it not to serve anything, since CherryPy isn't set up to listen for test2.com.
To me, it seems that CherryPy is just listening for everything on the given port (8030), and treating the socket_host part as if its 0.0.0.0
Am I missing something here? I've looked through lots of docs and tutorials, but all things suggest that this code snippet should be working as I expected.
Thanks
Here's how you can setup what you want...
root = Root()
RootApp = cherrypy.Application(root)
Domain2App = cherrypy.Application(root)
SecureApp = cherrypy.Application(Secure())
vhost = cherrypy._cpwsgi.VirtualHost(RootApp,
domains={'www.domain2.example': Domain2App,
'www.domain2.example:443': SecureApp,
})
cherrypy.tree.graft(vhost)
https://cherrypy.readthedocs.org/en/3.3.0/refman/_cpwsgi.html#classes
Hope this helps!
You misunderstand the socket listen address - they are IP addresses only, not on DNS names. Set this way, CherryPy listens to the localhost (127.0.0.1) only - try using your Ethernet/Wlan local address and you should get connection refused.
Also, you can wrap your application with a WSGI middleware that checks the Host header for the proper domain, or use CherryPy virtual host facility to check the host header.

How to get client IP address using python bottle framework

I need client IP address using python. I have tried below code but its not working in server:
from socket import gethostname, gethostbyname
ip = gethostbyname(gethostname())
print ip
On the server, I get '127.0.0.1' every time. Is there any way to find IP address of the client?
You're getting the IP address of your server, not of your server's clients.
You want to look at the request's REMOTE_ADDR, like this:
from bottle import Bottle, request
app = Bottle()
#app.route('/hello')
def hello():
client_ip = request.environ.get('REMOTE_ADDR')
return ['Your IP is: {}\n'.format(client_ip)]
app.run(host='0.0.0.0', port=8080)
EDIT #1: Some folks have observed that, for them, the value of REMOTE_ADDR is always the same IP address (usually 127.0.0.1). This is because they're behind a proxy (or load balancer). In this case, the client's original IP address is typically stored in header HTTP_X_FORWARDED_FOR. The following code will work in either case:
#app.route('/hello')
def hello():
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
return ['Your IP is: {}\n'.format(client_ip)]
EDIT #2: Thanks to #ArtOfWarfare's comment, I learned that REMOTE_ADDR is not required per PEP-333. Couple of observations:
The CGI spec does require REMOTE_ADDR:
The REMOTE_ADDR variable MUST be set to the network address of the client sending the request to the server.
However, PEP-333 does not explicitly require HTTP_REMOTE_ADDR, only going as far as this (emphasis mine):
A server or gateway SHOULD attempt to provide as many other CGI variables as are applicable.
All of the (admittedly few) web frameworks that I'm familiar with set HTTP_REMOTE_ADDR. AFAICT, it's a de facto "standard." But technically, YMMV.
Server might be behind a proxy. Use this for proxy and forward support:
request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
If you're trying to get the external IP, you will need to get it from an external source, i.e whatismyip.com or somewhere that offers an api. If that's what you're looking for, take a look at the Requests module http://docs.python-requests.org/

Categories