How to wrap a Go package with only a few modifications?

574 views Asked by At

How can I write a Go package that wraps a library such that calls to overridden functions use my implementation, and non-overridden functions "fall through" to the library I am wrapping?

In particular: I want my Go package to wrap net/http, except I initially want to only replace http.FileServer and http.NotFoundHandler, and leave all other functions the same. My library is a drop-in replacement for existing code that calls other net/http functions I won't overwrite. For example, I want to be able to do:

package main

import (
    "log"
    http "github.com/jstrieb/my-special-http-lib"
)

func main() {
    http.ListenAndServe(                            // Use the net/http ListenAndServe by "falling through" my library
        ":8080", 
        http.FileServer(http.Dir("/usr/share/doc")) // Use my custom, overridden http.FileServer
    )
}

What I have tried

I could manually override each function exported by the wrapped library (like below), but I would rather avoid this if possible. This approach is undesirable because it doesn't account for instances where functions I override are called from within the library I am wrapping.

func ExportedFunction(input1 type1) type2 {
    return http.ExportedFunction(input1)
}

I could also fully fork the net/http source and change it directly, but I want it to be clear what changes I am making without having to compare against the original version. It also doesn't makes sense to maintain a fork of part of the standard library to only override a few functions.


Justification

I am not looking for commentary on whether this is a "good" idea. I just want to know how to do it.

The plan for this library is to simply change the appearance of the 404 page and the directory listing index page. This purely aesthetic change does not affect the underlying functionality or API of net/http. If it was not structured to wrap all of net/http, then a user would have to switch between using two packages to do the same thing. Then my library could not be considered a "drop-in replacement" for code that already uses net/http.

I also intend to override more functions over time, but the API of my library will always match that of net/http. Doing it this way reduces the need to manually replace calls like http.Function with mylibrary.Function every time the library changes. Moreover, I want to be able to import my replacement in code (using net/http) that I did not write, and don't want to manually refactor.

2

There are 2 answers

0
Makoto On

Not a Go expert, but speaking anecdotally, this would introduce a significant vulnerability if by specifying one dependency you were then able to side-load a different version of it instead with whatever modifications you wanted.

It might speak to the notion of, if this dependency is something you can bring into your application, then you should build a library which builds off the one you want to modify, and import that one in instead. That is to say, given your application A and library B, with your modified library B', you would want to write your application such that A depends on B' which depends on B, which would make the relationship apparent. If you wanted to somehow have A depend on B but be able to side-load B' in dynamically, then that would represent the vulnerability that I allude to earlier.

0
advay rajhansa On

What you are trying to achieve here is extend the functionality of package. Simple answer here is you cannot do so as of now. Best possible way to achieve this is just doing this manually.
Personally I don't see any harm in having two packages seperate. I this it is more maintainable. Just wrap the functions you want to update. Sometimes all you need is a function.