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 }