Created
March 5, 2010 01:30
-
-
Save mascarenhas/322359 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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