Mulib/Examples
From Second Life Wiki
Contents |
Mulib Examples
These are some short examples to give a flavor of using mulib. You can find all the example code in the examples directory of a mulib checkout.
hello world
The following program will bring up a webserver listening on port 8080 which can respond to a single request, "GET /", with the response "hello, world":
# hello_world.py: from mulib import mu from eventlet import api, httpd class HelloWorld(mu.Resource): def handle_get(self, req): req.write("hello, world\n") if __name__ == "__main__": root = HelloWorld() httpd.server( api.tcp_listener(('0.0.0.0', 8080)), mu.SiteMap(root))
CGI
You can treat this resource as a CGI, by writing a second, wrapper, file that refers to it:
# hello_world.cgi: #!/usr/bin/python from mulib import cgiadapter cgiadapter.run_as_cgi('hello_world', 'HelloWorld')
Configure your web server to execute hello_world.cgi, and you should be able to interact with it just like the standalone version.
stacked
Stacked is a pure REST server, and you can use it to traverse native python objects like dicts. You invoke these special capabilities of Stacked by placing a python dict or list in the resource hierarchy instead of a mu.Resource, in this case at the root.
from mulib import mu from eventlet import api, httpd root = {'':'hello, world\n', 'other':"hello, other\n"} httpd.server(api.tcp_listener(('0.0.0.0', 8080)), mu.SiteMap(root))
You can then access this dictionary as a REST resource, e.g.
> curl http://localhost:8080/ hello, world > curl http://localhost:8080/other hello, other > curl -X PUT -d "the new data" http://localhost:8080/third > curl http://localhost:8080/third the new data
Mu/stacked can do content negotiation:
> curl -X PUT -H "Content-type: application/json" -d '{"hi": "there"}' http://localhost:8080/fourth
> curl http://localhost:8080/fourth/hi
there
> curl -H "Accept: application/json" http://localhost:8080/fourth
{'hi': 'there'}
Note: This means that anyone who has access to your stacked web service can modify the data in your process! In the future we might have a 'read-only' implementation.
Chat Server
EvilChuck wrote a nifty toy chat server that we will partially reproduce here at his permission.
Through stacked, and coros, we can tie a ChatMessage instance to the /listen url:
from mulib import mu, stacked, resources from eventlet import api, httpd, coros, util util.wrap_socket_with_coroutine_socket() chat_listener = coros.event() class ChatMessage(mu.Resource): """A Resource to handle receiving chat messages""" def handle_post(self, request): """POST to send chat messages expects nick and message as POST arguments""" nick = request.get_arg('nick') message = request.get_arg('message').strip() if message: # Send the chat message to all who are listening chat_listener.send("%s: %s" % (nick, message)) # Reset the listener so that it can receive more messages chat_listener.reset() request.write('') root = { 'chat' : resources.File('chat.html'), # /chat -> Load chat.html 'listen' : chat_listener, # /listen -> Wait for new msgs 'message' : ChatMessage(), # /message -> Send a message } # Start the server httpd.server( api.tcp_listener(('0.0.0.0', 5000)), mu.SiteMap(root), max_http_version="HTTP/1.0" )
And with some fancy html which is served up through the resources.File we get:
<html>
<head>
<link rel="stylesheet" href="http://yui.yahooapis.com/2.4.1/build/reset-fonts-grids/reset-fonts-grids.css" type="text/css">
<style>
#hd, #bd, #ft { border: 1px solid #808080;}
#hd h1 { margin-left: 5px; font-size: 120%; }
#bd { min-height: 350px; }
body { position: relative; }
.bd { text-align: left; }
input { width: 100%; }
</style>
</head>
<body>
<div id="doc" class="yui-t7">
<div id="hd"><h1>Web Chat</h1></div>
<div id="bd">
<div class="yui-g">
<div id="chat_text"></div>
</div>
</div>
<div id="ft">
<input type="text" maxlength="256" id="chat_message"/>
</div>
</body>
<script src="http://yui.yahooapis.com/2.4.1/build/yahoo/yahoo-min.js"></script>
<script src="http://yui.yahooapis.com/2.4.1/build/event/event-min.js" ></script>
<script src="http://yui.yahooapis.com/2.4.1/build/dom/dom-min.js"></script>
<script src="http://yui.yahooapis.com/2.4.1/build/connection/connection-min.js"></script>
<script type="text/javascript">
YAHOO.util.Event.onDOMReady(function () {
// Set up a random nickname for the user
var nick = 'User-' + Math.round(Math.random()*10000);
var handleReturn = function (type, args, obj) {
// Handler to run when the Enter key is pressed
var el = document.getElementById("chat_message");
if (el.value) {
// Send the message to the server
var postData = "nick="+escape(nick)+"&message="+escape(el.value);
YAHOO.util.Connect.asyncRequest('POST','/message', {
success : function(o) {
el.value = '';
},
}, postData);
}
};
var listen = function () {
// Listens for incoming messages
YAHOO.util.Connect.asyncRequest('GET', '/listen', {
success: function (o) {
if (o.status == 200) {
var el = document.getElementById("chat_text");
el.innerHTML += o.responseText + '<BR/>';
listen();
}
else if (o.status == 202) {
// 202 is returned when the connection has timed out
listen();
}
}
});
};
// Register the key listener to listen for the Enter key
var keyListener = new YAHOO.util.KeyListener(
document, {keys: 13}, handleReturn);
keyListener.enable();
// Start listening for incoming messages
listen();
});
</script>
</html>
To test it out, put both files in the same directory, launch chat.py, and browse to http://localhost:5000/chat

