http.c
来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 1,812 行 · 第 1/4 页
C
1,812 行
} if (n) add_bytes_to_string(&header, buffer, n);#undef POST_BUFFER_SIZE } request_from_socket(socket, header.source, header.length, S_SENT, SOCKET_END_ONCLOSE, http_got_header); done_string(&header);}/* This function decompresses the data block given in @data (if it was * compressed), which is long @len bytes. The decompressed data block is given * back to the world as the return value and its length is stored into * @new_len. * * In this function, value of either http->chunk_remaining or http->length is * being changed (it depends on if chunked mode is used or not). * * Note that the function is still a little esotheric for me. Don't take it * lightly and don't mess with it without grave reason! If you dare to touch * this without testing the changes on slashdot, freshmeat and cvsweb * (including revision history), don't dare to send me any patches! ;) --pasky * * This function gotta die. */static unsigned char *decompress_data(struct connection *conn, unsigned char *data, int len, int *new_len){ struct http_connection_info *http = conn->info; /* to_read is number of bytes to be read from the decoder. It is 65536 * (then we are just emptying the decoder buffer as we finished the walk * through the incoming stream already) or PIPE_BUF / 2 (when we are * still walking through the stream - then we write PIPE_BUF / 2 to the * pipe and read it back to the decoder ASAP; the point is that we can't * write more than PIPE_BUF to the pipe at once, but we also have to * never let read_encoded() (gzread(), in fact) to empty the pipe - that * causes further malfunction of zlib :[ ... so we will make sure that * 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 = (http->length == LEN_CHUNKED ? &http->chunk_remaining : &http->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 http->length == -1, so the test below */ if (*length_of_block > 0) *length_of_block -= written; /* http->length is 0 at the end of block for all modes: keep-alive, * non-keep-alive and chunked */ if (!http->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 == BIG_READ); shutdown_connection_stream(conn); return output;}static intis_line_in_buffer(struct read_buffer *rb){ int l; for (l = 0; l < rb->length; 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->length - 1) return l + 2; if (l == rb->length - 1) return 0; } if (a0 < ' ') return -1; } return 0;}static void read_http_data(struct socket *socket, struct read_buffer *rb);static voidread_more_http_data(struct connection *conn, struct read_buffer *rb, int already_got_anything){ enum connection_state state = already_got_anything ? S_TRANS : conn->state; read_from_socket(conn->socket, rb, state, read_http_data);}static voidread_http_data_done(struct connection *conn){ struct http_connection_info *http = conn->info; /* There's no content but an error so just print * that instead of nothing. */ if (!conn->from) { if (http->code >= 400) { http_error_document(conn, 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 *http = 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 (http->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 (http->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); http->chunk_remaining = n; if (!http->chunk_remaining) http->chunk_remaining = CHUNK_ZERO_SIZE; continue; } } else { unsigned char *data; int data_len; int len; int zero = (http->chunk_remaining == CHUNK_ZERO_SIZE); if (zero) http->chunk_remaining = 0; len = http->chunk_remaining; /* Maybe everything necessary didn't come yet.. */ int_upper_bound(&len, rb->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; 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. */ http->chunk_remaining = CHUNK_DATA_END; continue; } if (!http->chunk_remaining && rb->length > 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->length >= 2 && rb->data[1] != ASCII_LF)) { return -1; } if (rb->length < 2) break; kill_buffer_data(rb, 2); } http->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 *http = conn->info; unsigned char *data; int data_len; int len = rb->length; if (http->length >= 0 && http->length < len) { /* We won't read more than we have to go. */ len = http->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 (!http->length && conn->socket->state == SOCKET_RETRY_ONCLOSE) { return 2; } return !!data_len;}static voidread_http_data(struct socket *socket, struct read_buffer *rb){ struct connection *conn = socket->conn; struct http_connection_info *http = conn->info; int ret; if (socket->state == SOCKET_CLOSED) { if (conn->content_encoding && http->length == -1) { /* Flush decompression first. */ http->length = 0; } else { read_http_data_done(conn); return; } } if (http->length != LEN_CHUNKED) { ret = read_normal_http_data(conn, rb); } else { ret = read_chunked_http_data(conn, rb); } switch (ret) { 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: assertm(ret == -1, "Unexpected return value: %d", ret); abort_connection(conn, S_HTTP_ERROR); }}/* 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->length > 4 && strncasecmp(rb->data, "HTTP/", 5)) return -2; for (i = 0; i < rb->length; 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->length - 1) return i + 2; if (a0 == ASCII_CR && i < rb->length - 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?