httpTransformer_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /*
  2. * Copyright (c) 2023, 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. package transforms
  20. import (
  21. "bytes"
  22. "context"
  23. "errors"
  24. "fmt"
  25. "io"
  26. "math"
  27. "net"
  28. "net/http"
  29. "strings"
  30. "testing"
  31. "time"
  32. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  33. )
  34. func TestHTTPTransformerHTTPRequest(t *testing.T) {
  35. type test struct {
  36. name string
  37. input string
  38. wantOutput string
  39. wantError error
  40. chunkSize int
  41. transform Spec
  42. connWriteLimit int
  43. connWriteLens []int
  44. connWriteErrs []error
  45. }
  46. tests := []test{
  47. {
  48. name: "written in chunks",
  49. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  50. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  51. chunkSize: 1,
  52. },
  53. {
  54. name: "write header then body", // behaviour of net/http code
  55. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  56. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  57. chunkSize: 38,
  58. },
  59. {
  60. name: "write header then body with error",
  61. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  62. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  63. chunkSize: 38,
  64. connWriteErrs: []error{nil, errors.New("err1")},
  65. wantError: errors.New("err1"),
  66. },
  67. {
  68. name: "written in a single write",
  69. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  70. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  71. chunkSize: 999,
  72. },
  73. {
  74. name: "written in single write with error",
  75. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  76. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  77. chunkSize: 999,
  78. connWriteErrs: []error{errors.New("err1")},
  79. wantError: errors.New("err1"),
  80. },
  81. {
  82. name: "written with partial write and errors",
  83. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  84. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  85. chunkSize: 1,
  86. connWriteLimit: 1,
  87. connWriteErrs: []error{errors.New("err1"), errors.New("err2")},
  88. wantError: errors.New("err1"),
  89. },
  90. {
  91. name: "transform not applied to body",
  92. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  93. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  94. chunkSize: 1,
  95. transform: Spec{[2]string{"abcd", "efgh"}},
  96. },
  97. {
  98. name: "Content-Length missing",
  99. input: "POST / HTTP/1.1\r\n\r\nabcd",
  100. wantError: errors.New("Content-Length missing"),
  101. chunkSize: 1,
  102. },
  103. {
  104. name: "Content-Length overflow",
  105. input: fmt.Sprintf("POST / HTTP/1.1\r\nContent-Length: %d\r\n\r\nabcd", uint64(math.MaxUint64)),
  106. wantError: errors.New("strconv.ParseUint: parsing \"18446744073709551615\": value out of range"),
  107. chunkSize: 1,
  108. },
  109. {
  110. name: "incorrect Content-Length header value",
  111. input: "POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabcd",
  112. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc",
  113. chunkSize: 1,
  114. },
  115. {
  116. name: "transform",
  117. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  118. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcd",
  119. chunkSize: 1,
  120. transform: Spec{[2]string{"4", "100"}},
  121. },
  122. {
  123. name: "transform with separate write for header and body",
  124. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  125. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcd",
  126. chunkSize: 38, // length of header
  127. transform: Spec{[2]string{"4", "100"}},
  128. },
  129. {
  130. name: "transform with single write",
  131. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  132. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcd",
  133. chunkSize: 999,
  134. transform: Spec{[2]string{"4", "100"}},
  135. },
  136. {
  137. name: "transform with partial write and errors in header write",
  138. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  139. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcd",
  140. chunkSize: 1,
  141. transform: Spec{[2]string{"4", "100"}},
  142. connWriteLimit: 1,
  143. connWriteErrs: []error{errors.New("err1"), errors.New("err2")},
  144. wantError: errors.New("err1"),
  145. },
  146. {
  147. name: "transform with chunk write and errors in body write",
  148. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  149. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcd",
  150. chunkSize: 39,
  151. transform: Spec{[2]string{"4", "100"}},
  152. connWriteLimit: 1,
  153. connWriteErrs: []error{errors.New("err1"), errors.New("err2"), errors.New("err3")},
  154. wantError: errors.New("err1"),
  155. },
  156. //
  157. // Below tests document unsupported behavior.
  158. //
  159. {
  160. name: "written in a single write with errors and partial writes",
  161. input: "POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n",
  162. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 0\r\n\r\n",
  163. chunkSize: 999,
  164. transform: Spec{[2]string{"Host: example.com\r\n", ""}},
  165. connWriteErrs: []error{errors.New("err1"), nil, errors.New("err2"), nil, nil, errors.New("err3")},
  166. connWriteLens: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  167. wantError: errors.New("err1"),
  168. },
  169. {
  170. name: "written in a single write with error and partial write",
  171. input: "POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 4\r\n\r\nabcd",
  172. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcd",
  173. chunkSize: 999,
  174. transform: Spec{[2]string{"Host: example.com\r\n", ""}},
  175. connWriteErrs: []error{errors.New("err1")},
  176. connWriteLens: []int{28}, // write lands mid "\r\n\r\n"
  177. wantError: errors.New("err1"),
  178. },
  179. {
  180. name: "multiple HTTP requests written in a single write",
  181. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 2\r\n\r\n12",
  182. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 2\r\n\r\n12",
  183. chunkSize: 999,
  184. wantError: errors.New("multiple HTTP requests in single Write() not supported"),
  185. },
  186. {
  187. name: "multiple HTTP requests written in a single write with transform",
  188. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 4\r\n\r\n12POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\n34",
  189. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 100\r\n\r\n12POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\n34",
  190. chunkSize: 999,
  191. transform: Spec{[2]string{"4", "100"}},
  192. wantError: errors.New("multiple HTTP requests in single Write() not supported"),
  193. },
  194. // Multiple HTTP requests written in a single write. A write will occur
  195. // where it contains both the end of the previous HTTP request and the
  196. // start of a new one.
  197. {
  198. name: "multiple HTTP requests written in chunks",
  199. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 2\r\n\r\n12",
  200. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 2\r\n\r\n12",
  201. chunkSize: 4,
  202. wantError: errors.New("multiple HTTP requests in single Write() not supported"),
  203. },
  204. // Multiple HTTP requests written in a single write with transform. A
  205. // write will occur where it contains both the end of the previous HTTP
  206. // request and the start of a new one.
  207. {
  208. name: "multiple HTTP requests written in chunks with transform",
  209. input: "POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 4\r\n\r\n12",
  210. wantOutput: "POST / HTTP/1.1\r\nContent-Length: 100\r\n\r\nabcdPOST / HTTP/1.1\r\nContent-Length: 100\r\n\r\n12",
  211. chunkSize: 4, // ensure one write contains bytes from both reqs
  212. transform: Spec{[2]string{"4", "100"}},
  213. wantError: errors.New("multiple HTTP requests in single Write() not supported"),
  214. },
  215. }
  216. for _, tt := range tests {
  217. t.Run(tt.name, func(t *testing.T) {
  218. seed, err := prng.NewSeed()
  219. if err != nil {
  220. t.Fatalf("prng.NewSeed failed %v", err)
  221. }
  222. conn := testConn{
  223. writeLimit: tt.connWriteLimit,
  224. writeLens: tt.connWriteLens,
  225. writeErrs: tt.connWriteErrs,
  226. }
  227. transformer := &HTTPTransformer{
  228. transform: tt.transform,
  229. seed: seed,
  230. Conn: &conn,
  231. }
  232. remain := []byte(tt.input)
  233. // Write input bytes to transformer in chunks and then check
  234. // output.
  235. for {
  236. if len(remain) == 0 {
  237. break
  238. }
  239. var b []byte
  240. if len(remain) < tt.chunkSize || tt.chunkSize == 0 {
  241. b = remain
  242. } else {
  243. b = remain[:tt.chunkSize]
  244. }
  245. var n int
  246. n, err = transformer.Write(b)
  247. if err != nil {
  248. // The underlying transport will be a reliable stream
  249. // transport, i.e. TCP, and we expect the caller to stop
  250. // writing after an error is returned.
  251. break
  252. }
  253. remain = remain[n:]
  254. }
  255. if tt.wantError == nil {
  256. if err != nil {
  257. t.Fatalf("unexpected error %v", err)
  258. }
  259. if string(conn.b) != tt.wantOutput {
  260. t.Fatalf("expected \"%s\" of len %d but got \"%s\" of len %d", escapeNewlines(tt.wantOutput), len(tt.wantOutput), escapeNewlines(string(conn.b)), len(conn.b))
  261. }
  262. } else {
  263. // tt.wantError != nil
  264. if err == nil {
  265. t.Fatalf("expected error %v", tt.wantError)
  266. } else if !strings.Contains(err.Error(), tt.wantError.Error()) {
  267. t.Fatalf("expected error %v got %v", tt.wantError, err)
  268. }
  269. }
  270. })
  271. }
  272. }
  273. func TestHTTPTransformerHTTPServer(t *testing.T) {
  274. type test struct {
  275. name string
  276. request func(string) *http.Request
  277. wantBody string
  278. transform Spec
  279. connWriteLimit int
  280. connWriteLens []int
  281. connWriteErrs []error
  282. wantError error
  283. }
  284. tests := []test{
  285. {
  286. name: "request body truncated",
  287. transform: Spec{[2]string{"Content-Length: 4", "Content-Length: 3"}},
  288. request: func(addr string) *http.Request {
  289. body := bytes.NewReader([]byte("abcd"))
  290. req, err := http.NewRequest("POST", "http://"+addr, body)
  291. if err != nil {
  292. panic(err)
  293. }
  294. return req
  295. },
  296. wantBody: "abc",
  297. },
  298. // Expect HTTP request to abort after a single Write() call on
  299. // underlying net.Conn fails.
  300. {
  301. name: "transport fails",
  302. transform: Spec{[2]string{"", ""}},
  303. request: func(addr string) *http.Request {
  304. body := bytes.NewReader([]byte("abcd"))
  305. req, err := http.NewRequest("POST", "http://"+addr, body)
  306. if err != nil {
  307. panic(err)
  308. }
  309. return req
  310. },
  311. wantBody: "abc",
  312. connWriteErrs: []error{errors.New("test error")},
  313. wantError: errors.New("test error"),
  314. },
  315. }
  316. for _, tt := range tests {
  317. t.Run(tt.name, func(t *testing.T) {
  318. seed, err := prng.NewSeed()
  319. if err != nil {
  320. t.Fatalf("prng.NewSeed failed %v", err)
  321. }
  322. params := &HTTPTransformerParameters{
  323. ProtocolTransformName: "spec",
  324. ProtocolTransformSpec: tt.transform,
  325. ProtocolTransformSeed: seed,
  326. }
  327. dialer := func(ctx context.Context, network, address string) (net.Conn, error) {
  328. if network != "tcp" {
  329. return nil, errors.New("expected network tcp")
  330. }
  331. conn, err := net.Dial(network, address)
  332. if err != nil {
  333. return nil, err
  334. }
  335. wrappedConn := testConn{
  336. Conn: conn,
  337. writeLimit: tt.connWriteLimit,
  338. writeLens: tt.connWriteLens,
  339. writeErrs: tt.connWriteErrs,
  340. }
  341. return &wrappedConn, nil
  342. }
  343. httpTransport := &http.Transport{
  344. DialContext: WrapDialerWithHTTPTransformer(dialer, params),
  345. }
  346. type serverRequest struct {
  347. req *http.Request
  348. body []byte
  349. }
  350. serverReq := make(chan *serverRequest, 1)
  351. mux := http.NewServeMux()
  352. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  353. b, err := io.ReadAll(r.Body)
  354. if err != nil {
  355. w.WriteHeader(http.StatusInternalServerError)
  356. }
  357. go func() {
  358. serverReq <- &serverRequest{
  359. req: r,
  360. body: b,
  361. }
  362. close(serverReq)
  363. }()
  364. })
  365. listener, err := net.Listen("tcp", "127.0.0.1:0")
  366. if err != nil {
  367. t.Fatalf("net.Listen failed %v", err)
  368. }
  369. s := &http.Server{
  370. Addr: listener.Addr().String(),
  371. Handler: mux,
  372. }
  373. go func() {
  374. s.Serve(listener)
  375. }()
  376. client := http.Client{
  377. Transport: httpTransport,
  378. Timeout: 2 * time.Second,
  379. }
  380. req := tt.request(s.Addr)
  381. resp, err := client.Do(req)
  382. // first shutdown server, then check err
  383. shutdownErr := s.Shutdown(context.Background())
  384. if shutdownErr != nil {
  385. t.Fatalf("s.Shutdown failed %v", shutdownErr)
  386. }
  387. if tt.wantError == nil {
  388. if err != nil {
  389. t.Fatalf("client.Do failed %v", err)
  390. }
  391. if resp.StatusCode != http.StatusOK {
  392. t.Fatalf("expected 200 but got %d", resp.StatusCode)
  393. }
  394. r := <-serverReq
  395. if tt.wantBody != string(r.body) {
  396. t.Fatalf("expected body %s but got %s", tt.wantBody, string(r.body))
  397. }
  398. } else {
  399. // tt.wantError != nil
  400. if err == nil {
  401. t.Fatalf("expected error %v", tt.wantError)
  402. } else if !strings.Contains(err.Error(), tt.wantError.Error()) {
  403. t.Fatalf("expected error %v got %v", tt.wantError, err)
  404. }
  405. }
  406. })
  407. }
  408. }
  409. func escapeNewlines(s string) string {
  410. s = strings.ReplaceAll(s, "\n", "\\n")
  411. s = strings.ReplaceAll(s, "\r", "\\r")
  412. return s
  413. }
  414. type testConn struct {
  415. // b is the accumulated bytes from Write() calls.
  416. b []byte
  417. // writeLimit is the max number of bytes that will be written in a Write()
  418. // call.
  419. writeLimit int
  420. // writeLens are returned from Write() calls in order and determine the
  421. // max number of bytes that will be written. Overrides writeLimit if
  422. // non-empty. If empty, then the value of writeLimit is returned.
  423. writeLens []int
  424. // writeErrs are returned from Write() calls in order. If empty, then a nil
  425. // error is returned.
  426. writeErrs []error
  427. net.Conn
  428. }
  429. func (c *testConn) Read(b []byte) (n int, err error) {
  430. return c.Conn.Read(b)
  431. }
  432. func (c *testConn) Write(b []byte) (n int, err error) {
  433. if len(c.writeErrs) > 0 {
  434. err = c.writeErrs[0]
  435. c.writeErrs = c.writeErrs[1:]
  436. }
  437. if len(c.writeLens) > 0 {
  438. n = c.writeLens[0]
  439. c.writeLens = c.writeLens[1:]
  440. if len(b) <= n {
  441. c.b = append(c.b, b...)
  442. n = len(b)
  443. } else {
  444. c.b = append(c.b, b[:n]...)
  445. }
  446. } else if c.writeLimit != 0 && c.writeLimit < len(b) {
  447. c.b = append(c.b, b[:c.writeLimit]...)
  448. n = c.writeLimit
  449. } else {
  450. c.b = append(c.b, b...)
  451. n = len(b)
  452. }
  453. // Only write to net.Conn if set
  454. if c.Conn != nil && n > 0 {
  455. c.Conn.Write(b[:n])
  456. }
  457. return
  458. }
  459. func (c *testConn) Close() error {
  460. return c.Conn.Close()
  461. }
  462. func (c *testConn) LocalAddr() net.Addr {
  463. return c.Conn.LocalAddr()
  464. }
  465. func (c *testConn) RemoteAddr() net.Addr {
  466. return c.Conn.RemoteAddr()
  467. }
  468. func (c *testConn) SetDeadline(t time.Time) error {
  469. return c.Conn.SetDeadline(t)
  470. }
  471. func (c *testConn) SetReadDeadline(t time.Time) error {
  472. return c.Conn.SetReadDeadline(t)
  473. }
  474. func (c *testConn) SetWriteDeadline(t time.Time) error {
  475. return c.Conn.SetWriteDeadline(t)
  476. }