1 | <?php |
---|
2 | /* |
---|
3 | * Licensed to the Apache Software Foundation (ASF) under one |
---|
4 | * or more contributor license agreements. See the NOTICE file |
---|
5 | * distributed with this work for additional information |
---|
6 | * regarding copyright ownership. The ASF licenses this file |
---|
7 | * to you under the Apache License, Version 2.0 (the |
---|
8 | * "License"); you may not use this file except in compliance |
---|
9 | * with the License. You may obtain a copy of the License at |
---|
10 | * |
---|
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
12 | * |
---|
13 | * Unless required by applicable law or agreed to in writing, |
---|
14 | * software distributed under the License is distributed on an |
---|
15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
---|
16 | * KIND, either express or implied. See the License for the |
---|
17 | * specific language governing permissions and limitations under the License. |
---|
18 | */ |
---|
19 | |
---|
20 | /** |
---|
21 | * JSON-RPC handler servlet. |
---|
22 | */ |
---|
23 | class JsonRpcServlet extends ApiServlet { |
---|
24 | |
---|
25 | /** |
---|
26 | * Single request through GET |
---|
27 | * http://api.example.org/rpc?method=people.get&id=myself&userid=@me&groupid=@self |
---|
28 | */ |
---|
29 | public function doGet() |
---|
30 | { |
---|
31 | $token = $this->getSecurityToken(); |
---|
32 | if ($token == null) { |
---|
33 | $this->sendSecurityError(); |
---|
34 | return; |
---|
35 | } |
---|
36 | // Request object == GET params |
---|
37 | $request = $_GET; |
---|
38 | $this->dispatch($request, $token); |
---|
39 | } |
---|
40 | |
---|
41 | /** |
---|
42 | * RPC Post request |
---|
43 | */ |
---|
44 | public function doPost() |
---|
45 | { |
---|
46 | $token = $this->getSecurityToken(); |
---|
47 | if ($token == null || $token == false) { |
---|
48 | $this->sendSecurityError(); |
---|
49 | return; |
---|
50 | } |
---|
51 | if (isset($GLOBALS['HTTP_RAW_POST_DATA']) || isset($_POST['request'])) { |
---|
52 | $requestParam = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : $_POST['request']; |
---|
53 | if (get_magic_quotes_gpc()) { |
---|
54 | $requestParam = stripslashes($requestParam); |
---|
55 | } |
---|
56 | $request = json_decode($requestParam, true); |
---|
57 | if ($request == $requestParam) { |
---|
58 | throw new InvalidArgumentException("Malformed json string"); |
---|
59 | } |
---|
60 | } else { |
---|
61 | throw new InvalidArgumentException("Missing POST data"); |
---|
62 | } |
---|
63 | if ((strpos($requestParam, '[') !== false) && strpos($requestParam, '[') < strpos($requestParam, '{')) { |
---|
64 | // Is a batch |
---|
65 | $this->dispatchBatch($request, $token); |
---|
66 | } else { |
---|
67 | $this->dispatch($request, $token); |
---|
68 | } |
---|
69 | } |
---|
70 | |
---|
71 | public function dispatchBatch($batch, $token) |
---|
72 | { |
---|
73 | $responses = array(); |
---|
74 | // Gather all Futures. We do this up front so that |
---|
75 | // the first call to get() comes after all futures are created, |
---|
76 | // which allows for implementations that batch multiple Futures |
---|
77 | // into single requests. |
---|
78 | for ($i = 0; $i < count($batch); $i ++) { |
---|
79 | $batchObj = $batch[$i]; |
---|
80 | $requestItem = new RpcRequestItem($batchObj, $token); |
---|
81 | $responses[$i] = $this->handleRequestItem($requestItem); |
---|
82 | } |
---|
83 | // Resolve each Future into a response. |
---|
84 | // TODO: should use shared deadline across each request |
---|
85 | $result = array(); |
---|
86 | for ($i = 0; $i < count($batch); $i ++) { |
---|
87 | $batchObj = $batch[$i]; |
---|
88 | $key = isset($batchObj["id"]) ? $batchObj["id"] : null; |
---|
89 | $responseItem = $this->getJSONResponse($key, $this->getResponseItem($responses[$i])); |
---|
90 | $result[] = $responseItem; |
---|
91 | } |
---|
92 | echo json_encode($result); |
---|
93 | } |
---|
94 | |
---|
95 | public function dispatch($request, $token) |
---|
96 | { |
---|
97 | $key = null; |
---|
98 | if (isset($request["id"])) { |
---|
99 | $key = $request["id"]; |
---|
100 | } |
---|
101 | $requestItem = new RpcRequestItem($request, $token); |
---|
102 | // Resolve each Future into a response. |
---|
103 | // TODO: should use shared deadline across each request |
---|
104 | $response = $this->getResponseItem($this->handleRequestItem($requestItem)); |
---|
105 | $result = $this->getJSONResponse($key, $response); |
---|
106 | echo json_encode($result); |
---|
107 | } |
---|
108 | |
---|
109 | private function getJSONResponse($key, ResponseItem $responseItem) |
---|
110 | { |
---|
111 | $result = array(); |
---|
112 | if ($key != null) { |
---|
113 | $result["id"] = $key; |
---|
114 | } |
---|
115 | if ($responseItem->getError() != null) { |
---|
116 | $result["error"] = $this->getErrorJson($responseItem); |
---|
117 | } else { |
---|
118 | $response = $responseItem->getResponse(); |
---|
119 | $converted = $response; |
---|
120 | if ($response instanceof RestfulCollection) { |
---|
121 | // FIXME this is a little hacky because of the field names in the RestfulCollection |
---|
122 | $converted->list = $converted->entry; |
---|
123 | unset($converted->entry); |
---|
124 | $result['data'] = $converted; |
---|
125 | } elseif ($response instanceof DataCollection) { |
---|
126 | $result["data"] = $converted->getEntry(); |
---|
127 | } else { |
---|
128 | $result["data"] = $converted; |
---|
129 | } |
---|
130 | } |
---|
131 | return $result; |
---|
132 | } |
---|
133 | |
---|
134 | // TODO(doll): Refactor the responseItem so that the fields on it line up with this format. |
---|
135 | // Then we can use the general converter to output the response to the client and we won't |
---|
136 | // be harcoded to json. |
---|
137 | private function getErrorJson(ResponseItem $responseItem) |
---|
138 | { |
---|
139 | $error = array(); |
---|
140 | $error["code"] = $responseItem->getError(); |
---|
141 | $error["message"] = $responseItem->getErrorMessage(); |
---|
142 | return $error; |
---|
143 | } |
---|
144 | |
---|
145 | public function sendError(ResponseItem $responseItem) |
---|
146 | { |
---|
147 | $error = $this->getErrorJson($responseItem); |
---|
148 | echo json_encode($error); |
---|
149 | } |
---|
150 | |
---|
151 | private function sendBadRequest($t, $response) |
---|
152 | { |
---|
153 | $this->sendError($response, new ResponseItem(ResponseError::$BAD_REQUEST, "Invalid batch - " + $t->getMessage())); |
---|
154 | } |
---|
155 | } |
---|