aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-09-23 13:56:57 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-09-23 13:56:57 +0000
commit32a471e4db43b5c1115084d02d8cf54db2098a72 (patch)
tree657c3599d669011871edce65d7f99564f5e9aafa
parent9a4e08eaa8e77809e276e98c98e7d6bc485fccff (diff)
downloadbusybox-w32-32a471e4db43b5c1115084d02d8cf54db2098a72.tar.gz
busybox-w32-32a471e4db43b5c1115084d02d8cf54db2098a72.tar.bz2
busybox-w32-32a471e4db43b5c1115084d02d8cf54db2098a72.zip
httpd: simplify CGI i/o loop. -200 bytes.
-rw-r--r--networking/httpd.c351
1 files changed, 183 insertions, 168 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 057416040..33f083189 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -104,12 +104,15 @@
104//#define DEBUG 1 104//#define DEBUG 1
105#define DEBUG 0 105#define DEBUG 0
106 106
107#define IOBUF_SIZE 8192 /* IO buffer */
108
107/* amount of buffering in a pipe */ 109/* amount of buffering in a pipe */
108#ifndef PIPE_BUF 110#ifndef PIPE_BUF
109# define PIPE_BUF 4096 111# define PIPE_BUF 4096
110#endif 112#endif
111 113#if PIPE_BUF >= IOBUF_SIZE
112#define IOBUF_SIZE 8192 /* IO buffer */ 114# error "PIPE_BUF >= IOBUF_SIZE"
115#endif
113 116
114#define HEADER_READ_TIMEOUT 60 117#define HEADER_READ_TIMEOUT 60
115 118
@@ -1029,6 +1032,173 @@ static int get_line(void)
1029} 1032}
1030 1033
1031#if ENABLE_FEATURE_HTTPD_CGI 1034#if ENABLE_FEATURE_HTTPD_CGI
1035
1036/* gcc 4.2.1 fares better with NOINLINE */
1037static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) ATTRIBUTE_NORETURN;
1038static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len)
1039{
1040 enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
1041 struct pollfd pfd[3];
1042 int out_cnt; /* we buffer a bit of initial CGI output */
1043 int count;
1044
1045 /* iobuf is used for CGI -> network data,
1046 * hdr_buf is for network -> CGI data (POSTDATA) */
1047
1048 /* If CGI dies, we still want to correctly finish reading its output
1049 * and send it to the peer. So please no SIGPIPEs! */
1050 signal(SIGPIPE, SIG_IGN);
1051
1052 /* NB: breaking out of this loop jumps to log_and_exit() */
1053 out_cnt = 0;
1054 while (1) {
1055 memset(pfd, 0, sizeof(pfd));
1056
1057 pfd[FROM_CGI].fd = fromCgi_rd;
1058 pfd[FROM_CGI].events = POLLIN;
1059
1060 if (toCgi_wr) {
1061 pfd[TO_CGI].fd = toCgi_wr;
1062 if (hdr_cnt > 0) {
1063 pfd[TO_CGI].events = POLLOUT;
1064 } else if (post_len > 0) {
1065 pfd[0].events = POLLIN;
1066 } else {
1067 /* post_len <= 0 && hdr_cnt <= 0:
1068 * no more POST data to CGI,
1069 * let CGI see EOF on CGI's stdin */
1070 close(toCgi_wr);
1071 toCgi_wr = 0;
1072 }
1073 }
1074
1075 /* Now wait on the set of sockets */
1076 count = poll(pfd, 3, -1);
1077 if (count <= 0) {
1078#if 0
1079 if (errno == EINTR)
1080 continue;
1081#endif
1082#if 0
1083 if (waitpid(pid, &status, WNOHANG) <= 0) {
1084 /* Weird. CGI didn't exit and no fd's
1085 * are ready, yet poll returned?! */
1086 continue;
1087 }
1088 if (DEBUG && WIFEXITED(status))
1089 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status));
1090 if (DEBUG && WIFSIGNALED(status))
1091 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status));
1092#endif
1093 break;
1094 }
1095
1096 if (pfd[TO_CGI].revents) {
1097 /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */
1098 /* Have data from peer and can write to CGI */
1099 count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
1100 /* Doesn't happen, we dont use nonblocking IO here
1101 *if (count < 0 && errno == EAGAIN) {
1102 * ...
1103 *} else */
1104 if (count > 0) {
1105 hdr_ptr += count;
1106 hdr_cnt -= count;
1107 } else {
1108 /* EOF/broken pipe to CGI, stop piping POST data */
1109 hdr_cnt = post_len = 0;
1110 }
1111 }
1112
1113 if (pfd[0].revents) {
1114 /* post_len > 0 && hdr_cnt == 0 here */
1115 /* We expect data, prev data portion is eaten by CGI
1116 * and there *is* data to read from the peer
1117 * (POSTDATA) */
1118 //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len;
1119 //count = safe_read(0, hdr_buf, count);
1120 count = safe_read(0, hdr_buf, sizeof(hdr_buf));
1121 if (count > 0) {
1122 hdr_cnt = count;
1123 hdr_ptr = hdr_buf;
1124 post_len -= count;
1125 } else {
1126 /* no more POST data can be read */
1127 post_len = 0;
1128 }
1129 }
1130
1131 if (pfd[FROM_CGI].revents) {
1132 /* There is something to read from CGI */
1133 char *rbuf = iobuf;
1134
1135 /* Are we still buffering CGI output? */
1136 if (out_cnt >= 0) {
1137 /* HTTP_200[] has single "\r\n" at the end.
1138 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
1139 * CGI scripts MUST send their own header terminated by
1140 * empty line, then data. That's why we have only one
1141 * <cr><lf> pair here. We will output "200 OK" line
1142 * if needed, but CGI still has to provide blank line
1143 * between header and body */
1144
1145 /* Must use safe_read, not full_read, because
1146 * CGI may output a few first bytes and then wait
1147 * for POSTDATA without closing stdout.
1148 * With full_read we may wait here forever. */
1149 count = safe_read(fromCgi_rd, rbuf + out_cnt, PIPE_BUF - 8);
1150 if (count <= 0) {
1151 /* eof (or error) and there was no "HTTP",
1152 * so write it, then write received data */
1153 if (out_cnt) {
1154 full_write(1, HTTP_200, sizeof(HTTP_200)-1);
1155 full_write(1, rbuf, out_cnt);
1156 }
1157 break; /* CGI stdout is closed, exiting */
1158 }
1159 out_cnt += count;
1160 count = 0;
1161 /* "Status" header format is: "Status: 302 Redirected\r\n" */
1162 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1163 /* send "HTTP/1.0 " */
1164 if (full_write(1, HTTP_200, 9) != 9)
1165 break;
1166 rbuf += 8; /* skip "Status: " */
1167 count = out_cnt - 8;
1168 out_cnt = -1; /* buffering off */
1169 } else if (out_cnt >= 4) {
1170 /* Did CGI add "HTTP"? */
1171 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1172 /* there is no "HTTP", do it ourself */
1173 if (full_write(1, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1174 break;
1175 }
1176 /* Commented out:
1177 if (!strstr(rbuf, "ontent-")) {
1178 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1179 }
1180 * Counter-example of valid CGI without Content-type:
1181 * echo -en "HTTP/1.0 302 Found\r\n"
1182 * echo -en "Location: http://www.busybox.net\r\n"
1183 * echo -en "\r\n"
1184 */
1185 count = out_cnt;
1186 out_cnt = -1; /* buffering off */
1187 }
1188 } else {
1189 count = safe_read(fromCgi_rd, rbuf, PIPE_BUF);
1190 if (count <= 0)
1191 break; /* eof (or error) */
1192 }
1193 if (full_write(1, rbuf, count) != count)
1194 break;
1195 if (DEBUG)
1196 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1197 } /* if (pfd[FROM_CGI].revents) */
1198 } /* while (1) */
1199 log_and_exit();
1200}
1201
1032static void setenv1(const char *name, const char *value) 1202static void setenv1(const char *name, const char *value)
1033{ 1203{
1034 setenv(name, value ? value : "", 1); 1204 setenv(name, value ? value : "", 1);
@@ -1038,25 +1208,25 @@ static void setenv1(const char *name, const char *value)
1038 * Spawn CGI script, forward CGI's stdin/out <=> network 1208 * Spawn CGI script, forward CGI's stdin/out <=> network
1039 * 1209 *
1040 * Environment variables are set up and the script is invoked with pipes 1210 * Environment variables are set up and the script is invoked with pipes
1041 * for stdin/stdout. If a post is being done the script is fed the POST 1211 * for stdin/stdout. If a POST is being done the script is fed the POST
1042 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). 1212 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
1043 * 1213 *
1044 * Parameters: 1214 * Parameters:
1045 * const char *url The requested URL (with leading /). 1215 * const char *url The requested URL (with leading /).
1046 * int bodyLen Length of the post body. 1216 * int post_len Length of the POST body.
1047 * const char *cookie For set HTTP_COOKIE. 1217 * const char *cookie For set HTTP_COOKIE.
1048 * const char *content_type For set CONTENT_TYPE. 1218 * const char *content_type For set CONTENT_TYPE.
1049 */ 1219 */
1050static void send_cgi_and_exit( 1220static void send_cgi_and_exit(
1051 const char *url, 1221 const char *url,
1052 const char *request, 1222 const char *request,
1053 int bodyLen, 1223 int post_len,
1054 const char *cookie, 1224 const char *cookie,
1055 const char *content_type) ATTRIBUTE_NORETURN; 1225 const char *content_type) ATTRIBUTE_NORETURN;
1056static void send_cgi_and_exit( 1226static void send_cgi_and_exit(
1057 const char *url, 1227 const char *url,
1058 const char *request, 1228 const char *request,
1059 int bodyLen, 1229 int post_len,
1060 const char *cookie, 1230 const char *cookie,
1061 const char *content_type) 1231 const char *content_type)
1062{ 1232{
@@ -1065,9 +1235,7 @@ static void send_cgi_and_exit(
1065 char *fullpath; 1235 char *fullpath;
1066 char *script; 1236 char *script;
1067 char *purl; 1237 char *purl;
1068 int buf_count; 1238 int pid;
1069 int status;
1070 int pid = 0;
1071 1239
1072 /* 1240 /*
1073 * We are mucking with environment _first_ and then vfork/exec, 1241 * We are mucking with environment _first_ and then vfork/exec,
@@ -1138,8 +1306,8 @@ static void send_cgi_and_exit(
1138 } 1306 }
1139 } 1307 }
1140 setenv1("HTTP_USER_AGENT", user_agent); 1308 setenv1("HTTP_USER_AGENT", user_agent);
1141 if (bodyLen) 1309 if (post_len)
1142 putenv(xasprintf("CONTENT_LENGTH=%d", bodyLen)); 1310 putenv(xasprintf("CONTENT_LENGTH=%d", post_len));
1143 if (cookie) 1311 if (cookie)
1144 setenv1("HTTP_COOKIE", cookie); 1312 setenv1("HTTP_COOKIE", cookie);
1145 if (content_type) 1313 if (content_type)
@@ -1215,168 +1383,15 @@ static void send_cgi_and_exit(
1215 1383
1216 /* Parent process */ 1384 /* Parent process */
1217 1385
1218 /* First, restore variables possibly changed by child */ 1386 /* Restore variables possibly changed by child */
1219 xfunc_error_retval = 0; 1387 xfunc_error_retval = 0;
1220 1388
1221 /* Prepare for pumping data. 1389 /* Pump data */
1222 * iobuf is used for CGI -> network data,
1223 * hdr_buf is for network -> CGI data (POSTDATA) */
1224 buf_count = 0;
1225 close(fromCgi.wr); 1390 close(fromCgi.wr);
1226 close(toCgi.rd); 1391 close(toCgi.rd);
1227 1392 cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
1228 /* If CGI dies, we still want to correctly finish reading its output
1229 * and send it to the peer. So please no SIGPIPEs! */
1230 signal(SIGPIPE, SIG_IGN);
1231
1232 /* This loop still looks messy. What is an exit criteria?
1233 * "CGI's output closed"? Or "CGI has exited"?
1234 * What to do if CGI has closed both input and output, but
1235 * didn't exit? etc... */
1236
1237 /* NB: breaking out of this loop jumps to log_and_exit() */
1238 while (1) {
1239 fd_set readSet;
1240 fd_set writeSet;
1241 int nfound;
1242 int count;
1243
1244 FD_ZERO(&readSet);
1245 FD_ZERO(&writeSet);
1246 FD_SET(fromCgi.rd, &readSet);
1247 if (bodyLen > 0 || hdr_cnt > 0) {
1248 FD_SET(toCgi.wr, &writeSet);
1249 nfound = toCgi.wr > fromCgi.rd ? toCgi.wr : fromCgi.rd;
1250 if (hdr_cnt <= 0)
1251 FD_SET(0, &readSet);
1252 /* Now wait on the set of sockets! */
1253 nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
1254 } else {
1255 if (!bodyLen) {
1256 close(toCgi.wr); /* no more POST data to CGI */
1257 bodyLen = -1;
1258 }
1259 nfound = select(fromCgi.rd + 1, &readSet, NULL, NULL, NULL);
1260 }
1261
1262 if (nfound <= 0) {
1263 if (waitpid(pid, &status, WNOHANG) <= 0) {
1264 /* Weird. CGI didn't exit and no fd's
1265 * are ready, yet select returned?! */
1266 continue;
1267 }
1268 close(fromCgi.rd);
1269 if (DEBUG && WIFEXITED(status))
1270 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status));
1271 if (DEBUG && WIFSIGNALED(status))
1272 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status));
1273 break;
1274 }
1275
1276 if (hdr_cnt > 0 && FD_ISSET(toCgi.wr, &writeSet)) {
1277 /* Have data from peer and can write to CGI */
1278 count = safe_write(toCgi.wr, hdr_ptr, hdr_cnt);
1279 /* Doesn't happen, we dont use nonblocking IO here
1280 *if (count < 0 && errno == EAGAIN) {
1281 * ...
1282 *} else */
1283 if (count > 0) {
1284 hdr_ptr += count;
1285 hdr_cnt -= count;
1286 } else {
1287 hdr_cnt = bodyLen = 0; /* EOF/broken pipe to CGI */
1288 }
1289 } else if (bodyLen > 0 && hdr_cnt == 0
1290 && FD_ISSET(0, &readSet)
1291 ) {
1292 /* We expect data, prev data portion is eaten by CGI
1293 * and there *is* data to read from the peer
1294 * (POSTDATA?) */
1295 count = bodyLen > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : bodyLen;
1296 count = safe_read(0, hdr_buf, count);
1297 if (count > 0) {
1298 hdr_cnt = count;
1299 hdr_ptr = hdr_buf;
1300 bodyLen -= count;
1301 } else {
1302 bodyLen = 0; /* closed */
1303 }
1304 }
1305
1306#define PIPESIZE PIPE_BUF
1307#if PIPESIZE >= IOBUF_SIZE
1308# error "PIPESIZE >= IOBUF_SIZE"
1309#endif
1310 if (FD_ISSET(fromCgi.rd, &readSet)) {
1311 /* There is something to read from CGI */
1312 char *rbuf = iobuf;
1313
1314 /* Are we still buffering CGI output? */
1315 if (buf_count >= 0) {
1316 /* HTTP_200[] has single "\r\n" at the end.
1317 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
1318 * CGI scripts MUST send their own header terminated by
1319 * empty line, then data. That's why we have only one
1320 * <cr><lf> pair here. We will output "200 OK" line
1321 * if needed, but CGI still has to provide blank line
1322 * between header and body */
1323
1324 /* Must use safe_read, not full_read, because
1325 * CGI may output a few first bytes and then wait
1326 * for POSTDATA without closing stdout.
1327 * With full_read we may wait here forever. */
1328 count = safe_read(fromCgi.rd, rbuf + buf_count, PIPESIZE - 8);
1329 if (count <= 0) {
1330 /* eof (or error) and there was no "HTTP",
1331 * so write it, then write received data */
1332 if (buf_count) {
1333 full_write(1, HTTP_200, sizeof(HTTP_200)-1);
1334 full_write(1, rbuf, buf_count);
1335 }
1336 break; /* CGI stdout is closed, exiting */
1337 }
1338 buf_count += count;
1339 count = 0;
1340 /* "Status" header format is: "Status: 302 Redirected\r\n" */
1341 if (buf_count >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1342 /* send "HTTP/1.0 " */
1343 if (full_write(1, HTTP_200, 9) != 9)
1344 break;
1345 rbuf += 8; /* skip "Status: " */
1346 count = buf_count - 8;
1347 buf_count = -1; /* buffering off */
1348 } else if (buf_count >= 4) {
1349 /* Did CGI add "HTTP"? */
1350 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1351 /* there is no "HTTP", do it ourself */
1352 if (full_write(1, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1353 break;
1354 }
1355 /* Commented out:
1356 if (!strstr(rbuf, "ontent-")) {
1357 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1358 }
1359 * Counter-example of valid CGI without Content-type:
1360 * echo -en "HTTP/1.0 302 Found\r\n"
1361 * echo -en "Location: http://www.busybox.net\r\n"
1362 * echo -en "\r\n"
1363 */
1364 count = buf_count;
1365 buf_count = -1; /* buffering off */
1366 }
1367 } else {
1368 count = safe_read(fromCgi.rd, rbuf, PIPESIZE);
1369 if (count <= 0)
1370 break; /* eof (or error) */
1371 }
1372 if (full_write(1, rbuf, count) != count)
1373 break;
1374 if (DEBUG)
1375 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1376 } /* if (FD_ISSET(fromCgi.rd)) */
1377 } /* while (1) */
1378 log_and_exit();
1379} 1393}
1394
1380#endif /* FEATURE_HTTPD_CGI */ 1395#endif /* FEATURE_HTTPD_CGI */
1381 1396
1382/* 1397/*