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; }
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; }
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); }
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; }
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); }
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; }
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; }
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; }
int git_pkt_send_flush(int s) { char flush[] = "0000"; return gitno_send(s, flush, STRLEN(flush), 0); }