You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言HTTPS客户端请求返回HTTP 404,curl请求正常问题排查

C语言HTTPS客户端返回404但curl正常的问题排查

我开发了一个小型C语言HTTPS客户端,用于从公开API获取数据(无需认证)。但尝试获取数据时始终返回HTTP 404错误,尽管确认发送的GET请求资源路径正确——同一机器上用curl以完全相同路径请求可正常返回200。

客户端代码

int main(int argc, char **argv)
{

  char *message=NULL;

  char *CAfile=NULL;
  int port=443;
  
  char *host_and_port=NULL;
  
  unsigned int len=0;
  
  SSL *ssl=NULL;
  BIO *bio=NULL; 
  SSL_CTX *ctx=NULL;

  
  int r = 0;
  ssize_t rr = -1;
  ssize_t length=0;

  unsigned char *buffer=NULL;
  ssize_t l=0;
 
  unsigned char *https_stream=NULL;
  ssize_t https_stream_size=0;
 
  CAfile=strdup("mycert.pem");

  message=strdup("GET /path1/path2?arg1=val1&arg2=val2 HTTP/1.0\nHost: myhost.com\n\n");
  host_and_port=strdup("myhost.com:443");  
  
  SSL_load_error_strings();
  SSL_library_init();
  ERR_load_BIO_strings();
  OpenSSL_add_all_algorithms();

  if (!(ctx = SSL_CTX_new(TLS_client_method())))
    {
      fprintf(stderr,"The creation of a new SSL_CTX object failed.\n");
      fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
      fprintf(stderr, "%s:", ERR_error_string(ERR_get_error(), NULL));
      return -1;
    }
  
  
  if (!(r = SSL_CTX_load_verify_locations(ctx, CAfile, NULL)))
    {
      fprintf(stderr,"Unable to load the trust certificate from file: %s\n",CAfile);
      fprintf(stderr, "Error: %s\n",ERR_reason_error_string(ERR_get_error()));
      fprintf(stderr, "%s:",ERR_error_string(ERR_get_error(), NULL));
      return -1;
    }
  
  /* Setting up the BIO SSL object */
  bio = BIO_new_ssl_connect(ctx);
  BIO_get_ssl(bio, &ssl);
  if (!ssl)
    {
      fprintf(stderr,"Unable to allocate SSL pointer.\n");
      fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
      fprintf(stderr, "%s:",ERR_error_string(ERR_get_error(), NULL));
      return -1;
    }
  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
  
  /* Attempt to connect */
  BIO_set_conn_hostname(bio, host_and_port);

  /* Verify the connection opened and perform the handshake */
  if (BIO_do_connect(bio) < 1)
    {
      fprintf(stderr,"Unable to connect BIO. %s\n",host_and_port);
      fprintf(stderr, "Error: %s\n",ERR_reason_error_string(ERR_get_error()));
      fprintf(stderr, "%s:",ERR_error_string(ERR_get_error(), NULL));   
      return -1;
    }

  length=strlen(message);
 
  rr=-1;
  while (rr < 0)
    {
      rr = BIO_write(bio, message, length);
      if (rr <= 0)
    {
      if (!BIO_should_retry(bio))
        {
          fprintf(stderr,"BIO_write should retry.\n");
          fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
          fprintf(stderr, "%s:", ERR_error_string(ERR_get_error(), NULL));
          return -1;
        }
    }
    }
  
  if (rr!=length)
    {
      fprintf(stderr,"Error in sending encripted message.\n");
      return -1;
    }

  if (buffer)
    {
      free(buffer);
      buffer=NULL;
    }
  
  
  if (!(https_stream=malloc(sizeof(char))))
    {
      fprintf(stderr, "Allocation memory failed (https_stream string), code=%d (%s)\n",errno, strerror(errno));
      return -1;
    }
  https_stream[0]=0;

  https_stream_size=0;
  length=4096;
  if (!(buffer=malloc(length*sizeof(char))))
    {
      fprintf(stderr, "Allocation memory failed (buffer string), code=%d (%s)\n",errno, strerror(errno));
      return -1;
    }
  
  bzero(buffer,length);   

  rr=-1;
  while((rr = BIO_read(bio, buffer, length)))
    {
      if (rr<0)
    if (!BIO_should_retry(bio))
      {
        fprintf(stderr,"BIO_read should retry.\n");
        fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
        fprintf(stderr, "%s:", ERR_error_string(ERR_get_error(), NULL));
        return -1;
      }
      l=https_stream_size;
      https_stream_size += rr;
      if (!(https_stream=realloc(https_stream,https_stream_size*sizeof(char))))
        {
          fprintf(stderr, "Re-allocation memory failed (*https_stream string), code=%d (%s)\n",errno, strerror(errno));
          return -1;
    }
      memcpy(https_stream+l,buffer,rr);
      bzero(buffer,length);
    }
  
  /* clean up the SSL context resources for the encrypted link */
  SSL_CTX_free(ctx);

  
  fprintf(stdout,"==========================================\n");
  fprintf(stdout,"HTTP Request\n");
  fprintf(stdout,"==========================================\n");
  fprintf(stdout,"%s\n",message);
  fprintf(stdout,"==========================================\n");
  fprintf(stdout,"\n");
  fprintf(stdout,"Host and port: %s\n",host_and_port);
  fprintf(stdout,"\n");
  
  fprintf(stdout,"HTTP Response\n");
  fprintf(stdout,"==========================================\n");
  fprintf(stdout,"%s\n",https_stream);
  fprintf(stdout,"==========================================\n");
  
  
  
  if (message)
    {
      free(message);
      message=NULL;
    }

  if (buffer)
    {
      free(buffer);
      buffer=NULL;
    }

  
  if (https_stream)
    {
      free(https_stream);
      https_stream=NULL;
    }
  
  if (host_and_port)
    {
      free(host_and_port);
      host_and_port=NULL;
    }
 
  
  return 0;
}

