Exemplo n.º 1
0
  def post(self):
    """Handle POST requests by executing the API call."""
    if not self.CheckIsAdmin():
      return
















    self.response.headers['Content-Type'] = 'text/plain'

    response = remote_api_pb.Response()
    try:
      request = remote_api_pb.Request()



      request.ParseFromString(self.request.body)
      response_data = self.ExecuteRequest(request)
      response.set_response(response_data.Encode())
      self.response.set_status(200)
    except Exception as e:
      logging.exception('Exception while handling %s', request)
      self.response.set_status(200)



      response.set_exception(pickle.dumps(e))
      if isinstance(e, apiproxy_errors.ApplicationError):
        application_error = response.mutable_application_error()
        application_error.set_code(e.application_error)
        application_error.set_detail(e.error_detail)
    self.response.out.write(response.Encode())
    def MakeSyncCall(self, service, call, request, response, request_id=None):
        """ The main RPC entry point.

    Args:
      service: Must be name as provided to service_name of constructor.
      call: A string representing the rpc to make.  Must be part of
        the underlying services methods and impemented by _Dynamic_<call>.
      request: A protocol buffer of the type corresponding to 'call'.
      response: A protocol buffer of the type corresponding to 'call'.
      request_id: A unique string identifying the request associated with the
          API call.
    """
        assert service == self._service_name, ('Expected "%s" service name, '
                                               'was "%s"' %
                                               (self._service_name, service))

        if request.ByteSize() > self._max_request_size:
            raise apiproxy_errors.RequestTooLargeError(
                'The request to API call %s.%s() was too large.' %
                (service, call))

        messages = []
        assert request.IsInitialized(messages), messages

        remote_api_request = remote_api_pb.Request()
        remote_api_request.set_service_name(service)
        remote_api_request.set_method(call)
        remote_api_request.set_request(request.Encode())
        if request_id is not None:
            remote_api_request.set_request_id(request_id)

        url = 'http://{}'.format(self._location)
        request_handle = urllib2.Request(url, remote_api_request.Encode())
        response_handle = urllib2.urlopen(request_handle)
        remote_api_response = remote_api_pb.Response(response_handle.read())
        if remote_api_response.has_application_error():
            error_pb = remote_api_response.application_error()
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())

        if remote_api_response.has_exception():
            raise pickle.loads(remote_api_response.exception())

        response.ParseFromString(remote_api_response.response())
Exemplo n.º 3
0
    def _RemoteSend(self, request, response, method, request_id=None):
        """Sends a request remotely to the taskqueue server.

    Args:
      request: A protocol buffer request.
      response: A protocol buffer response.
      method: The function which is calling the remote server.
      request_id: A string specifying a request ID.
    Raises:
      taskqueue_service_pb.InternalError:
    """
        tag = self.__app_id
        api_request = remote_api_pb.Request()
        api_request.set_method(method)
        api_request.set_service_name("taskqueue")
        api_request.set_request(request.Encode())
        if request_id is not None:
            api_request.set_request_id(request_id)

        tq_locations = self._GetTQLocations()
        for index, tq_location in enumerate(tq_locations):
            api_response = remote_api_pb.Response()
            api_response = api_request.sendCommand(tq_location, tag,
                                                   api_response, 1, False,
                                                   KEY_LOCATION, CERT_LOCATION)

            if not api_response or not api_response.has_response():
                if index >= len(tq_locations) - 1:
                    raise apiproxy_errors.ApplicationError(
                        taskqueue_service_pb.TaskQueueServiceError.
                        INTERNAL_ERROR)
            else:
                break

        if api_response.has_application_error():
            error_pb = api_response.application_error()
            logging.error(error_pb.detail())
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())

        if api_response.has_exception():
            raise api_response.exception()

        response.ParseFromString(api_response.response())
Exemplo n.º 4
0
    def _RemoteSend(self, request, response, method, request_id=None):
        """Sends a request remotely to the datstore server. """
        tag = self.__app_id
        self._maybeSetDefaultAuthDomain()
        user = users.GetCurrentUser()
        if user != None:
            tag += ":" + user.email()
            tag += ":" + user.nickname()
            tag += ":" + user.auth_domain()
        api_request = remote_api_pb.Request()
        api_request.set_method(method)
        api_request.set_service_name("datastore_v3")
        api_request.set_request(request.Encode())
        if request_id is not None:
            api_request.set_request_id(request_id)

        api_response = remote_api_pb.Response()
        try:
            api_response = api_request.sendCommand(self.__datastore_location,
                                                   tag, api_response, 1,
                                                   self.__is_encrypted,
                                                   KEY_LOCATION, CERT_LOCATION)
        except socket.error as socket_error:
            if socket_error.errno == errno.ETIMEDOUT:
                raise apiproxy_errors.ApplicationError(
                    datastore_pb.Error.TIMEOUT,
                    'Connection timed out when making datastore request')
            raise

        if not api_response or not api_response.has_response():
            raise datastore_errors.InternalError(
                'No response from db server on %s requests.' % method)

        if api_response.has_application_error():
            error_pb = api_response.application_error()
            logging.error(error_pb.detail())
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())

        if api_response.has_exception():
            raise api_response.exception()

        response.ParseFromString(api_response.response())
