主要为了测试api的一些特性
3.2 :
发现不用带有content-length 字段也可以发送,但文档中说的是 must provide,待进一步确认
3.22:
做了个工具,推单条的
-----
via github:
https://github.com/wardenlym/apns2-demo
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdbool.h> 4 #include <fcntl.h> 5 #include <string.h> 6 #include <errno.h> 7 8 #include <sys/socket.h> 9 #include <netdb.h> 10 #include <netinet/in.h> 11 #include <arpa/inet.h> 12 #include <netinet/tcp.h> 13 14 #include <sys/epoll.h> 15 16 #include <openssl/ssl.h> 17 #include <openssl/err.h> 18 #include <openssl/bio.h> 19 20 #include <nghttp2/nghttp2.h> 21 22 23 enum { 24 IO_NONE, 25 WANT_READ, 26 WANT_WRITE 27 }; 28 29 #define MAKE_NV(NAME, VALUE) \ 30 { \ 31 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ 32 NGHTTP2_NV_FLAG_NONE \ 33 } 34 35 #define MAKE_NV_CS(NAME, VALUE) \ 36 { \ 37 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ 38 NGHTTP2_NV_FLAG_NONE \ 39 } 40 41 struct connection_t { 42 int fd; 43 SSL_CTX *ssl_ctx; 44 SSL *ssl; 45 nghttp2_session *session; 46 int want_io; 47 }; 48 49 struct uri_t { 50 const char *url; 51 const char *prefix; 52 const char *token; 53 uint16_t port; 54 const char *cert; 55 char *path; 56 }; 57 58 struct request_t { 59 struct uri_t uri; 60 uint8_t *data; 61 size_t data_len; 62 }; 63 64 struct loop_t { 65 int epfd; 66 }; 67 68 static void 69 die(const char *msg) 70 { 71 fprintf(stderr, "FATAL: %s\n", msg); 72 exit(EXIT_FAILURE); 73 } 74 75 static void 76 diec(const char *msg,int i) 77 { 78 fprintf(stderr, "FATAL: %s %d\n", msg,i); 79 exit(EXIT_FAILURE); 80 } 81 82 static bool 83 file_exsit(const char *f) 84 { 85 return 0 == access(f, 0) ? true : (printf("file not exsit:%s\n",f),false); 86 } 87 88 static bool 89 option_is_test(int argc, const char *arg1) 90 { 91 if (argc == 2 && 0 == strcmp(arg1, "test")) { 92 return true; 93 } else { 94 return false; 95 } 96 } 97 98 static bool 99 option_is_regular(int argc, const char *token, const char *cert, const char *msg) 100 { 101 if (argc == 4 && file_exsit(cert) && (msg!=NULL)) { 102 return true; 103 } else { 104 return false; 105 } 106 } 107 108 struct uri_t 109 make_uri(const char *url, uint16_t port, const char *prefix, const char *token ,const char *cert) 110 { 111 struct uri_t uri; 112 uri.url = url; 113 uri.port = port; 114 uri.prefix = prefix; 115 uri.token = token; 116 uri.cert = cert; 117 118 uri.path = malloc(strlen(prefix)+strlen(token)+1); 119 memset(uri.path,0,strlen(prefix)+strlen(token)+1); 120 strcat(uri.path,prefix); 121 strcat(uri.path,token); 122 return uri; 123 } 124 125 static void 126 init_global_library() 127 { 128 SSL_library_init(); 129 SSL_load_error_strings(); 130 } 131 132 static int 133 connect_to_url(const char *url, uint16_t port) 134 { 135 int sockfd; 136 int rv; 137 struct addrinfo hints, *res, *ressave; 138 char port_str[6]; 139 140 bzero(&hints, sizeof(struct addrinfo)); 141 bzero(port_str, sizeof(port_str)); 142 snprintf(port_str, 6, "%d", port); 143 hints.ai_family = AF_UNSPEC; 144 hints.ai_socktype = SOCK_STREAM; 145 146 printf("ns looking up ...\n"); 147 rv = getaddrinfo(url, port_str, &hints, &res); 148 if (rv != 0) { 149 freeaddrinfo(res); 150 return -1; 151 } 152 153 ressave = res; 154 do { 155 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 156 if(sockfd < 0) { 157 continue; 158 } 159 struct in_addr a = ((struct sockaddr_in*)res->ai_addr)->sin_addr; 160 const char *p = inet_ntoa(a); 161 printf("connecting to : %s\n",p); 162 while ((rv = connect(sockfd, res->ai_addr, res->ai_addrlen)) == -1 && 163 errno == EINTR) 164 ; 165 if (0 == rv) { 166 freeaddrinfo(ressave); 167 return sockfd; 168 } else { 169 close(sockfd); 170 } 171 } while ((res = res->ai_next) != NULL); 172 173 freeaddrinfo(ressave); 174 return -1; 175 } 176 177 static bool 178 socket_connect(const struct uri_t *uri, struct connection_t *conn) 179 { 180 int fd; 181 fd = connect_to_url(uri->url,uri->port); 182 if (fd > 0) { 183 conn->fd = fd; 184 printf("socket connect ok: fd=%d, host: %s:%d\n", conn->fd, uri->url, uri->port); 185 return true; 186 } 187 die("socket connect fail."); 188 return false; 189 } 190 191 static X509* 192 read_x509_certificate(const char* path) 193 { 194 BIO *bio = NULL; 195 X509 *x509 = NULL; 196 if (NULL == (bio = BIO_new_file(path, "r"))) { 197 return NULL; 198 } 199 x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); 200 BIO_free(bio); 201 return x509; 202 } 203 204 /* 205 * Callback function for TLS NPN. Since this program only supports 206 * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2 207 * library supports, we terminate program. 208 */ 209 static int 210 select_next_proto_cb(SSL *ssl, unsigned char **out, 211 unsigned char *outlen, const unsigned char *in, 212 unsigned int inlen, void *arg) 213 { 214 int rv; 215 /* nghttp2_select_next_protocol() selects HTTP/2 protocol the 216 nghttp2 library supports. */ 217 rv = nghttp2_select_next_protocol(out, outlen, in, inlen); 218 if (rv <= 0) { 219 die("Server did not advertise HTTP/2 protocol"); 220 } 221 return SSL_TLSEXT_ERR_OK; 222 } 223 224 static void 225 init_ssl_ctx(SSL_CTX *ssl_ctx) 226 { 227 /* Disable SSLv2 and enable all workarounds for buggy servers */ 228 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); 229 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); 230 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); 231 /* Set NPN callback */ 232 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); 233 } 234 235 static bool 236 ssl_allocate(struct connection_t *conn, const char *cert) 237 { 238 int rv; 239 X509 *x509 = NULL; 240 SSL_CTX *ssl_ctx = NULL; 241 SSL *ssl = NULL; 242 243 if (NULL == (x509 = read_x509_certificate(cert))) { 244 return false; 245 } 246 247 ssl_ctx = SSL_CTX_new(SSLv23_client_method()); 248 if (ssl_ctx == NULL) { 249 X509_free(x509); 250 } 251 init_ssl_ctx(ssl_ctx); 252 253 rv = SSL_CTX_use_certificate(ssl_ctx, x509); 254 X509_free(x509); 255 if (rv != 1) { 256 SSL_CTX_free(ssl_ctx); 257 return false; 258 } 259 260 rv = SSL_CTX_use_PrivateKey_file(ssl_ctx, cert, SSL_FILETYPE_PEM); 261 if (rv != 1) { 262 SSL_CTX_free(ssl_ctx); 263 return false; 264 } 265 266 rv = SSL_CTX_check_private_key(ssl_ctx); 267 if (rv != 1) { 268 SSL_CTX_free(ssl_ctx); 269 return false; 270 } 271 272 ssl = SSL_new(ssl_ctx); 273 if (ssl == NULL) { 274 SSL_CTX_free(ssl_ctx); 275 return false; 276 } 277 278 conn->ssl_ctx = ssl_ctx; 279 conn->ssl = ssl; 280 return true; 281 } 282 283 static bool 284 ssl_handshake(SSL *ssl, int fd) 285 { 286 int rv; 287 if (SSL_set_fd(ssl, fd) == 0) { 288 return false; 289 } 290 ERR_clear_error(); 291 rv = SSL_connect(ssl); 292 if (rv <= 0) { 293 fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); 294 return false; 295 } 296 return true; 297 } 298 299 static bool 300 ssl_connect(const struct uri_t *uri, struct connection_t *conn) 301 { 302 if (ssl_allocate(conn,uri->cert)) { 303 fprintf(stdout, "ssl allocation ok\n"); 304 } else { 305 fprintf(stderr, "ssl allocation error\n"); 306 return false; 307 } 308 309 fprintf(stderr, "ssl handshaking ...\n"); 310 if (ssl_handshake(conn->ssl, conn->fd)) { 311 fprintf(stderr, "ssl handshake ok\n"); 312 } else { 313 fprintf(stderr, "ssl handshake error\n"); 314 return false; 315 } 316 317 printf("tls/ssl connect ok: protocol= \n"); 318 return true; 319 } 320 321 // callback impelement 322 #define _U_ 323 /* 324 * The implementation of nghttp2_send_callback type. Here we write 325 * |data| with size |length| to the network and return the number of 326 * bytes actually written. See the documentation of 327 * nghttp2_send_callback for the details. 328 */ 329 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, 330 size_t length, int flags _U_, void *user_data) { 331 332 int rv; 333 struct connection_t *conn = user_data; 334 conn->want_io = IO_NONE; 335 ERR_clear_error(); 336 rv = SSL_write(conn->ssl, data, (int)length); 337 if (rv <= 0) { 338 int err = SSL_get_error(conn->ssl, rv); 339 if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { 340 conn->want_io = 341 (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); 342 rv = NGHTTP2_ERR_WOULDBLOCK; 343 } else { 344 rv = NGHTTP2_ERR_CALLBACK_FAILURE; 345 } 346 } 347 return rv; 348 } 349 350 /* 351 * The implementation of nghttp2_recv_callback type. Here we read data 352 * from the network and write them in |buf|. The capacity of |buf| is 353 * |length| bytes. Returns the number of bytes stored in |buf|. See 354 * the documentation of nghttp2_recv_callback for the details. 355 */ 356 static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf, 357 size_t length, int flags _U_, void *user_data) { 358 359 struct connection_t *conn; 360 int rv; 361 conn = (struct connection_t *)user_data; 362 conn->want_io = IO_NONE; 363 ERR_clear_error(); 364 rv = SSL_read(conn->ssl, buf, (int)length); 365 if (rv < 0) { 366 int err = SSL_get_error(conn->ssl, rv); 367 if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { 368 conn->want_io = 369 (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); 370 rv = NGHTTP2_ERR_WOULDBLOCK; 371 } else { 372 rv = NGHTTP2_ERR_CALLBACK_FAILURE; 373 } 374 } else if (rv == 0) { 375 rv = NGHTTP2_ERR_EOF; 376 } 377 return rv; 378 } 379 380 static int on_frame_send_callback(nghttp2_session *session, 381 const nghttp2_frame *frame, 382 void *user_data _U_) { 383 size_t i; 384 switch (frame->hd.type) { 385 case NGHTTP2_HEADERS: 386 if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { 387 const nghttp2_nv *nva = frame->headers.nva; 388 printf("[INFO] C ----------------------------> S (HEADERS)\n"); 389 for (i = 0; i < frame->headers.nvlen; ++i) { 390 fwrite(nva[i].name, nva[i].namelen, 1, stdout); 391 printf(": "); 392 fwrite(nva[i].value, nva[i].valuelen, 1, stdout); 393 printf("\n"); 394 } 395 } 396 break; 397 case NGHTTP2_RST_STREAM: 398 printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); 399 break; 400 case NGHTTP2_GOAWAY: 401 printf("[INFO] C ----------------------------> S (GOAWAY)\n"); 402 break; 403 } 404 return 0; 405 } 406 407 static int on_frame_recv_callback(nghttp2_session *session, 408 const nghttp2_frame *frame, 409 void *user_data _U_) { 410 switch (frame->hd.type) { 411 case NGHTTP2_HEADERS: 412 if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { 413 struct connection_t *conn = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 414 if (conn) { 415 printf("[INFO] C <---------------------------- S (HEADERS end)\n"); 416 } 417 } else { 418 printf("other header: %d",frame->headers.cat); 419 } 420 break; 421 case NGHTTP2_RST_STREAM: 422 printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); 423 break; 424 case NGHTTP2_GOAWAY: 425 printf("[INFO] C <---------------------------- S (GOAWAY)\n"); 426 break; 427 } 428 return 0; 429 } 430 431 static int on_header_callback(nghttp2_session *session, 432 const nghttp2_frame *frame, 433 const uint8_t *name, size_t namelen, 434 const uint8_t *value, size_t valuelen, 435 uint8_t flags, void *user_data) { 436 437 if (frame->hd.type == NGHTTP2_HEADERS) { 438 fwrite(name, namelen, 1, stdout); 439 printf(": "); 440 fwrite(value, valuelen, 1, stdout); 441 printf("\n"); 442 443 } 444 return 0; 445 } 446 447 static int on_begin_headers_callback(nghttp2_session *session, 448 const nghttp2_frame *frame, 449 void *user_data) { 450 printf("[INFO] C <---------------------------- S (HEADERS begin)\n"); 451 return 0; 452 } 453 454 /* 455 * The implementation of nghttp2_on_stream_close_callback type. We use 456 * this function to know the response is fully received. Since we just 457 * fetch 1 resource in this program, after reception of the response, 458 * we submit GOAWAY and close the session. 459 */ 460 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 461 uint32_t error_code _U_, 462 void *user_data _U_) { 463 struct connection_t *conn = nghttp2_session_get_stream_user_data(session, stream_id); 464 if (conn) { 465 int rv; 466 rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); 467 468 if (rv != 0) { 469 diec("nghttp2_session_terminate_session", rv); 470 } 471 } 472 return 0; 473 } 474 475 /* 476 * The implementation of nghttp2_on_data_chunk_recv_callback type. We 477 * use this function to print the received response body. 478 */ 479 static int on_data_chunk_recv_callback(nghttp2_session *session, 480 uint8_t flags _U_, int32_t stream_id, 481 const uint8_t *data, size_t len, 482 void *user_data _U_) { 483 printf("%s\n",__FUNCTION__); 484 char buf[1024] = {0}; 485 memcpy(buf,data,len); 486 buf[len]=0; 487 printf("%s\n",buf); 488 return 0; 489 } 490 491 /* 492 * Setup callback functions. nghttp2 API offers many callback 493 * functions, but most of them are optional. The send_callback is 494 * always required. Since we use nghttp2_session_recv(), the 495 * recv_callback is also required. 496 */ 497 static void 498 setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) 499 { 500 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 501 nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); 502 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback); 503 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); 504 nghttp2_session_callbacks_set_on_header_callback(callbacks,on_header_callback); 505 nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks,on_begin_headers_callback); 506 nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback); 507 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback); 508 509 } 510 511 static bool 512 set_nghttp2_session_info(struct connection_t *conn) 513 { 514 int rv; 515 nghttp2_session_callbacks *callbacks; 516 517 rv = nghttp2_session_callbacks_new(&callbacks); 518 if (rv != 0) { 519 fprintf(stderr, "nghttp2_session_callbacks_new"); 520 } 521 setup_nghttp2_callbacks(callbacks); 522 rv = nghttp2_session_client_new(&conn->session, callbacks, conn); 523 if (rv != 0) { 524 fprintf(stderr, "nghttp2_session_client_new"); 525 } 526 nghttp2_session_callbacks_del(callbacks); 527 528 rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, NULL, 0); 529 if (rv != 0) { 530 fprintf(stderr, "nghttp2_submit_settings %d",rv); 531 } 532 return true; 533 } 534 535 static struct request_t 536 make_request(struct uri_t uri, const char *msg) 537 { 538 struct request_t req; 539 req.uri = uri; 540 req.data_len = strlen(msg); 541 req.data = malloc(req.data_len); 542 memcpy(req.data, msg, req.data_len); 543 return req; 544 } 545 546 static int 547 set_nonblocking(int fd) 548 { 549 int flags, rv; 550 while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) 551 ; 552 if (flags == -1) { 553 return -1; 554 } 555 while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) 556 ; 557 if (rv == -1) { 558 return -1; 559 } 560 return 0; 561 } 562 563 static int 564 set_tcp_nodelay(int fd) 565 { 566 int val = 1; 567 if(-1 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val))) { 568 return -1; 569 } 570 return 0; 571 } 572 573 ssize_t data_prd_read_callback( 574 nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, 575 uint32_t *data_flags, nghttp2_data_source *source, void *user_data) { 576 struct request_t *req = source->ptr; 577 memcpy(buf,req->data,req->data_len); 578 *data_flags |= NGHTTP2_DATA_FLAG_EOF; 579 580 printf("[INFO] C ----------------------------> S (DATA post body)\n"); 581 char payload[1024]; 582 memcpy(payload,req->data,req->data_len); 583 payload[req->data_len]=0; 584 printf("%s\n",payload); 585 return req->data_len; 586 } 587 588 static int32_t 589 submit_request(struct connection_t *conn, const struct request_t* req) 590 { 591 int32_t stream_id; 592 const nghttp2_nv nva[] = { 593 MAKE_NV(":method", "POST"), 594 MAKE_NV_CS(":path", req->uri.path), 595 MAKE_NV("apns-id", "e77a3d12-bc9f-f410-a127-43f212597a9c") 596 }; 597 598 nghttp2_data_provider data_prd; 599 data_prd.source.ptr = (void*)req; 600 data_prd.read_callback = data_prd_read_callback; 601 602 stream_id = nghttp2_submit_request(conn->session, NULL, nva, 603 sizeof(nva) / sizeof(nva[0]), &data_prd, conn); 604 return stream_id; 605 } 606 607 608 static void 609 event_loop(struct loop_t *loop, struct connection_t *conn) 610 { 611 struct epoll_event ev,events[20]; 612 int epfd = loop->epfd; 613 614 ev.data.fd = conn->fd; 615 ev.events=EPOLLIN|EPOLLOUT; 616 epoll_ctl(epfd,EPOLL_CTL_ADD,conn->fd,&ev); 617 while (nghttp2_session_want_read(conn->session) || 618 nghttp2_session_want_write(conn->session)) { 619 int nfds=epoll_wait(epfd,events,20,-1); 620 int i; 621 for(i=0;i<nfds;++i) { 622 int rv; 623 if(events[i].events & EPOLLIN) { 624 rv = nghttp2_session_recv(conn->session); 625 if (rv != 0) { 626 diec("nghttp2_session_recv", rv); 627 } 628 ev.data.fd=events[i].data.fd; 629 ev.events = EPOLLOUT; 630 epoll_ctl(epfd,EPOLL_CTL_MOD,events[i].data.fd,&ev); 631 632 } else if(events[i].events & EPOLLOUT) { 633 rv = nghttp2_session_send(conn->session); 634 if (rv != 0) { 635 diec("nghttp2_session_send", rv); 636 } 637 ev.data.fd=events[i].data.fd; 638 ev.events = EPOLLIN; 639 epoll_ctl(epfd,EPOLL_CTL_MOD,events[i].data.fd,&ev); 640 } else { 641 if ((events[i].events & EPOLLHUP) || (events[i].events & EPOLLERR)) { 642 epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,NULL); 643 } else { 644 printf("%s\n","epoll other"); 645 } 646 } 647 } 648 } 649 } 650 651 static bool 652 blocking_post(struct loop_t *loop, struct connection_t *conn, const struct request_t *req) 653 { 654 set_nonblocking(conn->fd); 655 set_tcp_nodelay(conn->fd); 656 657 int32_t stream_id; 658 stream_id = submit_request(conn, req); 659 if (stream_id < 0) { 660 printf("stream id error: %d\n",stream_id); 661 return false; 662 } 663 664 printf("[INFO] Stream ID = %d\n", stream_id); 665 666 loop->epfd = epoll_create1(0); 667 668 if (loop->epfd < 0) { 669 printf("epoll_create fail : %d\n", loop->epfd); 670 return false; 671 } 672 673 /* maybe running in a thread */ 674 event_loop(loop,conn); 675 676 close(loop->epfd); 677 loop->epfd = -1; 678 printf("over.\n"); 679 return true; 680 } 681 682 static void 683 connection_cleanup(struct connection_t *conn) 684 { 685 if (conn->session && 686 conn->ssl && 687 conn->ssl_ctx) { 688 nghttp2_session_del(conn->session); 689 SSL_shutdown(conn->ssl); 690 SSL_free(conn->ssl); 691 SSL_CTX_free(conn->ssl_ctx); 692 shutdown(conn->fd, SHUT_WR); 693 close(conn->fd); 694 } 695 } 696 697 void 698 usage() 699 { 700 printf("usage: apns2demo token cert message \n"); 701 } 702 703 static void test(); 704 705 int 706 main(int argc, const char *argv[]) 707 { 708 struct connection_t conn; 709 struct uri_t uri; 710 struct loop_t loop; 711 const char *msg; 712 713 if (argc == 1) { 714 /* default: my test device info */ 715 uri = make_uri("api.push.apple.com", 2197, "/3/device/", 716 "73f98e1833fa744403fb4447e0f3a054d43f433b80e48c5bcaa62b501fd0f956", 717 "1fa5281c6c1d4cf5bb0bbbe0_dis_certkey.pem"); 718 msg="{\"aps\":{\"alert\":\"nghttp2 test.\",\"sound\":\"default\"}}"; 719 } else if (option_is_test(argc,argv[1])) { 720 test(); 721 exit(0); 722 } else if (option_is_regular(argc, argv[1], argv[2], argv[3])) { 723 /* production */ 724 uri = make_uri("api.push.apple.com", 2197, "/3/device/", argv[1], argv[2]); 725 msg = argv[3]; 726 } else { 727 usage(); 728 exit(0); 729 } 730 731 732 printf("nghttp2 version: %s\n", NGHTTP2_VERSION); 733 printf("tls/ssl version: %s\n", SSL_TXT_TLSV1_2); 734 735 init_global_library(); 736 737 socket_connect(&uri, &conn); 738 if(!ssl_connect(&uri, &conn)) 739 die("ssl connect fail."); 740 set_nghttp2_session_info(&conn); 741 742 struct request_t req = make_request(uri,msg); 743 blocking_post(&loop, &conn, &req); 744 745 connection_cleanup(&conn); 746 747 return 0; 748 } 749 750 static void 751 test() 752 { 753 // bad path 754 755 // invalid token 756 757 // feedback 758 759 // 目前以上几个response经我测试,行为跟官方文档完全相同,省略 760 761 }