1
+ import { field , logger } from "@coder/logger"
1
2
import * as http from "http"
3
+ import * as path from "path"
4
+ import { Readable } from "stream"
5
+ import * as tarFs from "tar-fs"
6
+ import * as zlib from "zlib"
2
7
import { HttpProvider , HttpResponse , Route } from "../http"
3
8
4
9
/**
5
- * Static file HTTP provider. Static requests do not require authentication and
6
- * they only allow access to resources within the application.
10
+ * Static file HTTP provider. Regular static requests (the path is the request
11
+ * itself) do not require authentication and they only allow access to resources
12
+ * within the application. Requests for tars (the path is in a query parameter)
13
+ * do require permissions and can access any directory.
7
14
*/
8
15
export class StaticHttpProvider extends HttpProvider {
9
16
public async handleRequest ( route : Route , request : http . IncomingMessage ) : Promise < HttpResponse > {
10
17
this . ensureMethod ( request )
18
+
19
+ if ( typeof route . query . tar === "string" ) {
20
+ this . ensureAuthenticated ( request )
21
+ return this . getTarredResource ( request , route . query . tar )
22
+ }
23
+
11
24
const response = await this . getReplacedResource ( route )
12
25
if ( ! this . isDev ) {
13
26
response . cache = true
@@ -30,4 +43,23 @@ export class StaticHttpProvider extends HttpProvider {
30
43
}
31
44
return this . getResource ( this . rootPath , ...split )
32
45
}
46
+
47
+ /**
48
+ * Tar up and stream a directory.
49
+ */
50
+ private async getTarredResource ( request : http . IncomingMessage , ...parts : string [ ] ) : Promise < HttpResponse > {
51
+ const filePath = path . join ( ...parts )
52
+ let stream : Readable = tarFs . pack ( filePath )
53
+ const headers : http . OutgoingHttpHeaders = { }
54
+ if ( request . headers [ "accept-encoding" ] && request . headers [ "accept-encoding" ] . includes ( "gzip" ) ) {
55
+ logger . debug ( "gzipping tar" , field ( "filePath" , filePath ) )
56
+ const compress = zlib . createGzip ( )
57
+ stream . pipe ( compress )
58
+ stream . on ( "error" , ( error ) => compress . destroy ( error ) )
59
+ stream . on ( "close" , ( ) => compress . end ( ) )
60
+ stream = compress
61
+ headers [ "content-encoding" ] = "gzip"
62
+ }
63
+ return { stream, filePath, mime : "application/x-tar" , cache : true , headers }
64
+ }
33
65
}
0 commit comments