about summary refs log blame commit diff stats
path: root/src/io/loader.nim
blob: 4a1e35e7d605a5258fc9cc6c297329aa20a5b406 (plain) (tree)
1
2
3
4
5
6
7
8
9

              
             

                    
 
                    
              
                 

                   

                 
 

                         
                                     

                                
                              

                           




                             
 





                                                        
                    
                     
                   
       
                     
















                                                             
















                                                                          
                                              
                                         
                                      













                                                                   




                                              
                                                     
       
                                                          
 
                                                             
             
                                        


































                                                        
 
                                   
                               
import options
import streams
import tables
when defined(posix):
  import posix

import bindings/curl
import io/http
import io/process
import io/request
import io/serialize
import types/mime
import types/url

const DefaultHeaders = {
  "User-Agent": "chawan",
  "Accept": "text/html,text/*;q=0.5",
  "Accept-Language": "en;q=1.0",
  "Pragma": "no-cache",
  "Cache-Control": "no-cache",
}.toTable().newHeaderList()

type FileLoader* = ref object
  defaultHeaders*: HeaderList
  process*: int
  istream*: Stream
  ostream*: Stream

proc loadFile(url: Url, ostream: Stream) =
  when defined(windows) or defined(OS2) or defined(DOS):
    let path = url.path.serialize_unicode_dos()
  else:
    let path = url.path.serialize_unicode()
  let istream = newFileStream(path, fmRead)
  if istream == nil:
    ostream.swrite(1)
    ostream.flush()
  else:
    ostream.swrite(0)
    ostream.swrite(200) # ok
    ostream.swrite(guessContentType(path))
    ostream.swrite(none(Url))
    while not istream.atEnd:
      const bufferSize = 4096
      var buffer {.noinit.}: array[bufferSize, char]
      while true:
        let n = readData(istream, addr buffer[0], bufferSize)
        if n == 0:
          break
        ostream.swrite(n)
        ostream.writeData(addr buffer[0], n)
        ostream.flush()
        if n < bufferSize:
          break
      ostream.swrite("")
      ostream.flush()

proc loadResource(loader: FileLoader, request: Request, ostream: Stream) =
  case request.url.scheme
  of "file":
    loadFile(request.url, ostream)
  of "http", "https":
    loadHttp(request, ostream)

proc runFileLoader(loader: FileLoader) =
  if curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK:
    eprint "Failed to initialize libcurl."
    quit(1)
  let istream = newFileStream(stdin)
  let ostream = newFileStream(stdout)
  while true:
    try:
      let request = istream.readRequest()
      for k, v in loader.defaultHeaders.table:
        if k notin request.headers.table:
          request.headers.table[k] = v
      loader.loadResource(request, ostream)
    except IOError:
      # End-of-file, quit.
      # TODO this should be EOFError
      break
  istream.close()
  ostream.close()
  curl_global_cleanup()
  quit(0)

proc doRequest*(loader: FileLoader, request: Request): LoadResult =
  if loader.istream != nil:
    loader.istream.swrite(request)
    loader.istream.flush()
    loader.ostream.sread(result.res)
    if result.res == 0:
      loader.ostream.sread(result.status)
      loader.ostream.sread(result.contenttype)
      loader.ostream.sread(result.redirect)
      result.body = newReadableStream(loader.ostream)
  else:
    raise newException(Defect, "Error: no loader process")

proc newFileLoader*(defaultHeaders: HeaderList): FileLoader =
  new(result)
  result.defaultHeaders = defaultHeaders
  when defined(posix):
    var pipefd_a: array[0..1, cint]
    var pipefd_b: array[0..1, cint]
    if pipe(pipefd_a) == -1:
      eprint "Failed to open pipe."
      quit(1)
    if pipe(pipefd_b) == -1:
      eprint "Failed to open pipe."
      quit(1)
    let pid = doFork()
    if pid == 0:
      # child process
      let readfd = pipefd_a[0] # get read a
      discard close(pipefd_a[1]) # close write a
      let writefd = pipefd_b[1] # get write b
      discard close(pipefd_b[0]) # close read b
      discard dup2(readfd, stdin.getFileHandle())
      discard dup2(writefd, stdout.getFileHandle())
      result.runFileLoader()
    else:
      result.process = pid
      let writefd = pipefd_a[1] # get write a
      discard close(pipefd_a[0]) # close read a
      let readfd = pipefd_b[0] # get read b
      discard close(pipefd_b[1]) # close write b
      var readf: File
      var writef: File
      if not open(readf, FileHandle(readfd), fmRead):
        eprint "Failed to open output handle."
        quit(1)
      if not open(writef, FileHandle(writefd), fmWrite):
        eprint "Failed to open input handle."
        quit(1)
      result.ostream = newFileStream(readf)
      result.istream = newFileStream(writef)

proc newFileLoader*(): FileLoader =
  newFileLoader(DefaultHeaders)