Difference between revisions of "Mulib/Examples"

From Second Life Wiki
Jump to: navigation, search
(New page: = Mulib Examples = These are some short examples to give a flavor of using mulib. == hello world == The following program will bring up a webserver listening on port 8080 which can resp...)
 
 
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
= Mulib Examples =
 
= Mulib Examples =
  
These are some short examples to give a flavor of using mulib.
+
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 ==
 
== hello world ==
Line 7: Line 7:
 
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":
 
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":
  
from mulib import mu
+
<syntaxhighlight lang="python"># hello_world.py:
+
from mulib import mu
from eventlet import api, httpd
+
 
+
from eventlet import api, httpd
class HelloWorld(mu.Resource):
+
 
 +
class HelloWorld(mu.Resource):
 
     def handle_get(self, req):
 
     def handle_get(self, req):
 
         req.write("hello, world\n")
 
         req.write("hello, world\n")
 
root = HelloWorld()
 
 
httpd.server(
 
    api.tcp_listener(('0.0.0.0', 8080)),
 
    mu.SiteMap(root))
 
  
 +
 +
if __name__ == "__main__":
 +
    root = HelloWorld()
 +
 +
    httpd.server(
 +
        api.tcp_listener(('0.0.0.0', 8080)),
 +
        mu.SiteMap(root))</syntaxhighlight>
 +
 +
=== CGI ===
 +
You can treat this resource as a [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI], by writing a second, wrapper, file that refers to it:
 +
 +
<syntaxhighlight lang="python"># hello_world.cgi:
 +
#!/usr/bin/python
 +
from mulib import cgiadapter
 +
 +
cgiadapter.run_as_cgi('hello_world', 'HelloWorld')</syntaxhighlight>
 +
 +
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 ==
  
Stacked is a pure REST server, and you can use it to traverse native python objects like <code>dicts</code>.
+
Stacked is a pure REST server, and you can use it to traverse native python objects like <code>dicts</code>.  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
+
<syntaxhighlight lang="python">from mulib import mu
+
 
from eventlet import api, httpd
+
from eventlet import api, httpd
+
 
root = {'':'hello, world\n',
+
root = {'':'hello, world\n',
        'other':"hello, other\n"}
+
        'other':"hello, other\n"}
+
 
httpd.server(api.tcp_listener(('0.0.0.0', 8080)), mu.SiteMap(root))
+
httpd.server(api.tcp_listener(('0.0.0.0', 8080)), mu.SiteMap(root))</syntaxhighlight>
  
 
You can then access this dictionary as a REST resource, e.g.
 
You can then access this dictionary as a REST resource, e.g.
 +
<pre><nowiki>
 
  > curl http://localhost:8080/     
 
  > curl http://localhost:8080/     
 
  hello, world
 
  hello, world
Line 43: Line 57:
 
  > curl http://localhost:8080/third
 
  > curl http://localhost:8080/third
 
  the new data
 
  the new data
 +
</nowiki></pre>
  
 
Mu/stacked can do content negotiation:
 
Mu/stacked can do content negotiation:
  
 +
<pre><nowiki>
 
  > curl -X PUT -H "Content-type: application/json" -d '{"hi": "there"}' http://localhost:8080/fourth
 
  > curl -X PUT -H "Content-type: application/json" -d '{"hi": "there"}' http://localhost:8080/fourth
 
  > curl http://localhost:8080/fourth/hi
 
  > curl http://localhost:8080/fourth/hi
Line 51: Line 67:
 
  > curl -H "Accept: application/json" http://localhost:8080/fourth
 
  > curl -H "Accept: application/json" http://localhost:8080/fourth
 
  {'hi': 'there'}
 
  {'hi': 'there'}
 +
</nowiki></pre>
  
 
'''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.
 
'''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 ==
 +
[http://www.evilchuck.com/ EvilChuck] wrote a nifty [http://www.evilchuck.com/2008/02/toy-chat-server-with-eventlet-and-mulib.html toy chat server] that we will partially reproduce here at his permission.
 +
 +
Through stacked, and coros, we can tie a <code>ChatMessage</code> instance to the ''/listen'' url:
 +
 +
<syntaxhighlight lang="python">
 +
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"
 +
)
 +
</syntaxhighlight>
 +
 +
And with some fancy html which is served up through the <code>resources.File</code> we get:
 +
 +
<pre>
 +
<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>
 +
</pre>
 +
 +
To test it out, put both files in the same directory, launch chat.py, and browse to http://localhost:5000/chat

Latest revision as of 08:39, 27 June 2017

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