@amritk/nish-x86_64-linux 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -114,6 +114,12 @@
114
114
  */
115
115
  import { Secret } from "nish:secret"
116
116
  import { copyBytes } from "nish/bytes"
117
+ import {
118
+ HTTP_CODING_IDENTITY,
119
+ HTTP_OFFER_GZIP,
120
+ HttpCompressor,
121
+ httpAcceptEncoding,
122
+ } from "nish/net/http-compress"
117
123
  import { netClose, netRead, netWrite, tcpAccept } from "nish:net"
118
124
  import {
119
125
  HTTP1_BODY,
@@ -1205,6 +1211,95 @@ export class Http1Connection {
1205
1211
  return 0
1206
1212
  }
1207
1213
 
1214
+ // ---- Content coding -----------------------------------------------------------
1215
+
1216
+ /**
1217
+ * The content coding to answer the current request with, read from its
1218
+ * `Accept-Encoding` with gzip offered (`httpAcceptEncoding`):
1219
+ * `HTTP_CODING_GZIP`, `HTTP_CODING_IDENTITY` (also with no such field), or
1220
+ * `HTTP_CODING_NONE` when the client refuses identity and does not take
1221
+ * gzip. Allocates nothing.
1222
+ */
1223
+ acceptEncoding(): i32 {
1224
+ const parser: Http1Parser = this.parser
1225
+ const i: i32 = parser.headerIndex("accept-encoding")
1226
+ const spans: i32[] = parser.spans
1227
+ if (i < 0 || 4 * i + 3 >= toI32(spans.length)) {
1228
+ return HTTP_CODING_IDENTITY
1229
+ }
1230
+ const start: i32 = spans[4 * i + 2]
1231
+ const end: i32 = spans[4 * i + 3]
1232
+ if (start < 0 || start > end || end > toI32(parser.head.length)) {
1233
+ return HTTP_CODING_IDENTITY
1234
+ }
1235
+ return httpAcceptEncoding(parser.head, start, end, HTTP_OFFER_GZIP)
1236
+ }
1237
+
1238
+ /**
1239
+ * Answers the current request with all of `body[off .. off + len)` in one
1240
+ * call: `respond`, `write` and `end`, the body compressed by `enc` when
1241
+ * `coding` (from `acceptEncoding`) is gzip and `enc.compress` takes it,
1242
+ * with `Content-Encoding: gzip` and the compressed `Content-Length`, and
1243
+ * otherwise sent as it is; `Vary: Accept-Encoding` either way while
1244
+ * `enc.vary` is set. The program's fields may not name `Content-Encoding`
1245
+ * or, with `enc.vary`, `Vary`. The whole response is written or none of
1246
+ * it: `H1_AGAIN` when the output has no room for it yet (`H1_WRITE` says
1247
+ * when to try again, compressing again), `H1_INVALID` for a response
1248
+ * larger than the output could ever hold or one `respond` refuses,
1249
+ * `H1_CLOSED` with no request to answer; 0 once it is queued. A window
1250
+ * outside `body` panics.
1251
+ */
1252
+ respondEncoded(
1253
+ status: i32,
1254
+ names: u8[][],
1255
+ values: u8[][],
1256
+ body: u8[],
1257
+ off: i32,
1258
+ len: i32,
1259
+ enc: HttpCompressor,
1260
+ coding: i32
1261
+ ): i32 {
1262
+ http1ServerCheckWindow("Http1Connection.respondEncoded", body, off, len)
1263
+ if (!this.answerable()) {
1264
+ return H1_CLOSED
1265
+ }
1266
+ const packed: i32 = enc.compress(coding, body, off, len)
1267
+ let n: i32 = len
1268
+ if (packed >= 0) {
1269
+ enc.addFields(names, values)
1270
+ n = packed
1271
+ } else {
1272
+ enc.identityFields(names, values)
1273
+ }
1274
+ // `respond` and the body together, or neither: what `respond` sets is
1275
+ // put back when the body does not fit behind the head.
1276
+ const outputEnd: i32 = this.outputEnd
1277
+ const closeAfter: boolean = this.closeAfter
1278
+ const r: i32 = this.respond(status, enc.names, enc.values, n)
1279
+ if (r !== 0) {
1280
+ return r
1281
+ }
1282
+ if (!this.responseEmpty && n > this.bodyRoom()) {
1283
+ const fits: boolean = this.outputEnd - outputEnd + n <= this.config.outputSize - H1_CONTROL_RESERVE
1284
+ this.outputEnd = outputEnd
1285
+ this.closeAfter = closeAfter
1286
+ this.response = H1_R_NONE
1287
+ this.responseFraming = 0
1288
+ this.responseLeft = 0
1289
+ this.responseEmpty = false
1290
+ if (!fits) {
1291
+ return H1_INVALID
1292
+ }
1293
+ this.writeHeld = true
1294
+ return H1_AGAIN
1295
+ }
1296
+ const w: i32 = packed >= 0 ? this.write(enc.output, toI32(0), n) : this.write(body, off, n)
1297
+ if (w !== n) {
1298
+ panic("Http1Connection.respondEncoded: a body the room was checked for did not fit")
1299
+ }
1300
+ return this.end()
1301
+ }
1302
+
1208
1303
  // ---- WebSocket ----------------------------------------------------------------
1209
1304
 
1210
1305
  /**
@@ -1334,18 +1429,24 @@ export class Http1Connection {
1334
1429
  }
1335
1430
 
1336
1431
  /**
1337
- * The soonest `expired` can turn true, however the connection moves from
1338
- * here: `lastActive` only grows, and a head clock not yet started starts
1339
- * no earlier than it.
1432
+ * The soonest `expired` can turn true at or after `now` (`monotonicNanos`),
1433
+ * however the connection moves from here: `lastActive` only grows, and a
1434
+ * head clock not yet started starts no earlier than `now`, unless an
1435
+ * exchange is open: `nextRequest` starts a pipelined head's clock at
1436
+ * `lastActive` when it ends. Counting a quiet keep-alive's clock from
1437
+ * `lastActive` put it in the past once it was quiet for longer than
1438
+ * `headTimeout`, so each `expire` walked every slot.
1340
1439
  */
1341
- expiresNoSooner(): i64 {
1342
- const idle: i64 = toI64(this.config.idleTimeout) * toI64(1000000)
1343
- const head: i64 = toI64(this.config.headTimeout) * toI64(1000000)
1344
- const soonest: i64 = this.lastActive + (head < idle ? head : idle)
1345
- if (this.headTimed && this.headSince + head < soonest) {
1346
- return this.headSince + head
1440
+ expiresNoSooner(now: i64): i64 {
1441
+ const idle: i64 = this.lastActive + toI64(this.config.idleTimeout) * toI64(1000000)
1442
+ let start: i64 = now
1443
+ if (this.headTimed) {
1444
+ start = this.headSince
1445
+ } else if (this.requestOpen) {
1446
+ start = this.lastActive
1347
1447
  }
1348
- return soonest
1448
+ const head: i64 = start + toI64(this.config.headTimeout) * toI64(1000000)
1449
+ return head < idle ? head : idle
1349
1450
  }
1350
1451
  }
1351
1452
 
@@ -1679,8 +1780,8 @@ export class Http1Server {
1679
1780
  if (conn.expired(now)) {
1680
1781
  this.close(k)
1681
1782
  n = n + 1
1682
- } else if (conn.expiresNoSooner() < soonest) {
1683
- soonest = conn.expiresNoSooner()
1783
+ } else if (conn.expiresNoSooner(now) < soonest) {
1784
+ soonest = conn.expiresNoSooner(now)
1684
1785
  }
1685
1786
  }
1686
1787
  }
@@ -1982,8 +2083,8 @@ export class Http1TlsServer {
1982
2083
  if (conn.expired(now)) {
1983
2084
  this.tls.close(k)
1984
2085
  n = n + 1
1985
- } else if (conn.expiresNoSooner() < soonest) {
1986
- soonest = conn.expiresNoSooner()
2086
+ } else if (conn.expiresNoSooner(now) < soonest) {
2087
+ soonest = conn.expiresNoSooner(now)
1987
2088
  }
1988
2089
  }
1989
2090
  }