0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HonoのマージされたPRを読んでいく#66 - #70

0
Last updated at Posted at 2025-12-13

この記事何?

エンジニア歴1年半。業務で利用しているHonoが大好きだが、TSもプログラミング知識も弱々すぎて上手く使いこなせない。速いはずが多分生かせてない。
ちゃんと理解するためにソースコードリーディングしたい。

でも目的なくソースコード眺めても意味ないし……ねむい……
そうだ!PRを全部読めば、変遷やなぜ変更されているかストーリー的に分かるのでは。
と思ったのでチャレンジしてみる。PR2000以上あるので、全部できるかは知らん。
https://github.com/honojs/hono

#66 perf: content length

マージではなくcloseされているためスキップ

#67 perf: content length

Content-Length 計算の高速化

変更前(PR #57)

  // src/middleware/default.ts
  export const defaultMiddleware = async (c: Context, next: Function) => {
    // ...
    await next()

    // すべてのレスポンスで実行される(遅い)
    if (c.res.body) {
      const buffer = await c.res.arrayBuffer()
      const res = new Response(buffer, {
        status: c.res.status,
        statusText: c.res.statusText,
        headers: c.res.headers,
      })
      res.headers.append('Content-Length', buffer.byteLength.toString())
      c.res = res
    }
  }

問題点:

  • arrayBuffer() は重い
  • すべてのレスポンスで実行される
  • レスポンス生成後に追加処理が必要

変更後

// src/context.ts
  newResponse(data: any, init: ResponseInit = {}): Response {
    init.status = this._status || init.status
    init.statusText = this._statusText || init.statusText

    // Content-Length を計算(同期的)
    const Encoder = new TextEncoder()
    const length = data ? data.byteLength || Encoder.encode(data).byteLength : 0
    //                                      ↑ 文字列の場合はエンコードしてバイト数取得

    init.headers = {
      ...init.headers,
      ...this._headers,
      ...{ 'Content-Length': String(length) }
    }

    return new Response(data, init)
  }

  // src/middleware/default.ts
  export const defaultMiddleware = async (c: Context, next: Function) => {
    // ...
    await next()

    // Content-Length 処理を削除!
  }
  • レスポンス生成時に直接計算
  // c.text(), c.json(), c.html() などが newResponse() を呼ぶ
  app.get('/', (c) => c.text('Hello'))
  • 同期処理に変更
  • デフォルトミドルウェアから削除

#68 なし

69 fix: Fixed context headers

Context のヘッダー処理のバグ修正

  • ヘッダーとステータスの優先順位を修正

#70 fix: change c.body() api

  // 変更前

  export class Context {
    body: (body: BodyInit, init?: ResponseInit) => Response

    constructor(req: Request, opts?: { res: Response; env: Env; event: FetchEvent }) {
      // ...
      this.body = this.newResponse  // ← newResponseのエイリアスだった
    }
  }

  // 変更後

  export class Context {
    render: (template: string, params?: object, options?: object) => Promise<Response>
    // ↑ renderが追加された

    constructor(req: Request, opts?: { res: Response; env: Env; event: FetchEvent }) {
      // ...
      // this.body = this.newResponse  // ← 削除
    }

    // c.body() が専用メソッドとして実装された
    body(data: any, status: number = this._status, headers: Headers = this._headers): Response {
      return this.newResponse(data, {
        status: status,
        headers: headers,
      })
    }
  }

「前より遅くなっちゃったな〜」という箇所を見つけるのはbugfix的に見つけられそうだけど、こうしたら速くなる!という代替案はどうやって見つけてるんだろう。
arrayBuffer()よりTextEncoderにした方が速い、というのをAIに聞かないと全然わからないし、AIに聞いても内部処理を理解してないから「ほえー、そうなんだぁ」になる。

使う道具の良さが分ってないので「100均の道具で充分でしょ」ってところと「いや、質も考えたらメーカーのちゃんとしたもののほうがよく動くよ」が判断できない。うーむ。どうやったら鍛えられるんだろう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?