Curl "Connection Refused" when connecting to server with external IP address - python

I've created a RESTful server and I can successfully make requests and get responses when I use my local IP address. I would like it to be exposed externally to the internet. I set up a port forwarding rule but I cannot seem to get things working. From what I'm reading the "Connection Refused" with (7) means something is blocking it whether it's a firewall or ISP issue. Any ideas on what to do?
Here's my curl command that works with my local IP:
Pako-2:Pokebot pako$ curl -X GET http://192.168.1.8:30000/api/v1/getrecord/test
{"data": [{"id": 1, "title": "learn python"}, {"id": 2, "title": "get paid"}]}
This is what I see when I try using my external IP address given to me by What's my ip
Pako-2:Pokebot pako$ curl -X GET http://MyIpHere:30000/api/v1/getrecord/test
curl: (7) Failed to connect to myIPAddress port 30000: Connection refused
Here's the port forwarding rule I made in my router/modem
Here are my router/modem advanced settings options. I tried tweaking my firewall settings, but no luck. I tried with NAT only and also with Low Security Level with all ports checked.
-----------------Edit-----------------
Here is the port forwarding screen, should I just set 30000 as the begin and end?
Here is some python code for my server:
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
import threading
import argparse
import re
import cgi
import json
TODOS = [
{'id': 1, 'title': 'learn python'},
{'id': 2, 'title': 'get paid'},
]
class LocalData(object):
records = {}
class HTTPRequestHandler(BaseHTTPRequestHandler):
print "HTTPRequestHandler BaseHTTPRequestHandler = ", BaseHTTPRequestHandler
def do_POST(self):
print "do_POST"
if None != re.search('/api/v1/addrecord/*', self.path):
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
if ctype == 'application/json':
length = int(self.headers.getheader('content-length'))
data = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
recordID = self.path.split('/')[-1]
LocalData.records[recordID] = data
print "record %s is added successfully" % recordID
else:
data = {}
self.send_response(200)
self.end_headers()
else:
self.send_response(403)
self.send_header('Content-Type', 'application/json')
self.end_headers()
return
def do_GET(self):
print "do_GET"
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({'data': TODOS}))
return
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
allow_reuse_address = True
def shutdown(self):
self.socket.close()
HTTPServer.shutdown(self)
class SimpleHttpServer():
def __init__(self, ip, port):
self.server = ThreadedHTTPServer((ip, port), HTTPRequestHandler)
def start(self):
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True
self.server_thread.start()
def waitForThread(self):
self.server_thread.join()
def addRecord(self, recordID, jsonEncodedRecord):
LocalData.records[recordID] = jsonEncodedRecord
def stop(self):
self.server.shutdown()
self.waitForThread()
if __name__ == '__main__':
server = SimpleHttpServer("0.0.0.0", 30000)
print 'HTTP Server Running...........'
server.start()
server.waitForThread()
------------------- Edit 2--------------------
Tried only having 1 port, not a begin/end range and it's expecting a range...

I find it strange that you'd be getting connection refused from within the local network because the router should be able to detect that the external IP is a reference to itself.
That ability may depend on the router, but that's been my experience with networking.
That being said, if you'd like to test the external firewall after playing with the port forwarding rules, then you need a device not on the local network.
If another device like a smart phone with a cell plan isn't available to you, then you could try to use CanYouSeeMe to test if the port is being opened.
You may also want to set DHCP reservation for your server. In other words, set a static IP address.

Related

Communicating with a server on a device using that server as a wifi hotspot

