rts-http.c
1 //! @file rts-http.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2023 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! HTTP client.
25
26 #include "a68g.h"
27 #include "a68g-genie.h"
28 #include "a68g-prelude.h"
29 #include "a68g-transput.h"
30
31 #if defined (BUILD_WWW)
32
33 #if defined (HAVE_NETDB_H)
34 # include <netdb.h>
35 #endif
36
37 #if defined (HAVE_NETINET_IN_H)
38 # include <netinet/in.h>
39 #endif
40
41 #if defined (HAVE_SYS_SELECT_H)
42 # include <sys/select.h>
43 #endif
44
45 #if defined (HAVE_SYS_SOCKET_H)
46 # include <sys/socket.h>
47 #endif
48
49 #define PROTOCOL "tcp"
50 #define SERVICE "http"
51
52 #define CONTENT_BUFFER_SIZE (64 * KILOBYTE)
53 #define TIMEOUT_INTERVAL 15
54
55 // Own implementation of connect with time-out.
56
57 int a68_connect (int socket_id, struct sockaddr * addr, size_t addrlen, struct timeval * timeout)
58 {
59 int flags = fcntl (socket_id, F_GETFL, NULL);
60 if (flags < 0) {
61 return -1;
62 }
63 if (fcntl (socket_id, F_SETFL, flags | O_NONBLOCK) < 0) {
64 return -1;
65 }
66 int ret = connect (socket_id, addr, addrlen);
67 if (ret < 0) {
68 if (errno == EINPROGRESS) {
69 fd_set wait_set;
70 FD_ZERO (&wait_set);
71 FD_SET (socket_id, &wait_set);
72 ret = select (socket_id + 1, NULL, &wait_set, NULL, timeout);
73 }
74 } else {
75 ret = 1;
76 }
77 if (fcntl (socket_id, F_SETFL, flags) < 0) {
78 return -1;
79 }
80 if (ret < 0) {
81 return -1;
82 } else if (ret == 0) {
83 errno = ETIMEDOUT;
84 return 1;
85 } else {
86 socklen_t len = sizeof (flags);
87 if (getsockopt (socket_id, SOL_SOCKET, SO_ERROR, &flags, &len) < 0) {
88 return -1;
89 }
90 if (flags) {
91 errno = flags;
92 return -1;
93 }
94 }
95 return 0;
96 }
97
98 //! @brief PROC (REF STRING, STRING, STRING, INT) INT http content
99
100 void genie_http_content (NODE_T * p)
101 {
102 // Send GET request to server and yield answer (TCP/HTTP only).
103 errno = 0;
104 // Pop arguments.
105 A68_INT port;
106 POP_OBJECT (p, &port, A68_INT);
107 CHECK_INIT (p, INITIALISED (&port), M_INT);
108 A68_REF path_string, domain_string, content_string;
109 POP_REF (p, &path_string);
110 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
111 POP_REF (p, &domain_string);
112 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
113 POP_REF (p, &content_string);
114 CHECK_REF (p, content_string, M_REF_STRING);
115 *DEREF (A68_REF, &content_string) = empty_string (p);
116 // Reset buffers.
117 reset_transput_buffer (DOMAIN_BUFFER);
118 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
119 reset_transput_buffer (PATH_BUFFER);
120 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
121 // Compose request. Double \r\n at the end is essential!
122 reset_transput_buffer (REQUEST_BUFFER);
123 add_string_transput_buffer (p, REQUEST_BUFFER, "GET ");
124 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
125 add_string_transput_buffer (p, REQUEST_BUFFER, " HTTP/1.0\r\n");
126 add_string_transput_buffer (p, REQUEST_BUFFER, "Host: ");
127 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (DOMAIN_BUFFER));
128 add_string_transput_buffer (p, REQUEST_BUFFER, "\r\nAccept: */*\r\nConnection: close\r\n\r\n");
129 // Connect to host.
130 struct timeval a68_timeout;
131 TV_SEC (&a68_timeout) = TIMEOUT_INTERVAL;
132 TV_USEC (&a68_timeout) = 0;
133 struct sockaddr_in socket_address;
134 FILL (&socket_address, 0, (int) sizeof (socket_address));
135 SIN_FAMILY (&socket_address) = AF_INET;
136 struct servent *service_address = getservbyname (SERVICE, PROTOCOL);
137 if (service_address == NULL) {
138 PUSH_VALUE (p, 1, A68_INT);
139 return;
140 }
141 if (VALUE (&port) == 0) {
142 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
143 } else {
144 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port))));
145 if (SIN_PORT (&socket_address) == 0) {
146 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
147 return;
148 }
149 }
150 struct hostent *host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
151 if (host_address == NULL) {
152 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
153 return;
154 }
155 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
156 struct protoent *protocol = getprotobyname (PROTOCOL);
157 if (protocol == NULL) {
158 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
159 return;
160 }
161 int socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
162 if (socket_id < 0) {
163 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
164 return;
165 }
166 int conn = a68_connect (socket_id, (struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address), &a68_timeout);
167 if (conn < 0) {
168 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
169 ASSERT (close (socket_id) == 0);
170 return;
171 }
172 // Read from host.
173 WRITE (socket_id, get_transput_buffer (REQUEST_BUFFER));
174 if (errno != 0) {
175 PUSH_VALUE (p, errno, A68_INT);
176 ASSERT (close (socket_id) == 0);
177 return;
178 }
179 // Initialise file descriptor set.
180 fd_set set;
181 FD_ZERO (&set);
182 FD_SET (socket_id, &set);
183 // Block until server replies or a68_timeout blows up.
184 switch (select (FD_SETSIZE, &set, NULL, NULL, &a68_timeout)) {
185 case 0: {
186 errno = ETIMEDOUT;
187 PUSH_VALUE (p, errno, A68_INT);
188 ASSERT (close (socket_id) == 0);
189 return;
190 }
191 case -1: {
192 PUSH_VALUE (p, errno, A68_INT);
193 ASSERT (close (socket_id) == 0);
194 return;
195 }
196 case 1: {
197 break;
198 }
199 default: {
200 ABEND (A68_TRUE, ERROR_ACTION, __func__);
201 }
202 }
203 // Read from the socket.
204 char buffer[CONTENT_BUFFER_SIZE];
205 reset_transput_buffer (CONTENT_BUFFER);
206 int k;
207 while ((k = (int) io_read (socket_id, &buffer, (CONTENT_BUFFER_SIZE - 1))) > 0) {
208 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
209 }
210 if (k < 0 || errno != 0) {
211 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
212 ASSERT (close (socket_id) == 0);
213 return;
214 }
215 // Convert string.
216 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
217 ASSERT (close (socket_id) == 0);
218 PUSH_VALUE (p, errno, A68_INT);
219 }
220
221 //! @brief PROC (REF STRING, STRING, STRING, INT) INT tcp request
222
223 void genie_tcp_request (NODE_T * p)
224 {
225 // Send request to server and yield answer (TCP only).
226 errno = 0;
227 // Pop arguments.
228 A68_INT port;
229 POP_OBJECT (p, &port, A68_INT);
230 CHECK_INIT (p, INITIALISED (&port), M_INT);
231 A68_REF request_string, domain_string, content_string;
232 POP_REF (p, &request_string);
233 CHECK_INIT (p, INITIALISED (&request_string), M_STRING);
234 POP_REF (p, &domain_string);
235 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
236 POP_REF (p, &content_string);
237 CHECK_REF (p, content_string, M_REF_STRING);
238 *DEREF (A68_REF, &content_string) = empty_string (p);
239 // Reset buffers.
240 reset_transput_buffer (DOMAIN_BUFFER);
241 reset_transput_buffer (REQUEST_BUFFER);
242 reset_transput_buffer (CONTENT_BUFFER);
243 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
244 add_a_string_transput_buffer (p, REQUEST_BUFFER, (BYTE_T *) & request_string);
245 add_string_transput_buffer (p, REQUEST_BUFFER, "\r\n\r\n");
246 // Connect to host.
247 struct timeval a68_timeout;
248 TV_SEC (&a68_timeout) = TIMEOUT_INTERVAL;
249 TV_USEC (&a68_timeout) = 0;
250 struct sockaddr_in socket_address;
251 FILL (&socket_address, 0, (int) sizeof (socket_address));
252 SIN_FAMILY (&socket_address) = AF_INET;
253 struct servent *service_address = getservbyname (SERVICE, PROTOCOL);
254 if (service_address == NULL) {
255 PUSH_VALUE (p, 1, A68_INT);
256 return;
257 }
258 if (VALUE (&port) == 0) {
259 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
260 } else {
261 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port))));
262 if (SIN_PORT (&socket_address) == 0) {
263 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
264 return;
265 }
266 }
267 struct hostent *host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
268 if (host_address == NULL) {
269 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
270 return;
271 }
272 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
273 struct protoent *protocol = getprotobyname (PROTOCOL);
274 if (protocol == NULL) {
275 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
276 return;
277 }
278 int socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
279 if (socket_id < 0) {
280 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
281 return;
282 }
283 int conn = a68_connect (socket_id, (struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address), &a68_timeout);
284 if (conn < 0) {
285 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
286 ASSERT (close (socket_id) == 0);
287 return;
288 }
289 // Read from host.
290 WRITE (socket_id, get_transput_buffer (REQUEST_BUFFER));
291 if (errno != 0) {
292 PUSH_VALUE (p, errno, A68_INT);
293 ASSERT (close (socket_id) == 0);
294 return;
295 }
296 // Initialise file descriptor set.
297 fd_set set;
298 FD_ZERO (&set);
299 FD_SET (socket_id, &set);
300 // Block until server replies or a68_timeout blows up.
301 switch (select (FD_SETSIZE, &set, NULL, NULL, &a68_timeout)) {
302 case 0: {
303 errno = ETIMEDOUT;
304 PUSH_VALUE (p, errno, A68_INT);
305 ASSERT (close (socket_id) == 0);
306 return;
307 }
308 case -1: {
309 PUSH_VALUE (p, errno, A68_INT);
310 ASSERT (close (socket_id) == 0);
311 return;
312 }
313 case 1: {
314 break;
315 }
316 default: {
317 ABEND (A68_TRUE, ERROR_ACTION, __func__);
318 }
319 }
320 char buffer[CONTENT_BUFFER_SIZE];
321 int k;
322 while ((k = (int) io_read (socket_id, &buffer, (CONTENT_BUFFER_SIZE - 1))) > 0) {
323 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
324 }
325 if (k < 0 || errno != 0) {
326 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
327 ASSERT (close (socket_id) == 0);
328 return;
329 }
330 // Convert string.
331 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
332 ASSERT (close (socket_id) == 0);
333 PUSH_VALUE (p, errno, A68_INT);
334 }
335
336 #endif