github.com/alejandroesc/spdy@v0.0.0-20200317064415-01a02f0eb389/proxy.go (about) 1 // Copyright 2014 Jamie Hall. 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 package spdy 6 7 import ( 8 "crypto/tls" 9 "fmt" 10 "net" 11 "net/http" 12 "net/http/httputil" 13 "net/url" 14 15 "github.com/SlyMarbo/spdy/common" 16 ) 17 18 // Connect is used to perform connection 19 // reversal where the client (who is normally 20 // behind a NAT of some kind) connects to a server 21 // on the internet. The connection is then reversed 22 // so that the 'server' sends requests to the 'client'. 23 // See ConnectAndServe() for a blocking version of this 24 func Connect(addr string, config *tls.Config, srv *http.Server) (Conn, error) { 25 if config == nil { 26 config = new(tls.Config) 27 } 28 if srv == nil { 29 srv = &http.Server{Handler: http.DefaultServeMux} 30 } 31 AddSPDY(srv) 32 33 u, err := url.Parse(addr) 34 if err != nil { 35 return nil, err 36 } 37 38 var conn net.Conn 39 40 conn, err = tls.Dial("tcp", u.Host, config) 41 if err != nil { 42 return nil, err 43 } 44 45 req, err := http.NewRequest("GET", addr, nil) 46 if err != nil { 47 return nil, err 48 } 49 50 client := httputil.NewClientConn(conn, nil) 51 err = client.Write(req) 52 if err != nil { 53 return nil, err 54 } 55 56 res, err := client.Read(req) 57 if err != nil && err != httputil.ErrPersistEOF { 58 fmt.Println(res) 59 return nil, err 60 } 61 62 if res.StatusCode != http.StatusOK { 63 log.Printf("Proxy responded with status code %d\n", res.StatusCode) 64 return nil, common.ErrConnectFail 65 } 66 67 conn, _ = client.Hijack() 68 69 server, err := NewServerConn(conn, srv, 3, 1) 70 if err != nil { 71 return nil, err 72 } 73 74 return server, nil 75 } 76 77 // ConnectAndServe is used to perform connection 78 // reversal. (See Connect() for more details.) 79 // 80 // This works very similarly to ListenAndServeTLS, 81 // except that addr and config are used to connect 82 // to the client. If srv is nil, a new http.Server 83 // is used, with http.DefaultServeMux as the handler. 84 func ConnectAndServe(addr string, config *tls.Config, srv *http.Server) error { 85 server, err := Connect(addr, config, srv) 86 if err != nil { 87 return err 88 } 89 90 return server.Run() 91 } 92 93 type ProxyConnHandler interface { 94 ProxyConnHandle(Conn) 95 } 96 97 type ProxyConnHandlerFunc func(Conn) 98 99 func (p ProxyConnHandlerFunc) ProxyConnHandle(c Conn) { 100 p(c) 101 } 102 103 type proxyHandler struct { 104 ProxyConnHandler 105 } 106 107 func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 108 r.Body.Close() 109 110 conn, _, err := w.(http.Hijacker).Hijack() 111 if err != nil { 112 log.Println("Failed to hijack connection in ProxyConnections.", err) 113 return 114 } 115 116 defer conn.Close() 117 118 if _, ok := conn.(*tls.Conn); !ok { 119 log.Println("Recieved a non-TLS connection in ProxyConnections.") 120 return 121 } 122 123 // Send the connection accepted response. 124 res := new(http.Response) 125 res.Status = "200 Connection Established" 126 res.StatusCode = http.StatusOK 127 res.Proto = "HTTP/1.1" 128 res.ProtoMajor = 1 129 res.ProtoMinor = 1 130 if err = res.Write(conn); err != nil { 131 log.Println("Failed to send connection established message in ProxyConnections.", err) 132 return 133 } 134 135 client, err := NewClientConn(conn, nil, 3, 1) 136 if err != nil { 137 log.Println("Error creating SPDY connection in ProxyConnections.", err) 138 return 139 } 140 141 go client.Run() 142 143 // Call user code. 144 p.ProxyConnHandle(client) 145 146 client.Close() 147 } 148 149 // ProxyConnections is used with ConnectAndServe in connection- 150 // reversing proxies. This returns an http.Handler which will call 151 // handler each time a client connects. The call is treated as 152 // an event loop and the connection may be terminated if the call 153 // returns. The returned Handler should then be used in a normal 154 // HTTP server, like the following: 155 // 156 // package main 157 // 158 // import ( 159 // "net/http" 160 // 161 // "github.com/SlyMarbo/spdy" 162 // ) 163 // 164 // func handleProxy(conn spdy.Conn) { 165 // // make requests... 166 // } 167 // 168 // func main() { 169 // handler := spdy.ProxyConnHandlerFunc(handleProxy) 170 // http.Handle("/", spdy.ProxyConnections(handler)) 171 // http.ListenAndServeTLS(":80", "cert.pem", "key.pem", nil) 172 // } 173 // 174 // Use Conn.Request to make requests to the client and Conn.Conn 175 // to access the underlying connection for further details like 176 // the client's address. 177 func ProxyConnections(handler ProxyConnHandler) http.Handler { 178 return proxyHandler{handler} 179 }