class Analyzer::Erlang::Cowboy

Overview

Cowboy keeps its routes in a dispatch table rather than in per-handler annotations:

Dispatch = cowboy_router:compile([
    {'_', [
        {"/",             hello_handler, []},
        {"/users/:id",    user_handler,  []},
        {"/static/[...]", cowboy_static, {priv_dir, app, "static"}}
    ]}
]),

The verb is deliberately absent from the table — Cowboy leaves method negotiation to the handler (allowed_methods/2 for REST handlers, or a match on cowboy_req:method/1). Resolving it therefore means following the handler atom to its module, which is what handler_info does; routes whose handler can't be resolved fall back to "ANY".

Defined in:

analyzer/analyzers/erlang/cowboy.cr

Constant Summary

ALLOWED_METHODS_REGEX = /allowed_methods\s*\([^)]*\)\s*->\s*\{\s*\[([^\]]*)\]/
BINDING_REGEX = /cowboy_req:binding\s*\(\s*([a-z][A-Za-z0-9_@]*)/
BODY_VERBS = Set {"POST", "PUT", "PATCH", "ANY"}

Verbs that can carry a request body. Handler params are collected per module rather than per clause, so a module that both serves GET and reads a body on POST would otherwise hang a body param off its GET route.

HANDLER_ATOM = /\A[a-z][A-Za-z0-9_@]*\z/
HEADER_REGEX = /cowboy_req:header\s*\(\s*<<\s*"([^"]+)"\s*>>/
HTTP_VERBS = Set {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE", "CONNECT"}
MATCH_COOKIE_START = /cowboy_req:match_cookies\s*\(\s*\[/
MATCH_QS_START = /cowboy_req:match_qs\s*\(\s*\[/
MAX_TUPLE_LINES = 20

A dispatch tuple that spans more lines than this is not a shape we can read; bounding the window also bounds the joined-text cost so a pathological file can't turn the scan quadratic.

METHOD_BINARY_REGEX = /<<\s*"([A-Za-z]+)"\s*>>/
PATH_LITERAL = /\A\s*(?:"((?:[^"\\]|\\.)*)"|<<\s*"((?:[^"\\]|\\.)*)"\s*>>)\s*\z/
QS_FIELD_REGEX = /\A\{?\s*([a-z][A-Za-z0-9_@]*)/

A match_qs field is either a bare atom (id) or a constraint tuple whose first element is the field name ({token, nonempty}, {page, int, 1}). Only that leading atom is the parameter — the rest is the constraint and its default.

READ_BODY_REGEX = /cowboy_req:read_(?:body|part)\s*\(/
REQ_METHOD_REGEX = /cowboy_req:method\s*\(/
ROUTE_TUPLE_START = /\{\s*(?:"\/|<<\s*"\/)/

Cowboy dispatch entries are {PathMatch, Handler, InitialState} or {PathMatch, Constraints, Handler, InitialState}, and PathMatch is always an absolute path — as a string or as a binary. Requiring the leading / right in the gate keeps every other Erlang tuple out.

STATIC_HANDLER = "cowboy_static"

cowboy_static is Cowboy's built-in file handler. It never reaches user code, so there is no allowed_methods/2 to follow — but it only ever serves GET and HEAD.

URLENCODED_REGEX = /cowboy_req:read_urlencoded_body\s*\(/

Instance Method Summary

Instance methods inherited from class Analyzer

analyze analyze, base_path : String base_path, base_paths : Array(String) base_paths, callees_needed? : Bool callees_needed?, http_header_name(name : String) : String | Nil http_header_name, line_number_for_index(content : String, char_index : Int32) : Int32 line_number_for_index, logger : NoirLogger logger, parallel_analyze(files : Array(String), &block : String -> Nil) parallel_analyze, read_file_content(path : String) : String read_file_content, result : Array(Endpoint) result, unique_params(params : Array(Param)) : Array(Param) unique_params, url : String url, web_root_path(path : String, markers : Array(String)) : String web_root_path

Constructor methods inherited from class Analyzer

new(options : Hash(String, YAML::Any)) new

Instance methods inherited from module FileHelper

all_files : Array(String) all_files, get_files_by_extension(extension : String) : Array(String) get_files_by_extension, get_files_by_extensions(extensions : Array(String)) : Array(String) get_files_by_extensions, get_files_by_prefix(prefix : String) : Array(String) get_files_by_prefix, get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String) get_files_by_prefix_and_extension, get_public_dir_files(base_path : String, folder : String) : Array(String) get_public_dir_files, get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String) get_public_files

Instance Method Detail

def analyze #

[View source]