主要为了测试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 }