public static final class ResponseStatusCodes {
    public static final int CONTINUE_100 = HttpResponseStatus.CONTINUE.code();
    public static final int SWITCHING_PROTOCOLS_101 = HttpResponseStatus.SWITCHING_PROTOCOLS.code();
    public static final int OK_200 = HttpResponseStatus.OK.code();
    public static final int MOVED_PERMANENTLY_301 = HttpResponseStatus.MOVED_PERMANENTLY.code();
    public static final int FOUND_302 = HttpResponseStatus.FOUND.code();
    public static final int SEE_OTHER_303 = HttpResponseStatus.SEE_OTHER.code();
    public static final int NOT_MODIFIED_304 = HttpResponseStatus.NOT_MODIFIED.code();
    public static final int TEMPORARY_REDIRECT_307 = HttpResponseStatus.TEMPORARY_REDIRECT.code();
    public static final int UNAUTHORIZED_401 = HttpResponseStatus.UNAUTHORIZED.code();
    public static final int PROXY_AUTHENTICATION_REQUIRED_407 =
        HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED.code();

    private ResponseStatusCodes() {}
  }
  private void messageReceive(ChannelHandlerContext ctx, DefaultFullHttpResponse response) {
    /** send next cmd * */
    switch (state) {
      case PLAY:
        String rtpInfo = response.headers().get("RTP-Info");

        setState(RTSP.PLAYING);
        break;
      case SETUP:
        // session
        String sessionId = response.headers().get("Session");
        if (null != sessionId) {
          Matcher matcher = Pattern.compile("([^;]+)").matcher(sessionId);
          if (matcher.find()) {
            rtspStack.setSession(matcher.group(1));
          } else {
            rtspStack.setSession(sessionId);
          }
        }

        // transport
        InetSocketAddress remoteHost = (InetSocketAddress) ctx.channel().remoteAddress();
        String transport = response.headers().get("Transport");
        RtpSessionExt rtp = rtspStack.getLastRtpSession();
        Matcher matcher = Pattern.compile("([^\\s=;]+)=(([^-;]+)(-([^;]+))?)").matcher(transport);
        while (matcher.find()) {
          String key = matcher.group(1).toLowerCase();
          if ("server_port".equals(key)) {
            rtp.connectRtp(
                new InetSocketAddress(
                    remoteHost.getHostString(), Integer.valueOf(matcher.group(3))));
            rtp.connectRtcp(
                new InetSocketAddress(
                    remoteHost.getHostString(), Integer.valueOf(matcher.group(5))));
          } else if ("ssrc".equals(key)) {
            rtp.setSsrc(Long.parseLong(matcher.group(2).trim(), 16));
          } else if ("interleaved".equals(key)) {
            rtp.setRtpChunk(Integer.valueOf(matcher.group(3)));
            rtp.setRtcpChunk(Integer.valueOf(matcher.group(5)));
          } else {
            logger.warn("ignored [{}={}]", key, matcher.group(2));
          }
        }

        // next action
        boolean finish = setup(sd, mediaIndex++);
        if (finish) {
          sendPlay();
          setState(RTSP.PLAY);
        }

        break;
      case DESCRIBE:
        if (response.getStatus().code() == HttpResponseStatus.UNAUTHORIZED.code()) {
          sendDescribe(buildAuthorizationString(response));
          break;
        }

        String desciptor = response.content().toString(Charset.forName("UTF8"));
        SessionDescriptionImpl sd = new SessionDescriptionImpl();
        StringTokenizer tokenizer = new StringTokenizer(desciptor);
        while (tokenizer.hasMoreChars()) {
          String line = tokenizer.nextToken();

          try {
            SDPParser paser = ParserFactory.createParser(line);
            if (null != paser) {
              SDPField obj = paser.parse();
              sd.addField(obj);
            }
          } catch (ParseException e) {
            logger.warn("fail parse [{}]", line, e);
          }
        }
        this.sd = sd;

        mediaIndex = 0;
        setup(sd, mediaIndex++);

        // 心跳
        rtspStack
            .getChannel()
            .pipeline()
            .addFirst("ping", new IdleStateHandler(30, 15, 13, TimeUnit.SECONDS));

        //
        rtspStack.setSessionDescription(sd);
        break;
      case OPTIONS:
        if (response.getStatus().code() == HttpResponseStatus.UNAUTHORIZED.code()) {
          sendDescribe(buildAuthorizationString(response));
        } else {
          sendDescribe();
        }

        setState(RTSP.DESCRIBE);
        break;
      case PLAYING:
        break;
      default:
        logger.warn("I dont't Known What to do with {}", response);
        break;
    }
  }
  @GET
  @ApiOperation(
      value = "Subscribe Event Stream",
      consumes = "text/event-stream",
      produces = "text/event-stream",
      authorizations = @Authorization(value = "read_key"),
      notes = "Subscribes the event stream.")
  @ApiResponses(value = {@ApiResponse(code = 400, message = "Project does not exist.")})
  @Path("/subscribe")
  @IgnorePermissionCheck
  @IgnoreApi
  public void subscribe(RakamHttpRequest request) {
    if (!Objects.equals(request.headers().get(HttpHeaders.Names.ACCEPT), "text/event-stream")) {
      request
          .response(
              "the response should accept text/event-stream", HttpResponseStatus.NOT_ACCEPTABLE)
          .end();
      return;
    }

    RakamHttpRequest.StreamResponse response = request.streamResponse();

    List<String> data = request.params().get("data");
    if (data == null || data.isEmpty()) {
      response
          .send(
              "result",
              encode(
                  errorMessage("data query parameter is required", HttpResponseStatus.BAD_REQUEST)))
          .end();
      return;
    }

    StreamQuery query;
    try {
      query = JsonHelper.readSafe(data.get(0), StreamQuery.class);
    } catch (IOException e) {
      response
          .send(
              "result",
              encode(errorMessage("json couldn't parsed", HttpResponseStatus.BAD_REQUEST)))
          .end();
      return;
    }
    List<String> api_key = request.params().get("api_key");
    if (api_key == null
        || api_key.isEmpty()
        || !metastore.checkPermission(
            query.project, Metastore.AccessKeyType.READ_KEY, api_key.get(0))) {
      response.send("result", HttpResponseStatus.UNAUTHORIZED.reasonPhrase()).end();
      return;
    }

    List<CollectionStreamQuery> collect;
    try {
      collect =
          query
              .collections
              .stream()
              .map(
                  collection -> {
                    Expression expression = null;
                    try {
                      expression =
                          collection.filter == null
                              ? null
                              : sqlParser.createExpression(collection.filter);
                    } catch (ParsingException e) {
                      ObjectNode obj =
                          errorMessage(
                              format(
                                  "Couldn't parse %s: %s", collection.filter, e.getErrorMessage()),
                              HttpResponseStatus.BAD_REQUEST);
                      request.response(encode(obj)).end();
                      throw e;
                    }
                    return new CollectionStreamQuery(
                        collection.name, expression == null ? null : expression.toString());
                  })
              .collect(Collectors.toList());
    } catch (ParsingException e) {
      return;
    }

    EventStream.EventStreamer subscribe =
        stream.subscribe(
            query.project, collect, query.columns, new StreamResponseAdapter(response));

    eventLoopGroup.schedule(
        new Runnable() {
          @Override
          public void run() {
            if (response.isClosed()) {
              subscribe.shutdown();
            } else {
              subscribe.sync();
              eventLoopGroup.schedule(this, 3, TimeUnit.SECONDS);
            }
          }
        },
        3,
        TimeUnit.SECONDS);
  }
 private void notAuthorised(HttpServerResponse response) {
   response.setStatusCode(HttpResponseStatus.UNAUTHORIZED.code());
   response.setStatusMessage(HttpResponseStatus.UNAUTHORIZED.reasonPhrase());
   response.end();
 }