#ifndef _H_GC_DEVICE_PROXY #define _H_GC_DEVICE_PROXY #include #include /** * @brief Get last error. * * The function returns the last error code and optionally also the last error * message. The error message will be copied into a given buffer (unless it is * NULL). * * The size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the error message. * If size is NULL then the function will only return the last error code. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param buffer buffer for the error message * @param size buffer capacity / length of the message * @return int error code */ int gcdp_get_last_error(char* buffer, size_t* size); typedef void log_record_t; typedef void log_handler_t(void* context, const log_record_t* record); typedef void authorization_t; typedef void request_t; typedef void response_t; typedef void header_iter_t; typedef void device_handler_result_t; typedef void device_handler_t(void* context, authorization_t* authorization, device_handler_result_t* result); typedef void client_handler_result_t; typedef void client_handler_t(void* context, request_t* request, client_handler_result_t* result); typedef void proxy_config_t; typedef void proxy_t; typedef void new_proxy_cb_t(void* context, proxy_t* proxy); typedef void join_proxy_cb_t(void* context, int res); #define LOG_LEVEL_TRACE 0 #define LOG_LEVEL_DEBUG 1 #define LOG_LEVEL_INFO 2 #define LOG_LEVEL_WARNING 3 #define LOG_LEVEL_ERROR 4 /** * @brief Set log handler. * * The log handler can be set only once. The handler is expected to be a * function that accepts two arguments (context and log record) and does not * return anything (see the log_handler_t type for more info). * * The handler and the context MUST be thread-safe. It must be safe to share * the context between multiple threads and it must be safe to call the handler * from multiple threads. * * The context is optional and it may be NULL. * * @param handler log handler * @param context an arbitrary context that will be passed to the handler * @return int 0 on success, error code in case of an error */ int gcdp_set_logger(log_handler_t* handler, void* context); /** * @brief Get log level from a given log record. * * @param record log record * @return int log level */ int gcdp__log_record__get_level(const log_record_t* record); /** * @brief Get log message from a given log record. * * The log message will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the log message. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param record log record * @param buffer buffer for the log message * @param size buffer capacity / length of the message */ void gcdp__log_record__get_message(const log_record_t* record, char* buffer, size_t* size); /** * @brief Create a new proxy configuration. * * @return proxy_config_t* proxy configuration */ proxy_config_t* gcdp__proxy_config__new(void); /** * @brief Set proxy hostname. * * @param config proxy config * @param hostname hostname * @return int 0 on success, error code in case of an error */ int gcdp__proxy_config__set_hostname(proxy_config_t* config, const char* hostname); /** * @brief Add HTTP binding. * * @param config proxy config * @param addr IPv4/IPv6 address as string (e.g. "192.168.1.1") * @param port port number * @return int 0 on success, error code in case of an error */ int gcdp__proxy_config__add_http_bind_addr(proxy_config_t* config, const char* addr, uint16_t port); /** * @brief Add HTTPS binding * * @param config proxy config * @param addr IPv4/IPv6 address as string (e.g. "192.168.1.1") * @param port port number * @return int 0 on success, error code in case of an error */ int gcdp__proxy_config__add_https_bind_addr(proxy_config_t* config, const char* addr, uint16_t port); /** * @brief Use Let's Encrypt to get an HTTPS certificate. * * In order to use Let's Encrypt, you need to set the proxy hostname and use * port 80 for HTTP binding. * * @param config proxy config */ void gcdp__proxy_config__use_lets_encrypt(proxy_config_t* config); /** * @brief Set TLS identity (i.e. a private key and a certificate chain for HTTPS). * * @param config proxy config * @param key private key in PEM format * @param key_size private key size * @param cert certificate chain in PEM format * @param cert_size certificate chain size */ void gcdp__proxy_config__set_tls_identity(proxy_config_t* config, const uint8_t* key, size_t key_size, const uint8_t* cert, size_t cert_size); /** * @brief Set device request handler. * * The device request handler will be called on every connection attempt * received from a GoodCam device. The handler may accept the request, reject * it or redirect the device to a different server. * * The handler is expected to be a function accepting three arguments (context, * authorization, result). See device_handler_t for more info. The context is * an arbitrary pointer passed to the handler. It may be NULL. The * authorization contains device ID and key. The result contains the decision * made by the handler. * * The handler takes ownership of the authorization. It is responsible for * freeing it. * * Both the handler and the context MUST be thread-safe. It must be safe to * share the context between multiple threads and it must be safe to call the * handler from multiple threads. * * @param config proxy config * @param handler device request handler * @param context arbitrary context (optional) */ void gcdp__proxy_config__set_device_handler(proxy_config_t* config, device_handler_t* handler, void* context); /** * @brief Set client request handler. * * The client request handler will be called for each request received from a * client attempting to a access a GoodCam device. The handler may accept the * request and forward it to the corresponding device or it may block the * request by returning a custom response. The handler is responsible for * determining the target device ID from the request. * * The handler is expected to be a function accepting three arguments (context, * request, result). See client_handler_t for more info. The context is an * arbitrary pointer passed to the handler. It may be NULL. The request is the * request received from the client. The result contains the decision made by * the handler. * * The handler takes ownership of the request. It must either forward it or * free it. * * Both the handler and the context MUST be thread-safe. It must be safe to * share the context between multiple threads and it must be safe to call the * handler from multiple threads. * * @param config proxy config * @param handler client request handler * @param context arbitrary context (optional) */ void gcdp__proxy_config__set_client_handler(proxy_config_t* config, client_handler_t* handler, void* context); /** * @brief Free the config. * * @param config proxy config */ void gcdp__proxy_config__free(proxy_config_t* config); /** * @brief Create and start a new proxy from a given config. * * @param config proxy config * @return proxy_t* proxy */ proxy_t* gcdp__proxy__new(const proxy_config_t* config); /** * @brief Create and start a new proxy from a given config. * * The function will return immediately and the resulting proxy will be passed * to a given callback once available. * * @param config proxy config * @param cb callback * @param context arbitrary context (optional) */ void gcdp__proxy__new_async(const proxy_config_t* config, new_proxy_cb_t* cb, void* context); /** * @brief Gracefully stop a given proxy. * * If the proxy isn't stopped until the timeout, its execution will be aborted. * * The function only initiates the stop. The caller should also use the join * method to make sure that the proxy has stopped. * * @param proxy proxy * @param timeout timeout in milliseconds */ void gcdp__proxy__stop(proxy_t* proxy, uint32_t timeout); /** * @brief Abort the proxy execution. * * The function only initiates the abort. The caller should also use the join * method to make sure that the proxy has stopped. * * @param proxy proxy */ void gcdp__proxy__abort(proxy_t* proxy); /** * @brief Wait until the proxy stops. * * @param proxy proxy * @return int 0 on success, error code on error */ int gcdp__proxy__join(proxy_t* proxy); /** * @brief Wait until the proxy stops. * * The function will return immediately and the join result will be passed to * a given callback once available. * * @param proxy proxy * @param cb callback * @param context arbitrary context (optional) */ void gcdp__proxy__join_async(proxy_t* proxy, join_proxy_cb_t* cb, void* context); /** * @brief Free a given proxy. * * The proxy execution will be aborted if the proxy is still running. * * @param proxy proxy */ void gcdp__proxy__free(proxy_t* proxy); /** * @brief Accept the corresponding GoodCam device. * * The function takes ownership of the result. * * @param result device handler result */ void gcdp__device_handler_result__accept(device_handler_result_t* result); /** * @brief Reject the corresponding GoodCam device. * * The function takes ownership of the result. * * @param result device handler result */ void gcdp__device_handler_result__unauthorized(device_handler_result_t* result); /** * @brief Redirect the corresponding GoodCam device to a given location. * * On success, this function takes ownership of the result. * * @param result device handler result * @param location new location (URL is expected) * @return int 0 on success, error code if the location is invalid */ int gcdp__device_handler_result__redirect(device_handler_result_t* result, const char* location); /** * @brief Report a device handler error. * * The error will be logged and the corresponding GoodCam device will receive * an internal server error (i.e. HTTP 500). This function is useful if the * handler isn't able make its decision (e.g. it cannot connect to a database). * * On success, this function takes ownership of the result. * * @param result device handler result * @param error error message * @return int 0 on success, error code if the error message is invalid */ int gcdp__device_handler_result__error(device_handler_result_t* result, const char* error); /** * @brief Forward a given request to a given GoodCam device. * * On success, this function takes ownership of the request and the result. * * @param result client handler result * @param device_id ID of the target GoodCam device * @param request request * @return int 0 on success, error code if the device ID or request are invalid */ int gcdp__client_handler_result__forward(client_handler_result_t* result, const char* device_id, request_t* request); /** * @brief Block the corresponding client request and send a given response. * * On success, this function takes ownership of the response and the result. * * @param result client handler result * @param response response to be sent back to the corresponding client * @return int 0 on success, error code if the response is invalid */ int gcdp__client_handler_result__block(client_handler_result_t* result, response_t* response); /** * @brief Report a client handler error. * * The error will be logged and the corresponding client will receive an * internal server error (i.e. HTTP 500). This function is useful if the * handler isn't able make its decision (e.g. it cannot connect to a database). * * On success, this function takes ownership of the result. * * @param result client handler result * @param error error message * @return int 0 on success, error code if the error message is invalid */ int gcdp__client_handler_result__error(client_handler_result_t* result, const char* error); /** * @brief Get device ID from a given authorization object. * * The device ID will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the device ID. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param authorization device authorization * @param buffer buffer for the device ID * @param size buffer capacity / length of the device ID */ void gcdp__authorization__get_device_id(const authorization_t* authorization, char* buffer, size_t* size); /** * @brief Get device key from a given authorization object. * * The device key will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the device key. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param authorization device authorization * @param buffer buffer for the device key * @param size buffer capacity / length of the device key */ void gcdp__authorization__get_device_key(const authorization_t* authorization, char* buffer, size_t* size); /** * @brief Get request method. * * String identifier for the corresponding HTTP method will be returned (e.g. * "GET", "POST", etc.). * * @param request request * @return const char* HTTP method */ const char* gcdp__request__get_method(const request_t* request); /** * @brief Get request URI. * * The request URI will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the request URI. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param request request * @param buffer buffer for the request URI * @param size buffer capacity / length of the request URI */ void gcdp__request__get_uri(const request_t* request, char* buffer, size_t* size); /** * @brief Get value of the first header with a given name (case-insensitive). * * The header value will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the header value. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * If there is no such header, an empty string will be returned (i.e. size and * the first byte of the buffer will be both 0). * * @param request request * @param name header name * @param buffer buffer for the header value * @param size buffer capacity / length of the header value * @return int 0 on success, error code in case of an error */ int gcdp__request__get_header_value(const request_t* request, const char* name, char* buffer, size_t* size); /** * @brief Get iterator over the request headers. * * The function returns NULL if there are no headers. * * @param request request * @return header_iter_t* iterator or NULL */ header_iter_t* gcdp__request__get_header_iter(const request_t* request); /** * @brief Free a given request. * * @param request request */ void gcdp__request__free(request_t* request); /** * @brief Get name of the current header. * * The header name will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the header name. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param iter header iterator * @param buffer buffer for the header name * @param size buffer capacity / length of the header name */ void gcdp__header_iter__get_name(const header_iter_t* iter, char* buffer, size_t* size); /** * @brief Get value of the current header. * * The header value will be copied into a given buffer (unless it is NULL). The * size parameter is expected to contain the buffer capacity and after the * function returns, it will contain the original length of the header value. * The size cannot be NULL. * * The string copied into the buffer may be truncated but it will be always * null-terminated. * * @param iter header iterator * @param buffer buffer for the header value * @param size buffer capacity / length of the header value */ void gcdp__header_iter__get_value(const header_iter_t* iter, char* buffer, size_t* size); /** * @brief Advance the header iterator. * * The function will return NULL and free the iterator if there are no more * items. * * @param iter header iterator * @return header_iter_t* new iterator or NULL */ header_iter_t* gcdp__header_iter__next(header_iter_t* iter); /** * @brief Free a given header iterator. * * @param iter iterator */ void gcdp__header_iter__free(header_iter_t* iter); /** * @brief Create a new HTTP response with a given status code. * * @param status status code * @return response_t* HTTP response */ response_t* gcdp__response__new(uint16_t status); /** * @brief Add a given header to the response. * * @param response response * @param name header name * @param value header value * @return int 0 on success, error code in case of an error */ int gcdp__response__add_header(response_t* response, const char* name, const char* value); /** * @brief Replace all header fields with a given name (case-insensitive). * * @param response response * @param name header name * @param value new header value * @return int 0 on success, error code in case of an error */ int gcdp__response__set_header(response_t* response, const char* name, const char* value); /** * @brief Set response body. * * @param response response * @param body buffer containing the body * @param size size of the buffer */ void gcdp__response__set_body(response_t* response, const uint8_t* body, size_t size); /** * @brief Free a given response. * * @param response response */ void gcdp__response__free(response_t* response); #endif