/[drupal]/contributions/modules/rest_provider/rest_provider.module
ViewVC logotype

Contents of /contributions/modules/rest_provider/rest_provider.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Fri Jul 25 20:30:27 2008 UTC (16 months ago) by glhines
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-BETA1, HEAD
Branch point for: DRUPAL-6--1
File MIME type: text/x-php
Initial commit of rest_provider module. This module provides a controller for creating RESTful web services that don't necessarily conform to a Drupalish service interface.
1 <?php
2 // $Id$
3
4 /**
5 * ========================================================================
6 * The REST Provider module exists to provide basic functionality for using
7 * idiomatic Drupal to create RESTful web services. It is intended to be
8 * lightweight but flexible, imposing few constraints on developers. The
9 * REST Provider API operates primarily through hooks and conventions,
10 * freeing them from having to write much glue code.
11 * ========================================================================
12 */
13
14
15 /**
16 * This class exists to get around PHP's limitation of scalar-only globals.
17 */
18 class rest_provider {
19 // The CONNECT and TRACE methods aren't supported because they deal with
20 // proxies, which are outside of the scope of this module.
21 public static function http_methods() {
22 return array('DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT');
23 }
24
25 // These are all of the valid HTTP response codes. Two response codes are
26 // intentionally excluded:
27 // 306 Unused (Deprecated; reserved.)
28 // 402 Payment Required (Reserved for future use.)
29 public static function response_codes() {
30 return array(100, 101, 200, 201, 202, 203, 204, 205, 206, 300, 301, 302,
31 303, 304, 305, 307, 400, 401, 403, 404, 405, 406, 407, 408, 409, 410,
32 411, 412, 413, 414, 415, 416, 417, 500, 501, 502, 503, 504, 505);
33 }
34 }
35
36
37 /**
38 * When called, this function will invoke a controller that will process how the
39 * resource in question is handled.
40 *
41 * @param string $module_name
42 * The name of the module containing the controller.
43 * @param string $controller_name
44 * The name of the controller that should handle the request.
45 */
46 function rest_provider_controller($module_name, $controller_name) {
47 $method = $_SERVER['REQUEST_METHOD'];
48
49 // Handle OPTIONS requests.
50 if ($method == 'OPTIONS') {
51 header('Allow: '. implode(', ', _rest_provider_get_options($module_name, $controller_name)));
52 http_send_status(200); // OK
53 exit();
54 }
55
56 // Include the controller file; if none exists, return a 404.
57 if (!_rest_provider_include_controller($module_name, $controller_name)) {
58 http_send_status(404); // Not Found
59 exit();
60 }
61
62 $handler_function = $module_name .'_'. $controller_name .'_'. $method;
63
64 // Check to see if the handler function exists for the request
65 // method; if not, return a 405.
66 if (!function_exists($handler_function)) {
67 http_send_status(405); // Method Not Allowed
68 exit();
69 }
70
71 // Check to see if the user has permission to access the given resource
72 // via the request method.
73 if (!_rest_provider_access($module_name, $controller_name, $method)) {
74 global $user;
75
76 // If the user is authenticated, return a 403 to tell them they don't have
77 // permission to access the resource; if the user isn't authenticated,
78 // return a 401 to tell them they might be able to access the resource if
79 // they authenticate.
80 if ($user->uid) {
81 http_send_status(403); // Forbidden
82 }
83 else {
84 // Make sure the httpauth module is installed and the user has enabled at
85 // least one authentication scheme helper method.
86 if (module_exists('httpauth') && !empty($auth_schemes)) {
87 http_send_status(401); // Unauthorized
88 header('WWW-Authenticate: '. httpauth_tokens);
89 }
90 }
91
92 exit();
93 }
94
95 // Send the request to the handler and get back the response.
96 $args = func_get_args();
97 $response = _rest_provider_invoke_handler_function($module_name, $controller_name, $method, $args);
98
99 // Process the response and send it to the client.
100 _rest_provider_send_response($response);
101 }
102
103
104 /**
105 * This function should be used when Drupal should pass all requests through to
106 * the REST Provider controller rather than limiting access before the
107 * controller gets a crack at the request.
108 *
109 * @return bool
110 * Always returns TRUE.
111 */
112 function rest_provider_access() {
113 return TRUE;
114 }
115
116
117 /**
118 * This function will include the requested controller file. If it can't find a
119 * file for the requested controller, it will send a 404 response to the client.
120 *
121 * @param string $module_name
122 * The name of the module containing the controller.
123 * @param string $controller_name
124 * The name of the controller that should handle the request.
125 */
126 function _rest_provider_include_controller($module_name, $controller_name) {
127 return module_exists($module_name) && include_once drupal_get_path('module', $module_name) .'/'. $module_name .'_'. $controller_name .'.inc';
128 }
129
130
131 /**
132 * Sends a request to the given handler function, which will then process the
133 * request and return a response.
134 *
135 * @param string $module_name
136 * The name of the module containing the controller.
137 * @param string $controller_name
138 * The name of the controller that should handle the request.
139 * @param string $method
140 * The HTTP method used in the request.
141 * @param array $args
142 * The arguments sent to the controller.
143 * @return array
144 * An associative array containing the information necessary to generate
145 * the response. The available elements are:
146 *
147 * 'response_code' => The HTTP response code to send to the client.
148 * Defaults to 200 (OK).
149 * 'headers' => An associative array of custom headers to send with
150 * the response.
151 * 'body' => The body of the response.
152 *
153 * None of these elements are required.
154 */
155 function _rest_provider_invoke_handler_function($module_name, $controller_name, $method, $args) {
156 $handler_function = $module_name .'_'. $controller_name .'_'. $method;
157
158 $request = array(
159 'uri' => $_SERVER['REQUEST_URI'],
160 'method' => $method,
161 'headers' => http_get_request_headers(),
162 'body' => http_get_request_body(),
163 );
164
165 // The first two arguments of the controller method were the module name and
166 // the controller name. These should not be forwarded to the handler function
167 // (as it's already aware of the module and controller names). Remove them
168 // from the array and set the request array as the first element in the array.
169 unset($args[0], $args[1]);
170 array_unshift($args, $request);
171
172 // Call the handler function for the request; get the response back.
173 return call_user_func_array($handler_function, $args);
174 }
175
176
177 /**
178 * Processes a response from a handler function and sends it to the client. If
179 * the client accepts content compression and Drupal is configured to compress
180 * content, the response will be compressed accordingly.
181 *
182 * @param array $response
183 * An associative array containing the information necessary to generate
184 * the response. The available elements are:
185 *
186 * 'response_code' => The HTTP response code to send to the client.
187 * Defaults to 200 (OK).
188 * 'headers' => An associative array of custom headers to send with
189 * the response.
190 * 'body' => The body of the response.
191 * 'media_type' => The MIME-type of the response body.
192 * 'charset' => The character encoding of the response body.
193 *
194 * None of these elements are required.
195 */
196 function _rest_provider_send_response($response) {
197 // Check the given response code for validity;
198 // if not given, default to 200 (OK) and continue;
199 // if invalid, use 500 (Internal Server Error) and exit with no content.
200 if (is_numeric($response['response_code']) && in_array($response['response_code'], rest_provider::response_codes())) {
201 http_send_status($response['response_code']);
202 }
203 elseif (!isset($response['response_code'])) {
204 http_send_status(200);
205 }
206 else {
207 http_send_status(500);
208 exit();
209 }
210
211 // Set the media type and character encoding.
212 if ($response['media_type']) {
213 $media_type = $response['media_type'] ? $response['media_type'] : 'text/html';
214 $charset = $response['charset'] ? $response['charset'] : $default_charset;
215 $content_type = $response['media_type'] .'; charset='. $charset;
216 header('Content-Type: '. $content_type);
217 }
218
219 // Add all other custom headers the user has sent.
220 foreach ($response['headers'] as $key => $value) {
221 header($key .': '. $value);
222 }
223
224 // Compress response content where applicable.
225 if (variable_get('page_compression', FALSE) && // page compression is enabled
226 @strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && // the client supports gzip compression
227 function_exists('gzencode')) { // the server supports gzip compression
228 header('Content-Encoding: gzip');
229 $response['body'] = gzencode($response['body'], 9, FORCE_GZIP);
230 }
231
232 exit($response['body']);
233 }
234
235
236 /**
237 * When called, this function will check to see if the user has permission
238 * to invoke the necessary handler function.
239 *
240 * If the interface for the given controller can't be found, a 404 is returned
241 * and exit() is called, so this function is not guaranteed to return a value.
242 *
243 * @param string $module_name
244 * The name of the module containing the controller.
245 * @param string $controller_name
246 * The name of the controller that should handle the request.
247 * @param string $method
248 * The HTTP method of the request (e.g. GET, PUT, DELETE, etc.).
249 * @return bool
250 * Returns TRUE if the user has permission to perform the action; FALSE
251 * otherwise.
252 */
253 function _rest_provider_access($module_name, $controller_name, $method) {
254 // This snippit exists just in case someone calls _rest_provider_access from
255 // outside of the rest_provider module; if they did, the permissions function
256 // wouldn't be loaded.
257 if (!_rest_provider_include_controller($module_name, $controller_name)) {
258 http_send_status(404); // Not Found
259 exit();
260 }
261
262 // Grab the permissions.
263 $permissions_function = $module_name .'_'. $controller_name .'_permissions';
264 $permissions = function_exists($permissions_function) ? $permissions_function() : array();
265
266 // Check to see if there are permissions for the request method.
267 if (count($permissions[$method])) {
268 // Check each permission set in the permissions for the request method.
269 foreach($permissions[$method] as $perm) {
270 if (!user_access($perm)) {
271 return FALSE;
272 }
273 }
274 }
275
276 // If no permissions are required to access the requested method or if the
277 // user has all of the necessary permissions, then the user is granted access.
278 return TRUE;
279 }
280
281
282 /**
283 * This is a special controller for the OPTIONS method. It will put together a
284 * list of HTTP methods that the user making the OPTIONS request is allowed to
285 * make.
286 *
287 * @param string $module_name
288 * The name of the module containing the controller.
289 * @param string $controller_name
290 * The name of the controller that should handle the request.
291 * @return array
292 * An array of the HTTP methods the requesting user has permission to request.
293 */
294 function _rest_provider_get_options($module_name, $controller_name) {
295 // This snippit exists just in case someone calls _rest_provider_get_options
296 // from outside of the rest_provider module; if they did, the handler function
297 // wouldn't be loaded.
298 if (!_rest_provider_include_controller($module_name, $controller_name)) {
299 http_send_status(404); // Not Found
300 exit();
301 }
302
303 $options_methods = array();
304
305 // Check each method available.
306 foreach(rest_provider::http_methods() as $method) {
307 $handler_function = $module_name .'_'. $controller_name .'_'. $method;
308
309 // Does the corresponding handler function exist?
310 // Does the user have access to the handler function?
311 if (function_exists($handler_function) && _rest_provider_access($module_name, $controller_name, $method)) {
312 $options_methods[] = $key;
313 }
314 }
315
316 // Process the response.
317 return $options_methods;
318 }

  ViewVC Help
Powered by ViewVC 1.1.2