yao/grpc/api/api.go
Max 6e68efaba3 Implement gRPC support in the Yao SDK
- Add gRPC server configuration to the application, allowing for gRPC communication.
- Introduce new Makefile targets for gRPC unit testing and proto code generation.
- Update CI workflows to include gRPC tests with SQLite as the transport layer.
- Refactor the sandbox design to support multi-node capabilities and improve isolation.
- Enhance the service layer to facilitate internal request forwarding for gRPC APIs.

This commit lays the groundwork for integrating gRPC into the Yao SDK, improving performance and scalability.
2026-03-04 13:17:48 +08:00

69 lines
1.7 KiB
Go

package api
import (
"bytes"
"context"
"net/http"
"net/http/httptest"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"github.com/yaoapp/yao/grpc/pb"
"github.com/yaoapp/yao/service"
)
// Handler implements the API gRPC method (internal HTTP proxy).
type Handler struct{}
// API proxies a gRPC request to the internal openapi HTTP router.
func (h *Handler) API(ctx context.Context, req *pb.APIRequest) (*pb.APIResponse, error) {
router := service.Router
if router == nil {
return nil, status.Error(codes.Unavailable, "HTTP router not initialized")
}
if req.Method == "" {
return nil, status.Error(codes.InvalidArgument, "method is required")
}
if req.Path == "" {
return nil, status.Error(codes.InvalidArgument, "path is required")
}
httpReq, err := http.NewRequestWithContext(ctx, req.Method, req.Path, bytes.NewReader(req.Body))
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to build HTTP request: %v", err)
}
for k, v := range req.Headers {
httpReq.Header.Set(k, v)
}
// Forward Bearer token from gRPC metadata to HTTP Authorization header
// when the caller didn't explicitly set it.
if httpReq.Header.Get("Authorization") == "" {
if md, ok := metadata.FromIncomingContext(ctx); ok {
if vals := md.Get("authorization"); len(vals) > 0 {
httpReq.Header.Set("Authorization", vals[0])
}
}
}
w := httptest.NewRecorder()
router.ServeHTTP(w, httpReq)
result := w.Result()
defer result.Body.Close()
respHeaders := make(map[string]string, len(result.Header))
for k := range result.Header {
respHeaders[k] = result.Header.Get(k)
}
return &pb.APIResponse{
Status: int32(result.StatusCode),
Headers: respHeaders,
Body: w.Body.Bytes(),
}, nil
}