OK, thanks Matt. What I will do is maybe create an endpoint with the Flask framework, and accept only PUT requests on this endpoint. I’ll post back here when I’ve done this.
Maybe in the future caddy can limit certain requests.
I tried creating an endpoint with Flask, but the proxy did not work well since all of the data had to be passed forward to the server. Unfortunately for this application, Flask does some pre-processing. However, it is possible to build a proxy using the Python Twisted library. This worked well.
In the Caddyfile, I set up a proxy to pass all requests to the database /db to localhost:5001. This is simply a port that I chose. All requests to /_session are sent directly to the Couchdb server listening on port 5984. In the code below, /db is the name of the database. This will have to be changed to the name of the actual database being used.
I’ve adapted the code to only allow GET requests that are required for the PouchDb client-side library. Note that this code is running on the server and connects directly to localhost.
#!/usr/bin/env python
LISTEN_PORT = 5001
SERVER_PORT = 5984
SERVER_ADDR = "localhost"
from twisted.internet import protocol, reactor
"""
SOURCE: http://blog.laplante.io/2013/08/a-basic-man-in-the-middle-proxy-with-twisted/
Modified to ensure that some GET requests are not passed. These requests are simply dropped
without any indication.
"""
# Adapted from http://stackoverflow.com/a/15645169/221061
class ServerProtocol(protocol.Protocol):
def __init__(self):
self.buffer = None
self.client = None
def connectionMade(self):
factory = protocol.ClientFactory()
factory.protocol = ClientProtocol
factory.server = self
reactor.connectTCP(SERVER_ADDR, SERVER_PORT, factory)
# Client => Proxy
def dataReceived(self, data):
s = str(data, 'utf-8')
if s.startswith('GET'): # ensure that GET requests do not pass
if not s.startswith('GET /db/_local/'):
return
# by here, the GET request is permitted
if self.client:
self.client.write(data)
else:
self.buffer = data
# Proxy => Client
def write(self, data):
self.transport.write(data)
class ClientProtocol(protocol.Protocol):
def connectionMade(self):
self.factory.server.client = self
self.write(self.factory.server.buffer)
self.factory.server.buffer = ''
# Server => Proxy
def dataReceived(self, data):
self.factory.server.write(data)
# Proxy => Server
def write(self, data):
if data:
self.transport.write(data)
def main():
factory = protocol.ServerFactory()
factory.protocol = ServerProtocol
reactor.listenTCP(LISTEN_PORT, factory)
reactor.run()
if __name__ == '__main__':
main()
@thechriswalker: Yes, this does work! Along with the rewrite that you suggest, I had to also use:
proxy /database localhost:5984
One caveat, though, is associated with the use of the Pouchdb library. To prevent the library from reporting a failing database replication from the browser, I had to allow only some GET requests using my Python code. The Client => Proxy code was changed to the following:
# Client => Proxy
def dataReceived(self, data):
s = str(data, 'utf-8')
if s.startswith('GET'): # ensure that some GET requests do not pass
if not s.startswith('GET /smartstorm/_local/'): # pass these requests only
return
if s.startswith('DELETE'): # no delete requests should pass
return
if self.client:
self.client.write(data)
else:
self.buffer = data
I am assuming that something similar could be done with the rewrite directive as well.