blocking
(listener net.Listener)
| 444 | |
| 445 | // blocking |
| 446 | func RunWebServer(listener net.Listener) { |
| 447 | gr := mux.NewRouter() |
| 448 | |
| 449 | // Streaming routes must be registered before the /wave/ prefix catch-all to bypass TimeoutHandler. |
| 450 | // http.TimeoutHandler buffers the entire response before flushing, which stalls streaming. |
| 451 | gr.HandleFunc("/wave/stream-local-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamLocalFile)) |
| 452 | gr.HandleFunc("/wave/stream-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile)) |
| 453 | gr.PathPrefix("/wave/stream-file/").HandlerFunc(WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile)) |
| 454 | gr.HandleFunc("/api/post-chat-message", WebFnWrap(WebFnOpts{AllowCaching: false}, aiusechat.WaveAIPostMessageHandler)) |
| 455 | |
| 456 | // Non-streaming /wave/ routes get timeout protection |
| 457 | waveRouter := mux.NewRouter() |
| 458 | waveRouter.HandleFunc("/wave/file", WebFnWrap(WebFnOpts{AllowCaching: false}, handleWaveFile)) |
| 459 | waveRouter.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService)) |
| 460 | waveRouter.HandleFunc("/wave/aichat", WebFnWrap(WebFnOpts{JsonErrors: true, AllowCaching: false}, aiusechat.WaveAIGetChatHandler)) |
| 461 | |
| 462 | vdomRouter := mux.NewRouter() |
| 463 | vdomRouter.HandleFunc("/vdom/{uuid}/{path:.*}", WebFnWrap(WebFnOpts{AllowCaching: true}, handleVDom)) |
| 464 | |
| 465 | gr.PathPrefix("/wave/").Handler(http.TimeoutHandler(waveRouter, HttpTimeoutDuration, "Timeout")) |
| 466 | gr.PathPrefix("/vdom/").Handler(http.TimeoutHandler(vdomRouter, HttpTimeoutDuration, "Timeout")) |
| 467 | |
| 468 | // Other routes without timeout |
| 469 | gr.PathPrefix(schemaPrefix).Handler(http.StripPrefix(schemaPrefix, schema.GetSchemaHandler())) |
| 470 | |
| 471 | handler := http.Handler(gr) |
| 472 | if wavebase.IsDevMode() { |
| 473 | originalHandler := handler |
| 474 | handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 475 | origin := r.Header.Get("Origin") |
| 476 | if origin != "" { |
| 477 | w.Header().Set("Access-Control-Allow-Origin", origin) |
| 478 | } |
| 479 | w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") |
| 480 | w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Session-Id, X-AuthKey, Authorization, X-Requested-With, Accept, x-vercel-ai-ui-message-stream") |
| 481 | w.Header().Set("Access-Control-Expose-Headers", "X-ZoneFileInfo, Content-Length, Content-Type, x-vercel-ai-ui-message-stream") |
| 482 | w.Header().Set("Access-Control-Allow-Credentials", "true") |
| 483 | |
| 484 | if r.Method == "OPTIONS" { |
| 485 | w.WriteHeader(204) |
| 486 | return |
| 487 | } |
| 488 | |
| 489 | originalHandler.ServeHTTP(w, r) |
| 490 | }) |
| 491 | } |
| 492 | server := &http.Server{ |
| 493 | ReadTimeout: HttpReadTimeout, |
| 494 | WriteTimeout: HttpWriteTimeout, |
| 495 | MaxHeaderBytes: HttpMaxHeaderBytes, |
| 496 | Handler: handler, |
| 497 | } |
| 498 | err := server.Serve(listener) |
| 499 | if err != nil { |
| 500 | log.Printf("ERROR: %v\n", err) |
| 501 | } |
| 502 | } |
no test coverage detected