1.9.30 sync.
[gae.git] / python / google / appengine / tools / dev_appserver_channel.py
blob3ee93663252b13e49f734ca5ff559d69713ea2b1
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Channel support classes.
19 Classes:
21 CreateChannelDispatcher:
22 Creates a dispatcher that is added to dispatcher chain. Handles polls from
23 the client to retrieve messages for a given channel.
24 """
29 import cgi
30 import os
31 import urlparse
33 from google.appengine.api.channel.channel_service_stub import InvalidTokenError
34 from google.appengine.api.channel.channel_service_stub import TokenTimedOutError
38 CHANNEL_POLL_PATTERN = '/_ah/channel/dev(?:/.*)?'
41 CHANNEL_JSAPI_PATTERN = '/_ah/channel/jsapi'
44 def CreateChannelDispatcher(channel_service_stub):
45 """Function to create channel dispatcher.
47 Args:
48 channel_service_stub: The service stub responsible for creating channels and
49 sending messages. This stub stores messages destined for channels, so we
50 query it when the client polls the _ah/channel/ dispatcher.
52 Returns:
53 New dispatcher capable of handling client polls for channel messages.
54 """
58 from google.appengine.tools import old_dev_appserver
60 class ChannelDispatcher(old_dev_appserver.URLDispatcher):
61 """Dispatcher that handles channel polls."""
63 def __init__(self, channel_service_stub):
64 """Constructor.
66 Args:
67 channel_service_stub: The channel service that receives channel messages
68 from the application.
69 """
70 self._channel_service_stub = channel_service_stub
73 def Dispatch(self,
74 request,
75 outfile,
76 base_env_dict=None):
77 """Handle post dispatch.
79 This dispatcher handles requests under the /_ah/channel/ path. Currently
80 it handles 3 sub-paths:
81 connect: place-holder for parity with the java dev channel.
82 poll: return pending messages for the channel identified by the
83 channel parameter.
84 jsapi: return the javascript library to retrieve messages for the
85 channel.
87 Args:
88 request: The HTTP request.
89 outfile: The response file.
90 base_env_dict: Dictionary of CGI environment parameters if available.
91 Defaults to None.
92 """
94 (unused_scheme, unused_netloc,
95 path, query,
96 unused_fragment) = urlparse.urlsplit(request.relative_url)
97 param_dict = cgi.parse_qs(query, True)
99 page = path.rsplit('/', 1)[-1]
101 if page == 'jsapi':
102 path = os.path.join(os.path.dirname(__file__), 'dev-channel-js.js')
103 outfile.write('Status: 200\r\n')
104 outfile.write('Content-type: text/javascript\r\n\r\n')
105 outfile.write(open(path).read())
106 elif page == 'dev':
107 token = param_dict['channel'][0]
109 token_error = None
110 try:
111 self._channel_service_stub.validate_token_and_extract_client_id(token)
114 except InvalidTokenError:
115 token_error = 'Invalid+token.'
116 except TokenTimedOutError:
117 token_error = 'Token+timed+out.'
119 if token_error is not None:
120 outfile.write('Status: 401 %s\r\n\r\n' % token_error)
121 return
123 outfile.write('Status: 200\r\n')
124 outfile.write('\r\n')
125 command = param_dict['command'][0]
127 if command == 'connect':
128 self._channel_service_stub.connect_channel(token)
129 outfile.write('1')
130 elif command == 'poll':
131 self._channel_service_stub.connect_channel(token)
132 if self._channel_service_stub.has_channel_messages(token):
133 outfile.write(self._channel_service_stub.pop_first_message(token))
135 return ChannelDispatcher(channel_service_stub)