/**
 * Invoked by the networking layer when there is new
 * data to be handled. The connection handler should
 * consume all the input possible, and generate responses
 * to all requests.
 * @arg handle The connection related information
 * @return 0 on success.
 */
int handle_client_connect(statsite_proxy_conn_handler *handle) {
    // Try to read the magic character, bail if no data
    unsigned char magic = 0;
    if (peek_client_bytes(handle->conn, 1, &magic) == -1) return 0;

    // Check the magic byte
    if (magic == BINARY_MAGIC_BYTE)
        return handle_binary_client_connect(handle);
    else
        return handle_ascii_client_connect(handle);
}
Esempio n. 2
0
/**
 * Invoked to handle binary commands.
 * @arg handle The connection related information
 * @return 0 on success.
 */
static int handle_binary_client_connect(statsite_conn_handler *handle) {
    metric_type type;
    uint16_t key_len;
    int should_free;
    unsigned char *cmd, *key;
    while (1) {
        // Peek and check for the header. This is up to 12 bytes.
        // Magic byte - 1 byte
        // Metric type - 1 byte
        // Key length - 2 bytes
        // Metric value - 8 bytes OR Set Length 2 bytes
        if (peek_client_bytes(handle->conn, MIN_BINARY_HEADER_SIZE, (char**)&cmd, &should_free))
            return 0;  // Return if no command is available

        // Check for the magic byte
        if (unlikely(cmd[0] != BINARY_MAGIC_BYTE)) {
            syslog(LOG_WARNING, "Received command from binary stream without magic byte! Byte: %u", cmd[0]);
            goto ERR_RET;
        }

        // Get the metric type
        switch (cmd[1]) {
            case BIN_TYPE_KV:
                type = KEY_VAL;
                break;
            case BIN_TYPE_COUNTER:
                type = COUNTER;
                break;
            case BIN_TYPE_TIMER:
                type = TIMER;
                break;
            case BIN_TYPE_GAUGE:
                type = GAUGE;
                break;
            case BIN_TYPE_GAUGE_DELTA:
                type = GAUGE_DELTA;
                break;

            // Special case set handling
            case BIN_TYPE_SET:
                switch (handle_binary_set(handle, (uint16_t*)cmd, should_free)) {
                    case -1:
                        return -1;
                    case -2:
                        return 0;
                    default:
                        continue;
                }

            default:
                syslog(LOG_WARNING, "Received command from binary stream with unknown type: %u!", cmd[1]);
                goto ERR_RET;
        }

        // Abort if we haven't received the full key, wait for the data
        key_len = *(uint16_t*)(cmd+2);

        // Read the full command if available
        if (unlikely(should_free)) free(cmd);
        if (read_client_bytes(handle->conn, MAX_BINARY_HEADER_SIZE + key_len, (char**)&cmd, &should_free))
            return 0;
        key = cmd + MAX_BINARY_HEADER_SIZE;

        // Verify the key contains a null terminator
        if (unlikely(*(key + key_len - 1))) {
            syslog(LOG_WARNING, "Received command from binary stream with non-null terminated key: %.*s!", key_len, key);
            goto ERR_RET;
        }

        // Increment the input counter
        if (GLOBAL_CONFIG->input_counter)
            metrics_add_sample(GLOBAL_METRICS, COUNTER, GLOBAL_CONFIG->input_counter, 1);

        // Add the sample
        metrics_add_sample(GLOBAL_METRICS, type, key, *(double*)(cmd+4));

        // Make sure to free the command buffer if we need to
        if (unlikely(should_free)) free(cmd);
    }
    return 0;
ERR_RET:
    if (unlikely(should_free)) free(cmd);
    return -1;
}
/**
 * Invoked to handle binary commands.
 * @arg handle The connection related information
 * @return 0 on success.
 */
static int handle_binary_client_connect(statsite_proxy_conn_handler *handle) {
    metric_type type;
    int status, should_free;
    char *key;
    unsigned char header[BINARY_HEADER_SIZE];
    uint8_t type_input;
    uint16_t key_len;
    while (1) {
        // Peek and check for the header. This is 12 bytes.
        // Magic byte - 1 byte
        // Metric type - 1 byte
        // Key length - 2 bytes
        // Metric value - 8 bytes
        status = peek_client_bytes(handle->conn, BINARY_HEADER_SIZE, (char*)&header);
        if (status == -1) return 0; // Return if no command is available

        // Check for the magic byte
        if (header[0] != BINARY_MAGIC_BYTE) {
            syslog(LOG_WARNING, "Received command from binary stream without magic byte!");
            return -1;
        }

        // Get the metric type
        type_input = header[1];
        switch (type_input) {
            case BIN_TYPE_KV:
                type = KEY_VAL;
                break;
            case BIN_TYPE_COUNTER:
                type = COUNTER;
                break;
            case BIN_TYPE_TIMER:
                type = TIMER;
                break;
            default:
                type = UNKNOWN;
                syslog(LOG_WARNING, "Received command from binary stream with unknown type: %u!", type_input);
                break;
        }

        // Extract the key length and value
        memcpy(&key_len, &header[2], 2);

        // Abort if we haven't received the full key, wait for the data
        if (available_bytes(handle->conn) < BINARY_HEADER_SIZE + key_len)
            return 0;

        // Seek past the header
        seek_client_bytes(handle->conn, BINARY_HEADER_SIZE);

        // Read the key now
        read_client_bytes(handle->conn, key_len, &key, &should_free);

        // Verify the key contains a null terminator
        if (memchr(key, '\0', key_len) == NULL) {
            syslog(LOG_WARNING, "Received command from binary stream with non-null terminated key: %.*s!", key_len, key);

            // For safety, we will just set the last byte to be null, and continue processing
            *(key + key_len - 1) = 0;
        }

        // Apply consistent hashing
        //char* server = hashring_getserver(handle->hashring, key);


        // Make sure to free the command buffer if we need to
        if (should_free) free(key);
    }

    return 0;
}