2 * Copyright (C) 2018 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 /* See web-server.h */
46 #include <sys/types.h>
48 #include <sys/socket.h>
53 static char tmpdir
[] = "/tmp/wsXXXXXX";
54 static char sockpath
[] = "............./sock";
55 static int listen_sock
= -1;
57 static struct stat statbuf
;
58 static char request
[16384];
60 static void *start_web_server (void *arg
);
61 static void handle_requests (int s
);
62 static void handle_request (int s
, bool headers_only
);
63 static void xwrite (int s
, const char *buf
, size_t len
);
64 static void xpread (int fd
, char *buf
, size_t count
, off_t offset
);
78 web_server (const char *filename
)
80 struct sockaddr_un addr
;
85 fd
= open (filename
, O_RDONLY
|O_CLOEXEC
);
90 if (fstat (fd
, &statbuf
) == -1) {
95 /* Create the temporary directory for the socket. */
96 if (mkdtemp (tmpdir
) == NULL
) {
101 /* Create the listening socket for the web server. */
102 memcpy (sockpath
, tmpdir
, strlen (tmpdir
));
103 listen_sock
= socket (AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
104 if (listen_sock
== -1) {
109 addr
.sun_family
= AF_UNIX
;
110 memcpy (addr
.sun_path
, sockpath
, strlen (sockpath
) + 1);
111 if (bind (listen_sock
, (struct sockaddr
*) &addr
, sizeof addr
) == -1) {
116 if (listen (listen_sock
, SOMAXCONN
) == -1) {
121 /* Run the web server in a separate thread. */
122 err
= pthread_create (&thread
, NULL
, start_web_server
, NULL
);
125 perror ("pthread_create");
128 err
= pthread_detach (thread
);
131 perror ("pthread_detach");
151 start_web_server (void *arg
)
155 fprintf (stderr
, "web server: listening on %s\n", sockpath
);
158 s
= accept (listen_sock
, NULL
, NULL
);
168 handle_requests (int s
)
173 fprintf (stderr
, "web server: accepted connection\n");
176 /* Read request until we see "\r\n\r\n" (end of headers) or EOF. */
179 if (n
>= sizeof request
- 1 /* allow one byte for \0 */) {
180 fprintf (stderr
, "web server: request too long\n");
183 sz
= sizeof request
- n
- 1;
184 r
= read (s
, &request
[n
], sz
);
195 if (strstr (request
, "\r\n\r\n"))
202 fprintf (stderr
, "web server: request:\n%s", request
);
204 /* HEAD or GET request? */
205 if (strncmp (request
, "HEAD ", 5) == 0)
206 handle_request (s
, true);
207 else if (strncmp (request
, "GET ", 4) == 0)
208 handle_request (s
, false);
210 /* Return 405 Method Not Allowed. */
211 const char response
[] =
212 "HTTP/1.1 405 Method Not Allowed\r\n"
213 "Content-Length: 0\r\n"
214 "Connection: close\r\n"
216 xwrite (s
, response
, strlen (response
));
226 handle_request (int s
, bool headers_only
)
228 uint64_t offset
, length
, end
;
230 const char response1_ok
[] = "HTTP/1.1 200 OK\r\n";
231 const char response1_partial
[] = "HTTP/1.1 206 Partial Content\r\n";
232 const char response2
[] =
233 "Accept-Ranges: bytes\r\n"
234 "Connection: keep-alive\r\n"
235 "Content-Type: application/octet-stream\r\n";
237 const char response4
[] = "\r\n";
240 /* If there's no Range request header then send the full size as the
243 p
= strcasestr (request
, "\r\nRange: bytes=");
246 length
= statbuf
.st_size
;
247 xwrite (s
, response1_ok
, strlen (response1_ok
));
251 if (sscanf (p
, "%" SCNu64
"-%" SCNu64
, &offset
, &end
) != 2) {
252 fprintf (stderr
, "web server: could not parse "
253 "range request from curl client\n");
256 /* Unclear but "Range: bytes=0-4" means bytes 0-3. '4' is the
257 * byte beyond the end of the range.
259 length
= end
- offset
;
260 xwrite (s
, response1_partial
, strlen (response1_partial
));
263 xwrite (s
, response2
, strlen (response2
));
264 snprintf (response3
, sizeof response3
,
265 "Content-Length: %" PRIu64
"\r\n", length
);
266 xwrite (s
, response3
, strlen (response3
));
267 xwrite (s
, response4
, strlen (response4
));
272 /* Send the file content. */
273 data
= malloc (length
);
279 xpread (fd
, data
, length
, offset
);
280 xwrite (s
, data
, length
);
286 xwrite (int s
, const char *buf
, size_t len
)
291 r
= write (s
, buf
, len
);
302 xpread (int fd
, char *buf
, size_t count
, off_t offset
)
307 r
= pread (fd
, buf
, count
, offset
);
313 fprintf (stderr
, "pread: unexpected end of file\n");