Exemplo n.º 5
0
  def _MakeRemoteSyncCall(self, service, call, request, response):
    """Send an RPC to a remote_api endpoint."""
    request_pb = remote_api_pb.Request()
    request_pb.set_service_name(service)
    request_pb.set_method(call)
    request_pb.set_request(request.Encode())

    response_pb = remote_api_pb.Response()
    encoded_request = request_pb.Encode()
    try:
      urlfetch_response = urlfetch.fetch(self.remote_url, encoded_request,
                                         urlfetch.POST, self.extra_headers,
                                         follow_redirects=False,
                                         deadline=10)
    except Exception, e:


      logging.exception('Fetch failed to %s', self.remote_url)
      raise FetchFailed(e)
Exemplo n.º 6
0
def CreateRequestPB(package, call, ticket, body):
  """Create a remote_api_pb.Request functionally.

  Args:
    package: String to set as the service name (package name).
    call: String to set as the method (API call name).
    ticket: String to set as the request ID (ticket).
    body: String to set as the request body.

  Returns:
    A remote_api_pb.Request.
  """

  request = remote_api_pb.Request()
  request.set_service_name(package)
  request.set_method(call)
  request.set_request_id(ticket)
  request.set_request(body)
  return request
Exemplo n.º 7
0
    def _handle_POST(self, environ, start_response):
        start_response('200 OK',
                       [('Content-Type', 'application/octet-stream')])

        start_time = time.time()
        response = remote_api_pb.Response()
        try:
            request = remote_api_pb.Request()
            # NOTE: Exceptions encountered when parsing the PB or handling the request
            # will be propagated back to the caller the same way as exceptions raised
            # by the actual API call.
            if environ.get('HTTP_TRANSFER_ENCODING') == 'chunked':
                # CherryPy concatenates all chunks  when 'wsgi.input' is read but v3.2.2
                # will not return even when all of the data in all chunks has been
                # read. See: https://bitbucket.org/cherrypy/cherrypy/issue/1131.
                wsgi_input = environ['wsgi.input'].read(2**32)
            else:
                wsgi_input = environ['wsgi.input'].read(
                    int(environ['CONTENT_LENGTH']))
            request.ParseFromString(wsgi_input)
            with self._request_context(environ) as request_id:
                api_response = _execute_request(request, request_id).Encode()
            response.set_response(api_response)
        except Exception, e:
            if isinstance(e, apiproxy_errors.ApplicationError):
                level = logging.DEBUG
                application_error = response.mutable_application_error()
                application_error.set_code(e.application_error)
                application_error.set_detail(e.error_detail)
                # TODO: is this necessary? Python remote stub ignores exception
                # when application error is specified; do other runtimes use it?
                response.set_exception(pickle.dumps(e))
            else:
                # If the runtime instance is not Python, it won't be able to unpickle
                # the exception so use level that won't be ignored by default.
                level = logging.ERROR
                # Even if the runtime is Python, the exception may be unpicklable if
                # it requires importing a class blocked by the sandbox so just send
                # back the exception representation.
                response.set_exception(pickle.dumps(RuntimeError(repr(e))))
            logging.log(level, 'Exception while handling %s\n%s', request,
                        traceback.format_exc())
Exemplo n.º 8
0
  def _RemoteSend(self, request, response, method):
    """ Sends a request remotely to the search server.

    Args:
      request: A request object.
      response: A response object to be filled in.
      method: A str, the dynamic function doing the call.
    """
    if not self.__search_location:
      raise search.InternalError("Search service not configured.")

    api_request = remote_api_pb.Request()
    api_request.set_method(method)
    api_request.set_service_name("search")
    api_request.set_request(request.Encode())

    api_response = remote_api_pb.Response()
    api_response = api_request.sendCommand(self.__search_location,
      "",
      api_response,
      1,
      False,
      KEY_LOCATION,
      CERT_LOCATION)

    if api_response.has_application_error():
      error_pb = api_response.application_error()
      logging.error(error_pb.detail())
      raise apiproxy_errors.ApplicationError(error_pb.code(),
                                             error_pb.detail())

    if api_response.has_exception():
      raise api_response.exception()

    if not api_response or not api_response.has_response():
      raise search.InternalError(
          'No response from search server on %s requests.' % method)

    response.ParseFromString(api_response.response())
Exemplo n.º 9
0
  def _MakeRealSyncCall(self, service, call, request, response):
    request_pb = remote_api_pb.Request()
    request_pb.set_service_name(service)
    request_pb.set_method(call)
    request_pb.set_request(request.Encode())

    response_pb = remote_api_pb.Response()
    encoded_request = request_pb.Encode()
    encoded_response = self._server.Send(self._path, encoded_request)
    response_pb.ParseFromString(encoded_response)
    
    if response_pb.has_application_error():
      error_pb = response_pb.application_error()
      raise apiproxy_errors.ApplicationError(error_pb.code(),
                                             error_pb.detail())
    elif response_pb.has_exception():
      raise pickle.loads(response_pb.exception())
    elif response_pb.has_java_exception():
      raise UnknownJavaServerError("An unknown error has occured in the "
                                   "Java remote_api handler for this call.")
    else:
      response.ParseFromString(response_pb.response())
