@@ -39,4 +39,152 @@ Ways to register a Post Serve Action
3939 }
4040
4141
42+ Choosing Between Local and Remote Execution
43+ --------------------------------------------
44+
45+ Hoverfly supports two execution modes for post-serve actions. Understanding the
46+ difference is important when running under significant load.
47+
48+ **Local execution **
49+
50+ Hoverfly forks a new subprocess for every request that triggers the action. The
51+ binary or script is re-executed from scratch each time:
52+
53+ .. code ::
54+
55+ hoverfly -post-serve-action "my-action python3 /path/to/script.py 0"
56+
57+ This is the easiest option to get started, but it does not scale. At high request
58+ rates (e.g. 40+ rps with a Python script), you will quickly accumulate dozens of
59+ concurrent processes — each with its own interpreter startup cost, memory footprint,
60+ and open connections. This commonly leads to OOMKilled in containerised environments.
61+
62+ **Remote execution (recommended for high throughput) **
63+
64+ Instead of forking a subprocess, Hoverfly makes an HTTP ``POST `` request to a
65+ server that you run separately. That server stays alive indefinitely — only one
66+ process, shared across all requests:
67+
68+ .. code ::
69+
70+ hoverfly -post-serve-action "my-action http://localhost:8080/trigger 0"
71+
72+ This means you can use async frameworks such as ``aiohttp `` or ``FastAPI `` in their
73+ natural form: one running event loop handling many concurrent webhooks efficiently,
74+ without paying the startup cost on every request.
75+
76+ Running a Remote Post Serve Action: Step by Step
77+ --------------------------------------------------
78+
79+ **Step 1 — Write your server **
80+
81+ Your server must accept HTTP ``POST `` requests and return ``HTTP 200 ``. Hoverfly
82+ considers any other status code a failure and logs an error. Here is a minimal
83+ Python example:
84+
85+ .. code :: python
86+
87+ # server.py
88+ from http.server import BaseHTTPRequestHandler, HTTPServer
89+ import json
90+
91+ class Handler (BaseHTTPRequestHandler ):
92+ def do_POST (self ):
93+ length = int (self .headers.get(" Content-Length" , 0 ))
94+ payload = json.loads(self .rfile.read(length))
95+
96+ # payload contains two keys: "request" and "response"
97+ # use them to implement your webhook/callback logic
98+ print (f " [action] path= { payload[' request' ][' path' ]} " )
99+
100+ self .send_response(200 )
101+ self .end_headers()
102+
103+ def log_message (self , format , * args ):
104+ pass # suppress default access log noise
105+
106+ if __name__ == " __main__" :
107+ print (" Listening on :8080" )
108+ HTTPServer((" " , 8080 ), Handler).serve_forever()
109+
110+ Start it:
111+
112+ .. code ::
113+
114+ python3 server.py
115+
116+ **Step 2 — Understand the payload Hoverfly sends **
117+
118+ On every matching request, Hoverfly POSTs a JSON body to your server containing
119+ the full request-response pair:
120+
121+ .. code :: json
122+
123+ {
124+ "request" : {
125+ "path" : [{"matcher" : " exact" , "value" : " /api/orders" }],
126+ "method" : [{"matcher" : " exact" , "value" : " POST" }],
127+ "destination" : [{"matcher" : " exact" , "value" : " example.com" }],
128+ "body" : [{"matcher" : " exact" , "value" : " " }],
129+ "headers" : {}
130+ },
131+ "response" : {
132+ "status" : 200 ,
133+ "body" : " Hello World" ,
134+ "encodedBody" : false
135+ }
136+ }
137+
138+ Your server can read any field from this payload to drive its logic — for example,
139+ extracting an order ID from the request body to send a webhook.
140+
141+ **Step 3 — Configure Hoverfly to call your server **
142+
143+ Pass the remote URL as the second token in ``-post-serve-action ``:
144+
145+ .. code ::
146+
147+ hoverfly -post-serve-action "my-action http://localhost:8080 0" -import simulation.json
148+
149+ The format is: ``"<name> <url> <delay-ms>" ``.
150+
151+ - ``my-action `` must match the ``postServeAction `` field in your simulation JSON.
152+ - The URL must be reachable from Hoverfly at runtime.
153+ - The delay (in milliseconds) is applied before Hoverfly calls the endpoint.
154+
155+ Alternatively, register it at runtime via hoverctl:
156+
157+ .. code ::
158+
159+ hoverctl post-serve-action set --name my-action --remote http://localhost:8080 --delay 0
160+
161+ **Step 4 — Confirm the action is registered **
162+
163+ .. code ::
164+
165+ curl http://localhost:8888/api/v2/hoverfly/post-serve-action
166+
167+ You should see your action listed with its remote URL.
168+
169+ **Running in Docker Compose **
170+
171+ When both Hoverfly and your action server run as containers, use the service name
172+ as the hostname. Make sure Hoverfly starts after the action server is ready:
173+
174+ .. code :: yaml
175+
176+ services :
177+ hoverfly :
178+ image : spectolabs/hoverfly
179+ command : >
180+ -post-serve-action "my-action http://postaction:8080 0"
181+ -import /app/simulation.json
182+ depends_on :
183+ - postaction
184+
185+ postaction :
186+ build : ./postaction-server
187+ ports :
188+ - " 8080:8080"
189+
42190
0 commit comments