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  }