Commits

Lynn Rees committed 9e36643

- restructure

Comments (0)

Files changed (40)

wire/http/agent.py

+# -*- coding: utf-8 -*-
+'''wire HTTP user agent'''
+
+import hashlib
+
+from stuf.six import items, values
+from callchain import ResetTypeMixin
+
+from wire.support import base64_encode
+
+
+class Request(ResetTypeMixin):
+
+    '''HTTP request user agent'''
+
+    def __init__(self, url, useragent, **kw):
+        '''
+        init
+
+        @param url: url for request
+        @param useragent: user agent callable
+        '''
+        super(Request, self).__init__()
+        # url for request
+        self.url = url
+        # requestor
+        self._useragent = useragent
+        # verify request
+        self._verify = kw.pop('verify', False)
+        # allow redirects
+        self._redirects = kw.pop('redirects', True)
+        # timeout
+        self._timeout = kw.pop('timeout', 10)
+        # authentication configuration
+        self._auth = kw.pop('auth', None)
+        # proxy configuration
+        self._proxy = kw.pop('proxy', None)
+        # cookies stub
+        self._cookies = None
+        # data stub
+        self._data = None
+        # files stub
+        self._files = None
+        # headers store
+        self._headers = None
+        # parameters stub
+        self._params = None
+        # method stub
+        self._method = None
+
+    def __call__(self):
+        self._headers.set('cookie', '; '.join(
+            c.render_request(prefix='') for c in values(self._cookies)
+        ))
+        useragent = self._useragent(self)
+        return useragent()
+
+    def cookies(self, cookies):
+        '''
+        set HTTP request cookies
+
+        @param cookies: cookies to set
+        '''
+        self._cookies = cookies
+        return self
+
+    def method(self, method):
+        '''
+        set HTTP request method
+
+        @param method: method to set
+        '''
+        self._method = method
+        return self
+
+    def data(self, data, mime, charset=None):
+        '''
+        set HTTP *CONTENT-TYPE* header.
+
+        @param data: data to set
+        @param mime: MIME media type
+        @param charset: charset (default: None)
+        '''
+        self._data = data
+        self._headers.content_type(mime, charset)
+        return self
+
+    def files(self, files):
+        '''
+        set HTTP request files
+
+        @param files: files to set
+        '''
+        self._files = files
+        return self
+
+    def headers(self, headers):
+        '''
+        set HTTP request headers
+
+        @param headers: headers to set
+        '''
+        self._headers.update(items(headers))
+        return self
+
+    def params(self, params):
+        '''
+        set HTTP request parameters
+
+        @param params: parameters to set
+        '''
+        self._params = params
+        return self
+
+    def content_md5(self):
+        '''take MD5 hash of data and set *CONTENT-MD5* HTTP header'''
+        self._headers.set('Content-MD5', base64_encode(
+            hashlib.md5.new(self._data).digest()
+        ))
+        return self

wire/http/agents/__init__.py

Empty file added.

wire/http/agents/agent.py

+'''wire HTTP response user agent'''
+
+import hashlib
+
+from stuf.six.moves import StringIO  # @UnresolvedImport
+from callchain import ResetTypeMixin
+
+from wire.http.util import statuscode
+from wire.support import base64_decode
+
+
+class Agent(ResetTypeMixin):
+
+    '''Core HTTP response manager'''
+
+    def __init__(self, request):
+        super(Agent, self).__init__()
+        self._request = request
+
+    def code(self):
+        '''
+        HTTP response code
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+        '''
+        return int(self._code)
+
+    def good(self):
+        '''true if HTTP was successful enough'''
+        return self._code.startswith('20')
+
+    def fail(self):
+        '''true if HTTP request failed'''
+        return self._code.startswith('40') or self._code.startswith('50')
+
+    def noop(self):
+        '''true if HTTP request was more or less harmless'''
+        return self._code.startswith('30') or self._code.startswith('10')
+
+    def ok(self):
+        '''true if HTTP request was successful'''
+        return self._code == '200'
+
+    def status(self):
+        '''status of HTTP response'''
+        return statuscode(self.code)
+
+    def data(self):
+        '''data, serialized if supported, original if not'''
+        return self._original
+
+    def touched(self):
+        '''Verify MD5 digest of response data against "Content-MD5" digest'''
+        digest = self.headers.content_md5
+        if digest is not None and digest:
+            digest = base64_decode(digest)
+            body_digest = hashlib.md5.new(self._original).digest()
+            if digest == body_digest:
+                return False
+        return True
+
+    def read(self):
+        '''read original response content'''
+        return StringIO(self._original).read()

wire/http/agents/apps.py

+
+
+# HTTP clients
+HTTPLIST = dict(
+    urllib2='ul2_client.Request',
+    httplib2='hl2_client.Request',
+    pycurl='pycurl_client.Request',
+)
+#HTTP = Loader(HTTPLIST, package='wire.http.client')
+
+# REST clients
+RESTLIST = dict(
+    urllib2='ul2_client.REST',
+    httplib2='hl2_client.REST',
+    pycurl='pycurl_client.REST',
+)
+#REST = Loader(RESTLIST, package='wire.http.client')

wire/http/agents/requestua.py

+'''requests wrapper'''
+
+from requests import request
+
+from wire.http.agents.agent import Agent
+
+
+class Response(Agent):
+
+    def __call__(self):
+        req = self._request
+        response = request(
+            req._method,
+            req.url,
+            params=req._params,
+            data=req._data,
+            headers=req._headers,
+            auth=req._auth,
+            timeout=req._timeout,
+            allow_redirects=req._redirects,
+            proxies=req._proxy,
+            verify=req._verify,
+        )
+        self._code = str(response.status_code)
+        self._cookies = response.cookies
+        self.url = response.url
+        self._original = response.iter_lines
+        self._headers = response.headers
+        return self

wire/http/client.py

