func (h *reqHandler) getUpstreamReader(start, end uint64) io.ReadCloser { subh := *h subh.req = subh.getNormalizedRequest() subh.req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end)) h.Logger.Debugf("[%p] Making upstream request for %s, bytes [%d-%d]...", subh.req, subh.req.URL, start, end) //!TODO: optimize requests for the same pieces? if possible, make only 1 request to the upstream for the same part r, w := io.Pipe() subh.resp = httputils.NewFlexibleResponseWriter(func(rw *httputils.FlexibleResponseWriter) { respRng, err := httputils.GetResponseRange(rw.Code, rw.Headers) if err != nil { h.Logger.Errorf("[%p] Could not parse the content-range for the partial upstream request: %s", subh.req, err) _ = w.CloseWithError(err) } h.Logger.Debugf("[%p] Received response with status %d and range %v", subh.req, rw.Code, respRng) if rw.Code == http.StatusPartialContent { //!TODO: check whether the returned range corresponds to the requested range rw.BodyWriter = w } else if rw.Code == http.StatusOK { //!TODO: handle this, use skipWriter or something like that _ = w.CloseWithError(fmt.Errorf("NOT IMPLEMENTED")) } else { _ = w.CloseWithError(fmt.Errorf("Upstream responded with status %d", rw.Code)) } }) go subh.carbonCopyProxy() return newWholeChunkReadCloser(r, h.Cache.PartSize.Bytes()) }
func (h *Headers) wrapResponseWriter(w http.ResponseWriter) http.ResponseWriter { var newW = httputils.NewFlexibleResponseWriter(func(frw *httputils.FlexibleResponseWriter) { httputils.CopyHeaders(frw.Header(), w.Header()) h.response.rewrite(w.Header()) frw.BodyWriter = utils.NopCloser(w) w.WriteHeader(frw.Code) }) httputils.CopyHeaders(w.Header(), newW.Header()) return newW }
func (h *reqHandler) carbonCopyProxy() { flexibleResp := httputils.NewFlexibleResponseWriter(h.getResponseHook()) defer func() { if flexibleResp.BodyWriter != nil { if err := flexibleResp.BodyWriter.Close(); err != nil { h.Logger.Errorf("[%p] Error while closing flexibleResponse: %s", h.req, err) } } //!TODO: cache small upstream responses that we did not cache because // there was no Content-Length header in the upstream response but it // was otherwise cacheable? Examples are folder listings for apache and // ngingx: `curl -i http://mirror.rackspace.com/` or `curl -i // https://mirrors.uni-plovdiv.net/` }() h.next.RequestHandle(h.ctx, flexibleResp, h.getNormalizedRequest()) }
func (rr *rangeReader) Range(start, length uint64) io.ReadCloser { newreq := copyRequest(rr.req) newreq.Header.Set("Range", httputils.Range{Start: start, Length: length}.Range()) var in, out = io.Pipe() flexible := httputils.NewFlexibleResponseWriter(func(frw *httputils.FlexibleResponseWriter) { if frw.Code != http.StatusPartialContent || !rr.callback(frw) { _ = out.CloseWithError(errUnsatisfactoryResponse) } frw.BodyWriter = out }) go func() { defer func() { if err := out.Close(); err != nil { rr.location.Logger.Errorf("handler.mp4[%p]: error on closing rangeReaders output: %s", rr.req, err) } }() rr.next.RequestHandle(rr.ctx, flexible, newreq) }() return in }