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  }