duandanai6470
duandanai6470
2019-07-24 12:57

解密后的反向代理服务文件

已采纳

I need to put an *os.File into the request.Body but I don't know how to handle the closure of the file descriptor (and the elimination of the temporary file). I cannot use http.ServeFile to serve the file.

I have 3 softwares: A) User interface ( that I cannot edit ) B) Backend which serves files C) An http reverse proxy whose role is to sign the requests coming from A going to B, to encrypt files going from A to B, and decrypto files going from B to A.

The software A is expected to receive the body of the decrypted file content. I know it would be easier just to decrypt the file in a directory using B and then have A open it, but unfortunately I cannot edit A which expects the file in the request body.

the work flow of the softwares is the following: software A sends a request to C which signs the request and forwards it to B, then C proceeds to decrypt the file and has to serve the decrypted content to A in a the request body.

type transportPageServeFile struct {
    http.RoundTripper
    // vault handles encryption and decryption of files
    vault *vault.Container
}

func (t *transportPageServeFile) RoundTrip(req *http.Request) (resp *http.Response, err error) {
    resp, err = t.RoundTripper.RoundTrip(req)
    if err != nil {
        return nil, errors.Wrap(err, "transportPageServeFile")
    }
    // create temporary destination dir for the decrypted file
    dstDir, err := utils.MkTmpDir()
    if err != nil {
        return nil, errors.Wrap(err, "transportPageServeFile")
    }
    defer os.RemoveAll(dstDir) // i'm deleting the directory where the temporary file is stored, deleting it before it even gets transmitted to software A
    fileName := req.Header.Get(api.FileNameHeader)
    decryptedFileDst := fmt.Sprintf("%s/%s", dstDir, fileName)
    // DecryptFileFromReader takes a reader and decrypts the file, saving it in the decided destination
    err = t.vault.DecryptFileFromReader(resp.Body, decryptedFileDst)
    if err != nil {
        return nil, errors.Wrap(err, "transportPageServeFile")
    }
    defer resp.Body.Close()
    // now open the saved decrypted file
    decryptedFile, err := os.Open(decryptedFileDst)
    if err != nil {
        return nil, errors.Wrap(err,"transportPageServeFile")
    }
    defer decryptedFile.Close() // close file before serving the content?
    resp.Body = decryptedFile
    return resp, nil
}
// pageServeFile is the handler of /serveFile
func (rpc *RPC) pageServeFile(c *gin.Context) {
    transport := &transportPageServeFile{RoundTripper:http.DefaultTransport, vault:rpc.vault}
    target, err := url.Parse(rpc.apiEndpoint)
    if err != nil {
        rpc.onError(c, errors.Wrapf(err, "pageServeFile"))
        return
    }
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.Transport = transport
    proxy.ServeHTTP(c.Writer, c.Request)
}

How do I copy the file in the request body (using a reader) and when/how do I close the file descriptor once the response is forwarded?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • dongmu2517 dongmu2517 2年前

    The response body is an io.ReadCloser, so it is reasonable to assume that the reverse proxy calls Close. And indeed it does, as you can see by running the following program:

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "net/http/httptest"
        "net/http/httputil"
        "net/url"
        "runtime/debug"
    )
    
    type dbg struct{ io.ReadCloser }
    
    func (d dbg) Close() error {
        fmt.Println("Close() called:")
        debug.PrintStack()
    
        return d.ReadCloser.Close()
    }
    
    func main() {
        // srv simulates "software B"
        srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("Hello, world"))
        }))
    
        target, _ := url.Parse(srv.URL)
        p := httputil.NewSingleHostReverseProxy(target)
    
        // To modify the response, use the aptly named ModifyResponse field; no
        // need to implement a RoundTripper.
        p.ModifyResponse = func(res *http.Response) error {
            res.Body = dbg{res.Body}
            return nil
        }
    
        req := httptest.NewRequest("GET", "http://example.com/hello", nil)
        rec := httptest.NewRecorder()
    
        p.ServeHTTP(rec, req)
    }
    
    // Output:
    // Close() called:
    // goroutine 1 [running]:
    // runtime/debug.Stack(0x10, 0x0, 0x0)
    //         /usr/local/go/src/runtime/debug/stack.go:24 +0x9d
    // runtime/debug.PrintStack()
    //         /usr/local/go/src/runtime/debug/stack.go:16 +0x22
    // main.dbg.Close(0x75b780, 0xc00013e140, 0xc000152000, 0x7f61fc523088)
    //         /tmp/tmp.H9O7cizbkv/main.go:17 +0x7f
    // net/http/httputil.(*ReverseProxy).ServeHTTP(0xc0000ac0f0, 0x75e460, 0xc000032440, 0xc00010a000)
    //         /usr/local/go/src/net/http/httputil/reverseproxy.go:311 +0x8a6
    // main.main()
    //         /tmp/tmp.H9O7cizbkv/main.go:41 +0x152
    
    点赞 评论 复制链接分享

相关推荐