109 lines
3.2 KiB
Nim
109 lines
3.2 KiB
Nim
import std/asyncdispatch, std/httpclient, std/strutils, std/uri
|
|
import ../blobsets
|
|
|
|
type
|
|
HttpBlobStream = ref HttpBlobStreamObj
|
|
HttpBlobStreamObj = object of BlobStreamObj
|
|
client: AsyncHttpClient
|
|
url: string
|
|
rangePos: BiggestInt
|
|
|
|
HttpIngestStream = ref HttpIngestStreamObj
|
|
HttpIngestStreamObj = object of IngestStreamObj
|
|
client: AsyncHttpClient
|
|
url: string
|
|
ctx: Blake2b256
|
|
leaves: seq[BlobId]
|
|
leaf: string
|
|
buffOff: int
|
|
pos, nodeOffset: BiggestInt
|
|
|
|
HttpStore* = ref HttpStoreObj
|
|
HttpStoreObj = object of BlobStoreObj
|
|
url: Uri
|
|
|
|
proc httpBlobClose(s: BlobStream) =
|
|
var s = (HttpBlobStream)s
|
|
close s.client
|
|
|
|
proc setPosHttp(s: BlobStream; pos: BiggestInt) =
|
|
var s = (HttpBlobStream)s
|
|
s.rangePos = pos
|
|
|
|
proc getPosHttp(s: BlobStream): BiggestInt =
|
|
var s = (HttpBlobStream)s
|
|
s.rangePos
|
|
|
|
proc httpBlobRead(s: BlobStream; buffer: pointer; len: Natural): int =
|
|
assert(not buffer.isNil)
|
|
var s = (HttpBlobStream)s
|
|
let
|
|
headers = newHttpHeaders({"range": "bytes=$1-$2" % [ $s.rangePos, $(s.rangePos+len) ]})
|
|
resp = waitFor s.client.request(s.url, HttpGET, headers=headers)
|
|
var body = waitFor resp.body
|
|
assert(not body.isNil)
|
|
result = (int)min(body.len, len)
|
|
if result > 0:
|
|
copyMem(buffer, body[0].addr, result)
|
|
s.rangePos.inc result
|
|
|
|
proc httpOpenBlobStream(store: BlobStore; id: BlobId; size: BiggestInt; kind: BlobKind): BlobStream =
|
|
var store = HttpStore(store)
|
|
let stream = HttpBlobStream(
|
|
client: newAsyncHttpClient(),
|
|
closeImpl: httpBlobClose,
|
|
setPosImpl: setPosHttp,
|
|
getPosImpl: getPosHttp,
|
|
readImpl: httpBlobRead,
|
|
url: $((store.url / $kind) / id.toHex)
|
|
)
|
|
let resp = waitFor stream.client.head(stream.url)
|
|
assert(resp.code in {Http200, Http204}, resp.status)
|
|
stream
|
|
|
|
proc httpFinish(s: IngestStream): tuple[id: BlobId, size: BiggestInt] =
|
|
var s = HttpIngestStream(s)
|
|
let resp = waitFor s.client.request($s.url, HttpPOST)
|
|
if not resp.code.is2xx:
|
|
raiseAssert(resp.status)
|
|
result.id = toBlobId resp.headers["blob-id"]
|
|
result.size = parseBiggestInt resp.headers["blob-size"]
|
|
|
|
proc httpIngest(x: IngestStream; buf: pointer; len: Natural) =
|
|
var
|
|
s = HttpIngestStream(x)
|
|
body = newString(len)
|
|
copyMem(body[0].addr, buf, body.len)
|
|
# TODO: zero-copy
|
|
let resp = waitFor s.client.request($s.url, HttpPUT, body)
|
|
if resp.code != Http204:
|
|
raiseAssert(resp.status)
|
|
|
|
proc httpOpenIngestStream(s: BlobStore; size: BiggestInt; kind: BlobKind): IngestStream =
|
|
var store = HttpStore(s)
|
|
let
|
|
client = newAsyncHttpClient()
|
|
url = (store.url / "ingest") / $kind
|
|
headers = newHttpHeaders({"ingest-size": $size})
|
|
resp = waitFor client.request($url, HttpPOST, headers=headers)
|
|
if resp.code == Http201:
|
|
var ingestUrl = parseUri resp.headers["Location"]
|
|
if not ingestUrl.isAbsolute:
|
|
ingestUrl = combine(store.url, ingestUrl)
|
|
result = HttpIngestStream(
|
|
finishImpl: httpFinish,
|
|
ingestImpl: httpIngest,
|
|
client: client,
|
|
url: $ingestUrl,
|
|
leaf: newString(min(size.int, blobLeafSize)),
|
|
)
|
|
else:
|
|
raiseAssert(resp.status)
|
|
|
|
proc newHttpStore*(url: string): HttpStore =
|
|
HttpStore(
|
|
url: parseUri url,
|
|
openBlobStreamImpl: httpOpenBlobStream,
|
|
openIngestStreamImpl: httpOpenIngestStream,
|
|
)
|