0

In a Twisted web server similar to this example code

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import reactor, endpoints
from twisted.web.static import File

root = Resource()
root.putChild(b"foo", File("/tmp"))
root.putChild(b"bar", File("/lost+found"))
root.putChild(b"baz", File("/opt"))

factory = Site(root)
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8880)
endpoint.listen(factory)
reactor.run()

How can you define a response for a multi segment path so that instead of the above example where a resource is available at

http://example.com/foo 

You want a resource to be available at, say,

http://example.com/europe/france/paris

And, in case it affects the answer the following URL's would not provide a response

http://example.com/europe/france
http://example.com/europe
http://example.com

I know the doco refers to the use of Resource Trees but the only examples given are single level trees which isn't too useful for my requirement.


RESOLUTION

With the help of @jean-paul-calderone I've written the following which does what I want.

For the question I had simplified my requirements a little , in fact I wanted to output something other than files and the code I've included below reflects that. I'm pretty sure that what I've written here isn't the best way of doing it but it does provide the multi segment URL I wanted and so might prove useful to others who have similar needs.

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import reactor, endpoints
from twisted.web.resource import NoResource

import html

class ContinentLevelResource(Resource):
    pass

class CountryLevelResource(Resource):
    pass

class CityLevelResource(Resource):
    def render_GET(self, request):
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title>City Page</title></head><body>"
                b"<p>This is the City page</p></body>")

root = Resource()
continent = ContinentLevelResource()
country = CountryLevelResource()
city = CityLevelResource()
#    
root.putChild(b"europe", continent)
continent.putChild(b"france", country)
country.putChild(b"paris", city)
#
factory = Site(root)
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8880)
endpoint.listen(factory)
reactor.run()
glaucon
  • 8,112
  • 9
  • 41
  • 63

1 Answers1

1

You merely apply the same API usage as appears in your question - recursively:

Given:

root = Resource()
tmp = File("/tmp")
root.putChild(b"foo", tmp)
lost_found = File("/lost+found")
root.putChild(b"bar", lost_found)
opt = File("/opt")
root.putChild(b"baz", opt)

You have /foo, /bar, and /baz. If you wantsome_resourceat/foo/quux` then:

tmp.putChild(b"quux", some_resource)

The same applies to add additional levels. Therefore:

root = Resource()
europe = Resource()
france = Resource()
paris = Resource()

root.putChild(b"europe", europe)
europe.putChild(b"france", france)
france.putChild(b"paris", paris)

There is not really any such thing as "not returning a response" in HTTP. However, you can make the intermediate resources return a 404 (not found) status if you want. Just replace the intermediate Resource instances with something that behaves as you desire (for example, a NotFound instance).

You may also want to investigate klein which implements the "route"-based resource hierarchy definition API which is often more common (in Python) these days.

Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122
  • Thank you Jean-Paul that was very useful. I've managed to write something which does, broadly, what I need on the basis of your answer. I've added that code to the question for the reference of others. Thanks for the Klein suggestion - I didn't know about that - I will take a look. – glaucon Jul 27 '18 at 06:07
  • I meant to say that I haven't yet addressed the need to return a 404 in the event he user requests, say ```europe/france```, and so Twisted returns a 405 which isn't ideal but I will try your suggestion about using a ```NotFound``` instance. – glaucon Jul 27 '18 at 06:14