Exemplo n.º 1
0
    def preprocess( self, method, url, body, basicauth ) :
        """Pre-process a request"""
        headers = {}

        # Cached Etag
        #if method in ('GET', 'HEAD') :
        #    etag = self.cache.get( url, [None, {}] )[1].get( 'etag', None )
        #    headers.update({ 'If-None-Match' : etag }) if etag else None

        # JSON body
        if body and (not isinstance( body, basestring ) ) :
            json = JSON()
            try : body = json.encode( body )
            except TypeError : pass
            headers.setdefault('Content-Type', 'application/json')
        # Content-length, Transfer-Encoding
        headers.setdefault( 'Content-Length', '0' ) if body is None else None
        headers.setdefault( 'Content-Length', str(len(body)) 
        ) if isinstance( body, basestring
        ) else headers.update({ 'Transfer-Encoding' : 'chunked' })
        # Basc-Authorization
        headers.update({ 'Authorization': basicauth }) if basicauth else None

        return headers, body
Exemplo n.º 2
0
    def request( self, method, url, body=None, headers=None, credentials=None,
                 num_redirects=0, chunk_cb=None ) :
        """Handle a request
        :method ::
            HTTP method, GET, PUT, POST, DELETE, ALL
        :url ::
            CouchDB url
        :headers ::
            A dictionary of http headers, like `Content-type` and `Accept`
            ``Content-type``
            The use of the Content-type on a request is highly recommended.
            When uploading attachments it should be the corresponding MIME
            type for the attachment or binary ``application/octet-stream``
            ``Accept``
            Again highly recommended.
        """

        # Sanitize arguments
        method = method.upper()
        url = self.perm_redirects.get( url, url )
        headers = headers or {}
        basicauth = self.basicauth( credentials ) if credentials else None
        retries = iter(self.retry_delays)
        json    = JSON()

        if method not in self._allowed_methods :
            raise HTTPError( 'Method ``%s`` not allowed' % method )

        # Process request
        headers.setdefault( 'Accept', 'application/json' )
        headers['User-Agent'] = self.user_agent
        h, body = self.preprocess( method, url, body, basicauth )
        headers.update(h)

        conn = self.obtain_connection(url)
        resp = self.try_request_with_retries(
                        conn, retries, method, url, headers, body )
        status = resp.status

        req = Dummy()
        req.conn, req.url, req.method, req.headers, req.body = \
                conn, url, method, headers, body

        # Handle NOT_MODIFIED
        #rc = self.resp_not_modified(resp, req)
        #if rc != None : return rc

        #self.cache.pop( url, None )

        # Handle redirects
        rc = self.resp_redirect(resp, req, num_redirects)
        if rc != None : return rc

        data = None
        streamed = False

        # Read the full response for empty responses so that the connection is
        # in good state for the next request
        conds = [ method == 'HEAD',
                  resp.getheader('content-length') == '0', 
                  status < OK,
                  status in (NO_CONTENT, NOT_MODIFIED) ]
        if any(conds) :
            resp.read()
            self.release_connection(url, conn)

        # Buffer small non-JSON response bodies
        elif int(resp.getheader('content-length', sys.maxint)) < CHUNK_SIZE:
            data = resp.read()
            self.release_connection(url, conn)

        # For large or chunked response bodies, do not buffer the full body,
        # and instead return a minimal file-like object
        else :
            close_cb = lambda: self.release_connection(url, conn)
            data = ResponseBody( resp, close_cb, chunk_cb=chunk_cb )
            streamed = True

        # Handle errors
        if status >= BAD_REQUEST :  # 400
            ctype = resp.getheader('content-type')
            if data is not None and 'application/json' in ctype:
                data = json.decode( data )
                error = data.get('error'), data.get('reason')
            elif method != 'HEAD':
                error = resp.read()
                self.release_connection(url, conn)
            else:
                error = ''

            cls = hterr_class.get( status, None )
            if cls :
                raise cls(error)
            else :
                raise ServerError((status, error))

        # Store cachable responses
        #if not streamed and method == 'GET' and 'etag' in resp.msg:
        #    self.cache[url] = (status, resp.msg, data)
        #    self.clean_cache() if len(self.cache) > CACHE_SIZE[1] else None

        if not streamed and data is not None:
            data = StringIO(data)

        return status, resp.msg, data