/* * Handles the request process. It uses the receive_request function * and parses the received data filling a request_t data structure. * * @param thread_id: the thread id handling the request * @param sockfd: socket file descriptor to read data from the client * @param req: request_t data structure to store the parsed data */ int handle_request(int thread_id, int sockfd, request_t *req) { char *buffer = NULL; char *method = NULL; char *query = NULL; char *content_length = NULL; int start, end, pos, tmp, n; int string_length, message_length; int received; uint8_t i; start = 0; end = 0; pos = 0; tmp = 0; n = 0; string_length = 0; message_length = 0; received = 0; /* allocate first 1024 bytes for the request */ buffer = malloc(REQUEST_ALLOC_SIZE); memset(buffer, 0, REQUEST_ALLOC_SIZE); if ((n = receive_request(thread_id, sockfd, &buffer)) < 0) { /* There has been an error receiving the client request :( */ free(buffer); return ERROR; } else if (n == 0) { free(buffer); debug(conf.output_level, "[%d] empty request\n", thread_id); return ERROR; } debug(conf.output_level, "[%d] Request:\n%s\n", thread_id, buffer); while (strncmp(&buffer[start], "\r\n", 2) != 0) { while (strncmp(&buffer[end], "\r\n", 2) != 0) end++; if (start == 0) { pos = 0; tmp = 0; /* allocate request. REMEMBER TO FREE request AFTER sending response */ while (strncmp(&buffer[pos], " ", 1) != 0) pos++; method = malloc(pos + 1); memset(method, 0, pos + 1); strncat(method, buffer, pos); for (i = 0; i < 7; i++) { if(strncmp(methods[i], method, strlen(method)) == 0) { req->method = i; } } paranoid_free_string(method); pos++; tmp = pos; while (strncmp(&buffer[pos], " ", 1) != 0) pos++; req->uri = malloc(pos + 1 - tmp); memset(req->uri, 0, pos + 1 - tmp); strncat(req->uri, &buffer[tmp], pos - tmp); req->_mask |= _REQUEST_URI; pos++; tmp = pos; while (strncmp(&buffer[pos], "\r\n", 2) != 0) pos++; req->version = malloc(pos + 1 - tmp); memset(req->version, 0, pos + 1 - tmp); strncat(req->version, &buffer[tmp], pos - tmp); req->_mask |= _REQUEST_VERSION; } else { pos = 0; tmp = 0; /* reallocate header_t struct of request_t */ if (req->num_headers == 0) { req->headers = malloc(sizeof(header_t *)); } else { req->headers = realloc(req->headers, (req->num_headers + 1)*sizeof(header_t *)); } req->headers[req->num_headers] = malloc(sizeof(header_t *)); /* parse headers */ while (strncmp(&buffer[start + pos], ":", 1) != 0) pos++; req->headers[req->num_headers]->name = malloc(pos++); tmp = pos; while (strncmp(&buffer[start + pos], "\r\n", 2) != 0) pos++; req->headers[req->num_headers]->value = malloc((pos++) - tmp); tmp = pos; sscanf(&buffer[start], "%[^:]: %[^\r\n]", req->headers[req->num_headers]->name, req->headers[req->num_headers]->value); req->num_headers++; } end += 2; start = end; } /* Look for the query part */ if (strchr(req->uri, '?') != NULL) { query = strchr(req->uri, '?'); string_length = strlen(query); req->query = malloc(string_length + 1); memset(req->query, 0, string_length + 1); strncat(req->query, query, string_length); req->_mask |= _REQUEST_QUERY; debug(conf.output_level, "[%d] DEBUG: query %s\n", thread_id, req->query); } /* Get the resource requested */ if (req->_mask & _REQUEST_QUERY) { string_length = strlen(req->uri) - strlen(req->query); // Strip query string from uri } else { string_length = strlen(req->uri); } req->resource = malloc(string_length + 1); memset(req->resource, 0, string_length + 1); strncat(req->resource, req->uri, string_length); req->_mask |= _REQUEST_RESOURCE; string_length = 0; /* free buffer */ free(buffer); if (get_request_header(req, "Content-Length", &content_length) != -1) { message_length = atoi(content_length); if (message_length > REQUEST_MAX_MESSAGE_SIZE) { return ERROR; } buffer = malloc(REQUEST_ALLOC_MESSAGE_SIZE); if ((received = receive_message_body(thread_id, sockfd, &buffer, message_length)) < 0) { /* There has been an error receiving the message body :( */ free(buffer); free_request(req); return ERROR; } req->message_body = malloc(received + 1); memset(req->message_body, 0, received + 1); memcpy(req->message_body, buffer, received); req->_mask |= _REQUEST_MESSAGE; free(buffer); debug(conf.output_level, "[%d] DEBUG: message body: %s\n", thread_id, req->message_body); } return 0; }
/** @brief Parse and response to request from a client * * @return 0 if the connection should be kept alive. -1 if the connection * should be closed. */ int http_parse(http_client_t *client) { int ret, i; char line[MAXBUF]; char* buf; if (client->status == C_IDLE) { /* A new request, parse request line */ ret = client_readline(client, line); if (strlen(line) == 0) return 0; log_msg(L_HTTP_DEBUG, "%s\n", line); if (ret == 0) return 0; /* The length of a line exceed MAXBUF */ if (ret < 0) { log_error("A line in request is too long"); return end_request(client, BAD_REQUEST); } if (client->req != NULL) free(client->req); client->req = new_request(); /* parse request line and store information in client->req */ if ((ret = parse_request_line(client->req, line)) > 0) return end_request(client, ret); /* Now start parsing header */ client->status = C_PHEADER; } /* * Read request headers. When finish reading request headers of a POST * request without error, client->req->content_length will be set * correspondingly. Thus client->req->content_length == -1 means the * request header section has not ended. */ while (client->status == C_PHEADER && client_readline(client, line) > 0) { log_msg(L_HTTP_DEBUG, "%s\n", line); if (strlen(line) == 0) { //Request header ends if (client->req->method == M_POST) { buf = get_request_header(client->req, "Content-Length"); if (buf == NULL) return end_request(client, LENGTH_REQUIRED); //validate content-length if (strlen(buf) == 0) return BAD_REQUEST; for (i = 0; i < strlen(buf); ++i) if (buf[i] < '0' || buf[i] >'9') //each char in range ['0', '9'] return end_request(client, BAD_REQUEST); client->req->content_length = atoi(buf); /* Now start receiving body */ client->status = C_PBODY; break; } if (client->req->method == M_GET) ret = handle_get(client); if (client->req->method == M_HEAD) ret = handle_head(client); if (ret != 0) return end_request(client, ret); else { /* The client signal a "Connection: Close" */ if (connection_close(client->req)) client->alive = 0; return ret; } } ret = parse_header(client->req, line); if (ret == -1) { log_msg(L_ERROR, "Bad request header format: %s\n", line); return end_request(client, BAD_REQUEST); } } /* * We've finished reading and parsing request header. Now, see if the body * of the request is ready. If so, copy data */ if (client->status == C_PBODY) { // Reveive complete body? if (client->in->datasize - client->in->pos >= client->req->content_length) { /* Let body points to corresponding memory in the input buffer */ client->req->body = client->in->buf + client->in->pos; client->in->pos += client->req->content_length; ret = handle_post(client); if (ret != 0) return end_request(client, ret); else { /* The client signal a "Connection: Close" */ if (connection_close(client->req)) client->alive = 0; return ret; } } /* Body not ready, next time then */ return 0; } return 0; }