def getTemplateRequest(self, httpMethod="GET", httpUri="/", httpVersion='HTTP/1.1', httpHeaders={}, httpBody=None): """ """ tpl = TestTemplates.TemplateMessage() if self.cfg['agent-support']: layer_agent = TestTemplates.TemplateLayer('AGENT') layer_agent.addKey(name='name', data=self.cfg['agent']['name']) layer_agent.addKey(name='type', data=self.cfg['agent']['type']) tpl.addLayer(layer_agent) tpl.addLayer(AdapterIP.ip(more=AdapterIP.received())) tpl.addLayer(AdapterTCP.tcp(more=AdapterTCP.received())) if self.ADP_TCP.cfg['ssl-support']: tpl.addLayer(AdapterSSL.ssl(more=AdapterSSL.received())) headers = {} headers.update(httpHeaders) tpl.addLayer( templates.request(method=httpMethod, uri=httpUri, version=httpVersion, headers=headers, body=httpBody)) return tpl
def hasReceivedHttpRequest(self, httpMethod="GET", httpUri="/", httpVersion='HTTP/1.1', timeout=1.0, httpHeaders={}, httpBody=None): """ Wait to receive "http request" until the end of the timeout. @param httpMethod: http method (default=GET) @type httpMethod: string @param httpUri: http phrase (default=OK) @type httpUri: string @param httpVersion: http version (default=HTTP/1.1) @type httpVersion: string @param httpHeaders: expected http headers @type httpHeaders: dict @param httpBody: expected body (default=None) @type httpBody: string/none @param timeout: time to wait in seconds (default=1s) @type timeout: float @return: http response @rtype: template """ if not (isinstance(timeout, int) or isinstance(timeout, float)): raise TestAdapter.ValueException( TestAdapter.caller(), "timeout argument is not a float or integer (%s)" % type(timeout)) tpl = TestTemplates.TemplateMessage() if self.cfg['agent-support']: layer_agent = TestTemplates.TemplateLayer('AGENT') layer_agent.addKey(name='name', data=self.cfg['agent']['name']) layer_agent.addKey(name='type', data=self.cfg['agent']['type']) tpl.addLayer(layer_agent) tpl.addLayer(AdapterIP.ip(more=AdapterIP.received())) tpl.addLayer(AdapterTCP.tcp(more=AdapterTCP.received())) if self.ADP_TCP.cfg['ssl-support']: tpl.addLayer(AdapterSSL.ssl(more=AdapterSSL.received())) headers = {} headers.update(httpHeaders) tpl.addLayer( templates.request(method=httpMethod, uri=httpUri, version=httpVersion, headers=headers, body=httpBody)) return self.hasReceivedRequest(expected=tpl, timeout=timeout)
def decode_req(self, req, nomoredata=False): """ Try to decode some data to a valid payload. """ if len(req) == 0 and nomoredata: return (DECODING_NOTHING_TODO, None, None, None) bod = '' # split all headers hdrs = req.split('\r\n') # parse response code and phrase reqDecoded = hdrs[0].split(" ", 2) if len(reqDecoded) != 3: raise Exception('malformed sip request: %s' % req) sipMethod = reqDecoded[0] sipUri = reqDecoded[1] sipVersion = reqDecoded[2] summary = "%s %s" % (sipMethod, sipUri) # parse hdrs to list sip_body_detected = False content_type_hdr = None content_length_hdr = None transfer_encoding_hdr = None hdrs_dict = {} i = 1 for hdr in hdrs[1:]: i += 1 hv = hdr.strip() # remove spaces before and after if not hdr: sip_body_detected = True break # reached body and its empty line fv = hdr.split(':', 1) if len(fv) != 2: raise Exception('invalid sip headers in response') # rfc3261: field names are always case-insensitive. f = fv[0].lower().strip() # extract field and remove spaces v = fv[1].strip() # extract value and remove spaces if f == "content-type": content_type_hdr = v if f == "content-length": content_length_hdr = v if f == "transfer-encoding": transfer_encoding_hdr = v if hdrs_dict.has_key(f): if isinstance(hdrs_dict[f], TestTemplatesLib.TemplateLayer): tpl.addKey(name="%s" % tpl.getLenItems(), data=v) else: tpl = TestTemplatesLib.TemplateLayer('') tpl.addKey(name="%s" % tpl.getLenItems(), data=hdrs_dict[f]) tpl.addKey(name="%s" % tpl.getLenItems(), data=v) hdrs_dict[f] = tpl else: hdrs_dict[f] = v if not sip_body_detected: return (DECODING_NEED_MORE_DATA, None, None, None) # transfer-encoding header is present ? if transfer_encoding_hdr is not None: self.debug("transfer encoding header detected (%s)" % str(transfer_encoding_hdr)) # content-length header is present ? else: bod = "\r\n".join(hdrs[i:]) if content_length_hdr is not None: self.debug("content-length header detected (%s)" % str(content_length_hdr)) cl = int(content_length_hdr) bl = len(bod) if bl < cl: return (DECODING_NEED_MORE_DATA, None, None, None) elif bl > cl: # Truncate the body bod = bod[:cl] else: self.debug( "no transfer encoding header or content-length header") # no content-length: wait until the end of the connection if not nomoredata: return (DECODING_NEED_MORE_DATA, None, None, None) # content-type header is present ? if content_type_hdr is not None: content_type = content_type_hdr.split( ";", 1)[0] # split the type of data # update summary with content type summary = "%s (%s)" % (summary, content_type) # decode the body with the charset defined in the header content-type and re-encode to utf8 if content_type == "text/html" or content_type == "text/plain": if len(bod) > 0: content_type_charset = content_type_hdr.split("charset=") if len(content_type_charset) == 1: raise Exception("charset is mssing") else: content_type_charset = content_type_hdr.split( "charset=")[1].split(";", 1)[0] try: bod_decoded = bod.decode(content_type_charset) bod = bod_decoded.encode("utf-8") except Exception as e: self.warning( 'Codec: decode body failed with charset: %s' % content_type_charset) # create template ret = templates.request(version=sipVersion, method=sipMethod, uri=sipUri, headers=hdrs_dict, body=bod) left_data = "\r\n".join(hdrs[i:]) if len(bod) == 0 and len(left_data) > 0: return (DECODING_OK_CONTINUE, ret, summary, left_data) else: return (DECODING_OK, ret, summary, None)
def decode(self, rsp, nomoredata=False, request=False): """ Try to decode some data to a valid payload. """ if len(rsp) == 0 and nomoredata: return (DECODING_NOTHING_TODO, None, None, None) # strict mode for the separator between headers and body ? if '\r\n\r\n' not in rsp: if not self.strictMode: if '\n\r\n' in rsp: rsp = rsp.replace('\n\r\n', '\r\n\r\n' ) bod = '' # split all headers hdrs = rsp.split(self.sep) # parse response code and phrase rspDecoded = hdrs[0].split(" ", 2) if len(rspDecoded) != 3: if request: self.debug('malformed http code request: %s' % rsp) else: self.debug('malformed http code response: %s' % rsp) return (DECODING_NEED_MORE_DATA, None, None, None) if request: httpMethod = rspDecoded[0] httpUri = rspDecoded[1] httpVersion = rspDecoded[2] summary = "%s %s %s" % (httpMethod, httpUri, httpVersion) else: httpVersion = rspDecoded[0] httpCode = rspDecoded[1] httpPhrase = (rspDecoded[2].decode("ISO8859-1")).encode("utf-8") summary = "%s %s %s" % (httpVersion, httpCode, httpPhrase) # parse hdrs to list http_body_detected = False content_type_hdr = None content_length_hdr = None transfer_encoding_hdr = None hdrs_dict = {} i = 1 for hdr in hdrs[1:]: i += 1 h = hdr.strip() # remove spaces before and after if not hdr: http_body_detected = True break # reached body and its empty line fv = hdr.split(':', 1) if len(fv) != 2: raise Exception('invalid http headers: %s' % fv ) # rfc 2616: Field names are case-insensitive f = fv[0].lower().strip() # extract field and remove spaces v = fv[1].strip() # extract value and remove spaces if f == "content-type": content_type_hdr = v if f == "content-length": content_length_hdr = v if f == "transfer-encoding": transfer_encoding_hdr = v if hdrs_dict.has_key(f): if isinstance( hdrs_dict[f], TestTemplatesLib.TemplateLayer): tpl.addKey(name="%s" % tpl.getLenItems(), data=v ) else: tpl = TestTemplatesLib.TemplateLayer('') tpl.addKey(name="%s" % tpl.getLenItems(), data=hdrs_dict[f]) tpl.addKey(name="%s" % tpl.getLenItems(), data=v ) hdrs_dict[f] = tpl else: hdrs_dict[f] = v if not http_body_detected: self.debug("body not detected") return (DECODING_NEED_MORE_DATA, None, None, None) # transfer-encoding header is present ? if transfer_encoding_hdr is not None: self.debug( "transfer encoding header detected (%s)" % str(transfer_encoding_hdr) ) if transfer_encoding_hdr == 'chunked': try: if not hdrs[i].strip(): self.debug("wait for the chunk size in a next payload" ) return (DECODING_NEED_MORE_DATA, None, None, None) # Let's wait for the chunk size in a next payload chunkSize = int(hdrs[i].strip(), 16) self.debug("chunk size detected: %s" % chunkSize ) remainingPayload = '\r\n'.join(hdrs[i+1:]) # consuming chunk with thr exactly chunkSize characters then followed by an empty line or possibly another chunksize while chunkSize != 0: chunk = remainingPayload[:chunkSize] if len(chunk) < chunkSize: self.debug("current chunk (%s) lower than chunk size expected (%s), need more data" % ( len(chunk), chunkSize) ) return (DECODING_NEED_MORE_DATA, None, None, None) bod += chunk remainingPayload = remainingPayload[chunkSize:] lines = remainingPayload.split('\r\n') # ['', '0', '', ''] if lines[0]: # should be an empty line... raise Exception("No chunk boundary at the end of the chunk. Invalid data.") if not lines[1]: self.debug("No next chunk size yet" ) return (DECODING_NEED_MORE_DATA, None, None, None) # No next chunk size yet else: chunkSize = int(lines[1].strip(), 16) self.debug("next chunk size detected: %s" % chunkSize ) # fix issue, final chunk size received but crlf is missing if len(lines) <= 2: self.debug("next chunk size received but crlf is missing, waiting more data" ) return (DECODING_NEED_MORE_DATA, None, None, None) # final chunk size received but crlf is missing # end of fix remainingPayload = '\r\n'.join(lines[2:]) except IndexError: return (DECODING_NEED_MORE_DATA, None, None, None) else: raise Exception("not yet supported") # content-length header is present ? else: bod ="\r\n".join(hdrs[i:]) if content_length_hdr is not None: self.debug( "content-length header detected (%s)" % str(content_length_hdr) ) cl = int(content_length_hdr) bl = len(bod) truncate_body = False if self.truncateBody: bod = '%s ...stream truncated....' % bod[:100] truncate_body = True if not ( truncate_body ): if bl < cl: return (DECODING_NEED_MORE_DATA, None, None, None) elif bl > cl: # Truncate the body bod = bod[:cl] else: self.debug( "no transfer encoding header or content-length header" ) if request: if http_body_detected and len(bod) == 0: nomoredata = True # if the status code is 100 or 200 OK from a proxy, discard the first response and read the next one instead. if len(hdrs) == 3 and len(bod) == 0: if len(hdrs[0]) and not len(hdrs[1]) and not len(hdrs[2]): self.debug('message with one line only detected, discard and read the next one') bod = '' nomoredata = True if self.websocketMode: self.debug( "websocket mode activated on codec" ) if http_body_detected and len(bod) == 0: nomoredata = True if not request: if http_body_detected and len(bod) == 0 and httpCode == "204": self.debug('204 no content detected') bod = '' nomoredata = True # No chunk, no content-length, no body separator detected # wait until the end of the connection if not nomoredata: return (DECODING_NEED_MORE_DATA, None, None, None) # content-type header is present ? if content_type_hdr is not None: self.debug( "content type header detected (%s)" % str(content_type_hdr) ) content_type = content_type_hdr.split(";", 1)[0] # split the type of data # update summary with content type summary = "%s (%s)" % (summary, content_type) # decode the body with the charset defined in the header content-type and re-encode to utf8 if content_type == "text/html" or content_type == "text/plain" : if len(bod)> 0: # fix error in adapter 2.1.0, decode body only if the length > 0 content_type_charset = content_type_hdr.split("charset=") if len(content_type_charset)==1: self.debug("Codec: charset is missing, decode body with iso-8859-1") content_type_charset = "iso-8859-1" else: content_type_charset = content_type_hdr.split("charset=")[1].split(";", 1)[0] try: bod_decoded = bod.decode(content_type_charset) bod = bod_decoded.encode("utf-8") except Exception as e: self.warning('Codec: unable to decode body with charset: %s' % content_type_charset) # create template if request: ret = templates.request(method=httpMethod, uri=httpUri, version=httpVersion, headers=hdrs_dict, body=bod) else: if not len(hdrs_dict): hdrs_dict = None if not len(bod): bod=None ret = templates.response(version=httpVersion, code=httpCode, phrase=httpPhrase, headers=hdrs_dict, body=bod) if not request: if httpCode == "100": left_data = "\r\n".join(hdrs[i:]) ret.addRaw(hdrs[0]) return (DECODING_OK_CONTINUE, ret, summary, left_data ) return (DECODING_OK, ret, summary, None)