Exemplo n.º 10
0
    def _MakeRemoteSyncCall(self, service, call, request, response):
        """Send an RPC to a remote_api endpoint."""
        request_pb = remote_api_pb.Request()
        request_pb.set_service_name(service)
        request_pb.set_method(call)
        request_pb.set_request(request.Encode())

        response_pb = remote_api_pb.Response()
        encoded_request = request_pb.Encode()
        try:
            urlfetch_response = urlfetch.fetch(self.remote_url,
                                               encoded_request,
                                               urlfetch.POST,
                                               self.extra_headers,
                                               follow_redirects=False,
                                               deadline=10)
        except Exception as e:

            logging.exception('Fetch failed to %s', self.remote_url)
            raise FetchFailed(e)
        if urlfetch_response.status_code != 200:
            logging.error('Fetch failed to %s; Status %s; body %s',
                          self.remote_url, urlfetch_response.status_code,
                          urlfetch_response.content)
            raise FetchFailed(urlfetch_response.status_code)
        response_pb.ParseFromString(urlfetch_response.content)

        if response_pb.has_application_error():
            error_pb = response_pb.application_error()
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())
        elif response_pb.has_exception():
            raise pickle.loads(response_pb.exception())
        elif response_pb.has_java_exception():
            raise UnknownJavaServerError(
                'An unknown error has occured in the '
                'Java remote_api handler for this call.')
        else:
            response.ParseFromString(response_pb.response())
Exemplo n.º 11
0
    def _make_request(self, method, body):
        request = remote_api_pb.Request()
        request.set_service_name(self.SERVICE_NAME)
        request.set_method(method)
        request.set_request(body)

        location = random.choice(self._locations)
        url = 'http://{}'.format(location)
        headers = {'protocolbuffertype': 'Request', 'appdata': PROJECT_ID}
        response = yield self._client.fetch(url,
                                            method='POST',
                                            body=request.Encode(),
                                            headers=headers)
        api_response = remote_api_pb.Response(response.body)

        if api_response.has_application_error():
            raise DatastoreError(api_response.application_error().detail())

        if api_response.has_exception():
            raise DatastoreError(str(api_response.exception()))

        raise gen.Return(api_response.response())
Exemplo n.º 12
0
    def do_POST(self):
        """Handles a single API request e.g. memcache.Get()."""
        self.send_response(httplib.OK)
        self.send_header('Content-Type', 'application/octet-stream')
        self.end_headers()

        response = remote_api_pb.Response()
        try:
            request = remote_api_pb.Request()

            request.ParseFromString(
                self.rfile.read(int(self.headers['content-length'])))
            api_response = _ExecuteRequest(request).Encode()
            response.set_response(api_response)
        except Exception, e:
            logging.debug('Exception while handling %s\n%s', request,
                          traceback.format_exc())
            response.set_exception(pickle.dumps(e))
            if isinstance(e, apiproxy_errors.ApplicationError):
                application_error = response.mutable_application_error()
                application_error.set_code(e.application_error)
                application_error.set_detail(e.error_detail)
Exemplo n.º 13
0
  def handle_read(self):
    self.data += self.recv(8192)
    if self.n == -1:
      i = self.data.find('\n')
      if i == -1:

        return
      try:
        self.n = int(self.data[:i])
      except:
        self.n = -2
      if self.n < 0:

        self.n = -2
        self.data = ''
        return
      self.data = self.data[i+1:]
    elif self.n == -2:
      self.data = ''
      return
    if len(self.data) < self.n:

      return

    req = remote_api_pb.Request()
    req.ParseFromString(self.data[:self.n])
    self.data, self.n = self.data[self.n:], -1
    rapi_result = None
    rapi_error = 'unknown error'
    try:
      rapi_result = RAPI_HANDLER.ExecuteRequest(req)
    except apiproxy_errors.CallNotFoundError, e:


      service_name = req.service_name()
      method = req.method()
      rapi_error = 'call not found for %s/%s' % (service_name, method)
