HTTP API: server, credential and the list of chats #12

Merged
clawbot merged 7 commits from issue-4-api-chats into next 2026-09-29 04:55:50 +02:00
2 changed files with 48 additions and 0 deletions
Showing only changes of commit 0c5163e69e - Show all commits
+3
View File
@@ -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),
+45
View File
@@ -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) {