Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14348019
D15492.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D15492.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2501,6 +2501,7 @@
'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php',
'PhabricatorIteratedMD5PasswordHasher' => 'infrastructure/util/password/PhabricatorIteratedMD5PasswordHasher.php',
'PhabricatorIteratedMD5PasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorIteratedMD5PasswordHasherTestCase.php',
+ 'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php',
'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php',
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
@@ -6947,6 +6948,7 @@
'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher',
'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource',
'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider',
'PhabricatorJavelinLinter' => 'ArcanistLinter',
'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType',
diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php
--- a/src/applications/diffusion/controller/DiffusionServeController.php
+++ b/src/applications/diffusion/controller/DiffusionServeController.php
@@ -891,7 +891,12 @@
}
$path = $this->getGitLFSRequestPath($repository);
- if ($path == 'objects/batch') {
+ $matches = null;
+
+ if (preg_match('(^upload/(.*)\z)', $path, $matches)) {
+ $oid = $matches[1];
+ return $this->serveGitLFSUploadRequest($repository, $viewer, $oid);
+ } else if ($path == 'objects/batch') {
return $this->serveGitLFSBatchRequest($repository, $viewer);
} else {
return DiffusionGitLFSResponse::newErrorResponse(
@@ -947,7 +952,7 @@
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
- ->withPHIDs(array($file_phids))
+ ->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
@@ -960,6 +965,7 @@
$oid = idx($object, 'oid');
$size = idx($object, 'size');
$ref = idx($refs, $oid);
+ $error = null;
// NOTE: If we already have a ref for this object, we only emit a
// "download" action. The client should not upload the file again.
@@ -968,9 +974,26 @@
if ($ref) {
$file = idx($files, $ref->getFilePHID());
if ($file) {
+ // Git LFS may prompt users for authentication if the action does
+ // not provide an "Authorization" header and does not have a query
+ // parameter named "token". See here for discussion:
+ // <https://github.com/github/git-lfs/issues/1088>
+ $no_authorization = 'Basic '.base64_encode('none');
+
$get_uri = $file->getCDNURIWithToken();
$actions['download'] = array(
'href' => $get_uri,
+ 'header' => array(
+ 'Authorization' => $no_authorization,
+ ),
+ );
+ } else {
+ $error = array(
+ 'code' => 404,
+ 'message' => pht(
+ 'Object "%s" was previously uploaded, but no longer exists '.
+ 'on this server.',
+ $oid),
);
}
} else if ($want_upload) {
@@ -995,11 +1018,20 @@
);
}
- $output[] = array(
+ $object = array(
'oid' => $oid,
'size' => $size,
- 'actions' => $actions,
);
+
+ if ($actions) {
+ $object['actions'] = $actions;
+ }
+
+ if ($error) {
+ $object['error'] = $error;
+ }
+
+ $output[] = $object;
}
$output = array(
@@ -1010,6 +1042,69 @@
->setContent($output);
}
+ private function serveGitLFSUploadRequest(
+ PhabricatorRepository $repository,
+ PhabricatorUser $viewer,
+ $oid) {
+
+ $ref = id(new PhabricatorRepositoryGitLFSRefQuery())
+ ->setViewer($viewer)
+ ->withRepositoryPHIDs(array($repository->getPHID()))
+ ->withObjectHashes(array($oid))
+ ->executeOne();
+ if ($ref) {
+ return DiffusionGitLFSResponse::newErrorResponse(
+ 405,
+ pht(
+ 'Content for object "%s" is already known to this server. It can '.
+ 'not be uploaded again.',
+ $oid));
+ }
+
+ $request_stream = new AphrontRequestStream();
+ $request_iterator = $request_stream->getIterator();
+ $hashing_iterator = id(new PhutilHashingIterator($request_iterator))
+ ->setAlgorithm('sha256');
+
+ $source = id(new PhabricatorIteratorFileUploadSource())
+ ->setName('lfs-'.$oid)
+ ->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
+ ->setIterator($hashing_iterator);
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $file = $source->uploadFile();
+ unset($unguarded);
+
+ $hash = $hashing_iterator->getHash();
+ if ($hash !== $oid) {
+ return DiffusionGitLFSResponse::newErrorResponse(
+ 400,
+ pht(
+ 'Uploaded data is corrupt or invalid. Expected hash "%s", actual '.
+ 'hash "%s".',
+ $oid,
+ $hash));
+ }
+
+ $ref = id(new PhabricatorRepositoryGitLFSRef())
+ ->setRepositoryPHID($repository->getPHID())
+ ->setObjectHash($hash)
+ ->setByteSize($file->getByteSize())
+ ->setAuthorPHID($viewer->getPHID())
+ ->setFilePHID($file->getPHID());
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ // Attach the file to the repository to give users permission
+ // to access it.
+ $file->attachToObject($repository->getPHID());
+ $ref->save();
+ unset($unguarded);
+
+ // This is just a plain HTTP 200 with no content, which is what `git lfs`
+ // expects.
+ return new DiffusionGitLFSResponse();
+ }
+
private function newGitLFSHTTPAuthorization(
PhabricatorRepository $repository,
PhabricatorUser $viewer,
diff --git a/src/applications/files/uploadsource/PhabricatorFileUploadSource.php b/src/applications/files/uploadsource/PhabricatorFileUploadSource.php
--- a/src/applications/files/uploadsource/PhabricatorFileUploadSource.php
+++ b/src/applications/files/uploadsource/PhabricatorFileUploadSource.php
@@ -72,7 +72,9 @@
$data->rewind();
$this->didRewind = true;
} else {
- $data->next();
+ if ($data->valid()) {
+ $data->next();
+ }
}
if (!$data->valid()) {
diff --git a/src/applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php b/src/applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php
@@ -0,0 +1,25 @@
+<?php
+
+final class PhabricatorIteratorFileUploadSource
+ extends PhabricatorFileUploadSource {
+
+ private $iterator;
+
+ public function setIterator(Iterator $iterator) {
+ $this->iterator = $iterator;
+ return $this;
+ }
+
+ public function getIterator() {
+ return $this->iterator;
+ }
+
+ protected function newDataIterator() {
+ return $this->getIterator();
+ }
+
+ protected function getDataLength() {
+ return null;
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Dec 20, 4:59 AM (20 h, 44 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6909549
Default Alt Text
D15492.diff (7 KB)
Attached To
Mode
D15492: Support pushing data into Git LFS
Attached
Detach File
Event Timeline
Log In to Comment