forked from quic-go/quic-go
pass the QUIC version to all frame parsing functions
This commit is contained in:
@@ -25,7 +25,7 @@ func (f *BlockedFrame) MinLength(version protocol.VersionNumber) (protocol.ByteC
|
||||
}
|
||||
|
||||
// ParseBlockedFrame parses a BLOCKED frame
|
||||
func ParseBlockedFrame(r *bytes.Reader) (*BlockedFrame, error) {
|
||||
func ParseBlockedFrame(r *bytes.Reader, version protocol.VersionNumber) (*BlockedFrame, error) {
|
||||
frame := &BlockedFrame{}
|
||||
|
||||
// read the TypeByte
|
||||
|
||||
@@ -12,17 +12,17 @@ var _ = Describe("BlockedFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{0x05, 0xEF, 0xBE, 0xAD, 0xDE})
|
||||
frame, err := ParseBlockedFrame(b)
|
||||
frame, err := ParseBlockedFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(0xDEADBEEF)))
|
||||
})
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
data := []byte{0x05, 0xEF, 0xBE, 0xAD, 0xDE}
|
||||
_, err := ParseBlockedFrame(bytes.NewReader(data))
|
||||
_, err := ParseBlockedFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseBlockedFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseBlockedFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ type ConnectionCloseFrame struct {
|
||||
}
|
||||
|
||||
// ParseConnectionCloseFrame reads a CONNECTION_CLOSE frame
|
||||
func ParseConnectionCloseFrame(r *bytes.Reader) (*ConnectionCloseFrame, error) {
|
||||
func ParseConnectionCloseFrame(r *bytes.Reader, version protocol.VersionNumber) (*ConnectionCloseFrame, error) {
|
||||
frame := &ConnectionCloseFrame{}
|
||||
|
||||
// read the TypeByte
|
||||
|
||||
@@ -14,7 +14,7 @@ var _ = Describe("ConnectionCloseFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{0x40, 0x19, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x4e, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e})
|
||||
frame, err := ParseConnectionCloseFrame(b)
|
||||
frame, err := ParseConnectionCloseFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.ErrorCode).To(Equal(qerr.ErrorCode(0x19)))
|
||||
Expect(frame.ReasonPhrase).To(Equal("No recent network activity."))
|
||||
@@ -23,7 +23,7 @@ var _ = Describe("ConnectionCloseFrame", func() {
|
||||
|
||||
It("parses a frame without a reason phrase", func() {
|
||||
b := bytes.NewReader([]byte{0x02, 0xAD, 0xFB, 0xCA, 0xDE, 0x00, 0x00})
|
||||
frame, err := ParseConnectionCloseFrame(b)
|
||||
frame, err := ParseConnectionCloseFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.ErrorCode).To(Equal(qerr.ErrorCode(0xDECAFBAD)))
|
||||
Expect(frame.ReasonPhrase).To(BeEmpty())
|
||||
@@ -32,16 +32,16 @@ var _ = Describe("ConnectionCloseFrame", func() {
|
||||
|
||||
It("rejects long reason phrases", func() {
|
||||
b := bytes.NewReader([]byte{0x02, 0xAD, 0xFB, 0xCA, 0xDE, 0xff, 0xf})
|
||||
_, err := ParseConnectionCloseFrame(b)
|
||||
_, err := ParseConnectionCloseFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidConnectionCloseData, "reason phrase too long")))
|
||||
})
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
data := []byte{0x40, 0x19, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x4e, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e}
|
||||
_, err := ParseConnectionCloseFrame(bytes.NewReader(data))
|
||||
_, err := ParseConnectionCloseFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseConnectionCloseFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseConnectionCloseFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
@@ -105,7 +105,7 @@ var _ = Describe("ConnectionCloseFrame", func() {
|
||||
}
|
||||
err := frame.Write(b, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
readframe, err := ParseConnectionCloseFrame(bytes.NewReader(b.Bytes()))
|
||||
readframe, err := ParseConnectionCloseFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(readframe.ErrorCode).To(Equal(frame.ErrorCode))
|
||||
Expect(readframe.ReasonPhrase).To(Equal(frame.ReasonPhrase))
|
||||
|
||||
@@ -17,7 +17,7 @@ type GoawayFrame struct {
|
||||
}
|
||||
|
||||
// ParseGoawayFrame parses a GOAWAY frame
|
||||
func ParseGoawayFrame(r *bytes.Reader) (*GoawayFrame, error) {
|
||||
func ParseGoawayFrame(r *bytes.Reader, version protocol.VersionNumber) (*GoawayFrame, error) {
|
||||
frame := &GoawayFrame{}
|
||||
|
||||
_, err := r.ReadByte()
|
||||
|
||||
@@ -20,7 +20,7 @@ var _ = Describe("GoawayFrame", func() {
|
||||
0x03, 0x00,
|
||||
'f', 'o', 'o',
|
||||
})
|
||||
frame, err := ParseGoawayFrame(b)
|
||||
frame, err := ParseGoawayFrame(b, protocol.VersionWhatever)
|
||||
Expect(frame).To(Equal(&GoawayFrame{
|
||||
ErrorCode: 1,
|
||||
LastGoodStream: 2,
|
||||
@@ -37,7 +37,7 @@ var _ = Describe("GoawayFrame", func() {
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff,
|
||||
})
|
||||
_, err := ParseGoawayFrame(b)
|
||||
_, err := ParseGoawayFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidGoawayData, "reason phrase too long")))
|
||||
})
|
||||
|
||||
@@ -48,10 +48,10 @@ var _ = Describe("GoawayFrame", func() {
|
||||
0x03, 0x00,
|
||||
'f', 'o', 'o',
|
||||
}
|
||||
_, err := ParseGoawayFrame(bytes.NewReader(data))
|
||||
_, err := ParseGoawayFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseGoawayFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseGoawayFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
type PingFrame struct{}
|
||||
|
||||
// ParsePingFrame parses a Ping frame
|
||||
func ParsePingFrame(r *bytes.Reader) (*PingFrame, error) {
|
||||
func ParsePingFrame(r *bytes.Reader, version protocol.VersionNumber) (*PingFrame, error) {
|
||||
frame := &PingFrame{}
|
||||
|
||||
_, err := r.ReadByte()
|
||||
|
||||
@@ -12,13 +12,13 @@ var _ = Describe("PingFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{0x07})
|
||||
_, err := ParsePingFrame(b)
|
||||
_, err := ParsePingFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(b.Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
_, err := ParsePingFrame(bytes.NewReader(nil))
|
||||
_, err := ParsePingFrame(bytes.NewReader(nil), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ func (f *RstStreamFrame) MinLength(version protocol.VersionNumber) (protocol.Byt
|
||||
}
|
||||
|
||||
// ParseRstStreamFrame parses a RST_STREAM frame
|
||||
func ParseRstStreamFrame(r *bytes.Reader) (*RstStreamFrame, error) {
|
||||
func ParseRstStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*RstStreamFrame, error) {
|
||||
frame := &RstStreamFrame{}
|
||||
|
||||
// read the TypeByte
|
||||
|
||||
@@ -12,7 +12,7 @@ var _ = Describe("RstStreamFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0x44, 0x33, 0x22, 0x11, 0xAD, 0xFB, 0xCA, 0xDE, 0x34, 0x12, 0x37, 0x13})
|
||||
frame, err := ParseRstStreamFrame(b)
|
||||
frame, err := ParseRstStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(0xDEADBEEF)))
|
||||
Expect(frame.ByteOffset).To(Equal(protocol.ByteCount(0xDECAFBAD11223344)))
|
||||
@@ -21,10 +21,10 @@ var _ = Describe("RstStreamFrame", func() {
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
data := []byte{0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0x44, 0x33, 0x22, 0x11, 0xAD, 0xFB, 0xCA, 0xDE, 0x34, 0x12, 0x37, 0x13}
|
||||
_, err := ParseRstStreamFrame(bytes.NewReader(data))
|
||||
_, err := ParseRstStreamFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseRstStreamFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseRstStreamFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,7 +25,7 @@ var (
|
||||
)
|
||||
|
||||
// ParseStreamFrame reads a stream frame. The type byte must not have been read yet.
|
||||
func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
|
||||
func ParseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) {
|
||||
frame := &StreamFrame{}
|
||||
|
||||
typeByte, err := r.ReadByte()
|
||||
|
||||
@@ -14,7 +14,7 @@ var _ = Describe("StreamFrame", func() {
|
||||
It("accepts sample frame", func() {
|
||||
// a STREAM frame, plus 3 additional bytes, not belonging to this frame
|
||||
b := bytes.NewReader([]byte{0xa0, 0x1, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r' /* additional bytes */, 'f', 'o', 'o'})
|
||||
frame, err := ParseStreamFrame(b)
|
||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.FinBit).To(BeFalse())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||
@@ -26,7 +26,7 @@ var _ = Describe("StreamFrame", func() {
|
||||
|
||||
It("accepts frame without data length", func() {
|
||||
b := bytes.NewReader([]byte{0x80, 0x1, 'f', 'o', 'o', 'b', 'a', 'r'})
|
||||
frame, err := ParseStreamFrame(b)
|
||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.FinBit).To(BeFalse())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||
@@ -39,7 +39,7 @@ var _ = Describe("StreamFrame", func() {
|
||||
It("accepts an empty frame with FinBit set, with data length set", func() {
|
||||
// the STREAM frame, plus 3 additional bytes, not belonging to this frame
|
||||
b := bytes.NewReader([]byte{0x80 ^ 0x40 ^ 0x20, 0x1 /* stream id */, 0, 0, 'f', 'o', 'o'})
|
||||
frame, err := ParseStreamFrame(b)
|
||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.FinBit).To(BeTrue())
|
||||
Expect(frame.DataLenPresent).To(BeTrue())
|
||||
@@ -49,7 +49,7 @@ var _ = Describe("StreamFrame", func() {
|
||||
|
||||
It("accepts an empty frame with the FinBit set", func() {
|
||||
b := bytes.NewReader([]byte{0x80 ^ 0x40, 0x1 /* stream id */, 'f', 'o', 'o', 'b', 'a', 'r'})
|
||||
frame, err := ParseStreamFrame(b)
|
||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.FinBit).To(BeTrue())
|
||||
Expect(frame.DataLenPresent).To(BeFalse())
|
||||
@@ -59,7 +59,7 @@ var _ = Describe("StreamFrame", func() {
|
||||
|
||||
It("accepts frames with offsets", func() {
|
||||
b := bytes.NewReader([]byte{0xa4, 0x1, 0x2a, 0x00, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'})
|
||||
frame, err := ParseStreamFrame(b)
|
||||
frame, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.FinBit).To(BeFalse())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(1)))
|
||||
@@ -71,13 +71,13 @@ var _ = Describe("StreamFrame", func() {
|
||||
|
||||
It("errors on empty stream frames that don't have the FinBit set", func() {
|
||||
b := bytes.NewReader([]byte{0x80 ^ 0x20, 0x1, 0, 0})
|
||||
_, err := ParseStreamFrame(b)
|
||||
_, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).To(MatchError(qerr.EmptyStreamFrameNoFin))
|
||||
})
|
||||
|
||||
It("rejects frames to too large dataLen", func() {
|
||||
b := bytes.NewReader([]byte{0xa0, 0x1, 0xff, 0xf})
|
||||
_, err := ParseStreamFrame(b)
|
||||
_, err := ParseStreamFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data len too large")))
|
||||
})
|
||||
|
||||
@@ -90,16 +90,16 @@ var _ = Describe("StreamFrame", func() {
|
||||
}
|
||||
b := &bytes.Buffer{}
|
||||
f.Write(b, protocol.VersionWhatever)
|
||||
_, err := ParseStreamFrame(bytes.NewReader(b.Bytes()))
|
||||
_, err := ParseStreamFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever)
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset")))
|
||||
})
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
data := []byte{0xa4, 0x1, 0x2a, 0x00, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}
|
||||
_, err := ParseStreamFrame(bytes.NewReader(data))
|
||||
_, err := ParseStreamFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseStreamFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseStreamFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ func (f *WindowUpdateFrame) MinLength(version protocol.VersionNumber) (protocol.
|
||||
}
|
||||
|
||||
// ParseWindowUpdateFrame parses a RST_STREAM frame
|
||||
func ParseWindowUpdateFrame(r *bytes.Reader) (*WindowUpdateFrame, error) {
|
||||
func ParseWindowUpdateFrame(r *bytes.Reader, version protocol.VersionNumber) (*WindowUpdateFrame, error) {
|
||||
frame := &WindowUpdateFrame{}
|
||||
|
||||
// read the TypeByte
|
||||
|
||||
@@ -12,7 +12,7 @@ var _ = Describe("WindowUpdateFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{0x04, 0xEF, 0xBE, 0xAD, 0xDE, 0x44, 0x33, 0x22, 0x11, 0xAD, 0xFB, 0xCA, 0xDE})
|
||||
frame, err := ParseWindowUpdateFrame(b)
|
||||
frame, err := ParseWindowUpdateFrame(b, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame.StreamID).To(Equal(protocol.StreamID(0xDEADBEEF)))
|
||||
Expect(frame.ByteOffset).To(Equal(protocol.ByteCount(0xDECAFBAD11223344)))
|
||||
@@ -21,10 +21,10 @@ var _ = Describe("WindowUpdateFrame", func() {
|
||||
|
||||
It("errors on EOFs", func() {
|
||||
data := []byte{0x04, 0xEF, 0xBE, 0xAD, 0xDE, 0x44, 0x33, 0x22, 0x11, 0xAD, 0xFB, 0xCA, 0xDE}
|
||||
_, err := ParseWindowUpdateFrame(bytes.NewReader(data))
|
||||
_, err := ParseWindowUpdateFrame(bytes.NewReader(data), protocol.VersionWhatever)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for i := range data {
|
||||
_, err := ParseWindowUpdateFrame(bytes.NewReader(data[0:i]))
|
||||
_, err := ParseWindowUpdateFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever)
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -50,7 +50,7 @@ func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, da
|
||||
|
||||
var frame frames.Frame
|
||||
if typeByte&0x80 == 0x80 {
|
||||
frame, err = frames.ParseStreamFrame(r)
|
||||
frame, err = frames.ParseStreamFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidStreamData, err.Error())
|
||||
} else {
|
||||
@@ -69,27 +69,27 @@ func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, da
|
||||
} else {
|
||||
switch typeByte {
|
||||
case 0x01:
|
||||
frame, err = frames.ParseRstStreamFrame(r)
|
||||
frame, err = frames.ParseRstStreamFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidRstStreamData, err.Error())
|
||||
}
|
||||
case 0x02:
|
||||
frame, err = frames.ParseConnectionCloseFrame(r)
|
||||
frame, err = frames.ParseConnectionCloseFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error())
|
||||
}
|
||||
case 0x03:
|
||||
frame, err = frames.ParseGoawayFrame(r)
|
||||
frame, err = frames.ParseGoawayFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidGoawayData, err.Error())
|
||||
}
|
||||
case 0x04:
|
||||
frame, err = frames.ParseWindowUpdateFrame(r)
|
||||
frame, err = frames.ParseWindowUpdateFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error())
|
||||
}
|
||||
case 0x05:
|
||||
frame, err = frames.ParseBlockedFrame(r)
|
||||
frame, err = frames.ParseBlockedFrame(r, u.version)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidBlockedData, err.Error())
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, da
|
||||
err = qerr.Error(qerr.InvalidStopWaitingData, err.Error())
|
||||
}
|
||||
case 0x07:
|
||||
frame, err = frames.ParsePingFrame(r)
|
||||
frame, err = frames.ParsePingFrame(r, u.version)
|
||||
default:
|
||||
err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user