github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/serve/dlna/dlna_test.go (about) 1 package dlna 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "html" 8 "io/ioutil" 9 "net/http" 10 "os" 11 "strings" 12 "testing" 13 14 "github.com/anacrolix/dms/soap" 15 16 "github.com/rclone/rclone/vfs" 17 18 _ "github.com/rclone/rclone/backend/local" 19 "github.com/rclone/rclone/cmd/serve/dlna/dlnaflags" 20 "github.com/rclone/rclone/fs" 21 "github.com/rclone/rclone/fs/config" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 var ( 27 dlnaServer *server 28 baseURL string 29 ) 30 31 const ( 32 testBindAddress = "localhost:0" 33 ) 34 35 func startServer(t *testing.T, f fs.Fs) { 36 opt := dlnaflags.DefaultOpt 37 opt.ListenAddr = testBindAddress 38 dlnaServer = newServer(f, &opt) 39 assert.NoError(t, dlnaServer.Serve()) 40 baseURL = "http://" + dlnaServer.HTTPConn.Addr().String() 41 } 42 43 func TestInit(t *testing.T) { 44 config.LoadConfig() 45 46 f, err := fs.NewFs("testdata/files") 47 l, _ := f.List(context.Background(), "") 48 fmt.Println(l) 49 require.NoError(t, err) 50 51 startServer(t, f) 52 } 53 54 // Make sure that it serves rootDesc.xml (SCPD in uPnP parlance). 55 func TestRootSCPD(t *testing.T) { 56 req, err := http.NewRequest("GET", baseURL+rootDescPath, nil) 57 require.NoError(t, err) 58 resp, err := http.DefaultClient.Do(req) 59 require.NoError(t, err) 60 assert.Equal(t, http.StatusOK, resp.StatusCode) 61 body, err := ioutil.ReadAll(resp.Body) 62 require.NoError(t, err) 63 // Make sure that the SCPD contains a CDS service. 64 require.Contains(t, string(body), 65 "<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>") 66 // Make sure that the SCPD contains a CM service. 67 require.Contains(t, string(body), 68 "<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>") 69 // Ensure that the SCPD url is configured. 70 require.Regexp(t, "<SCPDURL>/.*</SCPDURL>", string(body)) 71 } 72 73 // Make sure that it serves content from the remote. 74 func TestServeContent(t *testing.T) { 75 req, err := http.NewRequest("GET", baseURL+resPath+"video.mp4", nil) 76 require.NoError(t, err) 77 resp, err := http.DefaultClient.Do(req) 78 require.NoError(t, err) 79 defer fs.CheckClose(resp.Body, &err) 80 assert.Equal(t, http.StatusOK, resp.StatusCode) 81 actualContents, err := ioutil.ReadAll(resp.Body) 82 assert.NoError(t, err) 83 84 // Now compare the contents with the golden file. 85 node, err := dlnaServer.vfs.Stat("/video.mp4") 86 assert.NoError(t, err) 87 goldenFile := node.(*vfs.File) 88 goldenReader, err := goldenFile.Open(os.O_RDONLY) 89 assert.NoError(t, err) 90 defer fs.CheckClose(goldenReader, &err) 91 goldenContents, err := ioutil.ReadAll(goldenReader) 92 assert.NoError(t, err) 93 94 require.Equal(t, goldenContents, actualContents) 95 } 96 97 // Check that ContentDirectory#Browse returns appropriate metadata on the root container. 98 func TestContentDirectoryBrowseMetadata(t *testing.T) { 99 // Sample from: https://github.com/rclone/rclone/issues/3253#issuecomment-524317469 100 req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(` 101 <?xml version="1.0" encoding="utf-8"?> 102 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 103 s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 104 <s:Body> 105 <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"> 106 <ObjectID>0</ObjectID> 107 <BrowseFlag>BrowseMetadata</BrowseFlag> 108 <Filter>*</Filter> 109 <StartingIndex>0</StartingIndex> 110 <RequestedCount>0</RequestedCount> 111 <SortCriteria></SortCriteria> 112 </u:Browse> 113 </s:Body> 114 </s:Envelope>`)) 115 require.NoError(t, err) 116 req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`) 117 resp, err := http.DefaultClient.Do(req) 118 require.NoError(t, err) 119 assert.Equal(t, http.StatusOK, resp.StatusCode) 120 body, err := ioutil.ReadAll(resp.Body) 121 require.NoError(t, err) 122 // expect a <container> element 123 require.Contains(t, string(body), html.EscapeString("<container ")) 124 require.NotContains(t, string(body), html.EscapeString("<item ")) 125 // if there is a childCount, it better not be zero 126 require.NotContains(t, string(body), html.EscapeString(" childCount=\"0\"")) 127 // should have a dc:date element 128 require.Contains(t, string(body), html.EscapeString("<dc:date>")) 129 } 130 131 // Check that the X_MS_MediaReceiverRegistrar is faked out properly. 132 func TestMediaReceiverRegistrarService(t *testing.T) { 133 env := soap.Envelope{ 134 Body: soap.Body{ 135 Action: []byte("RegisterDevice"), 136 }, 137 } 138 req, err := http.NewRequest("POST", baseURL+serviceControlURL, bytes.NewReader(mustMarshalXML(env))) 139 require.NoError(t, err) 140 req.Header.Set("SOAPACTION", `"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1#RegisterDevice"`) 141 resp, err := http.DefaultClient.Do(req) 142 require.NoError(t, err) 143 assert.Equal(t, http.StatusOK, resp.StatusCode) 144 body, err := ioutil.ReadAll(resp.Body) 145 require.NoError(t, err) 146 require.Contains(t, string(body), "<RegistrationRespMsg>") 147 } 148 149 // Check that ContentDirectory#Browse returns the expected items. 150 func TestContentDirectoryBrowseDirectChildren(t *testing.T) { 151 // First the root... 152 req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(` 153 <?xml version="1.0" encoding="utf-8"?> 154 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 155 s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 156 <s:Body> 157 <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"> 158 <ObjectID>0</ObjectID> 159 <BrowseFlag>BrowseDirectChildren</BrowseFlag> 160 <Filter>*</Filter> 161 <StartingIndex>0</StartingIndex> 162 <RequestedCount>0</RequestedCount> 163 <SortCriteria></SortCriteria> 164 </u:Browse> 165 </s:Body> 166 </s:Envelope>`)) 167 require.NoError(t, err) 168 req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`) 169 resp, err := http.DefaultClient.Do(req) 170 require.NoError(t, err) 171 assert.Equal(t, http.StatusOK, resp.StatusCode) 172 body, err := ioutil.ReadAll(resp.Body) 173 require.NoError(t, err) 174 // expect video.mp4, video.srt, video.en.srt URLs to be in the DIDL 175 require.Contains(t, string(body), "/r/video.mp4") 176 require.Contains(t, string(body), "/r/video.srt") 177 require.Contains(t, string(body), "/r/video.en.srt") 178 179 // Then a subdirectory 180 req, err = http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(` 181 <?xml version="1.0" encoding="utf-8"?> 182 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 183 s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 184 <s:Body> 185 <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"> 186 <ObjectID>%2Fsubdir</ObjectID> 187 <BrowseFlag>BrowseDirectChildren</BrowseFlag> 188 <Filter>*</Filter> 189 <StartingIndex>0</StartingIndex> 190 <RequestedCount>0</RequestedCount> 191 <SortCriteria></SortCriteria> 192 </u:Browse> 193 </s:Body> 194 </s:Envelope>`)) 195 require.NoError(t, err) 196 req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`) 197 resp, err = http.DefaultClient.Do(req) 198 require.NoError(t, err) 199 assert.Equal(t, http.StatusOK, resp.StatusCode) 200 body, err = ioutil.ReadAll(resp.Body) 201 require.NoError(t, err) 202 // expect video.mp4, video.srt, URLs to be in the DIDL 203 require.Contains(t, string(body), "/r/subdir/video.mp4") 204 require.Contains(t, string(body), "/r/subdir/video.srt") 205 }