From a1814bb68021504e66747845c4303241a31a204c Mon Sep 17 00:00:00 2001
From: Mike Siomkin <msiomkin@mail.ru>
Date: Tue, 19 Mar 2019 16:35:06 +0300
Subject: [PATCH] httpc: add proxy server options for curl

Added support for the following curl options:
* CURLOPT_PROXY
* CURLOPT_PROXYPORT
* CURLOPT_PROXYUSERPWD
* CURLOPT_NOPROXY

@TarantoolBot document
Title: httpc: proxy server options

Use 'proxy' option to specify the proxy server host or IP-address
(optionally may be prefixed with a scheme - e.g. http:// or https://).
'proxy_port' and 'proxy_user_pwd' options may be used to specify the
proxy port (443 for https proxy and 1080 for others by default) and
user credentials (format: [user name]:[password]) respectively.

If 'proxy' option is not set a value from the corresponding
environment variable will be used. Environment variable names are:
'http_proxy', 'https_proxy', 'ftp_proxy' etc. 'all_proxy' variable
is used if no protocol specific proxy was set.

Setting 'proxy' option to an empty string will explicitly disable the
use of a proxy, even if there is an environment variable set for it.

Set 'no_proxy' option to specify a comma separated list of hosts that
do not require a proxy to get reached, even if one is specified by
'proxy' option (or the corresponding environment variable). The only
wildcard available is a single * character, which matches all hosts,
and effectively disables the proxy. 'no_proxy' environment variable
will be used if this option is not set.

Setting 'no_proxy' option to an empty string will explicitly enable
the proxy for all host names, even if there is an environment variable
set for it.

See:
https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html
https://curl.haxx.se/libcurl/c/CURLOPT_PROXYPORT.html
https://curl.haxx.se/libcurl/c/CURLOPT_USERPWD.html
https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html
---
 src/httpc.c       | 25 ++++++++++++++++++++
 src/httpc.h       | 60 +++++++++++++++++++++++++++++++++++++++++++++++
 src/lua/httpc.c   | 20 ++++++++++++++++
 src/lua/httpc.lua |  9 +++++++
 4 files changed, 114 insertions(+)

diff --git a/src/httpc.c b/src/httpc.c
index 54bc785e1b..8d18b9966a 100644
--- a/src/httpc.c
+++ b/src/httpc.c
@@ -324,6 +324,30 @@ httpc_set_ssl_cert(struct httpc_request *req, const char *ssl_cert)
 	curl_easy_setopt(req->curl_request.easy, CURLOPT_SSLCERT, ssl_cert);
 }
 
