Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to calculate response length? #526

Open
tib opened this issue Feb 15, 2024 · 11 comments
Open

How to calculate response length? #526

tib opened this issue Feb 15, 2024 · 11 comments
Labels
kind/support Adopter support requests. status/triage Collecting information required to triage the issue.
Milestone

Comments

@tib
Copy link
Contributor

tib commented Feb 15, 2024

Question

Hello,

I'm currently trying to implement a HEAD endpoint, using the Swift OpenAPI generator & runtime. I'm using the following snippet, but I'm aware that it's far from ideal:

package func head(
    _ input: Operations.head.Input
) async throws -> Operations.head.Output {
    let result = MyCodableJSONObject()
    
    /// NOTE1: is there an other way to calculate this?
    var headerFields = HTTPFields()
    let res =
        try OpenAPIRuntime.Converter(
            configuration: .init()
        )
        .setResponseBodyAsJSON(
            result,
            headerFields: &headerFields,
            contentType: ""
        )

    var length: Int64 = 0
    switch res.length {
    case .known(let value):
        length = value
    case .unknown:
        break
    }
    /// NOTE2: int64 -> int conversion is not good...
    return .ok(.init(headers: .init(Content_hyphen_Length: Int(length))))
}

Is there a better way to calculate the Content-Length header for HEAD requests? 🤔

Many thanks.

Tib

@tib tib added kind/support Adopter support requests. status/triage Collecting information required to triage the issue. labels Feb 15, 2024
@czechboy0
Copy link
Collaborator

Hi @tib,

Converter, setResponseBodyAsJSON, are SPIs that you shouldn't be calling directly. They're there only for the generated code to call.

Can you describe what you're trying to do first, and we can help you achieve it without using SPIs, which are not guaranteed to be stable?

@tib
Copy link
Contributor Author

tib commented Feb 15, 2024

I'm simply trying to implement a HEAD response and return the Content-Length.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD

I'm using a JSON response for the GET query (MyCodableJSONObject) and I'm trying to calculate the length of that response, so I can use the number in my HEAD handler.

I was not able to find a corresponding calculate method that I could use for this purpose.

I'm looking for a solution something like this (maybe a bit more generic, since this only works with JSON responses):

import HTTPTypes
@_spi(Generated) import OpenAPIRuntime

extension APIGateway {

    func calculateContentLength<T: Encodable>(_ value: T) throws -> Int64 {
        var headerFields = HTTPFields()
        let res =
            try OpenAPIRuntime.Converter(
                configuration: .init()
            )
            .setResponseBodyAsJSON(
                value,
                headerFields: &headerFields,
                contentType: ""
            )

        switch res.length {
        case .known(let value):
            return value
        case .unknown:
            return 0
        }
    }
}

@czechboy0
Copy link
Collaborator

Could you share the OpenAPI definition for the operation you're implementing?

From the top of my head, a ServerMiddleware that throws away response bodies should do the trick, and in it you can verify that the content-length is present (it should be there already: https://github.com/apple/swift-openapi-runtime/blob/76951d77a0609599d2dc233e7e40808a74767c6a/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift#L604).

That said, HEAD request support might be something we need to add first class support for. Would that help you here?

@czechboy0
Copy link
Collaborator

Hi @tib, are you still interested in this? I think we should consider adding first class support for HEAD requests.

@tib
Copy link
Contributor Author

tib commented Apr 16, 2024

Yes, this is still an issue for us. 👍

@czechboy0
Copy link
Collaborator

@tib Can you share the OpenAPI doc (or at least the snippet) that includes the operation you'd like to implement HEAD for? Just to make sure we focus on the right example.

@tib
Copy link
Contributor Author

tib commented Apr 18, 2024

openapi: 3.1.0
info:
  title: Example API
  description: 'Example'
  contact:
    name: Binary Birds
    url: https://binarybirds.com
    email: [email protected]
  version: 1.0.0
tags:
- name: Example
  description: ''
servers:
- url: http://localhost:8080
  description: dev
paths:
  /example:
    head:
      tags:
      - Example
      summary: Example head
      description: Example head request
      operationId: headOperation
      responses:
        200:
          $ref: '#/components/responses/ExampleResponse'
    
components:
  schemas:
    ExampleContentLength:
      type: integer
      description: Content length
    
  responses:
    ExampleResponse:
      description: Ok
      headers:
        Content-Length:
          $ref: '#/components/headers/Content-Length'
  headers:
    Content-Length:
      schema:
        $ref: '#/components/schemas/ExampleContentLength'
      description: Content length header

@czechboy0
Copy link
Collaborator

Interesting, so what's the reason to include $ref: '#/components/responses/ExampleResponse' in the 200 response, if it'll never get returned, because it's a HEAD request? Should it be a GET instead, and the ask here would be that all GET requests also support HEAD requests? (Maybe that should be done by the concrete transport, not sure.)

@czechboy0
Copy link
Collaborator

@adam-fowler @Joannis @0xTim What's the current handling of HEAD requests in Vapor and Hummingbird? Is this something that should be handled by the transport?

@adam-fowler
Copy link

You can add a flag to the Hummingbird router to auto generate head endpoints (It'll run endpoint but don't return body).

@czechboy0 czechboy0 added this to the Post-1.0 milestone Oct 29, 2024
@czechboy0
Copy link
Collaborator

Great - if Vapor has something similar, then I'd prefer to leave this to the transports to handle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/support Adopter support requests. status/triage Collecting information required to triage the issue.
Projects
None yet
Development

No branches or pull requests

3 participants