github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/testing/scenarios/http_test.go (about) 1 package scenarios 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 "io" 8 "net/http" 9 "net/url" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/google/go-cmp/cmp" 15 "github.com/xmplusdev/xmcore/app/proxyman" 16 "github.com/xmplusdev/xmcore/common" 17 "github.com/xmplusdev/xmcore/common/buf" 18 "github.com/xmplusdev/xmcore/common/net" 19 "github.com/xmplusdev/xmcore/common/serial" 20 "github.com/xmplusdev/xmcore/core" 21 "github.com/xmplusdev/xmcore/proxy/freedom" 22 v2http "github.com/xmplusdev/xmcore/proxy/http" 23 v2httptest "github.com/xmplusdev/xmcore/testing/servers/http" 24 "github.com/xmplusdev/xmcore/testing/servers/tcp" 25 ) 26 27 func TestHttpConformance(t *testing.T) { 28 httpServerPort := tcp.PickPort() 29 httpServer := &v2httptest.Server{ 30 Port: httpServerPort, 31 PathHandler: make(map[string]http.HandlerFunc), 32 } 33 _, err := httpServer.Start() 34 common.Must(err) 35 defer httpServer.Close() 36 37 serverPort := tcp.PickPort() 38 serverConfig := &core.Config{ 39 Inbound: []*core.InboundHandlerConfig{ 40 { 41 ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ 42 PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, 43 Listen: net.NewIPOrDomain(net.LocalHostIP), 44 }), 45 ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), 46 }, 47 }, 48 Outbound: []*core.OutboundHandlerConfig{ 49 { 50 ProxySettings: serial.ToTypedMessage(&freedom.Config{}), 51 }, 52 }, 53 } 54 55 servers, err := InitializeServerConfigs(serverConfig) 56 common.Must(err) 57 defer CloseAllServers(servers) 58 59 { 60 transport := &http.Transport{ 61 Proxy: func(req *http.Request) (*url.URL, error) { 62 return url.Parse("http://127.0.0.1:" + serverPort.String()) 63 }, 64 } 65 66 client := &http.Client{ 67 Transport: transport, 68 } 69 70 resp, err := client.Get("http://127.0.0.1:" + httpServerPort.String()) 71 common.Must(err) 72 if resp.StatusCode != 200 { 73 t.Fatal("status: ", resp.StatusCode) 74 } 75 76 content, err := io.ReadAll(resp.Body) 77 common.Must(err) 78 if string(content) != "Home" { 79 t.Fatal("body: ", string(content)) 80 } 81 } 82 } 83 84 func TestHttpError(t *testing.T) { 85 tcpServer := tcp.Server{ 86 MsgProcessor: func(msg []byte) []byte { 87 return []byte{} 88 }, 89 } 90 dest, err := tcpServer.Start() 91 common.Must(err) 92 defer tcpServer.Close() 93 94 time.AfterFunc(time.Second*2, func() { 95 tcpServer.ShouldClose = true 96 }) 97 98 serverPort := tcp.PickPort() 99 serverConfig := &core.Config{ 100 Inbound: []*core.InboundHandlerConfig{ 101 { 102 ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ 103 PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, 104 Listen: net.NewIPOrDomain(net.LocalHostIP), 105 }), 106 ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), 107 }, 108 }, 109 Outbound: []*core.OutboundHandlerConfig{ 110 { 111 ProxySettings: serial.ToTypedMessage(&freedom.Config{}), 112 }, 113 }, 114 } 115 116 servers, err := InitializeServerConfigs(serverConfig) 117 common.Must(err) 118 defer CloseAllServers(servers) 119 120 { 121 transport := &http.Transport{ 122 Proxy: func(req *http.Request) (*url.URL, error) { 123 return url.Parse("http://127.0.0.1:" + serverPort.String()) 124 }, 125 } 126 127 client := &http.Client{ 128 Transport: transport, 129 } 130 131 resp, err := client.Get("http://127.0.0.1:" + dest.Port.String()) 132 if resp != nil && resp.StatusCode != 503 || err != nil && !strings.Contains(err.Error(), "malformed HTTP status code") { 133 t.Error("should not receive http response", err) 134 } 135 } 136 } 137 138 func TestHTTPConnectMethod(t *testing.T) { 139 tcpServer := tcp.Server{ 140 MsgProcessor: xor, 141 } 142 dest, err := tcpServer.Start() 143 common.Must(err) 144 defer tcpServer.Close() 145 146 serverPort := tcp.PickPort() 147 serverConfig := &core.Config{ 148 Inbound: []*core.InboundHandlerConfig{ 149 { 150 ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ 151 PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, 152 Listen: net.NewIPOrDomain(net.LocalHostIP), 153 }), 154 ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), 155 }, 156 }, 157 Outbound: []*core.OutboundHandlerConfig{ 158 { 159 ProxySettings: serial.ToTypedMessage(&freedom.Config{}), 160 }, 161 }, 162 } 163 164 servers, err := InitializeServerConfigs(serverConfig) 165 common.Must(err) 166 defer CloseAllServers(servers) 167 168 { 169 transport := &http.Transport{ 170 Proxy: func(req *http.Request) (*url.URL, error) { 171 return url.Parse("http://127.0.0.1:" + serverPort.String()) 172 }, 173 } 174 175 client := &http.Client{ 176 Transport: transport, 177 } 178 179 payload := make([]byte, 1024*64) 180 common.Must2(rand.Read(payload)) 181 182 ctx := context.Background() 183 req, err := http.NewRequestWithContext(ctx, "Connect", "http://"+dest.NetAddr()+"/", bytes.NewReader(payload)) 184 req.Header.Set("X-a", "b") 185 req.Header.Set("X-b", "d") 186 common.Must(err) 187 188 resp, err := client.Do(req) 189 common.Must(err) 190 if resp.StatusCode != 200 { 191 t.Fatal("status: ", resp.StatusCode) 192 } 193 194 content := make([]byte, len(payload)) 195 common.Must2(io.ReadFull(resp.Body, content)) 196 if r := cmp.Diff(content, xor(payload)); r != "" { 197 t.Fatal(r) 198 } 199 } 200 } 201 202 func TestHttpPost(t *testing.T) { 203 httpServerPort := tcp.PickPort() 204 httpServer := &v2httptest.Server{ 205 Port: httpServerPort, 206 PathHandler: map[string]http.HandlerFunc{ 207 "/testpost": func(w http.ResponseWriter, r *http.Request) { 208 payload, err := buf.ReadAllToBytes(r.Body) 209 r.Body.Close() 210 211 if err != nil { 212 w.WriteHeader(500) 213 w.Write([]byte("Unable to read all payload")) 214 return 215 } 216 payload = xor(payload) 217 w.Write(payload) 218 }, 219 }, 220 } 221 222 _, err := httpServer.Start() 223 common.Must(err) 224 defer httpServer.Close() 225 226 serverPort := tcp.PickPort() 227 serverConfig := &core.Config{ 228 Inbound: []*core.InboundHandlerConfig{ 229 { 230 ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ 231 PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, 232 Listen: net.NewIPOrDomain(net.LocalHostIP), 233 }), 234 ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), 235 }, 236 }, 237 Outbound: []*core.OutboundHandlerConfig{ 238 { 239 ProxySettings: serial.ToTypedMessage(&freedom.Config{}), 240 }, 241 }, 242 } 243 244 servers, err := InitializeServerConfigs(serverConfig) 245 common.Must(err) 246 defer CloseAllServers(servers) 247 248 { 249 transport := &http.Transport{ 250 Proxy: func(req *http.Request) (*url.URL, error) { 251 return url.Parse("http://127.0.0.1:" + serverPort.String()) 252 }, 253 } 254 255 client := &http.Client{ 256 Transport: transport, 257 } 258 259 payload := make([]byte, 1024*64) 260 common.Must2(rand.Read(payload)) 261 262 resp, err := client.Post("http://127.0.0.1:"+httpServerPort.String()+"/testpost", "application/x-www-form-urlencoded", bytes.NewReader(payload)) 263 common.Must(err) 264 if resp.StatusCode != 200 { 265 t.Fatal("status: ", resp.StatusCode) 266 } 267 268 content, err := io.ReadAll(resp.Body) 269 common.Must(err) 270 if r := cmp.Diff(content, xor(payload)); r != "" { 271 t.Fatal(r) 272 } 273 } 274 } 275 276 func setProxyBasicAuth(req *http.Request, user, pass string) { 277 req.SetBasicAuth(user, pass) 278 req.Header.Set("Proxy-Authorization", req.Header.Get("Authorization")) 279 req.Header.Del("Authorization") 280 } 281 282 func TestHttpBasicAuth(t *testing.T) { 283 httpServerPort := tcp.PickPort() 284 httpServer := &v2httptest.Server{ 285 Port: httpServerPort, 286 PathHandler: make(map[string]http.HandlerFunc), 287 } 288 _, err := httpServer.Start() 289 common.Must(err) 290 defer httpServer.Close() 291 292 serverPort := tcp.PickPort() 293 serverConfig := &core.Config{ 294 Inbound: []*core.InboundHandlerConfig{ 295 { 296 ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ 297 PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, 298 Listen: net.NewIPOrDomain(net.LocalHostIP), 299 }), 300 ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{ 301 Accounts: map[string]string{ 302 "a": "b", 303 }, 304 }), 305 }, 306 }, 307 Outbound: []*core.OutboundHandlerConfig{ 308 { 309 ProxySettings: serial.ToTypedMessage(&freedom.Config{}), 310 }, 311 }, 312 } 313 314 servers, err := InitializeServerConfigs(serverConfig) 315 common.Must(err) 316 defer CloseAllServers(servers) 317 318 { 319 transport := &http.Transport{ 320 Proxy: func(req *http.Request) (*url.URL, error) { 321 return url.Parse("http://127.0.0.1:" + serverPort.String()) 322 }, 323 } 324 325 client := &http.Client{ 326 Transport: transport, 327 } 328 329 { 330 resp, err := client.Get("http://127.0.0.1:" + httpServerPort.String()) 331 common.Must(err) 332 if resp.StatusCode != 407 { 333 t.Fatal("status: ", resp.StatusCode) 334 } 335 } 336 337 { 338 ctx := context.Background() 339 req, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:"+httpServerPort.String(), nil) 340 common.Must(err) 341 342 setProxyBasicAuth(req, "a", "c") 343 resp, err := client.Do(req) 344 common.Must(err) 345 if resp.StatusCode != 407 { 346 t.Fatal("status: ", resp.StatusCode) 347 } 348 } 349 350 { 351 ctx := context.Background() 352 req, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:"+httpServerPort.String(), nil) 353 common.Must(err) 354 355 setProxyBasicAuth(req, "a", "b") 356 resp, err := client.Do(req) 357 common.Must(err) 358 if resp.StatusCode != 200 { 359 t.Fatal("status: ", resp.StatusCode) 360 } 361 362 content, err := io.ReadAll(resp.Body) 363 common.Must(err) 364 if string(content) != "Home" { 365 t.Fatal("body: ", string(content)) 366 } 367 } 368 } 369 }