static void fail_conn(int mid, uint32_t connid, void *userdata) { ebb_connection_info *conninfo; if (NULL != (conninfo = httpd_get_conninfo(connid))) { conninfo->http_status = 503; httpd_close(connid); } }
extern void httpd_timeout_conn(uint32_t connid) { ebb_connection_info *conninfo; if (NULL != (conninfo = httpd_get_conninfo(connid))) { conninfo->http_status = 408; // timeout httpd_close(connid); } }
void webapi_publish_cb(int mid) { uint32_t connid; if (0 == midconn_find(mid, &connid)) { httpd_close(connid); } else { LOG_ERROR("webapi_publish_cb mid not found %d", mid); } midconn_remove(mid); }
/** * httpd timeout. * * @param[in] id timeout ID */ static void hio_timeout(ioid_t id) { session_t *session; session = NULL; FOREACH_LLIST(&sessions, session, session_t *) { if (session->toid == id) { break; } } FOREACH_LLIST_END(&sessions, session, session_t *); if (session == NULL) { vtrace("httpd mystery timeout\n"); return; } session->toid = NULL_IOID; httpd_close(session->dhandle, "timeout"); hio_socket_close(session); }
static stream_t *webapi_subscribe_response(ebb_connection_info *conninfo, const char *topic) { stream_t *stream = NULL; if (NULL == (stream = stream_create(topic))) { LOG_ERROR("failed to create stream for topic patt %s", topic); conninfo->http_status = 503; } else { char seckey_str[STREAM_SECKEY_SIZE * 2 + 1]; // hex print int i; if (0 != streamlist_insert(stream->streamid, stream)) { LOG_ERROR("streamlist_insert failed"); conninfo->http_status = 503; } // print hex string for (i=0;i<STREAM_SECKEY_SIZE;i++) sprintf(seckey_str+i*2, "%02X", stream->seckey[i]); if (!conninfo->rawmode) { httpd_printf(conninfo->connid, "{\"url\":\"/stream/%u\",\"id\":%u,\"seckey\":\"%s\"}", stream->streamid, stream->streamid, seckey_str); } else { // attach conn to stream stream_set_connection(stream, conninfo->connid); idset_insert(conninfo->streamids, stream->streamid); stream_refresh_timer(stream); // stop from timing out } } if (!conninfo->rawmode) httpd_close(conninfo->connid); return stream; }
/** * New inbound data for an httpd connection. * * @param[in] fd socket file descriptor * @param[in] id I/O ID */ void hio_socket_input(iosrc_t fd, ioid_t id) { session_t *session; char buf[1024]; ssize_t nr; session = NULL; FOREACH_LLIST(&sessions, session, session_t *) { if (session->ioid == id) { break; } } FOREACH_LLIST_END(&sessions, session, session_t *); if (session == NULL) { vtrace("httpd mystery input\n"); return; } /* Move this session to the front of the list. */ llist_unlink(&session->link); llist_insert_before(&session->link, sessions.next); session->idle = 0; if (session->toid != NULL_IOID) { RemoveTimeOut(session->toid); session->toid = NULL_IOID; } nr = recv(session->s, buf, sizeof(buf), 0); if (nr <= 0) { const char *ebuf; bool harmless = false; if (nr < 0) { if (socket_errno() == SE_EWOULDBLOCK) { harmless = true; } ebuf = lazyaf("recv error: %s", socket_errtext()); vtrace("httpd %s%s\n", ebuf, harmless? " (harmless)": ""); } else { ebuf = "session EOF"; } if (!harmless) { httpd_close(session->dhandle, ebuf); hio_socket_close(session); } } else { httpd_status_t rv; rv = httpd_input(session->dhandle, buf, nr); if (rv < 0) { httpd_close(session->dhandle, "protocol error"); hio_socket_close(session); } else if (rv == HS_PENDING) { /* Stop input on this socket. */ RemoveInput(session->ioid); session->ioid = NULL_IOID; } else if (session->toid == NULL_IOID) { /* Leave input enabled and start the timeout. */ session->toid = AddTimeOut(IDLE_MAX * 1000, hio_timeout); } } }
static int webapi_stream(uint32_t connid, uint32_t method, int argc, char **argv, const char *body, const char *reqpath, const char *uri_str, const char *auth) { ebb_connection_info *conninfo; if (NULL == (conninfo = httpd_get_conninfo(connid))) { LOG_ERROR("bad connid"); return 1; } if (method == EBB_POST && argc == 0) { uint32_t streamids[MAX_STREAMS]; stream_t *streams[MAX_STREAMS]; uint8_t seckeys[MAX_STREAMS][STREAM_SECKEY_SIZE]; size_t num_streams; size_t i; bool close_conn = false; if (0 != json_parse_array_uint32_seckey(body, streamids, seckeys, &num_streams, MAX_STREAMS) || num_streams == 0) { conninfo->http_status = 400; // malformed return 1; } for (i=0;i<num_streams;i++) // reject request if any of the streams don't exist or wrong seckey { //LOG_INFO("webapi_stream %u for connid=%u", streamids[i], connid); if (NULL == (streams[i] = streamlist_find(streamids[i]))) { LOG_DEBUG("no such stream %u", streamids[i]); conninfo->http_status = 404; // no such return 1; } else { if (0!=memcmp(seckeys[i], streams[i]->seckey, STREAM_SECKEY_SIZE)) { LOG_INFO("invalid seckey for streamid %u", streamids[i]); conninfo->http_status = 403; // verboten return 1; } LOG_DEBUG("adding stream %u", streamids[i]); } } for (i=0;i<num_streams;i++) { if (!stream_isempty(streams[i])) { if (close_conn) httpd_printf(connid, "\r\n"); // FIXME, should this be a single JSON doc? httpd_printf(connid, "["); stream_drain(streams[i], stream_drainer, (void *)(uintptr_t)connid); httpd_printf(connid, "]"); close_conn = true; // defer close till all available streams have been drained } else { uint32_t old_connid; if (stream_get_connection(streams[i], &old_connid)) { ebb_connection_info *old_conninfo; LOG_DEBUG("closing old_connid %u replacing with %u for stream %u", old_connid, connid, streamids[i]); if (NULL != (old_conninfo = httpd_get_conninfo(old_connid))) idset_foreach(old_conninfo->streamids, strm_clr, NULL); httpd_close(old_connid); } stream_set_connection(streams[i], connid); // associate the stream to this connection if (0 != idset_insert(conninfo->streamids, streamids[i])) { conninfo->http_status = 503; return 1; } stream_refresh_timer(streams[i]); // stop from timing out } } if (close_conn) { LOG_DEBUG("closing"); httpd_close(connid); } return 0; } else { conninfo->http_status = 405; // method not allowed return 1; } return 0; }
static int webapi_publish(uint32_t connid, uint32_t method, int argc, char **argv, const char *body, const char *reqpath, const char *uri_str, const char *auth) { ebb_connection_info *conninfo; int mid; int qos = 2; char *topic = NULL; char *msg = NULL; if (NULL == (conninfo = httpd_get_conninfo(connid))) { LOG_ERROR("bad connid"); return 1; } if (method != EBB_PUT && method != EBB_POST) { conninfo->http_status = 405; // method not allowed return 1; } httpd_query_iterate(uri_str, parse_query_qos, (void *)&qos); if (qos < 0 || qos > 2) { conninfo->http_status = 400; return 1; } if (0 != json_fetch_keyval(body, &topic, &msg)) { LOG_INFO("malformed json %s", body); conninfo->http_status = 400; return 1; } accesslog_write("%s publish %s", conninfo->ipaddr, topic); if (mqtt_publish(&g_srvctx.mqttctx, topic, msg, qos, &mid) != 0) { conninfo->http_status = 503; free(topic); free(msg); return 1; } else { #ifdef LOCAL_ECHO // feed self webapi_message_cb(mid, topic, msg); #endif free(topic); free(msg); if (qos == 0) { httpd_close(connid); return 0; } else { LOG_DEBUG("midconn_insert mid=%d conn=%d", mid, connid); midconn_insert(mid, connid); conninfo->have_mid = true; conninfo->mid = mid; return 0; } } return 1; }
bool message_to_stream(uint32_t streamid, stream_t *stream, void *userdata) { struct topic_message *tm = (struct topic_message *)userdata; char *s = NULL; LOG_DEBUG("rx check: %s %s", stream->topic_patt, tm->topic); if (topic_match_string(stream->topic_patt, tm->topic)) { size_t len; LOG_DEBUG("%s:%s -> stream %u", tm->topic, tm->msg, streamid); len = strlen(tm->topic)*2 + strlen(tm->msg)*2 + 8; // {"":""} + null // FIXME if (NULL == (s = (char *)malloc(len))) LOG_ERROR("out of mem"); else { uint32_t connid; ebb_connection_info *conninfo = NULL; bool connected; connected = stream_get_connection(stream, &connid); if (connected) { if (NULL == (conninfo = httpd_get_conninfo(connid))) { LOG_ERROR("bad connid"); return false; } if (conninfo->finished) // if conn is in process of closing, connected = false; } if (conninfo && conninfo->rawmode) { // just the data memcpy(s, tm->msg, strlen(tm->msg)+1); } else { // wrapped in JSON obj cJSON *mj = NULL; char *msgesc; char *topicesc; if (NULL == (topicesc = json_escape(tm->topic))) { conninfo->http_status = 503; httpd_close(connid); return false; } if (tm->msg[0] == 0) { snprintf(s, len, "{%s:\"\"}", topicesc); } else if (0==strcmp(tm->msg, "inf")) { snprintf(s, len, "{%s:\"inf\"}", topicesc); } else if (NULL != (mj = cJSON_Parse(tm->msg))) { snprintf(s, len, "{%s:%s}", topicesc, cJSON_PrintUnformatted(mj)); cJSON_Delete(mj); } else { if (NULL == (msgesc = json_escape(tm->msg))) { conninfo->http_status = 503; httpd_close(connid); free(topicesc); return false; } else { snprintf(s, len, "{%s:%s}", topicesc, msgesc); free(msgesc); } } free(topicesc); } if (0 != stream_push(stream, s)) LOG_ERROR("stream_push %u failed", streamid); else { if (connected) { if (!conninfo->rawmode) httpd_printf(connid, "["); stream_drain(stream, stream_drainer, (void *)(uintptr_t)connid); if (!conninfo->rawmode) httpd_printf(connid, "]"); stream_clear_connection(stream); httpd_close(connid); } else { LOG_DEBUG("streamid %u not connected", stream->streamid); } } } } if (NULL != s) free(s); return false; }