public function __construct($request = null, $data = null) { //start stream handler $this->stream = Stream::receive($request); //store request $this->request = $this->stream->decode(); //store data $this->request_data = $data; //start logging $this->log = Log::_get()->setLevel(Log::WARN)->setLabel('Xport-Resp'); }
public static function process($xp, $crypt = null) { Validate::prime($xp->get()); Validate::go('action')->not('blank')->is('al'); Validate::paint(); $stream = Stream::receive($xp->getRequestData(), $crypt); $fileio = static::_get(); switch ($xp->get('action')) { case static::FILE_IO_READ: //validate request Validate::prime($xp->get()); Validate::go('path')->not('blank'); Validate::go('offset')->is('num'); Validate::go('length')->is('num'); Validate::paint(); //setup and read $fileio->setPath($xp->get('path')); $fileio->setOffset($xp->get('offset')); $data = $fileio->read($xp->get('length')); $sha1 = sha1($data); $md5 = md5($data); //tune data $stream->setPayload($sha1 . $md5 . $data); //pass data back return $xp->addResponseData($stream->encode()); break; case static::FILE_IO_WRITE: //validate request Validate::prime($xp->get()); Validate::go('path')->not('blank'); Validate::go('offset')->is('num'); Validate::paint(); //validate payload $data = $stream->decode(); $sha1 = substr($data, 0, 40); $md5 = substr($data, 40, 32); $data = substr($data, 72); if (sha1($data) !== $sha1) { throw new Exception('Payload hash mismatch (sha1) block could not be written'); } if (md5($data) !== $md5) { throw new Exception('Payload hash mismatch (md5) block could not be written'); } //Setup and write $fileio->setPath($xp->get('path')); $fileio->setOffset($xp->get('offset')); if (($bytes_written = $fileio->write($data)) === false) { throw new Exception('Failed to write block'); } //pass success return $xp->add('bytes_written', $bytes_written); break; case static::FILE_IO_RCOPY: //validate request Validate::prime($xp->get()); Validate::go('path')->not('blank'); Validate::go('offset')->is('num'); Validate::go('remote_path')->not('blank'); Validate::go('remote_offset')->is('num'); Validate::go('remote_length')->is('num'); Validate::paint(); //setup and copy $fileio->setPath($xp->get('path')); $fileio->setOffset($xp->get('offset')); if (!($bytes_written = $fileio->rcopy($xp->get('remote_path'), $xp->get('remote_offset'), $xp->get('remote_length')))) { throw new Exception('Failed to copy from remote file'); } //pass success return $xp->add('bytes_written', $bytes_written); break; case static::FILE_IO_STORE: //validate request Validate::prime($xp->get()); Validate::go('path')->not('blank'); Validate::paint(); //store new file $fileio->setPath($xp->get('path')); //send back file info return $xp->add('file', $fileio->store()); break; default: throw new Exception('Invalid File IO action'); break; } }
public function read($offset = 0, $length = null, $crypt = null) { if (strpos($this->getMode(), 'w') === 0 || strpos($this->getMode(), 'a') === 0) { throw new Exception('Reading not supported in write/append only mode'); } //set read length if null $readsize = min($this->getMaxReadLength(), $this->buffer_max); if (is_null($length)) { $length = $readsize; } //validate length format if (!is_numeric($length) || $length < 1) { throw new Exception('Read request must have a length'); } //sanity check offset and length with known file size if ($offset > $this->getSize()) { return false; } //eof //clamp the length to what's left in the file if ($offset + $length > $this->getSize()) { $length = $this->getSize() - $offset; } //clamp length to max if ($length > $readsize) { $length = $readsize; } //calculate our buffer window $buf_start = $this->buffer_ptr === -1 ? 0 : $this->buffer_ptr; $buf_end = $buf_start + $this->getBufferSize(); if ($offset < $buf_start || $offset + $length > $buf_end) { //we aren't in our buffered block, so fill the buffer with something useful //build request $url = $this->getURI(); $data = $this->block_stream->encode(); $rv = $this->call($url, array('action' => self::FILE_IO_READ, 'path' => $this->getPath(), 'offset' => $offset, 'length' => $readsize), $data); $rv = Stream::receive($data, $crypt)->decode(); //first 40 bytes is the sha1 $sha1 = substr($rv, 0, 40); //next 32 bytes is the md5 $md5 = substr($rv, 40, 32); //strip the first 72 bytes to get the real paylod $rv = substr($rv, 72, strlen($rv)); //verify sha1 if (sha1($rv) !== $sha1) { throw new Exception('Read failed, payload hash mismatch (sha1)'); } //verify the md5 if (md5($rv) !== $md5) { throw new Exception('Read failed, payload hash mismatch (md5)'); } //safe to buffer $this->setBuffer($offset, $rv); } //we should have the request data in the buffer now, deliver the request return $this->getBufferSlice($offset, $length); }
public function call($uri, $cmd = array(), &$data = null, $flags = array()) { $call_start = microtime(true); $url = $this->makeURL($uri); //inject auth params if (!is_callable(array($this->auth_handler, 'requestParams'))) { throw new Exception('Auth handler doesnt support requests'); } $cmd = array_merge($cmd, call_user_func($this->auth_handler . '::requestParams')); //print some info $this->log->add('Setting up call to: ' . $url); $this->log->add('Command Params: ' . print_r($cmd, true)); $this->log->add('Flags Present: ' . print_r($flags, true), Log::DEBUG); if (!is_null($data)) { $this->log->add('Data present (' . strlen($data) . '): ' . substr($data, 0, 50) . '...', Log::DEBUG); } //encode cmd params $request = $this->encode($cmd); $this->log->add('Request Encoded: ' . $request, Log::DEBUG); //start curl if needed if (!$this->ch) { $this->initCURL(); } //set the url to hit curl_setopt($this->ch, CURLOPT_URL, $url); //add the payload to the stream $this->stream->setPayload($request); //setup curl post $post_query = http_build_query(array('request' => $this->stream->encode(), 'data' => $data)); curl_setopt($this->ch, CURLOPT_POSTFIELDS, $post_query); //if noexec is passed we simple pass the prepared curl handle back if (in_array(self::CALL_NOEXEC, $flags)) { return $this->ch; } //try the actual call $tries = 1; //TODO: tunables that need to be in the config $max_tries = 10; //the retry sleep gets multiplied by the number of tries // thus the first call waits X ms and the last call waits X * $max_tries ms $retry_sleep = 300; //in ms //anything not defined here will trickle down and throw an exception $http_status_retry = array(5); $http_status_complete = array(2, 3); do { //execute the call $result = curl_exec($this->ch); if ($result === false) { throw new Exception('Call failed to ' . $url . ' ' . curl_error($this->ch)); } //check the response status $http_status = curl_getinfo($this->ch, CURLINFO_HTTP_CODE); $http_status_class = floor($http_status / 100); if (in_array($http_status_class, $http_status_retry)) { $sleep_time = $retry_sleep * $tries; $this->log->add("Request failed with response code: " . $http_status . " retrying " . ($max_tries - $tries) . " more times waiting " . $sleep_time . "ms" . " until next try" . " req(" . $url . ")", Log::WARN); usleep($sleep_time * 1000); continue; } if (in_array($http_status_class, $http_status_complete)) { //exit the loop on success and continue break; } throw new Exception("Unrecognized HTTP RESPONSE CODE: " . $http_status); } while (++$tries < $max_tries); //separate channels parse_str($result, $response); unset($result); if (isset($response['data'])) { $data = $response['data']; } if (isset($response['debug']) && trim($response['debug']) != '') { debug_dump($response['debug']); } if (!isset($response['response'])) { throw new Exception('No response received with request: ' . print_r($response, true)); } $response = $response['response']; //decode return payload $response = Stream::receive($response, $this->stream->getCrypt())->decode(); $this->log->add('Response Raw (' . strlen($response) . '): ' . substr($response, 0, 50) . '...', Log::DEBUG); //decode the response $encoding = $this->decode($response); //log response if ($encoding != self::ENC_RAW) { // $response = array_shift($response); $this->log->add('Response received: ' . print_r($response, true), Log::DEBUG); //pass to error handler $this->errorHandler($response); } $this->log->add('Request took ' . number_format(microtime(true) - $call_start, 5) . ' seconds', Log::DEBUG); //if we got here there were no errors return $response; }