Skip to content

[FEATURE] Support binary response types in protoc-gen-go-http #102

@hasanMshawrab

Description

@hasanMshawrab

Is your feature request related to a problem? Please describe.

Currently, sebuf's protoc-gen-go-http only supports JSON-serialized protobuf message responses. This limitation prevents sebuf from being used for APIs that need to return binary data such as images (PNG, JPG), PDFs, or other non-JSON content types. This forces developers to either handle binary endpoints separately outside of sebuf or adopt workarounds like base64-encoding binary data in JSON, which is inefficient and doesn't follow REST standards.

Describe the solution you'd like

Extend protoc-gen-go-http to support binary response types. When a service method returns a message containing only a bytes field, the code generator should produce HTTP handlers that:

  • Stream the bytes directly without JSON marshaling
  • Set appropriate Content-Type headers based on the endpoint's requirements
  • Return the raw binary data with proper HTTP headers (Content-Length, caching headers, etc.)
  • Generate correct OpenAPI documentation for binary responses

Describe alternatives you've considered

  1. Keep binary endpoints outside sebuf - requires manual HTTP handler management and loses benefits of sebuf's validation, documentation generation, and consistency
  2. Base64-encode binary in JSON responses - adds 33% overhead and doesn't follow RESTful patterns for serving binary assets
  3. Use gRPC streaming - different protocol, doesn't solve the REST API use case
  4. Return binary data as a separate service - adds complexity and doesn't integrate with existing protobuf service definitions

Use Cases

  1. Asset/Logo APIs - Services like Alpaca Markets that need to serve company logos or asset images:
   GET /v1beta1/logos/{symbol} -> returns PNG image
  1. Document Generation - APIs that generate and return PDFs, CSVs, or other documents on demand:
   GET /api/v1/reports/{id}/download -> returns PDF document
  1. File Download Endpoints - APIs that serve user-generated content, certificates, or processed files:
   GET /api/v1/files/{id}/content -> returns binary file

Proposed API/Interface (if applicable)

syntax = "proto3";

package alpaca.logos;

import "sebuf/http/annotations.proto";

service LogoService {
  rpc GetLogo(GetLogoRequest) returns (LogoResponse) {
    option (sebuf.http.response_content_type) = "image/png";
  };
  
  rpc GetDocument(GetDocumentRequest) returns (DocumentResponse) {
    option (sebuf.http.response_content_type) = "application/pdf";
  };
}

message GetLogoRequest {
  string symbol = 1;
  bool placeholder = 2;
}

message LogoResponse {
  bytes image_data = 1;
}

message GetDocumentRequest {
  string document_id = 1;
}

message DocumentResponse {
  bytes content = 1;
}
// Generated code would resemble:

func (s *logoServiceServer) GetLogo(w http.ResponseWriter, r *http.Request) error {
    // ... validation and business logic ...
    
    resp, err := s.impl.GetLogo(r.Context(), &req)
    if err != nil {
        return err
    }
    
    // Direct binary streaming instead of JSON marshaling
    w.Header().Set("Content-Type", "image/png")
    w.Header().Set("Content-Length", strconv.Itoa(len(resp.ImageData)))
    w.WriteHeader(http.StatusOK)
    w.Write(resp.ImageData)
    
    return nil
}

func (s *logoServiceServer) GetDocument(w http.ResponseWriter, r *http.Request) error {
    // ... validation and business logic ...
    
    resp, err := s.impl.GetDocument(r.Context(), &req)
    if err != nil {
        return err
    }
    
    w.Header().Set("Content-Type", "application/pdf")
    w.Header().Set("Content-Length", strconv.Itoa(len(resp.Content)))
    w.WriteHeader(http.StatusOK)
    w.Write(resp.Content)
    
    return nil
}

Impact on Existing Code

  • This is a breaking change
  • This is backwards compatible
  • This requires migration steps

This feature would be fully backwards compatible - existing protobuf services would continue to work as before. The new functionality only activates when the optional response_content_type annotation is used or when a response message contains only a single bytes field.

Additional Context

  • The annotation could be added to sebuf/http/annotations.proto
  • The code generator needs to detect single-bytes field responses and treat them specially
  • OpenAPI generation should output format: binary for these endpoints
  • This aligns with how other HTTP API frameworks (like Echo, Gin, standard library) handle binary responses

Are you willing to contribute this feature?

  • Yes, I can implement this feature
  • Yes, but I would need guidance
  • No, but I can help test it
  • No

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgen/go-httpprotoc-gen-go-http (HTTP server)v2.0Targeted for v2.0 release

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions