<commit_before>// Package bridgeproxy relays with a remote TLS host via a HTTP proxy bridge.
package bridgeproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
)

// Peer is a server we are connecting to. This can either be an
// intermediate http(s) proxy server or the final server we want
// to connect to.
type Peer struct {
	TLSConfig    *tls.Config // nil if unencrypted, valid config otherwise
	HostName     string      // The hostname to connect to
	Port         int         // The port to connect to on the hostname
	ConnectExtra string      // Extra headers to send after the CONNECT line
}

func forward(src io.ReadCloser, dst io.WriteCloser) {
	if _, err := io.Copy(dst, src); err != nil {
		fmt.Println("Could not forward:", err)
	}
	src.Close()
	dst.Close()
}

// readLine reads a network line one byte at a time. We need to read unbuffered
// as we might later turn the connection after a 200 response for a CONNECT
// into a tls connection, for which we need a net.Conn.
func readLine(src io.Reader) (string, error) {
	line := make([]byte, 0, 64)
	length := 0
	for length < 2 || line[length-2] != '\r' || line[length-1] != '\n' {
		line = append(line, 0)
		if _, err := io.ReadFull(src, line[length:]); err != nil {
			return "", err
		}
		length++
	}
	return string(line[:length]), nil
}

func writeResponse(w io.Writer, code int, format string, printf ...interface{}) {
	msg := fmt.Sprintf(format, printf...)
	fmt.Fprintf(w, "HTTP/1.0 %d %s\r\n", code, http.StatusText(code))
	fmt.Fprintf(w, "Content-Type: text/plain\r\n")
	fmt.Fprintf(w, "Content-Length: %d\r\n", len(msg))
	fmt.Fprintf(w, "\r\n")
	fmt.Fprintf(w, "%s", msg)
}

func handleRequest(client net.Conn, peers []Peer) {
	var connection net.Conn
	var err error
	var connectionClosureHandled bool

	defer func() {
		if !connectionClosureHandled {
			client.Close()
			if connection != nil {
				connection.Close()
			}
		}
	}()

	for i, peer := range peers {
		// The first peer has to be dialed, others happen via connect
		if i == 0 {
			connection, err = net.Dial("tcp", fmt.Sprintf("%s:%d", peer.HostName, peer.Port))
			if err != nil {
				fmt.Println("ERROR: Could not connect", err)
				writeResponse(client, 502, "Could not dial proxy: %s\r\n", err)
				return
			}
		} else {
			fmt.Fprintf(connection, "CONNECT %s:%d HTTP/1.0\r\n%s\r\n\r\n", peer.HostName, peer.Port, peers[i-1].ConnectExtra)

			line, err := readLine(io.LimitReader(connection, 1024))
			if err != nil {
				fmt.Println("Could not read:", err)
				writeResponse(client, 502, "Could not CONNECT to %s: %s\r\n", err.Error())
				return
			}
			if !strings.HasPrefix(line, "HTTP/1.0 200") && !strings.HasPrefix(line, "HTTP/1.1 200") {
				writeResponse(client, 502, "Could not CONNECT to %s: %s", peer.HostName, line)
				return
			}
			if line, err = readLine(connection); err != nil {
				fmt.Println("Invalid second response line:", line)
				writeResponse(client, 502, "Could not CONNECT to %s: Missing second line", peer)
				return
			}
		}

		if peer.TLSConfig != nil {
			connection = tls.Client(connection, peer.TLSConfig)
		}
	}

	connectionClosureHandled = true
	// Forward traffic between the client connected to us and the remote proxy
	go forward(client, connection)
	go forward(connection, client)
}

// Serve serves the specified configuration, forwarding any packets from the
// local address given in listenAdress to the last peer specified in peers via
// any peers before specified before it.
func Serve(listenAdress string, peers []Peer) {
	// Listen for incoming connections.<commit_msg>Simplify defered closing of connections<commit_after>// Package bridgeproxy relays with a remote TLS host via a HTTP proxy bridge.
package bridgeproxy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
)

// Peer is a server we are connecting to. This can either be an
// intermediate http(s) proxy server or the final server we want
// to connect to.
type Peer struct {
	TLSConfig    *tls.Config // nil if unencrypted, valid config otherwise
	HostName     string      // The hostname to connect to
	Port         int         // The port to connect to on the hostname
	ConnectExtra string      // Extra headers to send after the CONNECT line
}

func forward(src io.ReadCloser, dst io.WriteCloser) {
	if _, err := io.Copy(dst, src); err != nil {
		fmt.Println("Could not forward:", err)
	}
	src.Close()
	dst.Close()
}

// readLine reads a network line one byte at a time. We need to read unbuffered
// as we might later turn the connection after a 200 response for a CONNECT
// into a tls connection, for which we need a net.Conn.
func readLine(src io.Reader) (string, error) {
	line := make([]byte, 0, 64)
	length := 0
	for length < 2 || line[length-2] != '\r' || line[length-1] != '\n' {
		line = append(line, 0)
		if _, err := io.ReadFull(src, line[length:]); err != nil {
			return "", err
		}
		length++
	}
	return string(line[:length]), nil
}

func writeResponse(w io.Writer, code int, format string, printf ...interface{}) {
	msg := fmt.Sprintf(format, printf...)
	fmt.Fprintf(w, "HTTP/1.0 %d %s\r\n", code, http.StatusText(code))
	fmt.Fprintf(w, "Content-Type: text/plain\r\n")
	fmt.Fprintf(w, "Content-Length: %d\r\n", len(msg))
	fmt.Fprintf(w, "\r\n")
	fmt.Fprintf(w, "%s", msg)
}

func handleRequest(client net.Conn, peers []Peer) {
	var connection net.Conn
	var err error

	defer func() {
		if client != nil {
			client.Close()
		}
		if connection != nil {
			connection.Close()
		}

	}()

	for i, peer := range peers {
		// The first peer has to be dialed, others happen via connect
		if i == 0 {
			connection, err = net.Dial("tcp", fmt.Sprintf("%s:%d", peer.HostName, peer.Port))
			if err != nil {
				fmt.Println("ERROR: Could not connect", err)
				writeResponse(client, 502, "Could not dial proxy: %s\r\n", err)
				return
			}
		} else {
			fmt.Fprintf(connection, "CONNECT %s:%d HTTP/1.0\r\n%s\r\n\r\n", peer.HostName, peer.Port, peers[i-1].ConnectExtra)

			line, err := readLine(io.LimitReader(connection, 1024))
			if err != nil {
				fmt.Println("Could not read:", err)
				writeResponse(client, 502, "Could not CONNECT to %s: %s\r\n", err.Error())
				return
			}
			if !strings.HasPrefix(line, "HTTP/1.0 200") && !strings.HasPrefix(line, "HTTP/1.1 200") {
				writeResponse(client, 502, "Could not CONNECT to %s: %s", peer.HostName, line)
				return
			}
			if line, err = readLine(connection); err != nil {
				fmt.Println("Invalid second response line:", line)
				writeResponse(client, 502, "Could not CONNECT to %s: Missing second line", peer)
				return
			}
		}

		if peer.TLSConfig != nil {
			connection = tls.Client(connection, peer.TLSConfig)
		}
	}

	// Forward traffic between the client connected to us and the remote proxy
	go forward(client, connection)
	go forward(connection, client)

	connection, client = nil, nil
}

// Serve serves the specified configuration, forwarding any packets from the
// local address given in listenAdress to the last peer specified in peers via
// any peers before specified before it.
func Serve(listenAdress string, peers []Peer) {
	// Listen for incoming connections.
{'fraction_non_alphanumeric': 0.1352069351230425, 'fraction_numerical': 0.012024608501118568, 'mean_word_length': 3.3509732360097324, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1013631', 'n_tokens_mistral': 2606, 'n_tokens_neox': 2338, 'n_words': 815}