/* * Read from the socket and store the references in the vector */ static int store_refs(transport_git *t) { gitno_buffer *buf = &t->buf; int ret = 0; while (1) { if ((ret = gitno_recv(buf)) < 0) return -1; if (ret == 0) /* Orderly shutdown, so exit */ return 0; ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset); if (ret == GIT_EBUFS) { gitno_consume_n(buf, buf->len); continue; } if (ret < 0) return ret; gitno_consume_n(buf, buf->offset); if (t->proto.flush) { /* No more refs */ t->proto.flush = 0; return 0; } } }
static int store_refs(transport_http *t) { http_parser_settings settings; char buffer[1024]; gitno_buffer buf; git_pkt *pkt; int ret; http_parser_init(&t->parser, HTTP_RESPONSE); t->parser.data = t; memset(&settings, 0x0, sizeof(http_parser_settings)); settings.on_header_field = on_header_field; settings.on_header_value = on_header_value; settings.on_headers_complete = on_headers_complete; settings.on_body = on_body_store_refs; settings.on_message_complete = on_message_complete; gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer)); while(1) { size_t parsed; if ((ret = gitno_recv(&buf)) < 0) return -1; parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); /* Both should happen at the same time */ if (parsed != buf.offset || t->error < 0) return t->error; gitno_consume_n(&buf, parsed); if (ret == 0 || t->transfer_finished) return 0; } pkt = git_vector_get(&t->refs, 0); if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) { giterr_set(GITERR_NET, "Invalid HTTP response"); return t->error = -1; } else { git_vector_remove(&t->refs, 0); } return 0; }
static int parse_response(transport_http *t) { int ret = 0; http_parser_settings settings; char buffer[1024]; gitno_buffer buf; http_parser_init(&t->parser, HTTP_RESPONSE); t->parser.data = t; t->transfer_finished = 0; memset(&settings, 0x0, sizeof(http_parser_settings)); settings.on_header_field = on_header_field; settings.on_header_value = on_header_value; settings.on_headers_complete = on_headers_complete; settings.on_body = on_body_parse_response; settings.on_message_complete = on_message_complete; gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer)); while(1) { size_t parsed; if ((ret = gitno_recv(&buf)) < 0) return -1; parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); /* Both should happen at the same time */ if (parsed != buf.offset || t->error < 0) return t->error; gitno_consume_n(&buf, parsed); if (ret == 0 || t->transfer_finished || t->pack_ready) { return 0; } } return ret; }
static int git_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { git_stream *s = (git_stream *)stream; gitno_buffer buf; *bytes_read = 0; if (!s->sent_command && send_command(s) < 0) return -1; gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); if (gitno_recv(&buf) < 0) return -1; *bytes_read = buf.offset; return 0; }
static int git_proto_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { int error; git_proto_stream *s = (git_proto_stream *)stream; gitno_buffer buf; *bytes_read = 0; if (!s->sent_command && (error = send_command(s)) < 0) return error; gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size); if ((error = gitno_recv(&buf)) < 0) return error; *bytes_read = buf.offset; return 0; }
static int http_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { http_stream *s = (http_stream *)stream; http_subtransport *t = OWNING_SUBTRANSPORT(s); parser_context ctx; size_t bytes_parsed; replay: *bytes_read = 0; assert(t->connected); if (!s->sent_request) { git_buf request = GIT_BUF_INIT; clear_parser_state(t); if (gen_request(&request, s, 0) < 0) { giterr_set(GITERR_NET, "Failed to generate request"); return -1; } if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { git_buf_free(&request); return -1; } git_buf_free(&request); s->sent_request = 1; } if (!s->received_response) { if (s->chunked) { assert(s->verb == post_verb); /* Flush, if necessary */ if (s->chunk_buffer_len > 0 && write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; /* Write the final chunk. */ if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0) return -1; } s->received_response = 1; } while (!*bytes_read && !t->parse_finished) { t->parse_buffer.offset = 0; if (gitno_recv(&t->parse_buffer) < 0) return -1; /* This call to http_parser_execute will result in invocations of the * on_* family of callbacks. The most interesting of these is * on_body_fill_buffer, which is called when data is ready to be copied * into the target buffer. We need to marshal the buffer, buf_size, and * bytes_read parameters to this callback. */ ctx.t = t; ctx.s = s; ctx.buffer = buffer; ctx.buf_size = buf_size; ctx.bytes_read = bytes_read; /* Set the context, call the parser, then unset the context. */ t->parser.data = &ctx; bytes_parsed = http_parser_execute(&t->parser, &t->settings, t->parse_buffer.data, t->parse_buffer.offset); t->parser.data = NULL; /* If there was a handled authentication failure, then parse_error * will have signaled us that we should replay the request. */ if (PARSE_ERROR_REPLAY == t->parse_error) { s->sent_request = 0; if (http_connect(t) < 0) return -1; goto replay; } if (t->parse_error < 0) return -1; if (bytes_parsed != t->parse_buffer.offset) { giterr_set(GITERR_NET, "HTTP parser error: %s", http_errno_description((enum http_errno)t->parser.http_errno)); return -1; } } return 0; }
/* * As the server is probably using Transfer-Encoding: chunked, we have * to use the HTTP parser to download the pack instead of giving it to * the simple downloader. Furthermore, we're using keep-alive * connections, so the simple downloader would just hang. */ static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats) { transport_http *t = (transport_http *) transport; git_buf *oldbuf = &t->buf; int recvd; http_parser_settings settings; char buffer[1024]; gitno_buffer buf; git_buf path = GIT_BUF_INIT; git_indexer_stream *idx = NULL; download_pack_cbdata data; gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer)); if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) { giterr_set(GITERR_NET, "The pack doesn't start with a pack signature"); return -1; } if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0) return -1; if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0) return -1; /* * This is part of the previous response, so we don't want to * re-init the parser, just set these two callbacks. */ memset(stats, 0, sizeof(git_indexer_stats)); data.stats = stats; data.idx = idx; data.transport = t; t->parser.data = &data; t->transfer_finished = 0; memset(&settings, 0x0, sizeof(settings)); settings.on_message_complete = on_message_complete_download_pack; settings.on_body = on_body_download_pack; *bytes = git_buf_len(oldbuf); if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0) goto on_error; gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer)); do { size_t parsed; if ((recvd = gitno_recv(&buf)) < 0) goto on_error; parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); if (parsed != buf.offset || t->error < 0) goto on_error; *bytes += recvd; gitno_consume_n(&buf, parsed); } while (recvd > 0 && !t->transfer_finished); if (git_indexer_stream_finalize(idx, stats) < 0) goto on_error; git_indexer_stream_free(idx); return 0; on_error: git_indexer_stream_free(idx); git_buf_free(&path); return -1; }
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */ int git_fetch__download_pack( char **out, const char *buffered, size_t buffered_size, GIT_SOCKET fd, git_repository *repo) { git_filebuf file = GIT_FILEBUF_INIT; int error; char buff[1024]; git_buf path = GIT_BUF_INIT; static const char suff[] = "/objects/pack/pack-received"; gitno_buffer buf; gitno_buffer_setup(&buf, buff, sizeof(buff), fd); if (memcmp(buffered, "PACK", strlen("PACK"))) { return git__throw(GIT_ERROR, "The pack doesn't start with the signature"); } error = git_buf_joinpath(&path, repo->path_repository, suff); if (error < GIT_SUCCESS) goto cleanup; error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY); if (error < GIT_SUCCESS) goto cleanup; /* Part of the packfile has been received, don't loose it */ error = git_filebuf_write(&file, buffered, buffered_size); if (error < GIT_SUCCESS) goto cleanup; while (1) { error = git_filebuf_write(&file, buf.data, buf.offset); if (error < GIT_SUCCESS) goto cleanup; gitno_consume_n(&buf, buf.offset); error = gitno_recv(&buf); if (error < GIT_SUCCESS) goto cleanup; if (error == 0) /* Orderly shutdown */ break; } *out = git__strdup(file.path_lock); if (*out == NULL) { error = GIT_ENOMEM; goto cleanup; } /* A bit dodgy, but we need to keep the pack at the temporary path */ error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE); cleanup: if (error < GIT_SUCCESS) git_filebuf_cleanup(&file); git_buf_free(&path); return error; }