links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
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].


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: 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.

		send200Response: (listing convertToWithConverter: UTF8TextConverter new) 
		contentType: 'text/html; charset=utf-8'.