Exemplo n.º 14
0
    def _MakeCallImpl(self):
        """Makes an asynchronous API call over the service bridge.

    For this to work the following must be set:
      self.package: the API package name;
      self.call: the name of the API call/method to invoke;
      self.request: the API request body as a serialized protocol buffer.

    The actual API call is made by requests.post via a thread pool
    (multiprocessing.dummy.Pool). The thread pool restricts the number of
    concurrent requests to MAX_CONCURRENT_API_CALLS, so this method will
    block if that limit is exceeded, until other asynchronous calls resolve.

    If the main thread holds the import lock, waiting on thread work can cause
    a deadlock:
    https://docs.python.org/2/library/threading.html#importing-in-threaded-code

    Therefore, we try to detect this error case and fall back to sync calls.
    """
        assert self._state == apiproxy_rpc.RPC.IDLE, self._state

        self.lock = threading.Lock()
        self.event = threading.Event()

        if VMStub.ShouldUseRequestSecurityTicketForThread():

            ticket = os.environ.get(
                TICKET_HEADER,
                os.environ.get(DEV_TICKET_HEADER, self.stub.DefaultTicket()))
        else:
            ticket = self.stub.DefaultTicket()

        request = remote_api_pb.Request()
        request.set_service_name(self.package)
        request.set_method(self.call)
        request.set_request_id(ticket)
        request.set_request(self.request.SerializeToString())

        deadline = self.deadline or DEFAULT_TIMEOUT

        body_data = request.SerializeToString()
        headers = {
            SERVICE_DEADLINE_HEADER: str(deadline),
            SERVICE_ENDPOINT_HEADER: SERVICE_ENDPOINT_NAME,
            SERVICE_METHOD_HEADER: APIHOST_METHOD,
            'Content-type': RPC_CONTENT_TYPE,
        }

        dapper_header_value = os.environ.get(DAPPER_ENV_KEY)
        if dapper_header_value:
            headers[DAPPER_HEADER] = dapper_header_value

        api_host = os.environ.get('API_HOST', SERVICE_BRIDGE_HOST)
        api_port = os.environ.get('API_PORT', API_PORT)

        endpoint_url = urlparse.urlunparse(
            ('http', '%s:%s' % (api_host, api_port), PROXY_PATH, '', '', ''))

        self._state = apiproxy_rpc.RPC.RUNNING

        request_kwargs = dict(url=endpoint_url,
                              timeout=DEADLINE_DELTA_SECONDS + deadline,
                              headers=headers,
                              data=body_data)

        if imp.lock_held() and not app_is_loaded:
            try:
                value = CaptureStacktrace(requests.post, **request_kwargs)
                success = True
            except Exception as e:
                value = e
                success = False
            self._result_future = SyncResult(value, success)

        else:

            self._result_future = self.stub.thread_pool.apply_async(
                CaptureStacktrace, args=[requests.post], kwds=request_kwargs)
Exemplo n.º 15
0
    def remote_request(self, app_info, http_request_data):
        """ Receives a remote request to which it should give the correct
    response. The http_request_data holds an encoded protocol buffer of a
    certain type. Each type has a particular response type.

    Args:
      app_info: A dictionary containing the application, module, and version ID
        of the app that is sending this request.
      http_request_data: Encoded protocol buffer.
    """
        apirequest = remote_api_pb.Request()
        apirequest.ParseFromString(http_request_data)
        apiresponse = remote_api_pb.Response()
        response = None
        errcode = 0
        errdetail = ""
        method = ""
        http_request_data = ""
        app_id = app_info['app_id']
        if not apirequest.has_method():
            errcode = taskqueue_service_pb.TaskQueueServiceError.INVALID_REQUEST
            errdetail = "Method was not set in request"
            apirequest.set_method("NOT_FOUND")
        else:
            method = apirequest.method()

        if not apirequest.has_request():
            errcode = taskqueue_service_pb.TaskQueueServiceError.INVALID_REQUEST
            errdetail = "Request missing in call"
            apirequest.set_method("NOT_FOUND")
            apirequest.clear_request()
        else:
            http_request_data = apirequest.request()

        start_time = time.time()

        request_log = method
        if apirequest.has_request_id():
            request_log += ': {}'.format(apirequest.request_id())
        logger.debug(request_log)

        result = None
        if method == "FetchQueueStats":
            result = self.queue_handler.fetch_queue_stats(
                app_id, http_request_data)
        elif method == "PurgeQueue":
            result = self.queue_handler.purge_queue(app_id, http_request_data)
        elif method == "Delete":
            result = self.queue_handler.delete(app_id, http_request_data)
        elif method == "QueryAndOwnTasks":
            result = self.queue_handler.query_and_own_tasks(
                app_id, http_request_data)
        elif method == "Add":
            result = self.queue_handler.add(app_info, http_request_data)
        elif method == "BulkAdd":
            result = self.queue_handler.bulk_add(app_info, http_request_data)
        elif method == "ModifyTaskLease":
            result = self.queue_handler.modify_task_lease(
                app_id, http_request_data)
        elif method == "UpdateQueue":
            response = taskqueue_service_pb.TaskQueueUpdateQueueResponse()
            result = self.queue_handler.Encode(), 0, ""
        elif method == "FetchQueues":
            result = self.queue_handler.fetch_queue(app_id, http_request_data)
        elif method == "QueryTasks":
            result = self.queue_handler.query_tasks(app_id, http_request_data)
        elif method == "FetchTask":
            result = self.queue_handler.fetch_task(app_id, http_request_data)
        elif method == "ForceRun":
            result = self.queue_handler.force_run(app_id, http_request_data)
        elif method == "DeleteQueue":
            response = taskqueue_service_pb.TaskQueueDeleteQueueResponse()
            result = self.queue_handler.Encode(), 0, ""
        elif method == "PauseQueue":
            result = self.queue_handler.pause_queue(app_id, http_request_data)
        elif method == "DeleteGroup":
            result = self.queue_handler.delete_group(app_id, http_request_data)
        elif method == "UpdateStorageLimit":
            result = self.queue_handler.update_storage_limit(
                app_id, http_request_data)

        if result:
            response, errcode, errdetail = result

        elapsed_time = round(time.time() - start_time, 3)
        timing_log = 'Elapsed: {}'.format(elapsed_time)
        if apirequest.has_request_id():
            timing_log += ' ({})'.format(apirequest.request_id())
        logger.debug(timing_log)

        if response is not None:
            apiresponse.set_response(response)

        # If there was an error add it to the response.
        if errcode != 0:
            apperror_pb = apiresponse.mutable_application_error()
            apperror_pb.set_code(errcode)
            apperror_pb.set_detail(errdetail)

        self.write(apiresponse.Encode())
        status = taskqueue_service_pb.TaskQueueServiceError.ErrorCode_Name(
            errcode)
        return method, status
