PageRenderTime 43ms CodeModel.GetById 10ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/didactilab/gwt/phprpc/phprpc/phprpc/rpc/RPC.php

http://gwtphp-derpc.googlecode.com/
PHP | 519 lines | 424 code | 68 blank | 27 comment | 50 complexity | abf1d71b961554ba4f7a5f1f2bc7370e MD5 | raw file
  1<?php
  2
  3require_once PHPRPC_ROOT . 'classes.php';
  4require_once PHPRPC_ROOT . 'primitives.php';
  5require_once PHPRPC_ROOT . 'primitives_serializers.php';
  6require_once PHPRPC_ROOT . 'collections.php';
  7require_once PHPRPC_ROOT . 'collections_serializers.php';
  8require_once PHPRPC_ROOT . 'datetime.php';
  9require_once PHPRPC_ROOT . 'datetime_serializers.php';
 10require_once PHPRPC_ROOT . 'serialization.php';
 11
 12require_once PHPRPC_ROOT . 'rpc/SerializationPolicy.php';
 13require_once PHPRPC_ROOT . 'rpc/ServerSerializationStreamReader.php';
 14require_once PHPRPC_ROOT . 'rpc/ServerSerializationStreamWriter.php';
 15require_once PHPRPC_ROOT . 'rpc/RpcToken.php';
 16require_once PHPRPC_ROOT . 'rpc/RPCServletUtils.php';
 17require_once PHPRPC_ROOT . 'rpc/javaclasses.php';
 18
 19class RPC {
 20	
 21	private static $PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS;
 22	
 23	private static $serviceToImplementedInterfacesMap;
 24	
 25	private static $TYPE_NAMES = array();
 26	
 27	public static function init() {
 28		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS = new HashMap();
 29		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Boolean::clazz(), Boolean::typeClass());
 30		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Byte::clazz(), Byte::typeClass());
 31		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Character::clazz(), Character::typeClass());
 32		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Float::clazz(), Float::typeClass());
 33		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Integer::clazz(), Integer::typeClass());
 34		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Long::clazz(), Long::typeClass());
 35		self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->put(Short::clazz(), Short::typeClass());
 36		
 37		self::$TYPE_NAMES['Z'] = Boolean::typeClass();
 38		self::$TYPE_NAMES['B'] = Byte::typeClass();
 39		self::$TYPE_NAMES['C'] = Character::typeClass();
 40		self::$TYPE_NAMES['D'] = Double::typeClass();
 41		self::$TYPE_NAMES['F'] = Float::typeClass();
 42		self::$TYPE_NAMES['I'] = Integer::typeClass();
 43		self::$TYPE_NAMES['J'] = Long::typeClass();
 44		self::$TYPE_NAMES['S'] = Short::typeClass();
 45		
 46		self::$serviceToImplementedInterfacesMap = new HashMap();
 47	}
 48	
 49	public static function decodeRequest($encodedRequest, Clazz $type = null, 
 50			SerializationPolicyProvider $serializationPolicyPolicyProvider = null) {
 51		if (is_null($encodedRequest)) {
 52			throw new NullPointerException('encodedRequest cannot be null');
 53		}
 54		
 55		if (empty($encodedRequest)) {
 56			throw new IllegalArgumentException('encodedRequest cannot be empty');
 57		}
 58		
 59		try {
 60			$streamReader = new ServerSerializationStreamReader($serializationPolicyPolicyProvider);
 61			$streamReader->prepareToRead($encodedRequest);
 62
 63			$rpcToken = null;
 64			if ($streamReader->hasFlags(AbstractSerializationStream::FLAG_RPC_TOKEN_INCLUDED)) {
 65				// Read the RPC token
 66				$rpcToken = $streamReader->deserializeValue(Classes::classOf(RpcToken));
 67			}
 68			
 69			// Read the name of the RemoteService interface
 70			$serviceIntfName = self::maybeDeobfuscate($streamReader, $streamReader->readString());
 71			
 72			//TODO: implements model or is class
 73			/*if (!is_null($type)) {
 74				if (!self::implementsInterface($type, $serviceIntfName)) {
 75					$printedType = self::printTypeName($type);
 76					throw new IncompatibleRemoteServiceException(
 77						"Blocked attempt to access interface '$serviceIntfName', " .
 78						"which is not implemented by '$printedType'; " .
 79						"this is either misconfiguration or a hack attempt"
 80					);
 81				}
 82			}*/
 83			
 84			$serializationPolicy = $streamReader->getSerializationPolicy();
 85			try {
 86				$serviceIntf = self::getClassFromSerializedName($serviceIntfName);
 87				if (!Classes::classOf(RemoteService)->isAssignableFrom($serviceIntf)) {
 88					// The requested interface is not a RemoteService interface
 89					$printedType = $this->printTypeName($serviceIntf);
 90					throw new IncompatibleRemoteServiceException(
 91						"Blocked attempt to access interface '$printedType', " .
 92						"which doesn't extend RemoteService; this is either misconfiguration or a hack attempt"
 93					);
 94				}
 95			}
 96			catch (ClassNotFoundException $e) {
 97				throw new IncompatibleRemoteServiceException(
 98					"Could not locate requested interface '$serviceIntfName' : $e"
 99				);
100			}
101			
102			$serviceMethodName = $streamReader->readString();
103			$paramCount =$streamReader->readInt();
104			if ($paramCount > $streamReader->getNumberOfTokens()) {
105				throw new IncompatibleRemoteServiceException('Invalid number of parameters');
106			}
107			$parameterTypes = array();
108			for ($i=0; $i<$paramCount; $i++) {
109				$paramClassName = self::maybeDeobfuscate($streamReader, $streamReader->readString());
110				
111				try {
112					$parameterTypes[] = self::getClassFromSerializedName($paramClassName);
113				}
114				catch (ClassNotFoundException $e) {
115					throw new IncompatibleRemoteServiceException("Paramter $i is unknown type '$paramClassName' : $e");
116				}
117			}
118			
119			try {
120				$method = $serviceIntf->getMethod($serviceMethodName);
121				
122				$parameterValues = array();
123				for ($i=0; $i<$paramCount; $i++) {
124					$parameterValues[] = $streamReader->deserializeValue($parameterTypes[$i]);
125				}
126				
127				return new RPCRequest($method, $parameterValues, $serializationPolicy, 
128					$streamReader->getFlags(), $rpcToken);
129			}
130			catch (NoSuchMethodException $e) {
131				throw new IncompatibleRemoteServiceException(
132					self::formatMethodNotFoundErrorMessage($serviceIntf, $serviceMethodName, $parameterTypes)
133				);
134			}
135			
136		}
137		catch (SerizalizationException $ex) {
138			throw new IncompatibleRemoteServiceException($ex->getMessage() . ' : ' . $ex);
139		}
140	}
141	
142	public static function encodeResponseForFailure(Method $serviceMethod, Throwable $cause, 
143			SerializationPolicy $serializationPolicy = null,
144			$flags = AbstractSerializationStream::DEFAULT_FLAGS) {
145		if (is_null($cause)) {
146			throw new NullPointerException('cause cannot be null');
147		}
148		
149		if (is_null($serializationPolicy)) {
150			//throw new NullPointerException('serializationPolicy cannot be null');
151			$serializationPolicy = self::getDefaultSerializationPolicy();
152		}
153		
154		if (!is_null($serviceMethod) && !RPCServletUtils::isExpectedException($serviceMethod, $cause)) {
155			$cause = (string) $cause;
156			$source = self::getSourceRepresentation($serviceMethod);
157			throw new UnexpectedException("Service method '$source' threw an unexpected excetion: $cause");
158		}
159		
160		return self::encodeResponse($cause->getClass(), $cause, true, $flags, $serializationPolicy);
161	}
162	
163	public static function encodeResponseForSuccess(Method $serviceMethod, $object, 
164			SerializationPolicy $serializationPolicy = null,
165			$flags = AbstractSerializationStream::DEFAULT_FLAGS) {
166		if (is_null($serviceMethod)) {
167			throw new NullPointerException('serviceMethod cannot be null');
168		}
169		
170		if (is_null($serializationPolicy)) {
171			$serializationPolicy = self::getDefaultSerializationPolicy();
172			throw new NullPointerException('serializationPolicy cannot be null');
173		}
174		
175		$methodReturnTypeName = $serviceMethod->getReturnType();
176		//TODO see if must be an object
177		if (empty($methodReturnTypeName)) {
178			$methodReturnType =  Void::typeClass();
179		}
180		else {
181			$methodReturnType = Classes::classOf($methodReturnTypeName);
182		}
183		
184		if ($methodReturnType !== Void::typeClass() && !is_null($object)) {
185			// TODO : Verify
186			/*if ($methodReturnType->isPrimitive()) {
187				$actualReturnType = self::getPrimitiveClassFromWrapper(Classes::classOf($object));
188			}
189			else {
190				$actualReturnType = Classes::classOf($object);
191			}*/
192			$actualReturnType = Classes::classOfValue($object);
193			
194			//var_dump($methodReturnType);
195			
196			// TODO : Enum manage
197			if ($methodReturnType->isEnum() && $actualReturnType === Integer::typeClass()) {
198				return self::encodeResponse($methodReturnType, $object, false, $flags, $serializationPolicy);
199			}
200			
201			
202			if (is_null($actualReturnType) || !$methodReturnType->isAssignableFrom($actualReturnType)) {
203				$printedType = self::printTypeName(Classes::classOfValue($object));
204				$source = self::getSourceRepresentation($serviceMethod);
205				throw new IllegalArgumentException(
206					"Type '$printedType' does not match the return type in the method's signature : '$source'"
207				);
208			}
209		}
210		
211		return self::encodeResponse($methodReturnType, $object, false, $flags, $serializationPolicy);
212	}
213	
214	public static function getDefaultSerializationPolicy() {
215		return LegacySerializationPolicy::getInstance();
216	}
217	
218	public static function invokeAndEncodeResponse($target, Method $serviceMethod, $args,
219			SerializationPolicy $serializationPolicy = null,
220			$flags = AbstractSerializationStream::DEFAULT_FLAGS) {
221		if (is_null($serviceMethod)) {
222			throw new NullPointerException('serviceMethod cannot be null');
223		}
224		
225		if (is_null($serializationPolicy)) {
226			$serializationPolicy = self::getDefaultSerializationPolicy();
227			throw new NullPointerException('serializationPolicy cannot be null');
228		}
229		
230		try {
231			$result = $serviceMethod->invokeArgs($target, $args);
232			$responsePayload = self::encodeResponseForSuccess($serviceMethod, $result, $serializationPolicy, $flags);
233		}
234		//cannot be happen in Php
235		catch (IllegalAccessException $e) {
236			$securityException = new SecurityException(self::formatIllegalAccessErrorMessage($target, $serviceMethod), $e);
237			throw $securityException;
238		}
239		//cannot be happen in Php
240		catch (IllegalArgumentException $e) {
241			$securityException = new SecurityException(self::formatIllegalArgumentErrorMessage($target, $serviceMethod, $args), $e);
242			throw $securityException;
243		}
244		catch (Exception $e) {
245			$cause = new Throwable($e, $e->getMessage());
246			$responsePayload = self::encodeResponseForFailure($serviceMethod, $cause, $serializationPolicy, $flags);
247		}
248		
249		return $responsePayload;
250	}
251	
252	private static function encodeResponse(Clazz $responseClass, $object, $wasThrown, $flags, $serializationPolicy) {
253		$stream = new ServerSerializationStreamWriter($serializationPolicy);
254		$stream->setFlags($flags);
255		
256		$stream->prepareToWrite();
257		if ($responseClass !== Void::typeClass()) {
258			$stream->serializeValue($object, $responseClass);
259		}
260		
261		$bufferStr = ($wasThrown ? '//EX' : '//OK') . (string) $stream;
262		return $bufferStr;
263	}
264
265	private static function formatIllegalAccessErrorMessage($target, Method $serviceMethod) {
266		$source = self::getSourceRepresentation($serviceMethod);
267		$sb = "Blocked attempt to access inaccessible method '$source'";
268		
269		if (!is_null($target)) {
270			$printedType = self::printTypeName(Classes::classOf($target));
271			$sb .= " on target '$printedType'";
272		}
273		
274		$sb .= '; this is either misconfiguration or a hack attempt';
275		
276		return $sb;
277	}
278	
279	private static function formatIllegalArgumentErrorMessage($target, Method $serviceMethod, $args) {
280		$source = self::getSourceRepresentation($serviceMethod);
281		$sb = "Blocked attempt to invoke method '$source'";
282		
283		if (!is_null($target)) {
284			$printedType = self::printTypeName(Classes::classOf($target));
285			$sb .= " on target '$printedType'";
286		}
287		
288		$sb .= ' with invalid arguments';
289		
290		if (!is_null($args) && !empty($args)) {
291			$sb .= Arrays::asList($args);
292		}
293
294		return $sb;
295	}
296	
297	private static function formatMethodNotFoundErrorMessage(Clazz $serviceIntf, $serviceMethodName, $parameterTypes) {
298		$sb = "Could not locate requested method '$serviceMethodName(";
299		for ($i=0; $i<count($parameterTypes); $i++) {
300			if ($i > 0) {
301				$sb .= ', ';
302			}
303			$sb .= self::printTypeName($parameterTypes[$i]);
304		}
305		$sb .= ")'";
306		
307		$printedType = self::printTypeName($serviceIntf);
308		$sb .= " in interface '$printedType'";
309		
310		return $sb;
311	}
312	
313	private static function getClassFromSerializedName($serializedName) {
314		if (isset(self::$TYPE_NAMES[$serializedName])) {
315			return self::$TYPE_NAMES[$serializedName];
316		}
317		
318		return Classes::classOf($serializedName);
319	}
320	
321	private static function getPrimitiveClassFromWrapper(Clazz $wrapperClass) {
322		return self::$PRIMITIVE_WRAPPER_CLASS_TO_PRIMITIVE_CLASS->get($wrapperClass);
323	}
324	
325	private static function getSourceRepresentation(Method $method) {
326		return str_replace('$', '.', (string) $method);
327	}
328	
329	private static function implementsInterface(Clazz $service, $intfName) {
330		$interfaceSet = self::$serviceToImplementedInterfacesMap->get($service);
331		if (!is_null($interfaceSet)) {
332			if ($interfaceSet->contains($intfName)) {
333				return true;
334			}
335		}
336		else {
337			$interfaceSet = new HashSet();
338			self::$serviceToImplementedInterfacesMap->put($service, $interfaceSet);
339		}
340		
341		if (!$service->isInterface()) {
342			while ((!is_null($service)) && Classes::classOf('RemoteServiceServlet') !== $service) {
343				$intfs = $service->getInterfaces();
344				foreach ($intfs as $intf) {
345					if (self::implementsInterfaceRecursive($intf, $intfName)) {
346						$interfaceSet->add($intfName);
347						return true;
348					}
349				}
350			
351				// did not find the interface in this class so we look in the
352				// superclass
353				//
354				$service = $service->getSuperClass();
355			}
356		}
357		else {
358			if (self::implementsInterfaceRecursive($service, $intfName)) {
359				$interfaceSet->add($intfName);
360				return true;
361			}
362		}
363		
364		return false;
365	}
366	
367	private static function implementsInterfaceRecursive(Clazz $clazz, $intfName) {
368		assert($clazz->isInterface());
369		
370		if ($clazz->getName() === $intfName) {
371			return true;
372		}
373		
374		$intfs = $clazz->getInterfaces();
375		foreach ($intfs as $intf) {
376			if (self::implementsInterfaceRecursive($intf, $intfName)) {
377				return true;
378			}
379		}
380		
381		return false;
382	}
383	
384	private static function maybeDeobfuscate(ServerSerializationStreamReader $streamReader, $name) {
385		if ($streamReader->hasFlags(AbstractSerializationStream::FLAG_ELIDE_TYPE_NAMES)) {
386			$serializationPolicy = $streamReader->getSerializationPolicy();
387			if (!(serializationPolicy instanceof TypeNameObfuscator)) {
388				throw new IncompatibleRemoteServiceException(
389					'RPC request was encoded with obfuscated type names, ' .
390					'but the SerializationPolicy in use does not implement ' .
391					Classes::classOf(TypeNameObfuscator)->getName()
392				);
393			}
394			
395			$maybe = $serializationPolicy->getClassNameForTypeId($name);
396			if (!is_null($maybe)) {
397				return $maybe;
398			}
399		}
400		else if (($index = mb_strpos($name, '/')) !== false) {
401			return mb_substr($name, 0, $index);
402		}
403		return $name;
404	}
405	
406	private static function printTypeName(Clazz $type) {
407		// Primitives
408		//
409		if ($type === Integer::typeClass()) {
410			return 'int';
411		}
412		else if ($type === Long::typeClass()) {
413			return 'long';
414		}
415		else if ($type === Short::typeClass()) {
416			return 'short';
417		}
418		else if ($type === Byte::typeClass()) {
419			return 'byte';
420		}
421		else if ($type === Character::typeClass()) {
422			return 'char';
423		}
424		else if ($type === Boolean::typeClass()) {
425			return 'boolean';
426		}
427		else if ($type === Float::typeClass()) {
428			return 'float';
429		}
430		else if ($type === Double::typeClass()) {
431			return 'double';
432		}
433		
434		// Arrays
435		//
436		if ($type->isArray()) {
437			$componentType = $type->getComponentType();
438			return self::printTypeName($componentType) . '[]';
439		}
440		
441		// Everything else
442		//
443		return str_replace('$', '.', $type->getName());
444	}
445	
446}
447RPC::init();
448
449class RPCRequest {
450	private $flags;
451	private $method;
452	private $parameters;
453	private $rpcToken;
454	private $serializationPolicy;
455	
456	public function __construct(Method $method, $parameters, SerializationPolicy $serializationPolicy, $flags, RpcToken $rpcToken = null) {
457		$this->method = $method;
458		$this->parameters = $parameters;
459		$this->rpcToken = $rpcToken;
460		$this->serializationPolicy = $serializationPolicy;
461		$this->flags = $flags;
462	}
463	
464	public function getMethod() {
465		return $this->method;
466	}
467	
468	public function getParameters() {
469		return $this->parameters;
470	}
471	
472	public function getRpcToken() {
473		return $this->rpcToken;
474	}
475	
476	public function getSerializationPolicy() {
477		return $this->serializationPolicy;
478	}
479	
480	public function getFlags() {
481		return $this->flags;
482	}
483	
484	public function __toString() {
485		$sb = $this->method->getDeclaringClass()->getName();
486		$sb .= '.';
487		$sb .= $this->method->getName();
488		
489		$sb .= '(';
490		for ($i=0, $c=count($this->parameters); $i<$c; $i++) {
491			$param = $this->parameters[$i];
492			if ($i < $c - 1) {
493				$sb .= ', ';
494			}
495			if (is_string($param)) {
496				$escaped = str_replace('\\\"', '\\\\\"', $param);
497				$sb .= "\"$escaped\"";
498			}
499			else if (is_null($param)) {
500				$sb .= 'null';
501			}
502			else {
503				$sb .= (string) $param;
504			}
505		}
506		$sb .= ')';
507		
508		return $sb;
509	}
510	
511}
512
513/** @gwtname com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException */
514class IncompatibleRemoteServiceException extends Exception {
515}
516
517/** @gwtname com.google.gwt.user.client.rpc.RpcTokenException */
518class RpcTokenException extends Exception {
519}