WebWorker
WebWorker is a specialized actor for handling HTTP requests sent as meta.MessageWebRequest messages. It automatically routes requests to HTTP-method-specific callbacks and ensures the request completion signal is called.
Used with meta.WebHandler to convert HTTP requests into actor messages. See Web for integration approaches.
Purpose
When WebHandler sends meta.MessageWebRequest to an actor, that actor must:
Extract the message from mailbox
Determine HTTP method
Process the request
Write response to
http.ResponseWriterCall
Done()to unblock the waiting HTTP handler
WebWorker automates this. Embed it, implement method-specific callbacks, and the framework handles routing and cleanup.
Basic Usage
Embed act.WebWorker and implement callbacks for HTTP methods you handle:
type APIWorker struct {
act.WebWorker
}
func (w *APIWorker) HandleGet(from gen.PID, writer http.ResponseWriter, request *http.Request) error {
// Process GET request
user := w.lookupUser(request.URL.Query().Get("id"))
json.NewEncoder(writer).Encode(user)
return nil
}
func (w *APIWorker) HandlePost(from gen.PID, writer http.ResponseWriter, request *http.Request) error {
// Process POST request
var data CreateRequest
json.NewDecoder(request.Body).Decode(&data)
result := w.createResource(data)
writer.WriteHeader(http.StatusCreated)
json.NewEncoder(writer).Encode(result)
return nil
}
func (w *APIWorker) HandleDelete(from gen.PID, writer http.ResponseWriter, request *http.Request) error {
id := request.URL.Query().Get("id")
w.deleteResource(id)
writer.WriteHeader(http.StatusNoContent)
return nil
}Spawn worker with registered name:
type WebService struct {
act.Actor
}
func (s *WebService) Init(args ...any) error {
// Spawn worker
_, err := s.SpawnRegister("api-worker",
func() gen.ProcessBehavior { return &APIWorker{} },
gen.ProcessOptions{},
)
if err != nil {
return err
}
// Create WebHandler pointing to worker
handler := meta.CreateWebHandler(meta.WebHandlerOptions{
Worker: "api-worker",
})
_, err = s.SpawnMeta(handler, gen.MetaOptions{})
// rest of setup...
}When HTTP request arrives:
WebHandler sends
meta.MessageWebRequestto "api-worker"WebWorker detects message type, extracts HTTP method
WebWorker calls appropriate
Handle*methodYour callback processes request, writes response
WebWorker calls
Done()automaticallyHTTP handler unblocks, response sent to client
Available Callbacks
All callbacks are optional. Implement only the methods you need:
HTTP methods:
HandleGet(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandlePost(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandlePut(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandlePatch(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandleDelete(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandleHead(from gen.PID, writer http.ResponseWriter, request *http.Request) errorHandleOptions(from gen.PID, writer http.ResponseWriter, request *http.Request) error
Actor callbacks:
Init(args ...any) error- initializationHandleMessage(from gen.PID, message any) error- non-HTTP messagesHandleCall(from gen.PID, ref gen.Ref, request any) (any, error)- synchronous requestsHandleEvent(message gen.MessageEvent) error- event subscriptionsTerminate(reason error)- cleanupHandleInspect(from gen.PID, item ...string) map[string]string- introspection
Unimplemented HTTP methods return 501 Not Implemented automatically.
Error Handling
Return nil to continue processing requests. Return non-nil error to terminate the worker:
func (w *APIWorker) HandlePost(from gen.PID, writer http.ResponseWriter, request *http.Request) error {
var data CreateRequest
if err := json.NewDecoder(request.Body).Decode(&data); err != nil {
// Invalid JSON - return error to client, continue processing
http.Error(writer, "Invalid JSON", http.StatusBadRequest)
return nil
}
if err := w.createResource(data); err != nil {
// Transient error - return error to client, continue processing
http.Error(writer, "Create failed", http.StatusInternalServerError)
return nil
}
writer.WriteHeader(http.StatusCreated)
return nil
}Returning error terminates the worker. Use this for fatal errors only (database connection lost, critical resource unavailable). For transient errors (validation, not found, conflict), write error response and return nil.
Using with act.Pool
Single worker processes one request at a time. Use act.Pool for concurrent processing:
type APIWorkerPool struct {
act.Pool
}
func (p *APIWorkerPool) Init(args ...any) (act.PoolOptions, error) {
return act.PoolOptions{
PoolSize: 10,
WorkerMailboxSize: 20,
WorkerFactory: func() gen.ProcessBehavior { return &APIWorker{} },
}, nil
}Spawn pool instead of single worker:
_, err := s.SpawnRegister("api-worker",
func() gen.ProcessBehavior { return &APIWorkerPool{} },
gen.ProcessOptions{},
)WebHandler sends requests to pool. Pool distributes across 10 workers. System handles 10 concurrent requests.
For details on pools, see Pool.
Handling Non-HTTP Messages
WebWorker processes meta.MessageWebRequest specially, but also receives regular messages:
func (w *APIWorker) HandleMessage(from gen.PID, message any) error {
// meta.MessageWebRequest handled automatically by WebWorker
// Other messages reach this callback
switch m := message.(type) {
case ConfigUpdate:
w.config = m.Config
w.Log().Info("Configuration updated")
}
return nil
}This allows workers to receive configuration updates, control messages, or other actor communication while processing HTTP requests.
Implementation Details
WebWorker implements gen.ProcessBehavior at low level. It manages the mailbox loop, detects meta.MessageWebRequest, routes by HTTP method, and calls Done() after processing.
The Done() call is critical. It cancels the context that WebHandler blocks on. Without it, HTTP request would timeout. WebWorker guarantees Done() is called even if your callback panics or returns error.
Default implementations for all callbacks exist. Unimplemented HTTP methods log warning and return 501 Not Implemented. This allows implementing only the methods you need without boilerplate for unsupported methods.
Last updated
