~0x44/nova/bug838466

« back to all changes in this revision

Viewing changes to vendor/tornado/demos/chat/chatdemo.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
# Copyright 2009 Facebook
 
4
#
 
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 
6
# not use this file except in compliance with the License. You may obtain
 
7
# a copy of the License at
 
8
#
 
9
#     http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
11
# Unless required by applicable law or agreed to in writing, software
 
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
14
# License for the specific language governing permissions and limitations
 
15
# under the License.
 
16
 
 
17
import logging
 
18
import tornado.auth
 
19
import tornado.escape
 
20
import tornado.httpserver
 
21
import tornado.ioloop
 
22
import tornado.options
 
23
import tornado.web
 
24
import os.path
 
25
import uuid
 
26
 
 
27
from tornado.options import define, options
 
28
 
 
29
define("port", default=8888, help="run on the given port", type=int)
 
30
 
 
31
 
 
32
class Application(tornado.web.Application):
 
33
    def __init__(self):
 
34
        handlers = [
 
35
            (r"/", MainHandler),
 
36
            (r"/auth/login", AuthLoginHandler),
 
37
            (r"/auth/logout", AuthLogoutHandler),
 
38
            (r"/a/message/new", MessageNewHandler),
 
39
            (r"/a/message/updates", MessageUpdatesHandler),
 
40
        ]
 
41
        settings = dict(
 
42
            cookie_secret="43oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
 
43
            login_url="/auth/login",
 
44
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
 
45
            static_path=os.path.join(os.path.dirname(__file__), "static"),
 
46
            xsrf_cookies=True,
 
47
        )
 
48
        tornado.web.Application.__init__(self, handlers, **settings)
 
49
 
 
50
 
 
51
class BaseHandler(tornado.web.RequestHandler):
 
52
    def get_current_user(self):
 
53
        user_json = self.get_secure_cookie("user")
 
54
        if not user_json: return None
 
55
        return tornado.escape.json_decode(user_json)
 
56
 
 
57
 
 
58
class MainHandler(BaseHandler):
 
59
    @tornado.web.authenticated
 
60
    def get(self):
 
61
        self.render("index.html", messages=MessageMixin.cache)
 
62
 
 
63
 
 
64
class MessageMixin(object):
 
65
    waiters = []
 
66
    cache = []
 
67
    cache_size = 200
 
68
 
 
69
    def wait_for_messages(self, callback, cursor=None):
 
70
        cls = MessageMixin
 
71
        if cursor:
 
72
            index = 0
 
73
            for i in xrange(len(cls.cache)):
 
74
                index = len(cls.cache) - i - 1
 
75
                if cls.cache[index]["id"] == cursor: break
 
76
            recent = cls.cache[index + 1:]
 
77
            if recent:
 
78
                callback(recent)
 
79
                return
 
80
        cls.waiters.append(callback)
 
81
 
 
82
    def new_messages(self, messages):
 
83
        cls = MessageMixin
 
84
        logging.info("Sending new message to %r listeners", len(cls.waiters))
 
85
        for callback in cls.waiters:
 
86
            try:
 
87
                callback(messages)
 
88
            except:
 
89
                logging.error("Error in waiter callback", exc_info=True)
 
90
        cls.waiters = []
 
91
        cls.cache.extend(messages)
 
92
        if len(cls.cache) > self.cache_size:
 
93
            cls.cache = cls.cache[-self.cache_size:]
 
94
 
 
95
 
 
96
class MessageNewHandler(BaseHandler, MessageMixin):
 
97
    @tornado.web.authenticated
 
98
    def post(self):
 
99
        message = {
 
100
            "id": str(uuid.uuid4()),
 
101
            "from": self.current_user["first_name"],
 
102
            "body": self.get_argument("body"),
 
103
        }
 
104
        message["html"] = self.render_string("message.html", message=message)
 
105
        if self.get_argument("next", None):
 
106
            self.redirect(self.get_argument("next"))
 
107
        else:
 
108
            self.write(message)
 
109
        self.new_messages([message])
 
110
 
 
111
 
 
112
class MessageUpdatesHandler(BaseHandler, MessageMixin):
 
113
    @tornado.web.authenticated
 
114
    @tornado.web.asynchronous
 
115
    def post(self):
 
116
        cursor = self.get_argument("cursor", None)
 
117
        self.wait_for_messages(self.async_callback(self.on_new_messages),
 
118
                               cursor=cursor)
 
119
 
 
120
    def on_new_messages(self, messages):
 
121
        # Closed client connection
 
122
        if self.request.connection.stream.closed():
 
123
            return
 
124
        self.finish(dict(messages=messages))
 
125
 
 
126
 
 
127
class AuthLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
 
128
    @tornado.web.asynchronous
 
129
    def get(self):
 
130
        if self.get_argument("openid.mode", None):
 
131
            self.get_authenticated_user(self.async_callback(self._on_auth))
 
132
            return
 
133
        self.authenticate_redirect(ax_attrs=["name"])
 
134
    
 
135
    def _on_auth(self, user):
 
136
        if not user:
 
137
            raise tornado.web.HTTPError(500, "Google auth failed")
 
138
        self.set_secure_cookie("user", tornado.escape.json_encode(user))
 
139
        self.redirect("/")
 
140
 
 
141
 
 
142
class AuthLogoutHandler(BaseHandler):
 
143
    def get(self):
 
144
        self.clear_cookie("user")
 
145
        self.write("You are now logged out")
 
146
 
 
147
 
 
148
def main():
 
149
    tornado.options.parse_command_line()
 
150
    http_server = tornado.httpserver.HTTPServer(Application())
 
151
    http_server.listen(options.port)
 
152
    tornado.ioloop.IOLoop.instance().start()
 
153
 
 
154
 
 
155
if __name__ == "__main__":
 
156
    main()