HTTP API: server, credential and the list of chats #12
@@ -95,6 +95,9 @@ func New(p Params) *http.Server {
|
|||||||
ReadTimeout: readTimeout,
|
ReadTimeout: readTimeout,
|
||||||
WriteTimeout: writeTimeout,
|
WriteTimeout: writeTimeout,
|
||||||
IdleTimeout: idleTimeout,
|
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
|
// net/http's own messages, such as a handler's panic, go to the
|
||||||
// same JSON log as everything else.
|
// same JSON log as everything else.
|
||||||
ErrorLog: slog.NewLogLogger(p.Log.Handler(), slog.LevelError),
|
ErrorLog: slog.NewLogLogger(p.Log.Handler(), slog.LevelError),
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"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
|
// TestHeaders: every response, whatever its status, carries the
|
||||||
// security headers, and none lets another origin in.
|
// security headers, and none lets another origin in.
|
||||||
func TestHeaders(t *testing.T) {
|
func TestHeaders(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user