/* * Since this is a network connection, we need to parse and store the * pkt-lines at this stage and keep them there. */ static int git_connect(git_transport *transport, int direction) { transport_git *t = (transport_git *) transport; if (direction == GIT_DIR_PUSH) { giterr_set(GITERR_NET, "Pushing over git:// is not supported"); return -1; } t->parent.direction = direction; /* Connect and ask for the refs */ if (do_connect(t, transport->url) < 0) return -1; gitno_buffer_setup(transport, &transport->buffer, t->buff, sizeof(t->buff)); t->parent.connected = 1; if (git_protocol_store_refs(transport, 1) < 0) return -1; if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0) return -1; return 0; }
/* * Since this is a network connection, we need to parse and store the * pkt-lines at this stage and keep them there. */ static int git_connect(git_transport *transport, int direction) { transport_git *t = (transport_git *) transport; if (direction == GIT_DIR_PUSH) { giterr_set(GITERR_NET, "Pushing over git:// is not supported"); return -1; } t->parent.direction = direction; if (git_vector_init(&t->refs, 16, NULL) < 0) return -1; /* Connect and ask for the refs */ if (do_connect(t, transport->url) < 0) goto cleanup; gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket); t->parent.connected = 1; if (store_refs(t) < 0) goto cleanup; if (detect_caps(t) < 0) goto cleanup; return 0; cleanup: git_vector_free(&t->refs); return -1; }
static void clear_parser_state(http_subtransport *t) { unsigned i; char *entry; http_parser_init(&t->parser, HTTP_RESPONSE); gitno_buffer_setup(&t->socket, &t->parse_buffer, t->parse_buffer_data, sizeof(t->parse_buffer_data)); t->last_cb = NONE; t->parse_error = 0; t->parse_finished = 0; git_buf_free(&t->parse_header_name); git_buf_init(&t->parse_header_name, 0); git_buf_free(&t->parse_header_value); git_buf_init(&t->parse_header_value, 0); git__free(t->content_type); t->content_type = NULL; git__free(t->location); t->location = NULL; git_vector_foreach(&t->www_authenticate, i, entry) git__free(entry); git_vector_free(&t->www_authenticate); }
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; }
/* * 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; }
static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) { transport_http *t = (transport_http *) transport; int ret; unsigned int i; char buff[128]; gitno_buffer buf; git_revwalk *walk = NULL; git_oid oid; git_pkt_ack *pkt; git_vector *common = &t->common; git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT; gitno_buffer_setup(transport, &buf, buff, sizeof(buff)); if (git_vector_init(common, 16, NULL) < 0) return -1; if (git_fetch_setup_walk(&walk, repo) < 0) return -1; do { if ((ret = do_connect(t, t->host, t->port)) < 0) goto cleanup; if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0) goto cleanup; /* We need to send these on each connection */ git_vector_foreach (common, i, pkt) { if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0) goto cleanup; } i = 0; while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) { if ((ret = git_pkt_buffer_have(&oid, &data)) < 0) goto cleanup; i++; } git_pkt_buffer_done(&data); if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", data.size, 0)) < 0) goto cleanup; if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0) goto cleanup; if ((ret = gitno_send(transport, data.ptr, data.size, 0)) < 0) goto cleanup; git_buf_clear(&request); git_buf_clear(&data); if (ret < 0 || i >= 256) break; if ((ret = parse_response(t)) < 0) goto cleanup; if (t->pack_ready) { ret = 0; goto cleanup; } } while(1); cleanup: git_buf_free(&request); git_buf_free(&data); git_revwalk_free(walk); return ret; }
/* 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; }