HTTP API: server, credential and the list of chats #12
@@ -95,6 +95,9 @@ func New(p Params) *http.Server {
|
||||
ReadTimeout: readTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
IdleTimeout: idleTimeout,
|
||||
// Otherwise net/http answers "OPTIONS *" itself, with 200 and
|
||||
// without the credential check or the headers.
|
||||
DisableGeneralOptionsHandler: true,
|
||||
// net/http's own messages, such as a handler's panic, go to the
|
||||
// same JSON log as everything else.
|
||||
ErrorLog: slog.NewLogLogger(p.Log.Handler(), slog.LevelError),
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -175,6 +177,49 @@ func TestNoPathIsExempt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptionsAsterisk: "OPTIONS *" is refused like any other request.
|
||||
// net/http would answer it before the handler, so this request goes to
|
||||
// a running server rather than to its handler.
|
||||
func TestOptionsAsterisk(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
srv := newAPI("", &fakeClient{})
|
||||
|
||||
listener, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() { _ = srv.Serve(listener) }()
|
||||
|
||||
t.Cleanup(func() { _ = srv.Close() })
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodOptions,
|
||||
"http://"+listener.Addr().String(), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The request line becomes "OPTIONS * HTTP/1.1".
|
||||
req.URL.Opaque = "*"
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusUnauthorized || string(body) != unauthorized {
|
||||
t.Errorf("OPTIONS *: %d %q, want 401 %q", resp.StatusCode, body, unauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeaders: every response, whatever its status, carries the
|
||||
// security headers, and none lets another origin in.
|
||||
func TestHeaders(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user