diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a69224..6841d97 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,22 +8,13 @@ on: jobs: test: - services: - presto: - image: ghcr.io/popsql/prestodb-sandbox:0.284 - ports: - - "18080:8080" - trino: - image: trinodb/trino:418 - ports: - - "18081:8080" - runs-on: ubuntu-latest strategy: matrix: node-version: [ 16.x, 18.x, 20.x ] steps: - uses: actions/checkout@v3 + - run: docker compose up --detach --wait - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} diff --git a/docker-compose.yml b/docker-compose.yml index 1e47f38..1beba77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,3 +11,14 @@ services: ports: - "18081:8080" + nginx: + image: nginx:1.25-alpine + depends_on: + - presto + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/ssl/nginx-selfsigned.crt:/etc/nginx/ssl/nginx-selfsigned.crt:ro + - ./nginx/ssl/nginx-selfsigned.key:/etc/nginx/ssl/nginx-selfsigned.key:ro diff --git a/index.spec.js b/index.spec.js index 10daaf7..5813bb7 100644 --- a/index.spec.js +++ b/index.spec.js @@ -418,4 +418,36 @@ describe('redirect tests', function(){ }, }); }); + + describe('when using reverse proxy that does not forward protocol', function(){ + // The first request will go to https://localhost:443/v1/statement, but the + // nextUri will respond with http://localhost/v1/statement/... where then + // the nginx proxy will 301 redirect us to https again. + const client = new Client({ + host: 'localhost', + port: 443, + catalog: 'tpch', + schema: 'tiny', + ssl: { + rejectUnauthorized: false, + } + }); + + test('that querying works', function(done){ + expect.assertions(5); + client.execute({ + query: 'SELECT 1 AS col', + data: function(error, data, columns){ + expect(error).toBeNull(); + expect(data).toEqual([[1]]); + expect(columns).toHaveLength(1); + expect(columns[0]).toEqual(expect.objectContaining({ name: 'col', type: 'integer' })); + }, + callback: function(error){ + expect(error).toBeNull(); + done(); + }, + }); + }, 10000); + }); }); diff --git a/lib/presto-client/index.js b/lib/presto-client/index.js index 3cf4363..98f9636 100644 --- a/lib/presto-client/index.js +++ b/lib/presto-client/index.js @@ -1,15 +1,11 @@ const { URL } = require('url') ; +const http = require('follow-redirects/http'); +const https = require('follow-redirects/https'); -var adapterFor = (function() { - var adapters = { - 'http:': require('follow-redirects/http'), - 'https:': require('follow-redirects/https'), - }; - - return function(protocol) { - return adapters[protocol]; - }; -}()); +var adapters = { + 'http:': http, + 'https:': https, +}; var PrestoHeaders = require('./headers').Headers; var TrinoHeaders = require('./headers').TrinoHeaders; @@ -70,12 +66,12 @@ var Client = exports.Client = function(args){ Client.prototype.request = function(opts, callback) { var client = this; - var adapter = adapterFor(client.protocol); var contentBody = null; if (opts instanceof Object) { opts.host = client.host; opts.port = client.port; + opts.protocol = client.protocol; if (! opts.user) opts.user = client.user if (! opts.headers) @@ -90,14 +86,13 @@ Client.prototype.request = function(opts, callback) { port: href.port || (href.protocol === 'https:' ? '443' : '80'), path: href.pathname + href.search, headers: {}, + protocol: href.protocol, }; } catch (error) { return callback(error); } } - opts.protocol = client.protocol; - - opts = Object.assign({}, opts, client.ssl); + var adapter = adapters[opts.protocol]; if (opts.user) opts.headers[client.headers.USER] = opts.user; @@ -113,7 +108,10 @@ Client.prototype.request = function(opts, callback) { if (opts.body) contentBody = opts.body; - opts.agent = new adapter.Agent(opts); // Otherwise SSL params are ignored. + opts.agents = { + http: new http.Agent({ keepAlive: false }), + https: new https.Agent({ ...client.ssl, keepAlive: false }), + }; var parser = this.jsonParser; diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..8bd94d1 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,31 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 80; + server_name _; + return 301 https://localhost:443$request_uri; + } + + server { + listen 443 ssl; + server_name _; + + ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt; + ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key; + + location / { + proxy_pass http://presto:8080/; + proxy_connect_timeout 3; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # don't forward the protocol, so that presto will default to responding with + # http:// URLs, to test the 301 redirect logic. + # proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/nginx/ssl/nginx-selfsigned.crt b/nginx/ssl/nginx-selfsigned.crt new file mode 100644 index 0000000..0748747 --- /dev/null +++ b/nginx/ssl/nginx-selfsigned.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUYAwPnvz5X7oQWyF/FbHEDYhiliAwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzExMjExNDA4MjVaFw0yNDEx +MjAxNDA4MjVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCMWr++NX5Hpt6vnkMg2I72ja7PLTQo1f8sP12L2dOc +sQbIpY4pMCcmpzh1mI+gH9pPSsxrcnoDiX+rMPMpu1VVREY95jOUWWKQ+8fosCtQ +T0sZ5X6XoPd4MLqiaDZajh6xxMgTz4L08xPMi7wMpSwnZGPTbr3KDqVD1gdwaj3L +YZZK1gfOYpt9XErZ+EePI0uCxRNyrkB+X3t2dJVRVQzWt23fFtsOVBW94kbG+Swh +7VO8UCJ56EJpC6767CdqNdQs119+yiVjabG3xA2PPczJIsLRcb4nZO25oNiDh3Lw +rfrxYoBvM6JN/IXuf4E3+nUMuwrzVsx6d1aPGZBm7qALAgMBAAGjUzBRMB0GA1Ud +DgQWBBRKe9ovcw7I2WQ/kz5CP3m9W1qxGjAfBgNVHSMEGDAWgBRKe9ovcw7I2WQ/ +kz5CP3m9W1qxGjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAx +2Svbu20Fx1HLPrbpDzlAHpI0Pnw5zzeEoIc/XIZ2tLa13vuFiUyT18kbPPmnI8Zh +cdPhNGgNxfa7VNM1/lGbfSnPBKR6uJcgXKO+olWHZkaRYjeJh/JpsobOn7L3hNpA +ygNioc0oiffkGOznATkYmq6HeV8OQ5H7/Z4wu1VhZBDNjkSPPy+xdvM/k/eLuzvU +dpErzxd2GRsv/tVVEryvNTcgJB+cm/WSXd6AgLQtKLcIZCO20cqAP78G25KA7qAl +iBSH+pXJk1snl8PE9jmn+4uj+9axsCEpvIMlVCBnTxjgSfVvvimGdnPyrWamIN/9 +CyW2rVZFarM0UVQ+BMw8 +-----END CERTIFICATE----- diff --git a/nginx/ssl/nginx-selfsigned.key b/nginx/ssl/nginx-selfsigned.key new file mode 100644 index 0000000..30d2e40 --- /dev/null +++ b/nginx/ssl/nginx-selfsigned.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMWr++NX5Hpt6v +nkMg2I72ja7PLTQo1f8sP12L2dOcsQbIpY4pMCcmpzh1mI+gH9pPSsxrcnoDiX+r +MPMpu1VVREY95jOUWWKQ+8fosCtQT0sZ5X6XoPd4MLqiaDZajh6xxMgTz4L08xPM +i7wMpSwnZGPTbr3KDqVD1gdwaj3LYZZK1gfOYpt9XErZ+EePI0uCxRNyrkB+X3t2 +dJVRVQzWt23fFtsOVBW94kbG+Swh7VO8UCJ56EJpC6767CdqNdQs119+yiVjabG3 +xA2PPczJIsLRcb4nZO25oNiDh3LwrfrxYoBvM6JN/IXuf4E3+nUMuwrzVsx6d1aP +GZBm7qALAgMBAAECggEAAuaD3Iq/H6dGJiZEyDKmzwI3NkF+jh17Vs8UatBx5roN +ztWvrhA4WZHOa+Je4ss7DV/EAQXDmbvHWff6qRvISVPOM8yZt5tv8FkTqi5XJs4M +r5imLhH91eED6llG+PDDl57ebPJhMsNXNGdALUQN496COvColrqzavHLA5AJKOEa +jrPa0jSpL/bKWiKddxfhWf5TQdcbiz13g3t+WzWdaBbuRY4/Nf/VMkPGiRKf9zW5 +RTqPJM9BiCAiniNS3SEmavPNnVk947hszAyIpHt60+0iE/inHFNAdWWCKs4BxnKL +OQBBcgkouSDzIBKiicl16YGVZ+8kgRvi+5+7H+2uwQKBgQC+Fft3EDj3EY59RgLd +2OMxsVMjCntCFM4W/8LJV0LhWjE3u84G3Z7CeYwy9qxv/3vWPyeaNT2KfkXLlnJf +cOKxED/R3ffFxYm5VMHH7p7EznIvx/abiTVrBIY1JiPPB1aw+i0K/QR0nY1Ctl7f +6h2i3TtbShPASMuwcYQzyU+aYQKBgQC9BhZS259EJ0F9AViZdryFFLw9n4OSKZz4 +KcLGvYauOFloY5SLjxXhBALBjewjDqeWAgK4ZjzEwvO+Ste5pgarIDRl55tCg6rR +7O7cZtHyGhMuNIDKWS/NsKMBkKT+3yLtzvuIi5aOej8z53XDkEXZP6k5jM4FyF+1 +E+8x9f6J6wKBgD1fi5mdu0bKfF8mQ7atWG2q+1TNBY8IeKIkYhfv/MAWBt4f3R2K +YMAZzpKF4pcmvi0mpvq7Ret5ab+aC8OVBxx0SqbQLDfG1/jmzMpo2QpiPav/vDfr +zTuIcmymq9kkXXE21WQ+3TlpxTpM98UEKh1zoL6Q5F+pIVsOy8iMrw8BAoGADIDN +kzeBxnFOTKV9JdGL0MW5Wd4mRtHH10Zz4s+AjB+gEl6b4ctaKCMqx4DKwNrkTPDf +GzFwDZfTpZ+vu1D3GfSsYp06UOECuJYSirOmaXw+Do1cg4X4uoNXlO/ragx6c/5/ +9t083TnK2nUR/hiZ9i/W0r60lf9WDVU/Oi/KYXcCgYBJcACw8NTt3gzfYPpPUgXk +88daWCHy91qdPdS/TZDemZ99TAFk2ArmLvxry+h6bi3RITCAWq5jmXlgjYKLaWAE +TLHiS1ApWTEQo9pI1i2HIiuENnCNjmaRJOcmefV/mEQ8/Oor7BOZM99RMvSvhebv +8a/h52cttrFHA+tQ+K78QA== +-----END PRIVATE KEY-----