客户端运行输出

==========================================
HTTP Request
==========================================
GET /path1/path2?arg1=val1&arg2=val2 HTTP/1.0
Host: myhost.com


==========================================

Host and port: myhost.com:443

HTTP Response
==========================================
HTTP/1.1 404 Not Found
Content-Length: 231
x-amz-request-id: tx00000000000000019debd-00636e7b40-39e6dd1-default
Accept-Ranges: bytes
Content-Type: application/xml
Date: Fri, 11 Nov 2022 16:41:36 GMT
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
Content-Security-Policy:script-src: https://www.google-analytics.com https://q.quora.com
Referrer-Policy: no-referrer-when-downgrade
Strict-Transport-Security: max-age=31536000;includeSubDomains;preload

<?xml version="1.0" encoding="UTF-8"?><Error><Code>NoSuchBucket</Code><BucketName>myhost.com</BucketName><RequestId>tx00000000000000019debd-00636e7b40-39e6dd1-default</RequestId><HostId>39e6dd1-default-default</HostId></Error>
==========================================

curl请求输出

curl -v --http1.0 "https://myhost.com/path1/path2?arg1=val1&arg2=val2"
*   Trying <host_IP>:443...
* Connected to myhost.com (<host_IP>) port 443 (#0)
* ALPN: offers http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
*  CAfile: none
*  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
...
*  SSL certificate verify ok.
> GET /path1/path2?arg1=val1&arg2=val2 HTTP/1.0
> Host: myhost.com
> User-Agent: curl/7.85.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: nginx/1.23.2
< date: Fri, 11 Nov 2022 17:40:16 GMT
< content-type: application/json; charset=utf-8
< transfer-encoding: chunked
< x-powered-by: Express
< set-cookie: 3866fdcd36aeaae468ebac902177effe=5f9cbf1450843f212673d67cb86a2f9a; path=/; HttpOnly
< cache-control: private
< X-Frame-Options: DENY
< X-Content-Type-Options: nosniff
< X-Xss-Protection: 1; mode=block
< Content-Security-Policy:script-src: https://www.google-analytics.com https://q.quora.com
< Referrer-Policy: no-referrer-when-downgrade
< Strict-Transport-Security: max-age=31536000;includeSubDomains;preload
< 

问题原因与修复

问题原因

返回的NoSuchBucket错误表明服务器将请求路由到了默认存储桶(myhost.com)而非预期的API服务。核心原因是客户端未设置SNI(Server Name Indication)——现代虚拟主机依赖SNI在TLS握手阶段识别目标域名,curl会自动发送SNI,但代码中缺少这一步,导致服务器无法正确解析要访问的虚拟主机。

修复方法

在获取ssl指针后、调用BIO_do_connect之前,添加SNI设置:

// 在SSL_set_mode之后添加以下代码
SSL_set_tlsext_host_name(ssl, "myhost.com");

修改后的关键代码片段:

BIO_get_ssl(bio, &ssl);
if (!ssl)
{
  fprintf(stderr,"Unable to allocate SSL pointer.\n");
  fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
  fprintf(stderr, "%s:",ERR_error_string(ERR_get_error(), NULL));
  return -1;
}
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
// 添加SNI设置
SSL_set_tlsext_host_name(ssl, "myhost.com");

/* Attempt to connect */
BIO_set_conn_hostname(bio, host_and_port);

验证说明

添加SNI后,TLS握手时会向服务器发送目标域名myhost.com,服务器就能正确路由到对应的API服务,返回预期的200响应。

内容的提问来源于stack exchange,提问作者gda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:15:35