@@ -72,6 +72,7 @@ const (
7272 WWWAuthenticate = "WWW-Authenticate"
7373 XForwardedFor = "X-Forwarded-For"
7474 XRealIP = "X-Real-Ip"
75+ Allow = "Allow"
7576
7677 Gzip = "gzip"
7778
@@ -126,7 +127,8 @@ type LARS struct {
126127 http404 HandlersChain // 404 Not Found
127128 http405 HandlersChain // 405 Method Not Allowed
128129
129- notFound HandlersChain
130+ automaticOPTIONS HandlersChain
131+ notFound HandlersChain
130132
131133 customHandlersFuncs customHandlers
132134
@@ -144,6 +146,10 @@ type LARS struct {
144146 // If no other Method is allowed, the request is delegated to the NotFound
145147 // handler.
146148 handleMethodNotAllowed bool
149+
150+ // if enabled automatically handles OPTION requests; manually configured OPTION
151+ // handlers take precidence. default true
152+ automaticallyHandleOPTIONS bool
147153}
148154
149155// RouteMap contains a single routes full path
@@ -161,17 +167,11 @@ var (
161167 }
162168
163169 methodNotAllowedHandler = func (c Context ) {
170+ c .Response ().WriteHeader (http .StatusMethodNotAllowed )
171+ }
164172
165- mth , _ := c .Get ("methods" )
166- methods := mth .([]string )
167-
168- res := c .Response ()
169-
170- for _ , m := range methods {
171- res .Header ().Add ("Allow" , m )
172- }
173-
174- res .WriteHeader (http .StatusMethodNotAllowed )
173+ automaticOPTIONSHandler = func (c Context ) {
174+ c .Response ().WriteHeader (http .StatusOK )
175175 }
176176)
177177
@@ -186,11 +186,12 @@ func New() *LARS {
186186 contextFunc : func (l * LARS ) Context {
187187 return NewContext (l )
188188 },
189- mostParams : 0 ,
190- http404 : []HandlerFunc {default404Handler },
191- http405 : []HandlerFunc {methodNotAllowedHandler },
192- redirectTrailingSlash : true ,
193- handleMethodNotAllowed : false ,
189+ mostParams : 0 ,
190+ http404 : []HandlerFunc {default404Handler },
191+ http405 : []HandlerFunc {methodNotAllowedHandler },
192+ redirectTrailingSlash : true ,
193+ handleMethodNotAllowed : false ,
194+ automaticallyHandleOPTIONS : true ,
194195 }
195196
196197 l .routeGroup .lars = l
@@ -241,6 +242,13 @@ func (l *LARS) Register404(notFound ...Handler) {
241242 l .http404 = chain
242243}
243244
245+ // SetAutomaticallyHandleOPTIONS tells lars whether to
246+ // automatically handle OPTION requests; manually configured
247+ // OPTION handlers take precedence. default true
248+ func (l * LARS ) SetAutomaticallyHandleOPTIONS (set bool ) {
249+ l .automaticallyHandleOPTIONS = set
250+ }
251+
244252// SetRedirectTrailingSlash tells lars whether to try
245253// and fix a URL by trying to find it
246254// lowercase -> with or without slash -> 404
@@ -265,6 +273,12 @@ func (l *LARS) Serve() http.Handler {
265273 copy (l .notFound , l .middleware )
266274 copy (l .notFound [len (l .middleware ):], l .http404 )
267275
276+ if l .automaticallyHandleOPTIONS {
277+ l .automaticOPTIONS = make (HandlersChain , len (l .middleware )+ 1 )
278+ copy (l .automaticOPTIONS , l .middleware )
279+ copy (l .automaticOPTIONS [len (l .middleware ):], []HandlerFunc {automaticOPTIONSHandler })
280+ }
281+
268282 return http .HandlerFunc (l .serveHTTP )
269283}
270284
@@ -280,85 +294,113 @@ func (l *LARS) serveHTTP(w http.ResponseWriter, r *http.Request) {
280294
281295 c .params = c .params [0 :0 ]
282296
283- if l .redirectTrailingSlash {
297+ if l .redirectTrailingSlash && len ( r . URL . Path ) > 1 {
284298
285299 // find again all lowercase
286300 lc := strings .ToLower (r .URL .Path )
287301
288302 if lc != r .URL .Path {
289303
290- r .URL .Path = lc
291-
292- if c .handlers , _ , _ = root .find (r .URL .Path , c .params ); c .handlers != nil {
304+ if c .handlers , _ , _ = root .find (lc , c .params ); c .handlers != nil {
305+ r .URL .Path = lc
293306 c .handlers = l .redirect (r .Method )
294307 goto END
295308 }
296309 }
297310
298- if r . URL . Path [len (r . URL . Path )- 1 :] == basePath {
299- r . URL . Path = r . URL . Path [:len (r . URL . Path )- 1 ]
311+ if lc [len (lc )- 1 :] == basePath {
312+ lc = lc [:len (lc )- 1 ]
300313 } else {
301- r . URL . Path = r . URL . Path + basePath
314+ lc = lc + basePath
302315 }
303316
304- if c .handlers , _ , _ = root .find (r .URL .Path , c .params ); c .handlers != nil {
317+ if c .handlers , _ , _ = root .find (lc , c .params ); c .handlers != nil {
318+ r .URL .Path = lc
305319 c .handlers = l .redirect (r .Method )
306320 goto END
307321 }
322+ }
308323
309- // slow, but get's the job done
310- if l .handleMethodNotAllowed {
324+ } else {
325+ goto END
326+ }
327+ }
311328
312- r .URL .Path = lc
329+ if l .automaticallyHandleOPTIONS && r .Method == OPTIONS {
330+ l .getOptions (c )
331+ goto END
332+ }
313333
314- if l .checkMethodNotAllowed (c ) {
315- goto END
316- }
317- }
334+ if l .handleMethodNotAllowed {
335+
336+ if l .checkMethodNotAllowed (c ) {
337+ goto END
338+ }
339+ }
340+
341+ // not found
342+ c .handlers = l .notFound
343+
344+ END:
345+
346+ c .parent .Next ()
347+ c .parent .RequestEnd ()
348+
349+ l .pool .Put (c )
350+ }
351+
352+ func (l * LARS ) getOptions (c * Ctx ) {
353+
354+ res := c .Response ()
355+
356+ if c .request .URL .Path == "*" { // check server-wide OPTIONS
357+
358+ for m := range l .trees {
359+
360+ if m == OPTIONS {
361+ continue
318362 }
319363
320- // lowercase, fix trailing 405....
321- c .handlers = l .notFound
364+ res .Header ().Add (Allow , m )
322365 }
366+
323367 } else {
368+ for m , tree := range l .trees {
324369
325- // slow, but get's the job done
326- if l .handleMethodNotAllowed {
370+ if m == c .request .Method || m == OPTIONS {
371+ continue
372+ }
327373
328- if l . checkMethodNotAllowed ( c ) {
329- goto END
374+ if c . handlers , _ , _ = tree . find ( c . request . URL . Path , c . params ); c . handlers != nil {
375+ res . Header (). Add ( Allow , m )
330376 }
331377 }
332378
333- c .handlers = l .notFound
334379 }
335380
336- END:
381+ res .Header ().Add (Allow , OPTIONS )
382+ c .handlers = l .automaticOPTIONS
337383
338- c .parent .Next ()
339- c .parent .RequestEnd ()
340-
341- l .pool .Put (c )
384+ return
342385}
343386
344387func (l * LARS ) checkMethodNotAllowed (c * Ctx ) (found bool ) {
345388
346- var methods [] string
389+ res := c . Response ()
347390
348391 for m , tree := range l .trees {
349392
350393 if m != c .request .Method {
351394 if c .handlers , _ , _ = tree .find (c .request .URL .Path , c .params ); c .handlers != nil {
352395 // add methods
353- methods = append (methods , m )
396+ res .Header ().Add (Allow , m )
397+ found = true
354398 }
355399 }
356400 }
357401
358- if len (methods ) > 0 {
359- c .Set ("methods" , methods )
402+ if found {
360403 c .handlers = l .http405
361- found = true
362404 }
363405
364406 return
0 commit comments