Skip to content

Commit 0edf55f

Browse files
committed
add signed patch update
1 parent 6b05383 commit 0edf55f

File tree

5 files changed

+287
-67
lines changed

5 files changed

+287
-67
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
testdata/privateKey
2+
testdata/publicKey
13
vendor
24
build
35
scm-source.json

api/handler.go

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"bytes"
66
"fmt"
77
"io"
8+
"io/ioutil"
89
"net/http"
910
"os"
1011
"path/filepath"
@@ -79,12 +80,11 @@ func (u *Update) isSupported() bool {
7980
return ok
8081
}
8182

82-
func (u *Update) getFilepath() string {
83+
func (u *Update) GetFilepath() string {
8384
return basedir + "/" + u.Name + "_" + u.Version + "_" + u.System.Arch + u.System.OS
8485
}
8586

86-
func (u *Update) GetReader() (io.ReadCloser, error) {
87-
filepath := u.getFilepath()
87+
func (u *Update) GetReader(filepath string) (io.ReadCloser, error) {
8888
fd, err := os.Open(filepath)
8989
if err != nil {
9090
return nil, errors.Wrap(errBinaryNotFound, filepath)
@@ -143,7 +143,8 @@ func (svc *Service) UpdateHandler(ginCtx *gin.Context) {
143143
ginCtx.String(http.StatusNotModified, "")
144144
return
145145
}
146-
rc, err := update.GetReader()
146+
fpath := update.GetFilepath()
147+
rc, err := update.GetReader(fpath)
147148
if err != nil {
148149
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
149150
}
@@ -156,6 +157,7 @@ func (svc *Service) UpdateHandler(ginCtx *gin.Context) {
156157
}
157158

158159
// PatchUpdateHandler handles /patch-update/:name endpoint
160+
// TODO: use file cache to read and store binarydiff
159161
func (svc *Service) PatchUpdateHandler(ginCtx *gin.Context) {
160162
newUpdate := newUpdateFromCtx(ginCtx)
161163
latestVersion := newUpdate.GetLatestVersion()
@@ -167,14 +169,16 @@ func (svc *Service) PatchUpdateHandler(ginCtx *gin.Context) {
167169
oldUpdate := newUpdate.Clone()
168170

169171
newUpdate.Version = latestVersion
170-
rcNew, err := newUpdate.GetReader()
172+
fpath := newUpdate.GetFilepath()
173+
rcNew, err := newUpdate.GetReader(fpath)
171174
if err != nil {
172175
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
173176
}
174177
defer rcNew.Close()
175178

176179
glog.Infof("old: %v, new: %v", oldUpdate, newUpdate)
177-
rcOld, err := oldUpdate.GetReader()
180+
fpath = oldUpdate.GetFilepath()
181+
rcOld, err := oldUpdate.GetReader(fpath)
178182
if err != nil {
179183
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
180184
}
@@ -188,18 +192,107 @@ func (svc *Service) PatchUpdateHandler(ginCtx *gin.Context) {
188192
ginCtx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to create a binary patch for %s: %v", newUpdate.Name, err))
189193
}
190194
rw.Flush()
195+
191196
n, err := io.Copy(ginCtx.Writer, rw)
192197
if err != nil {
193198
ginCtx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to copy %s to client: %v", newUpdate.Name, err))
194199
}
195-
196200
glog.Infof("Copied %d bytes to client to patch %s", n, newUpdate)
197201

198202
}
199203

200204
// SignedUpdateHandler handles /signed-update/:name endpoint
201205
func (svc *Service) SignedUpdateHandler(ginCtx *gin.Context) {
202-
ginCtx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("not implemented"))
206+
newUpdate := newUpdateFromCtx(ginCtx)
207+
latestVersion := newUpdate.GetLatestVersion()
208+
glog.V(2).Infof("client has version %s, we have latest version %s", newUpdate.Version, latestVersion)
209+
if newUpdate.Version == latestVersion {
210+
ginCtx.String(http.StatusNotModified, "")
211+
return
212+
}
213+
214+
newUpdate.Version = latestVersion
215+
fpath := newUpdate.GetFilepath()
216+
217+
binPatch, err := ioutil.ReadFile(fpath)
218+
if err != nil {
219+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
220+
}
221+
signature, err := ioutil.ReadFile(fpath + ".signature")
222+
if err != nil {
223+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
224+
}
225+
digest, err := ioutil.ReadFile(fpath + ".sha256")
226+
if err != nil {
227+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
228+
}
229+
230+
ginCtx.JSON(http.StatusOK, gin.H{
231+
"patch": binPatch,
232+
"signature": signature,
233+
"sha256": digest,
234+
})
235+
glog.Infof("Copied %d bytes to client to update %s", len(binPatch), newUpdate)
236+
}
237+
238+
// SignedPatchUpdateHandler handles /signed-patch-update/:name endpoint
239+
func (svc *Service) SignedPatchUpdateHandler(ginCtx *gin.Context) {
240+
newUpdate := newUpdateFromCtx(ginCtx)
241+
latestVersion := newUpdate.GetLatestVersion()
242+
glog.V(2).Infof("client has version %s, we have latest version %s", newUpdate.Version, latestVersion)
243+
if newUpdate.Version == latestVersion {
244+
ginCtx.String(http.StatusNotModified, "")
245+
return
246+
}
247+
oldUpdate := newUpdate.Clone()
248+
249+
newUpdate.Version = latestVersion
250+
fpath := newUpdate.GetFilepath()
251+
rcNew, err := newUpdate.GetReader(fpath)
252+
if err != nil {
253+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
254+
}
255+
defer rcNew.Close()
256+
257+
glog.Infof("old: %v, new: %v", oldUpdate, newUpdate)
258+
oldFpath := oldUpdate.GetFilepath()
259+
rcOld, err := oldUpdate.GetReader(oldFpath)
260+
if err != nil {
261+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
262+
}
263+
defer rcOld.Close()
264+
265+
buf := bytes.NewBuffer(nil)
266+
rw := bufio.NewReadWriter(bufio.NewReader(buf), bufio.NewWriter(buf))
267+
268+
err = binarydist.Diff(rcOld, rcNew, rw)
269+
if err != nil {
270+
ginCtx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to create a binary patch for %s: %v", newUpdate.Name, err))
271+
}
272+
rw.Flush()
273+
274+
binPatch, err := ioutil.ReadAll(rw)
275+
if err != nil {
276+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
277+
}
278+
279+
signature, err := ioutil.ReadFile(fpath + ".signature")
280+
if err != nil {
281+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
282+
}
283+
284+
digest, err := ioutil.ReadFile(fpath + ".sha256")
285+
if err != nil {
286+
ginCtx.AbortWithError(http.StatusInternalServerError, err) // TODO: AbortWithError creates StackTraces, we want to have 4xx and an error log
287+
}
288+
289+
ginCtx.JSON(http.StatusOK, gin.H{
290+
"patch": binPatch,
291+
"signature": signature,
292+
"sha256": digest,
293+
})
294+
295+
glog.Infof("Copied %d bytes patch to client to patch %s", len(binPatch), newUpdate)
203296
}
204297

205298
// RootHandler handles / endpoint

api/service.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,14 @@ func (svc *Service) Run(config *ServiceConfig) error {
113113
private.GET("/update/:name", svc.UpdateHandler)
114114
private.GET("/patch-update/:name", svc.PatchUpdateHandler)
115115
private.GET("/signed-update/:name", svc.SignedUpdateHandler)
116+
private.GET("/signed-patch-update/:name", svc.SignedPatchUpdateHandler)
116117
} else {
117118
// public routes
118119
router.GET("/", svc.RootHandler)
119120
router.GET("/update/:name", svc.UpdateHandler)
120121
router.GET("/patch-update/:name", svc.PatchUpdateHandler)
121122
router.GET("/signed-update/:name", svc.SignedUpdateHandler)
123+
router.GET("/signed-patch-update/:name", svc.SignedPatchUpdateHandler)
122124
}
123125

124126
// TLS config

0 commit comments

Comments
 (0)