Exemplo n.º 16
0
    def _RemoteSend(self,
                    request,
                    response,
                    method,
                    request_id=None,
                    service_id=None,
                    version_id=None):
        """Sends a request remotely to the taskqueue server.

    Args:
      request: A protocol buffer request.
      response: A protocol buffer response.
      method: The function which is calling the remote server.
      request_id: A string specifying a request ID.
      service_id: A string specifying the client service ID.
      version_id: A string specifying the client version ID.
    Raises:
      taskqueue_service_pb.InternalError:
    """
        tag = self.__app_id
        api_request = remote_api_pb.Request()
        api_request.set_method(method)
        api_request.set_service_name("taskqueue")
        api_request.set_request(request.Encode())
        if request_id is not None:
            api_request.set_request_id(request_id)

        api_response = remote_api_pb.Response()

        retry_count = 0
        max_retries = 3
        location = random.choice(self.__tq_locations)
        while True:
            try:
                api_request.sendCommand(location, tag, api_response, 1, False,
                                        KEY_LOCATION, CERT_LOCATION)
                break
            except socket.error as socket_error:
                if socket_error.errno in (errno.ECONNREFUSED,
                                          errno.EHOSTUNREACH):
                    backoff_ms = 500 * 3**retry_count  # 0.5s, 1.5s, 4.5s
                    retry_count += 1
                    if retry_count > max_retries:
                        raise

                    logging.warning(
                        'Failed to call {} method of TaskQueue ({}). Retry #{} in {}ms.'
                        .format(method, socket_error, retry_count, backoff_ms))
                    time.sleep(float(backoff_ms) / 1000)
                    location = random.choice(self.__tq_locations)
                    api_response = remote_api_pb.Response()
                    continue

                if socket_error.errno == errno.ETIMEDOUT:
                    raise apiproxy_errors.ApplicationError(
                        taskqueue_service_pb.TaskQueueServiceError.
                        INTERNAL_ERROR,
                        'Connection timed out when making taskqueue request')
                raise
            # AppScale: Interpret ProtocolBuffer.ProtocolBufferReturnError as
            # datastore_errors.InternalError
            except ProtocolBuffer.ProtocolBufferReturnError as e:
                raise apiproxy_errors.ApplicationError(
                    taskqueue_service_pb.TaskQueueServiceError.INTERNAL_ERROR,
                    str(e))

        if not api_response or not api_response.has_response():
            raise apiproxy_errors.ApplicationError(
                taskqueue_service_pb.TaskQueueServiceError.INTERNAL_ERROR)

        if api_response.has_application_error():
            error_pb = api_response.application_error()
            logging.error(error_pb.detail())
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())

        if api_response.has_exception():
            raise api_response.exception()

        response.ParseFromString(api_response.response())
