/* * 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; }
/* * 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; }