Skip to content

Instantly share code, notes, and snippets.

@mascarenhas
Created March 5, 2010 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mascarenhas/322359 to your computer and use it in GitHub Desktop.
Save mascarenhas/322359 to your computer and use it in GitHub Desktop.
local lpeg = require "lpeg"
local re = require "re"
local json = require "json"
local P = lpeg.P
local S = lpeg.S
local R = lpeg.R
local C = lpeg.C
local Ct = lpeg.Ct
local Cmt = lpeg.Cmt
local function snake_upper(s)
s = string.upper(s)
return (s:gsub("%-", "_"))
end
-- HTTP PROTOCOL GRAMMAR
--line endings
CRLF = P"\r\n"
-- character types
any = P(1)
cntrl = R(string.char(0) .. string.char(31))
CTL = cntrl + string.char(127)
safe = S"$-_."
extra = S"!*'(),"
reserved = S";/?:@&=+"
sorta_safe = S"\"<>"
unsafe = CTL + S" #%" + sorta_safe
alpha = R("az", "AZ")
digit = R"09"
national = any - (alpha + digit + reserved + extra + safe + unsafe)
unreserved = alpha + digit + safe + extra + national
xdigit = R("09", "af", "AF")
escape = P"%" * xdigit * xdigit
uchar = unreserved + escape + sorta_safe
pchar = uchar + S":@&=+"
tspecials = S"()<>@,;:\\\"/[]?={} \t"
lws = S" \t"
-- elements
ascii = R(string.char(0) .. string.char(127))
token = ascii - (CTL + tspecials)
-- URI schemes and absolute paths
scheme = S"hH" * S"tT" * S"tT" * S"pP" * S"sS"^-1 / function (s) return string.lower(s) end
alnum = R("09", "az", "AZ")
hostname = (alnum + S"-._")^1
host_with_port = C(hostname) * (":" * C(digit^1))^-1 / function (h, p)
return { host = h, port = tonumber(p) or 80 }
end
userinfo = ((unreserved + escape + S";:&=+")^1 * "@")^0
path = pchar^1 * ( P"/" * pchar^0 )^0
query = (uchar + reserved)^0
param = C((pchar + "/")^0)
params = param * (";" * param)^0
rel_path = (C(path^-1) * Ct((";" * params)^-1)) * ("?" * C(query))^-1 / function (path, params, query)
return { path = path, params = params,
query = query }
end
absolute_path = P("/")^1 * rel_path / function (rel_path)
rel_path.path = "/" .. rel_path.path
return rel_path
end
path_uri = absolute_path
Absolute_URI = scheme * "://" * userinfo * host_with_port * path_uri / function (scheme, host, path)
return {
scheme = scheme,
host = host.host,
port = host.port,
path = path.path,
params = path.params,
query = path.query
}
end
Request_URI = absolute_path + (P"*" / function () return {} end) + Absolute_URI
Fragment = (uchar + reserved)^0;
Method = token^1 / function (s) return string.upper(s) end
GetOnly = C"GET"
http_number = digit^1 * "." * digit^1
HTTP_Version = C("HTTP/" * http_number)
Request_Line = Method * " " * Request_URI * ("#" * C(Fragment))^-1 *
" " * HTTP_Version * CRLF / function (method, uri, fragment, version)
if fragment and not version then
version, fragment = fragment, version
end
return { method = method, uri = uri, version = version,
fragment = fragment }
end
field_name = token^1 / function (s)
return snake_upper(s)
end
field_value = C((any - CRLF)^0)
value_cont = lws^1 * C((any - CRLF)^0) * CRLF
message_header = field_name * ":" * P(" ")^0 * field_value * CRLF *
Ct(value_cont^0) / function (name, value, conts)
value = value .. table.concat(conts)
return { name = name, value = value }
end
FullRequest = Request_Line * Ct(message_header^0) * CRLF / function (reqline, headers)
return { line = reqline, headers = headers }
end
SimpleRequest = GetOnly * " " * Request_URI * (P"#" * C(Fragment))^-1 * CRLF * CRLF / function (uri, fragment)
return {
line = {
method = "GET",
uri = uri,
fragment =
fragment
},
headers = {}
}
end
main = FullRequest + SimpleRequest
do
local http = "GET / HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET /foo/bar.html HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST /foo/bar.html HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST /foo/bar.html\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST http://foo.com/foo/bar.html HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST https://foo.com/foo/bar.html HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST https://foo.com:8080/foo/bar.html HTTP/1.1\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\nHost: foo\r\nX-Forwarded-Proto: https\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\nX-ASDF: \r\n\t\r\n \r\n ASDF\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET / HTTP/1.1\r\nX-ASDF: hi\r\n y\r\n\t\r\n x\r\n ASDF\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "GET /test HTTP/1.1\r\nUser-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\nHost: 0.0.0.0=5000\r\nAccept: */*\r\n\r\n"
local req = main:match(http)
print(json.encode(req))
end
do
local http = "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\nAccept: */*\r\nTransfer-Encoding: identity\r\nContent-Length: 5\r\n\r\n"
local req
local n = 100000
local t1 = os.clock()
for i = 1, n do
req = main:match(http)
end
local t2 = os.clock()
print(t2-t1, n/(t2-t1))
print(json.encode(req))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment