/* See section 4.2 of RFC 2616 for header format. */ int http_parse_header(struct http_header **result, const char *header) { const char *p, *q; size_t value_len, value_offset; struct http_header *node, **prev; *result = NULL; prev = result; p = header; while (*p != '\0' && !is_crlf(p)) { /* Get the field name. */ q = p; while (*q != '\0' && is_token_char(*q)) q++; if (*q != ':') { http_header_free(*result); return 400; } node = (struct http_header *) safe_malloc(sizeof(*node)); node->name = mkstr(p, q); node->value = NULL; node->next = NULL; value_len = 0; value_offset = 0; /* Copy the header field value until we hit a CRLF. */ p = q + 1; p = skip_lws(p); for (;;) { q = p; while (*q != '\0' && !is_space_char(*q) && !is_crlf(q)) { /* Section 2.2 of RFC 2616 disallows control characters. */ if (iscntrl((int) (unsigned char) *q)) { http_header_node_free(node); return 400; } q++; } strbuf_append(&node->value, &value_len, &value_offset, p, q - p); p = skip_lws(q); if (is_crlf(p)) break; /* Replace LWS with a single space. */ strbuf_append_str(&node->value, &value_len, &value_offset, " "); } *prev = node; prev = &node->next; p = skip_crlf(p); } return 0; }
/* Returns 0 on success and nonzero on failure. */ int http_parse_status_line(const char *line, struct http_response *response) { const char *p, *q; http_response_init(response); /* Version. */ p = parse_http_version(line, &response->version); if (p == line) return -1; while (*p == ' ') p++; /* Status code. */ errno = 0; response->code = parse_long(p, (char **) &q); if (errno != 0 || q == p) return -1; p = q; /* Reason phrase. */ while (*p == ' ') p++; q = p; while (!is_crlf(q)) q++; /* We expect that the CRLF ends the string. */ if (*skip_crlf(q) != '\0') return -1; response->phrase = mkstr(p, q); return 0; }
int http_read_header(struct socket_buffer *buf, char **result) { char *line = NULL; char *header; size_t n = 0; size_t count; int blank; header = NULL; do { line = socket_buffer_readline(buf, &count, MAX_HEADER_LENGTH); if (line == NULL) { free(header); if (n >= MAX_HEADER_LENGTH) /* Request Entity Too Large. */ return 413; else return 400; } blank = is_crlf(line); if (n + count >= MAX_HEADER_LENGTH) { free(line); free(header); /* Request Entity Too Large. */ return 413; } header = (char *) safe_realloc(header, n + count + 1); memcpy(header + n, line, count); n += count; free(line); } while (!blank); header[n] = '\0'; *result = header; return 0; }
int http_read_request_line(struct socket_buffer *buf, char **line) { size_t n; *line = NULL; /* Section 4.1 of RFC 2616 says "servers SHOULD ignore any empty line(s) received where a Request-Line is expected." */ do { free(*line); *line = socket_buffer_readline(buf, &n, MAX_REQUEST_LINE_LENGTH); if (*line == NULL) { if (n >= MAX_REQUEST_LINE_LENGTH) /* Request Entity Too Large. */ return 413; else return 400; } } while (is_crlf(*line)); return 0; }
int main(/*int argc, char *argv[]*/) { INFO("Started\n"); int server_socket, client_socket, port, res, client_len, n; struct sockaddr_in srv_addr, client_addr; int iSetOption = 1; port = SRVPORT; server_socket = socket(AF_INET, SOCK_STREAM, 0); setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&iSetOption, sizeof(iSetOption)); if (server_socket < 0) { ERROR("Error opening socket\n"); IFERROR(perror("")); } INFO("Made server socket\n"); memset((char *) &srv_addr, 0, sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(port); srv_addr.sin_addr.s_addr = INADDR_ANY; while ((res = bind(server_socket, (struct sockaddr *) &srv_addr, sizeof(srv_addr))) < 0) { ERROR("Error binding socket\n"); IFERROR(perror("")); } listen(server_socket, 5); INFO("Listening on port %d\n", port); client_len = sizeof(client_addr); if (chdir("content")) { ERROR("Could not chdir to the content directory\n"); IFERROR(perror("")); exit(1); } do { client_socket = accept(server_socket, (struct sockaddr *) &client_addr, (socklen_t *) &client_len); INFO("Accepted client socket\n"); if (client_socket < 0) { ERROR("Error on client accept\n"); IFERROR(perror("")); } request *req = request_new(); char line[MAXLEN]; while ((n = fd_readline(client_socket, line, MAXLEN)) > 0) { if (n == MAXLEN-1 && line[MAXLEN-1] != '\n') { WARN("Line length exceeded MAXLEN %d\n", MAXLEN); char c; int dropped = 0; while ((n = read(client_socket, &c, 1)) > 0) { dropped += 1; if (c == '\n') break; } WARN("Skipped to the next line, dropped %d characters\n", dropped); } if (is_crlf(line)) { // We've reached the end of the headers int left_to_read = request_get_content_length(req); int total_read = 0, len; if (left_to_read > 0) { DEBUG("There's %d chars of body to read\n", left_to_read); // There's a Content-Length so there must be a body req->body = malloc(sizeof(char) * (left_to_read+1)); len = MAXLEN < left_to_read ? MAXLEN : left_to_read; len += 1; while (left_to_read && (n = fd_readline(client_socket, line, len)) > 0) { DEBUG("Read %d characters of body: %s\n", n, line); strcpy(&req->body[total_read], line); total_read += n; left_to_read -= n; DEBUG("total_read: %d, left_to_read: %d\n", total_read, left_to_read); len = MAXLEN < left_to_read ? MAXLEN : left_to_read; } req->body[total_read+1] = '\0'; DEBUG("Done reading body in %d chars:\n", total_read); DEBUG("%s\n", req->body); respond(&req, client_socket); continue; } else { DEBUG("No body to read, generating response\n"); respond(&req, client_socket); continue; } } if (request_parse(req, line)) { WARN("An error occurred while parsing the previous line\n"); } } DEBUG("Done with this request! n = %d\n", n); } while (1); close(server_socket); close(client_socket); return 0; }