Repository navigation
0.5.0
Spindle moves to wiretype 0.5.0 and rowtype 0.7.0, renames its packages, types more of HTTP, keeps secrets out of logs, and fixes compressed answers and proxied clients.
Upgrading
Packages and dependencies
- The packages are
spindle-postgresandspindle-cli, as are their libraries:(libraries spindle_postgres)→(libraries spindle-postgres). Module names are unchanged. - The log source
spindle_postgres→spindle-postgres. spindleneeds wiretype 0.5.0 and Uuidm;spindle-postgresneeds rowtype 0.7.0 and postgres-eio 0.5.0.
Codecs
Codec.uuidis aUuidm.t, was astring, so one id is one type in a body and in a row.Codec.custom ~kind ~parse ~print ():~parseanswersOk vorError "what the client is told", where it answered an option with?expects.Codec.parseanswers aresult.
Requests and routes
Spindle.body_stream ~content_type:(fun ct -> …)→~content_types:[ "application/octet-stream" ], eachtype/subtype,type/*ortype/*+suffix.Dep.of_body ~content_typeis gone: the types go in its need,Dep.Raw { content_types }.- A route's content type that is not a valid pattern (
"json") makesApp.makeraise;Compress.make ~typesraises on one too. ?or_not_found:()→?unparsed:Next_routeon a path parameter;Route.param's fieldor_not_found→unparsed.Cors.make ~headers→~allow_headers.?now/~nowonSpindle.serve,Server.run,Server.serve_onandRequest.make→read_clock.Test.call ~nowandSession's~nowstay aPtime.t.Spindle.clock_now clock ()→Spindle.of_clock clock.
HTTP types
Status.tnames every RFC 9110 status plus 103, 425, 428, 451 and 511:`Code 402→`Payment_required.Meth.thas`CONNECTand`TRACE, where they were`Other s.Websocket.sendanswersError (Unwritable _)for text that is not UTF-8 or JSON it cannot encode, and sends nothing; a handler returning it closes with 1011.Spindle_http.Multipart.to_stringanswers aresult, refusing a bad boundary or content type, or content holding the delimiter.
Postgres
Spindle_postgres.connectandPool.createtake aConninfo.t:"postgres://…"→Rowtype_postgres.Conninfo.of_string "postgres://…".Pool.runstill takes the string.?connect_timeoutis gone:Conninfo.with_connect_timeout. Without one it is still 10 s.Pool.query→Pool.exec.Spindle_postgres.erroris[ Rowtype.error | Rowtype_postgres.Transaction.failure | Rowtype_postgres.Pool.error ]; its printer isRowtype_postgres.error_to_string.
Client
Spindle_client.erroris[ `Unsent of string | `Lost of string ]:UnansweredandTimeout→`Lost.websocket_erroris[ error | `Not_upgraded of response | `Websocket of Spindle_http.Websocket.error ], where it wasFailed,RefusedandEnded.events_end'sRefused→Answered.
Logging
- An exception's message is never logged:
Log.raisedwriteserror.kindanderror.stack, noerror.message, and a raise's500names the exception's kind.
OpenAPI and Zod
- Routes told apart only by a parameter's type are one operation, its parameter any of their types.
- Paths that differ only in a parameter's name (
/items/{id},/items/{item}) are the document'sError: give them one name. - Routes of one operation that answer differently or read different bodies are an
Errortoo.
New
Codec.enum's~kindis optional.error_to_stringbeside every publicerror:Session,Body,Multipart,Spindle_http.Head,Head.Response,Spindle_http.Multipart;Test.websocket_error_to_string.Spindle_http.Media_type.is_patternandMedia_type.matchescheck and compare content types.- A raw body's content types are listed in the OpenAPI document, where it always said
application/octet-stream. - A database that cannot be reached (
`Unsent) answers503 busywithRetry-After: 1, logged as a warning. - An answer outside its description's bounds is never written: it is a logged
500. Read-only members are not read, write-only ones never answered.
Fixed
- A client behind a trusted proxy whose
X-Forwarded-Forhas a port (1.2.3.4:5678) is that address, soRate.limitby client limits again. - A compressed answer revalidates:
If-None-Matchwith its-gziptag answers304, andIf-Matchwith it passes. - Every
304saysVary: Accept-Encodingwhen its200would,StaticandFilesincluded. - A client call's timeout covers the DNS lookup; a slow resolver held it for as long as it took.
- A log line is always valid UTF-8 on one line;
Prettywrites line breaks in a message as\nand\r. - A body read a handler left running is stopped when the handler returns, where the connection hung.
- A
426sendsConnection: upgradewith itsUpgrade. - A client that left before
100 Continuemakes the body read answerError (Unreadable _), not raise. - A stream's
sendafter its producer has returned answersError Gone. - A WebSocket takeover in flight when the server stops gets the drain to close, not cancelled at once.