1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 /* <DESC>
25 * Demonstrate curl_easy_send() and curl_easy_recv() usage.
26 * </DESC>
27 */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <curl/curl.h>
32
33 /* Avoid warning in FD_SET() with pre-2020 Cygwin/MSYS releases:
34 * warning: conversion to 'long unsigned int' from 'curl_socket_t' {aka 'int'}
35 * may change the sign of the result [-Wsign-conversion]
36 */
37 #ifdef __GNUC__
38 #pragma GCC diagnostic ignored "-Wsign-conversion"
39 #ifdef __DJGPP__
40 #pragma GCC diagnostic ignored "-Warith-conversion"
41 #endif
42 #elif defined(_MSC_VER)
43 #pragma warning(disable:4127) /* conditional expression is constant */
44 #endif
45
46 /* Auxiliary function that waits on the socket. */
wait_on_socket(curl_socket_t sockfd,int for_recv,long timeout_ms)47 static int wait_on_socket(curl_socket_t sockfd, int for_recv, long timeout_ms)
48 {
49 struct timeval tv;
50 fd_set infd, outfd, errfd;
51 int res;
52
53 #if defined(MSDOS) || defined(__AMIGA__)
54 tv.tv_sec = (time_t)(timeout_ms / 1000);
55 tv.tv_usec = (time_t)(timeout_ms % 1000) * 1000;
56 #else
57 tv.tv_sec = timeout_ms / 1000;
58 tv.tv_usec = (int)(timeout_ms % 1000) * 1000;
59 #endif
60
61 FD_ZERO(&infd);
62 FD_ZERO(&outfd);
63 FD_ZERO(&errfd);
64
65 FD_SET(sockfd, &errfd); /* always check for error */
66
67 if(for_recv) {
68 FD_SET(sockfd, &infd);
69 }
70 else {
71 FD_SET(sockfd, &outfd);
72 }
73
74 /* select() returns the number of signalled sockets or -1 */
75 res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
76 return res;
77 }
78
main(void)79 int main(void)
80 {
81 CURL *curl;
82 /* Minimalistic http request */
83 const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
84 size_t request_len = strlen(request);
85
86 /* A general note of caution here: if you are using curl_easy_recv() or
87 curl_easy_send() to implement HTTP or _any_ other protocol libcurl
88 supports "natively", you are doing it wrong and you should stop.
89
90 This example uses HTTP only to show how to use this API, it does not
91 suggest that writing an application doing this is sensible.
92 */
93
94 curl = curl_easy_init();
95 if(curl) {
96 CURLcode res;
97 curl_socket_t sockfd;
98 size_t nsent_total = 0;
99
100 curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
101 /* Do not do the transfer - only connect to host */
102 curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
103 res = curl_easy_perform(curl);
104
105 if(res != CURLE_OK) {
106 printf("Error: %s\n", curl_easy_strerror(res));
107 return 1;
108 }
109
110 /* Extract the socket from the curl handle - we need it for waiting. */
111 res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd);
112
113 if(res != CURLE_OK) {
114 printf("Error: %s\n", curl_easy_strerror(res));
115 return 1;
116 }
117
118 printf("Sending request.\n");
119
120 do {
121 /* Warning: This example program may loop indefinitely.
122 * A production-quality program must define a timeout and exit this loop
123 * as soon as the timeout has expired. */
124 size_t nsent;
125 do {
126 nsent = 0;
127 res = curl_easy_send(curl, request + nsent_total,
128 request_len - nsent_total, &nsent);
129 nsent_total += nsent;
130
131 if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 0, 60000L)) {
132 printf("Error: timeout.\n");
133 return 1;
134 }
135 } while(res == CURLE_AGAIN);
136
137 if(res != CURLE_OK) {
138 printf("Error: %s\n", curl_easy_strerror(res));
139 return 1;
140 }
141
142 printf("Sent %lu bytes.\n", (unsigned long)nsent);
143
144 } while(nsent_total < request_len);
145
146 printf("Reading response.\n");
147
148 for(;;) {
149 /* Warning: This example program may loop indefinitely (see above). */
150 char buf[1024];
151 size_t nread;
152 do {
153 nread = 0;
154 res = curl_easy_recv(curl, buf, sizeof(buf), &nread);
155
156 if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) {
157 printf("Error: timeout.\n");
158 return 1;
159 }
160 } while(res == CURLE_AGAIN);
161
162 if(res != CURLE_OK) {
163 printf("Error: %s\n", curl_easy_strerror(res));
164 break;
165 }
166
167 if(nread == 0) {
168 /* end of the response */
169 break;
170 }
171
172 printf("Received %lu bytes.\n", (unsigned long)nread);
173 }
174
175 /* always cleanup */
176 curl_easy_cleanup(curl);
177 }
178 return 0;
179 }
180