"flag"
"fmt"
"io"
+ "io/ioutil"
"log"
"net"
"net/http"
+ "os"
+ "os/exec"
"strings"
"sync"
"time"
acceptedM sync.RWMutex
rejected = make(map[string]string)
rejectedM sync.RWMutex
+
+ CmdDWebP = "dwebp"
+ CmdDJXL = "djxl"
)
func spkiHash(cert *x509.Certificate) string {
}
sinkReq <- fmt.Sprintf("%s %s", req.Method, req.URL.String())
host := strings.TrimSuffix(req.URL.Host, ":443")
+
for _, spy := range SpyDomains {
if strings.HasSuffix(host, spy) {
http.NotFound(w, req)
return
}
}
+
if strings.HasPrefix(req.URL.Host, "www.reddit.com") {
req.URL.Host = "old.reddit.com"
http.Redirect(w, req, req.URL.String(), http.StatusMovedPermanently)
return
}
+
resp, err := transport.RoundTrip(req)
if err != nil {
sinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
w.Write([]byte(err.Error()))
return
}
- contentType := resp.Header.Get("Content-Type")
- switch contentType {
+
+ for k, vs := range resp.Header {
+ if k == "Location" || k == "Content-Type" || k == "Content-Length" {
+ continue
+ }
+ for _, v := range vs {
+ w.Header().Add(k, v)
+ }
+ }
+
+ switch resp.Header.Get("Content-Type") {
case "application/font-woff", "application/font-sfnt":
// Those are deprecated types
fallthrough
)
resp.Body.Close()
return
- }
- for k, vs := range resp.Header {
- if k == "Location" || k == "Content-Type" || k == "Content-Length" {
- continue
+ case "image/webp":
+ if strings.Contains(req.Header.Get("User-Agent"), "AppleWebKit/538.15") {
+ // My Xombrero
+ break
}
- for _, v := range vs {
- w.Header().Add(k, v)
+ tmpFd, err := ioutil.TempFile("", "tofuproxy.*.webp")
+ if err != nil {
+ log.Fatalln(err)
+ }
+ defer tmpFd.Close()
+ defer os.Remove(tmpFd.Name())
+ defer resp.Body.Close()
+ if _, err = io.Copy(tmpFd, resp.Body); err != nil {
+ log.Printf("Error during %s: %+v\n", req.URL, err)
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
+ }
+ tmpFd.Close()
+ cmd := exec.Command(CmdDWebP, tmpFd.Name(), "-o", "-")
+ data, err := cmd.Output()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
}
+ w.Header().Add("Content-Type", "image/png")
+ w.WriteHeader(http.StatusOK)
+ w.Write(data)
+ sinkOther <- fmt.Sprintf(
+ "%s %s\t%d\tWebP transcoded to PNG",
+ req.Method,
+ req.URL.String(),
+ http.StatusOK,
+ )
+ return
+ case "image/jxl":
+ tmpFd, err := ioutil.TempFile("", "tofuproxy.*.jxl")
+ if err != nil {
+ log.Fatalln(err)
+ }
+ defer tmpFd.Close()
+ defer os.Remove(tmpFd.Name())
+ defer resp.Body.Close()
+ if _, err = io.Copy(tmpFd, resp.Body); err != nil {
+ log.Printf("Error during %s: %+v\n", req.URL, err)
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
+ }
+ tmpFd.Close()
+ dstFn := tmpFd.Name() + ".png"
+ cmd := exec.Command(CmdDJXL, tmpFd.Name(), dstFn)
+ err = cmd.Run()
+ defer os.Remove(dstFn)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
+ }
+ data, err := ioutil.ReadFile(dstFn)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadGateway)
+ return
+ }
+ w.Header().Add("Content-Type", "image/png")
+ w.WriteHeader(http.StatusOK)
+ w.Write(data)
+ sinkOther <- fmt.Sprintf(
+ "%s %s\t%d\tJPEG XL transcoded to PNG",
+ req.Method,
+ req.URL.String(),
+ http.StatusOK,
+ )
+ return
}
+
if req.Method == http.MethodGet {
var redirType string
switch resp.StatusCode {
)
return
}
+
NoRedir:
for _, h := range []string{"Location", "Content-Type", "Content-Length"} {
if v := resp.Header.Get(h); v != "" {