Skip to content

MySQL Provider HTTP/HTTPS Proxy Support #229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 2, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion mysql/provider.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package mysql

import (
"bufio"
"context"
"crypto/tls"
"crypto/x509"
"database/sql"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"net/url"
"os"
"regexp"
Expand Down Expand Up @@ -115,8 +118,12 @@ func Provider() *schema.Provider {
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
"ALL_PROXY",
"all_proxy",
"HTTP_PROXY",
"http_proxy",
"HTTPS_PROXY",
"https_proxy",
}, nil),
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^socks5h?://.*:\d+$`), "The proxy URL is not a valid socks url."),
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(socks5h?|http|https)://.*:\d+$`), "The proxy URL is not a valid proxy url. Must be in format: socks5://host:port, http://host:port, or https://host:port"),
},

"tls": {
Expand Down Expand Up @@ -588,6 +595,62 @@ func afterConnectVersion(ctx context.Context, mysqlConf *MySQLConfiguration, db

var identQuoteReplacer = strings.NewReplacer("`", "``")

// httpProxyDialer implements the proxy.Dialer interface for HTTP proxies
type httpProxyDialer struct {
proxyURL *url.URL
transport *http.Transport
}

// Dial connects to the address via the HTTP proxy
func (d *httpProxyDialer) Dial(network, addr string) (net.Conn, error) {
// For HTTP proxies with MySQL, we need to establish a TCP tunnel via the CONNECT method
conn, err := net.Dial("tcp", d.proxyURL.Host)
if err != nil {
return nil, err
}

// Send the CONNECT request
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: addr},
Host: addr,
Header: make(http.Header),
}

// Add proxy authentication if provided
if d.proxyURL.User != nil {
if password, ok := d.proxyURL.User.Password(); ok {
auth := d.proxyURL.User.Username() + ":" + password
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
connectReq.Header.Set("Proxy-Authorization", basicAuth)
}
}

// Write the request
err = connectReq.Write(conn)
if err != nil {
conn.Close()
return nil, err
}

// Read the response
br := bufio.NewReader(conn)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
conn.Close()
return nil, err
}
defer resp.Body.Close()

// Check if the connection was established
if resp.StatusCode != 200 {
conn.Close()
return nil, fmt.Errorf("proxy connection failed: %s", resp.Status)
}

return conn, nil
}

func makeDialer(d *schema.ResourceData) (proxy.Dialer, error) {
proxyFromEnv := proxy.FromEnvironment()
proxyArg := d.Get("proxy").(string)
Expand All @@ -597,6 +660,24 @@ func makeDialer(d *schema.ResourceData) (proxy.Dialer, error) {
if err != nil {
return nil, err
}

// Handle HTTP and HTTPS proxies differently from SOCKS
if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" {
log.Printf("[DEBUG] Using HTTP/HTTPS proxy: %s", proxyArg)

// Create an HTTP transport with the proxy
httpTransport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}

// Create a custom dialer that uses the HTTP transport
return &httpProxyDialer{
proxyURL: proxyURL,
transport: httpTransport,
}, nil
}

// For SOCKS proxies, use the standard library
proxyDialer, err := proxy.FromURL(proxyURL, proxy.Direct)
if err != nil {
return nil, err
Expand Down
Loading