func (c *client) serve() { defer c.close() c.closing = make(chan bool) if onc := c.srv.OnNewConnection; onc != nil { if err := onc(c.rwc); err != nil { c.log.Debug("newconn error", log.Ctx{"error": err}) return } } // Create the ldap response queue to be writted to client (buffered to 20) // buffered to 20 means that If client is slow to handler responses, Server // Handlers will stop to send more respones c.chanOut = make(chan *ldap.LDAPMessage) c.writeDone = make(chan bool) // for each message in c.chanOut send it to client go func() { for msg := range c.chanOut { c.writeMessage(msg) } close(c.writeDone) }() // Listen for server signal to shutdown go func() { for { select { case <-c.srv.chDone: // server signals shutdown process c.wg.Add(1) r := NewExtendedResponse(LDAPResultUnwillingToPerform) r.SetDiagnosticMessage("server is about to stop") r.SetResponseName(NoticeOfDisconnection) m := ldap.NewLDAPMessageWithProtocolOp(r) c.chanOut <- m c.wg.Done() c.rwc.SetReadDeadline(time.Now().Add(time.Millisecond)) return case <-c.closing: return } } }() c.requestList = make(map[int]*Message) for { if c.srv.ReadTimeout != 0 { c.rwc.SetReadDeadline(time.Now().Add(c.srv.ReadTimeout)) } if c.srv.WriteTimeout != 0 { c.rwc.SetWriteDeadline(time.Now().Add(c.srv.WriteTimeout)) } //Read client input as a ASN1/BER binary message messagePacket, err := readMessagePacket(c.br) if err != nil { if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { c.log.Debug("client timeout", log.Ctx{"clientnum": c.Numero, "error": err}) } else { c.log.Debug("Error readMessagePacket", log.Ctx{"error": err}) } return } //Convert ASN1 binaryMessage to a ldap Message message, err := messagePacket.readMessage() if err != nil { c.log.Debug("Error reading Message", log.Ctx{"error": err.Error()}) continue } c.log.Debug("incoming packet", log.Ctx{"opname": message.ProtocolOpName(), "packet": messagePacket}) // TODO: Use a implementation to limit runnuning request by client // solution 1 : when the buffered output channel is full, send a busy // solution 2 : when 10 client requests (goroutines) are running, send a busy message // And when the limit is reached THEN send a BusyLdapMessage // When message is an UnbindRequest, stop serving if _, ok := message.ProtocolOp().(ldap.UnbindRequest); ok { return } // If client requests a startTls, do not handle it in a // goroutine, connection has to remain free until TLS is OK // @see RFC https://tools.ietf.org/html/rfc4511#section-4.14.1 if req, ok := message.ProtocolOp().(ldap.ExtendedRequest); ok { if req.RequestName() == NoticeOfStartTLS { c.wg.Add(1) c.ProcessRequestMessage(&message) continue } } // TODO: go/non go routine choice should be done in the ProcessRequestMessage // not in the client.serve func c.wg.Add(1) go c.ProcessRequestMessage(&message) } }
func (w responseWriterImpl) Write(po ldap.ProtocolOp) { m := ldap.NewLDAPMessageWithProtocolOp(po) m.SetMessageID(w.messageID) w.chanOut <- m }