| package protocol |
| |
| import ( |
| "crypto/rand" |
| "fmt" |
| "reflect" |
| ) |
| |
| // RandReader is the random reader the protocol package will use to read |
| // random bytes from. This is exported for testing, and should not be used. |
| var RandReader = rand.Reader |
| |
| const idempotencyTokenFillTag = `idempotencyToken` |
| |
| // CanSetIdempotencyToken returns true if the struct field should be |
| // automatically populated with a Idempotency token. |
| // |
| // Only *string and string type fields that are tagged with idempotencyToken |
| // which are not already set can be auto filled. |
| func CanSetIdempotencyToken(v reflect.Value, f reflect.StructField) bool { |
| switch u := v.Interface().(type) { |
| // To auto fill an Idempotency token the field must be a string, |
| // tagged for auto fill, and have a zero value. |
| case *string: |
| return u == nil && len(f.Tag.Get(idempotencyTokenFillTag)) != 0 |
| case string: |
| return len(u) == 0 && len(f.Tag.Get(idempotencyTokenFillTag)) != 0 |
| } |
| |
| return false |
| } |
| |
| // GetIdempotencyToken returns a randomly generated idempotency token. |
| func GetIdempotencyToken() string { |
| b := make([]byte, 16) |
| RandReader.Read(b) |
| |
| return UUIDVersion4(b) |
| } |
| |
| // SetIdempotencyToken will set the value provided with a Idempotency Token. |
| // Given that the value can be set. Will panic if value is not setable. |
| func SetIdempotencyToken(v reflect.Value) { |
| if v.Kind() == reflect.Ptr { |
| if v.IsNil() && v.CanSet() { |
| v.Set(reflect.New(v.Type().Elem())) |
| } |
| v = v.Elem() |
| } |
| v = reflect.Indirect(v) |
| |
| if !v.CanSet() { |
| panic(fmt.Sprintf("unable to set idempotnecy token %v", v)) |
| } |
| |
| b := make([]byte, 16) |
| _, err := rand.Read(b) |
| if err != nil { |
| // TODO handle error |
| return |
| } |
| |
| v.Set(reflect.ValueOf(UUIDVersion4(b))) |
| } |
| |
| // UUIDVersion4 returns a Version 4 random UUID from the byte slice provided |
| func UUIDVersion4(u []byte) string { |
| // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 |
| // 13th character is "4" |
| u[6] = (u[6] | 0x40) & 0x4F |
| // 17th character is "8", "9", "a", or "b" |
| u[8] = (u[8] | 0x80) & 0xBF |
| |
| return fmt.Sprintf(`%X-%X-%X-%X-%X`, u[0:4], u[4:6], u[6:8], u[8:10], u[10:]) |
| } |