Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2017
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
IO 4.67 KB | None | 0 0
  1. #!/usr/bin/env io
  2.  
  3. urlEscapes := Map with("%20", " ", "%3C", "<", "%3E", ">", "%23", "#", "%25", "%", "%7B", "{", "%7D", "}", "%7C", "|", "%5C", "\\", "%5E", "^", "%7E", "~", "%5B", "[", "%5D", "]", "%60", "`", "%3B", ";", "%2F", "/", "%3F", "?", "%3A", ":", "%40", "@", "%3D", "=", "%26", "&", "%24", "$")
  4.  
  5. Sequence unescapeURL := method(
  6.     replaceMap(urlEscapes)
  7. )
  8.  
  9. mimeTypes := Map with("html", "text/html", "txt", "text/plain", "io", "text/plain")
  10.  
  11. Sequence corespondingMIMEType := method(
  12.     mimeTypes at(self)
  13. )
  14.  
  15.  
  16. WebRequest := Object clone do(
  17.     cache := Map clone
  18.    
  19.     handleSocket := method(socket, server,
  20.        
  21.         socket streamReadNextChunk
  22.         if(socket isOpen == false, return)
  23.         buf := socket readBuffer
  24.        
  25.         closeConnection := buf betweenSeq("Connection: ", "\n") == "close"
  26.        
  27.         if (buf betweenSeq("Host: ", "\n") == nil,
  28.             stream streamWrite(responseHeader(400, "Bad Request", "text/html", nil, true) .. noHostMessage)
  29.             return
  30.         )
  31.        
  32.         request := buf betweenSeq("GET ", " HTTP") asMutable
  33.         if (request == nil, return)
  34.        
  35.         if(request beginsWithSeq("/"), request lstrip("/"))
  36.         if(request size == 0, request appendPathSeq("."))
  37.         request unescapeURL
  38.  
  39.         cacheItem := cache atIfAbsentPut(request,
  40.             writeln("caching ", request)
  41.             file := File with(request)
  42.             if (file exists,
  43.                 // check if request is a directory, if so append index.html and build an index
  44.                 // cache items are lists, first is the data, then number of times requested
  45.                 if(file isDirectory,
  46.                     indexData := generateIndexForPath(request)
  47.                     request appendPathSeq("index.html")
  48.                     if (indexData, list(indexData, 0), nil)
  49.                 ,
  50.                     list(file contents, 0))
  51.             ,      
  52.                 nil)
  53.         )
  54.        
  55.         closeConnection = true // persistent connections are not working yet
  56.         if(cacheItem, okResponse(cacheItem, request, closeConnection), notFoundResponse(request)) foreach(part,
  57.             socket streamWrite(part))
  58.        
  59.         if (closeConnection or(cacheItem not),
  60.             socket close
  61.         )
  62.     )
  63.  
  64.    
  65.     generateIndexForPath := method(dirPath,
  66.         basePath := Directory currentWorkingDirectory .. "/"
  67.         dirPath = if(dirPath not or(dirPath size == 0), basePath, basePath cloneAppendPath(dirPath))
  68.         dir := Directory with(dirPath)
  69.         heading := "Index of " .. dir path afterSeq(basePath)
  70.         html := "<html><body><h2>#{heading}</h2><br><ul>" asMutable interpolateInPlace
  71.         if (dir isAccessible,
  72.             dir items foreach(item,
  73.                 if (item name beginsWithSeq(".") not,
  74.                     html appendSeq("<li><a href=\"#{item path afterSeq(basePath)}\">#{item name}</a>" interpolate)
  75.                 )
  76.             )
  77.         )
  78.         html appendSeq("</ul></body></html>")
  79.     )
  80.  
  81.    
  82.     okResponse := method(cacheItem, request, closeConnection,
  83.         data := cacheItem first
  84.         cacheItem push(cacheItem pop + 1)
  85.         range := call sender buf betweenSeq("Range: bytes=", "\n")
  86.         data = if(range, byteRange(range, data), data)
  87.         list(responseHeader(200, "OK", mimeTypeForFile(request), data size,closeConnection), data)
  88.     )
  89.    
  90.    
  91.     mimeTypeForFile := method(fileName,
  92.         fileName pathExtension corespondingMIMEType
  93.     )
  94.  
  95.  
  96.     byteRange := method(range, data,
  97.         ranges := range split("-")
  98.         if (ranges size == 0, return "")
  99.         data inclusiveSlice(ranges first asNumber, if(ranges size == 2, ranges last asNumber, data size))
  100.     )
  101.    
  102.    
  103.     notFoundResponse := method(request,
  104.         list(responseHeader(404, "Not Found", "text/html", nil, true), "<html><body><h2>Not Found</h2>#{request} was not found.</body></html>" interpolate)
  105.     )
  106.    
  107.  
  108.     responseHeader := method(code, reason, contentType, length, close,
  109.         "HTTP/1.1 #{code asString} #{reason}\r\nDate: #{formattedDate}" interpolate .. if(contentType, "\r\nContent-Type: " .. contentType, "") .. if(length, "\r\nContent-Length: " .. length asString, "") .. if(close, "\r\nConnection: close", "") .. "\r\n\n"
  110.     )
  111.  
  112.  
  113.     formattedDate := method(
  114.         Date clone now convertToUTC asString("%a, %d %b %Y %H:%M:%S %Z")
  115.     )
  116.    
  117.    
  118.     noHostMessage := "<html><body><h2>No Host: header received</h2>HTTP 1.1 requests must include the Host: header.</body></html>"
  119.  
  120.  
  121.     checkCache := method(
  122.         if (cache size > 100,
  123.             // remove least commonly requested items
  124.             cache foreach(k, v,
  125.                 if (v last < 4, cache removeAt(k))
  126.             )
  127.         )
  128.     )
  129. )
  130.  
  131.  
  132. /*FileChunk := Object clone do(
  133.     chunkForFile := method(file, startByte, length,
  134.         chunk := clone
  135.         chunk buffer := file clone setPosition(startBytes) futureSend(readStringOfLength(length))
  136.         chunk asString := buffer
  137.         chunk
  138.     )
  139. )*/
  140.  
  141.  
  142. WebServer := Server clone do(
  143.     setPort(7777)
  144.     socket setHost("127.0.0.1")
  145.     reqCount := 0
  146.     handleSocket := method(socket,
  147.         WebRequest clone @handleSocket(socket, self)
  148.         reqCount = reqCount + 1
  149.         if (reqCount > 250, WebRequest checkCache; reqCount = 0)
  150.     )
  151. ) start
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement