Version of :meth:`raw_datastream` without conditionals, for use
    in class-based views or elsewhere.

def _raw_datastream(request, pid, dsid, repo=None, headers=None,
       as_of_date=None):
    '''Version of :meth:`raw_datastream` without conditionals, for use
    in class-based views or elsewhere.'''
    if repo is None:
        repo = Repository()

    # if a range request is present, pass it through to fedora
    rqst_headers = {}
    if 'HTTP_RANGE' in request.META:
        rqst_headers['RANGE'] = request.META['HTTP_RANGE']

    try:
        if request.method == 'HEAD':
            response = repo.api.getDatastreamDissemination(pid, dsid, asOfDateTime=as_of_date,
                head=True, rqst_headers=rqst_headers)
            dj_response = HttpResponse()
        else:
            response = repo.api.getDatastreamDissemination(pid, dsid, asOfDateTime=as_of_date,
                stream=True, rqst_headers=rqst_headers)
            dj_response = StreamingHttpResponse(response.iter_content(4096))
    except RequestFailed as rf:
        # if error is object not found, raise generic django 404
        if rf.code == 404:
            raise Http404

        # otherwise, raise the error
        raise

    # make sure django response code matches fedora code
    # e.g. error code or 206 partial content for range requests
    dj_response.status_code = response.status_code

    # copy fedora response headers to the django response
    resp_headers = response.headers
    # any headers passed in should take precedence
    if headers is not None:
        resp_headers.update(headers)
    # etag needn't always be content md5, but for fedora datastreams it is
    if 'ETag' in resp_headers:
        resp_headers['Content-MD5'] = resp_headers['ETag']

    for header, value in six.iteritems(resp_headers):
        dj_response[header] = value

    return dj_response