Commit fb3002e8 authored by Denis Bilenko's avatar Denis Bilenko

core: add http_request_client and add support for initiating connections to...

core: add http_request_client and add support for initiating connections to http_connection (issue #9).

Based on the patch by Tommie Gannert, Örjan Persson.

--HG--
extra : transplant_source : p%9B%BAbw%B5C%BA%ADf%07%10%03%92%A0l%E3%D9%D6%7F
parent 9d4dae23
...@@ -118,6 +118,7 @@ cdef extern from "libevent.h": ...@@ -118,6 +118,7 @@ cdef extern from "libevent.h":
ctypedef void (*conn_closecb)(evhttp_connection *c, void *arg) ctypedef void (*conn_closecb)(evhttp_connection *c, void *arg)
evhttp_connection *evhttp_connection_new(char *addr, short port) evhttp_connection *evhttp_connection_new(char *addr, short port)
evhttp_connection *evhttp_connection_base_new(void*, void*, char *addr, short port)
void evhttp_connection_free(evhttp_connection *c) void evhttp_connection_free(evhttp_connection *c)
void evhttp_connection_set_local_address(evhttp_connection *c, char *addr) void evhttp_connection_set_local_address(evhttp_connection *c, char *addr)
void evhttp_connection_set_timeout(evhttp_connection *c, int secs) void evhttp_connection_set_timeout(evhttp_connection *c, int secs)
...@@ -140,7 +141,7 @@ class HttpConnectionDeleted(ObjectDeleted): ...@@ -140,7 +141,7 @@ class HttpConnectionDeleted(ObjectDeleted):
"""Raised when an attribute is accessed of http_connection instance whose _obj is 0""" """Raised when an attribute is accessed of http_connection instance whose _obj is 0"""
cdef class http_request: cdef class http_request_base:
"""Wrapper around libevent's :class:`evhttp_request` structure.""" """Wrapper around libevent's :class:`evhttp_request` structure."""
# It is possible to crash the process by using it directly. # It is possible to crash the process by using it directly.
...@@ -150,17 +151,9 @@ cdef class http_request: ...@@ -150,17 +151,9 @@ cdef class http_request:
cdef evhttp_request* __obj cdef evhttp_request* __obj
cdef public object _input_buffer cdef public object _input_buffer
cdef public object _output_buffer cdef public object _output_buffer
cdef public object default_response_headers
def __init__(self, size_t _obj, object default_response_headers): def __init__(self, size_t obj):
self.__obj = <evhttp_request*>_obj self.__obj = <evhttp_request *>obj
self.default_response_headers = default_response_headers
def __dealloc__(self):
cdef evhttp_request* obj = self.__obj
if obj != NULL:
self.__obj = NULL
report_internal_error(obj)
property _obj: property _obj:
...@@ -350,6 +343,73 @@ cdef class http_request: ...@@ -350,6 +343,73 @@ cdef class http_request:
self._output_buffer = buffer(<size_t>self.__obj.output_buffer) self._output_buffer = buffer(<size_t>self.__obj.output_buffer)
return self._output_buffer return self._output_buffer
def find_input_header(self, char* key):
if not self.__obj:
raise HttpRequestDeleted
cdef const_char_ptr val = evhttp_find_header(self.__obj.input_headers, key)
if val:
return val
def find_output_header(self, char* key):
if not self.__obj:
raise HttpRequestDeleted
cdef const_char_ptr val = evhttp_find_header(self.__obj.output_headers, key)
if val:
return val
def add_input_header(self, char* key, char* value):
if not self.__obj:
raise HttpRequestDeleted
if evhttp_add_header(self.__obj.input_headers, key, value):
raise RuntimeError('Internal error in evhttp_add_header')
def add_output_header(self, char* key, char* value):
if not self.__obj:
raise HttpRequestDeleted
if evhttp_add_header(self.__obj.output_headers, key, value):
raise RuntimeError('Internal error in evhttp_add_header')
def remove_input_header(self, char* key):
"""Return True if header was found and removed"""
if not self.__obj:
raise HttpRequestDeleted
return True if 0 == evhttp_remove_header(self.__obj.input_headers, key) else False
def remove_output_header(self, char* key):
"""Return True if header was found and removed"""
if not self.__obj:
raise HttpRequestDeleted
return True if 0 == evhttp_remove_header(self.__obj.output_headers, key) else False
def clear_input_headers(self):
if not self.__obj:
raise HttpRequestDeleted
evhttp_clear_headers(self.__obj.input_headers)
def clear_output_headers(self):
if not self.__obj:
raise HttpRequestDeleted
evhttp_clear_headers(self.__obj.output_headers)
cdef class http_request(http_request_base):
"""Wrapper around libevent's :class:`evhttp_request` structure."""
# It is possible to crash the process by using it directly.
# prefer gevent.http and gevent.wsgi which should be safe
cdef public object default_response_headers
def __init__(self, size_t obj, object default_response_headers=[]):
http_request_base.__init__(self, obj)
self.default_response_headers = default_response_headers
def __dealloc__(self):
cdef evhttp_request* obj = self.__obj
if obj != NULL:
self.detach()
report_internal_error(obj)
def _add_default_response_headers(self): def _add_default_response_headers(self):
for key, value in self.default_response_headers: for key, value in self.default_response_headers:
if not self.find_output_header(key): if not self.find_output_header(key):
...@@ -402,61 +462,67 @@ cdef class http_request: ...@@ -402,61 +462,67 @@ cdef class http_request:
self._add_default_response_headers() self._add_default_response_headers()
evhttp_send_error(self.__obj, code, reason) evhttp_send_error(self.__obj, code, reason)
def find_input_header(self, char* key):
if not self.__obj:
raise HttpRequestDeleted
cdef const_char_ptr val = evhttp_find_header(self.__obj.input_headers, key)
if val:
return val
def find_output_header(self, char* key):
if not self.__obj:
raise HttpRequestDeleted
cdef const_char_ptr val = evhttp_find_header(self.__obj.output_headers, key)
if val:
return val
def add_input_header(self, char* key, char* value): cdef class http_request_client(http_request_base):
if not self.__obj: """Wrapper around libevent's :class:`evhttp_request` structure."""
raise HttpRequestDeleted
if evhttp_add_header(self.__obj.input_headers, key, value):
raise RuntimeError('Internal error in evhttp_add_header')
def add_output_header(self, char* key, char* value): cdef public int _owned
if not self.__obj: cdef public callback
raise HttpRequestDeleted cdef int _incref
if evhttp_add_header(self.__obj.output_headers, key, value):
raise RuntimeError('Internal error in evhttp_add_header')
def remove_input_header(self, char* key): def __init__(self, object callback=None, size_t obj=0):
"""Return True if header was found and removed""" self._incref = 0
self.callback = callback
if obj:
self.__obj = <evhttp_request*>obj
self._owned = 0
else:
self.__obj = evhttp_request_new(_http_request_cb_handler, <void*>self)
if not self.__obj: if not self.__obj:
raise HttpRequestDeleted raise IOError('evhttp_request_new() failed')
return True if 0 == evhttp_remove_header(self.__obj.input_headers, key) else False self._owned = 1
self._addref()
def remove_output_header(self, char* key): cdef _addref(self):
"""Return True if header was found and removed""" if self._incref <= 0:
if not self.__obj: Py_INCREF(self)
raise HttpRequestDeleted self._incref += 1
return True if 0 == evhttp_remove_header(self.__obj.output_headers, key) else False
def clear_input_headers(self): cdef _delref(self):
if not self.__obj: if self._incref > 0:
raise HttpRequestDeleted Py_DECREF(self)
evhttp_clear_headers(self.__obj.input_headers) self._incref -= 1
self.callback = None
def clear_output_headers(self): def __dealloc__(self):
if not self.__obj: cdef evhttp_request* obj = self.__obj
raise HttpRequestDeleted if obj != NULL:
evhttp_clear_headers(self.__obj.output_headers) self.detach()
if self._owned:
evhttp_request_free(obj)
cdef class http_connection: cdef class http_connection:
cdef evhttp_connection* __obj cdef evhttp_connection* __obj
cdef public int _owned
def __init__(self, size_t obj, owned=0):
self.__obj = <evhttp_connection*>obj
self._owned = owned
def __init__(self, size_t _obj): @classmethod
self.__obj = <evhttp_connection*>_obj def new(cls, char* address, unsigned short port, event_base base):
cdef void* ptr = evhttp_connection_base_new(base._ptr, NULL, address, port)
if ptr != NULL:
return cls(<size_t>ptr, 1)
def __dealloc__(self):
cdef evhttp_connection* obj = self.__obj
if obj != NULL:
self.__obj = NULL
if self._owned:
evhttp_connection_free(obj)
property _obj: property _obj:
...@@ -497,6 +563,51 @@ cdef class http_connection: ...@@ -497,6 +563,51 @@ cdef class http_connection:
addr = None addr = None
return (addr, port) return (addr, port)
def set_local_address(self, char *addr):
if not self.__obj:
raise HttpConnectionDeleted
evhttp_connection_set_local_address(self.__obj, addr)
def set_timeout(self, int secs):
if not self.__obj:
raise HttpConnectionDeleted
evhttp_connection_set_timeout(self.__obj, secs)
def set_retries(self, int retry_max):
if not self.__obj:
raise HttpConnectionDeleted
evhttp_connection_set_retries(self.__obj, retry_max)
def make_request(self, http_request_client req, int type, char* uri):
req._owned = 0
cdef int result = evhttp_make_request(self.__obj, req.__obj, type, uri)
if result != 0:
req.detach()
return result
cdef void _http_request_cb_handler(evhttp_request* c_request, void *arg) with gil:
if arg == NULL:
return
cdef http_request_client obj = <http_request_client>(arg)
try:
if obj.__obj != NULL:
if obj.__obj != c_request:
# sometimes this happens, don't know why
sys.stderr.write("Internal error in evhttp\n")
if obj.callback is not None:
# preferring c_request to obj.__obj because the latter sometimes causes crashes
obj.callback(http_request_client(obj=<size_t>c_request))
obj.detach()
except:
traceback.print_exc()
try:
sys.stderr.write('Failed to execute callback for evhttp request.\n')
except:
pass
finally:
obj._delref()
cdef void _http_cb_handler(evhttp_request* request, void *arg) with gil: cdef void _http_cb_handler(evhttp_request* request, void *arg) with gil:
cdef http server = <object>arg cdef http server = <object>arg
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment