github.com/livekit/protocol@v1.39.3/replay/cloud_replay.twirp.go (about) 1 // Code generated by protoc-gen-twirp v8.1.3, DO NOT EDIT. 2 // source: cloud_replay.proto 3 4 package replay 5 6 import context "context" 7 import fmt "fmt" 8 import http "net/http" 9 import io "io" 10 import json "encoding/json" 11 import strconv "strconv" 12 import strings "strings" 13 14 import protojson "google.golang.org/protobuf/encoding/protojson" 15 import proto "google.golang.org/protobuf/proto" 16 import twirp "github.com/twitchtv/twirp" 17 import ctxsetters "github.com/twitchtv/twirp/ctxsetters" 18 19 import google_protobuf "google.golang.org/protobuf/types/known/emptypb" 20 21 import bytes "bytes" 22 import errors "errors" 23 import path "path" 24 import url "net/url" 25 26 // Version compatibility assertion. 27 // If the constant is not defined in the package, that likely means 28 // the package needs to be updated to work with this generated code. 29 // See https://twitchtv.github.io/twirp/docs/version_matrix.html 30 const _ = twirp.TwirpPackageMinVersion_8_1_0 31 32 // ================ 33 // Replay Interface 34 // ================ 35 36 // Experimental (not currently available) 37 type Replay interface { 38 ListReplays(context.Context, *ListReplaysRequest) (*ListReplaysResponse, error) 39 40 LoadReplay(context.Context, *LoadReplayRequest) (*LoadReplayResponse, error) 41 42 SeekForRoom(context.Context, *RoomSeekRequest) (*google_protobuf.Empty, error) 43 44 CloseReplay(context.Context, *CloseReplayRequest) (*google_protobuf.Empty, error) 45 46 DeleteReplay(context.Context, *DeleteReplayRequest) (*google_protobuf.Empty, error) 47 } 48 49 // ====================== 50 // Replay Protobuf Client 51 // ====================== 52 53 type replayProtobufClient struct { 54 client HTTPClient 55 urls [5]string 56 interceptor twirp.Interceptor 57 opts twirp.ClientOptions 58 } 59 60 // NewReplayProtobufClient creates a Protobuf client that implements the Replay interface. 61 // It communicates using Protobuf and can be configured with a custom HTTPClient. 62 func NewReplayProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Replay { 63 if c, ok := client.(*http.Client); ok { 64 client = withoutRedirects(c) 65 } 66 67 clientOpts := twirp.ClientOptions{} 68 for _, o := range opts { 69 o(&clientOpts) 70 } 71 72 // Using ReadOpt allows backwards and forwards compatibility with new options in the future 73 literalURLs := false 74 _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 75 var pathPrefix string 76 if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 77 pathPrefix = "/twirp" // default prefix 78 } 79 80 // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 81 serviceURL := sanitizeBaseURL(baseURL) 82 serviceURL += baseServicePath(pathPrefix, "replay", "Replay") 83 urls := [5]string{ 84 serviceURL + "ListReplays", 85 serviceURL + "LoadReplay", 86 serviceURL + "SeekForRoom", 87 serviceURL + "CloseReplay", 88 serviceURL + "DeleteReplay", 89 } 90 91 return &replayProtobufClient{ 92 client: client, 93 urls: urls, 94 interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 95 opts: clientOpts, 96 } 97 } 98 99 func (c *replayProtobufClient) ListReplays(ctx context.Context, in *ListReplaysRequest) (*ListReplaysResponse, error) { 100 ctx = ctxsetters.WithPackageName(ctx, "replay") 101 ctx = ctxsetters.WithServiceName(ctx, "Replay") 102 ctx = ctxsetters.WithMethodName(ctx, "ListReplays") 103 caller := c.callListReplays 104 if c.interceptor != nil { 105 caller = func(ctx context.Context, req *ListReplaysRequest) (*ListReplaysResponse, error) { 106 resp, err := c.interceptor( 107 func(ctx context.Context, req interface{}) (interface{}, error) { 108 typedReq, ok := req.(*ListReplaysRequest) 109 if !ok { 110 return nil, twirp.InternalError("failed type assertion req.(*ListReplaysRequest) when calling interceptor") 111 } 112 return c.callListReplays(ctx, typedReq) 113 }, 114 )(ctx, req) 115 if resp != nil { 116 typedResp, ok := resp.(*ListReplaysResponse) 117 if !ok { 118 return nil, twirp.InternalError("failed type assertion resp.(*ListReplaysResponse) when calling interceptor") 119 } 120 return typedResp, err 121 } 122 return nil, err 123 } 124 } 125 return caller(ctx, in) 126 } 127 128 func (c *replayProtobufClient) callListReplays(ctx context.Context, in *ListReplaysRequest) (*ListReplaysResponse, error) { 129 out := new(ListReplaysResponse) 130 ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 131 if err != nil { 132 twerr, ok := err.(twirp.Error) 133 if !ok { 134 twerr = twirp.InternalErrorWith(err) 135 } 136 callClientError(ctx, c.opts.Hooks, twerr) 137 return nil, err 138 } 139 140 callClientResponseReceived(ctx, c.opts.Hooks) 141 142 return out, nil 143 } 144 145 func (c *replayProtobufClient) LoadReplay(ctx context.Context, in *LoadReplayRequest) (*LoadReplayResponse, error) { 146 ctx = ctxsetters.WithPackageName(ctx, "replay") 147 ctx = ctxsetters.WithServiceName(ctx, "Replay") 148 ctx = ctxsetters.WithMethodName(ctx, "LoadReplay") 149 caller := c.callLoadReplay 150 if c.interceptor != nil { 151 caller = func(ctx context.Context, req *LoadReplayRequest) (*LoadReplayResponse, error) { 152 resp, err := c.interceptor( 153 func(ctx context.Context, req interface{}) (interface{}, error) { 154 typedReq, ok := req.(*LoadReplayRequest) 155 if !ok { 156 return nil, twirp.InternalError("failed type assertion req.(*LoadReplayRequest) when calling interceptor") 157 } 158 return c.callLoadReplay(ctx, typedReq) 159 }, 160 )(ctx, req) 161 if resp != nil { 162 typedResp, ok := resp.(*LoadReplayResponse) 163 if !ok { 164 return nil, twirp.InternalError("failed type assertion resp.(*LoadReplayResponse) when calling interceptor") 165 } 166 return typedResp, err 167 } 168 return nil, err 169 } 170 } 171 return caller(ctx, in) 172 } 173 174 func (c *replayProtobufClient) callLoadReplay(ctx context.Context, in *LoadReplayRequest) (*LoadReplayResponse, error) { 175 out := new(LoadReplayResponse) 176 ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) 177 if err != nil { 178 twerr, ok := err.(twirp.Error) 179 if !ok { 180 twerr = twirp.InternalErrorWith(err) 181 } 182 callClientError(ctx, c.opts.Hooks, twerr) 183 return nil, err 184 } 185 186 callClientResponseReceived(ctx, c.opts.Hooks) 187 188 return out, nil 189 } 190 191 func (c *replayProtobufClient) SeekForRoom(ctx context.Context, in *RoomSeekRequest) (*google_protobuf.Empty, error) { 192 ctx = ctxsetters.WithPackageName(ctx, "replay") 193 ctx = ctxsetters.WithServiceName(ctx, "Replay") 194 ctx = ctxsetters.WithMethodName(ctx, "SeekForRoom") 195 caller := c.callSeekForRoom 196 if c.interceptor != nil { 197 caller = func(ctx context.Context, req *RoomSeekRequest) (*google_protobuf.Empty, error) { 198 resp, err := c.interceptor( 199 func(ctx context.Context, req interface{}) (interface{}, error) { 200 typedReq, ok := req.(*RoomSeekRequest) 201 if !ok { 202 return nil, twirp.InternalError("failed type assertion req.(*RoomSeekRequest) when calling interceptor") 203 } 204 return c.callSeekForRoom(ctx, typedReq) 205 }, 206 )(ctx, req) 207 if resp != nil { 208 typedResp, ok := resp.(*google_protobuf.Empty) 209 if !ok { 210 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 211 } 212 return typedResp, err 213 } 214 return nil, err 215 } 216 } 217 return caller(ctx, in) 218 } 219 220 func (c *replayProtobufClient) callSeekForRoom(ctx context.Context, in *RoomSeekRequest) (*google_protobuf.Empty, error) { 221 out := new(google_protobuf.Empty) 222 ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) 223 if err != nil { 224 twerr, ok := err.(twirp.Error) 225 if !ok { 226 twerr = twirp.InternalErrorWith(err) 227 } 228 callClientError(ctx, c.opts.Hooks, twerr) 229 return nil, err 230 } 231 232 callClientResponseReceived(ctx, c.opts.Hooks) 233 234 return out, nil 235 } 236 237 func (c *replayProtobufClient) CloseReplay(ctx context.Context, in *CloseReplayRequest) (*google_protobuf.Empty, error) { 238 ctx = ctxsetters.WithPackageName(ctx, "replay") 239 ctx = ctxsetters.WithServiceName(ctx, "Replay") 240 ctx = ctxsetters.WithMethodName(ctx, "CloseReplay") 241 caller := c.callCloseReplay 242 if c.interceptor != nil { 243 caller = func(ctx context.Context, req *CloseReplayRequest) (*google_protobuf.Empty, error) { 244 resp, err := c.interceptor( 245 func(ctx context.Context, req interface{}) (interface{}, error) { 246 typedReq, ok := req.(*CloseReplayRequest) 247 if !ok { 248 return nil, twirp.InternalError("failed type assertion req.(*CloseReplayRequest) when calling interceptor") 249 } 250 return c.callCloseReplay(ctx, typedReq) 251 }, 252 )(ctx, req) 253 if resp != nil { 254 typedResp, ok := resp.(*google_protobuf.Empty) 255 if !ok { 256 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 257 } 258 return typedResp, err 259 } 260 return nil, err 261 } 262 } 263 return caller(ctx, in) 264 } 265 266 func (c *replayProtobufClient) callCloseReplay(ctx context.Context, in *CloseReplayRequest) (*google_protobuf.Empty, error) { 267 out := new(google_protobuf.Empty) 268 ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[3], in, out) 269 if err != nil { 270 twerr, ok := err.(twirp.Error) 271 if !ok { 272 twerr = twirp.InternalErrorWith(err) 273 } 274 callClientError(ctx, c.opts.Hooks, twerr) 275 return nil, err 276 } 277 278 callClientResponseReceived(ctx, c.opts.Hooks) 279 280 return out, nil 281 } 282 283 func (c *replayProtobufClient) DeleteReplay(ctx context.Context, in *DeleteReplayRequest) (*google_protobuf.Empty, error) { 284 ctx = ctxsetters.WithPackageName(ctx, "replay") 285 ctx = ctxsetters.WithServiceName(ctx, "Replay") 286 ctx = ctxsetters.WithMethodName(ctx, "DeleteReplay") 287 caller := c.callDeleteReplay 288 if c.interceptor != nil { 289 caller = func(ctx context.Context, req *DeleteReplayRequest) (*google_protobuf.Empty, error) { 290 resp, err := c.interceptor( 291 func(ctx context.Context, req interface{}) (interface{}, error) { 292 typedReq, ok := req.(*DeleteReplayRequest) 293 if !ok { 294 return nil, twirp.InternalError("failed type assertion req.(*DeleteReplayRequest) when calling interceptor") 295 } 296 return c.callDeleteReplay(ctx, typedReq) 297 }, 298 )(ctx, req) 299 if resp != nil { 300 typedResp, ok := resp.(*google_protobuf.Empty) 301 if !ok { 302 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 303 } 304 return typedResp, err 305 } 306 return nil, err 307 } 308 } 309 return caller(ctx, in) 310 } 311 312 func (c *replayProtobufClient) callDeleteReplay(ctx context.Context, in *DeleteReplayRequest) (*google_protobuf.Empty, error) { 313 out := new(google_protobuf.Empty) 314 ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[4], in, out) 315 if err != nil { 316 twerr, ok := err.(twirp.Error) 317 if !ok { 318 twerr = twirp.InternalErrorWith(err) 319 } 320 callClientError(ctx, c.opts.Hooks, twerr) 321 return nil, err 322 } 323 324 callClientResponseReceived(ctx, c.opts.Hooks) 325 326 return out, nil 327 } 328 329 // ================== 330 // Replay JSON Client 331 // ================== 332 333 type replayJSONClient struct { 334 client HTTPClient 335 urls [5]string 336 interceptor twirp.Interceptor 337 opts twirp.ClientOptions 338 } 339 340 // NewReplayJSONClient creates a JSON client that implements the Replay interface. 341 // It communicates using JSON and can be configured with a custom HTTPClient. 342 func NewReplayJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Replay { 343 if c, ok := client.(*http.Client); ok { 344 client = withoutRedirects(c) 345 } 346 347 clientOpts := twirp.ClientOptions{} 348 for _, o := range opts { 349 o(&clientOpts) 350 } 351 352 // Using ReadOpt allows backwards and forwards compatibility with new options in the future 353 literalURLs := false 354 _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 355 var pathPrefix string 356 if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 357 pathPrefix = "/twirp" // default prefix 358 } 359 360 // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 361 serviceURL := sanitizeBaseURL(baseURL) 362 serviceURL += baseServicePath(pathPrefix, "replay", "Replay") 363 urls := [5]string{ 364 serviceURL + "ListReplays", 365 serviceURL + "LoadReplay", 366 serviceURL + "SeekForRoom", 367 serviceURL + "CloseReplay", 368 serviceURL + "DeleteReplay", 369 } 370 371 return &replayJSONClient{ 372 client: client, 373 urls: urls, 374 interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 375 opts: clientOpts, 376 } 377 } 378 379 func (c *replayJSONClient) ListReplays(ctx context.Context, in *ListReplaysRequest) (*ListReplaysResponse, error) { 380 ctx = ctxsetters.WithPackageName(ctx, "replay") 381 ctx = ctxsetters.WithServiceName(ctx, "Replay") 382 ctx = ctxsetters.WithMethodName(ctx, "ListReplays") 383 caller := c.callListReplays 384 if c.interceptor != nil { 385 caller = func(ctx context.Context, req *ListReplaysRequest) (*ListReplaysResponse, error) { 386 resp, err := c.interceptor( 387 func(ctx context.Context, req interface{}) (interface{}, error) { 388 typedReq, ok := req.(*ListReplaysRequest) 389 if !ok { 390 return nil, twirp.InternalError("failed type assertion req.(*ListReplaysRequest) when calling interceptor") 391 } 392 return c.callListReplays(ctx, typedReq) 393 }, 394 )(ctx, req) 395 if resp != nil { 396 typedResp, ok := resp.(*ListReplaysResponse) 397 if !ok { 398 return nil, twirp.InternalError("failed type assertion resp.(*ListReplaysResponse) when calling interceptor") 399 } 400 return typedResp, err 401 } 402 return nil, err 403 } 404 } 405 return caller(ctx, in) 406 } 407 408 func (c *replayJSONClient) callListReplays(ctx context.Context, in *ListReplaysRequest) (*ListReplaysResponse, error) { 409 out := new(ListReplaysResponse) 410 ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 411 if err != nil { 412 twerr, ok := err.(twirp.Error) 413 if !ok { 414 twerr = twirp.InternalErrorWith(err) 415 } 416 callClientError(ctx, c.opts.Hooks, twerr) 417 return nil, err 418 } 419 420 callClientResponseReceived(ctx, c.opts.Hooks) 421 422 return out, nil 423 } 424 425 func (c *replayJSONClient) LoadReplay(ctx context.Context, in *LoadReplayRequest) (*LoadReplayResponse, error) { 426 ctx = ctxsetters.WithPackageName(ctx, "replay") 427 ctx = ctxsetters.WithServiceName(ctx, "Replay") 428 ctx = ctxsetters.WithMethodName(ctx, "LoadReplay") 429 caller := c.callLoadReplay 430 if c.interceptor != nil { 431 caller = func(ctx context.Context, req *LoadReplayRequest) (*LoadReplayResponse, error) { 432 resp, err := c.interceptor( 433 func(ctx context.Context, req interface{}) (interface{}, error) { 434 typedReq, ok := req.(*LoadReplayRequest) 435 if !ok { 436 return nil, twirp.InternalError("failed type assertion req.(*LoadReplayRequest) when calling interceptor") 437 } 438 return c.callLoadReplay(ctx, typedReq) 439 }, 440 )(ctx, req) 441 if resp != nil { 442 typedResp, ok := resp.(*LoadReplayResponse) 443 if !ok { 444 return nil, twirp.InternalError("failed type assertion resp.(*LoadReplayResponse) when calling interceptor") 445 } 446 return typedResp, err 447 } 448 return nil, err 449 } 450 } 451 return caller(ctx, in) 452 } 453 454 func (c *replayJSONClient) callLoadReplay(ctx context.Context, in *LoadReplayRequest) (*LoadReplayResponse, error) { 455 out := new(LoadReplayResponse) 456 ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) 457 if err != nil { 458 twerr, ok := err.(twirp.Error) 459 if !ok { 460 twerr = twirp.InternalErrorWith(err) 461 } 462 callClientError(ctx, c.opts.Hooks, twerr) 463 return nil, err 464 } 465 466 callClientResponseReceived(ctx, c.opts.Hooks) 467 468 return out, nil 469 } 470 471 func (c *replayJSONClient) SeekForRoom(ctx context.Context, in *RoomSeekRequest) (*google_protobuf.Empty, error) { 472 ctx = ctxsetters.WithPackageName(ctx, "replay") 473 ctx = ctxsetters.WithServiceName(ctx, "Replay") 474 ctx = ctxsetters.WithMethodName(ctx, "SeekForRoom") 475 caller := c.callSeekForRoom 476 if c.interceptor != nil { 477 caller = func(ctx context.Context, req *RoomSeekRequest) (*google_protobuf.Empty, error) { 478 resp, err := c.interceptor( 479 func(ctx context.Context, req interface{}) (interface{}, error) { 480 typedReq, ok := req.(*RoomSeekRequest) 481 if !ok { 482 return nil, twirp.InternalError("failed type assertion req.(*RoomSeekRequest) when calling interceptor") 483 } 484 return c.callSeekForRoom(ctx, typedReq) 485 }, 486 )(ctx, req) 487 if resp != nil { 488 typedResp, ok := resp.(*google_protobuf.Empty) 489 if !ok { 490 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 491 } 492 return typedResp, err 493 } 494 return nil, err 495 } 496 } 497 return caller(ctx, in) 498 } 499 500 func (c *replayJSONClient) callSeekForRoom(ctx context.Context, in *RoomSeekRequest) (*google_protobuf.Empty, error) { 501 out := new(google_protobuf.Empty) 502 ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) 503 if err != nil { 504 twerr, ok := err.(twirp.Error) 505 if !ok { 506 twerr = twirp.InternalErrorWith(err) 507 } 508 callClientError(ctx, c.opts.Hooks, twerr) 509 return nil, err 510 } 511 512 callClientResponseReceived(ctx, c.opts.Hooks) 513 514 return out, nil 515 } 516 517 func (c *replayJSONClient) CloseReplay(ctx context.Context, in *CloseReplayRequest) (*google_protobuf.Empty, error) { 518 ctx = ctxsetters.WithPackageName(ctx, "replay") 519 ctx = ctxsetters.WithServiceName(ctx, "Replay") 520 ctx = ctxsetters.WithMethodName(ctx, "CloseReplay") 521 caller := c.callCloseReplay 522 if c.interceptor != nil { 523 caller = func(ctx context.Context, req *CloseReplayRequest) (*google_protobuf.Empty, error) { 524 resp, err := c.interceptor( 525 func(ctx context.Context, req interface{}) (interface{}, error) { 526 typedReq, ok := req.(*CloseReplayRequest) 527 if !ok { 528 return nil, twirp.InternalError("failed type assertion req.(*CloseReplayRequest) when calling interceptor") 529 } 530 return c.callCloseReplay(ctx, typedReq) 531 }, 532 )(ctx, req) 533 if resp != nil { 534 typedResp, ok := resp.(*google_protobuf.Empty) 535 if !ok { 536 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 537 } 538 return typedResp, err 539 } 540 return nil, err 541 } 542 } 543 return caller(ctx, in) 544 } 545 546 func (c *replayJSONClient) callCloseReplay(ctx context.Context, in *CloseReplayRequest) (*google_protobuf.Empty, error) { 547 out := new(google_protobuf.Empty) 548 ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[3], in, out) 549 if err != nil { 550 twerr, ok := err.(twirp.Error) 551 if !ok { 552 twerr = twirp.InternalErrorWith(err) 553 } 554 callClientError(ctx, c.opts.Hooks, twerr) 555 return nil, err 556 } 557 558 callClientResponseReceived(ctx, c.opts.Hooks) 559 560 return out, nil 561 } 562 563 func (c *replayJSONClient) DeleteReplay(ctx context.Context, in *DeleteReplayRequest) (*google_protobuf.Empty, error) { 564 ctx = ctxsetters.WithPackageName(ctx, "replay") 565 ctx = ctxsetters.WithServiceName(ctx, "Replay") 566 ctx = ctxsetters.WithMethodName(ctx, "DeleteReplay") 567 caller := c.callDeleteReplay 568 if c.interceptor != nil { 569 caller = func(ctx context.Context, req *DeleteReplayRequest) (*google_protobuf.Empty, error) { 570 resp, err := c.interceptor( 571 func(ctx context.Context, req interface{}) (interface{}, error) { 572 typedReq, ok := req.(*DeleteReplayRequest) 573 if !ok { 574 return nil, twirp.InternalError("failed type assertion req.(*DeleteReplayRequest) when calling interceptor") 575 } 576 return c.callDeleteReplay(ctx, typedReq) 577 }, 578 )(ctx, req) 579 if resp != nil { 580 typedResp, ok := resp.(*google_protobuf.Empty) 581 if !ok { 582 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 583 } 584 return typedResp, err 585 } 586 return nil, err 587 } 588 } 589 return caller(ctx, in) 590 } 591 592 func (c *replayJSONClient) callDeleteReplay(ctx context.Context, in *DeleteReplayRequest) (*google_protobuf.Empty, error) { 593 out := new(google_protobuf.Empty) 594 ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[4], in, out) 595 if err != nil { 596 twerr, ok := err.(twirp.Error) 597 if !ok { 598 twerr = twirp.InternalErrorWith(err) 599 } 600 callClientError(ctx, c.opts.Hooks, twerr) 601 return nil, err 602 } 603 604 callClientResponseReceived(ctx, c.opts.Hooks) 605 606 return out, nil 607 } 608 609 // ===================== 610 // Replay Server Handler 611 // ===================== 612 613 type replayServer struct { 614 Replay 615 interceptor twirp.Interceptor 616 hooks *twirp.ServerHooks 617 pathPrefix string // prefix for routing 618 jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response 619 jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names 620 } 621 622 // NewReplayServer builds a TwirpServer that can be used as an http.Handler to handle 623 // HTTP requests that are routed to the right method in the provided svc implementation. 624 // The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). 625 func NewReplayServer(svc Replay, opts ...interface{}) TwirpServer { 626 serverOpts := newServerOpts(opts) 627 628 // Using ReadOpt allows backwards and forwards compatibility with new options in the future 629 jsonSkipDefaults := false 630 _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) 631 jsonCamelCase := false 632 _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) 633 var pathPrefix string 634 if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 635 pathPrefix = "/twirp" // default prefix 636 } 637 638 return &replayServer{ 639 Replay: svc, 640 hooks: serverOpts.Hooks, 641 interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), 642 pathPrefix: pathPrefix, 643 jsonSkipDefaults: jsonSkipDefaults, 644 jsonCamelCase: jsonCamelCase, 645 } 646 } 647 648 // writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. 649 // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) 650 func (s *replayServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { 651 writeError(ctx, resp, err, s.hooks) 652 } 653 654 // handleRequestBodyError is used to handle error when the twirp server cannot read request 655 func (s *replayServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { 656 if context.Canceled == ctx.Err() { 657 s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) 658 return 659 } 660 if context.DeadlineExceeded == ctx.Err() { 661 s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) 662 return 663 } 664 s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) 665 } 666 667 // ReplayPathPrefix is a convenience constant that may identify URL paths. 668 // Should be used with caution, it only matches routes generated by Twirp Go clients, 669 // with the default "/twirp" prefix and default CamelCase service and method names. 670 // More info: https://twitchtv.github.io/twirp/docs/routing.html 671 const ReplayPathPrefix = "/twirp/replay.Replay/" 672 673 func (s *replayServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { 674 ctx := req.Context() 675 ctx = ctxsetters.WithPackageName(ctx, "replay") 676 ctx = ctxsetters.WithServiceName(ctx, "Replay") 677 ctx = ctxsetters.WithResponseWriter(ctx, resp) 678 679 var err error 680 ctx, err = callRequestReceived(ctx, s.hooks) 681 if err != nil { 682 s.writeError(ctx, resp, err) 683 return 684 } 685 686 if req.Method != "POST" { 687 msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) 688 s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 689 return 690 } 691 692 // Verify path format: [<prefix>]/<package>.<Service>/<Method> 693 prefix, pkgService, method := parseTwirpPath(req.URL.Path) 694 if pkgService != "replay.Replay" { 695 msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 696 s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 697 return 698 } 699 if prefix != s.pathPrefix { 700 msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) 701 s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 702 return 703 } 704 705 switch method { 706 case "ListReplays": 707 s.serveListReplays(ctx, resp, req) 708 return 709 case "LoadReplay": 710 s.serveLoadReplay(ctx, resp, req) 711 return 712 case "SeekForRoom": 713 s.serveSeekForRoom(ctx, resp, req) 714 return 715 case "CloseReplay": 716 s.serveCloseReplay(ctx, resp, req) 717 return 718 case "DeleteReplay": 719 s.serveDeleteReplay(ctx, resp, req) 720 return 721 default: 722 msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 723 s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 724 return 725 } 726 } 727 728 func (s *replayServer) serveListReplays(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 729 header := req.Header.Get("Content-Type") 730 i := strings.Index(header, ";") 731 if i == -1 { 732 i = len(header) 733 } 734 switch strings.TrimSpace(strings.ToLower(header[:i])) { 735 case "application/json": 736 s.serveListReplaysJSON(ctx, resp, req) 737 case "application/protobuf": 738 s.serveListReplaysProtobuf(ctx, resp, req) 739 default: 740 msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 741 twerr := badRouteError(msg, req.Method, req.URL.Path) 742 s.writeError(ctx, resp, twerr) 743 } 744 } 745 746 func (s *replayServer) serveListReplaysJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 747 var err error 748 ctx = ctxsetters.WithMethodName(ctx, "ListReplays") 749 ctx, err = callRequestRouted(ctx, s.hooks) 750 if err != nil { 751 s.writeError(ctx, resp, err) 752 return 753 } 754 755 d := json.NewDecoder(req.Body) 756 rawReqBody := json.RawMessage{} 757 if err := d.Decode(&rawReqBody); err != nil { 758 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 759 return 760 } 761 reqContent := new(ListReplaysRequest) 762 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 763 if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 764 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 765 return 766 } 767 768 handler := s.Replay.ListReplays 769 if s.interceptor != nil { 770 handler = func(ctx context.Context, req *ListReplaysRequest) (*ListReplaysResponse, error) { 771 resp, err := s.interceptor( 772 func(ctx context.Context, req interface{}) (interface{}, error) { 773 typedReq, ok := req.(*ListReplaysRequest) 774 if !ok { 775 return nil, twirp.InternalError("failed type assertion req.(*ListReplaysRequest) when calling interceptor") 776 } 777 return s.Replay.ListReplays(ctx, typedReq) 778 }, 779 )(ctx, req) 780 if resp != nil { 781 typedResp, ok := resp.(*ListReplaysResponse) 782 if !ok { 783 return nil, twirp.InternalError("failed type assertion resp.(*ListReplaysResponse) when calling interceptor") 784 } 785 return typedResp, err 786 } 787 return nil, err 788 } 789 } 790 791 // Call service method 792 var respContent *ListReplaysResponse 793 func() { 794 defer ensurePanicResponses(ctx, resp, s.hooks) 795 respContent, err = handler(ctx, reqContent) 796 }() 797 798 if err != nil { 799 s.writeError(ctx, resp, err) 800 return 801 } 802 if respContent == nil { 803 s.writeError(ctx, resp, twirp.InternalError("received a nil *ListReplaysResponse and nil error while calling ListReplays. nil responses are not supported")) 804 return 805 } 806 807 ctx = callResponsePrepared(ctx, s.hooks) 808 809 marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 810 respBytes, err := marshaler.Marshal(respContent) 811 if err != nil { 812 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 813 return 814 } 815 816 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 817 resp.Header().Set("Content-Type", "application/json") 818 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 819 resp.WriteHeader(http.StatusOK) 820 821 if n, err := resp.Write(respBytes); err != nil { 822 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 823 twerr := twirp.NewError(twirp.Unknown, msg) 824 ctx = callError(ctx, s.hooks, twerr) 825 } 826 callResponseSent(ctx, s.hooks) 827 } 828 829 func (s *replayServer) serveListReplaysProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 830 var err error 831 ctx = ctxsetters.WithMethodName(ctx, "ListReplays") 832 ctx, err = callRequestRouted(ctx, s.hooks) 833 if err != nil { 834 s.writeError(ctx, resp, err) 835 return 836 } 837 838 buf, err := io.ReadAll(req.Body) 839 if err != nil { 840 s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 841 return 842 } 843 reqContent := new(ListReplaysRequest) 844 if err = proto.Unmarshal(buf, reqContent); err != nil { 845 s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 846 return 847 } 848 849 handler := s.Replay.ListReplays 850 if s.interceptor != nil { 851 handler = func(ctx context.Context, req *ListReplaysRequest) (*ListReplaysResponse, error) { 852 resp, err := s.interceptor( 853 func(ctx context.Context, req interface{}) (interface{}, error) { 854 typedReq, ok := req.(*ListReplaysRequest) 855 if !ok { 856 return nil, twirp.InternalError("failed type assertion req.(*ListReplaysRequest) when calling interceptor") 857 } 858 return s.Replay.ListReplays(ctx, typedReq) 859 }, 860 )(ctx, req) 861 if resp != nil { 862 typedResp, ok := resp.(*ListReplaysResponse) 863 if !ok { 864 return nil, twirp.InternalError("failed type assertion resp.(*ListReplaysResponse) when calling interceptor") 865 } 866 return typedResp, err 867 } 868 return nil, err 869 } 870 } 871 872 // Call service method 873 var respContent *ListReplaysResponse 874 func() { 875 defer ensurePanicResponses(ctx, resp, s.hooks) 876 respContent, err = handler(ctx, reqContent) 877 }() 878 879 if err != nil { 880 s.writeError(ctx, resp, err) 881 return 882 } 883 if respContent == nil { 884 s.writeError(ctx, resp, twirp.InternalError("received a nil *ListReplaysResponse and nil error while calling ListReplays. nil responses are not supported")) 885 return 886 } 887 888 ctx = callResponsePrepared(ctx, s.hooks) 889 890 respBytes, err := proto.Marshal(respContent) 891 if err != nil { 892 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 893 return 894 } 895 896 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 897 resp.Header().Set("Content-Type", "application/protobuf") 898 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 899 resp.WriteHeader(http.StatusOK) 900 if n, err := resp.Write(respBytes); err != nil { 901 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 902 twerr := twirp.NewError(twirp.Unknown, msg) 903 ctx = callError(ctx, s.hooks, twerr) 904 } 905 callResponseSent(ctx, s.hooks) 906 } 907 908 func (s *replayServer) serveLoadReplay(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 909 header := req.Header.Get("Content-Type") 910 i := strings.Index(header, ";") 911 if i == -1 { 912 i = len(header) 913 } 914 switch strings.TrimSpace(strings.ToLower(header[:i])) { 915 case "application/json": 916 s.serveLoadReplayJSON(ctx, resp, req) 917 case "application/protobuf": 918 s.serveLoadReplayProtobuf(ctx, resp, req) 919 default: 920 msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 921 twerr := badRouteError(msg, req.Method, req.URL.Path) 922 s.writeError(ctx, resp, twerr) 923 } 924 } 925 926 func (s *replayServer) serveLoadReplayJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 927 var err error 928 ctx = ctxsetters.WithMethodName(ctx, "LoadReplay") 929 ctx, err = callRequestRouted(ctx, s.hooks) 930 if err != nil { 931 s.writeError(ctx, resp, err) 932 return 933 } 934 935 d := json.NewDecoder(req.Body) 936 rawReqBody := json.RawMessage{} 937 if err := d.Decode(&rawReqBody); err != nil { 938 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 939 return 940 } 941 reqContent := new(LoadReplayRequest) 942 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 943 if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 944 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 945 return 946 } 947 948 handler := s.Replay.LoadReplay 949 if s.interceptor != nil { 950 handler = func(ctx context.Context, req *LoadReplayRequest) (*LoadReplayResponse, error) { 951 resp, err := s.interceptor( 952 func(ctx context.Context, req interface{}) (interface{}, error) { 953 typedReq, ok := req.(*LoadReplayRequest) 954 if !ok { 955 return nil, twirp.InternalError("failed type assertion req.(*LoadReplayRequest) when calling interceptor") 956 } 957 return s.Replay.LoadReplay(ctx, typedReq) 958 }, 959 )(ctx, req) 960 if resp != nil { 961 typedResp, ok := resp.(*LoadReplayResponse) 962 if !ok { 963 return nil, twirp.InternalError("failed type assertion resp.(*LoadReplayResponse) when calling interceptor") 964 } 965 return typedResp, err 966 } 967 return nil, err 968 } 969 } 970 971 // Call service method 972 var respContent *LoadReplayResponse 973 func() { 974 defer ensurePanicResponses(ctx, resp, s.hooks) 975 respContent, err = handler(ctx, reqContent) 976 }() 977 978 if err != nil { 979 s.writeError(ctx, resp, err) 980 return 981 } 982 if respContent == nil { 983 s.writeError(ctx, resp, twirp.InternalError("received a nil *LoadReplayResponse and nil error while calling LoadReplay. nil responses are not supported")) 984 return 985 } 986 987 ctx = callResponsePrepared(ctx, s.hooks) 988 989 marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 990 respBytes, err := marshaler.Marshal(respContent) 991 if err != nil { 992 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 993 return 994 } 995 996 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 997 resp.Header().Set("Content-Type", "application/json") 998 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 999 resp.WriteHeader(http.StatusOK) 1000 1001 if n, err := resp.Write(respBytes); err != nil { 1002 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1003 twerr := twirp.NewError(twirp.Unknown, msg) 1004 ctx = callError(ctx, s.hooks, twerr) 1005 } 1006 callResponseSent(ctx, s.hooks) 1007 } 1008 1009 func (s *replayServer) serveLoadReplayProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1010 var err error 1011 ctx = ctxsetters.WithMethodName(ctx, "LoadReplay") 1012 ctx, err = callRequestRouted(ctx, s.hooks) 1013 if err != nil { 1014 s.writeError(ctx, resp, err) 1015 return 1016 } 1017 1018 buf, err := io.ReadAll(req.Body) 1019 if err != nil { 1020 s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 1021 return 1022 } 1023 reqContent := new(LoadReplayRequest) 1024 if err = proto.Unmarshal(buf, reqContent); err != nil { 1025 s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 1026 return 1027 } 1028 1029 handler := s.Replay.LoadReplay 1030 if s.interceptor != nil { 1031 handler = func(ctx context.Context, req *LoadReplayRequest) (*LoadReplayResponse, error) { 1032 resp, err := s.interceptor( 1033 func(ctx context.Context, req interface{}) (interface{}, error) { 1034 typedReq, ok := req.(*LoadReplayRequest) 1035 if !ok { 1036 return nil, twirp.InternalError("failed type assertion req.(*LoadReplayRequest) when calling interceptor") 1037 } 1038 return s.Replay.LoadReplay(ctx, typedReq) 1039 }, 1040 )(ctx, req) 1041 if resp != nil { 1042 typedResp, ok := resp.(*LoadReplayResponse) 1043 if !ok { 1044 return nil, twirp.InternalError("failed type assertion resp.(*LoadReplayResponse) when calling interceptor") 1045 } 1046 return typedResp, err 1047 } 1048 return nil, err 1049 } 1050 } 1051 1052 // Call service method 1053 var respContent *LoadReplayResponse 1054 func() { 1055 defer ensurePanicResponses(ctx, resp, s.hooks) 1056 respContent, err = handler(ctx, reqContent) 1057 }() 1058 1059 if err != nil { 1060 s.writeError(ctx, resp, err) 1061 return 1062 } 1063 if respContent == nil { 1064 s.writeError(ctx, resp, twirp.InternalError("received a nil *LoadReplayResponse and nil error while calling LoadReplay. nil responses are not supported")) 1065 return 1066 } 1067 1068 ctx = callResponsePrepared(ctx, s.hooks) 1069 1070 respBytes, err := proto.Marshal(respContent) 1071 if err != nil { 1072 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 1073 return 1074 } 1075 1076 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1077 resp.Header().Set("Content-Type", "application/protobuf") 1078 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1079 resp.WriteHeader(http.StatusOK) 1080 if n, err := resp.Write(respBytes); err != nil { 1081 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1082 twerr := twirp.NewError(twirp.Unknown, msg) 1083 ctx = callError(ctx, s.hooks, twerr) 1084 } 1085 callResponseSent(ctx, s.hooks) 1086 } 1087 1088 func (s *replayServer) serveSeekForRoom(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1089 header := req.Header.Get("Content-Type") 1090 i := strings.Index(header, ";") 1091 if i == -1 { 1092 i = len(header) 1093 } 1094 switch strings.TrimSpace(strings.ToLower(header[:i])) { 1095 case "application/json": 1096 s.serveSeekForRoomJSON(ctx, resp, req) 1097 case "application/protobuf": 1098 s.serveSeekForRoomProtobuf(ctx, resp, req) 1099 default: 1100 msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 1101 twerr := badRouteError(msg, req.Method, req.URL.Path) 1102 s.writeError(ctx, resp, twerr) 1103 } 1104 } 1105 1106 func (s *replayServer) serveSeekForRoomJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1107 var err error 1108 ctx = ctxsetters.WithMethodName(ctx, "SeekForRoom") 1109 ctx, err = callRequestRouted(ctx, s.hooks) 1110 if err != nil { 1111 s.writeError(ctx, resp, err) 1112 return 1113 } 1114 1115 d := json.NewDecoder(req.Body) 1116 rawReqBody := json.RawMessage{} 1117 if err := d.Decode(&rawReqBody); err != nil { 1118 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1119 return 1120 } 1121 reqContent := new(RoomSeekRequest) 1122 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 1123 if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 1124 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1125 return 1126 } 1127 1128 handler := s.Replay.SeekForRoom 1129 if s.interceptor != nil { 1130 handler = func(ctx context.Context, req *RoomSeekRequest) (*google_protobuf.Empty, error) { 1131 resp, err := s.interceptor( 1132 func(ctx context.Context, req interface{}) (interface{}, error) { 1133 typedReq, ok := req.(*RoomSeekRequest) 1134 if !ok { 1135 return nil, twirp.InternalError("failed type assertion req.(*RoomSeekRequest) when calling interceptor") 1136 } 1137 return s.Replay.SeekForRoom(ctx, typedReq) 1138 }, 1139 )(ctx, req) 1140 if resp != nil { 1141 typedResp, ok := resp.(*google_protobuf.Empty) 1142 if !ok { 1143 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1144 } 1145 return typedResp, err 1146 } 1147 return nil, err 1148 } 1149 } 1150 1151 // Call service method 1152 var respContent *google_protobuf.Empty 1153 func() { 1154 defer ensurePanicResponses(ctx, resp, s.hooks) 1155 respContent, err = handler(ctx, reqContent) 1156 }() 1157 1158 if err != nil { 1159 s.writeError(ctx, resp, err) 1160 return 1161 } 1162 if respContent == nil { 1163 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling SeekForRoom. nil responses are not supported")) 1164 return 1165 } 1166 1167 ctx = callResponsePrepared(ctx, s.hooks) 1168 1169 marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 1170 respBytes, err := marshaler.Marshal(respContent) 1171 if err != nil { 1172 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 1173 return 1174 } 1175 1176 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1177 resp.Header().Set("Content-Type", "application/json") 1178 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1179 resp.WriteHeader(http.StatusOK) 1180 1181 if n, err := resp.Write(respBytes); err != nil { 1182 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1183 twerr := twirp.NewError(twirp.Unknown, msg) 1184 ctx = callError(ctx, s.hooks, twerr) 1185 } 1186 callResponseSent(ctx, s.hooks) 1187 } 1188 1189 func (s *replayServer) serveSeekForRoomProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1190 var err error 1191 ctx = ctxsetters.WithMethodName(ctx, "SeekForRoom") 1192 ctx, err = callRequestRouted(ctx, s.hooks) 1193 if err != nil { 1194 s.writeError(ctx, resp, err) 1195 return 1196 } 1197 1198 buf, err := io.ReadAll(req.Body) 1199 if err != nil { 1200 s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 1201 return 1202 } 1203 reqContent := new(RoomSeekRequest) 1204 if err = proto.Unmarshal(buf, reqContent); err != nil { 1205 s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 1206 return 1207 } 1208 1209 handler := s.Replay.SeekForRoom 1210 if s.interceptor != nil { 1211 handler = func(ctx context.Context, req *RoomSeekRequest) (*google_protobuf.Empty, error) { 1212 resp, err := s.interceptor( 1213 func(ctx context.Context, req interface{}) (interface{}, error) { 1214 typedReq, ok := req.(*RoomSeekRequest) 1215 if !ok { 1216 return nil, twirp.InternalError("failed type assertion req.(*RoomSeekRequest) when calling interceptor") 1217 } 1218 return s.Replay.SeekForRoom(ctx, typedReq) 1219 }, 1220 )(ctx, req) 1221 if resp != nil { 1222 typedResp, ok := resp.(*google_protobuf.Empty) 1223 if !ok { 1224 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1225 } 1226 return typedResp, err 1227 } 1228 return nil, err 1229 } 1230 } 1231 1232 // Call service method 1233 var respContent *google_protobuf.Empty 1234 func() { 1235 defer ensurePanicResponses(ctx, resp, s.hooks) 1236 respContent, err = handler(ctx, reqContent) 1237 }() 1238 1239 if err != nil { 1240 s.writeError(ctx, resp, err) 1241 return 1242 } 1243 if respContent == nil { 1244 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling SeekForRoom. nil responses are not supported")) 1245 return 1246 } 1247 1248 ctx = callResponsePrepared(ctx, s.hooks) 1249 1250 respBytes, err := proto.Marshal(respContent) 1251 if err != nil { 1252 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 1253 return 1254 } 1255 1256 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1257 resp.Header().Set("Content-Type", "application/protobuf") 1258 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1259 resp.WriteHeader(http.StatusOK) 1260 if n, err := resp.Write(respBytes); err != nil { 1261 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1262 twerr := twirp.NewError(twirp.Unknown, msg) 1263 ctx = callError(ctx, s.hooks, twerr) 1264 } 1265 callResponseSent(ctx, s.hooks) 1266 } 1267 1268 func (s *replayServer) serveCloseReplay(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1269 header := req.Header.Get("Content-Type") 1270 i := strings.Index(header, ";") 1271 if i == -1 { 1272 i = len(header) 1273 } 1274 switch strings.TrimSpace(strings.ToLower(header[:i])) { 1275 case "application/json": 1276 s.serveCloseReplayJSON(ctx, resp, req) 1277 case "application/protobuf": 1278 s.serveCloseReplayProtobuf(ctx, resp, req) 1279 default: 1280 msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 1281 twerr := badRouteError(msg, req.Method, req.URL.Path) 1282 s.writeError(ctx, resp, twerr) 1283 } 1284 } 1285 1286 func (s *replayServer) serveCloseReplayJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1287 var err error 1288 ctx = ctxsetters.WithMethodName(ctx, "CloseReplay") 1289 ctx, err = callRequestRouted(ctx, s.hooks) 1290 if err != nil { 1291 s.writeError(ctx, resp, err) 1292 return 1293 } 1294 1295 d := json.NewDecoder(req.Body) 1296 rawReqBody := json.RawMessage{} 1297 if err := d.Decode(&rawReqBody); err != nil { 1298 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1299 return 1300 } 1301 reqContent := new(CloseReplayRequest) 1302 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 1303 if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 1304 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1305 return 1306 } 1307 1308 handler := s.Replay.CloseReplay 1309 if s.interceptor != nil { 1310 handler = func(ctx context.Context, req *CloseReplayRequest) (*google_protobuf.Empty, error) { 1311 resp, err := s.interceptor( 1312 func(ctx context.Context, req interface{}) (interface{}, error) { 1313 typedReq, ok := req.(*CloseReplayRequest) 1314 if !ok { 1315 return nil, twirp.InternalError("failed type assertion req.(*CloseReplayRequest) when calling interceptor") 1316 } 1317 return s.Replay.CloseReplay(ctx, typedReq) 1318 }, 1319 )(ctx, req) 1320 if resp != nil { 1321 typedResp, ok := resp.(*google_protobuf.Empty) 1322 if !ok { 1323 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1324 } 1325 return typedResp, err 1326 } 1327 return nil, err 1328 } 1329 } 1330 1331 // Call service method 1332 var respContent *google_protobuf.Empty 1333 func() { 1334 defer ensurePanicResponses(ctx, resp, s.hooks) 1335 respContent, err = handler(ctx, reqContent) 1336 }() 1337 1338 if err != nil { 1339 s.writeError(ctx, resp, err) 1340 return 1341 } 1342 if respContent == nil { 1343 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling CloseReplay. nil responses are not supported")) 1344 return 1345 } 1346 1347 ctx = callResponsePrepared(ctx, s.hooks) 1348 1349 marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 1350 respBytes, err := marshaler.Marshal(respContent) 1351 if err != nil { 1352 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 1353 return 1354 } 1355 1356 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1357 resp.Header().Set("Content-Type", "application/json") 1358 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1359 resp.WriteHeader(http.StatusOK) 1360 1361 if n, err := resp.Write(respBytes); err != nil { 1362 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1363 twerr := twirp.NewError(twirp.Unknown, msg) 1364 ctx = callError(ctx, s.hooks, twerr) 1365 } 1366 callResponseSent(ctx, s.hooks) 1367 } 1368 1369 func (s *replayServer) serveCloseReplayProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1370 var err error 1371 ctx = ctxsetters.WithMethodName(ctx, "CloseReplay") 1372 ctx, err = callRequestRouted(ctx, s.hooks) 1373 if err != nil { 1374 s.writeError(ctx, resp, err) 1375 return 1376 } 1377 1378 buf, err := io.ReadAll(req.Body) 1379 if err != nil { 1380 s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 1381 return 1382 } 1383 reqContent := new(CloseReplayRequest) 1384 if err = proto.Unmarshal(buf, reqContent); err != nil { 1385 s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 1386 return 1387 } 1388 1389 handler := s.Replay.CloseReplay 1390 if s.interceptor != nil { 1391 handler = func(ctx context.Context, req *CloseReplayRequest) (*google_protobuf.Empty, error) { 1392 resp, err := s.interceptor( 1393 func(ctx context.Context, req interface{}) (interface{}, error) { 1394 typedReq, ok := req.(*CloseReplayRequest) 1395 if !ok { 1396 return nil, twirp.InternalError("failed type assertion req.(*CloseReplayRequest) when calling interceptor") 1397 } 1398 return s.Replay.CloseReplay(ctx, typedReq) 1399 }, 1400 )(ctx, req) 1401 if resp != nil { 1402 typedResp, ok := resp.(*google_protobuf.Empty) 1403 if !ok { 1404 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1405 } 1406 return typedResp, err 1407 } 1408 return nil, err 1409 } 1410 } 1411 1412 // Call service method 1413 var respContent *google_protobuf.Empty 1414 func() { 1415 defer ensurePanicResponses(ctx, resp, s.hooks) 1416 respContent, err = handler(ctx, reqContent) 1417 }() 1418 1419 if err != nil { 1420 s.writeError(ctx, resp, err) 1421 return 1422 } 1423 if respContent == nil { 1424 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling CloseReplay. nil responses are not supported")) 1425 return 1426 } 1427 1428 ctx = callResponsePrepared(ctx, s.hooks) 1429 1430 respBytes, err := proto.Marshal(respContent) 1431 if err != nil { 1432 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 1433 return 1434 } 1435 1436 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1437 resp.Header().Set("Content-Type", "application/protobuf") 1438 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1439 resp.WriteHeader(http.StatusOK) 1440 if n, err := resp.Write(respBytes); err != nil { 1441 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1442 twerr := twirp.NewError(twirp.Unknown, msg) 1443 ctx = callError(ctx, s.hooks, twerr) 1444 } 1445 callResponseSent(ctx, s.hooks) 1446 } 1447 1448 func (s *replayServer) serveDeleteReplay(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1449 header := req.Header.Get("Content-Type") 1450 i := strings.Index(header, ";") 1451 if i == -1 { 1452 i = len(header) 1453 } 1454 switch strings.TrimSpace(strings.ToLower(header[:i])) { 1455 case "application/json": 1456 s.serveDeleteReplayJSON(ctx, resp, req) 1457 case "application/protobuf": 1458 s.serveDeleteReplayProtobuf(ctx, resp, req) 1459 default: 1460 msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 1461 twerr := badRouteError(msg, req.Method, req.URL.Path) 1462 s.writeError(ctx, resp, twerr) 1463 } 1464 } 1465 1466 func (s *replayServer) serveDeleteReplayJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1467 var err error 1468 ctx = ctxsetters.WithMethodName(ctx, "DeleteReplay") 1469 ctx, err = callRequestRouted(ctx, s.hooks) 1470 if err != nil { 1471 s.writeError(ctx, resp, err) 1472 return 1473 } 1474 1475 d := json.NewDecoder(req.Body) 1476 rawReqBody := json.RawMessage{} 1477 if err := d.Decode(&rawReqBody); err != nil { 1478 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1479 return 1480 } 1481 reqContent := new(DeleteReplayRequest) 1482 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 1483 if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 1484 s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 1485 return 1486 } 1487 1488 handler := s.Replay.DeleteReplay 1489 if s.interceptor != nil { 1490 handler = func(ctx context.Context, req *DeleteReplayRequest) (*google_protobuf.Empty, error) { 1491 resp, err := s.interceptor( 1492 func(ctx context.Context, req interface{}) (interface{}, error) { 1493 typedReq, ok := req.(*DeleteReplayRequest) 1494 if !ok { 1495 return nil, twirp.InternalError("failed type assertion req.(*DeleteReplayRequest) when calling interceptor") 1496 } 1497 return s.Replay.DeleteReplay(ctx, typedReq) 1498 }, 1499 )(ctx, req) 1500 if resp != nil { 1501 typedResp, ok := resp.(*google_protobuf.Empty) 1502 if !ok { 1503 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1504 } 1505 return typedResp, err 1506 } 1507 return nil, err 1508 } 1509 } 1510 1511 // Call service method 1512 var respContent *google_protobuf.Empty 1513 func() { 1514 defer ensurePanicResponses(ctx, resp, s.hooks) 1515 respContent, err = handler(ctx, reqContent) 1516 }() 1517 1518 if err != nil { 1519 s.writeError(ctx, resp, err) 1520 return 1521 } 1522 if respContent == nil { 1523 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling DeleteReplay. nil responses are not supported")) 1524 return 1525 } 1526 1527 ctx = callResponsePrepared(ctx, s.hooks) 1528 1529 marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 1530 respBytes, err := marshaler.Marshal(respContent) 1531 if err != nil { 1532 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 1533 return 1534 } 1535 1536 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1537 resp.Header().Set("Content-Type", "application/json") 1538 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1539 resp.WriteHeader(http.StatusOK) 1540 1541 if n, err := resp.Write(respBytes); err != nil { 1542 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1543 twerr := twirp.NewError(twirp.Unknown, msg) 1544 ctx = callError(ctx, s.hooks, twerr) 1545 } 1546 callResponseSent(ctx, s.hooks) 1547 } 1548 1549 func (s *replayServer) serveDeleteReplayProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 1550 var err error 1551 ctx = ctxsetters.WithMethodName(ctx, "DeleteReplay") 1552 ctx, err = callRequestRouted(ctx, s.hooks) 1553 if err != nil { 1554 s.writeError(ctx, resp, err) 1555 return 1556 } 1557 1558 buf, err := io.ReadAll(req.Body) 1559 if err != nil { 1560 s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 1561 return 1562 } 1563 reqContent := new(DeleteReplayRequest) 1564 if err = proto.Unmarshal(buf, reqContent); err != nil { 1565 s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 1566 return 1567 } 1568 1569 handler := s.Replay.DeleteReplay 1570 if s.interceptor != nil { 1571 handler = func(ctx context.Context, req *DeleteReplayRequest) (*google_protobuf.Empty, error) { 1572 resp, err := s.interceptor( 1573 func(ctx context.Context, req interface{}) (interface{}, error) { 1574 typedReq, ok := req.(*DeleteReplayRequest) 1575 if !ok { 1576 return nil, twirp.InternalError("failed type assertion req.(*DeleteReplayRequest) when calling interceptor") 1577 } 1578 return s.Replay.DeleteReplay(ctx, typedReq) 1579 }, 1580 )(ctx, req) 1581 if resp != nil { 1582 typedResp, ok := resp.(*google_protobuf.Empty) 1583 if !ok { 1584 return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf.Empty) when calling interceptor") 1585 } 1586 return typedResp, err 1587 } 1588 return nil, err 1589 } 1590 } 1591 1592 // Call service method 1593 var respContent *google_protobuf.Empty 1594 func() { 1595 defer ensurePanicResponses(ctx, resp, s.hooks) 1596 respContent, err = handler(ctx, reqContent) 1597 }() 1598 1599 if err != nil { 1600 s.writeError(ctx, resp, err) 1601 return 1602 } 1603 if respContent == nil { 1604 s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf.Empty and nil error while calling DeleteReplay. nil responses are not supported")) 1605 return 1606 } 1607 1608 ctx = callResponsePrepared(ctx, s.hooks) 1609 1610 respBytes, err := proto.Marshal(respContent) 1611 if err != nil { 1612 s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 1613 return 1614 } 1615 1616 ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1617 resp.Header().Set("Content-Type", "application/protobuf") 1618 resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1619 resp.WriteHeader(http.StatusOK) 1620 if n, err := resp.Write(respBytes); err != nil { 1621 msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1622 twerr := twirp.NewError(twirp.Unknown, msg) 1623 ctx = callError(ctx, s.hooks, twerr) 1624 } 1625 callResponseSent(ctx, s.hooks) 1626 } 1627 1628 func (s *replayServer) ServiceDescriptor() ([]byte, int) { 1629 return twirpFileDescriptor0, 0 1630 } 1631 1632 func (s *replayServer) ProtocGenTwirpVersion() string { 1633 return "v8.1.3" 1634 } 1635 1636 // PathPrefix returns the base service path, in the form: "/<prefix>/<package>.<Service>/" 1637 // that is everything in a Twirp route except for the <Method>. This can be used for routing, 1638 // for example to identify the requests that are targeted to this service in a mux. 1639 func (s *replayServer) PathPrefix() string { 1640 return baseServicePath(s.pathPrefix, "replay", "Replay") 1641 } 1642 1643 // ===== 1644 // Utils 1645 // ===== 1646 1647 // HTTPClient is the interface used by generated clients to send HTTP requests. 1648 // It is fulfilled by *(net/http).Client, which is sufficient for most users. 1649 // Users can provide their own implementation for special retry policies. 1650 // 1651 // HTTPClient implementations should not follow redirects. Redirects are 1652 // automatically disabled if *(net/http).Client is passed to client 1653 // constructors. See the withoutRedirects function in this file for more 1654 // details. 1655 type HTTPClient interface { 1656 Do(req *http.Request) (*http.Response, error) 1657 } 1658 1659 // TwirpServer is the interface generated server structs will support: they're 1660 // HTTP handlers with additional methods for accessing metadata about the 1661 // service. Those accessors are a low-level API for building reflection tools. 1662 // Most people can think of TwirpServers as just http.Handlers. 1663 type TwirpServer interface { 1664 http.Handler 1665 1666 // ServiceDescriptor returns gzipped bytes describing the .proto file that 1667 // this service was generated from. Once unzipped, the bytes can be 1668 // unmarshalled as a 1669 // google.golang.org/protobuf/types/descriptorpb.FileDescriptorProto. 1670 // 1671 // The returned integer is the index of this particular service within that 1672 // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a 1673 // low-level field, expected to be used for reflection. 1674 ServiceDescriptor() ([]byte, int) 1675 1676 // ProtocGenTwirpVersion is the semantic version string of the version of 1677 // twirp used to generate this file. 1678 ProtocGenTwirpVersion() string 1679 1680 // PathPrefix returns the HTTP URL path prefix for all methods handled by this 1681 // service. This can be used with an HTTP mux to route Twirp requests. 1682 // The path prefix is in the form: "/<prefix>/<package>.<Service>/" 1683 // that is, everything in a Twirp route except for the <Method> at the end. 1684 PathPrefix() string 1685 } 1686 1687 func newServerOpts(opts []interface{}) *twirp.ServerOptions { 1688 serverOpts := &twirp.ServerOptions{} 1689 for _, opt := range opts { 1690 switch o := opt.(type) { 1691 case twirp.ServerOption: 1692 o(serverOpts) 1693 case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument 1694 twirp.WithServerHooks(o)(serverOpts) 1695 case nil: // backwards compatibility, allow nil value for the argument 1696 continue 1697 default: 1698 panic(fmt.Sprintf("Invalid option type %T, please use a twirp.ServerOption", o)) 1699 } 1700 } 1701 return serverOpts 1702 } 1703 1704 // WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). 1705 // Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks. 1706 // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) 1707 func WriteError(resp http.ResponseWriter, err error) { 1708 writeError(context.Background(), resp, err, nil) 1709 } 1710 1711 // writeError writes Twirp errors in the response and triggers hooks. 1712 func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { 1713 // Convert to a twirp.Error. Non-twirp errors are converted to internal errors. 1714 var twerr twirp.Error 1715 if !errors.As(err, &twerr) { 1716 twerr = twirp.InternalErrorWith(err) 1717 } 1718 1719 statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) 1720 ctx = ctxsetters.WithStatusCode(ctx, statusCode) 1721 ctx = callError(ctx, hooks, twerr) 1722 1723 respBody := marshalErrorToJSON(twerr) 1724 1725 resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON 1726 resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) 1727 resp.WriteHeader(statusCode) // set HTTP status code and send response 1728 1729 _, writeErr := resp.Write(respBody) 1730 if writeErr != nil { 1731 // We have three options here. We could log the error, call the Error 1732 // hook, or just silently ignore the error. 1733 // 1734 // Logging is unacceptable because we don't have a user-controlled 1735 // logger; writing out to stderr without permission is too rude. 1736 // 1737 // Calling the Error hook would confuse users: it would mean the Error 1738 // hook got called twice for one request, which is likely to lead to 1739 // duplicated log messages and metrics, no matter how well we document 1740 // the behavior. 1741 // 1742 // Silently ignoring the error is our least-bad option. It's highly 1743 // likely that the connection is broken and the original 'err' says 1744 // so anyway. 1745 _ = writeErr 1746 } 1747 1748 callResponseSent(ctx, hooks) 1749 } 1750 1751 // sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed. 1752 // If the URL is unparsable, the baseURL is returned unchanged. 1753 func sanitizeBaseURL(baseURL string) string { 1754 u, err := url.Parse(baseURL) 1755 if err != nil { 1756 return baseURL // invalid URL will fail later when making requests 1757 } 1758 if u.Scheme == "" { 1759 u.Scheme = "http" 1760 } 1761 return u.String() 1762 } 1763 1764 // baseServicePath composes the path prefix for the service (without <Method>). 1765 // e.g.: baseServicePath("/twirp", "my.pkg", "MyService") 1766 // 1767 // returns => "/twirp/my.pkg.MyService/" 1768 // 1769 // e.g.: baseServicePath("", "", "MyService") 1770 // 1771 // returns => "/MyService/" 1772 func baseServicePath(prefix, pkg, service string) string { 1773 fullServiceName := service 1774 if pkg != "" { 1775 fullServiceName = pkg + "." + service 1776 } 1777 return path.Join("/", prefix, fullServiceName) + "/" 1778 } 1779 1780 // parseTwirpPath extracts path components form a valid Twirp route. 1781 // Expected format: "[<prefix>]/<package>.<Service>/<Method>" 1782 // e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat") 1783 func parseTwirpPath(path string) (string, string, string) { 1784 parts := strings.Split(path, "/") 1785 if len(parts) < 2 { 1786 return "", "", "" 1787 } 1788 method := parts[len(parts)-1] 1789 pkgService := parts[len(parts)-2] 1790 prefix := strings.Join(parts[0:len(parts)-2], "/") 1791 return prefix, pkgService, method 1792 } 1793 1794 // getCustomHTTPReqHeaders retrieves a copy of any headers that are set in 1795 // a context through the twirp.WithHTTPRequestHeaders function. 1796 // If there are no headers set, or if they have the wrong type, nil is returned. 1797 func getCustomHTTPReqHeaders(ctx context.Context) http.Header { 1798 header, ok := twirp.HTTPRequestHeaders(ctx) 1799 if !ok || header == nil { 1800 return nil 1801 } 1802 copied := make(http.Header) 1803 for k, vv := range header { 1804 if vv == nil { 1805 copied[k] = nil 1806 continue 1807 } 1808 copied[k] = make([]string, len(vv)) 1809 copy(copied[k], vv) 1810 } 1811 return copied 1812 } 1813 1814 // newRequest makes an http.Request from a client, adding common headers. 1815 func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { 1816 req, err := http.NewRequest("POST", url, reqBody) 1817 if err != nil { 1818 return nil, err 1819 } 1820 req = req.WithContext(ctx) 1821 if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { 1822 req.Header = customHeader 1823 } 1824 req.Header.Set("Accept", contentType) 1825 req.Header.Set("Content-Type", contentType) 1826 req.Header.Set("Twirp-Version", "v8.1.3") 1827 return req, nil 1828 } 1829 1830 // JSON serialization for errors 1831 type twerrJSON struct { 1832 Code string `json:"code"` 1833 Msg string `json:"msg"` 1834 Meta map[string]string `json:"meta,omitempty"` 1835 } 1836 1837 // marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. 1838 // If serialization fails, it will use a descriptive Internal error instead. 1839 func marshalErrorToJSON(twerr twirp.Error) []byte { 1840 // make sure that msg is not too large 1841 msg := twerr.Msg() 1842 if len(msg) > 1e6 { 1843 msg = msg[:1e6] 1844 } 1845 1846 tj := twerrJSON{ 1847 Code: string(twerr.Code()), 1848 Msg: msg, 1849 Meta: twerr.MetaMap(), 1850 } 1851 1852 buf, err := json.Marshal(&tj) 1853 if err != nil { 1854 buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback 1855 } 1856 1857 return buf 1858 } 1859 1860 // errorFromResponse builds a twirp.Error from a non-200 HTTP response. 1861 // If the response has a valid serialized Twirp error, then it's returned. 1862 // If not, the response status code is used to generate a similar twirp 1863 // error. See twirpErrorFromIntermediary for more info on intermediary errors. 1864 func errorFromResponse(resp *http.Response) twirp.Error { 1865 statusCode := resp.StatusCode 1866 statusText := http.StatusText(statusCode) 1867 1868 if isHTTPRedirect(statusCode) { 1869 // Unexpected redirect: it must be an error from an intermediary. 1870 // Twirp clients don't follow redirects automatically, Twirp only handles 1871 // POST requests, redirects should only happen on GET and HEAD requests. 1872 location := resp.Header.Get("Location") 1873 msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) 1874 return twirpErrorFromIntermediary(statusCode, msg, location) 1875 } 1876 1877 respBodyBytes, err := io.ReadAll(resp.Body) 1878 if err != nil { 1879 return wrapInternal(err, "failed to read server error response body") 1880 } 1881 1882 var tj twerrJSON 1883 dec := json.NewDecoder(bytes.NewReader(respBodyBytes)) 1884 dec.DisallowUnknownFields() 1885 if err := dec.Decode(&tj); err != nil || tj.Code == "" { 1886 // Invalid JSON response; it must be an error from an intermediary. 1887 msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) 1888 return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) 1889 } 1890 1891 errorCode := twirp.ErrorCode(tj.Code) 1892 if !twirp.IsValidErrorCode(errorCode) { 1893 msg := "invalid type returned from server error response: " + tj.Code 1894 return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes)) 1895 } 1896 1897 twerr := twirp.NewError(errorCode, tj.Msg) 1898 for k, v := range tj.Meta { 1899 twerr = twerr.WithMeta(k, v) 1900 } 1901 return twerr 1902 } 1903 1904 // twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. 1905 // The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. 1906 // Returned twirp Errors have some additional metadata for inspection. 1907 func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { 1908 var code twirp.ErrorCode 1909 if isHTTPRedirect(status) { // 3xx 1910 code = twirp.Internal 1911 } else { 1912 switch status { 1913 case 400: // Bad Request 1914 code = twirp.Internal 1915 case 401: // Unauthorized 1916 code = twirp.Unauthenticated 1917 case 403: // Forbidden 1918 code = twirp.PermissionDenied 1919 case 404: // Not Found 1920 code = twirp.BadRoute 1921 case 429: // Too Many Requests 1922 code = twirp.ResourceExhausted 1923 case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout 1924 code = twirp.Unavailable 1925 default: // All other codes 1926 code = twirp.Unknown 1927 } 1928 } 1929 1930 twerr := twirp.NewError(code, msg) 1931 twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary 1932 twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) 1933 if isHTTPRedirect(status) { 1934 twerr = twerr.WithMeta("location", bodyOrLocation) 1935 } else { 1936 twerr = twerr.WithMeta("body", bodyOrLocation) 1937 } 1938 return twerr 1939 } 1940 1941 func isHTTPRedirect(status int) bool { 1942 return status >= 300 && status <= 399 1943 } 1944 1945 // wrapInternal wraps an error with a prefix as an Internal error. 1946 // The original error cause is accessible by github.com/pkg/errors.Cause. 1947 func wrapInternal(err error, prefix string) twirp.Error { 1948 return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err}) 1949 } 1950 1951 type wrappedError struct { 1952 prefix string 1953 cause error 1954 } 1955 1956 func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() } 1957 func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As 1958 func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors 1959 1960 // ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal 1961 // error response (status 500), and error hooks are properly called with the panic wrapped as an error. 1962 // The panic is re-raised so it can be handled normally with middleware. 1963 func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) { 1964 if r := recover(); r != nil { 1965 // Wrap the panic as an error so it can be passed to error hooks. 1966 // The original error is accessible from error hooks, but not visible in the response. 1967 err := errFromPanic(r) 1968 twerr := &internalWithCause{msg: "Internal service panic", cause: err} 1969 // Actually write the error 1970 writeError(ctx, resp, twerr, hooks) 1971 // If possible, flush the error to the wire. 1972 f, ok := resp.(http.Flusher) 1973 if ok { 1974 f.Flush() 1975 } 1976 1977 panic(r) 1978 } 1979 } 1980 1981 // errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error. 1982 func errFromPanic(p interface{}) error { 1983 if err, ok := p.(error); ok { 1984 return err 1985 } 1986 return fmt.Errorf("panic: %v", p) 1987 } 1988 1989 // internalWithCause is a Twirp Internal error wrapping an original error cause, 1990 // but the original error message is not exposed on Msg(). The original error 1991 // can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap 1992 type internalWithCause struct { 1993 msg string 1994 cause error 1995 } 1996 1997 func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As 1998 func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors 1999 func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() } 2000 func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal } 2001 func (e *internalWithCause) Msg() string { return e.msg } 2002 func (e *internalWithCause) Meta(key string) string { return "" } 2003 func (e *internalWithCause) MetaMap() map[string]string { return nil } 2004 func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e } 2005 2006 // malformedRequestError is used when the twirp server cannot unmarshal a request 2007 func malformedRequestError(msg string) twirp.Error { 2008 return twirp.NewError(twirp.Malformed, msg) 2009 } 2010 2011 // badRouteError is used when the twirp server cannot route a request 2012 func badRouteError(msg string, method, url string) twirp.Error { 2013 err := twirp.NewError(twirp.BadRoute, msg) 2014 err = err.WithMeta("twirp_invalid_route", method+" "+url) 2015 return err 2016 } 2017 2018 // withoutRedirects makes sure that the POST request can not be redirected. 2019 // The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or 2020 // 303 response, and also 301s in go1.8. It redirects by making a second request, changing the 2021 // method to GET and removing the body. This produces very confusing error messages, so instead we 2022 // set a redirect policy that always errors. This stops Go from executing the redirect. 2023 // 2024 // We have to be a little careful in case the user-provided http.Client has its own CheckRedirect 2025 // policy - if so, we'll run through that policy first. 2026 // 2027 // Because this requires modifying the http.Client, we make a new copy of the client and return it. 2028 func withoutRedirects(in *http.Client) *http.Client { 2029 copy := *in 2030 copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { 2031 if in.CheckRedirect != nil { 2032 // Run the input's redirect if it exists, in case it has side effects, but ignore any error it 2033 // returns, since we want to use ErrUseLastResponse. 2034 err := in.CheckRedirect(req, via) 2035 _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. 2036 } 2037 return http.ErrUseLastResponse 2038 } 2039 return © 2040 } 2041 2042 // doProtobufRequest makes a Protobuf request to the remote Twirp service. 2043 func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { 2044 reqBodyBytes, err := proto.Marshal(in) 2045 if err != nil { 2046 return ctx, wrapInternal(err, "failed to marshal proto request") 2047 } 2048 reqBody := bytes.NewBuffer(reqBodyBytes) 2049 if err = ctx.Err(); err != nil { 2050 return ctx, wrapInternal(err, "aborted because context was done") 2051 } 2052 2053 req, err := newRequest(ctx, url, reqBody, "application/protobuf") 2054 if err != nil { 2055 return ctx, wrapInternal(err, "could not build request") 2056 } 2057 ctx, err = callClientRequestPrepared(ctx, hooks, req) 2058 if err != nil { 2059 return ctx, err 2060 } 2061 2062 req = req.WithContext(ctx) 2063 resp, err := client.Do(req) 2064 if err != nil { 2065 return ctx, wrapInternal(err, "failed to do request") 2066 } 2067 defer func() { _ = resp.Body.Close() }() 2068 2069 if err = ctx.Err(); err != nil { 2070 return ctx, wrapInternal(err, "aborted because context was done") 2071 } 2072 2073 if resp.StatusCode != 200 { 2074 return ctx, errorFromResponse(resp) 2075 } 2076 2077 respBodyBytes, err := io.ReadAll(resp.Body) 2078 if err != nil { 2079 return ctx, wrapInternal(err, "failed to read response body") 2080 } 2081 if err = ctx.Err(); err != nil { 2082 return ctx, wrapInternal(err, "aborted because context was done") 2083 } 2084 2085 if err = proto.Unmarshal(respBodyBytes, out); err != nil { 2086 return ctx, wrapInternal(err, "failed to unmarshal proto response") 2087 } 2088 return ctx, nil 2089 } 2090 2091 // doJSONRequest makes a JSON request to the remote Twirp service. 2092 func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { 2093 marshaler := &protojson.MarshalOptions{UseProtoNames: true} 2094 reqBytes, err := marshaler.Marshal(in) 2095 if err != nil { 2096 return ctx, wrapInternal(err, "failed to marshal json request") 2097 } 2098 if err = ctx.Err(); err != nil { 2099 return ctx, wrapInternal(err, "aborted because context was done") 2100 } 2101 2102 req, err := newRequest(ctx, url, bytes.NewReader(reqBytes), "application/json") 2103 if err != nil { 2104 return ctx, wrapInternal(err, "could not build request") 2105 } 2106 ctx, err = callClientRequestPrepared(ctx, hooks, req) 2107 if err != nil { 2108 return ctx, err 2109 } 2110 2111 req = req.WithContext(ctx) 2112 resp, err := client.Do(req) 2113 if err != nil { 2114 return ctx, wrapInternal(err, "failed to do request") 2115 } 2116 2117 defer func() { 2118 cerr := resp.Body.Close() 2119 if err == nil && cerr != nil { 2120 err = wrapInternal(cerr, "failed to close response body") 2121 } 2122 }() 2123 2124 if err = ctx.Err(); err != nil { 2125 return ctx, wrapInternal(err, "aborted because context was done") 2126 } 2127 2128 if resp.StatusCode != 200 { 2129 return ctx, errorFromResponse(resp) 2130 } 2131 2132 d := json.NewDecoder(resp.Body) 2133 rawRespBody := json.RawMessage{} 2134 if err := d.Decode(&rawRespBody); err != nil { 2135 return ctx, wrapInternal(err, "failed to unmarshal json response") 2136 } 2137 unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 2138 if err = unmarshaler.Unmarshal(rawRespBody, out); err != nil { 2139 return ctx, wrapInternal(err, "failed to unmarshal json response") 2140 } 2141 if err = ctx.Err(); err != nil { 2142 return ctx, wrapInternal(err, "aborted because context was done") 2143 } 2144 return ctx, nil 2145 } 2146 2147 // Call twirp.ServerHooks.RequestReceived if the hook is available 2148 func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { 2149 if h == nil || h.RequestReceived == nil { 2150 return ctx, nil 2151 } 2152 return h.RequestReceived(ctx) 2153 } 2154 2155 // Call twirp.ServerHooks.RequestRouted if the hook is available 2156 func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { 2157 if h == nil || h.RequestRouted == nil { 2158 return ctx, nil 2159 } 2160 return h.RequestRouted(ctx) 2161 } 2162 2163 // Call twirp.ServerHooks.ResponsePrepared if the hook is available 2164 func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { 2165 if h == nil || h.ResponsePrepared == nil { 2166 return ctx 2167 } 2168 return h.ResponsePrepared(ctx) 2169 } 2170 2171 // Call twirp.ServerHooks.ResponseSent if the hook is available 2172 func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { 2173 if h == nil || h.ResponseSent == nil { 2174 return 2175 } 2176 h.ResponseSent(ctx) 2177 } 2178 2179 // Call twirp.ServerHooks.Error if the hook is available 2180 func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { 2181 if h == nil || h.Error == nil { 2182 return ctx 2183 } 2184 return h.Error(ctx, err) 2185 } 2186 2187 func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) { 2188 if h == nil || h.ResponseReceived == nil { 2189 return 2190 } 2191 h.ResponseReceived(ctx) 2192 } 2193 2194 func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) { 2195 if h == nil || h.RequestPrepared == nil { 2196 return ctx, nil 2197 } 2198 return h.RequestPrepared(ctx, req) 2199 } 2200 2201 func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) { 2202 if h == nil || h.Error == nil { 2203 return 2204 } 2205 h.Error(ctx, err) 2206 } 2207 2208 var twirpFileDescriptor0 = []byte{ 2209 // 433 bytes of a gzipped FileDescriptorProto 2210 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcb, 0x6e, 0xd3, 0x40, 2211 0x14, 0x55, 0x1c, 0x29, 0xd0, 0xeb, 0xf2, 0x9a, 0x22, 0x08, 0xce, 0x82, 0xe0, 0x55, 0x90, 0x90, 2212 0x23, 0x05, 0xb1, 0xe9, 0x02, 0x09, 0xdc, 0x56, 0x8a, 0x88, 0x50, 0x65, 0x76, 0x6c, 0x2c, 0x3f, 2213 0x6e, 0xcd, 0x28, 0xb6, 0xaf, 0xf1, 0x8c, 0x2b, 0xf1, 0x4b, 0xfc, 0x13, 0x3f, 0xc0, 0x57, 0xa0, 2214 0xf1, 0x8c, 0x63, 0x87, 0xa6, 0x94, 0x9d, 0xe7, 0x9c, 0x73, 0x5f, 0xe7, 0x5e, 0x03, 0x4b, 0x72, 2215 0x6a, 0xd2, 0xb0, 0xc6, 0x2a, 0x8f, 0x7e, 0x78, 0x55, 0x4d, 0x92, 0xd8, 0x44, 0xbf, 0x9c, 0x59, 2216 0x46, 0x94, 0xe5, 0xb8, 0x6c, 0xd1, 0xb8, 0xb9, 0x5a, 0x62, 0x51, 0x49, 0x23, 0x72, 0x9f, 0x02, 2217 0xdb, 0x70, 0x21, 0x83, 0x56, 0x2a, 0x02, 0xfc, 0xde, 0xa0, 0x90, 0xae, 0x0f, 0x27, 0x7b, 0xa8, 2218 0xa8, 0xa8, 0x14, 0xc8, 0xde, 0xc0, 0x3d, 0x9d, 0x53, 0x4c, 0x47, 0xf3, 0xf1, 0xc2, 0x5e, 0x31, 2219 0xcf, 0x54, 0xd4, 0xca, 0x75, 0x79, 0x45, 0x41, 0x27, 0x71, 0x5f, 0x03, 0xf4, 0x30, 0x9b, 0xc1, 2220 0x91, 0x26, 0x42, 0x9e, 0x4e, 0x47, 0xf3, 0xd1, 0xe2, 0x28, 0xb8, 0xaf, 0x81, 0x75, 0xea, 0x56, 2221 0xf0, 0x64, 0x43, 0x51, 0xaa, 0xe5, 0xa6, 0x89, 0x7f, 0x46, 0xb4, 0x24, 0x51, 0x11, 0x96, 0x51, 2222 0x81, 0x53, 0xcb, 0x90, 0x44, 0xc5, 0xe7, 0xa8, 0x40, 0xf6, 0x0a, 0x8e, 0x85, 0x8c, 0x6a, 0xc9, 2223 0xcb, 0x2c, 0xac, 0xa4, 0x98, 0x8e, 0xe7, 0xa3, 0xc5, 0x38, 0xb0, 0x3b, 0xec, 0x52, 0x0a, 0xf7, 2224 0x1d, 0xb0, 0x61, 0x45, 0x33, 0xe0, 0x4b, 0xb0, 0xd5, 0x3b, 0x8e, 0x92, 0x6d, 0x5f, 0x14, 0x3a, 2225 0x68, 0x9d, 0xba, 0x67, 0xf0, 0x28, 0x20, 0x2a, 0xbe, 0x20, 0x6e, 0xbb, 0x36, 0xef, 0x8a, 0x61, 2226 0x8f, 0x61, 0xac, 0x9a, 0xb0, 0xda, 0x26, 0xd4, 0xa7, 0x2a, 0xee, 0xe7, 0x24, 0x70, 0x7f, 0xde, 2227 0x3b, 0x8b, 0xaf, 0xe0, 0xe4, 0x0c, 0x73, 0x94, 0xf8, 0xff, 0x3e, 0xad, 0x7e, 0x59, 0x30, 0xd1, 2228 0x72, 0x76, 0x01, 0xf6, 0x60, 0xa9, 0xcc, 0xe9, 0x76, 0x77, 0x73, 0xff, 0xce, 0xec, 0x20, 0x67, 2229 0x4c, 0xf2, 0x01, 0x7a, 0xeb, 0xd8, 0x8b, 0x9d, 0xf4, 0xef, 0x05, 0x3a, 0xce, 0x21, 0xca, 0x24, 2230 0x79, 0x0f, 0xb6, 0x32, 0xf1, 0x82, 0x6a, 0xe5, 0x27, 0x7b, 0xbe, 0x3b, 0xa4, 0x7d, 0x77, 0x9d, 2231 0x67, 0x9e, 0xbe, 0x5e, 0xaf, 0xbb, 0x5e, 0xef, 0x5c, 0x5d, 0x2f, 0xfb, 0x00, 0xf6, 0xc0, 0xc2, 2232 0x7e, 0x98, 0x9b, 0xbe, 0xde, 0x9a, 0xc2, 0x87, 0xe3, 0xa1, 0x9d, 0x6c, 0x37, 0xf4, 0x01, 0x93, 2233 0x6f, 0x4b, 0xf2, 0xf1, 0xfc, 0xab, 0x9b, 0x71, 0xf9, 0xad, 0x89, 0xbd, 0x84, 0x8a, 0x65, 0xce, 2234 0xaf, 0x71, 0xcb, 0xa5, 0xfe, 0xd5, 0x12, 0xca, 0x97, 0x3a, 0xe3, 0x4f, 0xeb, 0xc1, 0x86, 0x5f, 2235 0xe3, 0x27, 0x2e, 0xbd, 0x4b, 0xc5, 0xfc, 0xb6, 0x1e, 0x9a, 0xf7, 0xe9, 0x69, 0x0b, 0xc4, 0x93, 2236 0x36, 0xe2, 0xed, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x33, 0x43, 0xa1, 0xef, 0xc8, 0x03, 0x00, 2237 0x00, 2238 }