thirdparty/google_appengine/google/appengine/tools/appengine_rpc.py
changeset 2273 e4cb9c53db3e
parent 828 f5fd65cc3bf3
child 3031 7678f72140e6
equal deleted inserted replaced
2272:26491ee91e33 2273:e4cb9c53db3e
    39         ca_certs=certpath)
    39         ca_certs=certpath)
    40     uses_cert_verification = True
    40     uses_cert_verification = True
    41 except ImportError:
    41 except ImportError:
    42   pass
    42   pass
    43 
    43 
       
    44 logger = logging.getLogger('google.appengine.tools.appengine_rpc')
    44 
    45 
    45 def GetPlatformToken(os_module=os, sys_module=sys, platform=sys.platform):
    46 def GetPlatformToken(os_module=os, sys_module=sys, platform=sys.platform):
    46   """Returns a 'User-agent' token for the host system platform.
    47   """Returns a 'User-agent' token for the host system platform.
    47 
    48 
    48   Args:
    49   Args:
    59     uname = os_module.uname()
    60     uname = os_module.uname()
    60     return "%s/%s" % (uname[0], uname[2])
    61     return "%s/%s" % (uname[0], uname[2])
    61   else:
    62   else:
    62     return "unknown"
    63     return "unknown"
    63 
    64 
       
    65 def HttpRequestToString(req, include_data=True):
       
    66   """Converts a urllib2.Request to a string.
       
    67 
       
    68   Args:
       
    69     req: urllib2.Request
       
    70   Returns:
       
    71     Multi-line string representing the request.
       
    72   """
       
    73 
       
    74   headers = ""
       
    75   for header in req.header_items():
       
    76     headers += "%s: %s\n" % (header[0], header[1])
       
    77 
       
    78   template = ("%(method)s %(selector)s %(type)s/1.1\n"
       
    79               "Host: %(host)s\n"
       
    80               "%(headers)s")
       
    81   if include_data:
       
    82     template = template + "\n%(data)s"
       
    83 
       
    84   return template % {
       
    85       'method' : req.get_method(),
       
    86       'selector' : req.get_selector(),
       
    87       'type' : req.get_type().upper(),
       
    88       'host' : req.get_host(),
       
    89       'headers': headers,
       
    90       'data': req.get_data(),
       
    91       }
    64 
    92 
    65 class ClientLoginError(urllib2.HTTPError):
    93 class ClientLoginError(urllib2.HTTPError):
    66   """Raised to indicate there was an error authenticating with ClientLogin."""
    94   """Raised to indicate there was an error authenticating with ClientLogin."""
    67 
    95 
    68   def __init__(self, url, code, msg, headers, args):
    96   def __init__(self, url, code, msg, headers, args):
    69     urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
    97     urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
    70     self.args = args
    98     self.args = args
    71     self.reason = args["Error"]
    99     self.reason = args["Error"]
    72 
   100 
       
   101   def read(self):
       
   102     return '%d %s: %s' % (self.code, self.msg, self.reason)
       
   103 
    73 
   104 
    74 class AbstractRpcServer(object):
   105 class AbstractRpcServer(object):
    75   """Provides a common interface for a simple RPC server."""
   106   """Provides a common interface for a simple RPC server."""
    76 
   107 
    77   def __init__(self, host, auth_function, user_agent, source,
   108   def __init__(self, host, auth_function, user_agent, source,
    78                host_override=None, extra_headers=None, save_cookies=False,
   109                host_override=None, extra_headers=None, save_cookies=False,
    79                auth_tries=3, account_type=None):
   110                auth_tries=3, account_type=None, debug_data=True, secure=False):
    80     """Creates a new HttpRpcServer.
   111     """Creates a new HttpRpcServer.
    81 
   112 
    82     Args:
   113     Args:
    83       host: The host to send requests to.
   114       host: The host to send requests to.
    84       auth_function: A function that takes no arguments and returns an
   115       auth_function: A function that takes no arguments and returns an
    93       save_cookies: If True, save the authentication cookies to local disk.
   124       save_cookies: If True, save the authentication cookies to local disk.
    94         If False, use an in-memory cookiejar instead.  Subclasses must
   125         If False, use an in-memory cookiejar instead.  Subclasses must
    95         implement this functionality.  Defaults to False.
   126         implement this functionality.  Defaults to False.
    96       auth_tries: The number of times to attempt auth_function before failing.
   127       auth_tries: The number of times to attempt auth_function before failing.
    97       account_type: One of GOOGLE, HOSTED_OR_GOOGLE, or None for automatic.
   128       account_type: One of GOOGLE, HOSTED_OR_GOOGLE, or None for automatic.
    98     """
   129       debug_data: Whether debugging output should include data contents.
       
   130     """
       
   131     if secure:
       
   132       self.scheme = "https"
       
   133     else:
       
   134       self.scheme = "http"
    99     self.host = host
   135     self.host = host
   100     self.host_override = host_override
   136     self.host_override = host_override
   101     self.auth_function = auth_function
   137     self.auth_function = auth_function
   102     self.source = source
   138     self.source = source
   103     self.authenticated = False
   139     self.authenticated = False
   104     self.auth_tries = auth_tries
   140     self.auth_tries = auth_tries
       
   141     self.debug_data = debug_data
   105 
   142 
   106     self.account_type = account_type
   143     self.account_type = account_type
   107 
   144 
   108     self.extra_headers = {}
   145     self.extra_headers = {}
   109     if user_agent:
   146     if user_agent:
   113 
   150 
   114     self.save_cookies = save_cookies
   151     self.save_cookies = save_cookies
   115     self.cookie_jar = cookielib.MozillaCookieJar()
   152     self.cookie_jar = cookielib.MozillaCookieJar()
   116     self.opener = self._GetOpener()
   153     self.opener = self._GetOpener()
   117     if self.host_override:
   154     if self.host_override:
   118       logging.info("Server: %s; Host: %s", self.host, self.host_override)
   155       logger.info("Server: %s; Host: %s", self.host, self.host_override)
   119     else:
   156     else:
   120       logging.info("Server: %s", self.host)
   157       logger.info("Server: %s", self.host)
   121 
   158 
   122     if ((self.host_override and self.host_override == "localhost") or
   159     if ((self.host_override and self.host_override == "localhost") or
   123         self.host == "localhost" or self.host.startswith("localhost:")):
   160         self.host == "localhost" or self.host.startswith("localhost:")):
   124       self._DevAppServerAuthenticate()
   161       self._DevAppServerAuthenticate()
   125 
   162 
   198       HTTPError: If there was an error fetching the authentication cookies.
   235       HTTPError: If there was an error fetching the authentication cookies.
   199     """
   236     """
   200     continue_location = "http://localhost/"
   237     continue_location = "http://localhost/"
   201     args = {"continue": continue_location, "auth": auth_token}
   238     args = {"continue": continue_location, "auth": auth_token}
   202     login_path = os.environ.get("APPCFG_LOGIN_PATH", "/_ah")
   239     login_path = os.environ.get("APPCFG_LOGIN_PATH", "/_ah")
   203     req = self._CreateRequest("http://%s%s/login?%s" %
   240     req = self._CreateRequest("%s://%s%s/login?%s" %
   204                               (self.host, login_path, urllib.urlencode(args)))
   241                               (self.scheme, self.host, login_path,
       
   242                                urllib.urlencode(args)))
   205     try:
   243     try:
   206       response = self.opener.open(req)
   244       response = self.opener.open(req)
   207     except urllib2.HTTPError, e:
   245     except urllib2.HTTPError, e:
   208       response = e
   246       response = e
   209     if (response.code != 302 or
   247     if (response.code != 302 or
   289     """
   327     """
   290     old_timeout = socket.getdefaulttimeout()
   328     old_timeout = socket.getdefaulttimeout()
   291     socket.setdefaulttimeout(timeout)
   329     socket.setdefaulttimeout(timeout)
   292     try:
   330     try:
   293       tries = 0
   331       tries = 0
       
   332       auth_tried = False
   294       while True:
   333       while True:
   295         tries += 1
   334         tries += 1
   296         args = dict(kwargs)
   335         args = dict(kwargs)
   297         url = "http://%s%s?%s" % (self.host, request_path,
   336         url = "%s://%s%s?%s" % (self.scheme, self.host, request_path,
   298                                   urllib.urlencode(args))
   337                                 urllib.urlencode(args))
   299         req = self._CreateRequest(url=url, data=payload)
   338         req = self._CreateRequest(url=url, data=payload)
   300         req.add_header("Content-Type", content_type)
   339         req.add_header("Content-Type", content_type)
   301         req.add_header("X-appcfg-api-version", "1")
   340         req.add_header("X-appcfg-api-version", "1")
   302         try:
   341         try:
       
   342           logger.debug('Sending HTTP request:\n%s' %
       
   343                        HttpRequestToString(req, include_data=self.debug_data))
   303           f = self.opener.open(req)
   344           f = self.opener.open(req)
   304           response = f.read()
   345           response = f.read()
   305           f.close()
   346           f.close()
   306           return response
   347           return response
   307         except urllib2.HTTPError, e:
   348         except urllib2.HTTPError, e:
   308           logging.debug("Got http error, this is try #%s" % tries)
   349           logger.debug("Got http error, this is try #%s" % tries)
   309           if tries > self.auth_tries:
   350           if tries > self.auth_tries:
   310             raise
   351             raise
   311           elif e.code == 401:
   352           elif e.code == 401:
       
   353             if auth_tried:
       
   354               raise
       
   355             auth_tried = True
   312             self._Authenticate()
   356             self._Authenticate()
   313           elif e.code >= 500 and e.code < 600:
   357           elif e.code >= 500 and e.code < 600:
   314             continue
   358             continue
   315           elif e.code == 302:
   359           elif e.code == 302:
       
   360             if auth_tried:
       
   361               raise
       
   362             auth_tried = True
   316             loc = e.info()["location"]
   363             loc = e.info()["location"]
   317             logging.debug("Got 302 redirect. Location: %s" % loc)
   364             logger.debug("Got 302 redirect. Location: %s" % loc)
   318             if loc.startswith("https://www.google.com/accounts/ServiceLogin"):
   365             if loc.startswith("https://www.google.com/accounts/ServiceLogin"):
   319               self._Authenticate()
   366               self._Authenticate()
   320             elif re.match(r"https://www.google.com/a/[a-z0-9.-]+/ServiceLogin",
   367             elif re.match(r"https://www.google.com/a/[a-z0-9.-]+/ServiceLogin",
   321                           loc):
   368                           loc):
   322               self.account_type = "HOSTED"
   369               self.account_type = "HOSTED"
   335   DEFAULT_COOKIE_FILE_PATH = "~/.appcfg_cookies"
   382   DEFAULT_COOKIE_FILE_PATH = "~/.appcfg_cookies"
   336 
   383 
   337   def _Authenticate(self):
   384   def _Authenticate(self):
   338     """Save the cookie jar after authentication."""
   385     """Save the cookie jar after authentication."""
   339     if cert_file_available and not uses_cert_verification:
   386     if cert_file_available and not uses_cert_verification:
   340       logging.warn("ssl module not found. Without this the identity of the "
   387       logger.warn("ssl module not found. Without this the identity of the "
   341                    "remote host cannot be verified, and connections are NOT "
   388                   "remote host cannot be verified, and connections are NOT "
   342                    "secure. To fix this, please install the ssl module from "
   389                   "secure. To fix this, please install the ssl module from "
   343                    "http://pypi.python.org/pypi/ssl")
   390                   "http://pypi.python.org/pypi/ssl")
   344     super(HttpRpcServer, self)._Authenticate()
   391     super(HttpRpcServer, self)._Authenticate()
   345     if self.cookie_jar.filename is not None and self.save_cookies:
   392     if self.cookie_jar.filename is not None and self.save_cookies:
   346       logging.info("Saving authentication cookies to %s" %
   393       logger.info("Saving authentication cookies to %s" %
   347                    self.cookie_jar.filename)
   394                   self.cookie_jar.filename)
   348       self.cookie_jar.save()
   395       self.cookie_jar.save()
   349 
   396 
   350   def _GetOpener(self):
   397   def _GetOpener(self):
   351     """Returns an OpenerDirector that supports cookies and ignores redirects.
   398     """Returns an OpenerDirector that supports cookies and ignores redirects.
   352 
   399 
   367 
   414 
   368       if os.path.exists(self.cookie_jar.filename):
   415       if os.path.exists(self.cookie_jar.filename):
   369         try:
   416         try:
   370           self.cookie_jar.load()
   417           self.cookie_jar.load()
   371           self.authenticated = True
   418           self.authenticated = True
   372           logging.info("Loaded authentication cookies from %s" %
   419           logger.info("Loaded authentication cookies from %s" %
   373                        self.cookie_jar.filename)
   420                       self.cookie_jar.filename)
   374         except (OSError, IOError, cookielib.LoadError), e:
   421         except (OSError, IOError, cookielib.LoadError), e:
   375           logging.debug("Could not load authentication cookies; %s: %s",
   422           logger.debug("Could not load authentication cookies; %s: %s",
   376                         e.__class__.__name__, e)
   423                        e.__class__.__name__, e)
   377           self.cookie_jar.filename = None
   424           self.cookie_jar.filename = None
   378       else:
   425       else:
   379         try:
   426         try:
   380           fd = os.open(self.cookie_jar.filename, os.O_CREAT, 0600)
   427           fd = os.open(self.cookie_jar.filename, os.O_CREAT, 0600)
   381           os.close(fd)
   428           os.close(fd)
   382         except (OSError, IOError), e:
   429         except (OSError, IOError), e:
   383           logging.debug("Could not create authentication cookies file; %s: %s",
   430           logger.debug("Could not create authentication cookies file; %s: %s",
   384                         e.__class__.__name__, e)
   431                        e.__class__.__name__, e)
   385           self.cookie_jar.filename = None
   432           self.cookie_jar.filename = None
   386 
   433 
   387     opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar))
   434     opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar))
   388     return opener
   435     return opener