[Sungrazr-svn] [95] Sungrazr_Server_Adapter_JsonRpc: [NEW] completed initial work on JsonRpc adapter.

0 views
Skip to first unread message

cl...@killersoft.com

unread,
Dec 18, 2007, 1:35:39 AM12/18/07
to Sungra...@googlegroups.com
Revision
95
Author
clay
Date
2007-12-17 22:35:39 -0800 (Mon, 17 Dec 2007)

Log Message

Sungrazr_Server_Adapter_JsonRpc: [NEW] completed initial work on JsonRpc adapter.

Modified Paths

Diff

Modified: trunk/Sungrazr/Server/Adapter/JsonRpc.php (94 => 95)


--- trunk/Sungrazr/Server/Adapter/JsonRpc.php	2007-12-18 06:30:43 UTC (rev 94)
+++ trunk/Sungrazr/Server/Adapter/JsonRpc.php	2007-12-18 06:35:39 UTC (rev 95)
@@ -1,7 +1,7 @@
 <?php
 /**
  * 
- * JSON-RPC server adapter.
+ * JSON-RPC 1.0 server adapter.
  * 
  * Developed for, and then donated by, Mashery.com <http://mashery.com>.
  * 
@@ -21,16 +21,108 @@
     
     /**
      * 
+     * From the request: method being called
+     * 
+     * @param string
+     * 
+     */
+    public $method = null;
+    
+    /**
+     * 
+     * From the request: unnamed parameters to pass to the method.
+     * 
+     * @param array
+     * 
+     */
+    public $params = null;
+    
+    /**
+     * 
+     * For the response: mixed result of the method call.
+     * 
+     * @param mixed
+     * 
+     */
+    public $result = null;
+    
+    /**
+     * 
+     * For the response: mixed error message. If no error occurred, this 
+     * should be null. Otherwise, it should be an object.
+     * 
+     * @param mixed
+     * 
+     */
+    public $error = null;
+    
+    /**
+     * 
+     * For request and response: if request was tagged with an id, it is 
+     * stored here to be attached to the response.
+     * 
+     * If request was not tagged with an id, JSON-RPC 1.0 indicates that 
+     * id value should be null.
+     * 
+     * @param mixed
+     * 
+     */
+    public $id = null;
+    
+    /**
+     * 
+     * Constructor.
+     * 
+     * @param array $config User-provided configuration values.
+     * 
+     */
+    public function __construct($config = null)
+    {
+        // basic construction
+        parent::__construct($config);                        
+    }
+    
+    /**
+     * 
      * Handle a JSON-RPC request.
      * 
-     * @param mixed $request
+     * Rudimentary request validation is performed. Handler methods should
+     * perform in-depth validation on passed parameters.
      * 
+     * @param Solar_Request $request A Solar Request object. Raw POST data
+     * will be read from php://input.
+     * 
      * @return mixed
      * 
      */    
-    public function handle($request = false)
+    public function handle(Solar_Request $request)
     {
+        // avoid bad/inappropriate requests if possible
+        if (! $this->_validate($request)) {
+            return;
+        }
         
+        // call the handler object + method
+        $callback = $this->_server_api[$this->method]['callback'];
+        $obj = Solar::factory($callback[0], 
+            array('caller' => $this)
+        );
+        $this->result = call_user_func_array(
+            array($obj, $callback[1]),
+            $this->params
+        );
+        
+        // should we return a fault?
+        if ($obj->isFault === true) {
+            $fault = $obj->getFault();
+            return $this->fault(
+                $fault['message'],
+                $fault['code']
+            );
+        }
+        
+        // done!
+        return $this->_response();
     }
     
     /**
@@ -46,30 +138,234 @@
      */
     public function fault($fault = null, $code = 404)
     {
+        // result MUST be null if a fault occurs.
+        $this->result = null;
         
+        // Construct an error object
+        $err = new stdClass;
+        $err->name = 'JSONRPCError';
+        $err->code = (int) $code;
+        $err->message = $fault;
+        $this->error = $err;
+        return $this->_response();
+        exit((int) $code);
     }
     
     /**
      * 
+     * Handle special method calls.
+     * 
+     * Returns the response from the server special method if appropriate.
+     * Otherwise, returns **TRUE**.
+     * 
+     * @return mixed
+     * 
+     */
+    protected function _handleSpecials()
+    {
+        if (array_key_exists($this->method, $this->_server_specials)) {
+            $this->result = call_user_func_array(
+                $this->_server_specials[$this->method],
+                $this->params
+            );
+            return $this->_response();
+        }
+        return true;
+    }
+    
+    /**
+     * 
+     * Read raw post data and unserialize it.
+     * 
+     * @param Solar_Request $request Solar_Request object
+     * 
+     * @return bool
+     * 
+     */
+    protected function _loadInput(Solar_Request $request)
+    {
+        $input = file_get_contents('php://input');
+        if (empty($input)) {
+            return $this->fault(
+                'Unable to read from input',
+                101
+            );
+        }
+        
+        // pick out content length from raw input
+        $len = $request->http('Content-Length', false);
+        if (! $len) {
+            return $this->fault(
+                'No Content-Length information found in request.',
+                102
+            );
+        }
+
+        // use Content-Length header to determine how much to retrieve
+        $body = substr($input, 0, $len);
+        
+        // attempt to unserialize
+        return $this->unserialize($body);
+    }
+    
+    /**
+     * 
+     * Reject calls to unknown methods.
+     * 
+     * Returns a server fault for unknown methods, **TRUE** if method is 
+     * known.
+     * 
+     * @return mixed
+     * 
+     */
+    protected function _rejectUnknownMethods()
+    {
+        if (empty($this->_server_api[$this->method])) {
+            return $this->fault(
+                'Unknown method: ' . $this->method,
+                103
+            );
+        }
+        return true;
+    }
+    
+    /**
+     * 
      * Serialize the data in this object to a JSON-RPC response.
      * 
      * @return string
      * 
      */
     protected function _serialize()