+# -*- coding: utf-8 -*-
+'''wire HTTP request client'''
+
+from functools import partial
+
+from appspace.keys import appifies
+from callchain.chain import inside, LinkedQ
+from twoq.lazy.mixins import AutoResultMixin
+from callchain.lazy_auto.event import eventq
+
+from wire.http.agent import Request
+from wire.http.config import Defaults, Required
+from wire.http.keys.client import KRequestClient
+from wire.http.keys.apps import events, request, response
+
+
+@appifies(KRequestClient)
+class http(eventq):
+
+    '''HTTP client chain'''
+
+    def __init__(self, **kw):
+        '''
+        init
+
+        @param pattern: pattern configuration or appspace label (default: None)
+        @param required: required settings (default: None)
+        @param defaults: default settings (default: None)
+        '''
+        _setdefault = self._m_setdefault
+        _defaults = self.defaults
+        # set useragent
+        kw['useragent'] = kw.pop('useragent', _defaults.useragent)
+        super(http, self).__init__(request, events, Required, Defaults, **kw)
+        # allow redirects for request
+        _setdefault(
+            '_redirects', kw.get('redirects', _defaults.follow_redirects),
+        )
+        # cert verification
+        _setdefault('_verify', kw.get('verify', _defaults.verify_certificates))
+        # default request timeout
+        _setdefault('_timeout', kw.get('timeout', _defaults.request_timeout))
+        # use random user agent identifier
+        _setdefault(
+            '_randomua', kw.pop('randomua', _defaults.random_user_agent),
+        )
+        # allowed HTTP methods
+        _setdefault('_METHODS', kw.pop('methods', _defaults.allowed_methods))
+        # authentication config
+        _setdefault('_auth', kw.pop('auth', {}))
+        # proxy config
+        _setdefault('_proxy', kw.pop('proxy', {}))
+        # headers store
+        self._headers = None
+        # cookies stub
+        self._cookies = None
+        # parameters stub
+        self._params = None
+
+    def __getattr__(self, key):
+        return partial(
+            self._pack, key
+        ) if key.lower() in self._METHODS else self._c_getattr(key)
+
+    _h_gettar = __getattr__
+
+    ###########################################################################
+    ## http configuration #####################################################
+    ###########################################################################
+
+    def auth(self, username, password):
+        '''
+        set HTTP request authentication settings
+
+        @param username: usernameish credential
+        @param password: passwordish credential
+        '''
+        self._auth = (username, password)
+        return self
+
+    _hauth = auth
+
+    def proxy(self, server, host):
+        '''
+        set HTTP request proxy settings
+
+        @param server: proxy server
+        @param host: proxy host
+        '''
+        self._proxy = (server, host)
+        return self
+
+    _hproxy = proxy
+
+    ###########################################################################
+    ## internal management ####################################################
+    ###########################################################################
+
+    def clear(self):
+        '''clear all queues'''
+        self._m_defaults()
+        self._qclear()
+        return self
+
+    _hclear = clear
+
+    def back(self, link):
+        '''
+        handle branch chain end
+
+        @param link: linked chain
+        '''
+        self._cookies = link._cookies
+        self._headers = link._headers
+        self._params = link._params
+        return self._qback(link)
+
+    _hback = back
+
+    ###########################################################################
+    ## http methods ###########################################################
+    ###########################################################################
+
+    @property
+    def _useragent(self):
+        '''http user agent'''
+        return self.M.get(self.G.useragent, self.G.userspace)
+
+    _h_useragent = _useragent
+
+    def _pack(self, method):
+        '''
+        run HTTP `method`
+
+        @param method: HTTP method
+        '''
+        self.swap('_chain', '_chain')
+        self.args(method).invoke('method')
+        self.args(self._cookies).invoke('cookies')
+        self.args(self._headers).invoke('headers')
+        self.args(self._params).invoke('params')
+        self.args(self.incoming).invoke('data').invoke('content_md5')
+        return self.swap().commit().switch(self.G.responses, self.G.userspace)
+
+    _hpack = _pack
+
+    def __call__(self, *urls, **config):
+        '''
+        load Uniform Resource Locators (URLs)
+
+        @param *urls: sequence of URL arguments
+        '''
+        # authentication configuration
+        auth = config.pop('auth', self._auth)
+        if auth:
+            self.auth(*auth)
+        # proxy configuration
+        proxy = config.pop('proxy', self._proxy)
+        if proxy:
+            self.proxy(*proxy)
+        # default timeout
+        timeout = config.pop('timeout', self._timeout)
+        # allow redirects
+        redirects = self._redirects
+        # verify certs
+        verify = self._verify
+        # call chain
+        _chain = self._cchain
+        # user agent (whatever)
+        useragent = self._h_useragent
+        # process url arguments
+        self._chain.extend(_chain(Request(
+            url,
+            useragent,
+            verify=verify,
+            redirects=redirects,
+            timeout=timeout,
+            auth=auth,
+            proxy=proxy,
+        )) for url in urls)
+        return self._dcall()
+
+    _h_call = __call__
+
+
+@inside(response)
+class Response(LinkedQ, AutoResultMixin):
+
+    '''HTTP response collection'''
+
+    def __init__(self, root):
+        super(response, self).__init__(root)

wire/http/config.py

+# -*- coding: utf-8 -*-
+'''wire HTTP configuration'''
+
+from callchain.settings import DefaultSettings, RequiredSettings
+
+
+class Required(RequiredSettings):
+    userspace = 'request'
+
+
+class Defaults(DefaultSettings):
+    useragent = 'requestsua'
+    follow_redirects = True
+    verify_certificates = False
+    request_timeout = 10
+    random_user_agent = False
+    allowed_methods = frozenset([
+        'OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE',
+    ])

wire/http/drivers/__init__.py

Empty file removed.

wire/http/drivers/apps.py

-
-
-# HTTP clients
-HTTPLIST = dict(
-    urllib2='ul2_client.Request',
-    httplib2='hl2_client.Request',
-    pycurl='pycurl_client.Request',
-)
-#HTTP = Loader(HTTPLIST, package='wire.http.client')
-
-# REST clients
-RESTLIST = dict(
-    urllib2='ul2_client.REST',
-    httplib2='hl2_client.REST',
-    pycurl='pycurl_client.REST',
-)
-#REST = Loader(RESTLIST, package='wire.http.client')

wire/http/drivers/hl2.py

-#'''httplib2 wire HTTP client wrapper'''
-#
-##FIXME: don't import eventlet in test mode
-#try:
-#    import eventlet
-#    httplib2 = eventlet.import_patched('httplib2')
-#except ImportError:
-#    import httplib2
-#
-#from wire.util import lazy, lazier
-#from wire.http.response import CoreResponse
-#from wire.http.request import CoreRequest, CoreREST
-#
-#
-#class Response(CoreResponse):
-#
-#    '''httplib2 response object'''
-#
-#    def __init__(self, resp, **kw):
-#        response, content = resp
-#        self._hset('_original', content)
-#        h = self._hset('_headers', dict(response))
-#        self._hset('_code', h.pop('status', '500'))
-#        self._hset('url', h.pop('content-location', None))
-#        super(Response, self).__init__(**kw)
-#
-#
-#class Request(CoreRequest):
-#
-#    def _factory(self):
-#        auth = self._auth
-#        proxy = self._proxy
-#        if proxy:
-#            try:
-#                import socks
-#                h = httplib2.Http(
-#                    '.cache',
-#                    proxy_info=httplib2.ProxyInfo(
-#                        socks.PROXY_TYPE_HTTP, proxy['server'], proxy['host'],
-#                    ),
-#                )
-#            except ImportError:
-#                raise ImportWarning(
-#                    u'httplib2 proxy support requires "socks" library'
-#                )
-#        else:
-#            h = httplib2.Http('.cache')
-#        if auth: h.add_credentials(*(auth['username'], auth['password']))
-#        return h
-#
-#    def _request(self, **kw):
-#        try:
-#            factory = self._requestor
-#            kw, newkw, respkw = self._kwfy(kw)
-#            # temporary auth
-#            auth = newkw.pop('auth', self._auth)
-#            if auth: factory.add_credentials(
-#                *(auth['username'], auth['password'])
-#            )
-#            # temporary timeout
-#            factory.timeout = newkw.pop('timeout', self._timeout)
-#            result = factory.request(
-#                newkw.pop('url'),
-#                method=kw.get('method', 'GET'),
-#                body=newkw.pop('data', ''),
-#                headers=dict(newkw.pop('headers')),
-#            )
-#            return self._response(result, **respkw)
-#        except httplib2.RelativeURIError, e:
-#            raise ValueError(e)
-#        finally:
-#            # clear temporary auth
-#            if auth: factory.clear_credentials()
-#            # clear temporary timeout
-#            factory.timeout = None
-#
-#    @lazy
-#    def _requestor(self):
-#        return self._factory()
-#
-#    @lazier
-#    def _response(self):
-#        return Response
-#
-#
-#class RESTClient(Request, CoreREST):
-#
-#    '''httplib2 REST client'''

