Beispiel #1
0
static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
{
	git_buf buf = GIT_BUF_INIT;

	/* Chunk header */
	git_buf_printf(&buf, "%X\r\n", (unsigned)len);

	if (git_buf_oom(&buf))
		return -1;

	if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) {
		git_buf_free(&buf);
		return -1;
	}

	git_buf_free(&buf);

	/* Chunk body */
	if (len > 0 && gitno_send(socket, buffer, len, 0) < 0)
		return -1;

	/* Chunk footer */
	if (gitno_send(socket, "\r\n", 2, 0) < 0)
		return -1;

	return 0;
}
Beispiel #2
0
static int http_connect(git_transport *transport, int direction)
{
	transport_http *t = (transport_http *) transport;
	int ret;
	git_buf request = GIT_BUF_INIT;
	const char *service = "upload-pack";
	const char *url = t->parent.url, *prefix_http = "http://", *prefix_https = "https://";
	const char *default_port;

	if (direction == GIT_DIR_PUSH) {
		giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
		return -1;
	}

	t->parent.direction = direction;
	if (git_vector_init(&t->refs, 16, NULL) < 0)
		return -1;

	if (!git__prefixcmp(url, prefix_http)) {
		url = t->parent.url + strlen(prefix_http);
		default_port = "80";
	}

	if (!git__prefixcmp(url, prefix_https)) {
		url += strlen(prefix_https);
		default_port = "443";
	}

	t->path = strchr(url, '/');

	if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, default_port)) < 0)
		goto cleanup;

	t->service = git__strdup(service);
	GITERR_CHECK_ALLOC(t->service);

	if ((ret = do_connect(t, t->host, t->port)) < 0)
		goto cleanup;

	/* Generate and send the HTTP request */
	if ((ret = gen_request(&request, t->path, t->host, "GET", service, 0, 1)) < 0) {
		giterr_set(GITERR_NET, "Failed to generate request");
		goto cleanup;
	}


	if (gitno_send(transport, request.ptr, request.size, 0) < 0)
		goto cleanup;

	ret = store_refs(t);

cleanup:
	git_buf_free(&request);
	git_buf_clear(&t->buf);

	return ret;
}
Beispiel #3
0
static int git_stream_write(
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
{
	git_stream *s = (git_stream *)stream;

	if (!s->sent_command && send_command(s) < 0)
		return -1;

	return gitno_send(&s->socket, buffer, len, 0);
}
Beispiel #4
0
static int http_stream_write_single(
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
{
	http_stream *s = (http_stream *)stream;
	http_subtransport *t = OWNING_SUBTRANSPORT(s);
	git_buf request = GIT_BUF_INIT;

	assert(t->connected);

	if (s->sent_request) {
		giterr_set(GITERR_NET, "Subtransport configured for only one write");
		return -1;
	}

	clear_parser_state(t);

	if (gen_request(&request, s, len) < 0) {
		giterr_set(GITERR_NET, "Failed to generate request");
		return -1;
	}

	if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
		goto on_error;

	if (len && gitno_send(&t->socket, buffer, len, 0) < 0)
		goto on_error;

	git_buf_free(&request);
	s->sent_request = 1;

	return 0;

on_error:
	git_buf_free(&request);
	return -1;
}
static int send_request(git_transport *t, const char *cmd, const char *url)
{
	int error;
	git_buf request = GIT_BUF_INIT;

	error = gen_proto(&request, cmd, url);
	if (error < 0)
		goto cleanup;

	error = gitno_send(t, request.ptr, request.size, 0);

cleanup:
	git_buf_free(&request);
	return error;
}
Beispiel #6
0
static int send_command(git_stream *s)
{
	int error;
	git_buf request = GIT_BUF_INIT;

	error = gen_proto(&request, s->cmd, s->url);
	if (error < 0)
		goto cleanup;

	/* It looks like negative values are errors here, and positive values
	 * are the number of bytes sent. */
	error = gitno_send(&s->socket, request.ptr, request.size, 0);

	if (error >= 0)
		s->sent_command = 1;

cleanup:
	git_buf_free(&request);
	return error;
}
static int git_close(git_transport *t)
{
	git_buf buf = GIT_BUF_INIT;

	if (git_pkt_buffer_flush(&buf) < 0)
		return -1;
	/* Can't do anything if there's an error, so don't bother checking  */
	gitno_send(t, buf.ptr, buf.size, 0);
	git_buf_free(&buf);

	if (gitno_close(t->socket) < 0) {
		giterr_set(GITERR_NET, "Failed to close socket");
		return -1;
	}

	t->connected = 0;

#ifdef GIT_WIN32
	WSACleanup();
#endif

	return 0;
}
static int git_negotiation_step(struct git_transport *transport, void *data, size_t len)
{
	return gitno_send(transport, data, len, 0);
}
Beispiel #9
0
static int http_stream_write_chunked(
	git_smart_subtransport_stream *stream,
	const char *buffer,
	size_t len)
{
	http_stream *s = (http_stream *)stream;
	http_subtransport *t = OWNING_SUBTRANSPORT(s);

	assert(t->connected);

	/* Send the request, if necessary */
	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 (len > CHUNK_SIZE) {
		/* Flush, if necessary */
		if (s->chunk_buffer_len > 0) {
			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				return -1;

			s->chunk_buffer_len = 0;
		}

		/* Write chunk directly */
		if (write_chunk(&t->socket, buffer, len) < 0)
			return -1;
	}
	else {
		/* Append as much to the buffer as we can */
		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);

		if (!s->chunk_buffer)
			s->chunk_buffer = git__malloc(CHUNK_SIZE);

		memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
		s->chunk_buffer_len += count;
		buffer += count;
		len -= count;

		/* Is the buffer full? If so, then flush */
		if (CHUNK_SIZE == s->chunk_buffer_len) {
			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
				return -1;

			s->chunk_buffer_len = 0;

			if (len > 0) {
				memcpy(s->chunk_buffer, buffer, len);
				s->chunk_buffer_len = len;
			}
		}
	}

	return 0;
}
Beispiel #10
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;
}
Beispiel #11
0
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;
}
Beispiel #12
0
int git_pkt_send_flush(int s)
{
	char flush[] = "0000";

	return gitno_send(s, flush, STRLEN(flush), 0);
}