requesthandler.go 11 KB

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