worker_test.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package frankenphp_test
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "net/http"
  7. "net/http/httptest"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "testing"
  12. "github.com/dunglas/frankenphp"
  13. "github.com/stretchr/testify/assert"
  14. "go.uber.org/zap"
  15. "go.uber.org/zap/zapcore"
  16. "go.uber.org/zap/zaptest/observer"
  17. )
  18. func TestWorker(t *testing.T) {
  19. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  20. formData := url.Values{"baz": {"bat"}}
  21. req := httptest.NewRequest("POST", "http://example.com/worker.php?foo=bar", strings.NewReader(formData.Encode()))
  22. req.Header.Set("Content-Type", strings.Clone("application/x-www-form-urlencoded"))
  23. w := httptest.NewRecorder()
  24. handler(w, req)
  25. resp := w.Result()
  26. body, _ := io.ReadAll(resp.Body)
  27. assert.Contains(t, string(body), fmt.Sprintf("Requests handled: %d", i*2))
  28. formData2 := url.Values{"baz2": {"bat2"}}
  29. req2 := httptest.NewRequest("POST", "http://example.com/worker.php?foo2=bar2", strings.NewReader(formData2.Encode()))
  30. req2.Header.Set("Content-Type", strings.Clone("application/x-www-form-urlencoded"))
  31. w2 := httptest.NewRecorder()
  32. handler(w2, req2)
  33. resp2 := w2.Result()
  34. body2, _ := io.ReadAll(resp2.Body)
  35. assert.Contains(t, string(body2), fmt.Sprintf("Requests handled: %d", i*2+1))
  36. }, &testOptions{workerScript: "worker.php", nbWorkers: 1, nbParrallelRequests: 1})
  37. }
  38. func TestWorkerDie(t *testing.T) {
  39. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  40. req := httptest.NewRequest("GET", "http://example.com/die.php", nil)
  41. w := httptest.NewRecorder()
  42. handler(w, req)
  43. }, &testOptions{workerScript: "die.php", nbWorkers: 1, nbParrallelRequests: 10})
  44. }
  45. func TestNonWorkerModeAlwaysWorks(t *testing.T) {
  46. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  47. req := httptest.NewRequest("GET", "http://example.com/index.php", nil)
  48. w := httptest.NewRecorder()
  49. handler(w, req)
  50. resp := w.Result()
  51. body, _ := io.ReadAll(resp.Body)
  52. assert.Contains(t, string(body), "I am by birth a Genevese")
  53. }, &testOptions{workerScript: "phpinfo.php"})
  54. }
  55. func TestCannotCallHandleRequestInNonWorkerMode(t *testing.T) {
  56. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  57. req := httptest.NewRequest("GET", "http://example.com/non-worker.php", nil)
  58. w := httptest.NewRecorder()
  59. handler(w, req)
  60. resp := w.Result()
  61. body, _ := io.ReadAll(resp.Body)
  62. assert.Contains(t, string(body), "<b>Fatal error</b>: Uncaught RuntimeException: frankenphp_handle_request() called while not in worker mode")
  63. }, nil)
  64. }
  65. func TestWorkerEnv(t *testing.T) {
  66. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  67. req := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/worker-env.php?i=%d", i), nil)
  68. w := httptest.NewRecorder()
  69. handler(w, req)
  70. resp := w.Result()
  71. body, _ := io.ReadAll(resp.Body)
  72. assert.Equal(t, fmt.Sprintf("bar%d", i), string(body))
  73. }, &testOptions{workerScript: "worker-env.php", nbWorkers: 1, env: map[string]string{"FOO": "bar"}, nbParrallelRequests: 10})
  74. }
  75. func TestWorkerGetOpt(t *testing.T) {
  76. observer, logs := observer.New(zapcore.InfoLevel)
  77. logger := zap.New(observer)
  78. runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
  79. req := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/worker-getopt.php?i=%d", i), nil)
  80. req.Header.Add("Request", strconv.Itoa(i))
  81. w := httptest.NewRecorder()
  82. handler(w, req)
  83. resp := w.Result()
  84. body, _ := io.ReadAll(resp.Body)
  85. assert.Contains(t, string(body), fmt.Sprintf("[HTTP_REQUEST] => %d", i))
  86. assert.Contains(t, string(body), fmt.Sprintf("[REQUEST_URI] => /worker-getopt.php?i=%d", i))
  87. }, &testOptions{logger: logger, workerScript: "worker-getopt.php", env: map[string]string{"FOO": "bar"}})
  88. for _, log := range logs.FilterFieldKey("exit_status").All() {
  89. assert.Failf(t, "unexpected exit status", "exit status: %d", log.ContextMap()["exit_status"])
  90. }
  91. }
  92. func ExampleServeHTTP_workers() {
  93. if err := frankenphp.Init(
  94. frankenphp.WithWorkers("worker1.php", 4, map[string]string{"ENV1": "foo"}),
  95. frankenphp.WithWorkers("worker2.php", 2, map[string]string{"ENV2": "bar"}),
  96. ); err != nil {
  97. panic(err)
  98. }
  99. defer frankenphp.Shutdown()
  100. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  101. req, err := frankenphp.NewRequestWithContext(r, frankenphp.WithRequestDocumentRoot("/path/to/document/root", false))
  102. if err != nil {
  103. panic(err)
  104. }
  105. if err := frankenphp.ServeHTTP(w, req); err != nil {
  106. panic(err)
  107. }
  108. })
  109. log.Fatal(http.ListenAndServe(":8080", nil))
  110. }