diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/plugins/fts-solr/solr-connection.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/plugins/fts-solr/solr-connection.c')
-rw-r--r-- | src/plugins/fts-solr/solr-connection.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/plugins/fts-solr/solr-connection.c b/src/plugins/fts-solr/solr-connection.c new file mode 100644 index 0000000..41a4fee --- /dev/null +++ b/src/plugins/fts-solr/solr-connection.c @@ -0,0 +1,327 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "hash.h" +#include "str.h" +#include "strescape.h" +#include "ioloop.h" +#include "istream.h" +#include "http-url.h" +#include "http-client.h" +#include "fts-solr-plugin.h" +#include "solr-connection.h" + +#include <expat.h> + +struct solr_lookup_context { + pool_t result_pool; + struct istream *payload; + struct io *io; + + int request_status; + + struct solr_response_parser *parser; + struct solr_result **results; +}; + +struct solr_connection_post { + struct solr_connection *conn; + + struct http_client_request *http_req; + int request_status; + + bool failed:1; +}; + +struct solr_connection { + struct event *event; + char *http_host; + in_port_t http_port; + char *http_base_url; + char *http_failure; + char *http_user; + char *http_password; + + bool debug:1; + bool posting:1; + bool http_ssl:1; +}; + +/* Regardless of the specified URL, make sure path ends in '/' */ +static char *solr_connection_create_http_base_url(struct http_url *http_url) +{ + if (http_url->path == NULL) + return i_strconcat("/", http_url->enc_query, NULL); + size_t len = strlen(http_url->path); + if (len > 0 && http_url->path[len-1] != '/') + return i_strconcat(http_url->path, "/", + http_url->enc_query, NULL); + /* http_url->path is NULL on empty path, so this is impossible. */ + i_assert(len != 0); + return i_strconcat(http_url->path, http_url->enc_query, NULL); +} + +int solr_connection_init(const struct fts_solr_settings *solr_set, + const struct ssl_iostream_settings *ssl_client_set, + struct event *event_parent, + struct solr_connection **conn_r, const char **error_r) +{ + struct http_client_settings http_set; + struct solr_connection *conn; + struct http_url *http_url; + const char *error; + + if (http_url_parse(solr_set->url, NULL, HTTP_URL_ALLOW_USERINFO_PART, + pool_datastack_create(), &http_url, &error) < 0) { + *error_r = t_strdup_printf( + "fts_solr: Failed to parse HTTP url: %s", error); + return -1; + } + + conn = i_new(struct solr_connection, 1); + conn->event = event_create(event_parent); + conn->http_host = i_strdup(http_url->host.name); + conn->http_port = http_url->port; + conn->http_base_url = solr_connection_create_http_base_url(http_url); + conn->http_ssl = http_url->have_ssl; + if (http_url->user != NULL) { + conn->http_user = i_strdup(http_url->user); + /* allow empty password */ + conn->http_password = i_strdup(http_url->password != NULL ? + http_url->password : ""); + } + + conn->debug = solr_set->debug; + + if (solr_http_client == NULL) { + i_zero(&http_set); + http_set.max_idle_time_msecs = 5*1000; + http_set.max_parallel_connections = 1; + http_set.max_pipelined_requests = 1; + http_set.max_redirects = 1; + http_set.max_attempts = 3; + http_set.connect_timeout_msecs = 5*1000; + http_set.request_timeout_msecs = 60*1000; + http_set.ssl = ssl_client_set; + http_set.debug = solr_set->debug; + http_set.rawlog_dir = solr_set->rawlog_dir; + http_set.event_parent = conn->event; + + /* FIXME: We should initialize a shared client instead. However, + this is currently not possible due to an obscure bug + in the blocking HTTP payload API, which causes + conflicts with other HTTP applications like FTS Tika. + Using a private client will provide a quick fix for + now. */ + solr_http_client = http_client_init_private(&http_set); + } + + *conn_r = conn; + return 0; +} + +void solr_connection_deinit(struct solr_connection **_conn) +{ + struct solr_connection *conn = *_conn; + + *_conn = NULL; + event_unref(&conn->event); + i_free(conn->http_host); + i_free(conn->http_base_url); + i_free(conn->http_user); + i_free(conn->http_password); + i_free(conn); +} + +static void solr_connection_payload_input(struct solr_lookup_context *lctx) +{ + int ret; + + /* read payload */ + ret = solr_response_parse(lctx->parser, &lctx->results); + + if (ret == 0) { + /* we will be called again for more data */ + } else { + if (lctx->payload->stream_errno != 0) { + i_assert(ret < 0); + i_error("fts_solr: " + "failed to read payload from HTTP server: %s", + i_stream_get_error(lctx->payload)); + } + if (ret < 0) + lctx->request_status = -1; + solr_response_parser_deinit(&lctx->parser); + io_remove(&lctx->io); + } +} + +static void +solr_connection_select_response(const struct http_response *response, + struct solr_lookup_context *lctx) +{ + if (response->status / 100 != 2) { + i_error("fts_solr: Lookup failed: %s", + http_response_get_message(response)); + lctx->request_status = -1; + return; + } + + if (response->payload == NULL) { + i_error("fts_solr: Lookup failed: Empty response payload"); + lctx->request_status = -1; + return; + } + + lctx->parser = solr_response_parser_init(lctx->result_pool, + response->payload); + lctx->payload = response->payload; + lctx->io = io_add_istream(response->payload, + solr_connection_payload_input, lctx); + solr_connection_payload_input(lctx); +} + +int solr_connection_select(struct solr_connection *conn, const char *query, + pool_t pool, struct solr_result ***box_results_r) +{ + struct solr_lookup_context lctx; + struct http_client_request *http_req; + const char *url; + + i_zero(&lctx); + lctx.result_pool = pool; + + i_free_and_null(conn->http_failure); + url = t_strconcat(conn->http_base_url, "select?", query, NULL); + + http_req = http_client_request(solr_http_client, "GET", + conn->http_host, url, + solr_connection_select_response, + &lctx); + if (conn->http_user != NULL) { + http_client_request_set_auth_simple( + http_req, conn->http_user, conn->http_password); + } + http_client_request_set_port(http_req, conn->http_port); + http_client_request_set_ssl(http_req, conn->http_ssl); + http_client_request_submit(http_req); + + lctx.request_status = 0; + http_client_wait(solr_http_client); + + if (lctx.request_status < 0) + return -1; + + *box_results_r = lctx.results; + return 0; +} + +static void +solr_connection_update_response(const struct http_response *response, + struct solr_connection_post *post) +{ + if (response->status / 100 != 2) { + i_error("fts_solr: Indexing failed: %s", + http_response_get_message(response)); + post->request_status = -1; + } +} + +static struct http_client_request * +solr_connection_post_request(struct solr_connection_post *post) +{ + struct solr_connection *conn = post->conn; + struct http_client_request *http_req; + const char *url; + + url = t_strconcat(conn->http_base_url, "update", NULL); + + http_req = http_client_request(solr_http_client, "POST", + conn->http_host, url, + solr_connection_update_response, post); + if (conn->http_user != NULL) { + http_client_request_set_auth_simple( + http_req, conn->http_user, conn->http_password); + } + http_client_request_set_port(http_req, conn->http_port); + http_client_request_set_ssl(http_req, conn->http_ssl); + http_client_request_add_header(http_req, "Content-Type", "text/xml"); + return http_req; +} + +struct solr_connection_post * +solr_connection_post_begin(struct solr_connection *conn) +{ + struct solr_connection_post *post; + + i_assert(!conn->posting); + conn->posting = TRUE; + + post = i_new(struct solr_connection_post, 1); + post->conn = conn; + post->http_req = solr_connection_post_request(post); + return post; +} + +void solr_connection_post_more(struct solr_connection_post *post, + const unsigned char *data, size_t size) +{ + i_assert(post->conn->posting); + + if (post->failed) + return; + + if (post->request_status == 0) { + (void)http_client_request_send_payload( + &post->http_req, data, size); + } + if (post->request_status < 0) + post->failed = TRUE; +} + +int solr_connection_post_end(struct solr_connection_post **_post) +{ + struct solr_connection_post *post = *_post; + struct solr_connection *conn = post->conn; + int ret = post->failed ? -1 : 0; + + i_assert(conn->posting); + + *_post = NULL; + + if (!post->failed) { + if (http_client_request_finish_payload(&post->http_req) < 0 || + post->request_status < 0) { + ret = -1; + } + } else { + http_client_request_abort(&post->http_req); + } + i_free(post); + + conn->posting = FALSE; + return ret; +} + +int solr_connection_post(struct solr_connection *conn, const char *cmd) +{ + struct istream *post_payload; + struct solr_connection_post post; + + i_assert(!conn->posting); + + i_zero(&post); + post.conn = conn; + + post.http_req = solr_connection_post_request(&post); + post_payload = i_stream_create_from_data(cmd, strlen(cmd)); + http_client_request_set_payload(post.http_req, post_payload, TRUE); + i_stream_unref(&post_payload); + http_client_request_submit(post.http_req); + + post.request_status = 0; + http_client_wait(solr_http_client); + + return post.request_status; +} |