class Element(Node): preserveCase = 0 caseInsensitive = 1 nsprefixes = None def __init__(self, tagName, attributes=None, parentNode=None, filename=None, markpos=None, caseInsensitive=1, preserveCase=0, namespace=None): Node.__init__(self, parentNode) self.preserveCase = preserveCase or not caseInsensitive self.caseInsensitive = caseInsensitive if not preserveCase: tagName = tagName.lower() if attributes is None: self.attributes = {} else: self.attributes = attributes for k, v in self.attributes.items(): self.attributes[k] = unescape(v) if caseInsensitive: self.attributes = InsensitiveDict(self.attributes, preserve=preserveCase) self.endTagName = self.nodeName = self.tagName = tagName self._filename = filename self._markpos = markpos self.namespace = namespace def addPrefixes(self, pfxs): if self.nsprefixes is None: self.nsprefixes = pfxs else: self.nsprefixes.update(pfxs) def endTag(self, endTagName): if not self.preserveCase: endTagName = endTagName.lower() self.endTagName = endTagName def isEqualToElement(self, n): if self.caseInsensitive: return ((self.attributes == n.attributes) and (self.nodeName.lower() == n.nodeName.lower())) return (self.attributes == n.attributes) and (self.nodeName == n.nodeName) def isEqualToNode(self, other): """ Compare this element to C{other}. If the C{nodeName}, C{namespace}, C{attributes}, and C{childNodes} are all the same, return C{True}, otherwise return C{False}. """ return (self.nodeName.lower() == other.nodeName.lower() and self.namespace == other.namespace and self.attributes == other.attributes and Node.isEqualToNode(self, other)) def cloneNode(self, deep=0, parent=None): clone = Element(self.tagName, parentNode=parent, namespace=self.namespace, preserveCase=self.preserveCase, caseInsensitive=self.caseInsensitive) clone.attributes.update(self.attributes) if deep: clone.childNodes = [ child.cloneNode(1, clone) for child in self.childNodes ] else: clone.childNodes = [] return clone def getElementsByTagName(self, name): if self.caseInsensitive: return getElementsByTagNameNoCase(self, name) return getElementsByTagName(self, name) def hasAttributes(self): return 1 def getAttribute(self, name, default=None): return self.attributes.get(name, default) def getAttributeNS(self, ns, name, default=None): nsk = (ns, name) if self.attributes.has_key(nsk): return self.attributes[nsk] if ns == self.namespace: return self.attributes.get(name, default) return default def getAttributeNode(self, name): return _Attr(self.getAttribute(name), self) def setAttribute(self, name, attr): self.attributes[name] = attr def removeAttribute(self, name): if name in self.attributes: del self.attributes[name] def hasAttribute(self, name): return name in self.attributes def writexml(self, stream, indent='', addindent='', newl='', strip=0, nsprefixes={}, namespace=''): # write beginning ALLOWSINGLETON = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area', 'input', 'col', 'basefont', 'isindex', 'frame') BLOCKELEMENTS = ('html', 'head', 'body', 'noscript', 'ins', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'script', 'ul', 'ol', 'dl', 'pre', 'hr', 'blockquote', 'address', 'p', 'div', 'fieldset', 'table', 'tr', 'form', 'object', 'fieldset', 'applet', 'map') FORMATNICELY = ('tr', 'ul', 'ol', 'head') # this should never be necessary unless people start # changing .tagName on the fly(?) if not self.preserveCase: self.endTagName = self.tagName w = stream.write if self.nsprefixes: newprefixes = self.nsprefixes.copy() for ns in nsprefixes.keys(): if ns in newprefixes: del newprefixes[ns] else: newprefixes = {} begin = ['<'] if self.tagName in BLOCKELEMENTS: begin = [newl, indent] + begin bext = begin.extend writeattr = lambda _atr, _val: bext( (' ', _atr, '="', escape(_val), '"')) if namespace != self.namespace and self.namespace is not None: if nsprefixes.has_key(self.namespace): prefix = nsprefixes[self.namespace] bext(prefix + ':' + self.tagName) else: bext(self.tagName) writeattr("xmlns", self.namespace) else: bext(self.tagName) j = ''.join for attr, val in self.attributes.iteritems(): if isinstance(attr, tuple): ns, key = attr if nsprefixes.has_key(ns): prefix = nsprefixes[ns] else: prefix = genprefix() newprefixes[ns] = prefix assert val is not None writeattr(prefix + ':' + key, val) else: assert val is not None writeattr(attr, val) if newprefixes: for ns, prefix in newprefixes.iteritems(): if prefix: writeattr('xmlns:' + prefix, ns) newprefixes.update(nsprefixes) downprefixes = newprefixes else: downprefixes = nsprefixes w(j(begin)) if self.childNodes: w(">") newindent = indent + addindent for child in self.childNodes: if self.tagName in BLOCKELEMENTS and \ self.tagName in FORMATNICELY: w(j((newl, newindent))) child.writexml(stream, newindent, addindent, newl, strip, downprefixes, self.namespace) if self.tagName in BLOCKELEMENTS: w(j((newl, indent))) w(j(("</", self.endTagName, '>'))) elif self.tagName.lower() not in ALLOWSINGLETON: w(j(('></', self.endTagName, '>'))) else: w(" />") def __repr__(self): rep = "Element(%s" % repr(self.nodeName) if self.attributes: rep += ", attributes=%r" % (self.attributes, ) if self._filename: rep += ", filename=%r" % (self._filename, ) if self._markpos: rep += ", markpos=%r" % (self._markpos, ) return rep + ')' def __str__(self): rep = "<" + self.nodeName if self._filename or self._markpos: rep += " (" if self._filename: rep += repr(self._filename) if self._markpos: rep += " line %s column %s" % self._markpos if self._filename or self._markpos: rep += ")" for item in self.attributes.items(): rep += " %s=%r" % item if self.hasChildNodes(): rep += " >...</%s>" % self.nodeName else: rep += " />" return rep
class Element(Node): preserveCase = 0 caseInsensitive = 1 nsprefixes = None namespace = '' def __init__(self, tagName, attributes=None, parentNode=None, filename=None, markpos=None, caseInsensitive=1, preserveCase=0, namespace=''): Node.__init__(self, parentNode) self.preserveCase = preserveCase or not caseInsensitive self.caseInsensitive = caseInsensitive if not preserveCase: tagName = tagName.lower() if attributes is None: self.attributes = {} else: self.attributes = attributes for k, v in self.attributes.items(): self.attributes[k] = unescape(v) if caseInsensitive: self.attributes = InsensitiveDict(self.attributes, preserve=preserveCase) self.endTagName = self.nodeName = self.tagName = tagName self._filename = filename self._markpos = markpos self.namespace = namespace def addPrefixes(self, pfxs): if self.nsprefixes is None: self.nsprefixes = pfxs else: self.nsprefixes.update(pfxs) def endTag(self, endTagName): if not self.preserveCase: endTagName = endTagName.lower() self.endTagName = endTagName def isEqualToElement(self, n): if self.caseInsensitive: return (self.attributes == n.attributes) and (self.nodeName.lower() == n.nodeName.lower()) return (self.attributes == n.attributes) and (self.nodeName == n.nodeName) def cloneNode(self, deep=0, parent=None): clone = Element( self.tagName, parentNode=parent, namespace=self.namespace, preserveCase=self.preserveCase, caseInsensitive=self.caseInsensitive) clone.attributes.update(self.attributes) if deep: clone.childNodes = [child.cloneNode(1, clone) for child in self.childNodes] else: clone.childNodes = [] return clone def getElementsByTagName(self, name): if self.caseInsensitive: return getElementsByTagNameNoCase(self, name) return getElementsByTagName(self, name) def hasAttributes(self): return 1 def getAttribute(self, name, default=None): return self.attributes.get(name, default) def getAttributeNS(self, ns, name, default=None): nsk = (ns, name) if self.attributes.has_key(nsk): return self.attributes[nsk] if ns == self.namespace: return self.attributes.get(name, default) return default def getAttributeNode(self, name): return _Attr(self.getAttribute(name), self) def setAttribute(self, name, attr): self.attributes[name] = attr def removeAttribute(self, name): if name in self.attributes: del self.attributes[name] def removeAttribute_has_key(self, name): if self.attributes.has_key(name): del self.attributes[name] def hasAttribute(self, name): return name in self.attributes def hasAttribute_has_key(self, name): return self.attributes.has_key(name) if dictsAreNotSequences: hasAttribute = hasAttribute_has_key removeAttribute = removeAttribute_has_key def writexml(self, stream, indent='', addindent='', newl='', strip=0, nsprefixes={}, namespace=''): # write beginning ALLOWSINGLETON = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area', 'input', 'col', 'basefont', 'isindex', 'frame') BLOCKELEMENTS = ('html', 'head', 'body', 'noscript', 'ins', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'script', 'ul', 'ol', 'dl', 'pre', 'hr', 'blockquote', 'address', 'p', 'div', 'fieldset', 'table', 'tr', 'form', 'object', 'fieldset', 'applet', 'map') FORMATNICELY = ('tr', 'ul', 'ol', 'head') # this should never be necessary unless people start # changing .tagName on the fly(?) if not self.preserveCase: self.endTagName = self.tagName w = stream.write if self.nsprefixes: newprefixes = self.nsprefixes.copy() for ns in nsprefixes.keys(): del newprefixes[ns] else: newprefixes = {} begin = ['<'] if self.tagName in BLOCKELEMENTS: begin = [newl, indent] + begin bext = begin.extend writeattr = lambda _atr, _val: bext((' ', _atr, '="', escape(_val), '"')) if namespace != self.namespace and self.namespace: if nsprefixes.has_key(self.namespace): prefix = nsprefixes[self.namespace] bext(prefix+':'+self.tagName) else: bext(self.tagName) writeattr("xmlns", self.namespace) else: bext(self.tagName) j = ''.join for attr, val in self.attributes.iteritems(): if isinstance(attr, tuple): ns, key = attr if nsprefixes.has_key(ns): prefix = nsprefixes[ns] else: prefix = genprefix() newprefixes[ns] = prefix assert val is not None writeattr(prefix+':'+key,val) else: assert val is not None writeattr(attr, val) if newprefixes: for ns, prefix in newprefixes.iteritems(): if prefix: writeattr('xmlns:'+prefix, ns) newprefixes.update(nsprefixes) downprefixes = newprefixes else: downprefixes = nsprefixes w(j(begin)) if self.childNodes: w(">") newindent = indent + addindent for child in self.childNodes: if self.tagName in BLOCKELEMENTS and \ self.tagName in FORMATNICELY: w(j((newl, newindent))) child.writexml(stream, newindent, addindent, newl, strip, downprefixes, self.namespace) if self.tagName in BLOCKELEMENTS: w(j((newl, indent))) w(j(("</", self.endTagName, '>'))) elif self.tagName.lower() not in ALLOWSINGLETON: w(j(('></', self.endTagName, '>'))) else: w(" />") def __repr__(self): rep = "Element(%s" % repr(self.nodeName) if self.attributes: rep += ", attributes=%r" % (self.attributes,) if self._filename: rep += ", filename=%r" % (self._filename,) if self._markpos: rep += ", markpos=%r" % (self._markpos,) return rep + ')' def __str__(self): rep = "<" + self.nodeName if self._filename or self._markpos: rep += " (" if self._filename: rep += repr(self._filename) if self._markpos: rep += " line %s column %s" % self._markpos if self._filename or self._markpos: rep += ")" for item in self.attributes.items(): rep += " %s=%r" % item if self.hasChildNodes(): rep += " >...</%s>" % self.nodeName else: rep += " />" return rep
class Element(Node): preserveCase = 0 caseInsensitive = 1 nsprefixes = None def __init__(self, tagName, attributes=None, parentNode=None, filename=None, markpos=None, caseInsensitive=1, preserveCase=0, namespace=None): Node.__init__(self, parentNode) self.preserveCase = preserveCase or not caseInsensitive self.caseInsensitive = caseInsensitive if not preserveCase: tagName = tagName.lower() if attributes is None: self.attributes = {} else: self.attributes = attributes for k, v in self.attributes.items(): self.attributes[k] = unescape(v) if caseInsensitive: self.attributes = InsensitiveDict(self.attributes, preserve=preserveCase) self.endTagName = self.nodeName = self.tagName = tagName self._filename = filename self._markpos = markpos self.namespace = namespace def addPrefixes(self, pfxs): if self.nsprefixes is None: self.nsprefixes = pfxs else: self.nsprefixes.update(pfxs) def endTag(self, endTagName): if not self.preserveCase: endTagName = endTagName.lower() self.endTagName = endTagName def isEqualToElement(self, n): if self.caseInsensitive: return ((self.attributes == n.attributes) and (self.nodeName.lower() == n.nodeName.lower())) return (self.attributes == n.attributes) and (self.nodeName == n.nodeName) def isEqualToNode(self, other): """ Compare this element to C{other}. If the C{nodeName}, C{namespace}, C{attributes}, and C{childNodes} are all the same, return C{True}, otherwise return C{False}. """ return ( self.nodeName.lower() == other.nodeName.lower() and self.namespace == other.namespace and self.attributes == other.attributes and Node.isEqualToNode(self, other)) def cloneNode(self, deep=0, parent=None): clone = Element( self.tagName, parentNode=parent, namespace=self.namespace, preserveCase=self.preserveCase, caseInsensitive=self.caseInsensitive) clone.attributes.update(self.attributes) if deep: clone.childNodes = [child.cloneNode(1, clone) for child in self.childNodes] else: clone.childNodes = [] return clone def getElementsByTagName(self, name): if self.caseInsensitive: return getElementsByTagNameNoCase(self, name) return getElementsByTagName(self, name) def hasAttributes(self): return 1 def getAttribute(self, name, default=None): return self.attributes.get(name, default) def getAttributeNS(self, ns, name, default=None): nsk = (ns, name) if self.attributes.has_key(nsk): return self.attributes[nsk] if ns == self.namespace: return self.attributes.get(name, default) return default def getAttributeNode(self, name): return _Attr(self.getAttribute(name), self) def setAttribute(self, name, attr): self.attributes[name] = attr def removeAttribute(self, name): if name in self.attributes: del self.attributes[name] def hasAttribute(self, name): return name in self.attributes def writexml(self, stream, indent='', addindent='', newl='', strip=0, nsprefixes={}, namespace=''): """ Serialize this L{Element} to the given stream. @param stream: A file-like object to which this L{Element} will be written. @param nsprefixes: A C{dict} mapping namespace URIs as C{str} to prefixes as C{str}. This defines the prefixes which are already in scope in the document at the point at which this L{Element} exists. This is essentially an implementation detail for namespace support. Applications should not try to use it. @param namespace: The namespace URI as a C{str} which is the default at the point in the document at which this L{Element} exists. This is essentially an implementation detail for namespace support. Applications should not try to use it. """ # write beginning ALLOWSINGLETON = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area', 'input', 'col', 'basefont', 'isindex', 'frame') BLOCKELEMENTS = ('html', 'head', 'body', 'noscript', 'ins', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'script', 'ul', 'ol', 'dl', 'pre', 'hr', 'blockquote', 'address', 'p', 'div', 'fieldset', 'table', 'tr', 'form', 'object', 'fieldset', 'applet', 'map') FORMATNICELY = ('tr', 'ul', 'ol', 'head') # this should never be necessary unless people start # changing .tagName on the fly(?) if not self.preserveCase: self.endTagName = self.tagName w = stream.write if self.nsprefixes: newprefixes = self.nsprefixes.copy() for ns in nsprefixes.keys(): if ns in newprefixes: del newprefixes[ns] else: newprefixes = {} begin = ['<'] if self.tagName in BLOCKELEMENTS: begin = [newl, indent] + begin bext = begin.extend writeattr = lambda _atr, _val: bext((' ', _atr, '="', escape(_val), '"')) # Make a local for tracking what end tag will be used. If namespace # prefixes are involved, this will be changed to account for that # before it's actually used. endTagName = self.endTagName if namespace != self.namespace and self.namespace is not None: # If the current default namespace is not the namespace of this tag # (and this tag has a namespace at all) then we'll write out # something related to namespaces. if self.namespace in nsprefixes: # This tag's namespace already has a prefix bound to it. Use # that prefix. prefix = nsprefixes[self.namespace] bext(prefix + ':' + self.tagName) # Also make sure we use it for the end tag. endTagName = prefix + ':' + self.endTagName else: # This tag's namespace has no prefix bound to it. Change the # default namespace to this tag's namespace so we don't need # prefixes. Alternatively, we could add a new prefix binding. # I'm not sure why the code was written one way rather than the # other. -exarkun bext(self.tagName) writeattr("xmlns", self.namespace) # The default namespace just changed. Make sure any children # know about this. namespace = self.namespace else: # This tag has no namespace or its namespace is already the default # namespace. Nothing extra to do here. bext(self.tagName) j = ''.join for attr, val in self.attributes.iteritems(): if isinstance(attr, tuple): ns, key = attr if nsprefixes.has_key(ns): prefix = nsprefixes[ns] else: prefix = genprefix() newprefixes[ns] = prefix assert val is not None writeattr(prefix+':'+key,val) else: assert val is not None writeattr(attr, val) if newprefixes: for ns, prefix in newprefixes.iteritems(): if prefix: writeattr('xmlns:'+prefix, ns) newprefixes.update(nsprefixes) downprefixes = newprefixes else: downprefixes = nsprefixes w(j(begin)) if self.childNodes: w(">") newindent = indent + addindent for child in self.childNodes: if self.tagName in BLOCKELEMENTS and \ self.tagName in FORMATNICELY: w(j((newl, newindent))) child.writexml(stream, newindent, addindent, newl, strip, downprefixes, namespace) if self.tagName in BLOCKELEMENTS: w(j((newl, indent))) w(j(('</', endTagName, '>'))) elif self.tagName.lower() not in ALLOWSINGLETON: w(j(('></', endTagName, '>'))) else: w(" />") def __repr__(self): rep = "Element(%s" % repr(self.nodeName) if self.attributes: rep += ", attributes=%r" % (self.attributes,) if self._filename: rep += ", filename=%r" % (self._filename,) if self._markpos: rep += ", markpos=%r" % (self._markpos,) return rep + ')' def __str__(self): rep = "<" + self.nodeName if self._filename or self._markpos: rep += " (" if self._filename: rep += repr(self._filename) if self._markpos: rep += " line %s column %s" % self._markpos if self._filename or self._markpos: rep += ")" for item in self.attributes.items(): rep += " %s=%r" % item if self.hasChildNodes(): rep += " >...</%s>" % self.nodeName else: rep += " />" return rep
def __send_http_response(self, inh, request, response_msg): """ When we've know what message to send back to the HTTP client which has connected to the HTTP server, we issue this message back. @param inh: an inhabinant who will receive the response @type inh: AbstractInhabitant @param request: web server request. @type request: Request @param response_msg: response message to send. @type response_msg: AbstractMessage """ if not self.connection_alive: logger.debug( 'Connection died inside %r while trying ' 'to reply with %r', self, response_msg) # That's very very bad! We should put the message back to queue. self.tr_manager.post_message(response_msg) else: logger.debug('Sending response %r to %r', response_msg, request.requestHeaders) response_msg.in_transfer = True # Add the default server-specific headers, # then the message-specific headers. _headers = InsensitiveDict( dict(request.responseHeaders.getAllRawHeaders())) _pragma = ','.join( ifilter(None, [response_msg.get_pragma(), self.extra_pragma()])) HTTPTransport.update_headers(_headers, {'Pragma': [_pragma]}) HTTPTransport.update_headers(_headers, response_msg.get_headers()) for k, v_list in _headers.iteritems(): request.responseHeaders.setRawHeaders(k, v_list) logger.debug('New headers for %r are: %r', response_msg, request.responseHeaders) @contract_epydoc def produce_body_in_main(body, msg): """ @type body: col.Iterable @type msg: AbstractMessage """ assert in_main_thread() try: producer = LazyPullProducer(request, body) producer.completed.addBoth(self._ready_to_finish.callback) except Exception: logger.exception('Error during writing back the request:') # We haven't succeeded to send the message to the client. # Put it back to the queue. callInThread(self.tr_manager.post_message, msg) assert not in_main_thread() # get_body() may take long _body = response_msg.get_body() logger.debug('Writing body: %r', response_msg) callFromThread(produce_body_in_main, _body, response_msg)
def _render(self, request, avatar): """Node-specific request processing. @param request: Web server request. @type request: Request @type avatar: Host @returns: nothing (not a C{Deferred} object!). But, due to C{inlineCallbacks}, yields the intermediate C{Deferred}s. @todo: Proper error handling. @todo: Properly handle the situation when the passthrough is restricted: generate the reply which contains the Error 404. @todo: This should be streamlined using super() as soon as C{twisted.web.resource.Resource} becomes a new-style class """ assert not in_main_thread() # Kind-of-calling the parent, but the parent in this case should # return a Deferred as well,... let's not bother with it. # DeferredProcessingResource._render(self, request, avatar) logger.debug('') logger.debug('') logger.debug('Avatar %r;' '\tincoming headers %r;' '\tfrom %r', avatar, request.requestHeaders, request.client) # We setup the default headers for response even before # we start analyzing the incoming message, # so that if analyzing fails for some reason, we have the headers # and can create a more-or-less proper error reply. _requestHeaders = InsensitiveDict( dict(request.requestHeaders.getAllRawHeaders())) _responseHeaders = InsensitiveDict() self.initialize_headers(_responseHeaders) message = self.create_message_from_headers(headers=_requestHeaders, auth_peer=avatar) message.body_receiver = RequestBodyReceiver(message=message, request=request) # Make sure the body is received, before going further dummy_received_data = yield message.body_receiver.on_finish assert not in_main_thread() if message.direct: # If this is the message directed to ourselves, # process it and send the response logger.debug('') logger.debug('> Der Mitteilung Nahert: %r von %s', message, message.src) # Process the incoming message try: self.tr_manager.handle_incoming_message(message) except TransactionProcessingException as e: # The incoming transaction cannot be processed. # TODO: handle error properly, send a error reply maybe logger.error('Error (%r) during incoming msg processing: %s', e, e) logger.debug('Will send response to %r from %r %s', message, message.src, hash(message.src)) for k, v_list in _responseHeaders.iteritems(): request.responseHeaders.setRawHeaders(k, v_list) logger.debug('Done with response headers') elif self._accept_passthrough_message(message): # Non-direct message, and passthrough allowed: # wait for the body, then put in the queue. logger.debug('Passthrough message %r: body already available', message) self.tr_manager.post_message(message) else: # Passthrough is restricted for this message! raise NotImplementedError(u'Failed message {!r}:\n' u'from: {!r}\n' u' to: {!r}'.format( message, message.src, message.dst)) # How to check if we still need a message? still_need_checker = lambda: self.connection_alive # No matter if we are processing the message or passing it through, # we must send a response somehow. # If this is the message to us, we'd prefer a response to reply it; # but if this is a passthrough message, it will go to a different peer. prefer_msg_uuid = message.uuid if message.direct else None try: # Let's (asynchronously) wait for the response message here. resp_msg = self.__response_msg \ = yield self.tr_manager.wait_for_message_for_peer( inh=message.src, prefer_msg_uuid=prefer_msg_uuid, still_wait_checker=still_need_checker) except internet_error.ConnectionClosed as e: logger.warning('Stopped to wait for a message due to %r', e) self._handle_connection_close(Failure(e)) except Exception as e: logger.exception('Stopped to wait for a message due to %r', e) self._handle_connection_close(Failure(e)) else: # Since now and till the end of transfer we store the message # in self.__response_msg. assert not in_main_thread() if resp_msg is None: # The connection died already, while waiting for the message. assert not self.connection_alive else: # Got the message, send it back assert isinstance(resp_msg, AbstractMessage), repr(resp_msg) self.__send_http_response(message.src, request, resp_msg)
def __send_http_response(self, inh, request, response_msg): """ When we've know what message to send back to the HTTP client which has connected to the HTTP server, we issue this message back. @param inh: an inhabinant who will receive the response @type inh: AbstractInhabitant @param request: web server request. @type request: Request @param response_msg: response message to send. @type response_msg: AbstractMessage """ if not self.connection_alive: logger.debug('Connection died inside %r while trying ' 'to reply with %r', self, response_msg) # That's very very bad! We should put the message back to queue. self.tr_manager.post_message(response_msg) else: logger.debug('Sending response %r to %r', response_msg, request.requestHeaders) response_msg.in_transfer = True # Add the default server-specific headers, # then the message-specific headers. _headers = InsensitiveDict(dict(request.responseHeaders .getAllRawHeaders())) _pragma = ','.join(ifilter(None, [response_msg.get_pragma(), self.extra_pragma()])) HTTPTransport.update_headers(_headers, {'Pragma': [_pragma]}) HTTPTransport.update_headers(_headers, response_msg.get_headers()) for k, v_list in _headers.iteritems(): request.responseHeaders.setRawHeaders(k, v_list) logger.debug('New headers for %r are: %r', response_msg, request.responseHeaders) @contract_epydoc def produce_body_in_main(body, msg): """ @type body: col.Iterable @type msg: AbstractMessage """ assert in_main_thread() try: producer = LazyPullProducer(request, body) producer.completed.addBoth(self._ready_to_finish.callback) except Exception: logger.exception('Error during writing back the request:') # We haven't succeeded to send the message to the client. # Put it back to the queue. callInThread(self.tr_manager.post_message, msg) assert not in_main_thread() # get_body() may take long _body = response_msg.get_body() logger.debug('Writing body: %r', response_msg) callFromThread(produce_body_in_main, _body, response_msg)
def _render(self, request, avatar): """Node-specific request processing. @param request: Web server request. @type request: Request @type avatar: Host @returns: nothing (not a C{Deferred} object!). But, due to C{inlineCallbacks}, yields the intermediate C{Deferred}s. @todo: Proper error handling. @todo: Properly handle the situation when the passthrough is restricted: generate the reply which contains the Error 404. @todo: This should be streamlined using super() as soon as C{twisted.web.resource.Resource} becomes a new-style class """ assert not in_main_thread() # Kind-of-calling the parent, but the parent in this case should # return a Deferred as well,... let's not bother with it. # DeferredProcessingResource._render(self, request, avatar) logger.debug('') logger.debug('') logger.debug('Avatar %r;' '\tincoming headers %r;' '\tfrom %r', avatar, request.requestHeaders, request.client) # We setup the default headers for response even before # we start analyzing the incoming message, # so that if analyzing fails for some reason, we have the headers # and can create a more-or-less proper error reply. _requestHeaders = InsensitiveDict(dict(request.requestHeaders .getAllRawHeaders())) _responseHeaders = InsensitiveDict() self.initialize_headers(_responseHeaders) message = self.create_message_from_headers(headers=_requestHeaders, auth_peer=avatar) message.body_receiver = RequestBodyReceiver(message=message, request=request) # Make sure the body is received, before going further dummy_received_data = yield message.body_receiver.on_finish assert not in_main_thread() if message.direct: # If this is the message directed to ourselves, # process it and send the response logger.debug('') logger.debug('> Der Mitteilung Nahert: %r von %s', message, message.src) # Process the incoming message try: self.tr_manager.handle_incoming_message(message) except TransactionProcessingException as e: # The incoming transaction cannot be processed. # TODO: handle error properly, send a error reply maybe logger.error('Error (%r) during incoming msg processing: %s', e, e) logger.debug('Will send response to %r from %r %s', message, message.src, hash(message.src)) for k, v_list in _responseHeaders.iteritems(): request.responseHeaders.setRawHeaders(k, v_list) logger.debug('Done with response headers') elif self._accept_passthrough_message(message): # Non-direct message, and passthrough allowed: # wait for the body, then put in the queue. logger.debug('Passthrough message %r: body already available', message) self.tr_manager.post_message(message) else: # Passthrough is restricted for this message! raise NotImplementedError(u'Failed message {!r}:\n' u'from: {!r}\n' u' to: {!r}'.format(message, message.src, message.dst)) # How to check if we still need a message? still_need_checker = lambda: self.connection_alive # No matter if we are processing the message or passing it through, # we must send a response somehow. # If this is the message to us, we'd prefer a response to reply it; # but if this is a passthrough message, it will go to a different peer. prefer_msg_uuid = message.uuid if message.direct else None try: # Let's (asynchronously) wait for the response message here. resp_msg = self.__response_msg \ = yield self.tr_manager.wait_for_message_for_peer( inh=message.src, prefer_msg_uuid=prefer_msg_uuid, still_wait_checker=still_need_checker) except internet_error.ConnectionClosed as e: logger.warning('Stopped to wait for a message due to %r', e) self._handle_connection_close(Failure(e)) except Exception as e: logger.exception('Stopped to wait for a message due to %r', e) self._handle_connection_close(Failure(e)) else: # Since now and till the end of transfer we store the message # in self.__response_msg. assert not in_main_thread() if resp_msg is None: # The connection died already, while waiting for the message. assert not self.connection_alive else: # Got the message, send it back assert isinstance(resp_msg, AbstractMessage), repr(resp_msg) self.__send_http_response(message.src, request, resp_msg)