Commits

coady committed b16ceb5

Discarding of replicated hosts and more validation headers.

  • Participants
  • Parent commits 5a317da

Comments (0)

Files changed (5)

File docs/client.rst

 .. autoclass:: Resources
   :show-inheritance:
   :members:
-  :exclude-members: queue
 
 Shards
 ---------

File lupyne/client.py

         self.request('GET', path)
         return self.getresponse(filename)()
     def multicall(self, *requests):
-        "Pipeline requests (method, path[, body]) and return completed responses."
+        "Pipeline requests (method, path[, body]) and generate completed responses."
         responses = []
         for request in requests:
             self.request(*request)
         for response in responses:
             response.begin()
             response.end()
-        return responses
+            yield response
     def get(self, path, **params):
         "Return response body from GET request."
         return self.call('GET', path, params=params)()
         pool = self[host]
         if not pool.failure:
             return -len(pool)
+    def discard(self, host):
+        "Remove host from availablity."
+        try:
+            self.hosts.remove(host)
+        except ValueError:
+            pass
     def call(self, method, path, body=None, params=(), retry=False):
         """Send request and return completed `response`_, even if hosts are unreachable.
         
         except socket.error:
             self[host].failure = time.time()
             if method != 'GET':
-                try:
-                    self.hosts.remove(host)
-                except ValueError:
-                    pass
+                self.discard(host)
             return self.call(method, path, body, retry=retry)
         return response if (response or not retry) else self.call(method, path, body, retry=retry-1)

File lupyne/server.py

     response.headers['x-response-time'] = time.time() - response.time
 
 @tool('on_start_resource')
-def validate(methods=('GET', 'HEAD'), etag=True, last_modified=True, max_age=None, expires=None):
-    """Return and validate caching headers for GET requests.
+def validate(etag=True, last_modified=True, max_age=None, expires=None):
+    """Return and validate caching headers.
     
-    :param methods: only set headers for specified methods
     :param etag: return weak entity tag header based on index version and validate if-match headers
     :param last_modified: return last-modified header based on index timestamp and validate if-modified headers
     :param max_age: return cache-control max-age and age headers based on last update timestamp
     :param expires: return expires header offset from last update timestamp
     """
-    request = cherrypy.serving.request
+    root = cherrypy.request.app.root
     headers = cherrypy.response.headers
-    if request.method in methods and not isinstance(request.handler, cherrypy.HTTPError):
-        if etag:
-            headers['etag'] = 'W/"{0}"'.format(request.app.root.searcher.version)
-            cherrypy.lib.cptools.validate_etags()
-        if last_modified:
-            headers['last-modified'] = formatdate(request.app.root.searcher.timestamp, usegmt=True)
-            cherrypy.lib.cptools.validate_since()
-        if max_age is not None:
-            headers['age'] = int(time.time() - request.app.root.updated)
-            headers['cache-control'] = 'max-age={0}'.format(max_age)
-        if expires is not None:
-            headers['expires'] = formatdate(expires + request.app.root.updated, usegmt=True)
+    if etag:
+        headers['etag'] = 'W/"{0}"'.format(root.searcher.version)
+        cherrypy.lib.cptools.validate_etags()
+    if last_modified:
+        headers['last-modified'] = formatdate(root.searcher.timestamp, usegmt=True)
+        cherrypy.lib.cptools.validate_since()
+    if max_age is not None:
+        headers['age'] = int(time.time() - root.updated)
+        headers['cache-control'] = 'max-age={0}'.format(max_age)
+    if expires is not None:
+        headers['expires'] = formatdate(expires + root.updated, usegmt=True)
 
 @tool('before_handler')
 def params(**types):
                 names = self.sync(*host.split('/'))
                 break
             except socket.error:
-                try:
-                    self.hosts.remove(host)
-                except ValueError:
-                    pass
+                client.Replicas.discard.__func__(self, host)
             except httplib.HTTPException as exc:
                 assert exc[0] == httplib.METHOD_NOT_ALLOWED, exc
                 break

File test/distributed.py

         for args in [('-r', self.tempdir), (update, self.tempdir), (update, self.tempdir, self.tempdir)]:
             assert subprocess.call((sys.executable, '-m', 'lupyne.server', sync) + args, stderr=subprocess.PIPE)
         replicas = client.Replicas(self.hosts[:2], limit=1)
+        replicas.discard(None)
         replicas.post('/docs', [{}])
         assert replicas.post('/update') == 1
         resource = client.Resource(self.hosts[2])

File test/remote.py

         del resource.headers['if-none-match']
         resource.headers['if-modified-since'] = modified
         assert resource.call('GET', '/').status == httplib.NOT_MODIFIED
+        del resource.headers['if-modified-since']
         time.sleep(max(0, calendar.timegm(parsedate(modified)) + 1 - time.time()))
         assert resource.post('/update')
         response = resource.call('GET', '/')
         with assertRaises(httplib.HTTPException, httplib.NOT_FOUND):
             resource.get('/docs/missing/sample')
         assert not resource.delete('/search')
-        responses = resource.multicall(('POST', '/docs', [{}]), ('POST', '/update'), ('GET', '/docs'))
+        responses = list(resource.multicall(('POST', '/docs', [{}]), ('POST', '/update'), ('GET', '/docs')))
         assert responses[0].status == httplib.ACCEPTED and responses[1]() == len(responses[2]()) == 1
         assert resource.post('/', [self.tempdir]).values() == [2]
         with local.assertWarns(DeprecationWarning, UserWarning):