55 "fmt"
66 "log"
77 "net/http"
8+ "strconv"
9+ "strings"
10+ "time"
811)
912
1013type Response struct {
@@ -14,13 +17,17 @@ type Response struct {
1417func writeJSONResponse (w http.ResponseWriter , message string , statusCode int ) {
1518 w .Header ().Set ("Content-Type" , "application/json" )
1619 w .WriteHeader (statusCode )
17-
20+
1821 response := Response {Message : message }
1922 json .NewEncoder (w ).Encode (response )
2023}
2124
2225func okHandler (w http.ResponseWriter , r * http.Request ) {
23- writeJSONResponse (w , "OK" , http .StatusOK )
26+ if r .URL .Path == "/" {
27+ writeJSONResponse (w , "OK" , http .StatusOK )
28+ return
29+ }
30+ http .NotFound (w , r )
2431}
2532
2633func error403Handler (w http.ResponseWriter , r * http.Request ) {
@@ -41,13 +48,276 @@ func redirectTwoHandler(w http.ResponseWriter, r *http.Request) {
4148 writeJSONResponse (w , message , http .StatusOK )
4249}
4350
51+ func timeoutHandler (w http.ResponseWriter , r * http.Request ) {
52+ path := strings .TrimPrefix (r .URL .Path , "/timeout/" )
53+
54+ seconds , err := strconv .Atoi (path )
55+ if err != nil {
56+ writeJSONResponse (w , "Invalid timeout value. Must be a number." , http .StatusBadRequest )
57+ return
58+ }
59+
60+ if seconds < 1 || seconds >= 300 {
61+ writeJSONResponse (w , "Timeout value must be between 1 and 299 seconds." , http .StatusBadRequest )
62+ return
63+ }
64+
65+ time .Sleep (time .Duration (seconds ) * time .Second )
66+ message := fmt .Sprintf ("Request completed after %d seconds" , seconds )
67+ writeJSONResponse (w , message , http .StatusOK )
68+ }
69+
70+ const openAPISpec = `{
71+ "openapi": "3.0.3",
72+ "info": {
73+ "title": "Best API",
74+ "description": "A simple HTTP API with various endpoints for testing",
75+ "version": "1.0.0"
76+ },
77+ "servers": [
78+ {
79+ "url": "http://localhost:9999",
80+ "description": "Development server"
81+ }
82+ ],
83+ "paths": {
84+ "/": {
85+ "get": {
86+ "summary": "Health check endpoint",
87+ "description": "Returns OK status",
88+ "responses": {
89+ "200": {
90+ "description": "Successful response",
91+ "content": {
92+ "application/json": {
93+ "schema": {
94+ "$ref": "#/components/schemas/Response"
95+ },
96+ "example": {
97+ "message": "OK"
98+ }
99+ }
100+ }
101+ }
102+ }
103+ }
104+ },
105+ "/error403": {
106+ "get": {
107+ "summary": "Forbidden error endpoint",
108+ "description": "Returns 403 Forbidden status",
109+ "responses": {
110+ "403": {
111+ "description": "Forbidden error",
112+ "content": {
113+ "application/json": {
114+ "schema": {
115+ "$ref": "#/components/schemas/Response"
116+ },
117+ "example": {
118+ "message": "Forbidden"
119+ }
120+ }
121+ }
122+ }
123+ }
124+ }
125+ },
126+ "/error500": {
127+ "get": {
128+ "summary": "Internal server error endpoint",
129+ "description": "Returns 500 Internal Server Error status",
130+ "responses": {
131+ "500": {
132+ "description": "Internal server error",
133+ "content": {
134+ "application/json": {
135+ "schema": {
136+ "$ref": "#/components/schemas/Response"
137+ },
138+ "example": {
139+ "message": "Internal Server Error"
140+ }
141+ }
142+ }
143+ }
144+ }
145+ }
146+ },
147+ "/redirect": {
148+ "get": {
149+ "summary": "Redirect endpoint",
150+ "description": "Returns redirect to /redirect-two",
151+ "responses": {
152+ "303": {
153+ "description": "See Other redirect",
154+ "content": {
155+ "application/json": {
156+ "schema": {
157+ "$ref": "#/components/schemas/Response"
158+ },
159+ "example": {
160+ "message": "Redirecting to /redirect-two"
161+ }
162+ }
163+ }
164+ }
165+ }
166+ }
167+ },
168+ "/redirecttwo": {
169+ "get": {
170+ "summary": "Redirect target endpoint",
171+ "description": "Shows referrer information",
172+ "responses": {
173+ "200": {
174+ "description": "Successful response with referrer info",
175+ "content": {
176+ "application/json": {
177+ "schema": {
178+ "$ref": "#/components/schemas/Response"
179+ },
180+ "example": {
181+ "message": "Redirected from http://localhost:9999/redirect"
182+ }
183+ }
184+ }
185+ }
186+ }
187+ }
188+ },
189+ "/timeout/{seconds}": {
190+ "get": {
191+ "summary": "Timeout endpoint",
192+ "description": "Waits for specified number of seconds (1-299) before responding",
193+ "parameters": [
194+ {
195+ "name": "seconds",
196+ "in": "path",
197+ "required": true,
198+ "schema": {
199+ "type": "integer",
200+ "minimum": 1,
201+ "maximum": 299
202+ },
203+ "description": "Number of seconds to wait before responding"
204+ }
205+ ],
206+ "responses": {
207+ "200": {
208+ "description": "Successful response after timeout",
209+ "content": {
210+ "application/json": {
211+ "schema": {
212+ "$ref": "#/components/schemas/Response"
213+ },
214+ "example": {
215+ "message": "Request completed after 30 seconds"
216+ }
217+ }
218+ }
219+ },
220+ "400": {
221+ "description": "Bad request - invalid timeout value",
222+ "content": {
223+ "application/json": {
224+ "schema": {
225+ "$ref": "#/components/schemas/Response"
226+ },
227+ "examples": {
228+ "invalid_number": {
229+ "value": {
230+ "message": "Invalid timeout value. Must be a number."
231+ }
232+ },
233+ "out_of_range": {
234+ "value": {
235+ "message": "Timeout value must be between 1 and 299 seconds."
236+ }
237+ }
238+ }
239+ }
240+ }
241+ }
242+ }
243+ }
244+ }
245+ },
246+ "components": {
247+ "schemas": {
248+ "Response": {
249+ "type": "object",
250+ "properties": {
251+ "message": {
252+ "type": "string",
253+ "description": "Response message"
254+ }
255+ },
256+ "required": ["message"]
257+ }
258+ }
259+ }
260+ }`
261+
262+ func swaggerHandler (w http.ResponseWriter , r * http.Request ) {
263+ if strings .HasSuffix (r .URL .Path , "/openapi.json" ) {
264+ w .Header ().Set ("Content-Type" , "application/json" )
265+ w .WriteHeader (http .StatusOK )
266+ w .Write ([]byte (openAPISpec ))
267+ return
268+ }
269+
270+ swaggerHTML := `<!DOCTYPE html>
271+ <html>
272+ <head>
273+ <title>API Documentation</title>
274+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui.css" />
275+ <style>
276+ html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
277+ *, *:before, *:after { box-sizing: inherit; }
278+ body { margin:0; background: #fafafa; }
279+ </style>
280+ </head>
281+ <body>
282+ <div id="swagger-ui"></div>
283+ <script src="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui-bundle.js"></script>
284+ <script src="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui-standalone-preset.js"></script>
285+ <script>
286+ window.onload = function() {
287+ const ui = SwaggerUIBundle({
288+ url: '/swagger/openapi.json',
289+ dom_id: '#swagger-ui',
290+ deepLinking: true,
291+ presets: [
292+ SwaggerUIBundle.presets.apis,
293+ SwaggerUIStandalonePreset
294+ ],
295+ plugins: [
296+ SwaggerUIBundle.plugins.DownloadUrl
297+ ],
298+ layout: "StandaloneLayout"
299+ });
300+ };
301+ </script>
302+ </body>
303+ </html>`
304+
305+ w .Header ().Set ("Content-Type" , "text/html" )
306+ w .WriteHeader (http .StatusOK )
307+ w .Write ([]byte (swaggerHTML ))
308+ }
309+
44310func main () {
45311 http .HandleFunc ("/" , okHandler )
46312 http .HandleFunc ("/error403" , error403Handler )
47313 http .HandleFunc ("/error500" , error500Handler )
48314 http .HandleFunc ("/redirect" , redirectHandler )
49315 http .HandleFunc ("/redirecttwo" , redirectTwoHandler )
316+ http .HandleFunc ("/timeout/" , timeoutHandler )
317+ http .HandleFunc ("/swagger/" , swaggerHandler )
318+ http .HandleFunc ("/swagger/openapi.json" , swaggerHandler )
50319
51320 log .Println ("Server starting on :9999" )
321+ log .Println ("OpenAPI documentation available at http://localhost:9999/swagger/" )
52322 log .Fatal (http .ListenAndServe (":9999" , nil ))
53- }
323+ }
0 commit comments