+void
+httpc_set_proxy(struct httpc_request *req, const char *proxy)
+{
+	curl_easy_setopt(req->curl_request.easy, CURLOPT_PROXY, proxy);
+}
+
+void
+httpc_set_proxy_port(struct httpc_request *req, long port)
+{
+	curl_easy_setopt(req->curl_request.easy, CURLOPT_PROXYPORT, port);
+}
+
+void
+httpc_set_proxy_user_pwd(struct httpc_request *req, const char *user_pwd)
+{
+	curl_easy_setopt(req->curl_request.easy, CURLOPT_PROXYUSERPWD, user_pwd);
+}
+
+void
+httpc_set_no_proxy(struct httpc_request *req, const char *no_proxy)
+{
+	curl_easy_setopt(req->curl_request.easy, CURLOPT_NOPROXY, no_proxy);
+}
+
 void
 httpc_set_interface(struct httpc_request *req, const char *interface)
 {
@@ -402,6 +426,7 @@ httpc_execute(struct httpc_request *req, double timeout)
 		req->reason = curl_easy_strerror(req->curl_request.code);
 		++env->stat.failed_requests;
 		break;
+	case CURLE_COULDNT_RESOLVE_PROXY:
 	case CURLE_COULDNT_RESOLVE_HOST:
 	case CURLE_COULDNT_CONNECT:
 		/* 595 Connection Problem (AnyEvent non-standard) */
diff --git a/src/httpc.h b/src/httpc.h
index c6882a534a..99fd8fbd43 100644
--- a/src/httpc.h
+++ b/src/httpc.h
@@ -291,6 +291,66 @@ httpc_set_ssl_key(struct httpc_request *req, const char *ssl_key);
 void
 httpc_set_ssl_cert(struct httpc_request *req, const char *ssl_cert);
 
+/**
+ * Specify a proxy to use (optionally may be prefixed with a scheme -
+ * e.g. http:// or https://).
+ *
+ * If this option is not set a value from the corresponding
+ * environment variable will be used. Environment variable names are:
+ * 'http_proxy', 'https_proxy', 'ftp_proxy' etc. 'all_proxy' variable
+ * is used if no protocol specific proxy was set.
+ *
+ * Setting this option to an empty string will explicitly disable the
+ * use of a proxy, even if there is an environment variable set for it.
+ *
+ * @param req request
+ * @param proxy - a host name or an IP address. The application does not
+ * have to keep the string around after setting this option.
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html
+ */
+void
+httpc_set_proxy(struct httpc_request *req, const char *proxy);
+
+/**
+ * Specify a port number the proxy listens on
+ * @param req request
+ * @param port - a port number the proxy listens on
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXYPORT.html
+ */
+void
+httpc_set_proxy_port(struct httpc_request *req, long port);
+
+/**
+ * Specify a user name and a password to use in authentication
+ * @param req request
+ * @param user_pwd - a login details string for the connection.
+ * The format is: [user name]:[password]. The application does not
+ * have to keep the string around after setting this option.
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_USERPWD.html
+ */
+void
+httpc_set_proxy_user_pwd(struct httpc_request *req, const char *user_pwd);
+
+/**
+ * Specify a comma separated list of host names that do not require a proxy
+ * to get reached, even if one is specified by 'proxy' option. The only
+ * wildcard available is a single * character, which matches all hosts, and
+ * effectively disables the proxy.
+ *
+ * 'no_proxy' environment variable will be used if this option is not set.
+ *
+ * Setting this option to an empty string will
+ * explicitly enable the proxy for all host names, even if there is an
+ * environment variable set for it.
+ *
+ * @param req request
+ * @param no_proxy - a comma separated list of host names. The application
+ * does not have to keep the string around after setting this option.
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html
+ */
+void
+httpc_set_no_proxy(struct httpc_request *req, const char *no_proxy);
+
 /**
  * Specify source interface for outgoing traffic
  * @param req request
diff --git a/src/lua/httpc.c b/src/lua/httpc.c
index e02adb9fb9..a8e3e2525b 100644
--- a/src/lua/httpc.c
+++ b/src/lua/httpc.c
@@ -243,6 +243,26 @@ luaT_httpc_request(lua_State *L)
 		httpc_set_ssl_cert(req, lua_tostring(L, -1));
 	lua_pop(L, 1);
 
+	lua_getfield(L, 5, "proxy");
+	if (!lua_isnil(L, -1))
+		httpc_set_proxy(req, lua_tostring(L, -1));
+	lua_pop(L, 1);
+
+	lua_getfield(L, 5, "proxy_port");
+	if (!lua_isnil(L, -1))
+		httpc_set_proxy_port(req, (long) lua_tonumber(L, -1));
+	lua_pop(L, 1);
+
+	lua_getfield(L, 5, "proxy_user_pwd");
+	if (!lua_isnil(L, -1))
+		httpc_set_proxy_user_pwd(req, lua_tostring(L, -1));
+	lua_pop(L, 1);
+
+	lua_getfield(L, 5, "no_proxy");
+	if (!lua_isnil(L, -1))
+		httpc_set_no_proxy(req, lua_tostring(L, -1));
+	lua_pop(L, 1);
+
 	long keepalive_idle = 0;
 	long keepalive_interval = 0;
 
diff --git a/src/lua/httpc.lua b/src/lua/httpc.lua
index 50ff91a369..ce9bb9771e 100644
--- a/src/lua/httpc.lua
+++ b/src/lua/httpc.lua
@@ -258,6 +258,15 @@ end
 --
 --      ssl_cert - set path to the file with SSL client certificate;
 --
+--      proxy - set a proxy to use;
+--
+--      proxy_port - set a port number the proxy listens on;
+--
+--      proxy_user_pwd - set a user name and a password to use
+--          in authentication;
+--
+--      no_proxy - disable proxy use for specific hosts;
+--
 --      headers - a table of HTTP headers;
 --
 --      keepalive_idle & keepalive_interval -
-- 
GitLab