nginx 使用C https客户端时出现HTTP 404错误

x0fgdtte  于 2022-11-21  发布在  Nginx
关注(0)|答案(1)|浏览(105)

这里有一个很奇怪的问题...我开发了一个小型的https客户端,从一个公共API获取一些数据(不需要认证)问题是当我试图获取数据时,我总是得到一个404 HTTP错误,即使我确信我在发送给服务器的GET命令中请求的资源的路径是正确的。如果我尝试用curl获取相同的资源(使用完全相同的路径),它可以毫无问题地工作,这一事实也证实了这一点。
这是我编写的https客户端(下面是我在网上找到的一个示例):

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\r\nHost: myhost.com\r\n\r\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;
}

其中,“myhost.com“和“/path 1/path 2?arg 1 =val1&arg2= val 2”分别表示主机的完整地址和要获取的资源的路径。
此代码编译良好(没有任何警告),输出为:

==========================================
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>
==========================================

因此,正如您所看到的,我得到了HTTP错误代码404。现在,如果在同一台计算机上使用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
<

正如你所看到的,curl可以正确地返回HTTP代码200。这证明了在服务器上,我所寻找的资源实际上在指定的路径上是可用的(这是我在curl和我的客户端中指定的相同路径)。因此,我写的客户端中应该有什么错误,不幸的是我不能弄清楚。有什么想法吗?任何帮助都是非常欢迎的。
很遗憾,我无法访问服务器日志文件....
提前感谢您的可用性!

jogvjijk

jogvjijk1#

我怀疑curl自动把协议升级到了1.1。

ALPN: offers http/1.1

ALPN: server did not agree on a protocol. Uses default.

您要包括主机标头,因此请尝试使用1.1。

message=strdup("GET /path1/path2?arg1=val1&arg2=val2 HTTP/1.1\r\nHost: myhost.com\r\n\r\n");

相关问题