func TestStartInitiating(t *testing.T) { t.Parallel() var want, got []byte var props stream.Properties var err error pipe1, pipe2 := net.Pipe() tcpTsp := NewTCP(pipe1, stream.Initiating, nil, false) props.Header = stream.Header{} // Should get error when starting stream with empty header _, err = tcpTsp.Start(props) if err != stream.ErrHeaderNotSet { t.Error("Should get error when starting stream with empty header.") t.Errorf("\nWant:%s\nGot :%s", stream.ErrHeaderNotSet, err) } // Should write header to underlying connection hdr := stream.Header{To: "foo", From: "bar"} want = hdr.WriteBytes() props.Header = hdr got = make([]byte, len(want)) go func() { _, err := pipe2.Read(got) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } }() _, err = tcpTsp.Start(props) if err != nil { t.Errorf("Unexpected error while starting stream: %s", err) } if !bytes.Equal(want, got) { t.Error("Should write header to underlying connection") t.Errorf("\nWant:%s\nGot :%s", want, got) } }
// Start starts or restarts the stream. // // In recieving mode, the transport will wait to recieve a stream header // from the initiating entity, then sends its own header and the stream // features. This transport will add the starttls feature under certain // conditions. func (t *TCP) Start(props stream.Properties) (stream.Properties, error) { if t.mode == stream.Initiating { if props.Header == (stream.Header{}) { return props, stream.ErrHeaderNotSet } b := props.Header.WriteBytes() _, err := t.Write(b) return props, err } // We're in recieving mode if props.Domain == "" { return props, stream.ErrDomainNotSet } var el element.Element var h stream.Header var err error el, err = t.Next() if err != nil { return props, err } h, err = stream.NewHeader(el) if err != nil { return props, err } h.ID = genStreamID() if h.To != props.Domain { h.To, h.From = h.From, props.Domain b := h.WriteBytes() t.Write(b) err = t.WriteElement(element.StreamError.HostUnknown) props.Status = stream.Closed return props, err } h.From, h.To = props.Domain, h.From if props.To != "" { h.To = props.To } props.Header = h b := props.Header.WriteBytes() _, err = t.Write(b) if err != nil { return props, err } ftrs := element.StreamFeatures for _, f := range props.Features { ftrs = ftrs.AddChild(f) } // Stream features if t.conf != nil && !t.secure { tlsFeature := element.StartTLS if t.tlsRequired { tlsFeature = tlsFeature.AddChild(element.Required) } // Overwrite any other features ftrs.Child = []element.Token{tlsFeature} } err = t.WriteElement(ftrs) return props, err }
func TestStartReceiving(t *testing.T) { t.Parallel() var want, got []byte var props, gotProps stream.Properties var err, wantErr error pipe1, pipe2 := net.Pipe() tcpTsp := NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) props.Header = stream.Header{} // Should return Domain Not Set error if the domain isnot set on the // stream properties. _, err = tcpTsp.Start(props) if err != stream.ErrDomainNotSet { t.Error("Should return ErrDomainNotSet error if the domain is no set on the properties.") t.Errorf("\nWant:%s\nGot :%s", stream.ErrDomainNotSet, err) } // Should return error from Next props.Domain = "localhost" err = pipe2.Close() if err != nil { t.Errorf("Unexpected error from pipe2.Close: %s", err) } _, err = tcpTsp.Start(props) if err != io.EOF { t.Error("Should return error from Next") t.Errorf("\nWant:%s\nGot :%s", io.EOF, err) } // Should return error from NewHeader pipe1, pipe2 = net.Pipe() tcpTsp = NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) go func() { _, err := pipe2.Write([]byte("<baz xmlns='foo:bar'/>")) if err != nil { t.Errorf("Unexpected error while writing to pipe2: %s", err) } }() _, err = tcpTsp.Start(props) wantErr = fmt.Errorf("Element is not <stream:stream> it is a <foo:bar:baz>") if err.Error() != wantErr.Error() { t.Error("Should return error from NewHeader") t.Errorf("\nWant:%s\nGot :%s", wantErr, err) } // Should send HostUnknown if the to field of the header does not match the // Domain field on properties pipe1, pipe2 = net.Pipe() tcpTsp = NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) go func() { hdr := stream.Header{To: "not-localhost", From: "foo@bar"} _, err := pipe2.Write(hdr.WriteBytes()) if err != nil { t.Errorf("Unexpected error while writing to pipe2: %s", err) } hdr.To, hdr.From = hdr.From, "localhost" want = hdr.WriteBytes() // We need to add an extra 36 bytes for the id length hdrLen := len(want) + 36 want = append(want, element.StreamError.HostUnknown.WriteBytes()...) // We need to add an extra 36 bytes for the id length got = make([]byte, len(want)+36) _, err = pipe2.Read(got) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } _, err = pipe2.Read(got[hdrLen:]) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } }() gotProps, err = tcpTsp.Start(props) if err != nil { t.Errorf("Unexpected error from Start: %s", err) } if gotProps.Status != stream.Closed { t.Error("Expected stream to be marked as closed after host unknown error") } // Need to remove stream ID before comparing idx := bytes.Index(got, []byte("id='")) if idx == -1 { t.Error("Received stream is missing id attribute") } // We slice the id out of the received stream header. got = append(got[:idx+4], got[idx+40:]...) if !bytes.Equal(want, got) { t.Error("Should send HostUnknown if the to field of the header does not match the domain field on properties.") t.Errorf("\nWant:%s\nGot :%s", want, got) } // Should return error from writing header to the underlying connection pipe1, pipe2 = net.Pipe() tcpTsp = NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) go func() { hdr := stream.Header{To: "localhost", From: "foo@bar"} _, err := pipe2.Write(hdr.WriteBytes()) if err != nil { t.Errorf("Unexpected error while writing to pipe2: %s", err) } err = pipe2.Close() if err != nil { t.Errorf("Unexpected error while closing pipe2: %s", err) } }() gotProps, err = tcpTsp.Start(props) if err != io.ErrClosedPipe { t.Error("Should return error from writing header to the underlying connection.") t.Errorf("\nWant:%s\nGot :%s", io.ErrClosedPipe, err) } // Should set the To field to the properties To field if it is set props.To = "authenticatedFoo@bar" // Should overwrite stream features if there is a tls config and the stream // is not yet secure pipe1, pipe2 = net.Pipe() tcpTsp = NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) go func() { hdr := stream.Header{To: "localhost", From: "foo@bar"} _, err := pipe2.Write(hdr.WriteBytes()) if err != nil { t.Errorf("Unexpected error while writing to pipe2: %s", err) } // Doing two tests at the same time, because they are orthogonal hdr.To, hdr.From = "authenticatedFoo@bar", "localhost" want = hdr.WriteBytes() // We need to add an extra 36 bytes for the id length hdrLen := len(want) + 36 ftrs := element.StreamFeatures.AddChild( element.StartTLS.AddChild( element.Required), ) want = append(want, ftrs.WriteBytes()...) // We need to add an extra 36 bytes for the id length got = make([]byte, len(want)+36) _, err = pipe2.Read(got) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } _, err = pipe2.Read(got[hdrLen:]) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } }() gotProps, err = tcpTsp.Start(props) if err != nil { t.Errorf("Unexpected error from Start: %s", err) } // Need to remove stream ID before comparing idx = bytes.Index(got, []byte("id='")) if idx == -1 { t.Error("Received stream is missing id attribute") } // We slice the id out of the received stream header. got = append(got[:idx+4], got[idx+40:]...) if !bytes.Equal(want, got) { t.Error("Should overwrite stream features if there is a tls config and the stream is not yet secure.") t.Errorf("\nWant:%s\nGot :%s", want, got) } // Should be able to start stream props.Features = append(props.Features, element.Bind) pipe1, pipe2 = net.Pipe() tcpTsp = NewTCP(pipe1, stream.Receiving, &tls.Config{}, true) tcpTsp.(*TCP).secure = true go func() { hdr := stream.Header{To: "localhost", From: "foo@bar"} _, err := pipe2.Write(hdr.WriteBytes()) if err != nil { t.Errorf("Unexpected error while writing to pipe2: %s", err) } // Doing two tests at the same time, because they are orthogonal hdr.To, hdr.From = "authenticatedFoo@bar", "localhost" want = hdr.WriteBytes() // We need to add an extra 36 bytes for the id length hdrLen := len(want) + 36 ftrs := element.StreamFeatures.AddChild(element.Bind) want = append(want, ftrs.WriteBytes()...) // We need to add an extra 36 bytes for the id length got = make([]byte, len(want)+36) _, err = pipe2.Read(got) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } _, err = pipe2.Read(got[hdrLen:]) if err != nil { t.Errorf("Unexpected error while reading from pipe2: %s", err) } }() gotProps, err = tcpTsp.Start(props) if err != nil { t.Errorf("Unexpected error from Start: %s", err) } // Need to remove stream ID before comparing idx = bytes.Index(got, []byte("id='")) if idx == -1 { t.Error("Received stream is missing id attribute") } // We slice the id out of the received stream header. got = append(got[:idx+4], got[idx+40:]...) if !bytes.Equal(want, got) { t.Error("Should be able to start stream") t.Errorf("\nWant:%s\nGot :%s", want, got) } }