/** * @brief Upgrade a client's connection to SSL/TLS * * This function begins the SSL handshake process on connected client @p client. * An AZY_CLIENT_UPGRADE event will be emitted on success, and EINA_FALSE will be * returned immediately on failure. * @param client The client object (NOT NULL) * @return #EINA_TRUE if successful, or #EINA_FALSE on failure */ Eina_Bool azy_client_upgrade(Azy_Client *client) { DBG("(client=%p)", client); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return EINA_FALSE; } if (!client->connected) return EINA_FALSE; return ecore_con_ssl_server_upgrade(client->net->conn, ECORE_CON_USE_MIXED); }
/** * @brief Returns the currently active call id * * This function returns the currently active #Azy_Client_Call_Id, or 0 if no call * is currently active/pending. * @param cli The client object (NOT NULL) * @return The currently active/pending call id, or 0 on failure */ Azy_Client_Call_Id azy_client_current(Azy_Client *cli) { Azy_Client_Handler_Data *hd; if (!AZY_MAGIC_CHECK(cli, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(cli, AZY_MAGIC_CLIENT); return 0; } if (!cli->conns) return 0; hd = cli->conns->data; return hd->id; }
/** * @brief Set the port that the client connects to * * This function sets the port number on the server that @p client * connects to. * @param client The client object (NOT NULL) * @param port The port number (-1 < port < 65536) * @return #EINA_TRUE on success, or #EINA_FALSE on failure */ Eina_Bool azy_client_port_set(Azy_Client *client, int port) { DBG("(client=%p)", client); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return EINA_FALSE; } if ((port < 0) || (port > 65535)) return EINA_FALSE; client->port = port; return EINA_TRUE; }
/** * @brief Upgrade a client's connection to SSL/TLS * * This function begins the SSL handshake process on connected client represented by @p module. * An AZY_EVENT_SERVER_CLIENT_UPGRADE event will be emitted on success, and EINA_FALSE will be * returned immediately on failure. * @param module The client object (NOT NULL) * @return #EINA_TRUE if successful, or #EINA_FALSE on failure */ Eina_Bool azy_server_module_upgrade(Azy_Server_Module *module) { DBG("(module=%p)", module); if (!AZY_MAGIC_CHECK(module, AZY_MAGIC_SERVER_MODULE)) { AZY_MAGIC_FAIL(module, AZY_MAGIC_SERVER_MODULE); return EINA_FALSE; } if (module->client->dead) return EINA_FALSE; if (!ecore_con_ssl_client_upgrade(module->client->net->conn, ECORE_CON_USE_MIXED)) return EINA_FALSE; module->client->upgrading_module = module; return EINA_TRUE; }
/** * @brief Free the given #Azy_Server_Module_Def * * This function frees the given #Azy_Server_Module_Def, and should only * be called after the module will no longer be used. * @param def The #Azy_Server_Module_Def to free */ void azy_server_module_def_free(Azy_Server_Module_Def *def) { if (!def) return; if (!AZY_MAGIC_CHECK(def, AZY_MAGIC_SERVER_MODULE_DEF)) { AZY_MAGIC_FAIL(def, AZY_MAGIC_SERVER_MODULE_DEF); return; } eina_stringshare_del(def->name); eina_hash_free(def->methods); if (def->module) eina_module_free(def->module); AZY_MAGIC_SET(def, AZY_MAGIC_NONE); free(def); }
/** * @brief Get a param from a module * * This function gets a previously set method call param * named @p name from module @p module. It is used by the parser. * @param module The module (NOT NULL) * @param name The param name (NOT NULL) * @return The param value, NULL on failure */ void * azy_server_module_param_get(Azy_Server_Module *module, const char *name) { Azy_Server_Module_Param *param; if (!AZY_MAGIC_CHECK(module, AZY_MAGIC_SERVER_MODULE)) { AZY_MAGIC_FAIL(module, AZY_MAGIC_SERVER_MODULE); return NULL; } EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(module->params, NULL); EINA_SAFETY_ON_TRUE_RETURN_VAL(!name[0], NULL); param = eina_hash_find(module->params, name); EINA_SAFETY_ON_NULL_RETURN_VAL(param, NULL); return (void *)param->data; }
/** * @brief Set the address of the server that the client connects to * * This function sets the address string of the server that @p client * connects to. * @param client The client object (NOT NULL) * @param addr The address string (NOT NULL) * @return The address string */ Eina_Bool azy_client_addr_set(Azy_Client *client, const char *addr) { DBG("(client=%p)", client); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return EINA_FALSE; } EINA_SAFETY_ON_NULL_RETURN_VAL(addr, EINA_FALSE); if (!strncmp(addr, "http://", 7)) addr += 7; else if (!strncmp(addr, "https://", 8)) addr += 8; client->addr = eina_stringshare_add(addr); return EINA_TRUE; }
/** * @brief Close a client's connection * * This function is the opposite of azy_client_connect, it * terminates an existing connection. * @param client The client (NOT NULL) */ void azy_client_close(Azy_Client *client) { DBG("(client=%p)", client); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return; } EINA_SAFETY_ON_FALSE_RETURN(client->connected); EINA_SAFETY_ON_NULL_RETURN(client->net); ecore_con_server_del(client->net->conn); azy_net_free(client->net); client->net = NULL; client->connected = EINA_FALSE; }
/** * @brief Set a callback to free the return struct of @p id * * This function, when set, frees the returned user-type struct of a call. * @param client The client * @param id The transmission id * @param callback The free callback */ Eina_Bool azy_client_callback_free_set(Azy_Client *client, Azy_Client_Call_Id id, Ecore_Cb callback) { DBG("(client=%p, id=%u)", client, id); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return EINA_FALSE; } EINA_SAFETY_ON_NULL_RETURN_VAL(callback, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(id < 1, EINA_FALSE); if (!client->free_callbacks) client->free_callbacks = eina_hash_int32_new(NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(client->free_callbacks, EINA_FALSE); return eina_hash_add(client->free_callbacks, &id, callback); }
/** * @brief Set a param to a module * * This function sets a method call param named @p name to a module. It is used by the parser. * @param module The module (NOT NULL) * @param name The param name (NOT NULL) * @param value The param value * @param free_func The function to free @p value * @return EINA_TRUE on success, else EINA_FALSE */ Eina_Bool azy_server_module_param_set(Azy_Server_Module *module, const char *name, const void *value, Eina_Free_Cb free_func) { Azy_Server_Module_Param *param, *old; if (!AZY_MAGIC_CHECK(module, AZY_MAGIC_SERVER_MODULE)) { AZY_MAGIC_FAIL(module, AZY_MAGIC_SERVER_MODULE); return EINA_FALSE; } EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!name[0], EINA_FALSE); if (!module->params) module->params = eina_hash_string_djb2_new((Eina_Free_Cb)azy_server_module_param_free_); param = calloc(1, sizeof(Azy_Server_Module_Param)); EINA_SAFETY_ON_NULL_RETURN_VAL(param, EINA_FALSE); param->data = value; param->free_func = free_func; old = eina_hash_set(module->params, name, param); if (old) azy_server_module_param_free_(old); return EINA_TRUE; }
/** * @brief Helper function to automatically handle redirection * * This function is used inside an AZY_CLIENT_DISCONNECTED callback to automatically * reconnect to the server if necessary (HTTP 301/302/303 returned). * @param cli The client object (NOT NULL) * @return #EINA_TRUE only if reconnection has succeeded, else #EINA_FALSE */ Eina_Bool azy_client_redirect(Azy_Client *cli) { int code; Azy_Net *net; if (!AZY_MAGIC_CHECK(cli, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(cli, AZY_MAGIC_CLIENT); return EINA_FALSE; } net = azy_client_net_get(cli); if (!net) return EINA_FALSE; code = azy_net_code_get(net); if ((code >= 301) && (code <= 303)) { azy_client_connect(cli, cli->secure); return EINA_TRUE; } return EINA_FALSE; }
/** * @brief Add a module to the server object * * This function adds @p module to @p server. After calling this, * the module should not be freed until the server has stopped running. * @param server The server object (NOT NULL) * @param module The module definition (NOT NULL) * @return EINA_TRUE on success, else EINA_FALSE */ Eina_Bool azy_server_module_add(Azy_Server *server, Azy_Server_Module_Def *module) { if (!AZY_MAGIC_CHECK(server, AZY_MAGIC_SERVER)) { AZY_MAGIC_FAIL(server, AZY_MAGIC_SERVER); return EINA_FALSE; } if (!module) return EINA_FALSE; if (server->module_defs) { if (azy_server_module_def_find(server, module->name)) /* avoid adding same module twice */ return EINA_TRUE; } else server->module_defs = eina_hash_string_superfast_new(NULL); if (!server->module_defs) return EINA_FALSE; INFO("Adding new module: '%s'", module->name); return eina_hash_add(server->module_defs, module->name, module); }
/** * @brief Make an HTTP GET or POST request using a connected client with no HTTP BODY * * This function is used to make a GET or POST request using @p client to the uri of the client's * #Azy_Net object (azy_net_get(client)) using HTTP method @p type, content-type * defined by @p transport, and the optional deserialization function specified by @p cb. * @param client The client (NOT NULL) * @param type The HTTP method to use (NOT NULL) * @param netdata The HTTP BODY to send with a POST * @param cb The deserialization callback to use for the response * @param data The user data to be passed to resulting callbacks * @return The #Azy_Client_Call_Id of the transmission, to be used with azy_client_callback_set, * or 0 on failure */ Azy_Client_Call_Id azy_client_blank(Azy_Client *client, Azy_Net_Type type, Azy_Net_Data *netdata, Azy_Content_Cb cb, void *data) { Eina_Strbuf *msg; Azy_Client_Handler_Data *hd; DBG("(client=%p, net=%p)", client, client->net); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return 0; } EINA_SAFETY_ON_NULL_RETURN_VAL(client->net, 0); EINA_SAFETY_ON_TRUE_RETURN_VAL((type != AZY_NET_TYPE_GET) && (type != AZY_NET_TYPE_POST), 0); while (++azy_client_send_id__ < 1) ; client->net->type = type; if (!client->net->http.req.http_path) { WARN("NULL URI passed, defaulting to \"/\""); azy_net_uri_set(client->net, "/"); } if (netdata && netdata->size && (type == AZY_NET_TYPE_POST)) azy_net_message_length_set(client->net, netdata->size); msg = azy_net_header_create(client->net); EINA_SAFETY_ON_NULL_GOTO(msg, error); #ifdef ISCOMFITOR char buf[64]; snprintf(buf, sizeof(buf), "\nSENDING >>>>>>>>>>>>>>>>>>>>>>>>\n%%.%zus\n>>>>>>>>>>>>>>>>>>>>>>>>", eina_strbuf_length_get(msg)); DBG(buf, eina_strbuf_string_get(msg)); #endif EINA_SAFETY_ON_TRUE_GOTO(!ecore_con_server_send(client->net->conn, eina_strbuf_string_get(msg), eina_strbuf_length_get(msg)), error); if (netdata && netdata->size && (type == AZY_NET_TYPE_POST)) { INFO("Send [1/2] complete! %zu bytes queued for sending.", eina_strbuf_length_get(msg)); EINA_SAFETY_ON_TRUE_GOTO(!ecore_con_server_send(client->net->conn, netdata->data, netdata->size), error); INFO("Send [2/2] complete! %" PRIi64 " bytes queued for sending.", netdata->size); } else INFO("Send [1/1] complete! %zu bytes queued for sending.", eina_strbuf_length_get(msg)); eina_strbuf_free(msg); msg = NULL; ecore_con_server_flush(client->net->conn); hd = calloc(1, sizeof(Azy_Client_Handler_Data)); EINA_SAFETY_ON_NULL_RETURN_VAL(hd, 0); hd->client = client; hd->callback = cb; hd->type = type; hd->content_data = data; if (netdata && netdata->size && (type == AZY_NET_TYPE_POST)) { hd->send = eina_strbuf_new(); eina_strbuf_append_length(hd->send, (char *)netdata->data, netdata->size); } hd->id = azy_client_send_id__; AZY_MAGIC_SET(hd, AZY_MAGIC_CLIENT_DATA_HANDLER); if (!client->conns) { client->recv = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb)_azy_client_handler_data, hd); ecore_con_server_data_set(client->net->conn, client); } client->conns = eina_list_append(client->conns, hd); DBG("(client=%p, net=%p, hd=%p)", client, client->net, hd); return azy_client_send_id__; error: if (msg) eina_strbuf_free(msg); return 0; }
/** * @brief Make a method call using a connected client * * This function is used to make a method call on @p client as defined in * @p content, using content-type defined by @p transport and the deserialization * function specified by @p cb. This should generally not be used by users, as azy_parser * will automatically generate the correct calls from a .azy file. * @param client The client (NOT NULL) * @param content The content containing the method name and parameters (NOT NULL) * @param transport The content-type (xml/json/etc) to use * @param cb The deserialization callback to use for the response * @return The #Azy_Client_Call_Id of the transmission, to be used with azy_client_callback_set, * or 0 on failure */ Azy_Client_Call_Id azy_client_call(Azy_Client *client, Azy_Content *content, Azy_Net_Transport transport, Azy_Content_Cb cb) { Eina_Strbuf *msg; Azy_Client_Handler_Data *hd; DBG("(client=%p, net=%p, content=%p)", client, client->net, content); if (!AZY_MAGIC_CHECK(client, AZY_MAGIC_CLIENT)) { AZY_MAGIC_FAIL(client, AZY_MAGIC_CLIENT); return 0; } EINA_SAFETY_ON_NULL_RETURN_VAL(client->net, 0); EINA_SAFETY_ON_NULL_RETURN_VAL(content, 0); EINA_SAFETY_ON_NULL_RETURN_VAL(content->method, 0); INFO("New method call: '%s'", content->method); while (++azy_client_send_id__ < 1) ; content->id = azy_client_send_id__; azy_net_transport_set(client->net, transport); if (!azy_content_serialize_request(content, transport)) return 0; azy_net_type_set(client->net, AZY_NET_TYPE_POST); if (!client->net->http.req.http_path) { WARN("URI currently set to NULL, defaulting to \"/\""); azy_net_uri_set(client->net, "/"); } azy_net_message_length_set(client->net, content->length); msg = azy_net_header_create(client->net); EINA_SAFETY_ON_NULL_GOTO(msg, error); if (azy_rpc_log_dom >= 0) { char buf[64]; snprintf(buf, sizeof(buf), "\nSENDING >>>>>>>>>>>>>>>>>>>>>>>>\n%%.%is%%.%llis\n>>>>>>>>>>>>>>>>>>>>>>>>", eina_strbuf_length_get(msg), content->length); RPC_DBG(buf, eina_strbuf_string_get(msg), content->buffer); } EINA_SAFETY_ON_TRUE_GOTO(!ecore_con_server_send(client->net->conn, eina_strbuf_string_get(msg), eina_strbuf_length_get(msg)), error); INFO("Send [1/2] complete! %zu bytes queued for sending.", eina_strbuf_length_get(msg)); eina_strbuf_free(msg); msg = NULL; EINA_SAFETY_ON_TRUE_GOTO(!ecore_con_server_send(client->net->conn, content->buffer, content->length), error); INFO("Send [2/2] complete! %lli bytes queued for sending.", content->length); ecore_con_server_flush(client->net->conn); hd = calloc(1, sizeof(Azy_Client_Handler_Data)); EINA_SAFETY_ON_NULL_RETURN_VAL(hd, 0); hd->client = client; hd->method = eina_stringshare_ref(content->method); hd->callback = cb; hd->type = AZY_NET_TYPE_POST; hd->content_data = content->data; hd->send = eina_strbuf_new(); eina_strbuf_append_length(hd->send, (char *)content->buffer, content->length); hd->id = azy_client_send_id__; AZY_MAGIC_SET(hd, AZY_MAGIC_CLIENT_DATA_HANDLER); if (!client->conns) { client->recv = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb)_azy_client_handler_data, hd); ecore_con_server_data_set(client->net->conn, client); } client->conns = eina_list_append(client->conns, hd); DBG("(client=%p, net=%p, content=%p, hd=%p)", client, client->net, content, hd); return azy_client_send_id__; error: if (msg) eina_strbuf_free(msg); return 0; }
Eina_Bool azy_events_header_parse(Azy_Net *net, unsigned char *event_data, size_t event_len, int offset) { unsigned char *r = NULL, *p = NULL, *start = NULL, *buf_start = NULL; unsigned char *data = (event_data) ? event_data + offset : NULL; int64_t len = (event_len) ? event_len - offset : 0; const char *s = NULL; unsigned char slen = 0; unsigned char sep[5]; int line_len = 0; int64_t prev_size = 0; DBG("(net=%p, event_data=%p, len=%zu, offset=%i)", net, event_data, event_len, offset); if (!AZY_MAGIC_CHECK(net, AZY_MAGIC_NET)) { AZY_MAGIC_FAIL(net, AZY_MAGIC_NET); return EINA_FALSE; } if (net->headers_read) return EINA_TRUE; EINA_SAFETY_ON_TRUE_RETURN_VAL((!net->buffer) && (!data), EINA_FALSE); if (net->size && net->buffer) { if (event_data && (azy_rpc_log_dom >= 0)) { char buf[64]; snprintf(buf, sizeof(buf), "STORED:\n<<<<<<<<<<<<<\n%%.%llis\n<<<<<<<<<<<<<", net->size); RPC_INFO(buf, net->buffer); snprintf(buf, sizeof(buf), "RECEIVED:\n<<<<<<<<<<<<<\n%%.%llis\n<<<<<<<<<<<<<", len - offset); RPC_INFO(buf, data); } /* previous buffer */ /* alloca should be safe here because ecore_con reads at most 64k * and even if no headers were found previously, the entire * buffer would not be copied */ buf_start = alloca(len + net->size - offset); /* grab and combine buffers */ if (event_data) { memcpy(buf_start, net->buffer + offset, net->size - offset); memcpy(buf_start + net->size, event_data, len); } else memcpy(buf_start, net->buffer + offset, net->size - offset); free(net->buffer); net->buffer = NULL; len += net->size - offset; prev_size = net->size; net->size = 0; start = buf_start; AZY_SKIP_BLANK(start); } else /* only current buffer plus possible net->overflow */ { /* copy pointer */ start = data; /* skip all spaces/newlines/etc and decrement len */ AZY_SKIP_BLANK(start); } if ((!len) && (event_len - offset > 0)) /* only blanks were passed, assume http separator */ { net->headers_read = EINA_TRUE; return EINA_TRUE; } /* apparently this can happen? */ EINA_SAFETY_ON_NULL_RETURN_VAL(start, EINA_FALSE); /* find a header or append to buffer */ if ((!(r = memchr(start, '\r', len)) && !(r = memchr(start, '\n', len)))) /* append to a buffer and use net->overflow */ { unsigned char *tmp; if (net->size) { tmp = realloc(net->buffer, net->size + len); EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, EINA_FALSE); net->buffer = tmp; memcpy(net->buffer + net->size, start, len); net->size += len; } else { tmp = realloc(net->buffer, len); EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, EINA_FALSE); net->buffer = tmp; memcpy(net->buffer, start, len); net->size = len; } return EINA_TRUE; } if (*r == '\r') { unsigned char *x; if ((x = memchr(start, '\n', len))) { if ((x - r) > 0) s = "\r\n"; else { /* we currently have \n\r: b64 encoding can leave a trailing \n * so we have to check for an extra \n */ if ((x - r < 0) && ((unsigned int)(r + 1 - start) < len) && (r[1] == '\n')) /* \n\r\n */ { if (((unsigned int)(r + 2 - start) < len) && (r[2] == '\r')) /* \n\r\n\r */ { if (((unsigned int)(r + 3 - start) < len) && (r[3] == '\n')) /* \n\r\n\r\n oh hey I'm gonna stop here before it gets too insane */ s = "\r\n"; else s = "\n\r"; } else s = "\r\n"; } else s = "\n\r"; } } else s = "\r"; } else s = "\n"; slen = strlen(s); snprintf((char *)sep, sizeof(sep), "%s%s", s, s); p = start; line_len = r - p; while (len && r) { unsigned char *ptr, *semi = p; if (line_len > MAX_HEADER_SIZE) { WARN("Ignoring unreasonably large header starting with:\n %.32s\n", p); goto skip_header; } semi += (line_len - _azy_events_valid_header_name((const char *)p, line_len)); if (semi == p) goto skip_header; ptr = semi + 1; while ((isspace(*ptr)) && (ptr - p < line_len)) ptr++; if (_azy_events_valid_header_value((const char *)ptr, line_len - (ptr - p))) { const char *key, *value; p[semi - p] = 0; ptr[line_len - (ptr - p)] = 0; key = (const char *)p; value = (const char *)ptr; INFO("Found header: key='%s'", key); INFO("Found header: value='%s'", value); azy_net_header_set(net, key, value); if (!strcasecmp(key, "content-length")) net->http.content_length = strtol((const char *)value, NULL, 10); } skip_header: len -= line_len + slen; if (len < slen) break; p = r + slen; /* double separator: STOP */ if (!strncmp((char*)p, s, slen)) { net->headers_read = EINA_TRUE; break; } r = azy_memstr(p, (const unsigned char *)s, len, slen); line_len = r - p; /* FIXME: to be fully 1.1 compliant, lines without a colon * be filtered and checked to see if is a continuing header * from the previous line */ } AZY_SKIP_BLANK(p); if (!net->headers_read) return EINA_TRUE; if (!net->http.content_length) net->http.content_length = -1; if (len) { int64_t rlen; /* if we get here, we need to append to the buffers */ if (net->http.content_length > 0) { if (len > net->http.content_length) { rlen = net->http.content_length; net->overflow_length = len - rlen; WARN("Extra content length of %lli!", net->overflow_length); net->overflow = malloc(net->overflow_length); /* FIXME: uhhhh f**k? */ EINA_SAFETY_ON_NULL_RETURN_VAL(net->overflow, EINA_FALSE); memcpy(net->overflow, p + rlen, net->overflow_length); #ifdef ISCOMFITOR if (azy_rpc_log_dom >= 0) { int64_t x; RPC_INFO("OVERFLOW:\n<<<<<<<<<<<<<"); for (x = 0; x < net->overflow_length; x++) putc(net->overflow[x], stdout); fflush(stdout); } #endif } else rlen = len; } else /* this shouldn't be possible unless someone is violating spec */ rlen = len; INFO("Set recv size to %lli (previous %lli)", rlen, prev_size); net->size = rlen; net->buffer = malloc(rlen); /* FIXME: cleanup */ EINA_SAFETY_ON_NULL_RETURN_VAL(net->buffer, EINA_FALSE); memcpy(net->buffer, p, rlen); } return EINA_TRUE; }
/** * @brief Send data to a client * * This function is used to queue arbitrary data to send to a client through its module. It will automatically * generate all http header strings from @p net (if provided) including the content-length (based on @p data). * @param module The client's #Azy_Server_Module object (NOT NULL) * @param net An #Azy_Net object containing http information to use * @param data The data to send * @return EINA_TRUE on success, else EINA_FALSE */ Eina_Bool azy_server_module_send(Azy_Server_Module *module, Azy_Net *net, const Azy_Net_Data *data) { Eina_Strbuf *header; char chunk_size[20]; Eina_Binbuf *chunk_data; Eina_Bool nullify = EINA_FALSE; if (!AZY_MAGIC_CHECK(module, AZY_MAGIC_SERVER_MODULE)) { AZY_MAGIC_FAIL(module, AZY_MAGIC_SERVER_MODULE); return EINA_FALSE; } if (net) { if (!module->client->current) { module->client->current = net; nullify = EINA_TRUE; } if (net->headers_sent) goto post_header; Eina_Bool s; if ((data) && (net->http.transfer_encoding != AZY_NET_TRANSFER_ENCODING_CHUNKED)) azy_net_content_length_set(net, data->size); if (!net->http.res.http_code) azy_net_code_set(net, 200); /* OK */ azy_net_type_set(net, AZY_NET_TYPE_RESPONSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!(header = azy_net_header_create(net)), EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(module->client->current->conn, EINA_FALSE); s = !!ecore_con_client_send(module->client->current->conn, eina_strbuf_string_get(header), eina_strbuf_length_get(header)); eina_strbuf_free(header); if (!s) { ERR("Could not queue header for sending!"); return EINA_FALSE; } net->headers_sent = EINA_TRUE; } post_header: if ((!net) || (net->http.transfer_encoding != AZY_NET_TRANSFER_ENCODING_CHUNKED)) { if (!data || !data->data) return EINA_TRUE; EINA_SAFETY_ON_NULL_RETURN_VAL(module->client->current->conn, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!ecore_con_client_send(module->client->current->conn, data->data, data->size), EINA_FALSE); goto post_send; } if (!data || !data->data) { EINA_SAFETY_ON_NULL_RETURN_VAL(module->client->current->conn, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!ecore_con_client_send(module->client->current->conn, "0\r\n\r\n", 5), EINA_FALSE); goto post_send; } net->refcount++; sprintf((char *)chunk_size, "%" PRIx64 "\r\n", data->size); chunk_data = eina_binbuf_new(); eina_binbuf_append_length(chunk_data, (unsigned char *)chunk_size, strlen(chunk_size)); eina_binbuf_append_length(chunk_data, data->data, data->size); eina_binbuf_append_length(chunk_data, (unsigned char *)"\r\n", 2); EINA_SAFETY_ON_NULL_RETURN_VAL(module->client->current->conn, EINA_FALSE); EINA_SAFETY_ON_TRUE_RETURN_VAL(!ecore_con_client_send(module->client->current->conn, eina_binbuf_string_get(chunk_data), eina_binbuf_length_get(chunk_data)), EINA_FALSE); eina_binbuf_free(chunk_data); post_send: if (nullify) module->client->current = NULL; return EINA_TRUE; }