Exemplo n.º 17
0
  def remote_request(self, app_id, http_request_data, service_id, version_id):
    """ Receives a remote request to which it should give the correct 
        response. The http_request_data holds an encoded protocol buffer
        of a certain type. Each type has a particular response type. 
    
    Args:
      app_id: The application ID that is sending this request.
      http_request_data: Encoded protocol buffer.
      service_id: A string specifying the client's service ID.
      version_id: A string specifying the client's version ID.
    """
    apirequest = remote_api_pb.Request()
    apirequest.ParseFromString(http_request_data)
    apiresponse = remote_api_pb.Response()
    response = None
    if not apirequest.has_method():
      apirequest.set_method("NOT_FOUND")
    if not apirequest.has_request():
      apirequest.set_method("NOT_FOUND")
      apirequest.clear_request()
    method = apirequest.method()
    http_request_data = apirequest.request()
    start = time.time()

    request_log = method
    if apirequest.has_request_id():
      request_log += ': {}'.format(apirequest.request_id())
    logger.debug(request_log)

    if method == "Put":
      response, errcode, errdetail = yield self.put_request(
        app_id, http_request_data)
    elif method == "Get":
      response, errcode, errdetail = yield self.get_request(
        app_id, http_request_data)
    elif method == "Delete":
      response, errcode, errdetail = yield self.delete_request(
        app_id, http_request_data)
    elif method == "RunQuery":
      response, errcode, errdetail = yield self.run_query(http_request_data)
    elif method == "BeginTransaction":
      response, errcode, errdetail = yield self.begin_transaction_request(
        app_id, http_request_data)
    elif method == "Commit":
      response, errcode, errdetail = yield self.commit_transaction_request(
        app_id, http_request_data)
    elif method == "Rollback":
      response, errcode, errdetail = self.rollback_transaction_request(
        app_id, http_request_data)
    elif method == "AllocateIds":
      response, errcode, errdetail = yield self.allocate_ids_request(
        app_id, http_request_data)
    elif method == "CreateIndex":
      response, errcode, errdetail = yield self.create_index_request(
        app_id, http_request_data)
    elif method == "GetIndices":
      response, errcode, errdetail = yield self.get_indices_request(app_id)
    elif method == "UpdateIndex":
      response, errcode, errdetail = self.update_index_request(
        app_id, http_request_data)
    elif method == "DeleteIndex":
      response, errcode, errdetail = yield self.delete_index_request(
        app_id, http_request_data)
    elif method == 'AddActions':
      response, errcode, errdetail = yield self.add_actions_request(
        app_id, http_request_data, service_id, version_id)
    elif method == 'datastore_v4.AllocateIds':
      response, errcode, errdetail = yield self.v4_allocate_ids_request(
        app_id, http_request_data)
    else:
      errcode = datastore_pb.Error.BAD_REQUEST
      errdetail = "Unknown datastore message"

    time_taken = time.time() - start
    if method in STATS:
      if errcode in STATS[method]:
        prev_req, pre_time = STATS[method][errcode]
        STATS[method][errcode] = prev_req + 1, pre_time + time_taken
      else:
        STATS[method][errcode] = (1, time_taken)
    else:
      STATS[method] = {}
      STATS[method][errcode] = (1, time_taken)

    apiresponse.set_response(response)
    if errcode != 0:
      apperror_pb = apiresponse.mutable_application_error()
      apperror_pb.set_code(errcode)
      apperror_pb.set_detail(errdetail)

    self.write(apiresponse.Encode())
Exemplo n.º 18
0
    def _MakeCallImpl(self):
        """Makes an asynchronous API call over the service bridge.

    For this to work the following must be set:
      self.package: the API package name;
      self.call: the name of the API call/method to invoke;
      self.request: the API request body as a serialized protocol buffer.

    The actual API call is made by requests.post via a thread pool
    (multiprocessing.dummy.Pool). The thread pool restricts the number of
    concurrent requests to MAX_CONCURRENT_API_CALLS, so this method will
    block if that limit is exceeded, until other asynchronous calls resolve.
    """
        assert self._state == apiproxy_rpc.RPC.IDLE, self._state

        self.lock = threading.Lock()
        self.event = threading.Event()

        if VMStub.ShouldUseRequestSecurityTicketForThread():

            ticket = os.environ.get(
                TICKET_HEADER,
                os.environ.get(DEV_TICKET_HEADER, self.stub.DefaultTicket()))
        else:
            ticket = self.stub.DefaultTicket()

        request = remote_api_pb.Request()
        request.set_service_name(self.package.encode())
        request.set_method(self.call.encode())
        request.set_request_id(ticket.encode())
        request.set_request(self.request.SerializeToString())

        deadline = self.deadline or DEFAULT_TIMEOUT

        body_data = request.SerializeToString()
        headers = {
            SERVICE_DEADLINE_HEADER: deadline,
            SERVICE_ENDPOINT_HEADER: SERVICE_ENDPOINT_NAME,
            SERVICE_METHOD_HEADER: APIHOST_METHOD,
            'Content-type': RPC_CONTENT_TYPE,
        }

        dapper_header_value = os.environ.get(DAPPER_ENV_KEY)
        if dapper_header_value:
            headers[DAPPER_HEADER] = dapper_header_value

        api_host = os.environ.get('API_HOST', SERVICE_BRIDGE_HOST)
        api_port = os.environ.get('API_PORT', API_PORT)

        endpoint_url = urllib.parse.urlunparse(
            ('http', '%s:%s' % (api_host, api_port), PROXY_PATH, '', '', ''))

        self._state = apiproxy_rpc.RPC.RUNNING

        request_kwargs = dict(url=endpoint_url,
                              timeout=DEADLINE_DELTA_SECONDS + deadline,
                              headers=headers,
                              data=body_data)

        self._result_future = self.stub.thread_pool.apply_async(
            requests.post, kwds=request_kwargs)
Exemplo n.º 19
0
    def remote_request(self, app_id, http_request_data):
        """ Receives a remote request to which it should give the correct 
        response. The http_request_data holds an encoded protocol buffer
        of a certain type. Each type has a particular response type. 
    
    Args:
      app_id: The application ID that is sending this request.
      http_request_data: Encoded protocol buffer.
    """
        global task_queue
        apirequest = remote_api_pb.Request()
        apirequest.ParseFromString(http_request_data)
        apiresponse = remote_api_pb.Response()
        response = None
        errcode = 0
        errdetail = ""
        apperror_pb = None
        method = ""
        http_request_data = ""

        if not apirequest.has_method():
            errcode = taskqueue_service_pb.TaskQueueServiceError.INVALID_REQUEST
            errdetail = "Method was not set in request"
            apirequest.set_method("NOT_FOUND")
        else:
            method = apirequest.method()

        if not apirequest.has_request():
            errcode = taskqueue_service_pb.TaskQueueServiceError.INVALID_REQUEST
            errdetail = "Request missing in call"
            apirequest.set_method("NOT_FOUND")
            apirequest.clear_request()
        else:
            http_request_data = apirequest.request()

        if method == "FetchQueueStats":
            response, errcode, errdetail = task_queue.fetch_queue_stats(
                app_id, http_request_data)
        elif method == "PurgeQueue":
            response, errcode, errdetail = task_queue.purge_queue(
                app_id, http_request_data)
        elif method == "Delete":
            response, errcode, errdetail = task_queue.delete(
                app_id, http_request_data)
        elif method == "QueryAndOwnTasks":
            response, errcode, errdetail = task_queue.query_and_own_tasks(
                app_id, http_request_data)
        elif method == "Add":
            response, errcode, errdetail = task_queue.add(
                app_id, http_request_data)
        elif method == "BulkAdd":
            response, errcode, errdetail = task_queue.bulk_add(
                app_id, http_request_data)
        elif method == "ModifyTaskLease":
            response, errcode, errdetail = task_queue.modify_task_lease(
                app_id, http_request_data)
        elif method == "UpdateQueue":
            response, errcode, errdetail = task_queue.update_queue(
                app_id, http_request_data)
        elif method == "FetchQueues":
            response, errcode, errdetail = task_queue.fetch_queue(
                app_id, http_request_data)
        elif method == "QueryTasks":
            response, errcode, errdetail = task_queue.query_tasks(
                app_id, http_request_data)
        elif method == "FetchTask":
            response, errcode, errdetail = task_queue.fetch_task(
                app_id, http_request_data)
        elif method == "ForceRun":
            response, errcode, errdetail = task_queue.force_run(
                app_id, http_request_data)
        elif method == "DeleteQueue":
            response, errcode, errdetail = task_queue.delete_queue(
                app_id, http_request_data)
        elif method == "PauseQueue":
            response, errcode, errdetail = task_queue.pause_queue(
                app_id, http_request_data)
        elif method == "DeleteGroup":
            response, errcode, errdetail = task_queue.delete_group(
                app_id, http_request_data)
        elif method == "UpdateStorageLimit":
            response, errcode, errdetail = task_queue.update_storage_limit(
                app_id, http_request_data)

        if response:
            apiresponse.set_response(response)

        # If there was an error add it to the response.
        if errcode != 0:
            apperror_pb = apiresponse.mutable_application_error()
            apperror_pb.set_code(errcode)
            apperror_pb.set_detail(errdetail)

        self.write(apiresponse.Encode())
Exemplo n.º 20
0
  def _handle_POST(self, environ, start_response):
    """Handles a POST request containing a serialized remote_api_pb.Request.

    Args:
      environ: An environ dict for the request as defined in PEP-333.
      start_response: A start_response function with semantics defined in
        PEP-333.

    Returns:
      A single element list containing the string body of the HTTP response.
    """
    start_response('200 OK', [('Content-Type', 'application/octet-stream')])

    start_time = time.time()
    response = remote_api_pb.Response()
    try:
      request = remote_api_pb.Request()
      # NOTE: Exceptions encountered when parsing the PB or handling the request
      # will be propagated back to the caller the same way as exceptions raised
      # by the actual API call.
      if environ.get('HTTP_TRANSFER_ENCODING') == 'chunked':
        # CherryPy concatenates all chunks  when 'wsgi.input' is read but v3.2.2
        # will not return even when all of the data in all chunks has been
        # read. See: https://bitbucket.org/cherrypy/cherrypy/issue/1131.
        wsgi_input = environ['wsgi.input'].read(2**32)
      else:
        wsgi_input = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
      request.ParseFromString(wsgi_input)

      service = request.service_name()
      service_stub = apiproxy_stub_map.apiproxy.GetStub(service)

      if isinstance(service_stub, datastore_grpc_stub.DatastoreGrpcStub):
        # len(request.request()) is equivalent to calling ByteSize() on
        # deserialized request.request.
        if len(request.request()) > apiproxy_stub.MAX_REQUEST_SIZE:
          raise apiproxy_errors.RequestTooLargeError(
              apiproxy_stub.REQ_SIZE_EXCEEDS_LIMIT_MSG_TEMPLATE % (
                  service, request.method()))
        response = service_stub.MakeSyncCallForRemoteApi(request)
      else:
        if request.has_request_id():
          request_id = request.request_id()
          environ['HTTP_HOST'] = self._balanced_address
          op = getattr(service_stub.request_data, 'register_request_id', None)
          if callable(op):
            op(environ, request_id)
        api_response = _execute_request(request).Encode()
        response.set_response(api_response)
    except Exception, e:
      if isinstance(e, apiproxy_errors.ApplicationError):
        level = logging.DEBUG
        application_error = response.mutable_application_error()
        application_error.set_code(e.application_error)
        application_error.set_detail(e.error_detail)
      else:
        # If the runtime instance is not Python, it won't be able to unpickle
        # the exception so use level that won't be ignored by default.
        level = logging.ERROR
        # Even if the runtime is Python, the exception may be unpicklable if
        # it requires importing a class blocked by the sandbox so just send
        # back the exception representation.
        # But due to our use of the remote API, at least some apiproxy errors
        # are generated in the Dev App Server main instance and not in the
        # language runtime and wrapping them causes different behavior from
        # prod so don't wrap them.
        if not isinstance(e, apiproxy_errors.Error):
          e = RuntimeError(repr(e))
      # While not strictly necessary for ApplicationError, do this to limit
      # differences with remote_api:handler.py.
      response.set_exception(pickle.dumps(e))






      logging.log(level, 'Exception while handling %s.%s()\n%s',
                  request.service_name(),
                  request.method(), traceback.format_exc())
