void http_read(TCP_NODE * n, char *p_cmd, char *p_url, char *p_proto, HASH * p_head) { int code = 0; char lastmodified[DATE_SIZE]; char resource[BUF_SIZE]; char filename[BUF_SIZE]; char range[BUF_SIZE]; char *p_range = range; size_t filesize = 0; size_t content_length = 0; char keepalive[BUF_SIZE]; const char *mimetype = NULL; /* Get protocol */ if (!http_proto(n, p_proto)) { node_status(n, NODE_SHUTDOWN); goto END; } /* Get action */ if (!http_action(n, p_cmd)) { node_status(n, NODE_SHUTDOWN); goto END; } /* Get resource */ if (!http_resource(n, p_url, resource)) { node_status(n, NODE_SHUTDOWN); goto END; } /* Check Keep-Alive */ n->keepalive = http_keepalive(p_head, keepalive); /* Compute filename */ if (!http_filename(resource, filename)) { http_404(n, keepalive); info(_log, &n->c_addr, "404 %s", resource); goto END; } /* Compute file size */ filesize = http_size_simple(filename); /* Compute mime type */ mimetype = mime_find(filename); /* Last-Modified. */ if (!http_resource_modified(filename, p_head, lastmodified)) { http_304(n, keepalive); info(_log, &n->c_addr, "304 %s", resource); goto END; } /* Range request? */ if (http_range_detected(p_head, range)) { code = 206; } else { code = 200; } /* Normal 200-er request */ if (code == 200) { info(_log, &n->c_addr, "200 %s", resource); http_200(n, lastmodified, filename, filesize, keepalive, mimetype); http_body(n, filename, filesize); goto END; } /* Check for 'bytes=' in range. Fallback to 200 if necessary. */ if (!http_range_prepare(&p_range)) { info(_log, &n->c_addr, "200 %s", resource); http_200(n, lastmodified, filename, filesize, keepalive, mimetype); http_body(n, filename, filesize); goto END; } /* multipart/byteranges */ if (!http_range_multipart(p_range)) { RESPONSE *r_head = NULL, *r_file = NULL; /* Header */ if ((r_head = resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) { node_status(n, NODE_SHUTDOWN); goto END; } /* File */ if ((r_file = resp_put(n->response, RESPONSE_FROM_FILE)) == NULL) { node_status(n, NODE_SHUTDOWN); goto END; } /* Parse range. */ if (!http_range_simple(n, r_file, filename, filesize, p_range, &content_length)) { node_status(n, NODE_SHUTDOWN); goto END; } /* Header with known content_length */ http_206_simple(n, r_head, r_file, lastmodified, filename, filesize, content_length, keepalive, mimetype); info(_log, &n->c_addr, "206 %s [%s]", resource, range); goto END; } else { RESPONSE *r_head = NULL, *r_bottom = NULL, *r_zsyncbug = NULL; char boundary[12]; /* Create boundary string */ http_random(boundary, 12); /* Header */ if ((r_head = resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) { node_status(n, NODE_SHUTDOWN); goto END; } /* zsync bug? One more \r\n between header and body. */ if ((r_zsyncbug = resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) { node_status(n, NODE_SHUTDOWN); goto END; } /* Parse range. */ if (!http_range_complex (n, filename, filesize, mimetype, p_range, &content_length, boundary)) { node_status(n, NODE_SHUTDOWN); goto END; } /* Bottom */ if ((r_bottom = resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) { node_status(n, NODE_SHUTDOWN); goto END; } http_206_boundary_finish(r_bottom, &content_length, boundary); /* Header with known content_length */ http_206_complex(n, r_head, lastmodified, filename, filesize, content_length, boundary, keepalive); /* zsync bug? One more \r\n between header and body. */ http_newline(r_zsyncbug, &content_length); info(_log, &n->c_addr, "206 %s [%s]", resource, range); goto END; } info(_log, &n->c_addr, "FIXME: HTTP parser end reached without result"); node_status(n, NODE_SHUTDOWN); END: /* HTTP Pipeline: There is at least one more request to parse. * Recursive request! Limited by the input buffer. */ if (n->pipeline != NODE_SHUTDOWN && n->recv_size > 0) { http_buf(n); } }
/* HTTP Functions */ int http_parse(http_t *http, char ch) { int mode = 0; if (http->mode < MD_POST) { if (ch == '\r') return 0; if (ch == ' ' && http->idx == 0) return 0; } switch (http->mode) { /* HTTP Parsing */ case MD_METHOD: if (ch == ' ') http_mode(http, MD_PATH, http->req_method); else if (http->idx < MAX_METHOD) http->req_method[http->idx++] = ch; return 0; case MD_PATH: if (ch == ' ') http_mode(http, MD_VERSION, http->req_path); else if (http->idx < MAX_PATH) http->req_path[http->idx++] = ch; return 0; case MD_VERSION: if (ch == '\n') { http_mode(http, MD_FIELD, http->req_version); http_open(http, http->req_method, http->req_path, http->req_version); } else if (http->idx < MAX_VERSION) http->req_version[http->idx++] = ch; return 0; case MD_FIELD: if (ch == '\n') { mode = http_body(http); http_mode(http, mode, http->hdr_field); switch (mode) { case MD_METHOD: return HTTP_DONE; case MD_SOCK: return HTTP_SOCK; case MD_POST: return HTTP_POST; } } else if (ch == ':') http_mode(http, MD_VALUE, http->hdr_field); else if (http->idx < MAX_FIELD) http->hdr_field[http->idx++] = ch; return 0; case MD_VALUE: if (ch == '\n') { http_mode(http, MD_FIELD, http->hdr_value); http_head(http, http->hdr_field, http->hdr_value); } else if (http->idx < MAX_VALUE) http->hdr_value[http->idx++] = ch; return 0; case MD_POST: return 0; case MD_SOCK: return 0; default: return 0; } }