📄 http.c
字号:
* we will always have at least PIPE_BUF / 2 + 1 in the pipe (returning * early otherwise)). */ int to_read = PIPE_BUF / 2, did_read = 0; int *length_of_block; unsigned char *output = NULL; length_of_block = (info->length == LEN_CHUNKED ? &info->chunk_remaining : &info->length);#define BIG_READ 65536 if (!*length_of_block) { /* Going to finish this decoding bussiness. */ /* Some nicely big value - empty encoded output queue by reading * big chunks from it. */ to_read = BIG_READ; } if (conn->content_encoding == ENCODING_NONE) { *new_len = len; if (*length_of_block > 0) *length_of_block -= len; return data; } *new_len = 0; /* new_len must be zero if we would ever return NULL */ if (conn->stream_pipes[0] == -1 && (c_pipe(conn->stream_pipes) < 0 || set_nonblocking_fd(conn->stream_pipes[0]) < 0 || set_nonblocking_fd(conn->stream_pipes[1]) < 0)) { return NULL; } do { int init = 0; if (to_read == PIPE_BUF / 2) { /* ... we aren't finishing yet. */ int written = safe_write(conn->stream_pipes[1], data, len > to_read ? to_read : len); if (written > 0) { data += written; len -= written; /* In non-keep-alive connections info->length == -1, so the test below */ if (*length_of_block > 0) *length_of_block -= written; /* info->length is 0 at the end of block for all modes: keep-alive, * non-keep-alive and chunked */ if (!info->length) { /* That's all, folks - let's finish this. */ to_read = BIG_READ; } else if (!len) { /* We've done for this round (but not done * completely). Thus we will get out with * what we have and leave what we wrote to * the next round - we have to do that since * we MUST NOT ever empty the pipe completely * - this would cause a disaster for * read_encoded(), which would simply not * work right then. */ return output; } } } if (!conn->stream) { conn->stream = open_encoded(conn->stream_pipes[0], conn->content_encoding); if (!conn->stream) return NULL; /* On "startup" pipe is treated with care, but if everything * was already written to the pipe, caution isn't necessary */ else if (to_read != BIG_READ) init = 1; } else init = 0; output = (unsigned char *) mem_realloc(output, *new_len + to_read); if (!output) break; did_read = read_encoded(conn->stream, output + *new_len, init ? PIPE_BUF / 32 : to_read); /* on init don't read too much */ if (did_read > 0) *new_len += did_read; else if (did_read == -1) { mem_free_set(&output, NULL); *new_len = 0; break; /* Loop prevention (bug 517), is this correct ? --Zas */ } } while (!(!len && did_read != to_read)); decompress_shutdown(conn); return output;}/* FIXME: Unfortunately, we duplicate this in free_connection_data(). */static voiddecompress_shutdown(struct connection *conn){ if (conn->stream) { close_encoded(conn->stream); conn->stream = NULL; } if (conn->stream_pipes[1] >= 0) close(conn->stream_pipes[1]); conn->stream_pipes[0] = conn->stream_pipes[1] = -1;}static intis_line_in_buffer(struct read_buffer *rb){ int l; for (l = 0; l < rb->len; l++) { unsigned char a0 = rb->data[l]; if (a0 == ASCII_LF) return l + 1; if (a0 == ASCII_CR) { if (rb->data[l + 1] == ASCII_LF && l < rb->len - 1) return l + 2; if (l == rb->len - 1) return 0; } if (a0 < ' ') return -1; } return 0;}static void read_http_data(struct connection *conn, struct read_buffer *rb);static voidread_more_http_data(struct connection *conn, struct read_buffer *rb, int already_got_anything){ read_from_socket(conn, &conn->socket, rb, read_http_data); if (already_got_anything) set_connection_state(conn, S_TRANS);}static voidread_http_data_done(struct connection *conn){ struct http_connection_info *info = conn->info; /* There's no content but an error so just print * that instead of nothing. */ if (!conn->from) { if (info->http_code >= 400) { http_error_document(conn, info->http_code); } else { /* This is not an error, thus fine. No need generate any * document, as this may be empty and it's not a problem. * In case of 3xx, we're probably just getting kicked to * another page anyway. And in case of 2xx, the document * may indeed be empty and thus the user should see it so. */ } } http_end_request(conn, S_OK, 0);}/* Returns: * -1 on error * 0 if more to read * 1 if done */static intread_chunked_http_data(struct connection *conn, struct read_buffer *rb){ struct http_connection_info *info = conn->info; int total_data_len = 0; while (1) { /* Chunked. Good luck! */ /* See RFC2616, section 3.6.1. Basically, it looks like: * 1234 ; a = b ; c = d\r\n * aklkjadslkfjalkfjlkajkljfdkljdsfkljdf*1234\r\n * 0\r\n * \r\n */ if (info->chunk_remaining == CHUNK_DATA_END) { int l = is_line_in_buffer(rb); if (l) { if (l == -1) { /* Invalid character in buffer. */ return -1; } /* Remove everything to the EOLN. */ kill_buffer_data(rb, l); if (l <= 2) { /* Empty line. */ return 2; } continue; } } else if (info->chunk_remaining == CHUNK_SIZE) { int l = is_line_in_buffer(rb); if (l) { unsigned char *de; int n = 0; if (l != -1) { errno = 0; n = strtol(rb->data, (char **) &de, 16); if (errno || !*de) { return -1; } } if (l == -1 || de == rb->data) { return -1; } /* Remove everything to the EOLN. */ kill_buffer_data(rb, l); info->chunk_remaining = n; if (!info->chunk_remaining) info->chunk_remaining = CHUNK_ZERO_SIZE; continue; } } else { unsigned char *data; int data_len; int len; int zero = (info->chunk_remaining == CHUNK_ZERO_SIZE); if (zero) info->chunk_remaining = 0; len = info->chunk_remaining; /* Maybe everything necessary didn't come yet.. */ int_upper_bound(&len, rb->len); conn->received += len; data = decompress_data(conn, rb->data, len, &data_len); if (add_fragment(conn->cached, conn->from, data, data_len) == 1) conn->tries = 0; if (data && data != rb->data) mem_free(data); conn->from += data_len; total_data_len += data_len; kill_buffer_data(rb, len); if (zero) { /* Last chunk has zero length, so this is last * chunk, we finished decompression just now * and now we can happily finish reading this * stuff. */ info->chunk_remaining = CHUNK_DATA_END; continue; } if (!info->chunk_remaining && rb->len > 0) { /* Eat newline succeeding each chunk. */ if (rb->data[0] == ASCII_LF) { kill_buffer_data(rb, 1); } else { if (rb->data[0] != ASCII_CR || (rb->len >= 2 && rb->data[1] != ASCII_LF)) { return -1; } if (rb->len < 2) break; kill_buffer_data(rb, 2); } info->chunk_remaining = CHUNK_SIZE; continue; } } break; } /* More to read. */ return !!total_data_len;}/* Returns 0 if more data, 1 if done. */static intread_normal_http_data(struct connection *conn, struct read_buffer *rb){ struct http_connection_info *info = conn->info; unsigned char *data; int data_len; int len = rb->len; if (info->length >= 0 && info->length < len) { /* We won't read more than we have to go. */ len = info->length; } conn->received += len; data = decompress_data(conn, rb->data, len, &data_len); if (add_fragment(conn->cached, conn->from, data, data_len) == 1) conn->tries = 0; if (data && data != rb->data) mem_free(data); conn->from += data_len; kill_buffer_data(rb, len); if (!info->length && rb->close == READ_BUFFER_RETRY_ONCLOSE) { return 2; } return !!data_len;}static voidread_http_data(struct connection *conn, struct read_buffer *rb){ struct http_connection_info *info = conn->info; int ret; set_connection_timeout(conn); if (rb->close == READ_BUFFER_END) { if (conn->content_encoding && info->length == -1) { /* Flush decompression first. */ info->length = 0; } else { read_http_data_done(conn); return; } } if (info->length != LEN_CHUNKED) { ret = read_normal_http_data(conn, rb); } else { ret = read_chunked_http_data(conn, rb); } switch (ret) { case -1: abort_conn_with_state(conn, S_HTTP_ERROR); break; case 0: read_more_http_data(conn, rb, 0); break; case 1: read_more_http_data(conn, rb, 1); break; case 2: read_http_data_done(conn); break; default: INTERNAL("Unexpected return value: %d", ret); }}/* Returns offset of the header end, zero if more data is needed, -1 when * incorrect data was received, -2 if this is HTTP/0.9 and no header is to * come. */static intget_header(struct read_buffer *rb){ int i; /* XXX: We will have to do some guess about whether an HTTP header is * coming or not, in order to support HTTP/0.9 reply correctly. This * means a little code duplication with get_http_code(). --pasky */ if (rb->len > 4 && strncasecmp(rb->data, "HTTP/", 5)) return -2; for (i = 0; i < rb->len; i++) { unsigned char a0 = rb->data[i]; unsigned char a1 = rb->data[i + 1]; if (a0 == 0) { rb->data[i] = ' '; continue; } if (a0 == ASCII_LF && a1 == ASCII_LF && i < rb->len - 1) return i + 2; if (a0 == ASCII_CR && i < rb->len - 3) { if (a1 == ASCII_CR) continue; if (a1 != ASCII_LF) return -1; if (rb->data[i + 2] == ASCII_CR) { if (rb->data[i + 3] != ASCII_LF) return -1; return i + 4; } } } return 0;}static voidcheck_http_authentication(struct uri *uri, unsigned char *header, unsigned char *header_field){ unsigned char *str, *d; d = parse_header(header, header_field, &str); while (d) { if (!strncasecmp(d, "Basic", 5)) { unsigned char *realm = get_header_param(d, "realm"); if (realm) { add_auth_entry(uri, realm, NULL, NULL, 0); mem_free(realm); mem_free(d); break; } } else if (!strncasecmp(d, "Digest", 6)) { unsigned char *realm = get_header_param(d, "realm"); unsigned char *nonce = get_header_param(d, "nonce"); unsigned char *opaque = get_header_param(d, "opaque"); add_auth_entry(uri, realm, nonce, opaque, 1); mem_free_if(realm); mem_free_if(nonce); mem_free_if(opaque); mem_free(d); break; } mem_free(d); d = parse_header(str, header_field, &str); }}voidhttp_got_header(struct connection *conn, struct read_buffer *rb){ struct http_connection_info *info = conn->info; unsigned char *head;#ifdef CONFIG_COOKIES unsigned char *cookie, *ch;#endif unsigned char *d; struct uri *uri = conn->proxied_uri; /* Set to the real uri */ struct http_version version; enum connection_state state = (conn->state != S_PROC ? S_GETH : S_PROC); int a, h = 200; int cf; set_connection_timeout(conn);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -