diff --git a/The-http-server.md b/The-http-server.md new file mode 100644 index 0000000..760c6e3 --- /dev/null +++ b/The-http-server.md @@ -0,0 +1,127 @@ +# Introduction + +Having a HTTP server in an IRC may be baffling, because it seems to be useless. +Yes, you are right, it is useless in most of the cases. But it may be cool. + +Some plugins (WebStats, Github, and PasteUfr are the only one, as far as I know) have their own embedded HTTP server. Unfortunately, a HTTP need a port, and consumes a few resources (CPU/memory). So, if you want many of these plugins, it becomes embarrassing. + +Anyway, if you are on this page, this is probably because you want to use this HTTP server, so I don't need to convince you. +Before reading further, be sure you have a Limnoria (or any Supybot fork that merged this HTTP server). + +# Using the HTTP server in a plugin + +Let's try to make a basic dictionary about Supybot! We'll call it Supystory. + +We want to get plain text information about Supybot, Gribble, and Limnoria when accessing http://localhost:8080/supystory/supybot, http://localhost:8080/supystory/gribble, and http://localhost:8080/supystory/limnoria, and an HTML error page if the page is not found + +### Importing the HTTP server + +Actually, you don't need to import anything more than the defaults, because the HTTP server is in `supybot.utils.httpserver`, and you probably have this line at the beginning of the `plugin.py`: +```python +import supybot.utils as utils +``` + +### Creating a callback + +If you are familiar with `BaseHTTPServer`, you will recognize the design, except you don't need to subclass BaseHTTPServer, because I've already done it in `supybot.utils.httpserver`. + +Now, you have to subclass `utils.httpserver.SupyHTTPServerCallback`. A callback is pretty much like an handler, but this is not an handler, because a callback is called by the handler. + +Here is how to do it: +```python +class SupystoryServerCallback(utils.httpserver.SupyHTTPServerCallback): + name = 'WebStats' +``` +Now, you have to register the callback, because the HTTP server does not know what subdirectory it should assign to your callback. Do it with adding a `__init__` to your plugin (read Supybot's docs about it, for more informations): +```python +class Supystory(callbacks.Plugin): + def __init__(self, irc): + # Some stuff needed by Supybot + self.__parent = super(Supystory, self) + callbacks.Plugin.__init__(self, irc) + + # registering the callback + callback = SupystoryServerCallback() # create an instance of the callback + utils.httpserver.hook('webstats', callback) # register the callback at `/webstats` +``` +By the way, don't forget to unhook your callback when unloading your plugin, unless what it will be impossible to reload the plugin! Append this code to the following: +```python + def die(self): + # unregister the callback + utils.httpserver.unhook('supystory') # unregister the callback hooked at /supystory + + # Stuff for Supybot + self.__parent.die() +``` + +Now, you can load your plugin, and you'll see on the server a beautiful link to `/supystory` called `Supystory`. + +### Overriding the default error message + +But our plugin does not do anything for the moment. If click the link, you'll get this message: +``` + This is a default response of the Supybot HTTP server. If you see this + message, it probably means you are developping a plugin, and you have + neither overriden this message or defined an handler for this query. +``` +That mean your browser sent a GET request, but you didn't teach your plugin how to handle it. First, we'll change this error message. +Here is a new code for your callback: +```python +class SupystoryServerCallback(utils.httpserver.SupyHTTPServerCallback): + name = 'Supystory' + defaultResponse = """ + This plugin handles only GET request, please don't use other requests.""" +``` +Now, you'll get your customized message. But your plugin still doesn't work. You need to implement the GET request. + +### Implementing the GET request + +As I said before, callbacks are pretty much like handlers. In order to handle GET requests, you have to implement a method called... `doGet` (if you used BaseHTTPServer, you will notice this is not do_GET, because doGet is more homogeneous with Supybot naming style: `doPrivmsg`, `doPing`, and so on). + +You will get the handler and the URI as arguments. The handler is a [BaseHTTPRequestHandler](http://docs.python.org/library/basehttpserver.html#BaseHTTPServer.BaseHTTPRequestHandler), and the URI is a string. + +Here is the code of the callback... pretty much simple, as ever: +```python +class SupystoryServerCallback(utils.httpserver.SupyHTTPServerCallback): + name = 'Supystory' + defaultResponse = """ + This plugin handles only GET request, please don't use other requests.""" + + def doGet(self, handler, path): + if path == '/supybot': + response = 'Supybot is the best IRC bot ever.' + elif path == '/gribble': + response = 'Thanks to Gribble, we have many bug fixes and SQLite 3 support' + elif path == '/limnoria': + response = 'Because of Limnoria, you need to internationalize your plugins and read this f*cking do.' + else: + response = None + if response is None: + handler.send_response(404) # Not found, as described by the HTTP protocol + handler.send_header('Content-type', 'text/html') # This is the MIME for HTML data + handler.end_headers() # We won't send more headers + handler.wfile.write(""" + +
++ The document could not be found. Try one of this links: + Supybot + Gribble + Limnoria +
+ + """) + else: + handler.send_response(404) # Not found, as described by the HTTP protocol + handler.send_header('Content-type', 'text/plain') # This is the MIME for plain text + handler.end_headers() # We won't send more headers + handler.wfile.write(response) +``` + +That's all ! + +You may not understand everything (I know I don't speak English very well); come on #limnoria at Freenode and ask for help of make suggestions! \ No newline at end of file