wire/http/drivers/pc.py

-#'''p wire HTTP client wrapper'''
-#
-#from StringIO import StringIO
-#from httplib import HTTPMessage
-## what pycurl needs...
-#try:
-#    import signal
-#    from signal import SIGPIPE, SIG_IGN
-#    signal.signal(SIGPIPE, SIG_IGN)
-#except ImportError:
-#    pass
-#
-#try:
-#    import pycurl as p
-#except ImportError:
-#    raise ImportError('pycurl_client requires the "p" library')
-#
-#from wire.util import lazy, lazier
-#from wire.http.response import CoreResponse
-#from wire.http.request import CoreRequest, CoreREST
-#
-#
-#class Request(CoreRequest):
-#
-#    @lazy
-#    def _factory(self):
-#        factory = p.Curl()
-#        proxy = self._proxy
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY
-#        if proxy:
-#            factory.setopt(p.PROXY, ':'.join([proxy['server'], proxy['host']]))
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTFOLLOWLOCATION
-#        factory.setopt(p.FOLLOWLOCATION, 1)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTMAXREDIRS
-#        factory.setopt(p.MAXREDIRS, 5)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCONNECTTIMEOUT
-#        factory.setopt(p.CONNECTTIMEOUT, 15)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOSIGNAL
-#        factory.setopt(p.NOSIGNAL, 1)
-#        # deprecated?
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTRANSFERTEXT
-#        factory.setopt(p.TRANSFERTEXT, 1)
-#        auth = self._auth
-#        if auth:
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHTTPAUTH
-#            factory.setopt(p.HTTPAUTH, p.HTTPAUTH_BASIC)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUSERPWD
-#            factory.setopt(
-#                p.USERPWD, ':'.join(auth['username'], auth['password'])
-#            )
-#        return factory
-#
-##    @lazy
-##    def multiple(self):
-##        def func(urls, **kw):
-##            multi = p.CurlMulti()
-##            makehandler = self._makehandler
-##            num_urls = len(urls)
-##            handlers = list(makehandler() for i in xrange(urls))
-##            return func
-#
-#    @lazier
-#    def _response(self):
-#        return Response
-#
-##    def _makehandler(self, **kw):
-##        factory = p.Curl()
-##        getinfo = factory.getinfo
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTFOLLOWLOCATION
-##        factory.setopt(p.FOLLOWLOCATION, 1)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTMAXREDIRS
-##        factory.setopt(p.MAXREDIRS, 5)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCONNECTTIMEOUT
-##        factory.setopt(p.CONNECTTIMEOUT, 15)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOSIGNAL
-##        factory.setopt(p.NOSIGNAL, 1)
-##        # deprecated?
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTRANSFERTEXT
-##        factory.setopt(p.TRANSFERTEXT, 1)
-##        # handle proxy
-##        proxy = kw.get('proxy', self._proxy)
-##        if proxy:
-##            factory.setopt(
-##                p.PROXY, ':'.join([proxy['server'], proxy['host']]),
-##            )
-##            # handle proxy auth
-##            factory.setopt(p.PROXYAUTH, p.HTTPAUTH_BASIC)
-##        # handle auth
-##        auth = kw.get('auth', self._auth)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUSERPWD
-##        if auth:
-##            factory.setopt(p.HTTPAUTH, p.HTTPAUTH_BASIC)
-##            factory.setopt(
-##                p.USERPWD, ':'.join(auth['username'], auth['password'])
-##            )
-##        # handle timeout
-##        timeout = kw.get('timeout', self._timeout)
-##        if timeout: factory.setopt(p.TIMEOUT, timeout)
-##        data = kw.get('data', '')
-##        # handle method
-##        method = kw.get('method', 'get').lower()
-##        headers = list(
-##            ': '.join(i) for i in kw.get('headers', list(self.h))
-##        )
-##        if method == 'post':
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPOST
-##            factory.setopt(p.POST, 1)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPOSTFIELDS
-##            factory.setopt(p.POSTFIELDS, data)
-##        elif method == 'put':
-##            # deprecated in favor of UPLOAD?
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPUT
-##            factory.setopt(p.PUT, 1)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUPLOAD
-##            factory.setopt(p.UPLOAD, 1)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTINFILESIZE
-##            factory.setopt(p.INFILESIZE, len(data))
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTREADDATA
-##            factory.setopt(p.READDATA, data)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOBODY
-##        elif method == 'head':
-##            factory.setopt(p.NOBODY, 1)
-### http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCUSTOMREQUEST
-##        elif method == 'delete':
-##            factory.setopt(p.CUSTOMREQUEST, 'DELETE')
-##        elif method == 'options':
-##            factory.setopt(p.CUSTOMREQUEST, 'OPTIONS')
-##        return factory
-#
-#    def _request(self, **kw):
-#        try:
-#            factory = self._factory
-#            getinfo = factory.getinfo
-#            # handle proxy
-#            proxy = kw.pop('proxy', self._proxy)
-#            if proxy:
-#                factory.setopt(
-#                    p.PROXY, ':'.join([proxy['server'], proxy['host']]),
-#                )
-#                # handle proxy auth
-#                factory.setopt(p.PROXYAUTH, p.HTTPAUTH_BASIC)
-#            # handle auth
-#            auth = kw.pop('auth', self._auth)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUSERPWD
-#            if auth:
-#                factory.setopt(
-#                    p.USERPWD, ':'.join([auth['username'], auth['password']])
-#                )
-#            # handle timeout
-#            timeout = kw.pop('timeout', self._timeout)
-#            if timeout: factory.setopt(p.TIMEOUT, timeout)
-#            data = kw.pop('data', '')
-#            # handle method
-#            method = kw.pop('method', 'get').lower()
-#            headers = list(
-#                ': '.join(i) for i in kw.pop('headers', list(self.headers))
-#            )
-#            if method == 'post':
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPOST
-#                factory.setopt(p.POST, 1)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPOSTFIELDS
-#                factory.setopt(p.POSTFIELDS, data)
-#            elif method == 'put':
-#                # deprecated in favor of UPLOAD?
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPUT
-#                factory.setopt(p.PUT, 1)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUPLOAD
-#                factory.setopt(p.UPLOAD, 1)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTINFILESIZE
-#                factory.setopt(p.INFILESIZE, len(data))
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTREADDATA
-#                factory.setopt(p.READDATA, data)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOBODY
-#            elif method == 'head':
-#                factory.setopt(p.NOBODY, 1)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCUSTOMREQUEST
-#            elif method == 'delete':
-#                factory.setopt(p.CUSTOMREQUEST, 'DELETE')
-#            elif method == 'options':
-#                factory.setopt(p.CUSTOMREQUEST, 'OPTIONS')
-#            # handle headers
-#            header = StringIO()
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTUSERAGENT
-#            factory.setopt(p.USERAGENT, self.headers.user_agent)
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHTTPHEADER
-#            factory.setopt(p.HTTPHEADER, list(headers))
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHEADERFUNCTION
-#            factory.setopt(p.HEADERFUNCTION, header.write)
-#            # handle data
-#            body = StringIO()
-## http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEFUNCTION
-#            factory.setopt(p.WRITEFUNCTION, body.write)
-#            # handle url
-#            factory.setopt(p.URL, kw.pop('url'))
-#            factory.perform()
-## INFO: http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
-#            result = dict(
-#                headers=header,
-#                data=data,
-#                status=getinfo(p.HTTP_CODE),
-#                content_location=getinfo(p.EFFECTIVE_URL),
-#            )
-#        except p.error, e:
-## INFO: http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
-#            result = dict(
-#                headers=header,
-#                data=data,
-#                status=getinfo(p.HTTP_CODE),
-#                url=getinfo(p.EFFECTIVE_URL),
-#                redirect_url=getinfo(p.REDIRECT_URL),
-#                errors='{0} {1}'.format(e[0], e[1]),
-#            )
-#        finally:
-#            factory.close()
-#        return result
-#
-#
-#class RESTClient(Request, CoreREST):
-#
-#    '''pycurl REST client'''
-#
-#
-#class Response(CoreResponse):
-#
-#    '''pycurl response object'''
-#
-#    def __init__(self, resp, **kw):
-#        # data
-#        data = resp.pop('data', StringIO())
-#        data.seek(0)
-#        # preserve original data
-#        self._original = data.read()
-#        # headers...
-#        headers = resp.pop('headers', StringIO())
-#        headers.seek(0)
-#        # clear first status line
-#        headers.readline()
-#        # yum...headers
-#        self._headers = h = dict(i for i in HTTPMessage(headers).items())
-#        # it's an int
-#        self._code = str(h.pop('status', 500))
-#        # final url
-#        self.url = h.pop('url', None)
-#        super(Response, self).__init__(**kw)
-#
-#
-##m = pycurl.CurlMulti()
-##m.handles = []
-##for i in range(num_conn):
-##    c = pycurl.Curl()
-##    c.fp = None
-##    c.setopt(pycurl.FOLLOWLOCATION, 1)
-##    c.setopt(pycurl.MAXREDIRS, 5)
-##    c.setopt(pycurl.CONNECTTIMEOUT, 30)
-##    c.setopt(pycurl.TIMEOUT, 300)
-##    c.setopt(pycurl.NOSIGNAL, 1)
-##    m.handles.append(c)
-##
-##
-### Main loop
-##freelist = m.handles[:]
-##num_processed = 0
-##while num_processed < num_urls:
-##    # If there is an url to process and a free curl object, add to multi stack
-##    while queue and freelist:
-##        url, filename = queue.pop(0)
-##        c = freelist.pop()
-##        c.fp = open(filename, "wb")
-##        c.setopt(pycurl.URL, url)
-##        c.setopt(pycurl.WRITEDATA, c.fp)
-##        m.add_handle(c)
-##        # store some info
-##        c.filename = filename
-##        c.url = url
-##    # Run the internal curl state machine for the multi stack
-##    while 1:
-##        ret, num_handles = m.perform()
-##        if ret != pycurl.E_CALL_MULTI_PERFORM:
-##            break
-##    # Check for curl objects which have terminated, and add them to the freelist
-##    while 1:
-##        num_q, ok_list, err_list = m.info_read()
-##        for c in ok_list:
-##            c.fp.close()
-##            c.fp = None
-##            m.remove_handle(c)
-##            print "Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)
-##            freelist.append(c)
-##        for c, errno, errmsg in err_list:
-##            c.fp.close()
-##            c.fp = None
-##            m.remove_handle(c)
-##            print "Failed: ", c.filename, c.url, errno, errmsg
-##            freelist.append(c)
-##        num_processed = num_processed + len(ok_list) + len(err_list)
-##        if num_q == 0:
-##            break
-##    # Currently no more I/O is pending, could do something in the meantime
-##    # (display a progress bar, etc.).
-##    # We just call select() to sleep until some more data is available.
-##    m.select(1.0)
-##
-##
-### Cleanup
-##for c in m.handles:
-##    if c.fp is not None:
-##        c.fp.close()
-##        c.fp = None
-##    c.close()
-##m.close()

