Пример #1
0
// requestHandler will handle a single web request.
func (cons *Http) requestHandler(resp http.ResponseWriter, req *http.Request) {
	if cons.withHeaders {
		// Read the whole package
		requestBuffer := bytes.NewBuffer(nil)
		if err := req.Write(requestBuffer); err != nil {
			resp.WriteHeader(http.StatusBadRequest)
			return // ### return, missing body or bad write ###
		}

		cons.Enqueue(requestBuffer.Bytes(), atomic.AddUint64(&cons.sequence, 1))
		resp.WriteHeader(http.StatusCreated)
	} else {
		// Read only the message body
		if req.Body == nil {
			resp.WriteHeader(http.StatusBadRequest)
			return // ### return, missing body ###
		}

		body := make([]byte, req.ContentLength)
		length, err := req.Body.Read(body)
		if err != nil {
			resp.WriteHeader(http.StatusBadRequest)
			return // ### return, missing body or bad write ###
		}

		cons.Enqueue(body[:length], atomic.AddUint64(&cons.sequence, 1))
		resp.WriteHeader(http.StatusCreated)
	}
}
Пример #2
0
// EncodeRequestToEvent will write the request out in wire protocol and encode it to b64 and store it in an Event object
func EncodeRequestToEvent(r *http.Request) string {
	var asBytes bytes.Buffer
	r.Write(&asBytes)

	uEnc := b64.StdEncoding.EncodeToString(asBytes.Bytes())
	return uEnc
}
Пример #3
0
// RoundTrip executes the Request and upgrades it. After a successful upgrade,
// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded
// connection.
func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	// TODO what's the best way to clone the request?
	r := *req
	req = &r
	req.Header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
	req.Header.Add(httpstream.HeaderUpgrade, HeaderSpdy31)

	conn, err := s.dial(req)
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		return nil, err
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	s.conn = conn

	return resp, nil
}
Пример #4
0
func send(req *http.Request) (resp *http.Response, err error) {
	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":http"
	}
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}
	reader := bufio.NewReader(conn)
	resp, err = http.ReadResponse(reader, req)
	if err != nil {
		conn.Close()
		return nil, err
	}
	r := io.Reader(reader)
	if v := resp.Header["Content-Length"]; v != nil {
		n, err := strconv.Atoi(v[0])
		if err != nil {
			return nil, &badStringError{"invalid Content-Length", v[0]}
		}
		v := int64(n)
		r = io.LimitReader(r, v)
	}
	resp.Body = readClose{r, conn}
	return
}
Пример #5
0
func send(url *url.URL, addr string) result {
	var req http.Request
	req.URL = url

	now := time.Now()
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return result{0, err}
	}
	defer conn.Close()

	err = req.Write(conn)
	if err != nil {
		return result{0, err}
	}

	ch := make(chan respErr, 1)
	go func() {
		reader := bufio.NewReader(conn)
		response, err := http.ReadResponse(reader, &req)
		ch <- respErr{response, err}
	}()

	var res result

	select {
	case <-time.After(time.Duration(*timeout * 1e6)):
		res = result{time.Now().Sub(now), errors.New("Timeout!")}
	case rerr := <-ch:
		res = result{time.Now().Sub(now), rerr.err}
		rerr.resp.Body.Close()
	}

	return res
}
Пример #6
0
func whoamI(w http.ResponseWriter, req *http.Request) {
	u, _ := url.Parse(req.URL.String())
	queryParams := u.Query()
	wait := queryParams.Get("wait")
	if len(wait) > 0 {
		duration, err := time.ParseDuration(wait)
		if err == nil {
			time.Sleep(duration)
		}
	}
	hostname, _ := os.Hostname()
	fmt.Fprintln(w, "Hostname:", hostname)
	ifaces, _ := net.Interfaces()
	for _, i := range ifaces {
		addrs, _ := i.Addrs()
		// handle err
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			fmt.Fprintln(w, "IP:", ip)
		}
	}
	req.Write(w)
}
Пример #7
0
func (h *HTTPProxy) wsProxy(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)

	if !ok {
		h.clientErr(w, r, ErrInvalidAction)
		return
	}

	client, _, err := hj.Hijack()
	if err != nil {
		h.clientErr(w, r, err)
		return
	}
	defer client.Close()

	server, err := net.Dial("tcp", r.URL.Host)
	if err != nil {
		h.clientErr(w, r, err)
		return
	}
	defer server.Close()

	err = r.Write(server)
	if err != nil {
		h.clientErr(w, r, err)
		return
	}

	go passBytes(client, server)
}
Пример #8
0
func ReverseProxy(w http.ResponseWriter, req *http.Request) {
	var err error
	var inConn, outConn net.Conn

	log.Printf("%s %v", req.Method, req.URL)

	if inConn, err = net.Dial("tcp", webapp); err != nil {
		SiteIsDown(w)
		return
	}
	if outConn, _, err = w.(http.Hijacker).Hijack(); err != nil {
		// log.Printf("Cannot hijack connection: %s", err)
		inConn.Close()
		return
	}

	go func() {
		io.Copy(outConn, inConn)
		outConn.Close()
	}()
	go func() {
		req.Header["X-Forwarded-For"] = []string{req.RemoteAddr}
		req.Write(inConn)
		io.Copy(inConn, outConn)
		inConn.Close()
	}()
	return
}
Пример #9
0
func (this Tunnel) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	conn, err := this.session.Open()
	if err != nil {
		log.Println(err.Error())
		response.Write([]byte("<html><body>" + err.Error() + "</body></html>"))
		return
	}
	defer conn.Close()

	if err := request.Write(conn); err != nil {
		log.Println(err.Error())
		return
	}

	tunnelResponse, err := http.ReadResponse(bufio.NewReader(conn), request)
	if err != nil {
		response.Write([]byte("<html><body>" + err.Error() + "</body></html>"))
		return
	}

	for header, values := range tunnelResponse.Header {
		for _, value := range values {
			response.Header().Add(header, value)
		}
	}

	response.WriteHeader(tunnelResponse.StatusCode)
	if tunnelResponse.Body != nil {
		io.Copy(response, tunnelResponse.Body)
	}
}
Пример #10
0
func (t *logTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	var buf bytes.Buffer

	os.Stdout.Write([]byte("\n[request]\n"))
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&readButCopy{req.Body, &buf})
	}
	req.Write(os.Stdout)
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&buf)
	}
	os.Stdout.Write([]byte("\n[/request]\n"))

	res, err := t.rt.RoundTrip(req)

	fmt.Printf("[response]\n")
	if err != nil {
		fmt.Printf("ERROR: %v", err)
	} else {
		body := res.Body
		res.Body = nil
		res.Write(os.Stdout)
		if body != nil {
			res.Body = ioutil.NopCloser(&echoAsRead{body})
		}
	}

	return res, err
}
Пример #11
0
func send(req *http.Request) (resp *http.Response, err error) {
	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":http"
	}
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}

	reader := bufio.NewReader(conn)
	resp, err = http.ReadResponse(reader, req)
	if err != nil {
		conn.Close()
		return nil, err
	}

	r := io.Reader(reader)
	if n := resp.ContentLength; n != -1 {
		r = io.LimitReader(r, n)
	}
	resp.Body = readClose{r, conn}

	return
}
Пример #12
0
func (st *supervisorTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL == nil {
		return nil, errors.New("unix: nil Request.URL")
	}

	if req.Header == nil {
		return nil, errors.New("unix: nil Request.Header")
	}

	if req.URL.Scheme != "unix" {
		panic("unix: unsupported protocol scheme")
	}

	sock, err := net.Dial("unix", req.URL.Path)
	if err != nil {
		return nil, err
	}
	defer sock.Close()

	//create shallow copy of request object
	newReq := new(http.Request)
	*newReq = *req

	newReq.URL = supervisorURL
	newReq.Write(sock)

	return http.ReadResponse(bufio.NewReader(sock), req)
}
Пример #13
0
func (p *Proxy) connect(req *http.Request) (*http.Response, net.Conn, error) {
	if p.proxyURL != nil {
		log.Debugf("martian: CONNECT with downstream proxy: %s", p.proxyURL.Host)

		conn, err := net.Dial("tcp", p.proxyURL.Host)
		if err != nil {
			return nil, nil, err
		}
		pbw := bufio.NewWriter(conn)
		pbr := bufio.NewReader(conn)

		req.Write(pbw)
		pbw.Flush()

		res, err := http.ReadResponse(pbr, req)
		if err != nil {
			return nil, nil, err
		}

		return res, conn, nil
	}

	log.Debugf("martian: CONNECT to host directly: %s", req.URL.Host)

	conn, err := net.Dial("tcp", req.URL.Host)
	if err != nil {
		return nil, nil, err
	}

	return proxyutil.NewResponse(200, nil, req), conn, nil
}
Пример #14
0
func (t *transport) UpgradeHTTP(req *http.Request, l log15.Logger) (*http.Response, net.Conn, error) {
	stickyBackend := t.getStickyBackend(req)
	backends := t.getOrderedBackends(stickyBackend, req)
	upconn, addr, err := dialTCP(context.Background(), l, backends)
	if err != nil {
		status := http.StatusServiceUnavailable
		if err == errTimeout {
			status = http.StatusGatewayTimeout
		}
		l.Error("dial failed", "status", status, "num_backends", len(backends))
		return nil, nil, err
	}
	conn := &streamConn{bufio.NewReader(upconn), upconn}
	req.URL.Host = addr

	if err := req.Write(conn); err != nil {
		conn.Close()
		l.Error("error writing request", "err", err, "backend", addr)
		return nil, nil, err
	}
	res, err := http.ReadResponse(conn.Reader, req)
	if err != nil {
		conn.Close()
		l.Error("error reading response", "err", err, "backend", addr)
		return nil, nil, err
	}
	t.setStickyBackend(res, stickyBackend)
	return res, conn, nil
}
Пример #15
0
func (p *WebsocketReverseProxy) hijackWebsocket(rw http.ResponseWriter, req *http.Request) {
	highjacker, ok := rw.(http.Hijacker)

	if !ok {
		http.Error(rw, "webserver doesn't support hijacking", http.StatusInternalServerError)
		return
	}

	conn, bufrw, err := highjacker.Hijack()
	defer conn.Close()

	conn2, err := net.Dial("tcp", p.Upstream)
	if err != nil {
		log.Printf("couldn't connect to backend websocket server: %v", err)
		http.Error(rw, "couldn't connect to backend server", http.StatusServiceUnavailable)
		return
	}
	defer conn2.Close()

	err = req.Write(conn2)
	if err != nil {
		log.Printf("writing WebSocket request to backend server failed: %v", err)
		return
	}

	bufferedBidirCopy(conn, bufrw, conn2, bufio.NewReadWriter(bufio.NewReader(conn2), bufio.NewWriter(conn2)))
}
Пример #16
0
func plumbWebsocket(w http.ResponseWriter, r *http.Request, route **Route) error {
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
		return errors.New("no-hijack")
	}
	conn, bufrw, err := hj.Hijack()
	conn2, err := net.Dial("tcp", r.URL.Host)
	if err != nil {
		http.Error(w, "couldn't connect to backend server", http.StatusServiceUnavailable)
		return errors.New("dead-backend")
	}
	err = r.Write(conn2)
	if err != nil {
		log.Warning("writing WebSocket request to backend server failed: %v", err)
		return errors.New("dead-backend")
	}
	CopyBidir(conn, bufrw, conn2, bufio.NewReadWriter(bufio.NewReader(conn2), bufio.NewWriter(conn2)), route)
	err = conn.Close()

	if err != nil {
		log.Warning("Could not close stream", err)
	}
	err = conn2.Close()
	if err != nil {
		log.Warning("Could not close stream", err)
	}
	return nil
}
Пример #17
0
func (p *WebsocketProxy) Proxy(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		log.Println("hijack assertion failed", r.Host, r.URL.Path)
		p.handler.ServeHTTP(w, r) // last-ditch effort as plain http
		return
	}
	conn, rw, err := hj.Hijack()
	if err != nil {
		log.Println("hijack failed", r.Host, r.URL.Path, err)
		p.handler.ServeHTTP(w, r) // last-ditch effort as plain http
		return
	}
	defer conn.Close()
	rw.Flush()

	wrapreq := new(http.Request)
	wrapreq.Proto = "HTTP/1.1"
	wrapreq.ProtoMajor, wrapreq.ProtoMinor = 1, 1
	wrapreq.Method = "WEBSOCKET"
	wrapreq.Host = r.Host
	const dummy = "/"
	wrapreq.URL = &url.URL{Path: dummy}
	var buf bytes.Buffer
	r.Write(&buf)
	wrapreq.Body = ioutil.NopCloser(io.MultiReader(&buf, conn))
	resp, err := p.transport.RoundTrip(wrapreq)
	if err != nil || resp.StatusCode != 200 {
		io.WriteString(conn, "HTTP/1.0 503 Gateway Failed\r\n")
		io.WriteString(conn, "Connection: close\r\n\r\n")
		return
	}
	defer resp.Body.Close()
	io.Copy(conn, resp.Body)
}
Пример #18
0
func (s *SSLBRequest) HijackWebSocket(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)

	if !ok {
		log.Println("Error: Webserver doesn't support hijacking")
		http.Error(w, "Internal Error", http.StatusInternalServerError)
		return
	}

	frontendConn, buffer, err := hj.Hijack()
	defer frontendConn.Close()

	URL := &url.URL{}
	UrlParsed, _ := URL.Parse(s.Backend.BackendConfig.Address)

	backendConn, err := net.Dial("tcp", UrlParsed.Host)
	if err != nil {
		log.Println("Error: Couldn't connect to backend server")
		http.Error(w, "Internal Error", http.StatusServiceUnavailable)
		return
	}
	defer backendConn.Close()

	err = r.Write(backendConn)
	if err != nil {
		log.Printf("Writing WebSocket request to backend server failed: %v", err)
		return
	}

	copyBidir(frontendConn, buffer, backendConn,
		bufio.NewReadWriter(bufio.NewReader(backendConn), bufio.NewWriter(backendConn)))
}
Пример #19
0
// proxyWebsocket copies data between websocket client and server until one side
// closes the connection.  (ReverseProxy doesn't work with websocket requests.)
func proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) {
	d, err := net.Dial("tcp", host)
	if err != nil {
		http.Error(w, "Error contacting backend server.", 500)
		revel.ERROR.Printf("Error dialing websocket backend %s: %v", host, err)
		return
	}
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "Not a hijacker?", 500)
		return
	}
	nc, _, err := hj.Hijack()
	if err != nil {
		revel.ERROR.Printf("Hijack error: %v", err)
		return
	}
	defer nc.Close()
	defer d.Close()

	err = r.Write(d)
	if err != nil {
		revel.ERROR.Printf("Error copying request to target: %v", err)
		return
	}

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}
	go cp(d, nc)
	go cp(nc, d)
	<-errc
}
Пример #20
0
func (t *HTTPUnixTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL == nil {
		return nil, errors.New("http+unix: nil Request.URL")
	}
	if req.URL.Scheme != Scheme {
		return nil, errors.New("unsupported protocol scheme: " + req.URL.Scheme)
	}
	if req.URL.Host == "" {
		return nil, errors.New("http+unix: no Host in request URL")
	}
	t.mu.Lock()
	path, ok := t.loc[req.URL.Host]
	t.mu.Unlock()
	if !ok {
		return nil, errors.New("unknown location: " + req.Host)
	}

	c, err := net.DialTimeout("unix", path, t.DialTimeout)
	if err != nil {
		return nil, err
	}
	r := bufio.NewReader(c)
	if t.RequestTimeout > 0 {
		c.SetWriteDeadline(time.Now().Add(t.RequestTimeout))
	}
	if err := req.Write(c); err != nil {
		return nil, err
	}
	if t.ResponseHeaderTimeout > 0 {
		c.SetReadDeadline(time.Now().Add(t.ResponseHeaderTimeout))
	}
	resp, err := http.ReadResponse(r, req)
	return resp, err
}
Пример #21
0
func setupServerAndHandshake(t *testing.T) (h *Handler, client net.Conn) {
	h = NewHandler()
	http.Handle("/myconn", h)
	go http.ListenAndServe(":8080", nil)
	var (
		req *http.Request
		err error
	)
	req, err = http.NewRequest("GET", "/myconn", nil)
	req.Header.Set("Upgrade", "websocket")
	req.Header.Set("Connection", "Upgrade")
	req.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
	req.Header.Set("Origin", "http://localhost")
	req.Header.Set("Sec-WebSocket-Version", "13")
	client, err = net.Dial("tcp", "localhost:8080")
	if err != nil {
		t.Error("Couldn't open TCP connection")
		t.FailNow()
	}
	err = req.Write(client)
	if err != nil {
		t.Error("Could not write request")
		t.FailNow()
	}
	_, err = io.CopyN(ioutil.Discard, client, 194) // Fixed handshake length
	if err != nil {
		t.Error("Could not discard server handshake")
		t.FailNow()
	}
	return
}
Пример #22
0
func (pc *PersistConn) WriteRequest(req *http.Request, opt *RequestOptions) (err error) {
	pc.lk.Lock()
	pc.numExpectedResponses++
	pc.lk.Unlock()

	// Separate started variable because pc.lastUsed may be updated concurrently.
	var started time.Time = time.Now()
	pc.lastUsed = started

	if opt == nil || opt.WriteTimeout == 0 {
		err = req.Write(pc.bw)
	} else {
		ch := make(chan error, 0)
		go func() {
			ch <- req.Write(pc.bw)
		}()
		select {
		case err = <-ch:
		case <-time.After(opt.WriteTimeout):
			err = &Error{str: "WriteRequest timeout", timeout: true, temporary: true}
		}
	}
	if opt != nil && opt.Stat != nil {
		opt.Stat.WriteTime = time.Now().Sub(started)
	}
	if err != nil {
		pc.Close()
		return
	}
	pc.bw.Flush()

	pc.reqch <- requestAndOptions{req, opt}

	return err
}
Пример #23
0
Файл: proxy.go Проект: nlf/dlite
func (p *Proxy) proxy(w http.ResponseWriter, r *http.Request) {
	if p.daemon.VM == nil {
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte("The virtual machine has not been started"))
		return
	}

	addr, err := p.daemon.VM.Address()
	if err != nil {
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte("Unable to locate the virtual machine"))
		return
	}

	backend, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte("Unable to connect to the virtual machine"))
		return
	}
	defer backend.Close()

	r.URL.Scheme = "http"
	r.URL.Host = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)

	hijacker, ok := w.(http.Hijacker)
	if !ok {
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte("Unable to create hijacker"))
		return
	}

	conn, _, err := hijacker.Hijack()
	if err != nil {
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte("Unable to hijack connection"))
		return
	}

	r.Write(backend)
	finished := make(chan error, 1)

	go func(backend *net.TCPConn, conn net.Conn, finished chan error) {
		buf := make([]byte, 8092)
		_, err := io.CopyBuffer(backend, conn, buf)
		backend.CloseWrite()
		finished <- err
	}(backend, conn, finished)

	go func(backend *net.TCPConn, conn net.Conn, finished chan error) {
		buf := make([]byte, 8092)
		_, err := io.CopyBuffer(conn, backend, buf)
		conn.Close()
		finished <- err
	}(backend, conn, finished)

	<-finished
	<-finished
}
Пример #24
0
func rootHandler(w http.ResponseWriter, r *http.Request) {
	rw := types.NewRequestWriter()
	var writer io.Writer = rw
	r.Write(writer)
	output := fmt.Sprintf("Root Handler \n\nRequest = %v  \n\n", rw.Data())

	w.Write([]byte(output))
}
Пример #25
0
func httpToBLIPRequest(httpReq *http.Request) *Message {
	req := NewRequest()
	req.SetProfile("HTTP")
	var body bytes.Buffer
	httpReq.Write(&body)
	req.SetBody(body.Bytes())
	return req
}
Пример #26
0
func getRequest(req *http.Request, params ...string) string {
	buf := new(bytes.Buffer)
	err := req.Write(buf)
	if err != nil {
		return ""
	}
	return buf.String()
}
Пример #27
0
func preview(w http.ResponseWriter, r *http.Request) {
	r.Write(w)
	env, err := shPipe("env", "sort")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprintln(w, env+"\n")
	fmt.Fprintln(w, "Open sourced by YP LLC. http://engineering.yp.com")
}
Пример #28
0
func (h *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	r.Write(os.Stdout)
	for _, route := range routes {
		if route.pattern.MatchString(r.URL.Path) {
			route.handler(h.Repo, w, r)
			break
		}
	}
}
Пример #29
0
func SendTo(req *http.Request, conn net.Conn) (e error) {
	// Write our request struct to the connection in http wire format.
	e = req.Write(conn)
	if e != nil {
		fmt.Println("Error writing request:", e)
	}
	fmt.Printf("Wrote request\n")
	return
}
Пример #30
0
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !isWebSocket(r) {
		// the usual path
		p.Default.ServeHTTP(w, r)
		return
	}

	// the websocket path
	req := new(http.Request)
	*req = *r
	p.Director(req)
	host := req.URL.Host

	if len(host) == 0 {
		http.Error(w, "invalid host", 500)
		return
	}

	// connect to the backend host
	conn, err := net.Dial("tcp", host)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// hijack the connection
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "failed to connect", 500)
		return
	}

	nc, _, err := hj.Hijack()
	if err != nil {
		return
	}

	defer nc.Close()
	defer conn.Close()

	if err = req.Write(conn); err != nil {
		return
	}

	errCh := make(chan error, 2)

	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errCh <- err
	}

	go cp(conn, nc)
	go cp(nc, conn)

	<-errCh
}