def event_callback(fd, evt, userdata_key): global now, event_counter now = time.time() event_counter += 1 evreq, req, byref_event = utils.get_and_del_userdata(userdata_key) req.event_key = None return root_handler(evreq, req, REQUEST_CONTINUE, evt)
def close_callback(conn, userdata_key): global now, event_counter now = time.time() event_counter += 1 evreq, req = utils.get_and_del_userdata(userdata_key) req.close_key = None return root_handler(evreq, req, REQUEST_CLOSE)
def event_schedule(fd, timeout, user_callback): byref_timev = None if timeout is not None: timev = libevent.timeval() timev.tv_sec = int(timeout) timev.tv_usec = int((timeout - int(timeout)) * 1000000) byref_timev = ctypes.byref(timev) event = libevent.event() byref_event = ctypes.byref(event) event_key = utils.set_userdata( (user_callback, byref_event, byref_timev) ) libevent.event_set(byref_event, fd, libevent.EV_READ, event_callback_ptr, event_key) libevent.evtimer_add(byref_event, byref_timev) return lambda : utils.get_and_del_userdata(userdata_key)
def event_callback(fd, evt, userdata_key): user_callback, _, _ = utils.get_and_del_userdata(userdata_key) user_callback(True if evt and (evt & libevent.EV_TIMEOUT) else False) return 0
def freeevcon_callback(fd, evt, userdata_key): evcon, byref_event, byref_timev = utils.get_and_del_userdata(userdata_key) del byref_event del byref_timev libevent.evhttp_connection_free(evcon) return 0
def root_handler(evreq, req, event_type, evt=None): content_length = 0 chunks_number = 0 response_dict = {'code':0, 'reason':''} request_closed = False # dispatcher, new reqest or resume one? # head. if event_type == REQUEST_NEW: response_dict, chunked, iterable = req.start_wsgi_application() if not chunked: data = '' for data in iterable: break data = str(data) buf = libevent.evbuffer_new() for key, value in response_dict['headers']: libevent.evhttp_add_header(evreq.contents.output_headers, str(key), str(value)) libevent.evbuffer_add(buf, data, len(data)) libevent.evhttp_send_reply(evreq, response_dict['code'], response_dict['reason'], buf) libevent.evbuffer_free(buf) response_dict['transmitted'] = True req.content_length += len(data) req.chunks_number += 1 content_length = len(data) chunks_number = 1 del buf if event_type == REQUEST_CONTINUE: response_dict, chunked, iterable = req.continue_wsgi_application(True if evt and (evt & libevent.EV_TIMEOUT) else False) assert(chunked) # body. if event_type in [REQUEST_NEW, REQUEST_CONTINUE] and chunked: assert(getattr(iterable, '__iter__', None)) iterator = getattr(iterable, '__iter__')() try: first_chunk = str(iterator.next()) except StopIteration: first_chunk = None if not req.buf: # must be after first evaluation of iterable req.buf = libevent.evbuffer_new() for key, value in response_dict['headers']: if req.environ['SERVER_PROTOCOL'] == 'HTTP/1.1' and key == "Transfer-Encoding": # libevent automagically adds it's own transer-encoding header. f**k you libevent continue libevent.evhttp_add_header(evreq.contents.output_headers, str(key), str(value)) libevent.evhttp_send_reply_start(evreq, response_dict['code'], response_dict['reason']) response_dict['transmitted'] = True # exit points: connection broken (cleared in handler), end of request (cleared in tail) req.close_key = utils.set_userdata( (evreq, req) ) libevent.evhttp_connection_set_closecb(evreq.contents.evcon, close_callback_ptr, req.close_key) if first_chunk: libevent.evbuffer_add(req.buf, first_chunk, len(first_chunk)) libevent.evhttp_send_reply_chunk(evreq, req.buf) req.content_length += len(first_chunk) req.chunks_number += 1 content_length += len(first_chunk) chunks_number += 1 # now transfer all the chunks for data_chunk in iterator: data_chunk = str(data_chunk) if not data_chunk: # can't really send empty data -> it means conn close continue # TODO: bug here, packets aren't on the wire just after evhttp_send_reply_chunk, and they should # TODO: this is not nice, but this is how http works in libevent, sorry. libevent.evbuffer_add(req.buf, data_chunk, len(data_chunk)) libevent.evhttp_send_reply_chunk(evreq, req.buf) req.content_length += len(data_chunk) req.chunks_number += 1 content_length += len(data_chunk) chunks_number += 1 # before closing req.update_cpu_time() # tail. if event_type == REQUEST_CLOSE or (chunked and req.is_closed()): if evreq.contents.evcon and evreq.contents.evcon.contents and evreq.contents.evcon.contents.closecb: libevent.evhttp_connection_set_closecb(evreq.contents.evcon, void_callback_ptr, None) if req.close_key: assert(event_type != REQUEST_CLOSE) utils.get_and_del_userdata(req.close_key) del req.close_key if event_type != REQUEST_CLOSE: libevent.evhttp_send_reply_end(evreq) else: assert(event_type == REQUEST_CLOSE) if evreq.contents.evcon: # BUG in libevent, memleak if conn is broken ''' event = libevent.event() timeval = libevent.timeval() timeval.tv_sec = 0 timeval.tv_usec = 1 byref_event = ctypes.byref(event) byref_timev = ctypes.byref(timeval) libevent.evtimer_set(byref_event, freeevcon_callback_ptr, utils.set_userdata( (evreq.contents.evcon, byref_event, byref_timev) ) ) libevent.evtimer_add(byref_event, byref_timev) ''' pass if req.buf: libevent.evbuffer_free(req.buf) del req.buf # free event, if scheduled: if req.event_key: assert(event_type == REQUEST_CLOSE) _, _, byref_event = utils.get_and_del_userdata( req.event_key ) libevent.event_del(byref_event) del byref_event del req.event_key req.close(con_broken=True if event_type == REQUEST_CLOSE else False) request_closed = True if event_type == REQUEST_NEW and not chunked:# cleanup single req.close(con_broken=False) # How the f**k to force closing connection here..., after sendig data #libevent.evhttp_request_free(evreq) #libevent.evhttp_connection_free(evreq.contents.evcon) log.debug('''%(host)s "%(method)s %(url)s %(http)s" %(status_code)i %(content_length)s/%(chunks_number)s (%(time).1fms) - %(event)s''' % { 'method': req.environ['REQUEST_METHOD'], 'url': req.get_url(), 'http': req.environ['SERVER_PROTOCOL'], 'status_code': response_dict['code'], 'content_length': content_length, 'chunks_number': chunks_number, 'host': '%s:%i' % (req.environ['REMOTE_HOST'], req.environ['REMOTE_PORT']), 'time': (req.now_cpu_time) * 1000, # in miliseconds 'event': 'close' if request_closed else 'single' if not chunked else 'chunk', }) return 1