wire/http/drivers/ul2.py

-#'''urllib2 client wrapper'''
-#
-#import urllib2
-#from collections import deque
-#from stuf.utils import lazy_class
-#from wire.http.client.response import CoreResponse
-#
-##FIXME: don't import eventlet in test mode
-#try:
-#    import eventlet
-#    ulib = eventlet.import_patched('urllib2')
-#except ImportError:
-#    ulib = urllib2
-#
-#from stuf import stuf
-#
-#
-#class Response(CoreResponse):
-#
-#    def __init__(self, response, **kw):
-#        self._original = response.read()
-#        self._code = response.code
-#        self._headers = response.headers
-#        self.url = response.url
-#        super(Response, self).__init__(**kw)
-#
-#
-#class Request(CoreRequest):
-#
-#    def _factory(self, **kw):
-#        if kw.pop('test', False):
-#            handlers = deque([
-#                ulib.HTTPHandler,
-#                ulib.HTTPDefaultErrorHandler,
-#                ulib.HTTPRedirectHandler,
-#                ulib.FileHandler,
-#                ulib.HTTPErrorProcessor,
-#                ulib.HTTPSHandler,
-#            ])
-#            proxy = self._proxy
-#            if proxy:
-#                proxy_set = '{0} {1}'.format(proxy['server'], proxy['host'])
-#                handlers.appendleft(
-#                    ulib.ProxyHandler(dict(http=proxy_set, https=proxy_set))
-#                )
-#            auth = kw.pop('auth', self._auth)
-#            if auth:
-#                password_mgr = ulib.HTTPPasswordMgrWithDefaultRealm()
-#                password_mgr.add_password(
-#                    None, kw.get('url'), auth['username'], auth['password'],
-#                )
-#                handlers.append(ulib.HTTPBasicAuthHandler(password_mgr))
-#            ulib.install_opener(ulib.build_opener(*tuple(h for h in handlers)))
-#
-#    @lazy_class
-#    def _response(self):
-#        return Response
-#
-#    def _request(self, **kw):
-#        kw, newkw, respkw = self._kwfy(kw)
-#        factory = self._factory(**newkw)
-#        url = newkw.pop('url')
-#        data = newkw.pop('data')
-#        headers = newkw.pop('headers', dict(self.headers))
-#        request = urllib2.Request(url=url, data=data, headers=headers)
-#        request.get_method = lambda: kw.get('method', 'GET')
-#        try:
-#            return self._response(factory.open(request), **respkw)
-#        except ulib.HTTPError, e:
-#            return stuf(
-#                read=e.fp.read,
-#                code=e.code,
-#                headers=e.hdrs,
-#                url=e.filename,
-#            )
-#
-#
-#class RESTClient(Request, CoreREST):
-#
-#    '''urllib2 REST client'''

