requesthandler.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * DudelDu
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package dudeldu
  11. import (
  12. "bytes"
  13. "encoding/base64"
  14. "fmt"
  15. "io"
  16. "log"
  17. "math"
  18. "net"
  19. "regexp"
  20. "strconv"
  21. "strings"
  22. "devt.de/krotik/common/datautil"
  23. )
  24. /*
  25. MaxRequestSize is the maximum size for a request
  26. */
  27. const MaxRequestSize = 1024
  28. /*
  29. MetaDataInterval is the data interval in which meta data is send
  30. */
  31. var MetaDataInterval uint64 = 65536
  32. /*
  33. peerNoAuthTimeout is the time in seconds a peer can open new connections without
  34. sending new authentication information.
  35. */
  36. const peerNoAuthTimeout = 10
  37. /*
  38. MaxMetaDataSize is the maximum size for meta data (everything over is truncated)
  39. Must be a multiple of 16 which fits into one byte. Maximum: 16 * 255 = 4080
  40. */
  41. var MaxMetaDataSize = 4080
  42. /*
  43. requestPathPattern is the pattern which is used to extract the requested path
  44. (i case-insensitive / m multi-line mode: ^ and $ match begin/end line)
  45. */
  46. var requestPathPattern = regexp.MustCompile("(?im)get\\s+([^\\s]+).*")
  47. /*
  48. requestOffsetPattern is the pattern which is used to extract the requested offset
  49. (i case-insensitive / m multi-line mode: ^ and $ match begin/end line)
  50. */
  51. var requestOffsetPattern = regexp.MustCompile("(?im)^Range: bytes=([0-9]+)-.*$")
  52. /*
  53. requestAuthPattern is the pattern which is used to extract the request authentication
  54. (i case-insensitive / m multi-line mode: ^ and $ match begin/end line)
  55. */
  56. var requestAuthPattern = regexp.MustCompile("(?im)^Authorization: Basic (\\S+).*$")
  57. /*
  58. Print logger method. Using a custom type so it can be customized.
  59. */
  60. var Print = log.Print
  61. /*
  62. DebugOutput is a flag to enable additional debugging output
  63. */
  64. var DebugOutput = false
  65. /*
  66. DefaultRequestHandler data structure
  67. */
  68. type DefaultRequestHandler struct {
  69. PlaylistFactory PlaylistFactory // Factory for playlists
  70. ServeRequest func(c net.Conn, path string,
  71. metaDataSupport bool, offset int, auth string) // Function to serve requests
  72. loop bool // Flag if the playlist should be looped
  73. LoopTimes int // Number of loops -1 loops forever
  74. shuffle bool // Flag if the playlist should be shuffled
  75. auth string // Required (basic) authentication string - may be empty
  76. authPeers *datautil.MapCache // Peers which have been authenticated
  77. }
  78. /*
  79. NewDefaultRequestHandler creates a new default request handler object.
  80. */
  81. func NewDefaultRequestHandler(pf PlaylistFactory, loop bool, shuffle bool, auth string) *DefaultRequestHandler {
  82. drh := &DefaultRequestHandler{
  83. PlaylistFactory: pf,
  84. loop: loop,
  85. LoopTimes: -1,
  86. shuffle: shuffle,
  87. auth: auth,
  88. authPeers: datautil.NewMapCache(0, peerNoAuthTimeout),
  89. }
  90. drh.ServeRequest = drh.defaultServeRequest
  91. return drh
  92. }
  93. /*
  94. HandleRequest handles requests from streaming clients. It tries to extract
  95. the path and if meta data is supported. Once a request has been successfully
  96. decoded ServeRequest is called. The connection is closed once HandleRequest
  97. finishes.
  98. */
  99. func (drh *DefaultRequestHandler) HandleRequest(c net.Conn, nerr net.Error) {
  100. if DebugOutput {
  101. Print("Handling request from: ", c.RemoteAddr())
  102. }
  103. defer func() {
  104. c.Close()
  105. }()
  106. // Check if there was an error
  107. if nerr != nil {
  108. Print(nerr)
  109. return
  110. }
  111. rbuf := make([]byte, 512, 512)
  112. var buf bytes.Buffer
  113. // Decode request
  114. n, err := c.Read(rbuf)
  115. for n > 0 || (err != nil && err != io.EOF) {
  116. // Do some error checking
  117. if err != nil {
  118. Print(err)
  119. return
  120. } else if buf.Len() > MaxRequestSize {
  121. Print("Illegal request: Request is too long")
  122. return
  123. }
  124. buf.Write(rbuf[:n])
  125. if strings.Contains(string(rbuf), "\r\n\r\n") {
  126. break
  127. }
  128. n, err = c.Read(rbuf)
  129. }
  130. // Add ending sequence in case the client "forgets"
  131. bufStr := buf.String() + "\r\n\r\n"
  132. // Determine the remote string
  133. clientString := "-"
  134. if c.RemoteAddr() != nil {
  135. clientString, _, _ = net.SplitHostPort(c.RemoteAddr().String())
  136. }
  137. if DebugOutput {
  138. Print("Client:", c.RemoteAddr(), " Request:", bufStr)
  139. }
  140. if i := strings.Index(bufStr, "\r\n\r\n"); i >= 0 {
  141. bufStr = strings.TrimSpace(bufStr[:i])
  142. // Check authentication
  143. auth := ""
  144. res := requestAuthPattern.FindStringSubmatch(bufStr)
  145. origBufStr, hasAuth := drh.authPeers.Get(clientString)
  146. if len(res) > 1 {
  147. // Decode authentication
  148. b, err := base64.StdEncoding.DecodeString(res[1])
  149. if err != nil {
  150. drh.writeUnauthorized(c)
  151. Print("Invalid request (cannot decode authentication): ", bufStr)
  152. return
  153. }
  154. auth = string(b)
  155. // Authorize request
  156. if auth != drh.auth && drh.auth != "" {
  157. if DebugOutput {
  158. Print("Wrong authentication:", string(b))
  159. }
  160. drh.writeUnauthorized(c)
  161. return
  162. }
  163. // Peer is now authorized store this so it can connect again
  164. drh.authPeers.Put(clientString, bufStr)
  165. } else if drh.auth != "" && !hasAuth {
  166. // No authorization
  167. if DebugOutput {
  168. Print("No authentication found")
  169. }
  170. drh.writeUnauthorized(c)
  171. return
  172. } else if bufStr == "" && hasAuth {
  173. // Workaround for strange clients like VLC which send first the
  174. // authentication then connect again on a different port and just
  175. // expect the stream
  176. bufStr = origBufStr.(string)
  177. // Get again the authentication
  178. res = requestAuthPattern.FindStringSubmatch(bufStr)
  179. if len(res) > 1 {
  180. if b, err := base64.StdEncoding.DecodeString(res[1]); err == nil {
  181. auth = string(b)
  182. }
  183. }
  184. }
  185. // Check if the client supports meta data
  186. metaDataSupport := false
  187. if strings.Contains(strings.ToLower(bufStr), "icy-metadata: 1") {
  188. metaDataSupport = true
  189. }
  190. // Extract offset
  191. offset := 0
  192. res = requestOffsetPattern.FindStringSubmatch(bufStr)
  193. if len(res) > 1 {
  194. if o, err := strconv.Atoi(res[1]); err == nil {
  195. offset = o
  196. }
  197. }
  198. // Extract the path
  199. res = requestPathPattern.FindStringSubmatch(bufStr)
  200. if len(res) > 1 {
  201. // Now serve the request
  202. drh.ServeRequest(c, res[1], metaDataSupport, offset, auth)
  203. return
  204. }
  205. }
  206. Print("Invalid request: ", bufStr)
  207. }
  208. /*
  209. defaultServeRequest is called once a request was successfully decoded.
  210. */
  211. func (drh *DefaultRequestHandler) defaultServeRequest(c net.Conn, path string, metaDataSupport bool, offset int, auth string) {
  212. var err error
  213. if DebugOutput {
  214. Print("Serve request path:", path, " Metadata support:", metaDataSupport, " Offset:", offset)
  215. }
  216. pl := drh.PlaylistFactory.Playlist(path, drh.shuffle)
  217. if pl == nil {
  218. // Stream was not found - no error checking here (don't care)
  219. drh.writeStreamNotFoundResponse(c)
  220. return
  221. }
  222. err = drh.writeStreamStartResponse(c, pl.Name(), pl.ContentType(), metaDataSupport)
  223. clientWritten := 0
  224. var writtenBytes uint64
  225. currentPlaying := ""
  226. frameOffset := offset
  227. for {
  228. for !pl.Finished() {
  229. if DebugOutput {
  230. playingString := fmt.Sprintf("%v - %v", pl.Title(), pl.Artist())
  231. if playingString != currentPlaying {
  232. currentPlaying = playingString
  233. Print("Written bytes: ", writtenBytes)
  234. Print("Sending: ", currentPlaying)
  235. }
  236. }
  237. // Check if there were any errors
  238. if err != nil {
  239. Print(err)
  240. return
  241. }
  242. frame, err := pl.Frame()
  243. // Handle offsets
  244. if frameOffset > 0 && err == nil {
  245. for frameOffset > len(frame) && err == nil {
  246. frameOffset -= len(frame)
  247. frame, err = pl.Frame()
  248. }
  249. if err == nil {
  250. frame = frame[frameOffset:]
  251. frameOffset = 0
  252. if len(frame) == 0 {
  253. frame, err = pl.Frame()
  254. }
  255. }
  256. }
  257. if frame == nil {
  258. if !pl.Finished() {
  259. Print(fmt.Sprintf("Empty frame for: %v - %v (Error: %v)", pl.Title(), pl.Artist(), err))
  260. }
  261. continue
  262. } else if err != nil {
  263. if err != ErrPlaylistEnd {
  264. Print(fmt.Sprintf("Error while retrieving playlist data: %v", err))
  265. }
  266. err = nil
  267. }
  268. // Check if meta data should be send
  269. if metaDataSupport && writtenBytes+uint64(len(frame)) >= MetaDataInterval {
  270. // Write rest data before sending meta data
  271. preMetaDataLength := MetaDataInterval - writtenBytes
  272. if preMetaDataLength > 0 {
  273. if err == nil {
  274. _, err = c.Write(frame[:preMetaDataLength])
  275. frame = frame[preMetaDataLength:]
  276. writtenBytes += preMetaDataLength
  277. }
  278. }
  279. if err == nil {
  280. // Write meta data - no error checking (next write should fail)
  281. drh.writeStreamMetaData(c, pl)
  282. // Write rest of the frame
  283. c.Write(frame)
  284. writtenBytes += uint64(len(frame))
  285. }
  286. writtenBytes -= MetaDataInterval
  287. } else {
  288. // Just write the frame to the client
  289. if err == nil {
  290. clientWritten, _ = c.Write(frame)
  291. // Abort if the client does not accept more data
  292. if clientWritten == 0 && len(frame) > 0 {
  293. Print(fmt.Sprintf("Could not write to client - closing connection"))
  294. return
  295. }
  296. }
  297. pl.ReleaseFrame(frame)
  298. writtenBytes += uint64(len(frame))
  299. }
  300. }
  301. // Handle looping - do not loop if close returns an error
  302. if pl.Close() != nil || !drh.loop {
  303. break
  304. } else if drh.LoopTimes != -1 {
  305. drh.LoopTimes--
  306. if drh.LoopTimes == 0 {
  307. break
  308. }
  309. }
  310. }
  311. if DebugOutput {
  312. Print("Serve request path:", path, " complete")
  313. }
  314. }
  315. /*
  316. writeStreamMetaData writes meta data information into the stream.
  317. */
  318. func (drh *DefaultRequestHandler) writeStreamMetaData(c net.Conn, playlist Playlist) {
  319. streamTitle := fmt.Sprintf("StreamTitle='%v - %v';", playlist.Title(), playlist.Artist())
  320. // Truncate stream title if necessary
  321. if len(streamTitle) > MaxMetaDataSize {
  322. streamTitle = streamTitle[:MaxMetaDataSize-2] + "';"
  323. }
  324. // Calculate the meta data frame size as a multiple of 16
  325. metaDataFrameSize := byte(math.Ceil(float64(len(streamTitle)) / 16.0))
  326. // Write meta data to the client
  327. metaData := make([]byte, 16.0*metaDataFrameSize+1, 16.0*metaDataFrameSize+1)
  328. metaData[0] = metaDataFrameSize
  329. copy(metaData[1:], streamTitle)
  330. c.Write(metaData)
  331. }
  332. /*
  333. writeStreamStartResponse writes the start response to the client.
  334. */
  335. func (drh *DefaultRequestHandler) writeStreamStartResponse(c net.Conn,
  336. name, contentType string, metaDataSupport bool) error {
  337. c.Write([]byte("ICY 200 OK\r\n"))
  338. c.Write([]byte(fmt.Sprintf("Content-Type: %v\r\n", contentType)))
  339. c.Write([]byte(fmt.Sprintf("icy-name: %v\r\n", name)))
  340. if metaDataSupport {
  341. c.Write([]byte("icy-metadata: 1\r\n"))
  342. c.Write([]byte(fmt.Sprintf("icy-metaint: %v\r\n", MetaDataInterval)))
  343. }
  344. _, err := c.Write([]byte("\r\n"))
  345. return err
  346. }
  347. /*
  348. writeStreamNotFoundResponse writes the not found response to the client.
  349. */
  350. func (drh *DefaultRequestHandler) writeStreamNotFoundResponse(c net.Conn) error {
  351. _, err := c.Write([]byte("HTTP/1.1 404 Not found\r\n\r\n"))
  352. return err
  353. }
  354. /*
  355. writeUnauthorized writes the Unauthorized response to the client.
  356. */
  357. func (drh *DefaultRequestHandler) writeUnauthorized(c net.Conn) error {
  358. _, err := c.Write([]byte("HTTP/1.1 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"DudelDu Streaming Server\"\r\n\r\n"))
  359. return err
  360. }