-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
xrootd: introduce a XRootD (un)marshal interface and generator #214
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a first pass.
but it looks good already :)
xrootd/client/login.go
Outdated
@@ -7,23 +7,13 @@ package client // import "go-hep.org/x/hep/xrootd/client" | |||
import ( | |||
"context" | |||
|
|||
"go-hep.org/x/hep/xrootd/protocol" | |||
"go-hep.org/x/hep/xrootd/protocol/login" | |||
) | |||
|
|||
// Login initializes a server connection using username | |||
// and token which can be supplied by the previous redirection response. | |||
func (client *Client) Login(ctx context.Context, username string, token string) (login.Response, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/username string, token string/username, token string/
xrootd/client/client.go
Outdated
@@ -149,6 +150,16 @@ func (client *Client) consume(ctx context.Context) { | |||
} | |||
} | |||
|
|||
// Send sends the request to the server and stores the response inside the resp. | |||
func (client *Client) Send(ctx context.Context, resp interface{}, req protocol.Request) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couldn't we request that resp
honours protocol.Response
instead of the empty interface{}
?
with:
package protocol
type Response interface {
RespID() uint16 // just to prevent passing a Request instead of a Response (and vice versa)
MarshalXrd
UnmarshalXrd
}
xrootd/client/client.go
Outdated
return err | ||
} | ||
|
||
return protocol.Unmarshal(serverResponse, resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then we could write:
return resp.UnmarshalXrd(data)
xrootd/client/client.go
Outdated
@@ -149,6 +150,16 @@ func (client *Client) consume(ctx context.Context) { | |||
} | |||
} | |||
|
|||
// Send sends the request to the server and stores the response inside the resp. | |||
func (client *Client) Send(ctx context.Context, resp interface{}, req protocol.Request) error { | |||
serverResponse, err := client.call(ctx, req) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/serverResponse/data/
xrootd/client/protocol_mock_test.go
Outdated
if err != nil { | ||
cancel() | ||
defer cancel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is putting back defer
a deliberate change? (or just a merge carried over by some previous CL?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, looks like a strange merge from somewhere (Have no idea how it leaked here...).
xrootd/protocol/protocol/protocol.go
Outdated
RequestLevel RequestLevel | ||
// SetIsMeta sets whether this server has meta attribute. | ||
func (resp *Response) SetIsMeta(v bool) { | ||
if v { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use an early return
to help the compiler inline calls of this method.
xrootd/protocol/protocol/protocol.go
Outdated
) | ||
// SetIsProxy sets whether this server has proxy attribute. | ||
func (resp *Response) SetIsProxy(v bool) { | ||
if v { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use an early return
to help the compiler inline calls of this method.
xrootd/protocol/protocol/protocol.go
Outdated
options |= ReturnSecurityRequirements | ||
// SetIsSupervisor sets whether this server has supervisor attribute. | ||
func (resp *Response) SetIsSupervisor(v bool) { | ||
if v { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use an early return
to help the compiler inline calls of this method.
xrootd/protocol/protocol/protocol.go
Outdated
|
||
// SetForceSecurity sets whether signing is forced. | ||
func (resp *Response) SetForceSecurity(v bool) { | ||
if v { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use an early return
to help the compiler inline calls of this method.
xrootd/protocol/protocol/protocol.go
Outdated
} | ||
|
||
// Response is a response for the `Protocol` request. See details in the xrootd protocol specification. | ||
type Response struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
somehow, the SetIsXyz
methods on this protocol.Response
type feel a bit wrong to me: calling those won't impact back the server from which we got them.
I understand you introduced them so you could create a Response
to test against in the Client
tests.
Perhaps we could just leave the flags
field of Response
exported (so s/flags/Flags/
) and re-export the IsXyz
bitmasks ?
the protocol
package isn't super crowded so it's fine to populate it somewhat.
that way, you could create a want
value like so:
want := protocol.Response{
 BinaryProtocolVersion: protocolVersion,
 Flags: protocol.IsManager | protocol.IsServer | protocol.IsMeta |
protocol.IsProxy | protocol.IsSupervisor,
SecurityLevel: xrdproto.Pedantic,
SecurityOverrides: []xrdproto.SecurityOverride{{1, xrdproto.SignNeeded}},
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand you introduced them so you could create a Response to test against in the Client tests.
was thinking about using them for the server implementation, not just tests. However, I don't argue about usage of Flags
, it's fine too. :)
xrootd/protocol/marshaling.go
Outdated
// UnmarshalXrd must copy the data if it wishes to retain the data after | ||
// returning. | ||
type Unmarshaler interface { | ||
UnmarshalXrd(data []byte) (int, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok... to address this annoying (to me, at least) issue of this extra int
return value, what about modifying the (un)marshalers to something like:
type Marshaler interface {
MarshalXrd(buf *Buffer) error
}
type Unmarshaler interface {
UnmarshalXrd(buf *Buffer) error
}
where Buffer
is the union of the current Encoder
and Decoder
type.
that way, the current issue of unmarshaling protocol/protocol.Request
:
// UnmarshalXrd implements xrootd/protocol.Unmarshaler
func (o *Response) UnmarshalXrd(data []byte) (pos int, err error) {
dec := xrdenc.NewDecoder(data)
o.BinaryProtocolVersion = dec.ReadI32()
o.flags = flags(dec.ReadI32())
if dec.Available() > 0 {
dec.Skip(1)
dec.Skip(1)
o.SecurityVersion = dec.ReadU8()
o.SecurityOptions = securityOptions(dec.ReadU8())
o.SecurityLevel = protocol.SecurityLevel(dec.ReadU8())
o.SecurityOverrides = make([]protocol.SecurityOverride, dec.ReadU8())
for i := 0; i < len(o.SecurityOverrides); i++ {
n, err := o.SecurityOverrides[i].UnmarshalXrd(dec.Bytes())
if err != nil {
return 0, err
}
dec.Skip(n)
}
}
return dec.Pos(), err
}
would become:
// UnmarshalXrd implements xrootd/protocol.Unmarshaler
func (o *Response) UnmarshalXrd(buf *xrdproto.Buffer) error {
o.BinaryProtocolVersion = buf.ReadI32()
o.flags = flags(buf.ReadI32())
if buf.Len() > 0 {
buf.Skip(1)
buf.Skip(1)
o.SecurityVersion = buf.ReadU8()
o.SecurityOptions = securityOptions(buf.ReadU8())
o.SecurityLevel = protocol.SecurityLevel(buf.ReadU8())
o.SecurityOverrides = make([]protocol.SecurityOverride, buf.ReadU8())
for i := 0; i < len(o.SecurityOverrides); i++ {
err := o.SecurityOverrides[i].UnmarshalXrd(buf)
if err != nil {
return err
}
}
}
return nil
}
most notably, the dec.Skip(n)
statement (which could easily be overlooked and fogotten) just disappeared.
what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about keeping Encoder
and Decoder
and passing them instead of xrdproto.Buffer
? Because we don't need to perform any writing in UnmarshalXrd
and any reading in MarshalXrd
.
So it will look like:
type Marshaler interface {
MarshalXrd(enc *xrdenc.Encoder) error
}
type Unmarshaler interface {
UnmarshalXrd(dec *xrdenc.Decoder) error
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, what about renaming 'Decoder' into 'RBuffer' and 'Encoder' into 'WBuffer'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
xrootd/protocol/protocol/protocol.go
Outdated
dec := xrdenc.NewDecoder(data) | ||
o.BinaryProtocolVersion = dec.ReadI32() | ||
o.flags = flags(dec.ReadI32()) | ||
if dec.Available() > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please invert the logic and apply an early return
.
xrootd/protocol/protocol/protocol.go
Outdated
func (o Response) MarshalXrd() (data []byte, err error) { | ||
var enc xrdenc.Encoder | ||
enc.WriteI32(o.BinaryProtocolVersion) | ||
enc.WriteI32(int32(o.flags)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hum... why isn't the marshaling handling the optional writing part, symmetric to what is done on the unmarshaling side?
f11712d
to
e9e54bd
Compare
This helps to generate boilerplate code which is used for encoding and decoding of the requests and the responses.
e9e54bd
to
3600d05
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This helps to generate boilerplate code which is used for encoding
and decoding of the requests and the responses.
It's a bit involved version of the #210.
@sbinet, thank you very much for your help!