wire/http/drivers/ul3.py

-#'''urllib2 client wrapper'''
-#
-#
-##from collections import deque
-#from stuf.utils import lazy_class
-#from wire.http.response import CoreResponse
-#
-#from stuf import stuf
-#import urllib3
-#
-#
-#class Response(CoreResponse):
-#
-#    def __init__(self, response, **kw):
-#        self._original = response.read()
-#        self._code = response.code
-#        self._headers = response.headers
-#        self.url = response.url
-#        super(Response, self).__init__(**kw)
-#
-#
-#class Request(Request):
-#
-##    def _factory(self, **kw):
-##        if kw.pop('test', False):
-##            handlers = deque([
-##                urllib3.HTTPHandler,
-##                urllib3.HTTPDefaultErrorHandler,
-##                urllib3.HTTPRedirectHandler,
-##                urllib3.FileHandler,
-##                urllib3.HTTPErrorProcessor,
-##                urllib3.HTTPSHandler,
-##            ])
-##            proxy = self._proxy
-##            if proxy:
-##                proxy_set = '{0} {1}'.format(proxy['server'], proxy['host'])
-##                handlers.appendleft(
-##                    urllib3.ProxyHandler(dict(http=proxy_set, https=proxy_set))
-##                )
-##            auth = kw.pop('auth', self._auth)
-##            if auth:
-##                password_mgr = urllib3.HTTPPasswordMgrWithDefaultRealm()
-##                password_mgr.add_password(
-##                    None, kw.get('url'), auth['username'], auth['password'],
-##                )
-##                handlers.append(urllib3.HTTPBasicAuthHandler(password_mgr))
-##            urllib3.install_opener(urllib3.build_opener(*tuple(h for h in handlers)))
-#
-#    @lazy_class
-#    def _response(self):
-#        return Response
-#
-#    def _request(self, **kw):
-#        kw, newkw, respkw = self._kwfy(kw)
-#        factory = self._factory(**newkw)
-#        url = newkw.pop('url')
-#        data = newkw.pop('data')
-#        headers = newkw.pop('headers', dict(self.headers))
-#        request = urllib3(url=url, data=data, headers=headers)
-#        request.get_method = lambda: kw.get('method', 'GET')
-#        try:
-#            return self._response(factory.open(request), **respkw)
-#        except urllib3.HTTPError, e:
-#            return stuf(
-#                read=e.fp.read,
-#                code=e.code,
-#                headers=e.hdrs,
-#                url=e.filename,
-#            )
-

wire/http/header.py

 '''wire HTTP header management'''
 
 import re
-
-from stuf.six import items
-from stuf.utils import lazy_class
+from random import choice
+from itertools import chain
+from datetime import datetime
+from email.header import Header
 
 from callchain import ResetLocalMixin
+from twoq.support import isstring, isunicode, items
+from stuf.utils import OrderedDict, lazy_class, exhaustmap
 
 from wire.support import n2b
 
+from wire.http.util import datehttp, httpdate
+
 n = n2b
 
 
     def _DATES(self):
         '''covert to datetime fields'''
         return self._freeze([
-            'date', 'expires', 'if-modified-since',  'if-unmodified-since',
+            'date', 'expires', 'if-modified-since', 'if-unmodified-since',
             'if-range', 'last-modified', 'retry-after',
         ])
 
     def _HTTPIF(self):
         '''HTTP if tests'''
         return self._freeze([
-            'if-match', 'if-none-match',  'if-modified-since', 'if-rangeset',
+            'if-match', 'if-none-match', 'if-modified-since', 'if-rangeset',
             'if-unmodified-since',
         ])
 
         return n('\r\n'.join(': '.join([k, v]) for k, v in self))
 
     _hdumps = dumps
