##// END OF EJS Templates
lfs: add basic routing for the server side wire protocol processing...
Matt Harbison -
r37165:a2566597 default
parent child Browse files
Show More
@@ -0,0 +1,75
1 # wireprotolfsserver.py - lfs protocol server side implementation
2 #
3 # Copyright 2018 Matt Harbison <matt_harbison@yahoo.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 from mercurial.hgweb import (
11 common as hgwebcommon,
12 )
13
14 from mercurial import (
15 pycompat,
16 )
17
18 def handlewsgirequest(orig, rctx, req, res, checkperm):
19 """Wrap wireprotoserver.handlewsgirequest() to possibly process an LFS
20 request if it is left unprocessed by the wrapped method.
21 """
22 if orig(rctx, req, res, checkperm):
23 return True
24
25 if not req.dispatchpath:
26 return False
27
28 try:
29 if req.dispatchpath == b'.git/info/lfs/objects/batch':
30 checkperm(rctx, req, 'pull')
31 return _processbatchrequest(rctx.repo, req, res)
32 # TODO: reserve and use a path in the proposed http wireprotocol /api/
33 # namespace?
34 elif req.dispatchpath.startswith(b'.hg/lfs/objects'):
35 return _processbasictransfer(rctx.repo, req, res,
36 lambda perm:
37 checkperm(rctx, req, perm))
38 return False
39 except hgwebcommon.ErrorResponse as e:
40 # XXX: copied from the handler surrounding wireprotoserver._callhttp()
41 # in the wrapped function. Should this be moved back to hgweb to
42 # be a common handler?
43 for k, v in e.headers:
44 res.headers[k] = v
45 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
46 res.setbodybytes(b'0\n%s\n' % pycompat.bytestr(e))
47 return True
48
49 def _processbatchrequest(repo, req, res):
50 """Handle a request for the Batch API, which is the gateway to granting file
51 access.
52
53 https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
54 """
55 return False
56
57 def _processbasictransfer(repo, req, res, checkperm):
58 """Handle a single file upload (PUT) or download (GET) action for the Basic
59 Transfer Adapter.
60
61 After determining if the request is for an upload or download, the access
62 must be checked by calling ``checkperm()`` with either 'pull' or 'upload'
63 before accessing the files.
64
65 https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
66 """
67
68 method = req.method
69
70 if method == b'PUT':
71 checkperm('upload')
72 elif method == b'GET':
73 checkperm('pull')
74
75 return False
@@ -148,10 +148,12 from mercurial import (
148 util,
148 util,
149 vfs as vfsmod,
149 vfs as vfsmod,
150 wireproto,
150 wireproto,
151 wireprotoserver,
151 )
152 )
152
153
153 from . import (
154 from . import (
154 blobstore,
155 blobstore,
156 wireprotolfsserver,
155 wrapper,
157 wrapper,
156 )
158 )
157
159
@@ -314,6 +316,8 def extsetup(ui):
314
316
315 wrapfunction(exchange, 'push', wrapper.push)
317 wrapfunction(exchange, 'push', wrapper.push)
316 wrapfunction(wireproto, '_capabilities', wrapper._capabilities)
318 wrapfunction(wireproto, '_capabilities', wrapper._capabilities)
319 wrapfunction(wireprotoserver, 'handlewsgirequest',
320 wireprotolfsserver.handlewsgirequest)
317
321
318 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
322 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
319 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
323 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
@@ -61,8 +61,13 def checkauthz(hgweb, req, op):
61 elif op == 'pull' or op is None: # op is None for interface requests
61 elif op == 'pull' or op is None: # op is None for interface requests
62 return
62 return
63
63
64 # Allow LFS uploading via PUT requests
65 if op == 'upload':
66 if req.method != 'PUT':
67 msg = 'upload requires PUT request'
68 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
64 # enforce that you can only push using POST requests
69 # enforce that you can only push using POST requests
65 if req.method != 'POST':
70 elif req.method != 'POST':
66 msg = 'push requires POST request'
71 msg = 'push requires POST request'
67 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
72 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
68
73
@@ -81,7 +86,7 def checkauthz(hgweb, req, op):
81
86
82 # Hooks for hgweb permission checks; extensions can add hooks here.
87 # Hooks for hgweb permission checks; extensions can add hooks here.
83 # Each hook is invoked like this: hook(hgweb, request, operation),
88 # Each hook is invoked like this: hook(hgweb, request, operation),
84 # where operation is either read, pull or push. Hooks should either
89 # where operation is either read, pull, push or upload. Hooks should either
85 # raise an ErrorResponse exception, or just return.
90 # raise an ErrorResponse exception, or just return.
86 #
91 #
87 # It is possible to do both authentication and authorization through
92 # It is possible to do both authentication and authorization through
@@ -112,6 +112,9 class _httprequesthandler(httpservermod.
112 self.log_error(r"Exception happened during processing "
112 self.log_error(r"Exception happened during processing "
113 r"request '%s':%s%s", self.path, newline, tb)
113 r"request '%s':%s%s", self.path, newline, tb)
114
114
115 def do_PUT(self):
116 self.do_POST()
117
115 def do_GET(self):
118 def do_GET(self):
116 self.do_POST()
119 self.do_POST()
117
120
General Comments 0
You need to be logged in to leave comments. Login now