Răsfoiți Sursa

Fix: return from AwaitClosed when MeekConn is closed

Avoids hanging MeekConn.Close in cases where the RoundTripper
fails to comply with https://golang.org/pkg/net/http/#RoundTripper
and doesn't close the body.
Rod Hynes 8 ani în urmă
părinte
comite
024860f841
1 a modificat fișierele cu 26 adăugiri și 9 ștergeri
  1. 26 9
      psiphon/meekConn.go

+ 26 - 9
psiphon/meekConn.go

@@ -724,14 +724,19 @@ func (meek *MeekConn) relay() {
 // RoundTrip has called Close and will no longer use the buffer.
 // RoundTrip has called Close and will no longer use the buffer.
 // See: https://golang.org/pkg/net/http/#RoundTripper
 // See: https://golang.org/pkg/net/http/#RoundTripper
 type readCloseSignaller struct {
 type readCloseSignaller struct {
-	reader io.Reader
-	closed chan struct{}
+	context context.Context
+	reader  io.Reader
+	closed  chan struct{}
 }
 }
 
 
-func NewReadCloseSignaller(reader io.Reader) *readCloseSignaller {
+func NewReadCloseSignaller(
+	context context.Context,
+	reader io.Reader) *readCloseSignaller {
+
 	return &readCloseSignaller{
 	return &readCloseSignaller{
-		reader: reader,
-		closed: make(chan struct{}, 1),
+		context: context,
+		reader:  reader,
+		closed:  make(chan struct{}, 1),
 	}
 	}
 }
 }
 
 
@@ -747,8 +752,13 @@ func (r *readCloseSignaller) Close() error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *readCloseSignaller) AwaitClosed() {
-	<-r.closed
+func (r *readCloseSignaller) AwaitClosed() bool {
+	select {
+	case <-r.context.Done():
+	case <-r.closed:
+		return true
+	}
+	return false
 }
 }
 
 
 // roundTrip configures and makes the actual HTTP POST request
 // roundTrip configures and makes the actual HTTP POST request
@@ -817,7 +827,7 @@ func (meek *MeekConn) roundTrip(sendBuffer *bytes.Buffer) (int64, error) {
 			// still reading the current round trip response. signaller provides
 			// still reading the current round trip response. signaller provides
 			// the hook for awaiting RoundTrip's call to Close.
 			// the hook for awaiting RoundTrip's call to Close.
 
 
-			signaller = NewReadCloseSignaller(bytes.NewReader(sendBuffer.Bytes()))
+			signaller = NewReadCloseSignaller(meek.runContext, bytes.NewReader(sendBuffer.Bytes()))
 			requestBody = signaller
 			requestBody = signaller
 			contentLength = sendBuffer.Len()
 			contentLength = sendBuffer.Len()
 		}
 		}
@@ -865,7 +875,14 @@ func (meek *MeekConn) roundTrip(sendBuffer *bytes.Buffer) (int64, error) {
 		// subsequently replace sendBuffer in both the success and
 		// subsequently replace sendBuffer in both the success and
 		// error cases.
 		// error cases.
 		if signaller != nil {
 		if signaller != nil {
-			signaller.AwaitClosed()
+			if !signaller.AwaitClosed() {
+				// AwaitClosed encountered Done(). Abort immediately. Do not
+				// replace sendBuffer, as we cannot be certain RoundTrip is
+				// done with it. MeekConn.Write will exit on Done and not hang
+				// awaiting sendBuffer.
+				sendBuffer = nil
+				return 0, common.ContextError(errors.New("meek connection has closed"))
+			}
 		}
 		}
 
 
 		if err != nil {
 		if err != nil {