github.com/ssetin/penguincast@v0.2.0/src/server/protocol.go (about) 1 // Package iceserver - icecast streaming server 2 package iceserver 3 4 /* 5 TODO: 6 */ 7 8 import ( 9 "bufio" 10 "errors" 11 "fmt" 12 "io" 13 "log" 14 "net" 15 "net/http" 16 "strconv" 17 "strings" 18 "sync/atomic" 19 "time" 20 ) 21 22 //223.33.152.54 - - [27/Feb/2012:13:37:21 +0300] "GET /gop_aac HTTP/1.1" 200 75638 "-" "WMPlayer/10.0.0.364 guid/3300AD50-2C39-46C0-AE0A-AC7B8159E203" 400 23 func (i *IceServer) closeMount(idx int, issource bool, bytessended *int, start time.Time, r *http.Request) { 24 if issource { 25 i.decSources() 26 i.Props.Mounts[idx].Clear() 27 } else { 28 i.Props.Mounts[idx].decListeners() 29 i.decListeners() 30 } 31 t := time.Now() 32 elapsed := t.Sub(start) 33 i.printAccess("%s - - [%s] \"%s\" %s %d \"%s\" \"%s\" %d\r\n", i.getHost(r.RemoteAddr), start.Format(time.RFC1123Z), r.Method+" "+r.RequestURI+" "+r.Proto, "200", *bytessended, r.Referer(), r.UserAgent(), int(elapsed.Seconds())) 34 } 35 36 func (i *IceServer) getHost(addr string) string { 37 idx := strings.Index(addr, ":") 38 if idx == -1 { 39 return addr 40 } 41 return addr[:idx] 42 } 43 44 func (i *IceServer) logHeaders(w http.ResponseWriter, r *http.Request) { 45 request := r.Method + " " + r.RequestURI + " " + r.Proto + "\n" 46 for name, headers := range r.Header { 47 name = strings.ToLower(name) 48 for _, h := range headers { 49 request += fmt.Sprintf("%v: %v\n", name, h) 50 } 51 } 52 i.printError(4, "\n"+request) 53 } 54 55 /* 56 openMount 57 Decide what to do, according to HTTP method 58 */ 59 func (i *IceServer) openMount(idx int, w http.ResponseWriter, r *http.Request) { 60 if r.Method == "SOURCE" || r.Method == "PUT" { 61 if !i.checkSources() { 62 i.printError(1, "Number of sources exceeded") 63 http.Error(w, "Number of sources exceeded", 403) 64 return 65 } 66 i.writeMount(idx, w, r) 67 } else { 68 if !i.checkListeners() { 69 i.printError(1, "Number of listeners exceeded") 70 http.Error(w, "Number of listeners exceeded", 403) 71 return 72 } 73 var im = false 74 if r.Header.Get("icy-metadata") == "1" { 75 im = true 76 } 77 i.readMount(idx, im, w, r) 78 } 79 } 80 81 func (i *IceServer) closeAndUnlock(pack *BufElement, err error) { 82 if te, ok := err.(net.Error); ok && te.Timeout() { 83 log.Println("Write timeout") 84 i.printError(1, "Write timeout") 85 } else { 86 i.printError(1, err.Error()) 87 } 88 pack.UnLock() 89 } 90 91 /* 92 readMount 93 Send stream from requested mount to client 94 */ 95 func (i *IceServer) readMount(idx int, icymeta bool, w http.ResponseWriter, r *http.Request) { 96 var mount *Mount 97 var meta []byte 98 var err error 99 var pack, nextpack *BufElement 100 101 bytessended := 0 102 writed := 0 103 idle := 0 104 idleTimeOut := i.Props.Limits.EmptyBufferIdleTimeOut * 1000 105 writeTimeOut := time.Second * time.Duration(i.Props.Limits.WriteTimeOut) 106 offset := 0 107 nometabytes := 0 108 partwrited := 0 109 nmtmp := 0 110 delta := 0 111 beginIteration := time.Now() 112 metalen := 0 113 n := 0 114 115 hj, ok := w.(http.Hijacker) 116 if !ok { 117 i.printError(1, "webserver doesn't support hijacking") 118 http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError) 119 return 120 } 121 122 conn, bufrw, err := hj.Hijack() 123 if err != nil { 124 http.Error(w, err.Error(), http.StatusInternalServerError) 125 return 126 } 127 defer conn.Close() 128 129 start := time.Now() 130 mount = &i.Props.Mounts[idx] 131 132 i.printError(3, "readMount "+mount.Name) 133 defer i.closeMount(idx, false, &bytessended, start, r) 134 135 //try to maximize unused buffer pages from begining 136 pack = mount.buffer.Start(mount.BurstSize) 137 138 if pack == nil { 139 i.printError(1, "readMount Empty buffer") 140 return 141 } 142 143 //bufrw := bufio.NewWriterSize(conn, 1024*mount.BitRate/8) 144 145 mount.sayListenerHello(bufrw, icymeta) 146 147 i.incListeners() 148 mount.incListeners() 149 150 for { 151 beginIteration = time.Now() 152 conn.SetWriteDeadline(time.Now().Add(writeTimeOut)) 153 //check, if server has to be stopped 154 if atomic.LoadInt32(&i.Started) == 0 { 155 break 156 } 157 158 n++ 159 pack.Lock() 160 if icymeta { 161 meta, metalen = mount.getIcyMeta() 162 163 if nometabytes+pack.len+delta > mount.State.MetaInfo.MetaInt { 164 offset = mount.State.MetaInfo.MetaInt - nometabytes - delta 165 166 //log.Printf("*** write block with meta ***") 167 //log.Printf(" offset = %d - %d(nometabytes) - %d (delta) = %d", mount.State.MetaInfo.MetaInt, nometabytes, delta, offset) 168 169 if offset < 0 || offset >= pack.len { 170 i.printError(2, "Bad metainfo offset %d", offset) 171 log.Printf("!!! Bad metainfo offset %d ***", offset) 172 offset = 0 173 } 174 175 partwrited, err = bufrw.Write(pack.buffer[:offset]) 176 if err != nil { 177 i.closeAndUnlock(pack, err) 178 break 179 } 180 writed += partwrited 181 partwrited, err = bufrw.Write(meta) 182 if err != nil { 183 i.closeAndUnlock(pack, err) 184 break 185 } 186 writed += partwrited 187 partwrited, err = bufrw.Write(pack.buffer[offset:]) 188 if err != nil { 189 i.closeAndUnlock(pack, err) 190 break 191 } 192 writed += partwrited 193 194 delta = writed - offset - metalen 195 nometabytes = 0 196 nmtmp = 0 197 198 //log.Printf(" delta = %d(writed) - %d(offset) - %d(metalen) = %d", writed, offset, metalen, delta) 199 } else { 200 writed = 0 201 nmtmp, err = bufrw.Write(pack.buffer) 202 nometabytes += nmtmp 203 } 204 } else { 205 writed, err = bufrw.Write(pack.buffer) 206 } 207 208 if err != nil { 209 i.closeAndUnlock(pack, err) 210 break 211 } 212 213 bytessended += writed + nmtmp 214 215 // send burst data whithout waiting 216 if bytessended >= mount.BurstSize { 217 if time.Since(beginIteration) < time.Second { 218 time.Sleep(time.Second - time.Since(beginIteration)) 219 } 220 } 221 222 nextpack = pack.Next() 223 for nextpack == nil { 224 time.Sleep(time.Millisecond * 250) 225 idle += 250 226 if idle >= idleTimeOut { 227 i.closeAndUnlock(pack, errors.New("Empty Buffer idle time is reached")) 228 break 229 } 230 nextpack = pack.Next() 231 } 232 idle = 0 233 pack.UnLock() 234 235 pack = nextpack 236 } 237 } 238 239 /* 240 writeMount 241 Authenticate SOURCE and write stream from it to appropriate mount buffer 242 */ 243 func (i *IceServer) writeMount(idx int, w http.ResponseWriter, r *http.Request) { 244 var mount *Mount 245 mount = &i.Props.Mounts[idx] 246 247 if !mount.State.Started { 248 mount.mux.Lock() 249 err := mount.auth(w, r) 250 if err != nil { 251 mount.mux.Unlock() 252 i.printError(1, err.Error()) 253 return 254 } 255 mount.writeICEHeaders(w, r) 256 mount.State.Started = true 257 mount.State.StartedTime = time.Now() 258 mount.mux.Unlock() 259 } else { 260 i.printError(1, "SOURCE already connected") 261 http.Error(w, "SOURCE already connected", 403) 262 return 263 } 264 265 bytessended := 0 266 idle := 0 267 readed := 0 268 var err error 269 start := time.Now() 270 271 i.printError(3, "writeMount "+mount.Name) 272 defer i.closeMount(idx, true, &bytessended, start, r) 273 274 hj, ok := w.(http.Hijacker) 275 if !ok { 276 i.printError(1, "webserver doesn't support hijacking") 277 http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError) 278 return 279 } 280 281 conn, _, err := hj.Hijack() 282 if err != nil { 283 http.Error(w, err.Error(), http.StatusInternalServerError) 284 return 285 } 286 defer conn.Close() 287 288 bufrw := bufio.NewReaderSize(conn, 1024*mount.BitRate/8) 289 290 i.incSources() 291 // max bytes per second according to bitrate 292 buff := make([]byte, mount.BitRate*1024/8) 293 294 for { 295 //check, if server has to be stopped 296 if atomic.LoadInt32(&i.Started) == 0 { 297 break 298 } 299 300 readed, err = bufrw.Read(buff) 301 if err != nil { 302 if err == io.EOF { 303 idle++ 304 if idle >= i.Props.Limits.SourceIdleTimeOut { 305 i.printError(1, "Source idle time is reached") 306 break 307 } 308 } 309 i.printError(3, err.Error()) 310 } else { 311 idle = 0 312 } 313 // append to the buffer's queue based on actual readed bytes 314 mount.buffer.Append(buff, readed) 315 bytessended += readed 316 i.printError(4, "writeMount "+strconv.Itoa(readed)+"") 317 318 if mount.dumpFile != nil { 319 mount.dumpFile.Write(mount.buffer.Last().buffer) 320 } 321 322 time.Sleep(1000 * time.Millisecond) 323 324 //check if maxbuffersize reached and truncate it 325 mount.buffer.checkAndTruncate() 326 } 327 }