finish implementing torrent verb
This commit is contained in:
parent
9b0a84c02f
commit
4cb06525a4
2 changed files with 49 additions and 7 deletions
|
@ -6,6 +6,7 @@ from flask import redirect, Blueprint, abort, send_file, make_response
|
||||||
|
|
||||||
from app import app, signer, storage
|
from app import app, signer, storage
|
||||||
from auth.auth import process_auth
|
from auth.auth import process_auth
|
||||||
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth.permissions import ReadRepositoryPermission
|
from auth.permissions import ReadRepositoryPermission
|
||||||
from data import model, database
|
from data import model, database
|
||||||
from endpoints.trackhelper import track_and_log
|
from endpoints.trackhelper import track_and_log
|
||||||
|
@ -14,9 +15,10 @@ from storage import Storage
|
||||||
|
|
||||||
from util.registry.queuefile import QueueFile
|
from util.registry.queuefile import QueueFile
|
||||||
from util.registry.queueprocess import QueueProcess
|
from util.registry.queueprocess import QueueProcess
|
||||||
|
from util.registry.torrent import make_torrent, private_torrent_name
|
||||||
from formats.squashed import SquashedDockerImage
|
from formats.squashed import SquashedDockerImage
|
||||||
from formats.aci import ACIImage
|
from formats.aci import ACIImage
|
||||||
from endpoints.v2.blob import BASE_BLOB_ROUTE, blob_fetch
|
from endpoints.v2.blob import BLOB_DIGEST_ROUTE
|
||||||
|
|
||||||
|
|
||||||
verbs = Blueprint('verbs', __name__)
|
verbs = Blueprint('verbs', __name__)
|
||||||
|
@ -278,13 +280,17 @@ def get_squashed_tag(namespace, repository, tag):
|
||||||
|
|
||||||
|
|
||||||
@anon_protect
|
@anon_protect
|
||||||
@verbs.route('/torrent'+BASE_BLOB_ROUTE, methods=['GET'])
|
@verbs.route('/torrent{0}'.format(BLOB_DIGEST_ROUTE), methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
def get_tag_torrent(namespace, repository, digest):
|
def get_tag_torrent(namespace, repository, digest):
|
||||||
permission = ReadRepositoryPermission(namespace, repository)
|
permission = ReadRepositoryPermission(namespace, repository)
|
||||||
if not permission.can() and not model.repository.repository_is_public(namespace, repository):
|
if not permission.can() and not model.repository.repository_is_public(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
user = get_authenticated_user()
|
||||||
|
if user is None:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
blob = model.blob.get_repo_blob_by_digest(namespace, repository, digest)
|
blob = model.blob.get_repo_blob_by_digest(namespace, repository, digest)
|
||||||
except model.BlobDoesNotExist:
|
except model.BlobDoesNotExist:
|
||||||
|
@ -296,10 +302,17 @@ def get_tag_torrent(namespace, repository, digest):
|
||||||
# We cannot support webseeds for storages that cannot provide direct downloads.
|
# We cannot support webseeds for storages that cannot provide direct downloads.
|
||||||
abort(501)
|
abort(501)
|
||||||
|
|
||||||
|
try:
|
||||||
torrent_info = model.storage.get_torrent_info(blob)
|
torrent_info = model.storage.get_torrent_info(blob)
|
||||||
torrent_file = make_torrent(namespace, repository, digest, webseed,
|
except model.TorrentInfoDoesNotExist:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
name = private_torrent_name(user.uuid, blob.uuid)
|
||||||
|
torrent_file = make_torrent(name, webseed, blob.image_size,
|
||||||
torrent_info.piece_length, torrent_info.pieces)
|
torrent_info.piece_length, torrent_info.pieces)
|
||||||
|
|
||||||
torrent_file = None
|
headers = {'Content-Type': 'application/x-bittorrent',
|
||||||
return make_response((torrent_file, 200, {'Content-Type': 'application/x-bittorrent',
|
'Content-Disposition': 'attachment; filename={0}.torrent'.format(name),
|
||||||
'Content-Length': len(torrent_file)}))
|
'Content-Length': len(torrent_file)}
|
||||||
|
|
||||||
|
return make_response((torrent_file, 200, headers))
|
||||||
|
|
|
@ -1,5 +1,34 @@
|
||||||
|
import time
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import bencode
|
||||||
import resumablehashlib
|
import resumablehashlib
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
|
||||||
|
|
||||||
|
TRACKER_ANNOUNCE_URL = app.config.get('BT_TRACKER_ANNOUNCE_URL')
|
||||||
|
NAMING_SALT = app.config.get('BT_NAMING_SALT')
|
||||||
|
|
||||||
|
def private_torrent_name(user_uuid, blob_uuid):
|
||||||
|
return hashlib.sha256(blob_uuid + user_uuid + NAMING_SALT).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def make_torrent(name, webseed, length, piece_length, pieces):
|
||||||
|
return bencode.bencode({
|
||||||
|
'announce': TRACKER_ANNOUNCE_URL,
|
||||||
|
'url-list': webseed,
|
||||||
|
'encoding': 'UTF-8',
|
||||||
|
'created by': 'Quay Container Registry',
|
||||||
|
'creation date': int(time.time()),
|
||||||
|
'info': {
|
||||||
|
'name': name,
|
||||||
|
'length': length,
|
||||||
|
'piece length': piece_length,
|
||||||
|
'pieces': pieces,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class PieceHasher(object):
|
class PieceHasher(object):
|
||||||
def __init__(self, piece_size, starting_offset, starting_piece_hash_bytes, hash_fragment_to_resume):
|
def __init__(self, piece_size, starting_offset, starting_piece_hash_bytes, hash_fragment_to_resume):
|
||||||
|
|
Reference in a new issue