diff options
author | Araq <rumpf_a@web.de> | 2018-09-03 18:29:00 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2018-09-03 18:29:11 +0200 |
commit | 320582a55c9ba1f91d70a3f120413e305fda2962 (patch) | |
tree | 6114d47a5342adfd4d1f472bb3484a73ad11cf06 /examples/httpserver2.nim | |
parent | 1a60ffcf1dc265c6b92dfd757e1bfd5e904c1f3d (diff) | |
download | Nim-320582a55c9ba1f91d70a3f120413e305fda2962.tar.gz |
cleanup Nim's examples/ directory; closes #7725
Diffstat (limited to 'examples/httpserver2.nim')
-rw-r--r-- | examples/httpserver2.nim | 247 |
1 files changed, 0 insertions, 247 deletions
diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim deleted file mode 100644 index 1843ff967..000000000 --- a/examples/httpserver2.nim +++ /dev/null @@ -1,247 +0,0 @@ -import strutils, os, osproc, strtabs, streams, sockets - -const - wwwNL* = "\r\L" - ServerSig = "Server: httpserver.nim/1.0.0" & wwwNL - -type - TRequestMethod = enum reqGet, reqPost - TServer* = object ## contains the current server state - s: Socket - job: seq[TJob] - TJob* = object - client: Socket - process: Process - -# --------------- output messages -------------------------------------------- - -proc sendTextContentType(client: Socket) = - send(client, "Content-type: text/html" & wwwNL) - send(client, wwwNL) - -proc badRequest(client: Socket) = - # 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, "<p>Your browser sent a bad request, " & - "such as a POST without a Content-Length.</p>" & wwwNL) - - -proc cannotExec(client: Socket) = - send(client, "HTTP/1.0 500 Internal Server Error" & wwwNL) - sendTextContentType(client) - send(client, "<P>Error prohibited CGI execution.</p>" & wwwNL) - - -proc headers(client: Socket, 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: Socket, path: string) = - send(client, "HTTP/1.0 404 NOT FOUND" & wwwNL) - send(client, ServerSig) - sendTextContentType(client) - send(client, "<html><title>Not Found</title>" & wwwNL) - send(client, "<body><p>The server could not fulfill" & wwwNL) - send(client, "your request because the resource <b>" & path & "</b>" & wwwNL) - send(client, "is unavailable or nonexistent.</p>" & wwwNL) - send(client, "</body></html>" & wwwNL) - - -proc unimplemented(client: Socket) = - send(client, "HTTP/1.0 501 Method Not Implemented" & wwwNL) - send(client, ServerSig) - sendTextContentType(client) - send(client, "<html><head><title>Method Not Implemented" & - "</title></head>" & - "<body><p>HTTP request method not supported.</p>" & - "</body></HTML>" & wwwNL) - - -# ----------------- file serving --------------------------------------------- - -proc discardHeaders(client: Socket) = skip(client) - -proc serveFile(client: Socket, filename: string) = - discardHeaders(client) - - var f: File - 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: - let err = osLastError() - dealloc(buf) - close(f) - raiseOSError(err) - if bytesread != bufSize: break - dealloc(buf) - close(f) - client.close() - else: - notFound(client, filename) - -# ------------------ CGI execution ------------------------------------------- - -proc executeCgi(server: var TServer, client: Socket, 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 = true - while dataAvail: - dataAvail = recvLine(client, buf) - if buf.len == 0: - break - var L = toLowerAscii(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) - return - - env["REQUEST_METHOD"] = "POST" - env["CONTENT_LENGTH"] = $contentLength - - send(client, "HTTP/1.0 200 OK" & wwwNL) - - var process = startProcess(command=path, env=env) - - var job: TJob - job.process = process - job.client = client - server.job.add(job) - - if meth == reqPost: - # get from client and post to CGI program: - var buf = alloc(contentLength) - if recv(client, buf, contentLength) != contentLength: - let err = osLastError() - dealloc(buf) - raiseOSError(err) - var inp = process.inputStream - inp.writeData(buf, contentLength) - dealloc(buf) - -proc animate(server: var TServer) = - # checks list of jobs, removes finished ones (pretty sloppy by seq copying) - var active_jobs: seq[TJob] = @[] - for i in 0..server.job.len-1: - var job = server.job[i] - if running(job.process): - active_jobs.add(job) - else: - # read process output stream and send it to client - var outp = job.process.outputStream - while true: - var line = outp.readstr(1024) - if line.len == 0: - break - else: - try: - send(job.client, line) - except: - echo("send failed, client diconnected") - close(job.client) - - server.job = active_jobs - -# --------------- Server Setup ----------------------------------------------- - -proc acceptRequest(server: var TServer, client: Socket) = - var cgi = false - var query = "" - var buf = "" - discard recvLine(client, buf) - var path = "" - var data = buf.split() - var meth = reqGet - var q = find(data[1], '?') - - # extract path - if q >= 0: - # strip "?..." from path, this may be found in both POST and GET - path = data[1].substr(0, q-1) - else: - path = data[1] - # path starts with "/", by adding "." in front of it we serve files from cwd - path = "." & path - - echo("accept: " & path) - - if cmpIgnoreCase(data[0], "GET") == 0: - if q >= 0: - cgi = true - query = data[1].substr(q+1) - elif cmpIgnoreCase(data[0], "POST") == 0: - cgi = true - meth = reqPost - else: - unimplemented(client) - - if path[path.len-1] == '/' or existsDir(path): - path = path / "index.html" - - if not existsFile(path): - discardHeaders(client) - notFound(client, path) - client.close() - else: - when defined(Windows): - var ext = splitFile(path).ext.toLowerAscii - if ext == ".exe" or ext == ".cgi": - # XXX: extract interpreter information here? - cgi = true - else: - if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}: - cgi = true - if not cgi: - serveFile(client, path) - else: - executeCgi(server, client, path, query, meth) - -when isMainModule: - var port = 80 - - var server: TServer - server.job = @[] - server.s = socket(AF_INET) - if server.s == invalidSocket: raiseOSError(osLastError()) - server.s.bindAddr(port=Port(port)) - listen(server.s) - echo("server up on port " & $port) - - while true: - # check for new new connection & handle it - var list: seq[Socket] = @[server.s] - if select(list, 10) > 0: - var client: Socket - new(client) - accept(server.s, client) - try: - acceptRequest(server, client) - except: - echo("failed to accept client request") - - # pooling events - animate(server) - # some slack for CPU - sleep(10) - server.s.close() |