I have some python server code running on a laptop, and am trying to send GET/POST to the server's URL. This works fine using a python client or browser on the laptop, as well as on other devices connected to my home network.
However, I'd like to now use that laptop as a wifi hotspot, and connect to the server using devices that are connecting to that hotspot; for example, connecting to the hotspot on a phone, opening a browser, and entering the IP address and port into the address bar (eg http://192.181.1.XXX:80XX/example).
The URL fails to resolve only on devices using the server as a hotspot; switching back to the normal wifi network without changing the URL then successfully resolves, and a browser on the server itself can access it fine.
Do I need to change the URL in some manner when using the target server as a wifi hotspot, or is there something else I'm missing? Or is this actually impossible due to some networking reasons?
In case it's relevant, here is the server code:
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import cgi
import time
hostName = "192.168.1.XXX"
serverPort = 80XX
class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
data = json.dumps({'wifi period': '60', 'poll period': '1', '#':'1'})
self.wfile.write(bytes(data, "utf-8"))
def do_POST(self):
if hasattr(self.headers, 'getheader'):
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
print(ctype)
if ctype == 'application/x-www-form-urlencoded':
#todo: save file
return
if ctype != 'application/json':
print("invalid ctype.")
print(ctype)
self.send_response(400)
self.end_headers()
return
length = int(self.headers.getheader('content-length'))
else:
header = self.headers.get('content-type')
if header == 'application/x-www-form-urlencoded':
length = int(self.headers.get('content-length'))
content = self.rfile.read(length)
#TODO: save file
self.send_response(200)
self.end_headers()
return
elif header != 'application/json':
print(f"Header: {header}")
self.send_response(400)
self.end_headers()
return
length = int(self.headers.get('content-length'))
# read the message and convert it into a python dictionary
message = json.loads(self.rfile.read(length))
print(message)
self.send_response(200)
self.end_headers()
return
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")

Python HTTP server keep-alive

How can I keep my Python HTTP server connected(streaming) to my browser in real time?
(Update image to infinity) Like raspberry pi's motion eye
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
def _set_response(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.send_header("Connection", "keep-alive")
self.send_header("keep-alive", "timeout=999999, max=99999")
self.end_headers()
def do_GET(self):
#self.send_response(204)
#self.end_headers()
if self.path == '/':
self.path = 'abc.jpg'
return http.server.SimpleHTTPRequestHandler.do_GET(self)
# Create an object of the above class
handler_object = MyHttpRequestHandler
PORT = 8000
my_server = socketserver.TCPServer(("", PORT), handler_object)
# Star the server
my_server.serve_forever()
Just keep writing, as in:
while True:
self.wfile.write(b"data")
This however won't get you into eventstream / server sent events territory, without using helper external libraries, as far as I'm aware.
I came across the same issue, I then found by chance (after much debugging) that you need to send linebreaks (\r\n or \n\n) to have the packets sent:
import http.server
import time
class MyHttpRequestHandler(http.server.BaseHTTPRequestHandler):
value = 0
# One can also set protocol_version = 'HTTP/1.1' here
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.send_header("Connection", "keep-alive")
self.end_headers()
while True:
self.wfile.write(str(self.value).encode())
self.wfile.write(b'\r\n') # Or \n\n, necessary to flush
self.value += 1
time.sleep(1)
PORT = 8000
my_server = http.server.HTTPServer(("", PORT), MyHttpRequestHandler)
# Start the server
my_server.serve_forever()
This enables you to send Server-sent Events (SSE) or HTTP long poll, or even json/raw http streams with the http.server library.
As the comment in the code says, you can also set the protocol version to HTTP/1.1 to enable keepalive by default. If you do so, you will have to specify Content-Length for every sent packet, otherwise the connection will never be terminated.
It is probably best to combine this with a threaded server to allow concurrent connections, as well as maybe setting a keepalive on the socket itself.

How to send URL back as reply by web server in python

I have written this HTTP web server in python which simply sends reply "Website Coming Soon!" to the browser/client, but I want that this web server should sends back the URL given by the client, like if I write
http://localhost:13555/ChessBoard_x16_y16.bmp
then server should reply back the same url instead of "Website Coming Soon!" message.
please tell how can I do this?
Server Code:
import sys
import http.server
from http.server import HTTPServer
from http.server import SimpleHTTPRequestHandler
#import usb.core
class MyHandler(SimpleHTTPRequestHandler): #handles client requests (by me)
#def init(self,req,client_addr,server):
# SimpleHTTPRequestHandler.__init__(self,req,client_addr,server)
def do_GET(self):
response="Website Coming Soon!"
self.send_response(200)
self.send_header("Content-type", "application/json;charset=utf-8")
self.send_header("Content-length", len(response))
self.end_headers()
self.wfile.write(response.encode("utf-8"))
self.wfile.flush()
print(response)
HandlerClass = MyHandler
Protocol = "HTTP/1.1"
port = 13555
server_address = ('localhost', port)
HandlerClass.protocol_version = Protocol
try:
httpd = HTTPServer(server_address, MyHandler)
print ("Server Started")
httpd.serve_forever()
except:
print('Shutting down server due to some problems!')
httpd.socket.close()
You can do what you're asking, sort of, but it's a little complicated.
When a client (e.g., a web browser) connects to your web server, it sends a request that look like this:
GET /ChessBoard_x16_y16.bmp HTTP/1.1
Host: localhost:13555
This assumes your client is using HTTP/1.1, which is likely true of anything you'll find these days. If you expect HTTP/1.0 or earlier clients, life is much more difficult because there is no Host: header.
Using the value of the Host header and the path passed as an argument to the GET request, you can construct a URL that in many cases will match the URL the client was using.
But it won't necessarily match in all cases:
There may be a proxy in between the client and your server, in which case both the path and hostname/port seen by your code may be different from that used by the client.
There may be packet manipulation rules in place that modify the destination ip address and/or port, so that the connection seen by your code does not match the parameters used by the client.
In your do_GET method, you can access request headers via the
self.headers attribute and the request path via self.path. For example:
def do_GET(self):
response='http://%s/%s' % (self.headers['host'],
self.path)

What port to use on heroku python app

So I have created 2 iOS apps (One sends coordinates, one receives them) and a python server. One of the apps sends GPS coordinates to my python server that is hosted on heroku. The server will then emit the received GPS coordinate to the OTHER iOS client app that will drop an Apple Maps pin on the received coordinate.
The project works perfectly while testing on local host with any specified port. However when I have migrated the server to Heroku I was receiving this error The error occurs because Heroku sets it's own port for you to use, where as my code was specifying which port to use. I have been browsing SO for numerous hours trying to implement other peoples solutions where they use os.environ["PORT"] and so on, however due to my novice Python and Twisted skills I haven't succeeded in getting the iOS apps to properly communicate with the Heroku server on the right port. My code for my server is below: (note: I am using Twisted)
import os
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
class IphoneChat(Protocol):
def connectionMade(self):
#self.transport.write("""connected""")
self.factory.clients.append(self)
print "clients are ", self.factory.clients
def connectionLost(self, reason):
self.factory.clients.remove(self)
def dataReceived(self, data):
#print "data is ", data
a = data.split(':')
if len(a) > 1:
command = a[0]
content = a[1]
msg = ""
if command == "new":
self.name = content
msg = content
elif command == "msg":
msg = self.name + ": " + content
print msg
for c in self.factory.clients:
c.message(msg)
def message(self, message):
self.transport.write(message + '\n')
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
port = 3000
reactor.listenTCP(port, factory)
print "Iphone Chat server started on port ", port
reactor.run()
Heroku have a section in your settings where you can define environment variables.
I have a similar situation when running Django locally, but a similar fix may help you.
In heroku dashboard, select your app and then click the settings tab.
Then if you click reveal config vars and add the key name ON_HEROKU (or something similar if you prefer) with the value True.
Then in your python:
import os
ON_HEROKU = os.environ.get('ON_HEROKU')
if ON_HEROKU:
# get the heroku port
port = int(os.environ.get('PORT', 17995)) # as per OP comments default is 17995
else:
port = 3000
I'm not 100% sure if get('PORT') would be correct, I'm doing this off the top of my head.
Implementing it into your own code would involve something like:
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
import os
ON_HEROKU = os.environ.get('ON_HEROKU')
if ON_HEROKU:
# get the heroku port
port = int(os.environ.get("PORT", 17995)) # as per OP comments default is 17995
else:
port = 3000
reactor.listenTCP(port, factory)
print "Iphone Chat server started on port %s" % port
reactor.run()
The answer is the following. The port is set by Heroku in the environment variables, and in this example 17995 is used only locally when the PORT environment variable is absent (on local).
port = int(os.environ.get("PORT", 17995))
app.run(host='0.0.0.0', port=port)
Source: https://blog.heroku.com/python_and_django

Socket Server in Python

I am trying to set up a local server so that other PCs on the same local network can connect to it. When trying to do so, on the client side, I get the following error:
[Errno 10061] No connection could be made because the target machine actively refused it
I have been searching around for hours and still couldn't resolve this issue. I tried turning off my Firewall too, but nothing.
These are my server and client codes:
Server Code:
import socket
import threading
import SocketServer
import datetime
ver_codes = []
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print threading.current_thread().isDaemon()
data = self.request.recv(1024)
command = data.split()[0]
if(command=="login"):
if(logged_in(data.split()[1])==False):
self.request.sendall(login(data.split()[1], data.split()[2]))
else:
self.request.sendall("already in")
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
def client(ip, port, message):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
try:
sock.sendall(message)
response = sock.recv(1024)
print "Received: {}".format(response)
finally:
sock.close()
def logged_in(id_num):
for i in ver_codes:
if(i[0]==id_num):
return True
return False
def login(username, password):
login_file = open("Login.txt", "r")
match = login_file.readline()
while(match!="*"):
if(match.split()[0]==username):
if(match.split()[1]==password):
ver_codes.append([match.split()[0], encryption_code(match.split()[2])])
login_file.close()
return "{} {}".format(match.split()[2], encryption_code(match.split()[2]))
print "And Here"
match = login_file.readline()
return "Denied"
login_file.close()
def encryption_code(to_encrypt):
now = datetime.datetime.now()
return int(str(now.microsecond)) * int(to_encrypt)
if __name__ == "__main__":
HOST, PORT = "localhost", 7274
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
print server.server_address
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = False
server_thread.start()
print "Server loop running in thread:", server_thread.name
Client Code:
import socket
import sys
HOST, PORT = "localhost", 7274
data = " ".join(sys.argv[1:])
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((HOST, PORT))
sock.sendall("login mahdiNolan m1373")
received = sock.recv(1024)
finally:
sock.close()
I really appreciate any help you could give me!
Thanks A LOT beforehand!
Your issue is because you're listening on localhost - this will only accept connections from the local machine.
If you want to accept connections from anywhere, instead of "localhost" just pass the empty string "". This is equivalent to specifying INADDR_ANY to the C sockets API - see the ip man page for more information, or this page also looks like it has some useful explanation. In short, this means "accept connections on any local interface".
Instead of the empty string you can instead specify an IP address of a local interface to only accept connections on that interface - it's unlikely you need to do this unless you machine has multiple network cards inside it (e.g. acting as a gateway) and you only want to serve requests on one of the networks.
Also, on the client side you should use the actual address of the machine - replace "localhost" with the IP address or hostname of the server machine. For example, something like "192.168.0.99". If you want to find the IP address of the server under Windows, open a DOS window and run the ipconfig command, look for the line with IPv4 Address (assuming you've got an IPv4 network which is very likely).
The Windows firewall will also block the server from accepting connections as you've already found, but you shouldn't need to disable it - as soon as you run your server you should see a popup window where you can instruct it to accept connections (that was on Windows 7, it might be different on other versions). In any case, turning the software firewall off should allow everything to work, although whether that's a security risk is a matter outside of the scope of this question.

Categories

Resources