Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
commit
05d9d395fe
3 changed files with 102 additions and 13 deletions
|
|
@ -35,26 +35,29 @@ proc sendTextContentType(client: TSocket) =
|
||||||
send(client, "Content-type: text/html" & wwwNL)
|
send(client, "Content-type: text/html" & wwwNL)
|
||||||
send(client, wwwNL)
|
send(client, wwwNL)
|
||||||
|
|
||||||
|
proc sendStatus(client: TSocket, status: string) =
|
||||||
|
send(client, "HTTP/1.1 " & status & wwwNL)
|
||||||
|
|
||||||
proc badRequest(client: TSocket) =
|
proc badRequest(client: TSocket) =
|
||||||
# Inform the client that a request it has made has a problem.
|
# Inform the client that a request it has made has a problem.
|
||||||
send(client, "HTTP/1.0 400 BAD REQUEST" & wwwNL)
|
send(client, "HTTP/1.1 400 Bad Request" & wwwNL)
|
||||||
sendTextContentType(client)
|
sendTextContentType(client)
|
||||||
send(client, "<p>Your browser sent a bad request, " &
|
send(client, "<p>Your browser sent a bad request, " &
|
||||||
"such as a POST without a Content-Length." & wwwNL)
|
"such as a POST without a Content-Length.</p>" & wwwNL)
|
||||||
|
|
||||||
proc cannotExec(client: TSocket) =
|
proc cannotExec(client: TSocket) =
|
||||||
send(client, "HTTP/1.0 500 Internal Server Error" & wwwNL)
|
send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
|
||||||
sendTextContentType(client)
|
sendTextContentType(client)
|
||||||
send(client, "<P>Error prohibited CGI execution." & wwwNL)
|
send(client, "<P>Error prohibited CGI execution." & wwwNL)
|
||||||
|
|
||||||
proc headers(client: TSocket, filename: string) =
|
proc headers(client: TSocket, filename: string) =
|
||||||
# XXX could use filename to determine file type
|
# XXX could use filename to determine file type
|
||||||
send(client, "HTTP/1.0 200 OK" & wwwNL)
|
send(client, "HTTP/1.1 200 OK" & wwwNL)
|
||||||
send(client, ServerSig)
|
send(client, ServerSig)
|
||||||
sendTextContentType(client)
|
sendTextContentType(client)
|
||||||
|
|
||||||
proc notFound(client: TSocket) =
|
proc notFound(client: TSocket) =
|
||||||
send(client, "HTTP/1.0 404 NOT FOUND" & wwwNL)
|
send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL)
|
||||||
send(client, ServerSig)
|
send(client, ServerSig)
|
||||||
sendTextContentType(client)
|
sendTextContentType(client)
|
||||||
send(client, "<html><title>Not Found</title>" & wwwNL)
|
send(client, "<html><title>Not Found</title>" & wwwNL)
|
||||||
|
|
@ -64,7 +67,7 @@ proc notFound(client: TSocket) =
|
||||||
send(client, "</body></html>" & wwwNL)
|
send(client, "</body></html>" & wwwNL)
|
||||||
|
|
||||||
proc unimplemented(client: TSocket) =
|
proc unimplemented(client: TSocket) =
|
||||||
send(client, "HTTP/1.0 501 Method Not Implemented" & wwwNL)
|
send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL)
|
||||||
send(client, ServerSig)
|
send(client, ServerSig)
|
||||||
sendTextContentType(client)
|
sendTextContentType(client)
|
||||||
send(client, "<html><head><title>Method Not Implemented" &
|
send(client, "<html><head><title>Method Not Implemented" &
|
||||||
|
|
@ -207,9 +210,11 @@ type
|
||||||
socket: TSocket
|
socket: TSocket
|
||||||
port: TPort
|
port: TPort
|
||||||
client*: TSocket ## the socket to write the file data to
|
client*: TSocket ## the socket to write the file data to
|
||||||
|
reqMethod*: string ## Request method. GET or POST.
|
||||||
path*, query*: string ## path and query the client requested
|
path*, query*: string ## path and query the client requested
|
||||||
headers*: PStringTable ## headers with which the client made the request
|
headers*: PStringTable ## headers with which the client made the request
|
||||||
|
body*: string ## only set with POST requests
|
||||||
|
|
||||||
proc open*(s: var TServer, port = TPort(80)) =
|
proc open*(s: var TServer, port = TPort(80)) =
|
||||||
## creates a new server at port `port`. If ``port == 0`` a free port is
|
## creates a new server at port `port`. If ``port == 0`` a free port is
|
||||||
## acquired that can be accessed later by the ``port`` proc.
|
## acquired that can be accessed later by the ``port`` proc.
|
||||||
|
|
@ -223,6 +228,8 @@ proc open*(s: var TServer, port = TPort(80)) =
|
||||||
else:
|
else:
|
||||||
s.port = port
|
s.port = port
|
||||||
s.client = InvalidSocket
|
s.client = InvalidSocket
|
||||||
|
s.reqMethod = ""
|
||||||
|
s.body = ""
|
||||||
s.path = ""
|
s.path = ""
|
||||||
s.query = ""
|
s.query = ""
|
||||||
s.headers = {:}.newStringTable()
|
s.headers = {:}.newStringTable()
|
||||||
|
|
@ -234,7 +241,7 @@ proc port*(s: var TServer): TPort =
|
||||||
proc next*(s: var TServer) =
|
proc next*(s: var TServer) =
|
||||||
## proceed to the first/next request.
|
## proceed to the first/next request.
|
||||||
s.client = accept(s.socket)
|
s.client = accept(s.socket)
|
||||||
s.headers = {:}.newStringTable()
|
s.headers = newStringTable(modeCaseInsensitive)
|
||||||
#headers(s.client, "")
|
#headers(s.client, "")
|
||||||
var data = ""
|
var data = ""
|
||||||
while not s.client.recvLine(data): nil
|
while not s.client.recvLine(data): nil
|
||||||
|
|
@ -252,8 +259,9 @@ proc next*(s: var TServer) =
|
||||||
var key = ""
|
var key = ""
|
||||||
var value = ""
|
var value = ""
|
||||||
i = header.parseUntil(key, ':')
|
i = header.parseUntil(key, ':')
|
||||||
|
inc(i) # skip :
|
||||||
i += header.skipWhiteSpace(i)
|
i += header.skipWhiteSpace(i)
|
||||||
i += header.parseUntil(value, whitespace, i)
|
i += header.parseUntil(value, {'\c', '\L'}, i)
|
||||||
s.headers[key] = value
|
s.headers[key] = value
|
||||||
else:
|
else:
|
||||||
s.client.close()
|
s.client.close()
|
||||||
|
|
@ -261,14 +269,62 @@ proc next*(s: var TServer) =
|
||||||
return
|
return
|
||||||
|
|
||||||
var i = skipWhitespace(data)
|
var i = skipWhitespace(data)
|
||||||
if skipIgnoreCase(data, "GET") > 0: inc(i, 3)
|
if skipIgnoreCase(data, "GET") > 0:
|
||||||
elif skipIgnoreCase(data, "POST") > 0: inc(i, 4)
|
s.reqMethod = "GET"
|
||||||
|
inc(i, 3)
|
||||||
|
elif skipIgnoreCase(data, "POST") > 0:
|
||||||
|
s.reqMethod = "POST"
|
||||||
|
inc(i, 4)
|
||||||
else:
|
else:
|
||||||
unimplemented(s.client)
|
unimplemented(s.client)
|
||||||
s.client.close()
|
s.client.close()
|
||||||
next(s)
|
next(s)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if s.reqMethod == "POST":
|
||||||
|
# Check for Expect header
|
||||||
|
if s.headers.hasKey("Expect"):
|
||||||
|
if s.headers["Expect"].toLower == "100-continue":
|
||||||
|
s.client.sendStatus("100 Continue")
|
||||||
|
else:
|
||||||
|
s.client.sendStatus("417 Expectation Failed")
|
||||||
|
|
||||||
|
# Read the body
|
||||||
|
# - Check for Content-length header
|
||||||
|
if s.headers.hasKey("Content-Length"):
|
||||||
|
var contentLength = 0
|
||||||
|
if parseInt(s.headers["Content-Length"], contentLength) == 0:
|
||||||
|
badRequest(s.client)
|
||||||
|
s.client.close()
|
||||||
|
next(s)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
var totalRead = 0
|
||||||
|
var totalBody = ""
|
||||||
|
while totalRead < contentLength:
|
||||||
|
var chunkSize = 8000
|
||||||
|
if (contentLength - totalRead) < 8000:
|
||||||
|
chunkSize = (contentLength - totalRead)
|
||||||
|
var bodyData = newString(chunkSize)
|
||||||
|
var octetsRead = s.client.recv(cstring(bodyData), chunkSize)
|
||||||
|
if octetsRead <= 0:
|
||||||
|
s.client.close()
|
||||||
|
next(s)
|
||||||
|
return
|
||||||
|
totalRead += octetsRead
|
||||||
|
totalBody.add(bodyData)
|
||||||
|
if totalBody.len != contentLength:
|
||||||
|
s.client.close()
|
||||||
|
next(s)
|
||||||
|
return
|
||||||
|
|
||||||
|
s.body = totalBody
|
||||||
|
else:
|
||||||
|
badRequest(s.client)
|
||||||
|
s.client.close()
|
||||||
|
next(s)
|
||||||
|
return
|
||||||
|
|
||||||
var L = skipWhitespace(data, i)
|
var L = skipWhitespace(data, i)
|
||||||
inc(i, L)
|
inc(i, L)
|
||||||
# XXX we ignore "HTTP/1.1" etc. for now here
|
# XXX we ignore "HTTP/1.1" etc. for now here
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,10 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
|
||||||
result = i-start
|
result = i-start
|
||||||
token = substr(s, start, i-1)
|
token = substr(s, start, i-1)
|
||||||
|
|
||||||
proc captureBetween*(s: string, first: char, second = '\0', i = 0): string =
|
proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
|
||||||
## Finds the first occurence of ``first``, then returns everything from there
|
## Finds the first occurence of ``first``, then returns everything from there
|
||||||
## up to ``second``(if ``second`` is '\0', then ``first`` is used).
|
## up to ``second``(if ``second`` is '\0', then ``first`` is used).
|
||||||
var i = skipUntil(s, first, i)+1
|
var i = skipUntil(s, first, start)+1+start
|
||||||
result = ""
|
result = ""
|
||||||
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
||||||
|
|
||||||
|
|
|
||||||
33
lib/pure/uri.nim
Normal file
33
lib/pure/uri.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
import strutils
|
||||||
|
type
|
||||||
|
TUrl* = distinct string
|
||||||
|
|
||||||
|
proc `$`*(url: TUrl): string = return string(url)
|
||||||
|
|
||||||
|
proc `/`*(a, b: TUrl): TUrl =
|
||||||
|
## Joins two URLs together, separating them with / if needed.
|
||||||
|
var urlS = $a
|
||||||
|
var bS = $b
|
||||||
|
if urlS == "": return b
|
||||||
|
if urlS[urlS.len-1] != '/':
|
||||||
|
urlS.add('/')
|
||||||
|
if bS[0] == '/':
|
||||||
|
urlS.add(bS.substr(1))
|
||||||
|
else:
|
||||||
|
urlS.add(bs)
|
||||||
|
result = TUrl(urlS)
|
||||||
|
|
||||||
|
proc add*(url: var TUrl, a: TUrl) =
|
||||||
|
## Appends url to url.
|
||||||
|
url = url / a
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
assert($("http://".TUrl / "localhost:5000".TUrl) == "http://localhost:5000")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue