github.com/vmware/govmomi@v0.51.0/simulator/http_nfc_lease.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package simulator 6 7 import ( 8 "crypto/sha1" 9 "encoding/hex" 10 "fmt" 11 "hash" 12 "io" 13 "log" 14 "net/http" 15 "net/url" 16 "os" 17 "strings" 18 "sync" 19 20 "github.com/vmware/govmomi/vim25/methods" 21 "github.com/vmware/govmomi/vim25/mo" 22 "github.com/vmware/govmomi/vim25/soap" 23 "github.com/vmware/govmomi/vim25/types" 24 ) 25 26 type metadata struct { 27 sha1 []byte 28 size int64 29 } 30 31 type HttpNfcLease struct { 32 mo.HttpNfcLease 33 files map[string]string 34 metadata map[string]metadata 35 } 36 37 var ( 38 nfcLease sync.Map // HTTP access to NFC leases are token based and do not require Session auth 39 nfcPrefix = "/nfc/" 40 ) 41 42 // ServeNFC handles NFC file upload/download 43 func ServeNFC(w http.ResponseWriter, r *http.Request) { 44 p := strings.Split(r.URL.Path, "/") 45 id, name := p[len(p)-2], p[len(p)-1] 46 ref := types.ManagedObjectReference{Type: "HttpNfcLease", Value: id} 47 l, ok := nfcLease.Load(ref) 48 if !ok { 49 log.Printf("invalid NFC lease: %s", id) 50 http.NotFound(w, r) 51 return 52 } 53 lease := l.(*HttpNfcLease) 54 file, ok := lease.files[name] 55 if !ok { 56 log.Printf("invalid NFC device id: %s", name) 57 http.NotFound(w, r) 58 return 59 } 60 61 status := http.StatusOK 62 var sum hash.Hash 63 var dst io.Writer = w 64 var src io.ReadCloser 65 66 switch r.Method { 67 case http.MethodPut, http.MethodPost: 68 sum = sha1.New() 69 dst = sum 70 src = r.Body 71 case http.MethodGet: 72 f, err := os.Open(file) 73 if err != nil { 74 http.NotFound(w, r) 75 return 76 } 77 src = f 78 default: 79 status = http.StatusMethodNotAllowed 80 } 81 82 n, err := io.Copy(dst, src) 83 _ = src.Close() 84 if sum != nil { 85 lease.metadata[name] = metadata{ 86 sha1: sum.Sum(nil), 87 size: n, 88 } 89 } 90 91 msg := fmt.Sprintf("transferred %d bytes", n) 92 if err != nil { 93 status = http.StatusInternalServerError 94 msg = err.Error() 95 } 96 tracef("nfc %s %s: %s", r.Method, file, msg) 97 w.WriteHeader(status) 98 } 99 100 func (l *HttpNfcLease) error(ctx *Context, err *types.LocalizedMethodFault) { 101 ctx.WithLock(l, func() { 102 ctx.Update(l, []types.PropertyChange{ 103 {Name: "state", Val: types.HttpNfcLeaseStateError}, 104 {Name: "error", Val: err}, 105 }) 106 }) 107 } 108 109 func (l *HttpNfcLease) ready(ctx *Context, entity types.ManagedObjectReference, urls []types.HttpNfcLeaseDeviceUrl) { 110 info := &types.HttpNfcLeaseInfo{ 111 Lease: l.Self, 112 Entity: entity, 113 DeviceUrl: urls, 114 LeaseTimeout: 300, 115 } 116 117 ctx.WithLock(l, func() { 118 ctx.Update(l, []types.PropertyChange{ 119 {Name: "state", Val: types.HttpNfcLeaseStateReady}, 120 {Name: "info", Val: info}, 121 }) 122 }) 123 } 124 125 func newHttpNfcLease(ctx *Context) *HttpNfcLease { 126 lease := &HttpNfcLease{ 127 HttpNfcLease: mo.HttpNfcLease{ 128 State: types.HttpNfcLeaseStateInitializing, 129 }, 130 files: make(map[string]string), 131 metadata: make(map[string]metadata), 132 } 133 134 ctx.Session.Put(lease) 135 nfcLease.Store(lease.Reference(), lease) 136 137 return lease 138 } 139 140 func leaseURL(ctx *Context) *url.URL { 141 opt := ctx.Map.OptionManager().find("vcsim.server.url") 142 143 u, _ := url.Parse(opt.Value.(string)) 144 145 // See NfcLease.DeviceUrl doc: 146 // If a "*" is returned the client must substitute "*" with the 147 // hostname or IP address used when connecting to the server. 148 // This is the case when connecting directly to an ESX host. 149 if ctx.Map.IsESX() { 150 u.Host = "*" 151 } 152 153 return u 154 } 155 156 func (l *HttpNfcLease) HttpNfcLeaseComplete(ctx *Context, req *types.HttpNfcLeaseComplete) soap.HasFault { 157 ctx.Session.Remove(ctx, req.This) 158 nfcLease.Delete(req.This) 159 160 return &methods.HttpNfcLeaseCompleteBody{ 161 Res: new(types.HttpNfcLeaseCompleteResponse), 162 } 163 } 164 165 func (l *HttpNfcLease) HttpNfcLeaseAbort(ctx *Context, req *types.HttpNfcLeaseAbort) soap.HasFault { 166 ctx.Session.Remove(ctx, req.This) 167 nfcLease.Delete(req.This) 168 169 return &methods.HttpNfcLeaseAbortBody{ 170 Res: new(types.HttpNfcLeaseAbortResponse), 171 } 172 } 173 174 func (l *HttpNfcLease) HttpNfcLeaseProgress(ctx *Context, req *types.HttpNfcLeaseProgress) soap.HasFault { 175 l.TransferProgress = req.Percent 176 177 return &methods.HttpNfcLeaseProgressBody{ 178 Res: new(types.HttpNfcLeaseProgressResponse), 179 } 180 } 181 182 func (l *HttpNfcLease) getDeviceKey(name string) string { 183 for _, devUrl := range l.Info.DeviceUrl { 184 if name == devUrl.TargetId { 185 return devUrl.Key 186 } 187 } 188 return "unknown" 189 } 190 191 func (l *HttpNfcLease) HttpNfcLeaseGetManifest(ctx *Context, req *types.HttpNfcLeaseGetManifest) soap.HasFault { 192 entries := []types.HttpNfcLeaseManifestEntry{} 193 for name, md := range l.metadata { 194 entries = append(entries, types.HttpNfcLeaseManifestEntry{ 195 Key: l.getDeviceKey(name), 196 Sha1: hex.EncodeToString(md.sha1), 197 Size: md.size, 198 }) 199 } 200 return &methods.HttpNfcLeaseGetManifestBody{ 201 Res: &types.HttpNfcLeaseGetManifestResponse{ 202 Returnval: entries, 203 }, 204 } 205 }