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);
}
Exemple #4
0
/**
 * 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;
}
Exemple #6
0
/**
 * 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;
}