/** * Copy steps from other AsyncSteps, useful for sub-step cloning * * @param \FutoIn\RI\AsyncSteps $other model AsyncSteps object for re-use * @return \FutoIn\AsyncSteps reference to $this */ public function copyFrom(\FutoIn\AsyncSteps $other) { if (!$other instanceof \FutoIn\RI\AsyncSteps) { throw new \FutoIn\Error(\FutoIn\Error::InternalError); } // Copy steps $oq = $other->_getInnerQueue(); $oq->rewind(); $q = $this->queue; for (; $oq->valid(); $oq->next()) { $q->enqueue($oq->current()); } // Copy state $s = $this->state; foreach ($other->state as $k => $v) { if (!isset($s->{$k})) { $s->{$k} = $v; } } }
/** @see \FutoIn\AdvancedCCM */ public function cacheInit(\FutoIn\AsyncSteps $as) { // TODO: $as->error(\FutoIn\Error::NotImplemented); }
/** @see \FutoIn\SimpleCCM */ public function register(\FutoIn\AsyncSteps $as, $name, $ifacever, $endpoint, $credentials = null, $options = null) { // Unregister First if (array_key_exists($name, $this->iface_info)) { $as->error(\FutoIn\Error::InvokerError, "Already registered"); } // Check ifacever if (!preg_match('/^(([a-z][a-z0-9]*)(\\.[a-z][a-z0-9]*)+):(([0-9]+)\\.([0-9]+))$/', $ifacever, $m)) { $as->error(\FutoIn\Error::InvokerError, "Invalid ifacever"); } $iface = $m[1]; $mjrmnr = $m[4]; $mjr = $m[5]; $mnr = $m[6]; if (!is_string($endpoint)) { $as->error(\FutoIn\Error::InvokerError, "Invalid endpoint"); } $endpoint = preg_replace('/^secure\\+/', '', $endpoint, 1, $repcnt); $secure_channel = $repcnt > 0; // Silently map WebSockets to HTTP/HTTPS as per FTN7 spec, if not supported $endpoint = preg_replace('/^ws(s?):\\/\\//', 'http${1}://', $endpoint); if (!$secure_channel && preg_match('/^(https|wss|unix):/', $endpoint)) { $secure_channel = true; } $url = parse_url($endpoint); if ($url['scheme'] === 'self') { $impl = str_replace('.', '\\', $url['host']); } else { $impl = "\\FutoIn\\RI\\Invoker\\Details\\NativeInterface"; } $options = array_merge((array) $options, $this->impl->options); $info = new Details\RegistrationInfo(); $info->iface = $iface; $info->version = $mjrmnr; $info->mjrver = $mjr; $info->mnrver = $mnr; $info->endpoint = $endpoint; $info->creds = $credentials; $info->secure_channel = $secure_channel; $info->impl = $impl; $info->regname = $name; $info->options =& $options; $this->iface_info[$name] = $info; $this->impl->onRegister($as, $info); $as->add(function ($as) use($name, $ifacever, $info) { if (!$info->simple_req) { if (!isset($info->constraints['AllowAnonymous']) && !$info->creds) { $as->error(\FutoIn\Error::SecurityError, "Requires authenticated user"); } if (isset($info->constraints['SecureChannel']) && !$info->secure_channel) { $as->error(\FutoIn\Error::SecurityError, "SecureChannel is required"); } if (isset($info->constraints['MessageSignature']) && !$info->creds_master && !$info->creds_hmac) { $as->error(\FutoIn\Error::SecurityError, "SecureChannel is required"); } if (isset($info->constraints['BiDirectChannel']) && !false) { $as->error(\FutoIn\Error::InvokerError, "BiDirectChannel is required"); } } $this->emit('register', [$name, $ifacever, $info]); }); }
public function call(\FutoIn\AsyncSteps $as, $name, $params, $upload_data = null, $download_stream = null, $timeout = null) { $ctx = new \StdClass(); $ctx->name = $name; $ctx->info = $this->raw_info; $ctx->upload_data = $upload_data; $ctx->download_stream = $download_stream; $params = (object) $params; // Create message //--- $as->add(function ($as) use($ctx, $params) { $this->ccmimpl->createMessage($as, $ctx, $params); }); // Perform request //--- $as->add(function ($as, $req) use($ctx, $upload_data, $download_stream, $timeout) { $curl_opts = array(CURLOPT_FORBID_REUSE => FALSE, CURLOPT_FRESH_CONNECT => FALSE, CURLOPT_NETRC => FALSE, CURLOPT_SSLVERSION => 3, CURLOPT_SSL_VERIFYPEER => TRUE, CURLOPT_FOLLOWLOCATION => FALSE, CURLOPT_CONNECTTIMEOUT_MS => 3000, CURLOPT_TIMEOUT_MS => 30000, CURLOPT_BINARYTRANSFER => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1) + $this->ccmimpl->curl_opts; $curl = curl_init(); $headers = array("Accept: application/futoin+json, */*"); $url = $this->raw_info->endpoint; if (substr($url, -1) !== '/') { $url .= '/'; } if ($upload_data) { // Encode according to FTN5: HTTP integration //--- // iface / ver / func $url .= str_replace(':', '/', $req->f); // /sec if (isset($req->sec)) { $url .= '/' . $req->sec; } // Params as query if (isset($req->p)) { $url .= '?' . http_build_query((array) $req->p); } //--- if (is_resource($upload_data)) { // Old C-style trick fseek($upload_data, -1, SEEK_END); $upload_size = ftell($upload_data) + 1; fseek($upload_data, 0, SEEK_SET); $curl_opts[CURLOPT_PUT] = true; $curl_opts[CURLOPT_CUSTOMREQUEST] = "POST"; $curl_opts[CURLOPT_INFILE] = $upload_data; $curl_opts[CURLOPT_INFILESIZE] = $upload_size; # Disable cURL-specific 1 second delay (empty 'Expect' does not work) $curl_opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; } else { $upload_size = strlen($upload_data); $curl_opts[CURLOPT_POST] = true; $curl_opts[CURLOPT_POSTFIELDS] = $upload_data; } $headers[] = "Content-Type: application/octet-stream"; $headers[] = 'Content-Length: ' . $upload_size; } else { $headers[] = "Content-Type: application/futoin+json"; $req = json_encode($req, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE); $curl_opts[CURLOPT_POST] = true; $curl_opts[CURLOPT_POSTFIELDS] = $req; } if ($download_stream) { $curl_opts[CURLOPT_FILE] = $download_stream; } else { $curl_opts[CURLOPT_RETURNTRANSFER] = true; // Limit size for security reasons $curl_opts[CURLOPT_PROGRESSFUNCTION] = function ($curl, $full_dlsize, $dlsize) { return $dlsize > self::MSG_MAXSIZE ? -1 : 0; }; } $curl_opts[CURLOPT_HTTPHEADER] = $headers; $curl_opts[CURLOPT_URL] = $url; curl_setopt_array($curl, $curl_opts); // cURL multi //--- if ($timeout === null) { $timeout = $curl_opts[CURLOPT_TIMEOUT_MS]; } if ((int) $timeout > 0) { $as->setTimeout($timeout); } $as->add(function ($as) use($curl) { $this->ccmimpl->multiCurlAdd($as, $curl); }); $as->add(function ($as, $curl, $info) use($ctx, $download_stream) { $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); $content_type = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); $error = curl_error($curl); curl_close($curl); //--- if ($http_code !== 200) { $as->error(\FutoIn\Error::CommError, "HTTP:{$http_code} CURL:{$error}"); } elseif ($download_stream) { if ($info['result'] === CURLE_OK) { $as->success(true); } else { $as->error(\FutoIn\Error::CommError, "CURL:{$error}"); } } elseif ($content_type === 'application/futoin+json') { $rsp = json_decode($as->_futoin_response); if ($rsp) { $this->ccmimpl->onMessageResponse($as, $ctx, $rsp); } else { $as->error(\FutoIn\Error::CommError, "JSON:" . json_last_error_msg()); } } else { $this->ccmimpl->onDataResponse($as, $ctx); } }); }); }
public function onDataResponse(\FutoIn\AsyncSteps $as, $ctx) { $as->success($as->_futoin_response); }
public function onDataResponse(\FutoIn\AsyncSteps $as, $ctx) { if ($ctx->info->funcs[$ctx->name]->rawresult) { $as->success($as->_futoin_response); } else { $as->error(\FutoIn\Error::InternalError, "Raw result is not expected"); } }
public static function checkFutoInType(\FutoIn\AsyncSteps $as, $type, $var, $val) { $rtype = ''; switch ($type) { case 'boolean': case 'integer': case 'string': case 'array': $rtype = $type; break; case 'map': $rtype = 'object'; break; case 'number': $rtype = 'string'; break; } if (gettype($val) !== $rtype) { $as->error(\FutoIn\Error::InvalidRequest, "Type mismatch for parameter"); } }