// ReqReplyStreamChan tests a request-reply type of com over a stream. func (*Handler) ReqReplyStreamChan(stream api.Stream, name string) error { for { var msg string err := stream.Receive(&msg) if err == io.EOF { break } if err != nil { log.Printf("Error: %v\n", err) return err } err = stream.Send(name + ":" + msg + " received") if err != nil { log.Printf("Error: %v\n", err) return err } } return stream.Close() }
// ServeHTTP serves individual HTTP requests. func (server *Server) ServeHTTP(resp http.ResponseWriter, req *http.Request) { origin := req.Header.Get("Origin") if origin != "" { // TODO: Perhaps a lever.json config param could restrict origin // to prevent CSRF. resp.Header().Set("Access-Control-Allow-Origin", origin) resp.Header().Set( "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, "+ "X-CSRF-Token, Authorization") } if req.Method == "OPTIONS" { resp.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") resp.WriteHeader(http.StatusOK) return } if req.Method != "POST" { resp.WriteHeader(http.StatusMethodNotAllowed) return } leverURLStr := fmt.Sprintf("lever://%s%s", req.Host, req.URL.Path) leverURL, err := core.ParseLeverURL(leverURLStr) if err != nil { logger.WithFields("err", err).Debug("Error parsing Lever URL") resp.WriteHeader(http.StatusBadRequest) return } queryValues, err := url.ParseQuery(req.URL.RawQuery) if err != nil { logger.WithFields("err", err).Debug("Error parsing URL query") resp.WriteHeader(http.StatusBadRequest) return } forceEnv := queryValues.Get("forceenv") if forceEnv != "" { leverURL.Environment = forceEnv } leverURLStr = leverURL.String() reader := bufferedReaderPool.Get().(*bufio.Reader) reader.Reset(req.Body) defer bufferedReaderPool.Put(reader) defer req.Body.Close() if leverapi.IsChanMethod(leverURL.Method) { // TODO: Byte args not supported. Any way to support that? // How to delimit args from rest? done := false var line []byte line, err = reader.ReadBytes('\n') if err != nil { if err == io.EOF { done = true } else { logger.WithFields("err", err).Error("Read error") return } } if len(line) == 0 { resp.WriteHeader(http.StatusBadRequest) return } var args []interface{} err = json.Unmarshal(line, &args) if err != nil { resp.WriteHeader(http.StatusBadRequest) logger.WithFields("err", err).Debug("Malformed JSON") return } var stream leverapi.Stream stream, err = server.leverClient.InvokeChanURL(leverURLStr, args...) if err != nil { resp.WriteHeader(http.StatusInternalServerError) logger.WithFields("err", err).Error("InvokeChanURL error") return } errCh := make(chan bool) workerDoneCh := make(chan struct{}) go replyStreamWorker(stream, resp, errCh, workerDoneCh) if req.Header.Get("Content-Type") == "application/json" { for !done { line, err = reader.ReadBytes('\n') if err != nil { if err == io.EOF { done = true } else { logger.WithFields("err", err).Error("Read error") errCh <- true <-workerDoneCh return } } if len(line) > 0 { var msg interface{} err = json.Unmarshal(line, &msg) if err != nil { logger.WithFields("err", err).Debug("Malformed JSON") errCh <- true <-workerDoneCh return } stream.Send(msg) } } } else { for !done { buffer := bufferPool.Get().([]byte) defer bufferPool.Put(buffer) var size int size, err = reader.Read(buffer) if err != nil { if err == io.EOF { done = true } logger.WithFields("err", err).Error("Read error") errCh <- true <-workerDoneCh return } msg := buffer[:size] stream.Send(msg) } } err = stream.Close() if err != nil { logger.WithFields("err", err).Debug( "Stream close error") errCh <- true <-workerDoneCh return } errCh <- false <-workerDoneCh } else { buffer := bufferPool.Get().([]byte) defer bufferPool.Put(buffer) var size int size, err = io.ReadFull(reader, buffer) if err != nil { if err != io.EOF && err != io.ErrUnexpectedEOF { logger.WithFields("err", err).Error("Read error") return } } if size == maxNonChanRequestSize && err != io.EOF && err != io.ErrUnexpectedEOF { resp.WriteHeader(http.StatusBadRequest) _, err = resp.Write([]byte("\"Exceeded maximum request size\"")) if err != nil { logger.WithFields("err", err).Debug("Write error") } return } var args []interface{} contentType := req.Header.Get("Content-Type") contentTypeSplit := strings.Split(contentType, ";") switch contentTypeSplit[0] { case "application/json": err = json.Unmarshal(buffer[:size], &args) if err != nil { resp.WriteHeader(http.StatusBadRequest) logger.WithFields("err", err).Debug("JSON unmarshal error") return } case "application/x-www-form-urlencoded": // TODO resp.WriteHeader(http.StatusBadRequest) logger.WithFields("contentType", contentType).Error( "Content type not yet supported") return default: args = make([]interface{}, 1) args[0] = buffer[:size] } var reply interface{} err = server.leverClient.InvokeURL(&reply, leverURLStr, args...) if err != nil { resp.WriteHeader(http.StatusInternalServerError) remoteErr, ok := err.(*leverapi.RemoteError) if ok { reply = remoteErr.Err } else { remoteByteErr, ok := err.(*leverapi.RemoteByteError) if ok { reply = remoteByteErr.Err } else { logger.WithFields("err", err).Error("Internal Lever error") return } } } else { resp.WriteHeader(http.StatusOK) } err = writeReply(resp, reply) if err != nil { logger.WithFields("err", err).Error("Writing reply failed") return } } }