Exemplo n.º 21
0
    def _RemoteSend(self, request, response, method, request_id=None):
        """Sends a request remotely to the datstore server. """
        tag = self.project_id
        self._maybeSetDefaultAuthDomain()
        user = users.GetCurrentUser()
        if user != None:
            tag += ":" + user.email()
            tag += ":" + user.nickname()
            tag += ":" + user.auth_domain()
        api_request = remote_api_pb.Request()
        api_request.set_method(method)
        api_request.set_service_name("datastore_v3")
        api_request.set_request(request.Encode())
        if request_id is not None:
            api_request.set_request_id(request_id)

        api_response = remote_api_pb.Response()

        retry_count = 0
        max_retries = 3
        location = self.__datastore_location
        while True:
            try:
                api_request.sendCommand(location, tag, api_response, 1,
                                        self.__is_encrypted, KEY_LOCATION,
                                        CERT_LOCATION)
                break
            except socket.error as socket_error:
                if socket_error.errno in (errno.ECONNREFUSED,
                                          errno.EHOSTUNREACH):
                    backoff_ms = 500 * 3**retry_count  # 0.5s, 1.5s, 4.5s
                    retry_count += 1
                    if retry_count > max_retries:
                        raise

                    logging.warning(
                        'Failed to call {} method of Datastore ({}). Retry #{} in {}ms.'
                        .format(method, socket_error, retry_count, backoff_ms))
                    time.sleep(float(backoff_ms) / 1000)
                    location = get_random_lb()
                    api_response = remote_api_pb.Response()
                    continue

                if socket_error.errno == errno.ETIMEDOUT:
                    raise apiproxy_errors.ApplicationError(
                        datastore_pb.Error.TIMEOUT,
                        'Connection timed out when making datastore request')
                raise
            # AppScale: Interpret ProtocolBuffer.ProtocolBufferReturnError as
            # datastore_errors.InternalError
            except ProtocolBuffer.ProtocolBufferReturnError as e:
                raise datastore_errors.InternalError(e)

        if not api_response or not api_response.has_response():
            raise datastore_errors.InternalError(
                'No response from db server on %s requests.' % method)

        if api_response.has_application_error():
            error_pb = api_response.application_error()
            logging.error(error_pb.detail())
            raise apiproxy_errors.ApplicationError(error_pb.code(),
                                                   error_pb.detail())

        if api_response.has_exception():
            raise api_response.exception()

        response.ParseFromString(api_response.response())
Exemplo n.º 22
0
    def add_tasks(self, project_id, service_id, version_id, add_requests):
        """ Makes a call to the TaskQueue service to enqueue tasks.

    Args:
      project_id: A string specifying the project ID.
      service_id: A string specifying the service ID.
      version_id: A string specifying the version ID.
      add_requests: A list of TaskQueueAddRequest messages.
    Raises:
      EnqueueError if unable to enqueue tasks.
    """
        request = taskqueue_service_pb.TaskQueueBulkAddRequest()
        for add_request in add_requests:
            request.add_add_request().MergeFrom(add_request)

        api_request = remote_api_pb.Request()
        api_request.set_method('BulkAdd')
        api_request.set_service_name('taskqueue')
        api_request.set_request(request.Encode())

        encoded_api_request = api_request.Encode()
        headers = {
            'ProtocolBufferType': 'Request',
            'AppData': project_id,
            'Module': service_id,
            'Version': version_id
        }
        api_response = None
        for location in self._locations:
            url = 'http://{}'.format(location)
            try:
                response = yield self._client.fetch(url,
                                                    method='POST',
                                                    body=encoded_api_request,
                                                    headers=headers)
            except socket_error:
                # Try a different location if the load balancer is not available.
                continue
            except HTTPError as error:
                raise EnqueueError(str(error))

            api_response = remote_api_pb.Response(response.body)
            break

        if api_response is None:
            raise EnqueueError('Unable to connect to any load balancers')

        if api_response.has_application_error():
            error_pb = api_response.application_error()
            raise EnqueueError(error_pb.detail())

        if api_response.has_exception():
            raise EnqueueError(api_response.exception())

        bulk_response = taskqueue_service_pb.TaskQueueBulkAddResponse(
            api_response.response())

        if bulk_response.taskresult_size() != len(add_requests):
            raise EnqueueError('Unexpected number of task results')

        for task_result in bulk_response.taskresult_list():
            if task_result.result(
            ) != taskqueue_service_pb.TaskQueueServiceError.OK:
                raise EnqueueError(
                    'Unable to enqueue task: {}'.format(task_result))