github.com/ck00004/CobaltStrikeParser-Go@v1.0.14/lib/http/httputil/reverseproxy.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // HTTP reverse proxy handler 6 7 package httputil 8 9 import ( 10 "context" 11 "fmt" 12 "io" 13 "log" 14 "net" 15 "net/textproto" 16 "strings" 17 "sync" 18 "time" 19 20 "github.com/ck00004/CobaltStrikeParser-Go/lib/url" 21 22 "github.com/ck00004/CobaltStrikeParser-Go/lib/http/internal/ascii" 23 24 "github.com/ck00004/CobaltStrikeParser-Go/lib/http" 25 26 "golang.org/x/net/http/httpguts" 27 ) 28 29 // ReverseProxy is an HTTP Handler that takes an incoming request and 30 // sends it to another server, proxying the response back to the 31 // client. 32 // 33 // ReverseProxy by default sets the client IP as the value of the 34 // X-Forwarded-For header. 35 // 36 // If an X-Forwarded-For header already exists, the client IP is 37 // appended to the existing values. As a special case, if the header 38 // exists in the Request.Header map but has a nil value (such as when 39 // set by the Director func), the X-Forwarded-For header is 40 // not modified. 41 // 42 // To prevent IP spoofing, be sure to delete any pre-existing 43 // X-Forwarded-For header coming from the client or 44 // an untrusted proxy. 45 type ReverseProxy struct { 46 // Director must be a function which modifies 47 // the request into a new request to be sent 48 // using Transport. Its response is then copied 49 // back to the original client unmodified. 50 // Director must not access the provided Request 51 // after returning. 52 Director func(*http.Request) 53 54 // The transport used to perform proxy requests. 55 // If nil, http.DefaultTransport is used. 56 Transport http.RoundTripper 57 58 // FlushInterval specifies the flush interval 59 // to flush to the client while copying the 60 // response body. 61 // If zero, no periodic flushing is done. 62 // A negative value means to flush immediately 63 // after each write to the client. 64 // The FlushInterval is ignored when ReverseProxy 65 // recognizes a response as a streaming response, or 66 // if its ContentLength is -1; for such responses, writes 67 // are flushed to the client immediately. 68 FlushInterval time.Duration 69 70 // ErrorLog specifies an optional logger for errors 71 // that occur when attempting to proxy the request. 72 // If nil, logging is done via the log package's standard logger. 73 ErrorLog *log.Logger 74 75 // BufferPool optionally specifies a buffer pool to 76 // get byte slices for use by io.CopyBuffer when 77 // copying HTTP response bodies. 78 BufferPool BufferPool 79 80 // ModifyResponse is an optional function that modifies the 81 // Response from the backend. It is called if the backend 82 // returns a response at all, with any HTTP status code. 83 // If the backend is unreachable, the optional ErrorHandler is 84 // called without any call to ModifyResponse. 85 // 86 // If ModifyResponse returns an error, ErrorHandler is called 87 // with its error value. If ErrorHandler is nil, its default 88 // implementation is used. 89 ModifyResponse func(*http.Response) error 90 91 // ErrorHandler is an optional function that handles errors 92 // reaching the backend or errors from ModifyResponse. 93 // 94 // If nil, the default is to log the provided error and return 95 // a 502 Status Bad Gateway response. 96 ErrorHandler func(http.ResponseWriter, *http.Request, error) 97 } 98 99 // A BufferPool is an interface for getting and returning temporary 100 // byte slices for use by io.CopyBuffer. 101 type BufferPool interface { 102 Get() []byte 103 Put([]byte) 104 } 105 106 func singleJoiningSlash(a, b string) string { 107 aslash := strings.HasSuffix(a, "/") 108 bslash := strings.HasPrefix(b, "/") 109 switch { 110 case aslash && bslash: 111 return a + b[1:] 112 case !aslash && !bslash: 113 return a + "/" + b 114 } 115 return a + b 116 } 117 118 func joinURLPath(a, b *url.URL) (path, rawpath string) { 119 if a.RawPath == "" && b.RawPath == "" { 120 return singleJoiningSlash(a.Path, b.Path), "" 121 } 122 // Same as singleJoiningSlash, but uses EscapedPath to determine 123 // whether a slash should be added 124 apath := a.EscapedPath() 125 bpath := b.EscapedPath() 126 127 aslash := strings.HasSuffix(apath, "/") 128 bslash := strings.HasPrefix(bpath, "/") 129 130 switch { 131 case aslash && bslash: 132 return a.Path + b.Path[1:], apath + bpath[1:] 133 case !aslash && !bslash: 134 return a.Path + "/" + b.Path, apath + "/" + bpath 135 } 136 return a.Path + b.Path, apath + bpath 137 } 138 139 // NewSingleHostReverseProxy returns a new ReverseProxy that routes 140 // URLs to the scheme, host, and base path provided in target. If the 141 // target's path is "/base" and the incoming request was for "/dir", 142 // the target request will be for /base/dir. 143 // NewSingleHostReverseProxy does not rewrite the Host header. 144 // To rewrite Host headers, use ReverseProxy directly with a custom 145 // Director policy. 146 func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { 147 targetQuery := target.RawQuery 148 director := func(req *http.Request) { 149 req.URL.Scheme = target.Scheme 150 req.URL.Host = target.Host 151 req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) 152 if targetQuery == "" || req.URL.RawQuery == "" { 153 req.URL.RawQuery = targetQuery + req.URL.RawQuery 154 } else { 155 req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery 156 } 157 if _, ok := req.Header["User-Agent"]; !ok { 158 // explicitly disable User-Agent so it's not set to default value 159 req.Header.Set("User-Agent", "") 160 } 161 } 162 return &ReverseProxy{Director: director} 163 } 164 165 func copyHeader(dst, src http.Header) { 166 for k, vv := range src { 167 for _, v := range vv { 168 dst.Add(k, v) 169 } 170 } 171 } 172 173 // Hop-by-hop headers. These are removed when sent to the backend. 174 // As of RFC 7230, hop-by-hop headers are required to appear in the 175 // Connection header field. These are the headers defined by the 176 // obsoleted RFC 2616 (section 13.5.1) and are used for backward 177 // compatibility. 178 var hopHeaders = []string{ 179 "Connection", 180 "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google 181 "Keep-Alive", 182 "Proxy-Authenticate", 183 "Proxy-Authorization", 184 "Te", // canonicalized version of "TE" 185 "Trailer", // not Trailers per URL above; https://www.rfc-editor.org/errata_search.php?eid=4522 186 "Transfer-Encoding", 187 "Upgrade", 188 } 189 190 func (p *ReverseProxy) defaultErrorHandler(rw http.ResponseWriter, req *http.Request, err error) { 191 p.logf("http: proxy error: %v", err) 192 rw.WriteHeader(http.StatusBadGateway) 193 } 194 195 func (p *ReverseProxy) getErrorHandler() func(http.ResponseWriter, *http.Request, error) { 196 if p.ErrorHandler != nil { 197 return p.ErrorHandler 198 } 199 return p.defaultErrorHandler 200 } 201 202 // modifyResponse conditionally runs the optional ModifyResponse hook 203 // and reports whether the request should proceed. 204 func (p *ReverseProxy) modifyResponse(rw http.ResponseWriter, res *http.Response, req *http.Request) bool { 205 if p.ModifyResponse == nil { 206 return true 207 } 208 if err := p.ModifyResponse(res); err != nil { 209 res.Body.Close() 210 p.getErrorHandler()(rw, req, err) 211 return false 212 } 213 return true 214 } 215 216 func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 217 transport := p.Transport 218 if transport == nil { 219 transport = http.DefaultTransport 220 } 221 222 ctx := req.Context() 223 if cn, ok := rw.(http.CloseNotifier); ok { 224 var cancel context.CancelFunc 225 ctx, cancel = context.WithCancel(ctx) 226 defer cancel() 227 notifyChan := cn.CloseNotify() 228 go func() { 229 select { 230 case <-notifyChan: 231 cancel() 232 case <-ctx.Done(): 233 } 234 }() 235 } 236 237 outreq := req.Clone(ctx) 238 if req.ContentLength == 0 { 239 outreq.Body = nil // Issue 16036: nil Body for http.Transport retries 240 } 241 if outreq.Body != nil { 242 // Reading from the request body after returning from a handler is not 243 // allowed, and the RoundTrip goroutine that reads the Body can outlive 244 // this handler. This can lead to a crash if the handler panics (see 245 // Issue 46866). Although calling Close doesn't guarantee there isn't 246 // any Read in flight after the handle returns, in practice it's safe to 247 // read after closing it. 248 defer outreq.Body.Close() 249 } 250 if outreq.Header == nil { 251 outreq.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate 252 } 253 254 p.Director(outreq) 255 outreq.Close = false 256 257 reqUpType := upgradeType(outreq.Header) 258 if !ascii.IsPrint(reqUpType) { 259 p.getErrorHandler()(rw, req, fmt.Errorf("client tried to switch to invalid protocol %q", reqUpType)) 260 return 261 } 262 removeConnectionHeaders(outreq.Header) 263 264 // Remove hop-by-hop headers to the backend. Especially 265 // important is "Connection" because we want a persistent 266 // connection, regardless of what the client sent to us. 267 for _, h := range hopHeaders { 268 outreq.Header.Del(h) 269 } 270 271 // Issue 21096: tell backend applications that care about trailer support 272 // that we support trailers. (We do, but we don't go out of our way to 273 // advertise that unless the incoming client request thought it was worth 274 // mentioning.) Note that we look at req.Header, not outreq.Header, since 275 // the latter has passed through removeConnectionHeaders. 276 if httpguts.HeaderValuesContainsToken(req.Header["Te"], "trailers") { 277 outreq.Header.Set("Te", "trailers") 278 } 279 280 // After stripping all the hop-by-hop connection headers above, add back any 281 // necessary for protocol upgrades, such as for websockets. 282 if reqUpType != "" { 283 outreq.Header.Set("Connection", "Upgrade") 284 outreq.Header.Set("Upgrade", reqUpType) 285 } 286 287 if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { 288 // If we aren't the first proxy retain prior 289 // X-Forwarded-For information as a comma+space 290 // separated list and fold multiple headers into one. 291 prior, ok := outreq.Header["X-Forwarded-For"] 292 omit := ok && prior == nil // Issue 38079: nil now means don't populate the header 293 if len(prior) > 0 { 294 clientIP = strings.Join(prior, ", ") + ", " + clientIP 295 } 296 if !omit { 297 outreq.Header.Set("X-Forwarded-For", clientIP) 298 } 299 } 300 301 res, err := transport.RoundTrip(outreq) 302 if err != nil { 303 p.getErrorHandler()(rw, outreq, err) 304 return 305 } 306 307 // Deal with 101 Switching Protocols responses: (WebSocket, h2c, etc) 308 if res.StatusCode == http.StatusSwitchingProtocols { 309 if !p.modifyResponse(rw, res, outreq) { 310 return 311 } 312 p.handleUpgradeResponse(rw, outreq, res) 313 return 314 } 315 316 removeConnectionHeaders(res.Header) 317 318 for _, h := range hopHeaders { 319 res.Header.Del(h) 320 } 321 322 if !p.modifyResponse(rw, res, outreq) { 323 return 324 } 325 326 copyHeader(rw.Header(), res.Header) 327 328 // The "Trailer" header isn't included in the Transport's response, 329 // at least for *http.Transport. Build it up from Trailer. 330 announcedTrailers := len(res.Trailer) 331 if announcedTrailers > 0 { 332 trailerKeys := make([]string, 0, len(res.Trailer)) 333 for k := range res.Trailer { 334 trailerKeys = append(trailerKeys, k) 335 } 336 rw.Header().Add("Trailer", strings.Join(trailerKeys, ", ")) 337 } 338 339 rw.WriteHeader(res.StatusCode) 340 341 err = p.copyResponse(rw, res.Body, p.flushInterval(res)) 342 if err != nil { 343 defer res.Body.Close() 344 // Since we're streaming the response, if we run into an error all we can do 345 // is abort the request. Issue 23643: ReverseProxy should use ErrAbortHandler 346 // on read error while copying body. 347 if !shouldPanicOnCopyError(req) { 348 p.logf("suppressing panic for copyResponse error in test; copy error: %v", err) 349 return 350 } 351 panic(http.ErrAbortHandler) 352 } 353 res.Body.Close() // close now, instead of defer, to populate res.Trailer 354 355 if len(res.Trailer) > 0 { 356 // Force chunking if we saw a response trailer. 357 // This prevents net/http from calculating the length for short 358 // bodies and adding a Content-Length. 359 if fl, ok := rw.(http.Flusher); ok { 360 fl.Flush() 361 } 362 } 363 364 if len(res.Trailer) == announcedTrailers { 365 copyHeader(rw.Header(), res.Trailer) 366 return 367 } 368 369 for k, vv := range res.Trailer { 370 k = http.TrailerPrefix + k 371 for _, v := range vv { 372 rw.Header().Add(k, v) 373 } 374 } 375 } 376 377 var inOurTests bool // whether we're in our own tests 378 379 // shouldPanicOnCopyError reports whether the reverse proxy should 380 // panic with http.ErrAbortHandler. This is the right thing to do by 381 // default, but Go 1.10 and earlier did not, so existing unit tests 382 // weren't expecting panics. Only panic in our own tests, or when 383 // running under the HTTP server. 384 func shouldPanicOnCopyError(req *http.Request) bool { 385 if inOurTests { 386 // Our tests know to handle this panic. 387 return true 388 } 389 if req.Context().Value(http.ServerContextKey) != nil { 390 // We seem to be running under an HTTP server, so 391 // it'll recover the panic. 392 return true 393 } 394 // Otherwise act like Go 1.10 and earlier to not break 395 // existing tests. 396 return false 397 } 398 399 // removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h. 400 // See RFC 7230, section 6.1 401 func removeConnectionHeaders(h http.Header) { 402 for _, f := range h["Connection"] { 403 for _, sf := range strings.Split(f, ",") { 404 if sf = textproto.TrimString(sf); sf != "" { 405 h.Del(sf) 406 } 407 } 408 } 409 } 410 411 // flushInterval returns the p.FlushInterval value, conditionally 412 // overriding its value for a specific request/response. 413 func (p *ReverseProxy) flushInterval(res *http.Response) time.Duration { 414 resCT := res.Header.Get("Content-Type") 415 416 // For Server-Sent Events responses, flush immediately. 417 // The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream 418 if resCT == "text/event-stream" { 419 return -1 // negative means immediately 420 } 421 422 // We might have the case of streaming for which Content-Length might be unset. 423 if res.ContentLength == -1 { 424 return -1 425 } 426 427 return p.FlushInterval 428 } 429 430 func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error { 431 if flushInterval != 0 { 432 if wf, ok := dst.(writeFlusher); ok { 433 mlw := &maxLatencyWriter{ 434 dst: wf, 435 latency: flushInterval, 436 } 437 defer mlw.stop() 438 439 // set up initial timer so headers get flushed even if body writes are delayed 440 mlw.flushPending = true 441 mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush) 442 443 dst = mlw 444 } 445 } 446 447 var buf []byte 448 if p.BufferPool != nil { 449 buf = p.BufferPool.Get() 450 defer p.BufferPool.Put(buf) 451 } 452 _, err := p.copyBuffer(dst, src, buf) 453 return err 454 } 455 456 // copyBuffer returns any write errors or non-EOF read errors, and the amount 457 // of bytes written. 458 func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) { 459 if len(buf) == 0 { 460 buf = make([]byte, 32*1024) 461 } 462 var written int64 463 for { 464 nr, rerr := src.Read(buf) 465 if rerr != nil && rerr != io.EOF && rerr != context.Canceled { 466 p.logf("httputil: ReverseProxy read error during body copy: %v", rerr) 467 } 468 if nr > 0 { 469 nw, werr := dst.Write(buf[:nr]) 470 if nw > 0 { 471 written += int64(nw) 472 } 473 if werr != nil { 474 return written, werr 475 } 476 if nr != nw { 477 return written, io.ErrShortWrite 478 } 479 } 480 if rerr != nil { 481 if rerr == io.EOF { 482 rerr = nil 483 } 484 return written, rerr 485 } 486 } 487 } 488 489 func (p *ReverseProxy) logf(format string, args ...interface{}) { 490 if p.ErrorLog != nil { 491 p.ErrorLog.Printf(format, args...) 492 } else { 493 log.Printf(format, args...) 494 } 495 } 496 497 type writeFlusher interface { 498 io.Writer 499 http.Flusher 500 } 501 502 type maxLatencyWriter struct { 503 dst writeFlusher 504 latency time.Duration // non-zero; negative means to flush immediately 505 506 mu sync.Mutex // protects t, flushPending, and dst.Flush 507 t *time.Timer 508 flushPending bool 509 } 510 511 func (m *maxLatencyWriter) Write(p []byte) (n int, err error) { 512 m.mu.Lock() 513 defer m.mu.Unlock() 514 n, err = m.dst.Write(p) 515 if m.latency < 0 { 516 m.dst.Flush() 517 return 518 } 519 if m.flushPending { 520 return 521 } 522 if m.t == nil { 523 m.t = time.AfterFunc(m.latency, m.delayedFlush) 524 } else { 525 m.t.Reset(m.latency) 526 } 527 m.flushPending = true 528 return 529 } 530 531 func (m *maxLatencyWriter) delayedFlush() { 532 m.mu.Lock() 533 defer m.mu.Unlock() 534 if !m.flushPending { // if stop was called but AfterFunc already started this goroutine 535 return 536 } 537 m.dst.Flush() 538 m.flushPending = false 539 } 540 541 func (m *maxLatencyWriter) stop() { 542 m.mu.Lock() 543 defer m.mu.Unlock() 544 m.flushPending = false 545 if m.t != nil { 546 m.t.Stop() 547 } 548 } 549 550 func upgradeType(h http.Header) string { 551 if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") { 552 return "" 553 } 554 return h.Get("Upgrade") 555 } 556 557 func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.Request, res *http.Response) { 558 reqUpType := upgradeType(req.Header) 559 resUpType := upgradeType(res.Header) 560 if !ascii.IsPrint(resUpType) { // We know reqUpType is ASCII, it's checked by the caller. 561 p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch to invalid protocol %q", resUpType)) 562 } 563 if !ascii.EqualFold(reqUpType, resUpType) { 564 p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType)) 565 return 566 } 567 568 hj, ok := rw.(http.Hijacker) 569 if !ok { 570 p.getErrorHandler()(rw, req, fmt.Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw)) 571 return 572 } 573 backConn, ok := res.Body.(io.ReadWriteCloser) 574 if !ok { 575 p.getErrorHandler()(rw, req, fmt.Errorf("internal error: 101 switching protocols response with non-writable body")) 576 return 577 } 578 579 backConnCloseCh := make(chan bool) 580 go func() { 581 // Ensure that the cancellation of a request closes the backend. 582 // See issue https://golang.org/issue/35559. 583 select { 584 case <-req.Context().Done(): 585 case <-backConnCloseCh: 586 } 587 backConn.Close() 588 }() 589 590 defer close(backConnCloseCh) 591 592 conn, brw, err := hj.Hijack() 593 if err != nil { 594 p.getErrorHandler()(rw, req, fmt.Errorf("Hijack failed on protocol switch: %v", err)) 595 return 596 } 597 defer conn.Close() 598 599 copyHeader(rw.Header(), res.Header) 600 601 res.Header = rw.Header() 602 res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above 603 if err := res.Write(brw); err != nil { 604 p.getErrorHandler()(rw, req, fmt.Errorf("response write: %v", err)) 605 return 606 } 607 if err := brw.Flush(); err != nil { 608 p.getErrorHandler()(rw, req, fmt.Errorf("response flush: %v", err)) 609 return 610 } 611 errc := make(chan error, 1) 612 spc := switchProtocolCopier{user: conn, backend: backConn} 613 go spc.copyToBackend(errc) 614 go spc.copyFromBackend(errc) 615 <-errc 616 return 617 } 618 619 // switchProtocolCopier exists so goroutines proxying data back and 620 // forth have nice names in stacks. 621 type switchProtocolCopier struct { 622 user, backend io.ReadWriter 623 } 624 625 func (c switchProtocolCopier) copyFromBackend(errc chan<- error) { 626 _, err := io.Copy(c.user, c.backend) 627 errc <- err 628 } 629 630 func (c switchProtocolCopier) copyToBackend(errc chan<- error) { 631 _, err := io.Copy(c.backend, c.user) 632 errc <- err 633 }