# # # Nimrod's Runtime Library # (c) Copyright 2011 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements a simple HTTP-Server. ## ## Example: ## ## .. code-block:: nimrod ## import strutils, sockets, httpserver ## ## var counter = 0 ## proc handleRequest(client: TSocket, path, query: string): bool {.procvar.} = ## inc(counter) ## client.send("Hello for the $#th time." % $counter & wwwNL) ## return false # do not stop processing ## ## run(handleRequest, TPort(80)) ## import parseutils, strutils, os, osproc, strtabs, streams, sockets const wwwNL* = "\r\L" ServerSig = "Server: httpserver.nim/1.0.0" & wwwNL # --------------- output messages -------------------------------------------- proc sendTextContentType(client: TSocket) = send(client, "Content-type: text/html" & wwwNL) send(client, wwwNL) proc badRequest(client: TSocket) = # Inform the client that a request it has made has a problem. send(client, "HTTP/1.0 400 BAD REQUEST" & wwwNL) sendTextContentType(client) send(client, "

Your browser sent a bad request, " & "such as a POST without a Content-Length." & wwwNL) proc cannotExec(client: TSocket) = send(client, "HTTP/1.0 500 Internal Server Error" & wwwNL) sendTextContentType(client) send(client, "

Error prohibited CGI execution." & wwwNL) proc headers(client: TSocket, filename: string) = # XXX could use filename to determine file type send(client, "HTTP/1.0 200 OK" & wwwNL) send(client, ServerSig) sendTextContentType(client) proc notFound(client: TSocket) = send(client, "HTTP/1.0 404 NOT FOUND" & wwwNL) send(client, ServerSig) sendTextContentType(client) send(client, "Not Found" & wwwNL) send(client, "

The server could not fulfill" & wwwNL) send(client, "your request because the resource specified" & wwwNL) send(client, "is unavailable or nonexistent.

" & wwwNL) send(client, "" & wwwNL) proc unimplemented(client: TSocket) = send(client, "HTTP/1.0 501 Method Not Implemented" & wwwNL) send(client, ServerSig) sendTextContentType(client) send(client, "Method Not Implemented" & "" & "

HTTP request method not supported.

" & "" & wwwNL) # ----------------- file serving --------------------------------------------- proc discardHeaders(client: TSocket) = skip(client) proc serveFile*(client: TSocket, filename: string) = ## serves a file to the client. when false: discardHeaders(client) var f: TFile if open(f, filename): headers(client, filename) const bufSize = 8000 # != 8K might be good for memory manager var buf = alloc(bufsize) while True: var bytesread = readBuffer(f, buf, bufsize) if bytesread > 0: var byteswritten = send(client, buf, bytesread) if bytesread != bytesWritten: dealloc(buf) close(f) OSError() if bytesread != bufSize: break dealloc(buf) close(f) else: notFound(client) # ------------------ CGI execution ------------------------------------------- type TRequestMethod = enum reqGet, reqPost proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = var env = newStringTable(modeCaseInsensitive) var contentLength = -1 case meth of reqGet: discardHeaders(client) env["REQUEST_METHOD"] = "GET" env["QUERY_STRING"] = query of reqPost: var buf = "" var dataAvail = false while dataAvail: dataAvail = recvLine(client, buf) var L = toLower(buf) if L.startsWith("content-length:"): var i = len("content-length:") while L[i] in Whitespace: inc(i) contentLength = parseInt(substr(L, i)) if contentLength < 0: badRequest(client