-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
193 lines (165 loc) · 4.56 KB
/
Copy pathmain.go
File metadata and controls
193 lines (165 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package main
import (
"bufio"
"fmt"
"log"
"net"
"net/http"
"net/url"
"strings"
"time"
)
type ProxyServer struct {
port string
}
type RequestLog struct {
Timestamp time.Time
Method string
URL string
Headers map[string][]string
ResponseTime time.Duration
StatusCode int
}
func NewProxyServer(port string) *ProxyServer {
return &ProxyServer{port: port}
}
func (p *ProxyServer) Start() error {
listener, err := net.Listen("tcp", ":"+p.port)
if err != nil {
return fmt.Errorf("failed to start proxy server: %v", err)
}
defer listener.Close()
log.Printf("HTTP Proxy Server listening on port %s", p.port)
log.Println("Ready to accept connections...")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Error accepting connection: %v", err)
continue
}
go p.handleConnection(conn)
}
}
func (p *ProxyServer) handleConnection(conn net.Conn) {
defer conn.Close()
startTime := time.Now()
requestLog := &RequestLog{
Timestamp: startTime,
Headers: make(map[string][]string),
}
reader := bufio.NewReader(conn)
request, err := http.ReadRequest(reader)
if err != nil {
log.Printf("Error reading request: %v", err)
return
}
// Ensure URL is absolute for proxy forwarding
if !request.URL.IsAbs() {
// If URL is relative, construct absolute URL from Host header
if host := request.Header.Get("Host"); host != "" {
request.URL.Scheme = "http"
request.URL.Host = host
} else {
log.Printf("Error: No Host header and URL is not absolute")
errorResponse := "HTTP/1.1 400 Bad Request\r\n\r\n"
conn.Write([]byte(errorResponse))
return
}
}
// Log request details
requestLog.Method = request.Method
requestLog.URL = request.URL.String()
requestLog.Headers = request.Header
log.Printf("\n=== Incoming Request ===")
log.Printf("Time: %s", requestLog.Timestamp.Format("2006-01-02 15:04:05"))
log.Printf("Method: %s", requestLog.Method)
log.Printf("URL: %s", requestLog.URL)
log.Printf("Headers:")
for key, values := range requestLog.Headers {
for _, value := range values {
log.Printf(" %s: %s", key, value)
}
}
// Forward request to target server
response, err := p.forwardRequest(request)
if err != nil {
log.Printf("Error forwarding request: %v", err)
errorResponse := "HTTP/1.1 502 Bad Gateway\r\n\r\n"
conn.Write([]byte(errorResponse))
return
}
defer response.Body.Close()
// Calculate response time
requestLog.ResponseTime = time.Since(startTime)
requestLog.StatusCode = response.StatusCode
// Log response details
log.Printf("\n=== Response ===")
log.Printf("Status Code: %d", requestLog.StatusCode)
log.Printf("Response Time: %v", requestLog.ResponseTime)
log.Printf("Response Headers:")
for key, values := range response.Header {
for _, value := range values {
log.Printf(" %s: %s", key, value)
}
}
log.Printf("===================\n")
// Write response back to client
err = response.Write(conn)
if err != nil {
log.Printf("Error writing response: %v", err)
}
}
func (p *ProxyServer) forwardRequest(req *http.Request) (*http.Response, error) {
// Ensure URL is properly formatted
targetURL := req.URL.String()
if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
targetURL = "http://" + targetURL
}
parsedURL, err := url.Parse(targetURL)
if err != nil {
return nil, fmt.Errorf("invalid URL: %v", err)
}
// Create a new request to forward
forwardReq, err := http.NewRequest(req.Method, parsedURL.String(), req.Body)
if err != nil {
return nil, err
}
// Copy headers from original request, but remove proxy-specific headers
for key, values := range req.Header {
lowerKey := strings.ToLower(key)
// Skip hop-by-hop headers
if lowerKey == "connection" || lowerKey == "proxy-connection" ||
lowerKey == "keep-alive" || lowerKey == "proxy-authenticate" ||
lowerKey == "proxy-authorization" || lowerKey == "te" ||
lowerKey == "trailers" || lowerKey == "transfer-encoding" ||
lowerKey == "upgrade" {
continue
}
for _, value := range values {
forwardReq.Header.Add(key, value)
}
}
// Create HTTP client
client := &http.Client{
Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// Forward the request
response, err := client.Do(forwardReq)
if err != nil {
return nil, err
}
return response, nil
}
func main() {
port := "8080"
if len(strings.TrimSpace(port)) == 0 {
port = "8080"
}
proxy := NewProxyServer(port)
if err := proxy.Start(); err != nil {
log.Fatalf("Failed to start proxy server: %v", err)
}
}