<commit_before>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016-2021 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"

#include "auth-priv.h"
#include "http/http-priv.h"

#include "search_handle.hh"
#include "capi/cmd_http.hh"

#include <regex>

#define LOGFMT "(NR=%p) "
#define LOGID(req) static_cast<const void *>(req)
#define LOGARGS(req, lvl) (req)->instance_->settings, "searchh", LCB_LOG_##lvl, __FILE__, __LINE__
#define LOGARGS2(req, lvl) (instance)->settings, "searchh", LCB_LOG_##lvl, __FILE__, __LINE__

static void chunk_callback(lcb_INSTANCE *, int, const lcb_RESPHTTP *resp)
{
    lcb_SEARCH_HANDLE_ *req = nullptr;
    lcb_resphttp_cookie(resp, reinterpret_cast<void **>(&req));

    req->http_response(resp);

    if (lcb_resphttp_is_final(resp)) {
        req->invoke_last();
        delete req;

    } else if (req->is_cancelled()) {
        /* Cancelled. Similar to the block above, except the http request
         * should remain alive (so we can cancel it later on) */
        delete req;
    } else {
        req->consume_http_chunk();
    }
}

void lcb_SEARCH_HANDLE_::invoke_row(lcb_RESPSEARCH *resp)
{
    resp->cookie = cookie_;
    resp->htresp = http_response_;
    resp->handle = this;
    if (resp->htresp) {
        resp->ctx.http_response_code = resp->htresp->ctx.response_code;
        resp->ctx.endpoint = resp->htresp->ctx.endpoint;
        resp->ctx.endpoint_len = resp->htresp->ctx.endpoint_len;
    }
    resp->ctx.index = index_name_.c_str();
    resp->ctx.index_len = index_name_.size();
    switch (resp->ctx.http_response_code) {
        case 500:
            resp->ctx.rc = LCB_ERR_INTERNAL_SERVER_FAILURE;
            break;
        case 401:
        case 403:
            resp->ctx.rc = LCB_ERR_AUTHENTICATION_FAILURE;
            break;
    }

    if (callback_) {
        if (resp->rflags & LCB_RESP_F_FINAL) {
            Json::Value meta;
            if (Json::Reader(Json::Features::strictMode()).parse(resp->row, resp->row + resp->nrow, meta)) {
                const Json::Value &top_error = meta["error"];
                if (top_error.isString()) {
                    resp->ctx.has_top_level_error = 1;
                    error_message_ = top_error.asString();
                } else {
                    const Json::Value &status = meta["status"];
                    if (status.isObject()) {
                        const Json::Value &errors = meta["errors"];
                        if (!errors.isNull()) {
                            error_message_ = Json::FastWriter().write(errors);
                        }
                    }
                }
                if (!error_message_.empty()) {
                    resp->ctx.error_message = error_message_.c_str();
                    resp->ctx.error_message_len = error_message_.size();
                    if (error_message_.find("QueryBleve parsing") != std::string::npos) {
                        resp->ctx.rc = LCB_ERR_PARSING_FAILURE;
                    } else if (resp->ctx.http_response_code == 400) {
                        if (error_message_.find("not_found") != std::string::npos) {
                            resp->ctx.rc = LCB_ERR_INDEX_NOT_FOUND;
                        } else if (error_message_.find("CreateIndex, Prepare failed, err: num_fts_indexes") !=
                                   std::string::npos) {
                            resp->ctx.rc = LCB_ERR_QUOTA_LIMITED;
                        }
                    } else if (resp->ctx.http_response_code == 429) {
                        std::regex rate_limiting_message(
                            "num_concurrent_requests|num_queries_per_min|ingress_mib_per_min|egress_mib_per_min");
                        if (std::regex_search(error_message_, rate_limiting_message)) {
                            resp->ctx.rc = LCB_ERR_RATE_LIMITED;
                        }
                    }
                }
            }<commit_msg>CCBC-1528: update parsing of "quota limit" error for FTS<commit_after>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016-2021 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"

#include "auth-priv.h"
#include "http/http-priv.h"

#include "search_handle.hh"
#include "capi/cmd_http.hh"

#include <regex>

#define LOGFMT "(NR=%p) "
#define LOGID(req) static_cast<const void *>(req)
#define LOGARGS(req, lvl) (req)->instance_->settings, "searchh", LCB_LOG_##lvl, __FILE__, __LINE__
#define LOGARGS2(req, lvl) (instance)->settings, "searchh", LCB_LOG_##lvl, __FILE__, __LINE__

static void chunk_callback(lcb_INSTANCE *, int, const lcb_RESPHTTP *resp)
{
    lcb_SEARCH_HANDLE_ *req = nullptr;
    lcb_resphttp_cookie(resp, reinterpret_cast<void **>(&req));

    req->http_response(resp);

    if (lcb_resphttp_is_final(resp)) {
        req->invoke_last();
        delete req;

    } else if (req->is_cancelled()) {
        /* Cancelled. Similar to the block above, except the http request
         * should remain alive (so we can cancel it later on) */
        delete req;
    } else {
        req->consume_http_chunk();
    }
}

void lcb_SEARCH_HANDLE_::invoke_row(lcb_RESPSEARCH *resp)
{
    resp->cookie = cookie_;
    resp->htresp = http_response_;
    resp->handle = this;
    if (resp->htresp) {
        resp->ctx.http_response_code = resp->htresp->ctx.response_code;
        resp->ctx.endpoint = resp->htresp->ctx.endpoint;
        resp->ctx.endpoint_len = resp->htresp->ctx.endpoint_len;
    }
    resp->ctx.index = index_name_.c_str();
    resp->ctx.index_len = index_name_.size();
    switch (resp->ctx.http_response_code) {
        case 500:
            resp->ctx.rc = LCB_ERR_INTERNAL_SERVER_FAILURE;
            break;
        case 401:
        case 403:
            resp->ctx.rc = LCB_ERR_AUTHENTICATION_FAILURE;
            break;
    }

    if (callback_) {
        if (resp->rflags & LCB_RESP_F_FINAL) {
            Json::Value meta;
            if (Json::Reader(Json::Features::strictMode()).parse(resp->row, resp->row + resp->nrow, meta)) {
                const Json::Value &top_error = meta["error"];
                if (top_error.isString()) {
                    resp->ctx.has_top_level_error = 1;
                    error_message_ = top_error.asString();
                } else {
                    const Json::Value &status = meta["status"];
                    if (status.isObject()) {
                        const Json::Value &errors = meta["errors"];
                        if (!errors.isNull()) {
                            error_message_ = Json::FastWriter().write(errors);
                        }
                    }
                }
                if (!error_message_.empty()) {
                    resp->ctx.error_message = error_message_.c_str();
                    resp->ctx.error_message_len = error_message_.size();
                    if (error_message_.find("QueryBleve parsing") != std::string::npos) {
                        resp->ctx.rc = LCB_ERR_PARSING_FAILURE;
                    } else if (resp->ctx.http_response_code == 400) {
                        if (error_message_.find("not_found") != std::string::npos) {
                            resp->ctx.rc = LCB_ERR_INDEX_NOT_FOUND;
                        } else if (error_message_.find("num_fts_indexes") != std::string::npos) {
                            resp->ctx.rc = LCB_ERR_QUOTA_LIMITED;
                        }
                    } else if (resp->ctx.http_response_code == 429) {
                        std::regex rate_limiting_message(
                            "num_concurrent_requests|num_queries_per_min|ingress_mib_per_min|egress_mib_per_min");
                        if (std::regex_search(error_message_, rate_limiting_message)) {
                            resp->ctx.rc = LCB_ERR_RATE_LIMITED;
                        }
                    }
                }
            }
{'fraction_non_alphanumeric': 0.11866460996033495, 'fraction_numerical': 0.007271925958572058, 'mean_word_length': 1.9966985803895676, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6786521', 'n_tokens_mistral': 2770, 'n_tokens_neox': 2658, 'n_words': 580}