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