WebServer exampleBrowse
Last updated at 9:53 am UTC on 16 March 2019
WebServer exampleBrowse
"This example implements a simple http server allowing to view
and download files (like browsing ftp sites etc)."
| server port |
port := 9999.
server := WebServer reset default.
server listenOn: port.
server addService: '/' action:[:req| self browseRequest: req].
#browseRequest
The implementation of #browserRequest needs to be changed if you want to server another directory
browseRequest: request
"Handle an HTTP request for browsing some resource"
| path fd entry file |
"Extract the file path from the request"
path := request url findTokens: '/'.
path ifEmpty:[^self browseDir: FileDirectory default request: request.].
"Find the directory entry for the resource"
fd := path allButLast inject: FileDirectory default into:[:dir :part| dir directoryNamed: part].
entry := fd entryAt: path last ifAbsent:[^request send404Response].
"Reply with the proper resource"
entry isDirectory ifTrue:[
"Send file listing. We handle this synchronously for simplicity."
self browseDir: (fd directoryNamed: entry name) request: request.
] ifFalse:[
"Send file content. Since files can be large, we fork this off.
However, since we forked it, we need to handle two different
possible conditions:
- closing the file in case of error
- handling errors when sending it
This makes the code below a bit ugly"
[[
file := fd readOnlyFileNamed: entry name.
[self browseFile: file request: request.]
ensure:[file close] "close file even in case of error"
] on: Error do:[] "ignore errors altogether"
] fork. "fork it"
].
#browseDir:request:
browseDir: directory request: request
"Responds with a directory listing back to the original request"
| entries listing url |
url := request url.
(url endsWith: '/') ifTrue:[url := url allButLast].
entries := directory entries sort:[:e1 :e2|
e1 isDirectory = e2 isDirectory
ifTrue:[e1 name <= e2 name]
ifFalse:[e2 isDirectory]].
listing := String streamContents:[:s|
s nextPutAll:'<html><head><title>Index of ', request url,'</title></head><body>'.
s nextPutAll:'<h1>Index of ', request url,'</h1>'.
s nextPutAll:'<pre> Name Last modified Size Description<hr>'.
s nextPutAll: '[up] <a href="', (url copyUpToLast: $/),'">parent</a>'; crlf.
entries do:[:e|
s nextPutAll:(e isDirectory ifTrue:['[dir] '] ifFalse:[' ']).
s nextPutAll: '<a href="', url, '/', e name, '">'.
s nextPutAll: (e name truncateTo: 30).
s nextPutAll: '</a>'.
e name size < 30 ifTrue:[s nextPutAll: (String new: 30 - e name size withAll: $ )].
s nextPutAll: (e modificationDateAndTime asString padded: #right to: 20 with: $ ).
s nextPutAll: (e fileSize asString padded: #right to: 20 with: $ ).
s crlf.
].
].
request
send200Response: (listing convertToWithConverter: UTF8TextConverter new)
contentType: 'text/html; charset=utf-8'.