在同一个套接字中使用 libevent 处理 HTTP 和 HTTPS 请求

Posted

技术标签:

【中文标题】在同一个套接字中使用 libevent 处理 HTTP 和 HTTPS 请求【英文标题】:Handling HTTP and HTTPS requests with libevent in a same socket 【发布时间】:2013-07-09 21:43:32 【问题描述】:

参照https://github.com/ppelleti/https-example,我尝试定义以下代码以处理(服务器端)https 和http 请求。有没有更简单的方法来处理单个监听套接字中的 https 和 http 请求?提前致谢

static int serve_some_http (void)
 struct event_base *base;
  struct evhttp *https, *http;
  struct evhttp_bound_socket *https_handle, *http_handle;

  unsigned short port = COMMON_HTTPS_PORT;
#ifdef _WIN32
  WSADATA WSAData;
  WSAStartup (0x101, &WSAData);
#endif

  base = event_base_new ();
  if (! base)
     fprintf (stderr, "Couldn't create an event_base: exiting\n");
      return 1;
    

  /* Create a new evhttp object to handle requests. */
  https = evhttp_new (base);
  http  = evhttp_new (base);
  if ((! https)||(! http))
     fprintf (stderr, "couldn't create evhttp. Exiting.\n");
      return 1;
    

  SSL_CTX *ctx = SSL_CTX_new (SSLv23_server_method ());
  SSL_CTX_set_options (ctx,
                   SSL_OP_SINGLE_DH_USE |
                   SSL_OP_SINGLE_ECDH_USE |
                   SSL_OP_NO_SSLv2);

  /* Cheesily pick an elliptic curve to use with elliptic curve ciphersuites.
   * We just hardcode a single curve which is reasonably decent.
   * See http://www.mail-archive.com/openssl-dev@openssl.org/msg30957.html */
  EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
  if (! ecdh)
    die_most_horribly_from_openssl_error ("EC_KEY_new_by_curve_name");
  if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh))
    die_most_horribly_from_openssl_error ("SSL_CTX_set_tmp_ecdh");

  /* Find and set up our server certificate. */
  const char *certificate_chain = SERVER_CERT_PEM;
  const char *private_key = SERVER_PRIV_PEM;
  server_setup_certs (ctx, certificate_chain, private_key);

  /* This is the magic that lets evhttp use SSL. */
  evhttp_set_bevcb (https, bevcb, ctx);

  /* This is the callback that gets called when a request comes in. */
  evhttp_set_gencb (https, https_document_cb, NULL);
  evhttp_set_gencb (http, http_document_cb, NULL);

  /* Now we tell the evhttp what port to listen on */
  https_handle = evhttp_bind_socket_with_handle (https, "127.0.0.1", port);
  http_handle = evhttp_bind_socket_with_handle (http, "127.0.0.1", port+1);
  if ((! https_handle)||(! http_handle))
     fprintf (stderr, "couldn't bind to port %d. Exiting.\n", (int) port);
      return 1;
    

   /* Extract and display the address we're listening on. */
    sock_hop ss, sss;
    evutil_socket_t fds,fd;
    ev_socklen_t socklen = sizeof (ss);
    ev_socklen_t s_socklen = sizeof (ss);
    char addrbuf[128];
    char s_addrbuf[128];
    void *inaddr, *s_inaddr;
    const char *addr, *s_addr;
    int got_port = -1;
    int s_got_port = -1;


    fds = evhttp_bound_socket_get_fd (https_handle);
    memset (&sss, 0, sizeof(sss));
    if (getsockname (fds, &sss.sa, &s_socklen))
       perror ("getsockname() failed");
    return 1;
      
    if (sss.ss.ss_family == AF_INET)
       s_got_port = ntohs (sss.in.sin_port);
    s_inaddr = &sss.in.sin_addr;
      
    else if (sss.ss.ss_family == AF_INET6)
       s_got_port = ntohs (sss.i6.sin6_port);
    s_inaddr = &sss.i6.sin6_addr;
      
    else
       fprintf (stderr, "Weird address family %d\n", sss.ss.ss_family);
    return 1;
      
    s_addr = evutil_inet_ntop (sss.ss.ss_family, s_inaddr, s_addrbuf,
                         sizeof (s_addrbuf));
    if (s_addr)
      printf ("Listening HTTPS on %s:%d\n", s_addr, s_got_port);
    else
       fprintf (stderr, "evutil_inet_ntop failed\n");
    return 1;
      





    fd = evhttp_bound_socket_get_fd (http_handle);
    memset (&ss, 0, sizeof(ss));
    if (getsockname (fd, &ss.sa, &socklen))
       perror ("getsockname() failed");
    return 1;
      
    if (ss.ss.ss_family == AF_INET)
       got_port = ntohs (ss.in.sin_port);
    inaddr = &ss.in.sin_addr;
      
    else if (ss.ss.ss_family == AF_INET6)
       got_port = ntohs (ss.i6.sin6_port);
    inaddr = &ss.i6.sin6_addr;
      
    else
       fprintf (stderr, "Weird address family %d\n", ss.ss.ss_family);
    return 1;
      
    addr = evutil_inet_ntop (ss.ss.ss_family, inaddr, addrbuf,
                         sizeof (addrbuf));
    if (addr)
      printf ("Listening HTTP on %s:%d\n", addr, got_port);
    else
       fprintf (stderr, "evutil_inet_ntop failed\n");
    return 1;
      

  

  event_base_dispatch (base);

  /* not reached; runs forever */

  return 0;

【问题讨论】:

【参考方案1】:

不,您不能从同一个端口提供 HTTPS 和 HTTP,并且 libevent 需要两个 struct http 实例来处理两者。

【讨论】:

以上是关于在同一个套接字中使用 libevent 处理 HTTP 和 HTTPS 请求的主要内容,如果未能解决你的问题,请参考以下文章

Libevent 和文件 I/O

知道所有回调都使用 libevent 和 bufferevent_free 运行

带有 libevent/libev 的 C 或 C++:监视 unix 套接字

使用 libev 的套接字

libevent 库学习

Libevent bufferevent 套接字刷新