(refs #1101)Add original GitLFS Transfer API implementation

This commit is contained in:
Naoki Takezoe
2017-01-04 07:09:56 +09:00
parent bfc88a489a
commit 9cded1b4de
6 changed files with 118 additions and 7 deletions

View File

@@ -57,7 +57,7 @@ abstract class ControllerBase extends ScalatraFilter
// Redirect to dashboard
httpResponse.sendRedirect(baseUrl + "/")
}
} else if(path.startsWith("/git/")){
} else if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
// Git repository
chain.doFilter(request, response)
} else {

View File

@@ -315,9 +315,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}.toMap
response.setContentLength(attrs("size").toInt)
val hash = attrs("oid").split(":")(1)
val oid = attrs("oid").split(":")(1)
using(new FileInputStream(Directory.LfsHome + "/" + hash.substring(0, 2) + "/" + hash.substring(2, 4) + "/" + hash)){ in =>
using(new FileInputStream(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)){ in =>
IOUtils.copy(in, response.getOutputStream)
}
} else {

View File

@@ -0,0 +1,78 @@
package gitbucket.core.servlet
import java.io.{File, FileInputStream, FileOutputStream}
import java.text.MessageFormat
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
import gitbucket.core.util.Directory
import org.apache.commons.io.{FileUtils, IOUtils}
import org.json4s.jackson.Serialization._
import org.apache.http.HttpStatus
import gitbucket.core.util.ControlUtil._
/**
* Provides GitLFS Transfer API
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
*/
class GitLfsTransferServlet extends HttpServlet {
private implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
private val LongObjectIdLength = 32
private val LongObjectIdStringLength = LongObjectIdLength * 2
override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
for {
oid <- getObjectId(req, res)
} yield {
val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
if(file.exists()){
res.setStatus(HttpStatus.SC_OK)
res.setContentType("application/octet-stream")
res.setContentLength(file.length.toInt)
using(new FileInputStream(file), res.getOutputStream){ (in, out) =>
IOUtils.copy(in, out)
out.flush()
}
} else {
sendError(res, HttpStatus.SC_NOT_FOUND,
MessageFormat.format("Object ''{0}'' not found", oid))
}
}
}
override protected def doPut(req: HttpServletRequest, res: HttpServletResponse): Unit = {
for {
oid <- getObjectId(req, res)
} yield {
val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
FileUtils.forceMkdir(file.getParentFile)
using(req.getInputStream, new FileOutputStream(file)){ (in, out) =>
IOUtils.copy(in, out)
}
res.setStatus(HttpStatus.SC_OK)
}
}
private def getObjectId(req: HttpServletRequest, rsp: HttpServletResponse): Option[String] = {
val info: String = req.getPathInfo
val length: Int = 1 + LongObjectIdStringLength
if (info.length != length) {
sendError(rsp, HttpStatus.SC_UNPROCESSABLE_ENTITY,
MessageFormat.format("Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}'''", info))
None
} else {
Some(info.substring(1, length))
}
}
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
res.setStatus(status)
using(res.getWriter()){ out =>
out.write(write(GitLfs.Error(message)))
out.flush()
}
}
}

View File

@@ -61,6 +61,10 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
}
}
/**
* Provides GitLFS Batch API
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
*/
protected def serviceGitLfsBatchAPI(req: HttpServletRequest, res: HttpServletResponse): Unit = {
val batchRequest = read[GitLfs.BatchRequest](req.getInputStream)
val settings = loadSystemSettings()
@@ -96,10 +100,10 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
}
res.setContentType("application/vnd.git-lfs+json")
val out = res.getWriter
out.print(write(batchResponse))
out.flush()
using(res.getWriter){ out =>
out.print(write(batchResponse))
out.flush()
}
}
}
}
@@ -317,4 +321,8 @@ object GitLfs {
expires_at: Date
)
case class Error(
message: String
)
}

View File

@@ -20,6 +20,20 @@ object ControlUtil {
}
}
def using[A <% { def close(): Unit }, B <% { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
try f(resource1, resource2) finally {
if(resource1 != null){
ignoring(classOf[Throwable]) {
resource1.close()
}
}
if(resource2 != null){
ignoring(classOf[Throwable]) {
resource2.close()
}
}
}
def using[T](git: Git)(f: Git => T): T =
try f(git) finally git.getRepository.close()

View File

@@ -46,6 +46,17 @@
<url-pattern>/git/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>GitLfsTransferServlet</servlet-name>
<servlet-class>gitbucket.core.servlet.GitLfsTransferServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GitLfsTransferServlet</servlet-name>
<url-pattern>/git-lfs/*</url-pattern>
</servlet-mapping>
<!-- ===================================================================== -->
<!-- Supply assets which are provided by plugins -->
<!-- ===================================================================== -->