-    {
-        return $str;
+    {        
+        return json_encode(array(
+            'result' => $this->result,
+            'error' => $this->error,
+            'id' => $this->id
+        ));
     }
     
     /**
      * 
      * Populate this request with data specified in the serialized request.
      * 
-     * @return void
+     * @return bool
      *
      */
     protected function _unserialize($serialized)
     {
+        // JSON-RPC 1.0 properties
+        $properties = array('method', 'params', 'id');
+        $obj = json_decode($serialized);
         
+        foreach ($properties as $p) {
+            if (! empty($obj->{$p})) {
+                $this->{$p} = $obj->{$p};
+            }
+        }
+        return true;
     }
+    
+    /**
+     * 
+     * Wrapper for all validation methods.
+     * 
+     * @return bool
+     * 
+     */
+    protected function _validate($request)
+    {
+        // JSON-RPC over HTTP must be a POST
+        $ok = $this->_validateRequestMethod($request);
+        if ($ok !== true) return false;
+    
+        // attempt to load the request
+        $ok = $this->_loadInput($request);
+        if ($ok !== true) return false;
+
+        // special request?
+        $ok = $this->_handleSpecials();
+        if ($ok !== true) return false;
+    
+        // is this an unknown method?
+        $ok = $this->_rejectUnknownMethods();
+        if ($ok !== true) return false;
+    
+        // validate type
+        $ok = $this->_validateParameterType();
+        if ($ok !== true) return false;
+
+        // validate parameter count
+        $ok = $this->_validateParameterCount();
+        if ($ok !== true) return false;
+
+        return true;
+    }
+    
+    /**
+     * 
+     * Validate the parameter count against what the handler method is
+     * expecting.
+     * 
+     * Returns a server fault on failure, **TRUE** on success.
+     * 
+     * @return mixed
+     * 
+     */
+    protected function _validateParameterCount()
+    {
+        $expected_num = count($this->_server_api[$this->method]['params']);
+        $actual_num   = count($this->params);
+        
+        if ($actual_num != $expected_num) {
+           return $this->fault(
+                "Invalid call to {$this->method}. " . 
+                    "Requires {$expected_num} parameters, " .
+                    "{$actual_num} parameters received",
+                400
+            );
+        }
+        return true;
+    }
+    
+    /**
+     * 
+     * Make sure the params value is an array.
+     * 
+     * Returns a server fault on failure, **TRUE** on success.
+     * 
+     * @return mixed
+     * 
+     */
+    protected function _validateParameterType()
+    {
+        if (! is_array($this->params)) {
+            return $this->fault(
+                "JSON-RPC expects \"params\" to be an array of objects.",
+                400
+            );
+        }
+        return true;        
+    }
+    
+    /**
+     * 
+     * Make sure the request is a POST.
+     * 
+     * Returns a server fault on failure, **TRUE** on success.
+     * 
+     * @return mixed
+     * 
+     */
+    protected function _validateRequestMethod($request)
+    {
+        // JSON-RPC over HTTP must be a POST
+        if (! $request->isPost()) {
+            return $this->fault(
+                'JSON-RPC calls over HTTP must be made using POST requests',
+                100
+            );
+        }
+        return true;
+    }
 }
\ No newline at end of file
Reply all
Reply to author
Forward
0 new messages