@@ -9,41 +9,137 @@ import GCDWebServer
9
9
10
10
class ReverseProxyServer {
11
11
12
- struct Event {
12
+ struct Event : CustomStringConvertible {
13
13
14
14
enum Kind {
15
15
case manifestRequestReceived
16
16
case segmentRequestReceived
17
- case segmentCacheMiss( key: URLRequest )
18
- case segmentCacheHit( key: URLRequest )
19
- case segmentCacheStored( key: URLRequest )
17
+ case segmentCacheMiss(
18
+ key: URLRequest
19
+ )
20
+ case segmentCacheHit(
21
+ key: URLRequest
22
+ )
23
+ case segmentCacheStored(
24
+ key: URLRequest ,
25
+ cacheDiskUsageInBytes: Int ,
26
+ segmentSizeInBytes: Int
27
+ )
20
28
}
21
29
22
30
let originURL : URL
23
31
24
32
let kind : Kind
25
33
34
+ var description : String {
35
+ switch kind {
36
+ case . manifestRequestReceived:
37
+ return " Manifest Request - Origin URL: \( originURL. absoluteString) "
38
+ case . segmentRequestReceived:
39
+ return " Segment Request Received - Origin URL: \( originURL. absoluteString) "
40
+ case . segmentCacheMiss( key: let key) :
41
+ return " Segment Cache Miss - Key: \( key) Origin URL: \( originURL. absoluteString) "
42
+ case . segmentCacheHit( key: let key) :
43
+ return " Segment Cache Hit - Key: \( key) Origin URL: \( originURL. absoluteString) "
44
+ case . segmentCacheStored( key: let key, cacheDiskUsageInBytes: let cacheDiskUsageInBytes, segmentSizeInBytes: let segmentSizeInBytes) :
45
+ return " Segment Cache Stored - Key: \( key) CacheDiskUsageInBytes: \( cacheDiskUsageInBytes) SegmentSizeInBytes: \( segmentSizeInBytes) Origin URL: \( originURL. absoluteString) "
46
+ }
47
+ }
48
+
26
49
}
27
50
28
51
class EventRecorder {
29
52
30
53
func didRecord( event: Event ) {
54
+ print ( " RPS - \( Date ( ) ) - \( event. description) " )
55
+ }
31
56
32
- switch event. kind {
33
- case . manifestRequestReceived:
34
- print ( " RPS Manifest Request: \( event. originURL. absoluteString) " )
35
- case . segmentRequestReceived:
36
- print ( " RPS Segment Request: \( event. originURL. absoluteString) " )
37
- case . segmentCacheMiss( key: let key) :
38
- print ( " RPS Cache Miss Key: \( key) Origin: \( event. originURL. absoluteString) " )
39
- case . segmentCacheHit( key: let key) :
40
- print ( " RPS Cache Hit Key: \( key) Origin: \( event. originURL. absoluteString) " )
41
- case . segmentCacheStored( key: let key) :
42
- print ( " RPS Cache Stored Key: \( key) Origin: \( event. originURL. absoluteString) " )
57
+ }
58
+
59
+ class ManifestReversifier {
60
+ let port : UInt = 1234
61
+ let originURLKey : String = " __hls_origin_url "
62
+
63
+ func reversifyManifest(
64
+ encodedManifest: Data ,
65
+ manifestOriginURL: URL
66
+ ) {
67
+ let originalManifest = String (
68
+ data: encodedManifest,
69
+ encoding: . utf8
70
+ )
71
+
72
+ let parsedManifest = originalManifest?
73
+ . components ( separatedBy: . newlines)
74
+ . map { line in self . processPlaylistLine ( line, forOriginURL: manifestOriginURL) }
75
+ . joined ( separator: " \n " )
76
+ }
77
+
78
+ func processPlaylistLine(
79
+ _ line: String ,
80
+ forOriginURL originURL: URL
81
+ ) -> String {
82
+ guard !line. isEmpty else { return line }
83
+
84
+ if line. hasPrefix ( " # " ) {
85
+ return lineByReplacingURI ( line: line, forOriginURL: originURL)
43
86
}
44
87
88
+ if let originalSegmentURL = absoluteURL ( from: line, forOriginURL: originURL) ,
89
+ let reverseProxyURL = reverseProxyURL ( from: originalSegmentURL) {
90
+ return reverseProxyURL. absoluteString
91
+ }
92
+ return line
93
+ }
94
+
95
+ func lineByReplacingURI(
96
+ line: String ,
97
+ forOriginURL originURL: URL
98
+ ) -> String {
99
+ let uriPattern = try ! NSRegularExpression ( pattern: " URI= \" ([^ \" ]*) \" " )
100
+ let lineRange = NSRange ( location: 0 , length: line. count)
101
+ guard let result = uriPattern. firstMatch ( in: line, options: [ ] , range: lineRange) else { return line }
102
+
103
+ let uri = ( line as NSString ) . substring ( with: result. range ( at: 1 ) )
104
+ guard let absoluteURL = absoluteURL ( from: uri, forOriginURL: originURL) else { return line }
105
+ guard let reverseProxyURL = reverseProxyURL ( from: absoluteURL) else { return line }
106
+
107
+ return uriPattern. stringByReplacingMatches ( in: line, options: [ ] , range: lineRange, withTemplate: " URI= \" \( reverseProxyURL. absoluteString) \" " )
45
108
}
46
109
110
+ func absoluteURL( from line: String , forOriginURL originURL: URL ) -> URL ? {
111
+ if line. hasPrefix ( " http:// " ) || line. hasPrefix ( " https:// " ) {
112
+ return URL ( string: line)
113
+ }
114
+
115
+ guard let scheme = originURL. scheme,
116
+ let host = originURL. host
117
+ else {
118
+ print ( " Error: bad url " )
119
+ return nil
120
+ }
121
+
122
+ let path : String
123
+ if line. hasPrefix ( " / " ) {
124
+ path = line
125
+ } else {
126
+ path = originURL. deletingLastPathComponent ( ) . appendingPathComponent ( line) . path
127
+ }
128
+
129
+ return URL ( string: scheme + " :// " + host + path) ? . standardized
130
+ }
131
+
132
+ func reverseProxyURL( from originURL: URL ) -> URL ? {
133
+ guard var components = URLComponents ( url: originURL, resolvingAgainstBaseURL: false ) else { return nil }
134
+ components. scheme = " http "
135
+ components. host = " 127.0.0.1 "
136
+ components. port = Int ( port)
137
+
138
+ let originURLQueryItem = URLQueryItem ( name: originURLKey, value: originURL. absoluteString)
139
+ components. queryItems = ( components. queryItems ?? [ ] ) + [ originURLQueryItem]
140
+
141
+ return components. url
142
+ }
47
143
}
48
144
49
145
var session : URLSession = . shared
@@ -52,16 +148,19 @@ class ReverseProxyServer {
52
148
var segmentCache : URLCache
53
149
54
150
var eventRecorder : EventRecorder = EventRecorder ( )
151
+ var manifestReversifier : ManifestReversifier = ManifestReversifier ( )
55
152
56
- private let port : UInt = 1234
153
+ let port : UInt = 1234
57
154
let originURLKey : String = " __hls_origin_url "
58
155
156
+ let defaultDiskCapacity = 256_000_000
157
+
59
158
init ( ) {
60
159
self . webServer = GCDWebServer ( )
61
160
62
161
self . segmentCache = URLCache (
63
162
memoryCapacity: 0 ,
64
- diskCapacity: 10_000_000
163
+ diskCapacity: defaultDiskCapacity
65
164
)
66
165
}
67
166
@@ -240,6 +339,7 @@ class ReverseProxyServer {
240
339
let task = self . session. dataTask (
241
340
with: reverseProxyRequest
242
341
) { data, response, error in
342
+
243
343
guard let data = data, let response = response else {
244
344
return completion ( GCDWebServerErrorResponse ( statusCode: 500 ) )
245
345
}
@@ -262,11 +362,17 @@ class ReverseProxyServer {
262
362
for: strippedRequest
263
363
)
264
364
365
+ let segmentSizeInBytes = data. count
366
+
367
+ let cacheDiskUsageInBytes = self . segmentCache. currentDiskUsage
368
+
265
369
self . eventRecorder. didRecord (
266
370
event: Event (
267
371
originURL: originURL,
268
372
kind: . segmentCacheStored(
269
- key: strippedRequest
373
+ key: strippedRequest,
374
+ cacheDiskUsageInBytes: cacheDiskUsageInBytes,
375
+ segmentSizeInBytes: segmentSizeInBytes
270
376
)
271
377
)
272
378
)
0 commit comments