+
+
+class RequestHeaders(CoreHeaders):
+
+    '''HTTP request header manager'''
+
+    def __init__(self):
+        super(RequestHeaders, self).__init__()
+        # "best practice" sends HTTP headers in order: 1. general headers
+        self._general = OrderedDict()
+        # 2. request headers
+        self._request = OrderedDict()
+        # 3. entity headers
+        self._entity = OrderedDict()
+        # 4. custom headers
+        self._custom = OrderedDict()
+
+    def __iter__(self):
+        # cast any header objects as `bytes` (calling `Header.decode`)
+        return ((k, n(v)) for k, v in chain(
+            # "best practice" sends HTTP headers in order: 1. general headers
+            items(self._general),
+            # 2. request headers
+            items(self._request),
+            # 3. entity headers
+            items(self._entity),
+            # 4. custom headers
+            items(self._custom),
+        ))
+
+    ###########################################################################
+    ## header definitions #####################################################
+    ###########################################################################
+
+    @lazy_class
+    def _langs(self):
+        '''language codes'''
+        from locale import locale_alias
+        return frozenset(n(i.replace('_', '-')) for i in locale_alias)
+
+    @staticmethod
+    def _randomua():
+        '''random user agent value'''
+        return choice([
+            n('Mozilla/5.0 (Windows; U; Windows NT 6.1; en; rv:1.9.1.3) '
+            'Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)'),
+            n('Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.3) '
+            'Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)'),
+            n('Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.1) '
+            'Gecko/20090718 Firefox/3.5.1'),
+            n('Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.1) '
+            'Gecko/20090718 Firefox/4.0.1'),
+            n('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/'
+            '532.1 (KHTML, like Gecko) Chrome/4.0.219.6 Safari/532.1'),
+            n('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; '
+            'Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2)'),
+            n('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0;'
+            ' SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.5.'
+            '30729; .NET CLR 3.0.30729)'),
+            n('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Win64; x64; '
+            'Trident/4.0)'),
+            n('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0;'
+            ' SV1; .NET CLR 2.0.50727; InfoPath.2) Mozilla/5.0 (Windows; U; '
+            'MSIE 7.0; Windows NT 6.0; en-US)'),
+            n('Mozilla/4.0 (compatible; MSIE 6.1; Windows XP)'),
+        ])
+
+    _h_random = _randomua
+
+    ###########################################################################
+    ## header validation ######################################################
+    ###########################################################################
+
+    @lazy_class
+    def _badheaders(self):
+        '''bad header detecting regex (from wsgiref)'''
+        return re.compile(r'[\000-\037]')
+
+    @lazy_class
+    def _rules(self):
+        '''validation rules for individual HTTP header fields'''
+        return {
+            # "ALLOW" values must be valid HTTP methods
+            'allow': (
+                lambda v: v.upper() not in self._METHODS,
+                '"Allow" value must be valid HTTP method'
+            ),
+            # "CONTENT-ENCODING" value must be zip, compress, or deflate
+            'content-encoding': (
+                lambda v: v.lower() not in self._ENCODINGS,
+                '"Content-Encoding" must be gzip, compress, deflate',
+            ),
+            # "CONTENT-LANGUAGE" values must be valid language codes
+            'content-language': (
+                lambda v: v.lower() not in self._langs,
+                'Unknown language code'
+            ),
+            # "CONTENT-LENGTH" value must be an integer
+            'content-length': (
+                lambda v: not v.isdigit(),
+                '"Content-Length" must be integer'
+            ),
+            # "CONTENT-RANGE" value must be in the proper format
+            'content-range': (
+                lambda v: not self._iscontentrange(v),
+                'Invalid format for "Content-Range"'
+            ),
+        }
+
+    @classmethod
+    def _validate(self, key, value):
+        '''
+        validate HTTP header `value`
+
+        @param key: HTTP header name
+        @param value: putative header value
+        '''
+        if not isstring(value):
+            return True
+        err_msg = None
+        # don't allow response headers
+        if key in self._RESPONSE:
+            err_msg = '"%s" is HTTP response-only header' % key
+        # don't allow unicode header names
+        if isunicode(key):
+            err_msg = 'HTTP header name must be non-Unicode string'
+        # don't allow unicode header values outside RFC 2822
+        elif isunicode(value):
+            err_msg = 'use `rfc2822` to set Unicode HTTP header values'
+        # don't allow "status" to be set in headers
+        elif key == 'status':
+            err_msg = 'HTTP status code cannot be set on HTTP request'
+        # don't allow newline or colon in header name
+        elif '\n' in key or ':' in key:
+            err_msg = 'HTTP header names may not contain ":" or "\\n"'
+        # do not allow invalid characters in header name
+        elif not self._ishttpname(key):
+            err_msg = 'Bad HTTP header name: %r' % key
+        # do not allow dash or underscore at end of header name
+        elif key.endswith('-') or key.endswith('_'):
+            err_msg = 'HTTP header name may not end in "-" or "_"'
+        # no control characters in header name
+        elif isstring(value) and self._badheaders.search(value):
+            err_msg = 'Bad character %r in HTTP header value %r' % (
+                self._badheaders.search(value).group(0), value
+            )
+        # validate RFC 1123 compliance
+        elif key in self._DATES and not self._isrfc1123(value):
+            err_msg = 'Date is not RFC 1123 compliant'
+        # validate individual header
+        else:
+            rule = self._rules.get(key)
+            if rule is not None and rule[0](value):
+                err_msg = rule[1]
+        if err_msg is not None:
+            raise TypeError(err_msg)
+
+    _h_validate = _validate
+
+    ###########################################################################
+    ## header retrieval #######################################################
+    ###########################################################################
+
+    def _getheaders(self, key):
+        '''
+        fetch tracker associated with header `key` category
+
+        @param key: header key
+        '''
+        # "best practice" sends HTTP headers in order: 1. general headers
+        if key in self._GENERAL:
+            return self._general
+        # 2. request headers
+        elif key in self._REQUEST:
+            return self._request
+        # 3. entity headers
+        elif key in self._ENTITY:
+            return self._entity
+        # 4. custom headers
+        return self._custom
+
+    _h_getheaders = _getheaders
+
+    def get(self, key):
+        '''
+        get HTTP request header value
+
+        @param key: header key
+        '''
+        key = self._key(key)
+        return self._getheaders(key).get(key)
+
+    _hget = get
+
+    ###########################################################################
+    ## header manipulation ####################################################
+    ###########################################################################
+
+    def _value(self, key, value):
+        '''
+        properly format for HTTP header `value`
+
+        @param key: key associated with value
+        @param value: value to format
+        '''
+        # keep nulls out of the pipeline
+        if value is None or not value:
+            return value
+        key = self._key(key)
+        # join with any existing value if header allows multiple values
+        if key in self._DATES and isinstance(value, datetime):
+            value = httpdate(value)
+        return key, n(value)
+
+    _hvalue = _value
+
+    def rfc2822(self, key, value, charset=None):
+        '''
+        set HTTP header `value` with `charset` encoded following RFC 2822
+
+        @param key: header key
+        @param value: header value
+        @param charset: charset (default: None)
+        '''
+        # use an existing email.Header instance
+        existing = self.get(key)
+        if existing is not None:
+            try:
+                existing.append(value, charset)
+            except AttributeError:
+                pass
+        else:
+            # override existing value if not Header instance or add new value
+            self.set(key, Header(value, charset))
+
+    _hrfc2822 = rfc2822
+
+    def set(self, key, value):
+        '''
+        set HTTP request header `value`
+
+        @param key: header key
+        @param value: header value
+        '''
+        fmt = self._value
+        # format key, value
+        key, value = fmt(key, value)
+        # validate
+        self._validate(key, value)
+        # fetch header dict
+        headers = self._getheaders(key)
+        previous = headers.get(key)
+        # if previous value exists
+        if previous is not None:
+            # try to assign as email.Header instance
+            try:
+                previous.append(value)
+            except AttributeError:
+                # join with existing values if multiple values allowed
+                if key in self._COMMA_SEP:
+                    headers[key] = fmt(', '.join([headers[key], value]))
+                # replace value in headers allowing only single values
+                elif key in self._SC_SEP:
+                    headers[key] = fmt('; '.join([headers[key], value]))
+                else:
+                    headers[key] = value
+        else:
+            # add value if not present
+            headers[key] = value
+
+    _hset = set
+
+    def delete(self, key):
+        '''
+        delete HTTP request header value associated with `key`
+
+        @param key: header key
+        '''
+        del self.get(key)[self._key(key)]
+
+    _hdelete = delete
+
+    def update(self, headers):
+        '''
+        update `headers` with `headers`
+
+        @param headers: other headers
+        '''
+        exhaustmap(headers, self._hset)
+
+    _hupdate = update
+
+
+class ResponseHeaders(CoreHeaders):
+
+    '''HTTP response headers'''
+
+    def __init__(self, headers):
+        super(ResponseHeaders, self).__init__()
+        newheaders = self._hset('_headers', dict())
+        cookies = list()
+        cappend = cookies.append
+        for k, v in items(headers):
+            if k.lower() in set(['set-cookie', 'cookie']):
+                cappend(v)
+            else:
+                # header keys will be lower case
+                newheaders[k.lower()] = v
+        # combine cookies
+        self._hset('_cookies', ', '.join(cookies) if cookies else '')
+
+    def __getattr__(self, key):
+        try:
+            return object.__getattribute__(self, key)
+        except AttributeError:
+            value = self.get(key)
+            if value is not None:
+                return value
+            raise AttributeError(key)
+
+    def __iter__(self):
+        return iter((k, self.get(k)) for k in self._headers)
+
+    @classmethod
+    def _set(cls, key, value):
+        '''
+        Ensures proper formatting for a content value
+
+        @param key: Key associated with value
+        @param value: Value to format if required
+        '''
+        # Keep nulls out of the pipeline
+        if isstring(value):
+            # HTTP variables to cast to integers
+            if key in set(['accept-ranges', 'age', 'content-length']):
+                value = cls._parseint(value)
+            elif key in cls.COMMA_SEP:
+                # Split comma-separatable values
+                newval = value.replace(', ', ',').replace(' ,', ',').split(',')
+                if len(newval) > 1:
+                    value = newval
+            # Reformat HTTP dates as datetime objects
+            elif key in cls.DATES:
+                value = datehttp(value)
+        return value
+
+    @lazy_class
+    def _headmap(self):
+        '''Python name -> header name mapping'''
+        return dict((self._checkname(h), h) for h in self.RES_HEADERS)
+
+    def header(self, key):
+        '''
+        Return an item associated with a key or a default value
+
+        Certain environ HTTP variables are automatically reformatted as Python
+        objects. Comma separated fields are returned as lists. Headers with
+        date values are returned as datetime objects
+        '''
+        k = key.lower()
+        tk = self._headmap.get(k)
+        v = self._headers.get(tk)
+        # Format value
+        self._headers[k] = v = self._set(k, v)
+        self._hset(tk, v)
+        return self

