def save_test(self): """Save a previously run test_id.""" try: # touch the save file so it isn't deleted. os.utime( os.path.join(save_dir, self.test_id), ( nbhttp.now(), nbhttp.now() + (save_days * 24 * 60 * 60) ) ) location = "?id=%s" % self.test_id if self.descend: location = "%s&descend=True" % location self.output_hdrs( "303 See Other", [ ("Location", location) ]) self.output_body("Redirecting to the saved test page...") except (OSError, IOError): self.output_hdrs( "500 Internal Server Error", [ ("Content-Type", "text/html; charset=%s" % charset), ]) # TODO: better error message (through formatter?) self.output_body( error_template % "Sorry, I couldn't save that." ) self.body_done()
def run(self, done_cb=None): """ Make an asynchronous HTTP request to uri, calling status_cb as it's updated and done_cb when it's done. Reason is used to explain what the request is in the status callback. """ self.outstanding_tasks += 1 self.done_cb = done_cb state = self.state if not self.preflight() or state.uri == None: # generally a good sign that we're not going much further. self.finish_task() return if 'user-agent' not in [i[0].lower() for i in state.req_hdrs]: state.req_hdrs.append( ("User-Agent", "RED/%s (http://redbot.org/)" % __version__)) self.client = RedHttpClient(self._response_start) if self.status_cb and state.type: self.status_cb("fetching %s (%s)" % (state.uri, state.type)) req_body, req_done = self.client.req_start( state.method, state.uri, state.req_hdrs, nbhttp.dummy) state.req_ts = nbhttp.now() if state.req_body != None: req_body(state.req_body) req_done(None)
def status(self, message): "Update the status bar of the browser" self.output(u""" <script> <!-- %3.3f window.status="%s"; --> </script> """ % (nbhttp.now() - self.start, e(message)))
def load_saved_test(self): """Load a saved test by test_id.""" try: fd = gzip.open(os.path.join( save_dir, os.path.basename(self.test_id) )) mtime = os.fstat(fd.fileno()).st_mtime except (OSError, IOError, zlib.error): self.output_hdrs( "404 Not Found", [ ("Content-Type", "text/html; charset=%s" % charset), ("Cache-Control", "max-age=600, must-revalidate") ]) # TODO: better error page (through formatter?) self.output_body(error_template % "I'm sorry, I can't find that saved response." ) self.body_done() return is_saved = mtime > nbhttp.now() try: ired = pickle.load(fd) except (pickle.PickleError, EOFError): self.output_hdrs( "500 Internal Server Error", [ ("Content-Type", "text/html; charset=%s" % charset), ("Cache-Control", "max-age=600, must-revalidate") ]) # TODO: better error page (through formatter?) self.output_body(error_template % "I'm sorry, I had a problem reading that response." ) self.body_done() return finally: fd.close() formatter = find_formatter(self.format, 'html', self.descend)( self.base_uri, ired.uri, ired.orig_req_hdrs, lang, self.output, allow_save=(not is_saved), is_saved=True, test_id=self.test_id ) self.output_hdrs( "200 OK", [ ("Content-Type", "%s; charset=%s" % ( formatter.media_type, charset)), ("Cache-Control", "max-age=3600, must-revalidate") ]) formatter.start_output() formatter.set_red(ired) formatter.finish_output() self.body_done()
def _response_start(self, version, status, phrase, res_headers, res_pause ): "Process the response start-line and headers." state = self.state state.res_ts = nbhttp.now() state.res_version = version state.res_status = status.decode('iso-8859-1', 'replace') state.res_phrase = phrase.decode('iso-8859-1', 'replace') state.res_hdrs = res_headers ra.ResponseHeaderParser(state) ra.ResponseStatusChecker(state) state.res_body_enc = state.parsed_hdrs.get( 'content-type', [None, {}] )[1].get('charset', 'utf-8') # default isn't really UTF-8, but oh well return self._response_body, self._response_done
def __init__(self, *args, **kw): Formatter.__init__(self, *args, **kw) self.hidden_text = [] self.start = nbhttp.now()
def final_status(self): self.status("RED made %(reqs)s requests in %(elapse)2.3f seconds." % { 'reqs': fetch.total_requests, 'elapse': nbhttp.now() - self.start })
def _response_done(self, err): "Finish anaylsing the response, handling any parse errors." state = self.state state.res_complete = True state.res_done_ts = nbhttp.now() state.transfer_length = self.client.input_transfer_length state.header_length = self.client.input_header_length self.client = None state.res_error = err if self.status_cb and state.type: self.status_cb("fetched %s (%s)" % (state.uri, state.type)) state.res_body_md5 = self._md5_processor.digest() state.res_body_post_md5 = self._md5_post_processor.digest() if err == None: pass elif err['desc'] == nbhttp.error.ERR_BODY_FORBIDDEN['desc']: state.setMessage('header-none', rs.BODY_NOT_ALLOWED) elif err['desc'] == nbhttp.error.ERR_EXTRA_DATA['desc']: state.res_body_len += len(err.get('detail', '')) elif err['desc'] == nbhttp.error.ERR_CHUNK['desc']: state.setMessage('header-transfer-encoding', rs.BAD_CHUNK, chunk_sample=e( err.get('detail', '')[:20].encode('string_escape') ) ) elif err['desc'] == nbhttp.error.ERR_CONNECT['desc']: state.res_complete = False elif err['desc'] == nbhttp.error.ERR_LEN_REQ['desc']: pass # TODO: length required elif err['desc'] == nbhttp.error.ERR_URL['desc']: state.res_complete = False elif err['desc'] == nbhttp.error.ERR_READ_TIMEOUT['desc']: state.res_complete = False elif err['desc'] == nbhttp.error.ERR_HTTP_VERSION['desc']: state.res_complete = False else: raise AssertionError, "Unknown response error: %s" % err if state.res_complete: checkCaching(state) if state.res_complete \ and state.method not in ['HEAD'] \ and state.res_status not in ['304']: # check payload basics if state.parsed_hdrs.has_key('content-length'): if state.res_body_len == state.parsed_hdrs['content-length']: state.setMessage('header-content-length', rs.CL_CORRECT) else: state.setMessage('header-content-length', rs.CL_INCORRECT, body_length=f_num(state.res_body_len) ) if state.parsed_hdrs.has_key('content-md5'): c_md5_calc = base64.encodestring(state.res_body_md5)[:-1] if state.parsed_hdrs['content-md5'] == c_md5_calc: state.setMessage('header-content-md5', rs.CMD5_CORRECT) else: state.setMessage('header-content-md5', rs.CMD5_INCORRECT, calc_md5=c_md5_calc) self.done() self.finish_task()
def __init__(self, test_id, test_uri, req_hdrs, base_uri, format, output_hdrs, output_body, descend=False, save=False): self.output_body = output_body self.start = time.time() timeout = nbhttp.schedule(max_runtime, self.timeoutError) if save and save_dir and test_id: try: os.utime( os.path.join(save_dir, test_id), ( nbhttp.now(), nbhttp.now() + (save_days * 24 * 60 * 60) ) ) location = "?id=%s" % test_id if descend: location = "%s&descend=True" % location output_hdrs("303 See Other", [ ("Location", location) ]) output_body("Redirecting...") except (OSError, IOError): output_hdrs("500 Internal Server Error", [ ("Content-Type", "text/html; charset=%s" % charset), ]) # TODO: better error message (through formatter?) output_body(error_template % "Sorry, I couldn't save that.") elif test_id: try: test_id = os.path.basename(test_id) fd = gzip.open(os.path.join(save_dir, test_id)) mtime = os.fstat(fd.fileno()).st_mtime except (OSError, IOError, zlib.error): output_hdrs("404 Not Found", [ ("Content-Type", "text/html; charset=%s" % charset), ("Cache-Control", "max-age=600, must-revalidate") ]) # TODO: better error page (through formatter?) self.output_body(error_template % "I'm sorry, I can't find that saved response." ) timeout.delete() return is_saved = mtime > nbhttp.now() try: ired = pickle.load(fd) except (pickle.PickleError, EOFError): output_hdrs("500 Internal Server Error", [ ("Content-Type", "text/html; charset=%s" % charset), ("Cache-Control", "max-age=600, must-revalidate") ]) # TODO: better error page (through formatter?) self.output_body(error_template % "I'm sorry, I had a problem reading that response." ) timeout.delete() return finally: fd.close() formatter = find_formatter(format, 'html', descend)( base_uri, ired.uri, ired.orig_req_hdrs, lang, self.output, allow_save=(not is_saved), is_saved=True, test_id=test_id ) output_hdrs("200 OK", [ ("Content-Type", "%s; charset=%s" % ( formatter.media_type, charset)), ("Cache-Control", "max-age=3600, must-revalidate") ]) formatter.start_output() formatter.finish_output(ired) elif test_uri: if save_dir and os.path.exists(save_dir): try: fd, path = tempfile.mkstemp(prefix='', dir=save_dir) test_id = os.path.split(path)[1] except (OSError, IOError): # Don't try to store it. test_id = None else: test_id = None formatter = find_formatter(format, 'html', descend)( base_uri, test_uri, req_hdrs, lang, self.output, allow_save=test_id, is_saved=False, test_id=test_id, descend=descend ) output_hdrs("200 OK", [ ("Content-Type", "%s; charset=%s" % ( formatter.media_type, charset)), ("Cache-Control", "max-age=60, must-revalidate") ]) formatter.start_output() ired = droid.InspectingResourceExpertDroid( test_uri, req_hdrs=req_hdrs, status_cb=formatter.status, body_procs=[formatter.feed], descend=descend ) formatter.finish_output(ired) if test_id: try: tmp_file = gzip.open(path, 'w') pickle.dump(ired, tmp_file) tmp_file.close() except (IOError, zlib.error, pickle.PickleError): pass # we don't cry if we can't store it. else: # no test_uri formatter = html.BaseHtmlFormatter( base_uri, test_uri, req_hdrs, lang, self.output) output_hdrs("200 OK", [ ("Content-Type", "%s; charset=%s" % ( formatter.media_type, charset) ), ("Cache-Control", "max-age=300") ]) formatter.start_output() formatter.finish_output(None) timeout.delete()