in app/controllers/Application.scala [138:202]
def streamTargetContent(targetUriString:String) = IsAuthenticatedAsync { uid=> request=>
val maybeTargetUri = Try {
URI.create(targetUriString)
}
val maybeLocator = maybeTargetUri.flatMap(targetUri => OMLocator.fromUri(targetUri))
/*
look up the object, using cache if possible, and get hold of the metadata
*/
val objectEntryFut = Future.fromTry(maybeLocator).flatMap(locator=>{
(objectCache ? Lookup(locator)).mapTo[OCMsg].map({
case ObjectNotFound(_) =>
val auditFile = AuditFile("",locator.filePath)
auditActor ! actors.Audit.LogEvent(AuditEvent.NOTFOUND, uid, Some(auditFile), Seq())
Left(NotFound(GenericErrorResponse("not_found", s"no object at $targetUriString").asJson))
case ObjectLookupFailed(_, err) =>
val auditFile = AuditFile("",locator.filePath)
auditActor ! actors.Audit.LogEvent(AuditEvent.OMERROR, uid, Some(auditFile), Seq(),notes=Some(err.toString))
logger.error(s"Could not look up object for $targetUriString: ", err)
Left(InternalServerError(GenericErrorResponse("server_error", s"lookup failed for $targetUriString").asJson))
case ObjectFound(_, objectEntry) =>
Right(objectEntry)
})
})
/*
break down the ranges header into structured data
*/
val rangesOrFailureFut = Future.fromTry(request.headers.get("Range") match {
case Some(hdr)=>RangeHeader.fromStringHeader(hdr)
case None=>Success(Seq())
})
/*
get hold of a streaming source, if possible
*/
//maybeLocator.get is safe because if maybeLocator is a Failure we don't execute this block
val srcOrFailureFut = getSourceFuture(maybeLocator.get, objectEntryFut, rangesOrFailureFut, uid)
/*
now either manifest the source to stream data to the client or output an error response to explain why we couldn't
*/
srcOrFailureFut.map({
case Right((byteSource, maybeResponseSize, headers, maybeMimetype, isPartialTransfer)) =>
logger.info(s"maybeResponseSize is $maybeResponseSize")
Result(
ResponseHeader(if(isPartialTransfer) 206 else 200, headers),
HttpEntity.Streamed(byteSource.log("outputstream").addAttributes(
Attributes.logLevels(
onElement = Attributes.LogLevels.Info,
onFailure = Attributes.LogLevels.Error,
onFinish = Attributes.LogLevels.Info)), None, maybeMimetype)
) //we can't give a proper content length, because if we are sending multipart chunks that adds overhead to the request size.
case Left(response)=>response
}).recover({
case err:BadDataError=>
BadRequest(err.getMessage)
case err:Throwable=>
logger.error(s"Could not get data for $targetUriString: ", err)
InternalServerError("see the logs for more information")
})
}