wire/http/keys/__init__.py

+# -*- coding: utf-8 -*-
+'''http response keys'''

wire/http/keys/apps.py

+# -*- coding: utf-8 -*-
+'''wire http appconf'''
+
+from callchain.patterns import Pathways, Nameways
+
+from wire.talk.apps import talk, untalk
+
+
+class events(Pathways):
+    pass
+
+
+class response(untalk):
+    pass
+
+
+class request(talk):
+    class data(Nameways):
+        key = 'wire.http.keys.request.KRequestData'
+        data = 'wire.http.request.Data'
+
+    class header(Nameways):
+        key = 'wire.http.keys.request.KRequestHeader'
+        header = 'wire.http.request.Header'
+
+    class requestsua(Nameways):
+        key = 'wire.http.keys.request.KRequestsUA'
+        header = 'wire.http.agents.requestua.Response'

wire/http/keys/client.py

+# -*- coding: utf-8 -*-
+#@PydevCodeAnalysisIgnore
+#pylint: disable-msg=e0211,e0213
+'''http client response keys'''
+
+from appspace.keys import AppspaceKey
+
+
+class KResponse(AppspaceKey):
+    
+    '''HTTP client response key'''
+
+    def code():
+        '''
+        HTTP response code
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+        '''
+
+    def good():
+        '''True if HTTP was successful enough'''
+
+    def fail():
+        '''True if HTTP request failed'''
+
+    def noop():
+        '''True if HTTP request was more or less harmless'''
+
+    def ok():
+        '''True if HTTP request was successful'''
+
+    def status():
+        '''status of HTTP response'''
+        
+        
+        
+class KRequestClient(AppspaceKey):
+
+    '''HTTP client key'''
+
+    def __call__(*urls, **config):
+        '''
+        load urls
+
+        @param urls: Uniform Resource Locations (URLs)
+        '''
+
+    def content_md5():
+        '''take MD5 hash of data and set *CONTENT-MD5* HTTP header'''
+
+    def get():
+        '''
+        *GET* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3
+        '''
+
+    def post():
+        '''
+        *POST* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5
+        '''
+
+    def delete():
+        '''
+        *DELETE* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7
+        '''
+
+    def put():
+        '''
+        *PUT* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6
+        '''
+
+    def head():
+        '''
+        *HEAD* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
+        '''
+
+    def options():
+        '''
+        *OPTIONS* HTTP method
+
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2
+        '''
+
+    def auth(username, password):
+        '''
+        set authentication settings
+
+        @param username: username credential
+        @param password: password credential
+        '''
+
+    def proxy(server, host):
+        '''
+        proxy settings
+
+        @param server: proxy server
+        @param host: proxy host
+        '''
+
+
+class KAPI(AppspaceKey):
+
+    '''HTTP API key'''
+
+    def api():
+        '''switch to HTTP API chain'''
+

wire/http/keys/data.py

+# -*- coding: utf-8 -*-
+#@PydevCodeAnalysisIgnore
+#pylint: disable-msg=e0211,e0213
+'''http client response data keys'''
+
+from appspace.keys import AppspaceKey
+        
+        
+
+
+        
+class KResponseData(AppspaceKey):
+    
+    '''HTTP response data key'''
+    
+    def touched():
+        '''Verify MD5 digest of response data against "Content-MD5" digest'''
+        
+    def raw():
+        '''read original response content'''   
+    
+    def data():
+        '''response data'''

wire/http/keys/header.py

