github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/httpProxy_test.go (about)

     1  /*
     2   * Copyright (c) 2017, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package psiphon
    21  
    22  import (
    23  	"bytes"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"net/url"
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"testing"
    31  )
    32  
    33  func TestToAbsoluteURL(t *testing.T) {
    34  	var urlTests = []struct {
    35  		base     string
    36  		relative string
    37  		expected string
    38  	}{
    39  		{"http://example.com/path1?q=p#hash", "relative/path", "http://example.com/relative/path"},
    40  		{"http://example.com/path1?q=p#hash", "relative/path?a=b", "http://example.com/relative/path?a=b"},
    41  		{"http://example.com/path1?q=p#hash", "relative/path#c", "http://example.com/relative/path#c"},
    42  		{"http://example.com/path1?q=p#hash", "relative/path?a=b#c", "http://example.com/relative/path?a=b#c"},
    43  		{"http://example.com/path1/path2?q=p#hash", "relative/path", "http://example.com/path1/relative/path"},
    44  		{"http://example.com/path1/path2?q=p#hash", "/relative/path", "http://example.com/relative/path"},
    45  		{"http://example.com/path1/path2?q=p#hash", "http://example.org/absolute/path", "http://example.org/absolute/path"},
    46  	}
    47  
    48  	for _, tt := range urlTests {
    49  		baseURL, _ := url.Parse(tt.base)
    50  		absURL := toAbsoluteURL(baseURL, tt.relative)
    51  		if absURL != tt.expected {
    52  			t.Errorf("toAbsoluteURL(%s, %s): expected %s, actual %s", tt.base, tt.relative, tt.expected, absURL)
    53  		}
    54  	}
    55  }
    56  
    57  func TestProxifyURL(t *testing.T) {
    58  	var urlTests = []struct {
    59  		ip            string
    60  		port          int
    61  		urlString     string
    62  		rewriteParams []string
    63  		expected      string
    64  	}{
    65  		{"127.0.0.1", 1234, "http://example.com/media/pl.m3u8?q=p&p=q#hash", []string{"rewriter1"}, "http://127.0.0.1:1234/tunneled-rewrite/http%3A%2F%2Fexample.com%2Fmedia%2Fpl.m3u8%3Fq%3Dp%26p%3Dq%23hash?rewriter1="},
    66  		{"127.0.0.2", 12345, "http://example.com/media/pl.aaa", []string{"rewriter1", "rewriter2"}, "http://127.0.0.2:12345/tunneled-rewrite/http%3A%2F%2Fexample.com%2Fmedia%2Fpl.aaa?rewriter1=&rewriter2="},
    67  		{"127.0.0.3", 12346, "http://example.com/media/bbb", nil, "http://127.0.0.3:12346/tunneled/http%3A%2F%2Fexample.com%2Fmedia%2Fbbb"},
    68  	}
    69  
    70  	for _, tt := range urlTests {
    71  		actual := proxifyURL(tt.ip, tt.port, tt.urlString, tt.rewriteParams)
    72  		if actual != tt.expected {
    73  			t.Errorf("proxifyURL(%d, %s, %v): expected %s, actual %s", tt.port, tt.urlString, tt.rewriteParams, tt.expected, actual)
    74  		}
    75  	}
    76  }
    77  
    78  func TestRewriteM3U8(t *testing.T) {
    79  	var tests = []struct {
    80  		url                 string
    81  		contentType         string
    82  		contentEncoding     string
    83  		inFilename          string
    84  		expectedFilename    string
    85  		expectedContentType string
    86  		expectError         bool
    87  	}{
    88  		// Relying on file extension to indicate type
    89  		{"http://example.com/test.m3u8", "", "", "testdata/master.m3u8.1", "testdata/master.m3u8.1.target", "application/x-mpegURL", false},
    90  		// No file extension, Content-Type set
    91  		{"http://example.com/test", "application/x-mpegURL", "", "testdata/master.m3u8.1", "testdata/master.m3u8.1.target", "application/x-mpegURL", false},
    92  		// No file extension, Content-Type set
    93  		{"http://example.com/test", "vnd.apple.mpegURL", "", "testdata/master.m3u8.1", "testdata/master.m3u8.1.target", "application/x-mpegURL", false},
    94  		// No file extension, no Content-Type, so no change
    95  		{"http://example.com/test", "", "", "testdata/master.m3u8.1", "testdata/master.m3u8.1", "", false},
    96  		// Media playlist
    97  		{"http://example.com/test.m3u8", "", "", "testdata/media.m3u8.1", "testdata/media.m3u8.1.target", "application/x-mpegURL", false},
    98  		// Complex master playlist
    99  		{"http://example.com/test.m3u8", "", "", "testdata/master.m3u8.2", "testdata/master.m3u8.2.target", "application/x-mpegURL", false},
   100  		// Complex media playlist
   101  		{"http://example.com/test.m3u8", "", "", "testdata/media.m3u8.2", "testdata/media.m3u8.2.target", "application/x-mpegURL", false},
   102  		// Invalid file
   103  		{"http://example.com/test.m3u8", "application/x-mpegURL", "", "httpProxy.go", "httpProxy.go", "", false},
   104  		// Gzipped file
   105  		{"http://example.com/test.m3u8", "", "gzip", "testdata/master.m3u8.1.gz", "testdata/master.m3u8.1.target", "application/x-mpegURL", false},
   106  		// Invalid Gzip file
   107  		{"http://example.com/test.m3u8", "", "gzip", "testdata/master.m3u8.1", "", "", true},
   108  	}
   109  
   110  	for i, tt := range tests {
   111  		response := http.Response{
   112  			Request: new(http.Request),
   113  			Header:  http.Header{},
   114  		}
   115  
   116  		response.Request.URL, _ = url.Parse(tt.url)
   117  		if tt.contentType != "" {
   118  			response.Header.Set("Content-Type", tt.contentType)
   119  		}
   120  		if tt.contentEncoding != "" {
   121  			response.Header.Set("Content-Encoding", tt.contentEncoding)
   122  		}
   123  
   124  		inFile, _ := os.Open(tt.inFilename)
   125  		inFileInfo, _ := inFile.Stat()
   126  
   127  		response.Body = inFile
   128  		response.Header.Set("Content-Length", strconv.FormatInt(inFileInfo.Size(), 10))
   129  
   130  		err := rewriteM3U8("127.0.0.1", 12345, &response)
   131  		if err != nil {
   132  			if !tt.expectError {
   133  				t.Errorf("rewriteM3U8 returned error: %s", err)
   134  			}
   135  			continue
   136  		}
   137  
   138  		rewrittenBody, _ := ioutil.ReadAll(response.Body)
   139  		response.Body.Close()
   140  
   141  		expectedBody, _ := ioutil.ReadFile(tt.expectedFilename)
   142  
   143  		if !bytes.Equal(rewrittenBody, expectedBody) {
   144  			t.Errorf("rewriteM3U8 body mismatch for test %d", i)
   145  		}
   146  
   147  		if tt.expectedContentType != "" && !strings.EqualFold(response.Header.Get("Content-Type"), tt.expectedContentType) {
   148  			t.Errorf("rewriteM3U8 Content-Type mismatch for test %d: %s %s", i, tt.expectedContentType, response.Header.Get("Content-Type"))
   149  		}
   150  
   151  		contentLength, _ := strconv.ParseInt(response.Header.Get("Content-Length"), 10, 64)
   152  		if contentLength != int64(len(rewrittenBody)) {
   153  			t.Errorf("rewriteM3U8 Content-Length incorrect for test %d: %d != %d", i, contentLength, len(rewrittenBody))
   154  		}
   155  	}
   156  }