github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/cmd/serve/dlna/dlna_util.go (about) 1 package dlna 2 3 import ( 4 "crypto/md5" 5 "encoding/xml" 6 "fmt" 7 "io" 8 "log" 9 "net" 10 "net/http" 11 "net/http/httptest" 12 "net/http/httputil" 13 "os" 14 15 "github.com/anacrolix/dms/soap" 16 "github.com/anacrolix/dms/upnp" 17 "github.com/ncw/rclone/fs" 18 ) 19 20 // Return a default "friendly name" for the server. 21 func makeDefaultFriendlyName() string { 22 hostName, err := os.Hostname() 23 if err != nil { 24 hostName = "" 25 } else { 26 hostName = " (" + hostName + ")" 27 } 28 return "rclone" + hostName 29 } 30 31 func makeDeviceUUID(unique string) string { 32 h := md5.New() 33 if _, err := io.WriteString(h, unique); err != nil { 34 log.Panicf("makeDeviceUUID write failed: %s", err) 35 } 36 buf := h.Sum(nil) 37 return upnp.FormatUUID(buf) 38 } 39 40 // Get all available active network interfaces. 41 func listInterfaces() []net.Interface { 42 ifs, err := net.Interfaces() 43 if err != nil { 44 log.Printf("list network interfaces: %v", err) 45 return []net.Interface{} 46 } 47 48 var active []net.Interface 49 for _, intf := range ifs { 50 if intf.Flags&net.FlagUp == 0 || intf.MTU <= 0 { 51 continue 52 } 53 active = append(active, intf) 54 } 55 return active 56 } 57 58 func didlLite(chardata string) string { 59 return `<DIDL-Lite` + 60 ` xmlns:dc="http://purl.org/dc/elements/1.1/"` + 61 ` xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"` + 62 ` xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"` + 63 ` xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">` + 64 chardata + 65 `</DIDL-Lite>` 66 } 67 68 func mustMarshalXML(value interface{}) []byte { 69 ret, err := xml.MarshalIndent(value, "", " ") 70 if err != nil { 71 log.Panicf("mustMarshalXML failed to marshal %v: %s", value, err) 72 } 73 return ret 74 } 75 76 // Marshal SOAP response arguments into a response XML snippet. 77 func marshalSOAPResponse(sa upnp.SoapAction, args map[string]string) []byte { 78 soapArgs := make([]soap.Arg, 0, len(args)) 79 for argName, value := range args { 80 soapArgs = append(soapArgs, soap.Arg{ 81 XMLName: xml.Name{Local: argName}, 82 Value: value, 83 }) 84 } 85 return []byte(fmt.Sprintf(`<u:%[1]sResponse xmlns:u="%[2]s">%[3]s</u:%[1]sResponse>`, 86 sa.Action, sa.ServiceURN.String(), mustMarshalXML(soapArgs))) 87 } 88 89 type loggingResponseWriter struct { 90 http.ResponseWriter 91 request *http.Request 92 committed bool 93 } 94 95 func (lrw *loggingResponseWriter) logRequest(code int, err interface{}) { 96 // Choose appropriate log level based on response status code. 97 var level fs.LogLevel 98 if code < 400 && err == nil { 99 level = fs.LogLevelInfo 100 } else { 101 level = fs.LogLevelError 102 } 103 104 fs.LogPrintf(level, lrw.request.URL.Path, "%s %s %d %s %s", 105 lrw.request.RemoteAddr, lrw.request.Method, code, 106 lrw.request.Header.Get("SOAPACTION"), err) 107 } 108 109 func (lrw *loggingResponseWriter) WriteHeader(code int) { 110 lrw.committed = true 111 lrw.logRequest(code, nil) 112 lrw.ResponseWriter.WriteHeader(code) 113 } 114 115 // HTTP handler that logs requests and any errors or panics. 116 func logging(next http.Handler) http.Handler { 117 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 118 lrw := &loggingResponseWriter{ResponseWriter: w, request: r} 119 defer func() { 120 err := recover() 121 if err != nil { 122 if !lrw.committed { 123 lrw.logRequest(http.StatusInternalServerError, err) 124 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) 125 } else { 126 // Too late to send the error to client, but at least log it. 127 fs.Errorf(r.URL.Path, "Recovered panic: %v", err) 128 } 129 } 130 }() 131 next.ServeHTTP(lrw, r) 132 }) 133 } 134 135 // HTTP handler that logs complete request and response bodies for debugging. 136 // Error recovery and general request logging are left to logging(). 137 func traceLogging(next http.Handler) http.Handler { 138 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 139 dump, err := httputil.DumpRequest(r, true) 140 if err != nil { 141 serveError(nil, w, "error dumping request", err) 142 return 143 } 144 fs.Debugf(nil, "%s", dump) 145 146 recorder := httptest.NewRecorder() 147 next.ServeHTTP(recorder, r) 148 149 dump, err = httputil.DumpResponse(recorder.Result(), true) 150 if err != nil { 151 // log the error but ignore it 152 fs.Errorf(nil, "error dumping response: %v", err) 153 } else { 154 fs.Debugf(nil, "%s", dump) 155 } 156 157 // copy from recorder to the real response writer 158 for k, v := range recorder.Header() { 159 w.Header()[k] = v 160 } 161 w.WriteHeader(recorder.Code) 162 _, err = recorder.Body.WriteTo(w) 163 if err != nil { 164 // Network error 165 fs.Debugf(nil, "Error writing response: %v", err) 166 } 167 }) 168 } 169 170 // HTTP handler that sets headers. 171 func withHeader(name string, value string, next http.Handler) http.Handler { 172 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 173 w.Header().Set(name, value) 174 next.ServeHTTP(w, r) 175 }) 176 } 177 178 // serveError returns an http.StatusInternalServerError and logs the error 179 func serveError(what interface{}, w http.ResponseWriter, text string, err error) { 180 fs.CountError(err) 181 fs.Errorf(what, "%s: %v", text, err) 182 http.Error(w, text+".", http.StatusInternalServerError) 183 }