+# -*- coding: utf-8 -*-
+#@PydevCodeAnalysisIgnore
+#pylint: disable-msg=e0211,e0213
+'''http client response keys'''
+
+from appspace.keys import AppspaceKey
+
+
+class KRequestHeaders(AppspaceKey):
+    
+    '''HTTP request key'''
+        
+    def accept(value):
+        '''
+        *ACCEPT* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def accept_charset(value):
+        '''
+        *ACCEPT-CHARSET* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def accept_encoding(value):
+        '''
+        *ACCEPT-ENCODING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def accept_language(value):
+        '''
+        *ACCEPT-LANGUAGE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def allow(value):
+        '''
+        *ALLOW* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def authorization(value):
+        '''
+        *AUTHORIZATION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def cache_control(value):
+        '''
+        set *CACHE-CONTROL* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def cookie(key, **values):
+        '''
+        cookie setter
+        
+        @param key: cookie key
+        @param **values: cookie field names, values
+        '''
+    
+    def connection(value):
+        '''
+        set *CONNECTION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def content_length(value):
+        '''
+        *CONTENT-LENGTH* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_encoding(value):
+        '''
+        *CONTENT-ENCODING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def content_language(value):
+        '''
+        *CONTENT-LANGUAGE* HTTP header
+        
+        @param value HTTP header value
+        '''
+
+    def content_location(value):
+        '''
+        *CONTENT-LOCATION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_range(first, last, length='*'):
+        '''
+        set rangeset in HTTP header *CONTENT-RANGE*
+
+        @param first: first byte position
+        @param last: last byte position
+        @param length: instance length
+        '''
+    
+    def date(value):
+        '''
+        *DATE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def expires(value):
+        '''
+        *EXPIRES* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def expect(value):
+        '''
+        *EXPECT* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def from_(value):
+        '''
+        *FROM* HTTP header
+        
+        @param value: HTTP header value
+        '''
+    
+    def uheader(key, value):
+        '''
+        HTTP request header setter
+        
+        @param key: HTTP header key
+        @param value: HTTP header value
+        '''
+        
+    def bheader(key, value):
+        '''
+        HTTP request header setter
+        
+        @param key: HTTP header key
+        @param value: HTTP header value
+        '''
+
+    def header(key, value):
+        '''
+        HTTP request header setter
+        
+        @param key: HTTP header key
+        @param value: HTTP header value
+        '''
+
+    def host(value):
+        '''
+        *HOST* HTTP header
+        
+        @param value: HTTP header value
+        '''
+    
+    def if_match(value):
+        '''
+        *IF-MATCH* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def if_modified_since(value):
+        '''
+        *IF-MODIFIED-SINCE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def if_none_match(value):
+        '''
+        *IF-NONE-MATCH* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def last_modified(value):
+        '''
+        *LAST-MODIFIED* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def if_range(value):
+        '''
+        *IF-RANGE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def if_unmodified_since(value):
+        '''
+        *IF-UNMODIFIED-SINCE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def max_forwards(value):
+        '''
+        *MAX-FORWARDS* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def pragma(value):
+        '''
+        *PRAGMA* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def proxy_authorization(value):
+        '''
+        *PROXY-AUTHORIZATION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def range(value):
+        '''
+        *RANGE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def referer(value):
+        '''
+        *REFERER* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def te(value):
+        '''
+        *TE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+        
+    def trailer(value):
+        '''
+        *TRAILER* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def transfer_enconding(value):
+        '''
+        *TRANSFER-ENCODING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def upgrade(value):
+        '''
+        *UPGRADE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def user_agent(value):
+        '''
+        *USER-AGENT* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def via(value):
+        '''
+        *VIA* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def warning(value):
+        '''
+        *WARNING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+
+class KResponseCookie(AppspaceKey):
+    
+    '''HTTP response cookie key'''
+    
+    def cookie(*keys):
+        '''
+        HTTP cookie
+        
+        @param *keys: cookie keys
+        '''
+        
+    def morsel(*keys):
+        '''
+        switch to HTTP response cookies chain
+        
+        @param *keys: cookie keys
+        '''
+
+
+class KResponseHeader(AppspaceKey):
+    
+    '''response headers key'''
+    
+    def header(*keys):
+        '''
+        fetch HTTP response headers
+        
+        @param key: header keys key
+        '''
+    
+    def cache_control():
+        '''*CACHE-CONTROL* HTTP header'''
+    
+    def connection():
+        '''*CONNECTION* HTTP header'''
+        
+    def date():
+        '''*DATE* HTTP header'''
+    
+    def pragma():
+        '''*PRAGMA* HTTP header'''
+        
+    def trailer():
+        '''*TRAILER* HTTP header'''
+        
+    def via():
+        '''*VIA* HTTP header'''
+        
+    def transfer_enconding():
+        '''*TRANSFER-ENCODING* HTTP header'''
+        
+    def upgrade():
+        '''*UPGRADE* HTTP header'''
+        
+    def warning():
+        '''*WARNING* HTTP header'''
+        
+    def allow():
+        '''*ALLOW* HTTP header'''
+    
+    def content_encoding():
+        '''*CONTENT-ENCODING* HTTP header'''
+        
+    def content_language():
+        '''*CONTENT-LANGUAGE* HTTP header'''
+        
+    def content_length():
+        '''*CONTENT-LENGTH* HTTP header'''
+        
+    def content_location():
+        '''*CONTENT-LOCATION* HTTP header'''
+        
+    def content_md5():
+        '''*CONTENT-MD5* HTTP header'''
+        
+    def content_range():
+        '''HTTP header *CONTENT-RANGE*'''
+
+    def content_type():
+        '''
+        HTTP *CONTENT-TYPE* header
+        
+        http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
+        '''
+    
+    def expires():
+        '''*EXPIRES* HTTP header'''
+        
+    def last_modified():
+        '''*LAST-MODIFIED* HTTP header'''
+    
+    def accept_ranges():
+        '''*ACCEPT-RANGES* HTTP header'''
+        
+    def age():
+        '''*AGE* HTTP header'''
+        
+    def etag():
+        '''*ETAG* HTTP header''' 
+        
+    def location():
+        '''*LOCATION* HTTP header'''
+        
+    def proxy_authenticate():
+        '''*PROXY-AUTHENTICATE* HTTP header'''
+
+    def retry_after():
+        '''*RETRY-AFTER* HTTP header'''
+        
+    def server():
+        '''*SERVER* HTTP header'''
+        
+    def vary():
+        '''*VARY* HTTP header'''
+
+    def www_authenticate():
+        '''*WWW-AUTHETICATE* HTTP header'''

wire/http/keys/request.py

+# -*- coding: utf-8 -*-
+#@PydevCodeAnalysisIgnore
+#pylint: disable-msg=e0211,e0213
+'''http request keys'''
+
+from callchain.services.queue import KService
+
+
+class KRequestHeaders(KService):
+
+    '''HTTP request key'''
+
+    def accept(value):
+        '''
+        *ACCEPT* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def accept_charset(value):
+        '''
+        *ACCEPT-CHARSET* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def accept_encoding(value):
+        '''
+        *ACCEPT-ENCODING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def accept_language(value):
+        '''
+        *ACCEPT-LANGUAGE* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def allow(value):
+        '''
+        *ALLOW* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def authorization(value):
+        '''
+        *AUTHORIZATION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def cache_control(value):
+        '''
+        set *CACHE-CONTROL* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def cookie(key, value, **attributes):
+        '''
+        set cookie values
+
+        @param key: cookie key
+        @param value: cookie value
+        @param **attrs: cookie attributes
+        '''
+
+    def connection(value):
+        '''
+        set *CONNECTION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_length(value):
+        '''
+        *CONTENT-LENGTH* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_encoding(value):
+        '''
+        *CONTENT-ENCODING* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_language(value):
+        '''
+        *CONTENT-LANGUAGE* HTTP header
+        
+        @param value HTTP header value
+        '''
+
+    def content_location(value):
+        '''
+        *CONTENT-LOCATION* HTTP header
+        
+        @param value: HTTP header value
+        '''
+
+    def content_range(first, last, length='*'):
+        '''
+        set rangeset in HTTP header *CONTENT-RANGE*
+
+        @param first: first byte position
+        @param last: last byte position
+        @param length: instance length
+        '''
+        
+    def content_type(mime, charset=None):
+        '''
+        set HTTP *CONTENT-TYPE* header.
+
+        @param mime: MIME media type