// processApplicationAckPacket processes an application ack packet
170531
func (d *Datapath) processApplicationAckPacket(tcpPacket *packet.Packet, context *pucontext.PUContext, conn *connection.TCPConnection) error {

	// Only process the first Ack of a connection. This means that we have received
	// as SynAck packet and we can now process the ACK.
	if conn.GetState() == connection.TCPSynAckReceived && tcpPacket.IsEmptyTCPPayload() {
		// Create a new token that includes the source and destinatio nonse
		// These are both challenges signed by the secret key and random for every
		// connection minimizing the chances of a replay attack
		token, err := d.tokenAccessor.CreateAckPacketToken(context, &conn.Auth)
		if err != nil {
			return err
		}

		tcpOptions := d.createTCPAuthenticationOption([]byte{})

		// Attach the tags to the packet
		if err := tcpPacket.TCPDataAttach(tcpOptions, token); err != nil {
			return err
		}

		conn.SetState(connection.TCPAckSend)

		// If its not a service connection, we release it to the kernel. Subsequent
		// packets after the first data packet, that might be already in the queue
		// will be transmitted through the kernel directly. Service connections are
		// delegated to the service module
		if !conn.ServiceConnection && tcpPacket.SourceAddress().String() != tcpPacket.DestinationAddress().String() &&
			!(tcpPacket.SourceAddress().IsLoopback() && tcpPacket.DestinationAddress().IsLoopback()) {
			if err := d.conntrack.UpdateApplicationFlowMark(
				tcpPacket.SourceAddress(),
				tcpPacket.DestinationAddress(),
				tcpPacket.IPProto(),
				tcpPacket.SourcePort(),
				tcpPacket.DestPort(),
				constants.DefaultConnMark,
			); err != nil {
				zap.L().Error("Failed to update conntrack table for flow after ack packet",
					zap.String("context", string(conn.Auth.LocalContext)),
					zap.String("app-conn", tcpPacket.L4ReverseFlowHash()),
					zap.String("state", fmt.Sprintf("%d", conn.GetState())),
					zap.Error(err),
				)
			}
		}

		return nil
	}

	// If we are already in the connection.TCPData connection just forward the packet
	if conn.GetState() == connection.TCPData {
		return nil
	}

	if conn.GetState() == connection.UnknownState {
		// Check if the destination is in the external services approved cache
		// and if yes, allow the packet to go and release the flow.
		_, policy, perr := context.ApplicationACLPolicyFromAddr(tcpPacket.DestinationAddress(), tcpPacket.DestPort())

		if perr != nil {
			// If it is an SSH PU, we let the connection go through
			// It means it is a new SSH Session connection, we mark
			// the packet and let it go through.
			if context.Type() == common.SSHSessionPU {
				if err := d.conntrack.UpdateApplicationFlowMark(
					tcpPacket.SourceAddress(),
					tcpPacket.DestinationAddress(),
					tcpPacket.IPProto(),
					tcpPacket.SourcePort(),
					tcpPacket.DestPort(),
					constants.DefaultConnMark,
				); err != nil {
					zap.L().Error("Failed to update conntrack entry for flow at Ack packet",
						zap.String("context", string(conn.Auth.LocalContext)),
						zap.String("app-conn", tcpPacket.L4ReverseFlowHash()),
						zap.String("state", fmt.Sprintf("%d", conn.GetState())),
						zap.Error(err),
					)
				}
				return nil
			}
			// Convert Ack to FinAck for all other pus
			return tcpPacket.ConvertAcktoFinAck()
		}

		if policy.Action.Rejected() {
			return errors.New("Reject the packet")
		}

		if err := d.conntrack.UpdateApplicationFlowMark(
			tcpPacket.SourceAddress(),
			tcpPacket.DestinationAddress(),
			tcpPacket.IPProto(),
			tcpPacket.SourcePort(),
			tcpPacket.DestPort(),
			constants.DefaultConnMark,
		); err != nil {
			zap.L().Error("Failed to update conntrack entry for flow at Ack packet",
				zap.String("context", string(conn.Auth.LocalContext)),
				zap.String("app-conn", tcpPacket.L4ReverseFlowHash()),
				zap.String("state", fmt.Sprintf("%d", conn.GetState())),
				zap.Error(err),
			)
		}
		return nil
	}

	// Here we capture the first data packet after an ACK packet by modyfing the
	// state. We will not release the caches though to deal with re-transmissions.
	// We will let the caches expire.
	if conn.GetState() == connection.TCPAckSend {
		conn.SetState(connection.TCPData)
		return nil
	}

	return fmt.Errorf("received application ack packet in the wrong state: %d", conn.GetState())
}
305934
func (h *modelRestHandler) processRemoteApplication(r *http.Request, w http.ResponseWriter, st *state.State) error {
	query := r.URL.Query()
	name := query.Get(":name")
	remoteApp, err := st.RemoteApplication(name)
	if err != nil {
		return errors.Trace(err)
	}
	attribute := query.Get(":attribute")
	// TODO(wallyworld) - support more than just "icon"
	if attribute != "icon" {
		return errors.NotSupportedf("attribute %v on entity %v", attribute, name)
	}

	// Get the backend state for the source model so we can lookup the app in that model to get the charm details.
	offerUUID := remoteApp.OfferUUID()
	sourceModelUUID := remoteApp.SourceModel().Id()
	sourceSt, err := h.ctxt.srv.shared.statePool.Get(sourceModelUUID)
	if err != nil {
		return errors.Trace(err)
	}
	defer sourceSt.Release()

	offers := state.NewApplicationOffers(sourceSt.State)
	offer, err := offers.ApplicationOfferForUUID(offerUUID)
	if err != nil {
		return errors.Trace(err)
	}
	app, err := sourceSt.Application(offer.ApplicationName)
	if err != nil {
		return errors.Trace(err)
	}
	ch, _, err := app.Charm()
	if err != nil {
		return errors.Trace(err)
	}

	store := storage.NewStorage(sourceSt.ModelUUID(), sourceSt.MongoSession())
	// Use the storage to retrieve and save the charm archive.
	charmPath, err := common.ReadCharmFromStorage(store, h.dataDir, ch.StoragePath())
	if errors.IsNotFound(err) {
		return h.byteSender(w, ".svg", []byte(common.DefaultCharmIcon))
	}
	if err != nil {
		return errors.Trace(err)
	}
	iconContents, err := common.CharmArchiveEntry(charmPath, "icon.svg", true)
	if errors.IsNotFound(err) {
		return h.byteSender(w, ".svg", []byte(common.DefaultCharmIcon))
	}
	if err != nil {
		return errors.Trace(err)
	}
	return h.byteSender(w, ".svg", iconContents)
}
752348
func NewApplication() *Application {

	return &Application{
		ReadOnly:               false,
		PerformanceMonitorType: "FIRST_PACKET",
		CertificateCommonName:  "*",
		NetworkSymmetry:        false,
		EnablePPS:              false,
		PostClassificationPath: "ANY",
		PreClassificationPath:  "DEFAULT",
		Protocol:               "NONE",
		Symmetry:               false,
	}
}
1019455
func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) {
	e.rcvMu.Lock()

	// Drop the packet if our buffer is currently full.
	if !e.rcvReady || e.rcvClosed || e.rcvBufSize >= e.rcvBufSizeMax {
		e.stack.Stats().DroppedPackets.Increment()
		e.rcvMu.Unlock()
		return
	}

	wasEmpty := e.rcvBufSize == 0

	// Push new packet into receive list and increment the buffer size.
	pkt := &icmpPacket{
		senderAddress: tcpip.FullAddress{
			NIC:  r.NICID(),
			Addr: id.RemoteAddress,
		},
	}

	pkt.data = vv.Clone(pkt.views[:])

	e.rcvList.PushBack(pkt)
	e.rcvBufSize += pkt.data.Size()

	pkt.timestamp = e.stack.NowNanoseconds()

	e.rcvMu.Unlock()

	// Notify any waiters that there's data to be read now.
	if wasEmpty {
		e.waiterQueue.Notify(waiter.EventIn)
	}
}
1317338
func (c *Client) processUnsuback(unsuback *packet.Unsuback) error {
	// remove packet from store
	err := c.Session.DeletePacket(session.Outgoing, unsuback.ID)
	if err != nil {
		return err
	}

	// get future
	unsubscribeFuture := c.futureStore.Get(unsuback.ID)
	if unsubscribeFuture == nil {
		return nil // ignore a wrongly sent Unsuback packet
	}

	// complete future
	unsubscribeFuture.Complete()

	// remove future from store
	c.futureStore.Delete(unsuback.ID)

	return nil
}
733814
func (d *Datapath) processNetworkSynAckPacket(context *pucontext.PUContext, conn *connection.TCPConnection, tcpPacket *packet.Packet) (action interface{}, claims *tokens.ConnectionClaims, err error) {

	// Packets with no authorization are processed as external services based on the ACLS
	if err = tcpPacket.CheckTCPAuthenticationOption(enforcerconstants.TCPAuthenticationOptionBaseLen); err != nil {

		if _, err := d.puFromContextID.Get(conn.Context.ID()); err != nil {
			// PU has been deleted. Ignore these packets
			return nil, nil, fmt.Errorf("PU is already dead - drop SynAck packet")
		}

		flowHash := tcpPacket.SourceAddress().String() + ":" + strconv.Itoa(int(tcpPacket.SourcePort()))
		if plci, plerr := context.RetrieveCachedExternalFlowPolicy(flowHash); plerr == nil {
			plc := plci.(*policyPair)
			d.releaseFlow(context, plc.report, plc.packet, tcpPacket)
			return plc.packet, nil, nil
		}

		// Never seen this IP before, let's parse them.
		report, pkt, perr := context.ApplicationACLPolicyFromAddr(tcpPacket.SourceAddress(), tcpPacket.SourcePort())
		if perr != nil || pkt.Action.Rejected() {
			d.reportReverseExternalServiceFlow(context, report, pkt, true, tcpPacket)
			return nil, nil, fmt.Errorf("no auth or acls: drop synack packet and connection: %s: action=%d", perr, pkt.Action)
		}

		// Added to the cache if we can accept it
		context.CacheExternalFlowPolicy(
			tcpPacket,
			&policyPair{
				report: report,
				packet: pkt,
			},
		)

		// Set the state to Data so the other state machines ignore subsequent packets
		conn.SetState(connection.TCPData)

		d.releaseFlow(context, report, pkt, tcpPacket)

		return pkt, nil, nil
	}

	// This is a corner condition. We are receiving a SynAck packet and we are in
	// a state that indicates that we have already processed one. This means that
	// our ack packet was lost. We need to revert conntrack in this case and get
	// back into the picture.
	if conn.GetState() != connection.TCPSynSend {

		// Revert the connmarks - dealing with retransmissions
		if cerr := d.conntrack.UpdateNetworkFlowMark(
			tcpPacket.SourceAddress(),
			tcpPacket.DestinationAddress(),
			tcpPacket.IPProto(),
			tcpPacket.SourcePort(),
			tcpPacket.DestPort(),
			0,
		); cerr != nil {
			zap.L().Error("Failed to update conntrack table for flow after synack packet",
				zap.String("context", string(conn.Auth.LocalContext)),
				zap.String("app-conn", tcpPacket.L4ReverseFlowHash()),
				zap.String("state", fmt.Sprintf("%d", conn.GetState())),
				zap.Error(err),
			)
		}
	}

	// Now we can process the SynAck packet with its options
	tcpData := tcpPacket.ReadTCPData()
	if len(tcpData) == 0 {
		d.reportRejectedFlow(tcpPacket, nil, context.ManagementID(), collector.DefaultEndPoint, context, collector.MissingToken, nil, nil, true)
		return nil, nil, errors.New("SynAck packet dropped because of missing token")
	}

	claims, err = d.tokenAccessor.ParsePacketToken(&conn.Auth, tcpPacket.ReadTCPData())
	if err != nil {
		d.reportRejectedFlow(tcpPacket, nil, context.ManagementID(), collector.DefaultEndPoint, context, collector.MissingToken, nil, nil, true)
		return nil, nil, fmt.Errorf("SynAck packet dropped because of bad claims: %s", err)
	}

	if claims == nil {
		d.reportRejectedFlow(tcpPacket, nil, context.ManagementID(), collector.DefaultEndPoint, context, collector.MissingToken, nil, nil, true)
		return nil, nil, errors.New("SynAck packet dropped because of no claims")
	}

	tcpPacket.ConnectionMetadata = &conn.Auth

	if err := tcpPacket.CheckTCPAuthenticationOption(enforcerconstants.TCPAuthenticationOptionBaseLen); err != nil {
		d.reportRejectedFlow(tcpPacket, conn, conn.Auth.RemoteContextID, context.ManagementID(), context, collector.InvalidHeader, nil, nil, true)
		return nil, nil, errors.New("TCP authentication option not found")
	}

	// Remove any of our data
	if err := tcpPacket.TCPDataDetach(enforcerconstants.TCPAuthenticationOptionBaseLen); err != nil {
		d.reportRejectedFlow(tcpPacket, conn, conn.Auth.RemoteContextID, context.ManagementID(), context, collector.InvalidPayload, nil, nil, true)
		return nil, nil, fmt.Errorf("SynAck packet dropped because of invalid format: %s", err)
	}

	tcpPacket.DropTCPDetachedBytes()

	if !d.mutualAuthorization {
		// If we dont do mutual authorization, dont lookup txt rules.
		conn.SetState(connection.TCPSynAckReceived)

		// conntrack
		d.netReplyConnectionTracker.AddOrUpdate(tcpPacket.L4FlowHash(), conn)
		return nil, claims, nil
	}

	report, pkt := context.SearchTxtRules(claims.T, !d.mutualAuthorization)

	// Report and release traffic belonging to the same pu
	if conn.Auth.RemoteContextID == context.ManagementID() {
		conn.SetState(connection.TCPData)
		conn.SetLoopbackConnection(true)
		d.reportAcceptedFlow(tcpPacket, conn, conn.Auth.RemoteContextID, context.ManagementID(), context, nil, nil, true)
		d.releaseUnmonitoredFlow(tcpPacket)
		return nil, nil, nil
	}

	// NOTE: For backward compatibility, remove this check later
	if claims.H != nil {
		if claims.H.ToClaimsHeader().Encrypt() != pkt.Action.Encrypted() {
			d.reportRejectedFlow(tcpPacket, conn, conn.Auth.RemoteContextID, context.ManagementID(), context, collector.EncryptionMismatch, nil, nil, true)
			return nil, nil, fmt.Errorf("syn/ack packet dropped because of encryption mismatch")
		}
	}

	if pkt.Action.Rejected() {
		d.reportRejectedFlow(tcpPacket, conn, conn.Auth.RemoteContextID, context.ManagementID(), context, collector.PolicyDrop, report, pkt, true)
		return nil, nil, fmt.Errorf("dropping because of reject rule on transmitter: %s", claims.T.String())
	}

	conn.SetState(connection.TCPSynAckReceived)

	// conntrack
	d.netReplyConnectionTracker.AddOrUpdate(tcpPacket.L4FlowHash(), conn)
	return pkt, claims, nil
}
1314336
func (client *Client) CreateApplicationProcessScale(appGUID string, process Process) (Process, Warnings, error) {
	body, err := json.Marshal(process)
	if err != nil {
		return Process{}, nil, err
	}

	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.PostApplicationProcessActionScaleRequest,
		Body:        bytes.NewReader(body),
		URIParams:   internal.Params{"app_guid": appGUID, "type": process.Type},
	})
	if err != nil {
		return Process{}, nil, err
	}

	var responseProcess Process
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &responseProcess,
	}
	err = client.connection.Make(request, &response)
	return responseProcess, response.Warnings, err
}
321502
func (c *Conn) receivePacket(h header, payload []byte) {
	c.packetReadTimeoutTimer.Reset(packetReadTimeout)
	c.processDelivery(h, payload)
}
563656
func (fw *Firewaller) startApplication(app *firewaller.Application) error {
	exposed, err := app.IsExposed()
	if err != nil {
		return err
	}
	applicationd := &applicationData{
		fw:          fw,
		application: app,
		exposed:     exposed,
		unitds:      make(map[names.UnitTag]*unitData),
	}
	fw.applicationids[app.Tag()] = applicationd

	err = catacomb.Invoke(catacomb.Plan{
		Site: &applicationd.catacomb,
		Work: func() error {
			return applicationd.watchLoop(exposed)
		},
	})
	if err != nil {
		return errors.Trace(err)
	}
	if err := fw.catacomb.Add(applicationd); err != nil {
		return errors.Trace(err)
	}
	return nil
}
1037327
func (s *blockWriteStream) ackPackets() {
	reader := bufio.NewReader(s.conn)

	for {
		p, ok := <-s.packets
		if !ok {
			// All packets all acked.
			return
		}

		// If we fail to read the ack at all, that counts as a failure from the
		// first datanode (the one we're connected to).
		ack := &hdfs.PipelineAckProto{}
		err := readPrefixedMessage(reader, ack)
		if err != nil {
			s.ackError = err
			break
		}

		seqno := int(ack.GetSeqno())
		for i, status := range ack.GetReply() {
			if status != hdfs.Status_SUCCESS {
				s.ackError = ackError{status: status, seqno: seqno, pipelineIndex: i}
				break
			}
		}

		if seqno != p.seqno {
			s.ackError = ErrInvalidSeqno
			break
		}
	}

	// Once we've seen an error, just keep reading packets off the channel (but
	// not off the socket) until the writing thread figures it out. If we don't,
	// the upstream thread could deadlock waiting for the channel to have space.
	for _ = range s.packets {
	}
}
72781
func Process(pkg, outdir string, newType string, optset ...func(*Options)) error {
	opts := DefaultOptions
	for _, fn := range optset {
		fn(&opts)
	}

	p, err := findPackage(pkg)
	if err != nil {
		return err
	}

	if outdir == "" {
		outdir = path.Base(pkg)
	}

	if err := os.MkdirAll(outdir, os.ModePerm); err != nil {
		return err
	}

	t := parseTargetType(newType)

	files, err := processFiles(p, p.GoFiles, t)
	if err != nil {
		return err
	}

	if err := write(outdir, files); err != nil {
		return err
	}

	if opts.SkipTestFiles {
		return nil
	}

	files, err = processFiles(p, p.TestGoFiles, t)
	if err != nil {
		return err
	}

	return write(outdir, files)
}
724212
func (s *packetManager) readyPacket(pkt orderedResponse) {
	s.responses <- pkt
	s.working.Done()
}
1287154
func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
	headerView := vv.First()
	h := header.IPv6(headerView)
	if !h.IsValid(vv.Size()) {
		return
	}

	vv.TrimFront(header.IPv6MinimumSize)
	vv.CapLength(int(h.PayloadLength()))

	p := h.TransportProtocol()
	if p == header.ICMPv6ProtocolNumber {
		e.handleICMP(r, headerView, vv)
		return
	}

	r.Stats().IP.PacketsDelivered.Increment()
	e.dispatcher.DeliverTransportPacket(r, p, headerView, vv)
}
398660
func (pc *processContainer) processPath(path string, info os.FileInfo, err error) error {
	if err != nil {
		utils.Logger.Error("Error scanning app source:", "error", err)
		return nil
	}

	if !info.IsDir() || info.Name() == "tmp" {
		return nil
	}

	// Get the import path of the package.
	pkgImportPath := pc.rootImportPath
	if pc.root != path {
		pkgImportPath = pc.rootImportPath + "/" + filepath.ToSlash(path[len(pc.root)+1:])
	}

	// Parse files within the path.
	var pkgs map[string]*ast.Package
	fset := token.NewFileSet()
	pkgs, err = parser.ParseDir(
		fset,
		path,
		func(f os.FileInfo) bool {
			return !f.IsDir() && !strings.HasPrefix(f.Name(), ".") && strings.HasSuffix(f.Name(), ".go")
		},
		0)

	if err != nil {
		if errList, ok := err.(scanner.ErrorList); ok {
			var pos = errList[0].Pos
			newError := &utils.Error{
				SourceType:  ".go source",
				Title:       "Go Compilation Error",
				Path:        pos.Filename,
				Description: errList[0].Msg,
				Line:        pos.Line,
				Column:      pos.Column,
				SourceLines: utils.MustReadLines(pos.Filename),
			}

			errorLink := pc.paths.Config.StringDefault("error.link", "")
			if errorLink != "" {
				newError.SetLink(errorLink)
			}
			return newError
		}

		// This is exception, err already checked above. Here just a print
		ast.Print(nil, err)
		utils.Logger.Fatal("Failed to parse dir", "error", err)
	}

	// Skip "main" packages.
	delete(pkgs, "main")

	// Ignore packages that end with _test
	// These cannot be included in source code that is not generated specifically as a test
	for i := range pkgs {
		if len(i) > 6 {
			if string(i[len(i)-5:]) == "_test" {
				delete(pkgs, i)
			}
		}
	}

	// If there is no code in this directory, skip it.
	if len(pkgs) == 0 {
		return nil
	}

	// There should be only one package in this directory.
	if len(pkgs) > 1 {
		for i := range pkgs {
			println("Found package ", i)
		}
		utils.Logger.Fatal("Most unexpected! Multiple packages in a single directory:", "packages", pkgs)
	}

	var pkg *ast.Package
	for _, v := range pkgs {
		pkg = v
	}

	if pkg != nil {
		pc.srcInfo = appendSourceInfo(pc.srcInfo, processPackage(fset, pkgImportPath, path, pkg))
	} else {
		utils.Logger.Info("Ignoring package, because it contained no packages", "path", path)
	}
	return nil
}
860350
func (api *Client) CreateApplication(data *ApplicationData) (string, error) {
	_, headers, err := api.makeRequest(http.MethodPost, api.concatUserPath(applicationsPath), nil, data)
	if err != nil {
		return "", err
	}
	return getIDFromLocationHeader(headers), nil
}
33161
func (d *Datapath) processApplicationSynAckPacket(tcpPacket *packet.Packet, context *pucontext.PUContext, conn *connection.TCPConnection) error {

	// if the traffic belongs to the same pu, let it go
	if conn.GetState() == connection.TCPData && conn.IsLoopbackConnection() {
		return nil
	}

	// If we are already in the connection.TCPData, it means that this is an external flow
	// At this point we can release the flow to the kernel by updating conntrack
	// We can also clean up the state since we are not going to see any more
	// packets from this connection.
	if conn.GetState() == connection.TCPData && !conn.ServiceConnection {
		if err := d.conntrack.UpdateApplicationFlowMark(
			tcpPacket.SourceAddress(),
			tcpPacket.DestinationAddress(),
			tcpPacket.IPProto(),
			tcpPacket.SourcePort(),
			tcpPacket.DestPort(),
			constants.DefaultConnMark,
		); err != nil {
			zap.L().Error("Failed to update conntrack entry for flow at SynAck packet",
				zap.String("context", string(conn.Auth.LocalContext)),
				zap.String("app-conn", tcpPacket.L4ReverseFlowHash()),
				zap.String("state", fmt.Sprintf("%d", conn.GetState())),
				zap.Error(err),
			)
		}

		err1 := d.netOrigConnectionTracker.Remove(tcpPacket.L4ReverseFlowHash())
		err2 := d.appReplyConnectionTracker.Remove(tcpPacket.L4FlowHash())

		if err1 != nil || err2 != nil {
			zap.L().Debug("Failed to remove cache entries")
		}

		return nil
	}

	// We now process packets that need authorization options

	// Create TCP Option
	tcpOptions := d.createTCPAuthenticationOption([]byte{})

	claimsHeader := claimsheader.NewClaimsHeader(
		claimsheader.OptionEncrypt(conn.PacketFlowPolicy.Action.Encrypted()),
	)

	tcpData, err := d.tokenAccessor.CreateSynAckPacketToken(context, &conn.Auth, claimsHeader)

	if err != nil {
		return err
	}

	// Set the state for future reference
	conn.SetState(connection.TCPSynAckSend)

	// Attach the tags to the packet
	return tcpPacket.TCPDataAttach(tcpOptions, tcpData)
}
1178658
func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
	// TODO support multi_ack mode
	// TODO support multi_ack_detailed mode
	// TODO support acks for common objects
	// TODO build a proper state machine for all these processing options

	if err := req.UploadRequest.Encode(w); err != nil {
		return fmt.Errorf("sending upload-req message: %s", err)
	}

	if err := req.UploadHaves.Encode(w, true); err != nil {
		return fmt.Errorf("sending haves message: %s", err)
	}

	if err := sendDone(w); err != nil {
		return fmt.Errorf("sending done message: %s", err)
	}

	if err := w.Close(); err != nil {
		return fmt.Errorf("closing input: %s", err)
	}

	return nil
}