]> diplodocus.org Git - nmh/blob - test/fakehttp.c
lock_file.c: close(2) file descriptor on failure, avoiding leak.
[nmh] / test / fakehttp.c
1 /*
2 * fakehttp - A fake HTTP server used by the nmh test suite
3 *
4 * This code is Copyright (c) 2014, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <errno.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #define PIDFN "/tmp/fakehttp.pid"
19
20 int serve(const char *, const char *);
21 void putcrlf(int, char *);
22
23 static void
24 strip_cr(char *buf, ssize_t *len)
25 {
26 ssize_t src, dst;
27 for (src = dst = 0; src < *len; src++) {
28 buf[dst] = buf[src];
29 if (buf[src] != '\r') {
30 dst++;
31 }
32 }
33 *len -= src - dst;
34 }
35
36 static void
37 save_req(int conn, FILE *req)
38 {
39 char buf[BUFSIZ];
40 ssize_t r;
41 int e; /* used to save errno */
42 int started = 0; /* whether the request has started coming in */
43
44 if (fcntl(conn, F_SETFL, O_NONBLOCK) < 0) {
45 fprintf(stderr, "Unable to make socket non-blocking: %s\n",
46 strerror(errno));
47 exit(1);
48 }
49
50 for (;;) {
51 r = read(conn, buf, sizeof buf);
52 if (!started) {
53 /* First keep trying until some data is ready; for testing, don't
54 * bother with using select to wait for input. */
55 if (r < 0) {
56 e = errno;
57 if (e == EAGAIN || e == EWOULDBLOCK) {
58 continue; /* keep waiting */
59 }
60 fclose(req);
61 fprintf(stderr, "Unable to read socket: %s\n", strerror(e));
62 exit(1);
63 }
64 /* Request is here. Fall through to the fwrite below and keep
65 * reading. */
66 started = 1;
67 }
68 if (r < 0) {
69 e = errno;
70 putc('\n', req); /* req body usually has no newline */
71 fclose(req);
72 if (e != EAGAIN && e != EWOULDBLOCK) {
73 fprintf(stderr, "Unable to read socket: %s\n", strerror(e));
74 exit(1);
75 }
76 /* For testing, we can get away without understand the HTTP request
77 * and just treating the would-block case as meaning the request is
78 * all done. */
79 return;
80 }
81 strip_cr(buf, &r);
82 fwrite(buf, 1, r, req);
83 }
84 }
85
86 static void
87 send_res(int conn, FILE *res)
88 {
89 size_t size;
90 ssize_t len;
91 char *res_line = NULL;
92
93 while ((len = getline(&res_line, &size, res)) > 0) {
94 res_line[len - 1] = '\0';
95 putcrlf(conn, res_line);
96 }
97 free(res_line);
98 if (!feof(res)) {
99 fprintf(stderr, "read response failed: %s\n", strerror(errno));
100 exit(1);
101 }
102 }
103
104 int
105 main(int argc, char *argv[])
106 {
107 struct st;
108 int conn;
109 FILE *req, *res;
110
111 if (argc != 4) {
112 fprintf(stderr, "Usage: %s output-filename port response\n",
113 argv[0]);
114 exit(1);
115 }
116
117 if (!(req = fopen(argv[1], "w"))) {
118 fprintf(stderr, "Unable to open output file \"%s\": %s\n",
119 argv[1], strerror(errno));
120 exit(1);
121 }
122
123 if (!(res = fopen(argv[3], "r"))) {
124 fprintf(stderr, "Unable to open response \"%s\": %s\n",
125 argv[3], strerror(errno));
126 exit(1);
127 }
128
129 conn = serve(PIDFN, argv[2]);
130
131 save_req(conn, req);
132
133 send_res(conn, res);
134
135 close(conn);
136
137 return 0;
138 }