httpTransformer_test.go 17 KB

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