github.com/ssetin/penguincast@v0.2.0/src/server/server.go (about) 1 // Package iceserver - icecast streaming server 2 package iceserver 3 4 import ( 5 "context" 6 "log" 7 "net/http" 8 "os" 9 "os/signal" 10 "path" 11 "strconv" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "time" 16 17 "github.com/ssetin/PenguinCast/src/fastStat" 18 ) 19 20 const ( 21 cServerName = "PenguinCast" 22 cVersion = "0.2.0" 23 ) 24 25 var ( 26 vCommands = [...]string{"metadata"} 27 ) 28 29 // IceServer ... 30 type IceServer struct { 31 serverName string 32 version string 33 34 Props Properties 35 36 mux sync.Mutex 37 Started int32 38 StartedTime time.Time 39 ListenersCount int32 40 SourcesCount int32 41 42 statReader fastStat.ProcStatsReader 43 // for monitor 44 cpuUsage float64 45 memUsage int 46 47 srv *http.Server 48 poolManager PoolManager 49 logError *log.Logger 50 logErrorFile *os.File 51 logAccess *log.Logger 52 logAccessFile *os.File 53 statFile *os.File 54 } 55 56 // Init - Load params from config.json 57 func (i *IceServer) Init() error { 58 var err error 59 60 i.serverName = cServerName 61 i.version = cVersion 62 63 i.ListenersCount = 0 64 i.SourcesCount = 0 65 i.Started = 0 66 67 log.Println("Init " + i.serverName + " " + i.version) 68 69 err = i.initConfig() 70 if err != nil { 71 return err 72 } 73 err = i.initLog() 74 if err != nil { 75 return err 76 } 77 err = i.initMounts() 78 if err != nil { 79 return err 80 } 81 82 i.srv = &http.Server{ 83 Addr: ":" + strconv.Itoa(i.Props.Socket.Port), 84 } 85 86 if i.Props.Logging.UseStat { 87 i.statReader.Init() 88 go i.processStats() 89 } 90 91 return nil 92 } 93 94 func (i *IceServer) initMounts() error { 95 var err error 96 for idx := range i.Props.Mounts { 97 err = i.Props.Mounts[idx].Init(i) 98 if err != nil { 99 return err 100 } 101 } 102 return nil 103 } 104 105 func (i *IceServer) incListeners() { 106 atomic.AddInt32(&i.ListenersCount, 1) 107 } 108 109 func (i *IceServer) decListeners() { 110 if atomic.LoadInt32(&i.ListenersCount) > 0 { 111 atomic.AddInt32(&i.ListenersCount, -1) 112 } 113 } 114 115 func (i *IceServer) checkListeners() bool { 116 clientsLimit := atomic.LoadInt32(&i.Props.Limits.Clients) 117 if atomic.LoadInt32(&i.ListenersCount) > clientsLimit { 118 return false 119 } 120 return true 121 } 122 123 func (i *IceServer) incSources() { 124 atomic.AddInt32(&i.SourcesCount, 1) 125 } 126 127 func (i *IceServer) decSources() { 128 if atomic.LoadInt32(&i.SourcesCount) > 0 { 129 atomic.AddInt32(&i.SourcesCount, -1) 130 } 131 } 132 133 func (i *IceServer) checkSources() bool { 134 sourcesLimit := atomic.LoadInt32(&i.Props.Limits.Sources) 135 if atomic.LoadInt32(&i.SourcesCount) > sourcesLimit { 136 return false 137 } 138 return true 139 } 140 141 // Close - finish 142 func (i *IceServer) Close() { 143 144 if err := i.srv.Shutdown(context.Background()); err != nil { 145 i.printError(1, err.Error()) 146 log.Printf("Error: %s\n", err.Error()) 147 } else { 148 log.Println("Stopped") 149 } 150 151 for idx := range i.Props.Mounts { 152 i.Props.Mounts[idx].Close() 153 } 154 155 i.statReader.Close() 156 i.logErrorFile.Close() 157 i.logAccessFile.Close() 158 if i.statFile != nil { 159 i.statFile.Close() 160 } 161 } 162 163 func (i *IceServer) checkIsMount(page string) int { 164 for idx := range i.Props.Mounts { 165 if i.Props.Mounts[idx].Name == page { 166 return idx 167 } 168 } 169 return -1 170 } 171 172 func (i *IceServer) checkIsCommand(page string, r *http.Request) int { 173 for idx := range vCommands { 174 if vCommands[idx] == page && r.URL.Query().Get("mode") == "updinfo" { 175 return idx 176 } 177 } 178 return -1 179 } 180 181 func (i *IceServer) handler(w http.ResponseWriter, r *http.Request) { 182 if i.Props.Logging.Loglevel == 4 { 183 i.logHeaders(w, r) 184 } 185 186 page, mountidx, cmdidx, err := i.checkPage(w, r) 187 if err != nil { 188 i.printError(1, err.Error()) 189 return 190 } 191 192 if mountidx >= 0 { 193 i.openMount(mountidx, w, r) 194 return 195 } 196 197 if cmdidx >= 0 { 198 i.runCommand(cmdidx, w, r) 199 return 200 } 201 202 if strings.HasSuffix(page, "info.html") || strings.HasSuffix(page, "info.json") || strings.HasSuffix(page, "monitor.html") { 203 i.renderPage(w, r, page) 204 } else { 205 http.ServeFile(w, r, page) 206 } 207 } 208 209 /* 210 runCommand 211 */ 212 func (i *IceServer) runCommand(idx int, w http.ResponseWriter, r *http.Request) { 213 if idx == 0 { 214 mountname := path.Base(r.URL.Query().Get("mount")) 215 i.printError(4, "runCommand 0 with "+mountname) 216 midx := i.checkIsMount(mountname) 217 if midx >= 0 { 218 i.Props.Mounts[midx].updateMeta(w, r) 219 } 220 } 221 222 } 223 224 func (i *IceServer) wrapHandlerWithStreaming(wrappedHandler http.Handler) http.Handler { 225 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 226 //streamWriter := newStreamResponseWritter(w) 227 wrappedHandler.ServeHTTP(w, req) 228 }) 229 } 230 231 /*Start - start listening port ...*/ 232 func (i *IceServer) Start() { 233 if atomic.LoadInt32(&i.Started) == 1 { 234 return 235 } 236 237 stop := make(chan os.Signal, 1) 238 signal.Notify(stop, os.Interrupt, os.Kill) 239 240 if i.Props.Logging.UseMonitor { 241 http.HandleFunc("/updateMonitor", func(w http.ResponseWriter, r *http.Request) { 242 ws, err := upgrader.Upgrade(w, r, nil) 243 if err != nil { 244 log.Fatal(err) 245 } 246 go i.sendMonitorInfo(ws) 247 }) 248 } 249 250 //streamedHandler := i.wrapHandlerWithStreaming(http.HandlerFunc(i.handler)) 251 //http.Handle("/", streamedHandler) 252 http.HandleFunc("/", i.handler) 253 254 go func() { 255 i.mux.Lock() 256 i.StartedTime = time.Now() 257 i.mux.Unlock() 258 atomic.StoreInt32(&i.Started, 1) 259 log.Print("Started on " + i.srv.Addr) 260 261 if err := i.srv.ListenAndServe(); err != http.ErrServerClosed { 262 log.Fatal(err) 263 } 264 265 }() 266 267 <-stop 268 atomic.StoreInt32(&i.Started, 0) 269 }