<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

func convertChildInodeEntry(
	clock timeutil.Clock,
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = in.AttributesExpiration.Sub(clock.Now())
	out.EntryValid = in.EntryExpiration.Sub(clock.Now())
}

func convertHeader(
	in bazilfuse.Header) (out RequestHeader) {
	out.Uid = in.Uid
	out.Gid = in.Gid
	return
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Convert the request.
		req := &InitRequest{
			Header: convertHeader(typed.Header),
		}

		// Call the file system.
		_, err := s.fs.Init(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.LookupRequest:
		// Convert the request.
		req := &LookUpInodeRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		resp, err := s.fs.LookUpInode(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.LookupResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, fuseResp)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.SetattrRequest:
		// Convert the request.
		req := &SetInodeAttributesRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
		}

		if typed.Valid&bazilfuse.SetattrSize != 0 {
			req.Size = &typed.Size
		}

		if typed.Valid&bazilfuse.SetattrMode != 0 {
			req.Mode = &typed.Mode
		}

		if typed.Valid&bazilfuse.SetattrAtime != 0 {
			req.Atime = &typed.Atime
		}

		if typed.Valid&bazilfuse.SetattrMtime != 0 {
			req.Mtime = &typed.Mtime
		}

		// Call the file system.
		resp, err := s.fs.SetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.SetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.MkdirRequest:
		// Convert the request.
		req := &MkDirRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}

		// Call the file system.
		resp, err := s.fs.MkDir(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.MkdirResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.CreateRequest:
		// Convert the request.
		req := &CreateFileRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
			Flags:  typed.Flags,
		}

		// Call the file system.
		resp, err := s.fs.CreateFile(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.CreateResponse{
			OpenResponse: bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			},
		}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.RemoveRequest:
		// We don't yet support files.
		if !typed.Dir {
			s.logger.Println("Not supported for files. Returning ENOSYS.")
			typed.RespondError(ENOSYS)
			return
		}

		// Convert the request.
		req := &RmDirRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		_, err := s.fs.RmDir(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Respond successfully.
		s.logger.Println("Responding OK.")
		typed.Respond()

	case *bazilfuse.OpenRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &OpenDirRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Flags:  typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &OpenFileRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Flags:  typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.ReadRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &ReadDirRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &ReadFileRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.WriteRequest:
		// Convert the request.
		req := &WriteFileRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}

		// Call the file system.
		_, err := s.fs.WriteFile(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.WriteResponse{
			Size: len(typed.Data),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}
<commit_msg>Added server support for Unlink.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

func convertChildInodeEntry(
	clock timeutil.Clock,
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = in.AttributesExpiration.Sub(clock.Now())
	out.EntryValid = in.EntryExpiration.Sub(clock.Now())
}

func convertHeader(
	in bazilfuse.Header) (out RequestHeader) {
	out.Uid = in.Uid
	out.Gid = in.Gid
	return
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Convert the request.
		req := &InitRequest{
			Header: convertHeader(typed.Header),
		}

		// Call the file system.
		_, err := s.fs.Init(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.LookupRequest:
		// Convert the request.
		req := &LookUpInodeRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		resp, err := s.fs.LookUpInode(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.LookupResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, fuseResp)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.SetattrRequest:
		// Convert the request.
		req := &SetInodeAttributesRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
		}

		if typed.Valid&bazilfuse.SetattrSize != 0 {
			req.Size = &typed.Size
		}

		if typed.Valid&bazilfuse.SetattrMode != 0 {
			req.Mode = &typed.Mode
		}

		if typed.Valid&bazilfuse.SetattrAtime != 0 {
			req.Atime = &typed.Atime
		}

		if typed.Valid&bazilfuse.SetattrMtime != 0 {
			req.Mtime = &typed.Mtime
		}

		// Call the file system.
		resp, err := s.fs.SetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.SetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.MkdirRequest:
		// Convert the request.
		req := &MkDirRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}

		// Call the file system.
		resp, err := s.fs.MkDir(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.MkdirResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.CreateRequest:
		// Convert the request.
		req := &CreateFileRequest{
			Header: convertHeader(typed.Header),
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
			Flags:  typed.Flags,
		}

		// Call the file system.
		resp, err := s.fs.CreateFile(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.CreateResponse{
			OpenResponse: bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			},
		}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.RemoveRequest:
		if typed.Dir {
			// Convert the request.
			req := &RmDirRequest{
				Header: convertHeader(typed.Header),
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}

			// Call the file system.
			_, err := s.fs.RmDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Respond successfully.
			s.logger.Println("Responding OK.")
			typed.Respond()
		} else {
			// Convert the request.
			req := &UnlinkRequest{
				Header: convertHeader(typed.Header),
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}

			// Call the file system.
			_, err := s.fs.Unlink(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Respond successfully.
			s.logger.Println("Responding OK.")
			typed.Respond()
		}

	case *bazilfuse.OpenRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &OpenDirRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Flags:  typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &OpenFileRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Flags:  typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.ReadRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &ReadDirRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &ReadFileRequest{
				Header: convertHeader(typed.Header),
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.WriteRequest:
		// Convert the request.
		req := &WriteFileRequest{
			Header: convertHeader(typed.Header),
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}

		// Call the file system.
		_, err := s.fs.WriteFile(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.WriteResponse{
			Size: len(typed.Data),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/dgrijalva/jwt-go"
	"github.com/dst-hackathon/socialradar-api/configuration"
	"github.com/dst-hackathon/socialradar-api/login"
	"github.com/dst-hackathon/socialradar-api/question"
	"github.com/dst-hackathon/socialradar-api/signup"
	"github.com/dst-hackathon/socialradar-api/user"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"github.com/rs/cors"
	"gopkg.in/unrolled/render.v1"
	"log"
	"net/http"
	"strings"
)
var config configuration.Configuration = configuration.ReadFile()

var config configuration.Configuration = configuration.ReadFile()
var signKey interface{} = []byte{1, 1, 1, 1, 1, 1, 1}

func main() {
	router := mux.NewRouter().StrictSlash(false)

	question.Init(router)
	user.Init(router)
	login.Init(router)
	signup.Init(router)

	c := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
	})

	n := negroni.Classic()
	n.Use(negroni.HandlerFunc(RenderIntializer))
	n.Use(negroni.HandlerFunc(ConfigInitializer))
	n.Use(negroni.HandlerFunc(DbInitializer))
	n.Use(c)
	n.Use(negroni.HandlerFunc(SecurityInitializer))
	n.UseHandler(router)
	n.Run(":3000")
}

func RenderIntializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	render := render.New(render.Options{
		IndentJSON: true,
	})
	context.Set(r, "render", render)

	next(rw, r)
}

func ConfigInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	context.Set(r, "config", config)
	next(rw, r)
}

func DbInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	dbUrl := fmt.Sprintf("postgres://%s:%s@%s:%s/socialradar_development?sslmode=disable", config.DbUser, config.DbPassword, config.DbHost, config.DbPort)
	db, err := sql.Open("postgres", dbUrl)
	if err != nil {
		log.Fatal(err)
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}

	context.Set(r, "db", db)
	next(rw, r)

	db.Close()
}

func SecurityInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	var tokenString string
	uri := r.RequestURI
	log.Printf("Security checking for : " + uri)
	if strings.HasPrefix(uri, "/login") ||
		strings.HasPrefix(uri, "/signup") {
		next(rw, r)
		return
	}
	tokenString = r.Header.Get("token") //r.FormValue("token")
	log.Printf("Token provide:" + tokenString)
	if tokenString != "" {
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			var signKey []byte = []byte(config.ApiSignKey);
			return signKey, nil
		})
		if err == nil && token.Valid {
			log.Printf("decode token.Claims[id] = " + token.Claims["id"].(string))
			context.Set(r, "user_id", token.Claims["id"])
		} else {
			log.Printf("Error")
			http.Error(rw, err.Error(), http.StatusUnauthorized)
			return
		}
	}
	next(rw, r)
}
<commit_msg>Fix duplicate config declaration in server.go<commit_after>package main

import (
	"database/sql"
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/dgrijalva/jwt-go"
	"github.com/dst-hackathon/socialradar-api/configuration"
	"github.com/dst-hackathon/socialradar-api/login"
	"github.com/dst-hackathon/socialradar-api/question"
	"github.com/dst-hackathon/socialradar-api/signup"
	"github.com/dst-hackathon/socialradar-api/user"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"github.com/rs/cors"
	"gopkg.in/unrolled/render.v1"
	"log"
	"net/http"
	"strings"
)

var config configuration.Configuration = configuration.ReadFile()
var signKey interface{} = []byte{1, 1, 1, 1, 1, 1, 1}

func main() {
	router := mux.NewRouter().StrictSlash(false)

	question.Init(router)
	user.Init(router)
	login.Init(router)
	signup.Init(router)

	c := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
	})

	n := negroni.Classic()
	n.Use(negroni.HandlerFunc(RenderIntializer))
	n.Use(negroni.HandlerFunc(ConfigInitializer))
	n.Use(negroni.HandlerFunc(DbInitializer))
	n.Use(c)
	n.Use(negroni.HandlerFunc(SecurityInitializer))
	n.UseHandler(router)
	n.Run(":3000")
}

func RenderIntializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	render := render.New(render.Options{
		IndentJSON: true,
	})
	context.Set(r, "render", render)

	next(rw, r)
}

func ConfigInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	context.Set(r, "config", config)
	next(rw, r)
}

func DbInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	dbUrl := fmt.Sprintf("postgres://%s:%s@%s:%s/socialradar_development?sslmode=disable", config.DbUser, config.DbPassword, config.DbHost, config.DbPort)
	db, err := sql.Open("postgres", dbUrl)
	if err != nil {
		log.Fatal(err)
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}

	context.Set(r, "db", db)
	next(rw, r)

	db.Close()
}

func SecurityInitializer(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	var tokenString string
	uri := r.RequestURI
	log.Printf("Security checking for : " + uri)
	if strings.HasPrefix(uri, "/login") ||
		strings.HasPrefix(uri, "/signup") {
		next(rw, r)
		return
	}
	tokenString = r.Header.Get("token") //r.FormValue("token")
	log.Printf("Token provide:" + tokenString)
	if tokenString != "" {
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			var signKey []byte = []byte(config.ApiSignKey)
			return signKey, nil
		})
		if err == nil && token.Valid {
			log.Printf("decode token.Claims[id] = " + token.Claims["id"].(string))
			context.Set(r, "user_id", token.Claims["id"])
		} else {
			log.Printf("Error")
			http.Error(rw, err.Error(), http.StatusUnauthorized)
			return
		}
	}
	next(rw, r)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"image/color"
	"image/png"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/cupcake/sigil/gen"
)

var config = gen.Sigil{
	Rows: 5,
	Foreground: []color.NRGBA{
		rgb(45, 79, 255),
		rgb(254, 180, 44),
		rgb(226, 121, 234),
		rgb(30, 179, 253),
		rgb(232, 77, 65),
		rgb(49, 203, 115),
		rgb(141, 69, 170),
	},
	Background: rgb(224, 224, 224),
}

func rgb(r, g, b uint8) color.NRGBA { return color.NRGBA{r, g, b, 255} }

func imageHandler(w http.ResponseWriter, r *http.Request) {
	ext := path.Ext(r.URL.Path)
	if ext != "" && ext != ".png" && ext != ".svg" {
		ext = ""
	}

	width := 240
	q := r.URL.Query()
	if ws := q.Get("w"); ws != "" {
		var err error
		width, err = strconv.Atoi(ws)
		if err != nil {
			http.Error(w, "Invalid w parameter, must be an integer", http.StatusBadRequest)
			return
		}
		if width > 600 {
			http.Error(w, "Invalid w parameter, must be less than 600", http.StatusBadRequest)
			return
		}
		div := (config.Rows + 1) * 2
		if width%div != 0 {
			http.Error(w, "Invalid w parameter, must be evenly divisible by "+strconv.Itoa(div), http.StatusBadRequest)
			return
		}
	}
	inverted := false
	if inv := q.Get("inverted"); inv != "" && inv != "false" && inv != "0" {
		inverted = true
	}

	base := path.Base(r.URL.Path)
	base = base[:len(base)-len(ext)]
	var data []byte
	if len(base) == 32 {
		// try to decode hex MD5
		data, _ = hex.DecodeString(base)
	}
	if data == nil {
		data = md5hash(base)
	}

	etag := `"` + base64.StdEncoding.EncodeToString(data) + `"`
	w.Header().Set("Etag", etag)
	if cond := r.Header.Get("If-None-Match"); cond != "" {
		if strings.Contains(cond, etag) {
			w.WriteHeader(http.StatusNotModified)
			return
		}
	}

	w.Header().Set("Cache-Control", "max-age=315360000")
	if ext == ".svg" {
		w.Header().Set("Content-Type", "image/svg+xml")
		config.MakeSVG(w, inverted, data)
		return
	}
	w.Header().Set("Content-Type", "image/png")
	png.Encode(w, config.Make(width, inverted, data))
}

func md5hash(s string) []byte {
	h := md5.New()
	h.Write([]byte(s))
	return h.Sum(nil)
}

func main() {
	http.HandleFunc("/favicon.ico", http.NotFound)
	http.HandleFunc("/", imageHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8000"
	}
	log.Println("Starting sigil on :" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<commit_msg>Allow slashes in data<commit_after>package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"image/color"
	"image/png"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/cupcake/sigil/gen"
)

var config = gen.Sigil{
	Rows: 5,
	Foreground: []color.NRGBA{
		rgb(45, 79, 255),
		rgb(254, 180, 44),
		rgb(226, 121, 234),
		rgb(30, 179, 253),
		rgb(232, 77, 65),
		rgb(49, 203, 115),
		rgb(141, 69, 170),
	},
	Background: rgb(224, 224, 224),
}

func rgb(r, g, b uint8) color.NRGBA { return color.NRGBA{r, g, b, 255} }

func imageHandler(w http.ResponseWriter, r *http.Request) {
	ext := path.Ext(r.URL.Path)
	if ext != "" && ext != ".png" && ext != ".svg" {
		ext = ""
	}

	width := 240
	q := r.URL.Query()
	if ws := q.Get("w"); ws != "" {
		var err error
		width, err = strconv.Atoi(ws)
		if err != nil {
			http.Error(w, "Invalid w parameter, must be an integer", http.StatusBadRequest)
			return
		}
		if width > 600 {
			http.Error(w, "Invalid w parameter, must be less than 600", http.StatusBadRequest)
			return
		}
		div := (config.Rows + 1) * 2
		if width%div != 0 {
			http.Error(w, "Invalid w parameter, must be evenly divisible by "+strconv.Itoa(div), http.StatusBadRequest)
			return
		}
	}
	inverted := false
	if inv := q.Get("inverted"); inv != "" && inv != "false" && inv != "0" {
		inverted = true
	}

	str := r.URL.Path[1 : len(r.URL.Path)-len(ext)]
	var data []byte
	if len(str) == 32 {
		// try to decode hex MD5
		data, _ = hex.DecodeString(str)
	}
	if data == nil {
		data = md5hash(str)
	}

	etag := `"` + base64.StdEncoding.EncodeToString(data) + `"`
	w.Header().Set("Etag", etag)
	if cond := r.Header.Get("If-None-Match"); cond != "" {
		if strings.Contains(cond, etag) {
			w.WriteHeader(http.StatusNotModified)
			return
		}
	}

	w.Header().Set("Cache-Control", "max-age=315360000")
	if ext == ".svg" {
		w.Header().Set("Content-Type", "image/svg+xml")
		config.MakeSVG(w, inverted, data)
		return
	}
	w.Header().Set("Content-Type", "image/png")
	png.Encode(w, config.Make(width, inverted, data))
}

func md5hash(s string) []byte {
	h := md5.New()
	h.Write([]byte(s))
	return h.Sum(nil)
}

func main() {
	http.HandleFunc("/favicon.ico", http.NotFound)
	http.HandleFunc("/", imageHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8000"
	}
	log.Println("Starting sigil on :" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bones/config"
	"bones/repositories"
	"bones/web/handlers"
	"github.com/gorilla/pat"
	"log"
	"net/http"
)

func main() {
	repositories.Connect(config.Database())
	defer repositories.Cleanup()

	r := pat.New()

	setupRouting(r)

	http.Handle("/", r)
	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))

	log.Println("Starting server on port 8080")

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func setupRouting(r *pat.Router) {
	r.Get("/signup", handlers.LoadSignupPage)
	r.Post("/signup", handlers.CreateNewUser)
	r.Get("/", handlers.LoadHomePage)
}
<commit_msg>Refactor routing code<commit_after>package main

import (
	"bones/config"
	"bones/repositories"
	"bones/web/handlers"
	"github.com/gorilla/pat"
	"log"
	"net/http"
)

func main() {
	repositories.Connect(config.Database())
	defer repositories.Cleanup()

	r := setupRouting()

	http.Handle("/", r)
	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))

	log.Println("Starting server on port 8080")

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func setupRouting() *pat.Router {
	r := pat.New()

	r.Get("/signup", handlers.LoadSignupPage)
	r.Post("/signup", handlers.CreateNewUser)
	r.Get("/", handlers.LoadHomePage)

	return r
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type Deploy struct {
	Id   string
	Note string

	// known to the system config, versus just found either
	// running on a port or in the deploys dir
	Tracked bool

	// Port either specified in the config (regardless of running)
	// or port running on, for untracked things found on a port.
	// -1 for not specified
	Port int

	// http status code,
	// 0 for no connection attempted,
	// -1 for connection failed or timeout
	Health int

	Errors []string
}

type Label string

type Server interface {
	ListLabels() ([]Label, error)
	ListDeploys() ([]*Deploy, error)
	Run(deployId string) error
	Stop(deployId string) error
	Label(deployId string, label Label) error

	// TODO Maintenance mode
}

const (
	deploysDirName       = "deploys"
	deployConfigFileName = "deploy.json"
	serverConfigFileName = "config.json"
	haproxyConfig        = "haproxy.cfg"
	haproxyPid           = "haproxy.pid"
)

type Config struct {
	Ports  map[string]string
	Labels map[string]string // todo.
}

type ServerImpl struct {
	root        string
	config      *Config
	startPort   int
	endPort     int
	client      *http.Client
	deploysPath string
}

func readConfig(path string) (*Config, error) {
	var config Config
	if data, err := ioutil.ReadFile(path); err == nil {
		err = json.Unmarshal(data, &config)
		if err != nil {
			return nil, err
		}
	}
	if config.Ports == nil {
		config.Ports = make(map[string]string)
	}
	if config.Labels == nil {
		config.Labels = make(map[string]string)
	}
	return &config, nil
}

func NewServerImpl(root string) (*ServerImpl, error) {
	root, err := filepath.Abs(root)
	if err != nil {
		log.Fatal("Root path:", err)
	}
	config, err := readConfig(path.Join(root, serverConfigFileName))
	if err != nil {
		return nil, err
	}
	deploysPath := path.Join(root, deploysDirName)
	if _, err = os.Open(deploysPath); os.IsNotExist(err) {
		os.MkdirAll(deploysPath, 0644)
	}
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return errors.New("health check should not redirect")
		},
		Timeout: MAX_HEALTH_CHECK_TIME,
	}
	return &ServerImpl{
		root:        root,
		config:      config,
		startPort:   8001,
		endPort:     8099,
		client:      client,
		deploysPath: deploysPath,
	}, nil
}

func (s *ServerImpl) NewDeployDir() NewDeployDirResponse {
	t := time.Now()
	timestamp := fmt.Sprintf("%d-%02d-%02d-%02d-%02d-%02d",
		t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())

	return NewDeployDirResponse{
		DeployId: timestamp,
		Path:     s.deployDir(timestamp),
	}
}

func (s *ServerImpl) deployDir(deployId string) string {
	return path.Join(s.deploysPath, deployId)
}
func (s *ServerImpl) deployConfigFile(deployId string) string {
	return path.Join(s.deployDir(deployId), deployConfigFileName)
}

func (s *ServerImpl) ListDeploys() ([]*Deploy, error) {

	result, err := s.scanDeployDirs()
	if err != nil {
		return nil, err
	}

	result = s.scanConfig(result)
	result = s.scanPorts(result)

	return result, nil
}

func (s *ServerImpl) scanDeployDirs() ([]*Deploy, error) {
	infos, err := ioutil.ReadDir(s.deploysPath)
	if err != nil {
		return nil, err
	}

	var result []*Deploy
	for _, info := range infos {
		result = append(result, &Deploy{
			Id:      info.Name(),
			Port:    -1,
			Tracked: false,
		})
	}

	return result, nil
}

func (s *ServerImpl) scanConfig(deploys []*Deploy) []*Deploy {
	result := []*Deploy{}
	result = append(result, deploys...)

	for portStr, deployId := range s.config.Ports {
		port, err := strconv.Atoi(portStr)
		if err != nil {
			println(err)
			continue
		}

		deploy := findDeployById(deployId, result)

		if deploy == nil {
			result = append(result, &Deploy{
				Id:      deployId,
				Port:    port,
				Tracked: true,
				Errors:  []string{"No deploy dir present!"},
			})
		} else {
			deploy.Port = port
			deploy.Tracked = true
		}
	}

	return result
}

// Finds ports with *something* on them,
// either checking the health status for known deploys
// (and updating its run state)
// or adding a deploy with "unknown" id.
func (s *ServerImpl) scanPorts(deploys []*Deploy) []*Deploy {
	healthChecks := 0
	checkSync := make(chan int)

	result := []*Deploy{}
	result = append(result, deploys...)

	for port := s.startPort; port <= s.endPort; port++ {
		if portFree(port) {
			continue
		}

		dep := findDeployByPort(port, result)
		if dep == nil {
			result = append(result, &Deploy{
				Id:      fmt.Sprintf("(unkonwn-%d)", port),
				Port:    port,
				Tracked: false,
				Health:  0,
			})
		} else {
			dep.Tracked = true
			healthChecks++
			go func(deploy *Deploy) {
				s.checkHealth(deploy)
				checkSync <- 0
			}(dep)
		}
	}

	for healthChecks > 0 {
		<-checkSync
		healthChecks--
	}

	return result
}

func (s *ServerImpl) checkHealth(deploy *Deploy) {
	app, err := ApplicationFromConfig(s.deployConfigFile(deploy.Id))
	if err != nil {
		deploy.Errors = append(deploy.Errors,
			fmt.Sprintf("!! missing deploy config !! %s", err))
		println("Missing config")
		deploy.Health = -4
		return
	}

	status, err := s.testApp(deploy.Port, app)
	if err != nil {
		deploy.Errors = append(deploy.Errors, fmt.Sprintf("%s", err))
		log.Println("Got http err ", err, " for ", deploy.Id)
		deploy.Health = -1
		return
	}

	deploy.Health = status
}

func findDeployByPort(port int, deploys []*Deploy) *Deploy {
	for _, deploy := range deploys {
		if deploy.Port == port {
			return deploy
		}
	}
	return nil
}

func findDeployById(id string, deploys []*Deploy) *Deploy {
	for _, deploy := range deploys {
		if deploy.Id == id {
			return deploy
		}
	}
	return nil
}

func (s *ServerImpl) findUnusedPort() (int, error) {
	for i := s.startPort; i <= s.endPort; i++ {

		if portFree(i) {
			return i, nil
		}

	}

	return -1, errors.New("Could not find free port")
}

func portFree(port int) bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		// TODO: Is this now safe to assume the port is free?
		// NOTE(dan): I tried implementing listening on the port
		// instead, but it always succeeded even if there was
		// actually something already there...
		return true
	} else {
		conn.Close()
		return false
	}
}

func (s *ServerImpl) writeConfig() error {
	data, err := json.MarshalIndent(s.config, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path.Join(s.root, serverConfigFileName),
		data, os.FileMode(0644))
}

func (s *ServerImpl) SetMainByPort(port int) error {
	return s.reloadHaproxy(port)
}

func (s *ServerImpl) Run(deployId string) error {
	port, err := s.findUnusedPort()
	if err != nil {
		return err
	}
	log.Println("Found port ", port)

	deployPath := s.deployDir(deployId)

	app, err := ApplicationFromConfig(path.Join(deployPath, "deploy.json"))
	if err != nil {
		return err
	}

	s.config.Ports[strconv.Itoa(port)] = deployId
	err = s.writeConfig()
	if err != nil {
		return fmt.Errorf("write config: %s", err)
	}
	println(deployPath)
	println(app.RunCmd(port))
	cmd := exec.Command("sh", "-c", app.RunCmd(port))

	// process working dir
	cmd.Dir = deployPath

	detachProc(cmd)

	err = cmd.Start()
	if err != nil {
		return err
	}

	return s.waitForAppToStart(port, app)
}

func detachProc(cmd *exec.Cmd) {
	// give it its own process group, so it doesn't die
	// when the manager process exits for whatever reason
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Setpgid = true
}

var MAX_STARTUP_TIME = time.Duration(10) * time.Second
var MAX_HEALTH_CHECK_TIME = time.Duration(2) * time.Second
var STARTUP_HEALTH_CHECK_INTERVAL = time.Duration(100) * time.Millisecond

func (s *ServerImpl) waitForAppToStart(port int, app Application) error {

	end := time.Now().Add(MAX_STARTUP_TIME)
	for {
		log.Print(".")

		status, err := s.testApp(port, app)

		if err == nil {
			if status == 200 {
				log.Println("ok")
				return nil
			} else {
				log.Println("bad:", status)
				return errors.New(fmt.Sprintf("Health check failed %d", status))
			}
		}

		if time.Now().After(end) {
			return errors.New("Failed to connect to app after timeout")
		}

		time.Sleep(STARTUP_HEALTH_CHECK_INTERVAL)
	}
}

func (s *ServerImpl) testApp(port int, app Application) (int, error) {
	resp, err := s.client.Get(
		fmt.Sprintf("http://localhost:%d%s", port, app.HealthEndpoint()))

	if err == nil {
		return resp.StatusCode, nil
	}

	return -1, err
}

func (s *ServerImpl) reloadHaproxy(port int) error {
	if port < s.startPort {
		return fmt.Errorf("Invalid prod port %d", port)
	}
	cfg := HaproxyConfig(s.endPort, s.endPort-1, port)

	cfgFile := path.Join(s.root, haproxyConfig)
	pidFile := path.Join(s.root, haproxyPid)

	if err := ioutil.WriteFile(cfgFile, []byte(cfg), os.FileMode(0644)); err != nil {
		return err
	}

	runningPid, err := readPid(pidFile)
	if err != nil {
		return err
	}

	cmd := haproxyCmd(cfgFile, pidFile, runningPid)

	return cmd.Start()
}

func haproxyCmd(cfgFile string, pidFile string, runningPid int) *exec.Cmd {
	log.Println("PID ", runningPid, " ", pidFile)
	var cmd *exec.Cmd
	if runningPid > 0 {
		cmd = exec.Command(
			"/usr/local/sbin/haproxy",
			"-f", cfgFile,
			"-p", pidFile,
			"-sf", strconv.Itoa(runningPid))
	} else {
		cmd = exec.Command(
			"/usr/local/sbin/haproxy",
			"-f", cfgFile,
			"-p", pidFile)
	}

	detachProc(cmd)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd
}

func readPid(pidFile string) (int, error) {
	if data, err := ioutil.ReadFile(pidFile); err == nil {
		pid, err := strconv.Atoi(strings.TrimSpace(string(data)))

		if err != nil {
			return -1, fmt.Errorf("Invalid pid data, %s", err)
		}

		return pid, nil
	} else {
		return -1, nil // OK - no current pid
	}

}
<commit_msg>add check to prevent re-running<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type Deploy struct {
	Id   string
	Note string

	// known to the system config, versus just found either
	// running on a port or in the deploys dir
	Tracked bool

	// Port either specified in the config (regardless of running)
	// or port running on, for untracked things found on a port.
	// -1 for not specified
	Port int

	// http status code,
	// 0 for no connection attempted,
	// -1 for connection failed or timeout
	Health int

	Errors []string
}

type Label string

type Server interface {
	ListLabels() ([]Label, error)
	ListDeploys() ([]*Deploy, error)
	Run(deployId string) error
	Stop(deployId string) error
	Label(deployId string, label Label) error

	// TODO Maintenance mode
}

const (
	deploysDirName       = "deploys"
	deployConfigFileName = "deploy.json"
	serverConfigFileName = "config.json"
	haproxyConfig        = "haproxy.cfg"
	haproxyPid           = "haproxy.pid"
)

type Config struct {
	Ports  map[string]string
	Labels map[string]string // todo.
}

type ServerImpl struct {
	root        string
	config      *Config
	startPort   int
	endPort     int
	client      *http.Client
	deploysPath string
}

func readConfig(path string) (*Config, error) {
	var config Config
	if data, err := ioutil.ReadFile(path); err == nil {
		err = json.Unmarshal(data, &config)
		if err != nil {
			return nil, err
		}
	}
	if config.Ports == nil {
		config.Ports = make(map[string]string)
	}
	if config.Labels == nil {
		config.Labels = make(map[string]string)
	}
	return &config, nil
}

func NewServerImpl(root string) (*ServerImpl, error) {
	root, err := filepath.Abs(root)
	if err != nil {
		log.Fatal("Root path:", err)
	}
	config, err := readConfig(path.Join(root, serverConfigFileName))
	if err != nil {
		return nil, err
	}
	deploysPath := path.Join(root, deploysDirName)
	if _, err = os.Open(deploysPath); os.IsNotExist(err) {
		os.MkdirAll(deploysPath, 0644)
	}
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return errors.New("health check should not redirect")
		},
		Timeout: MAX_HEALTH_CHECK_TIME,
	}
	return &ServerImpl{
		root:        root,
		config:      config,
		startPort:   8001,
		endPort:     8099,
		client:      client,
		deploysPath: deploysPath,
	}, nil
}

func (s *ServerImpl) NewDeployDir() NewDeployDirResponse {
	t := time.Now()
	timestamp := fmt.Sprintf("%d-%02d-%02d-%02d-%02d-%02d",
		t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())

	return NewDeployDirResponse{
		DeployId: timestamp,
		Path:     s.deployDir(timestamp),
	}
}

func (s *ServerImpl) deployDir(deployId string) string {
	return path.Join(s.deploysPath, deployId)
}
func (s *ServerImpl) deployConfigFile(deployId string) string {
	return path.Join(s.deployDir(deployId), deployConfigFileName)
}

func (s *ServerImpl) ListDeploys() ([]*Deploy, error) {

	result, err := s.scanDeployDirs()
	if err != nil {
		return nil, err
	}

	result = s.scanConfig(result)
	result = s.scanPorts(result)

	return result, nil
}

func (s *ServerImpl) scanDeployDirs() ([]*Deploy, error) {
	infos, err := ioutil.ReadDir(s.deploysPath)
	if err != nil {
		return nil, err
	}

	var result []*Deploy
	for _, info := range infos {
		result = append(result, &Deploy{
			Id:      info.Name(),
			Port:    -1,
			Tracked: false,
		})
	}

	return result, nil
}

func (s *ServerImpl) scanConfig(deploys []*Deploy) []*Deploy {
	result := []*Deploy{}
	result = append(result, deploys...)

	for portStr, deployId := range s.config.Ports {
		port, err := strconv.Atoi(portStr)
		if err != nil {
			println(err)
			continue
		}

		deploy := findDeployById(deployId, result)

		if deploy == nil {
			result = append(result, &Deploy{
				Id:      deployId,
				Port:    port,
				Tracked: true,
				Errors:  []string{"No deploy dir present!"},
			})
		} else {
			deploy.Port = port
			deploy.Tracked = true
		}
	}

	return result
}

// Finds ports with *something* on them,
// either checking the health status for known deploys
// (and updating its run state)
// or adding a deploy with "unknown" id.
func (s *ServerImpl) scanPorts(deploys []*Deploy) []*Deploy {
	healthChecks := 0
	checkSync := make(chan int)

	result := []*Deploy{}
	result = append(result, deploys...)

	for port := s.startPort; port <= s.endPort; port++ {
		if portFree(port) {
			continue
		}

		dep := findDeployByPort(port, result)
		if dep == nil {
			result = append(result, &Deploy{
				Id:      fmt.Sprintf("(unkonwn-%d)", port),
				Port:    port,
				Tracked: false,
				Health:  0,
			})
		} else {
			dep.Tracked = true
			healthChecks++
			go func(deploy *Deploy) {
				s.checkHealth(deploy)
				checkSync <- 0
			}(dep)
		}
	}

	for healthChecks > 0 {
		<-checkSync
		healthChecks--
	}

	return result
}

func (s *ServerImpl) checkHealth(deploy *Deploy) {
	app, err := ApplicationFromConfig(s.deployConfigFile(deploy.Id))
	if err != nil {
		deploy.Errors = append(deploy.Errors,
			fmt.Sprintf("!! missing deploy config !! %s", err))
		println("Missing config")
		deploy.Health = -4
		return
	}

	status, err := s.testApp(deploy.Port, app)
	if err != nil {
		deploy.Errors = append(deploy.Errors, fmt.Sprintf("%s", err))
		log.Println("Got http err ", err, " for ", deploy.Id)
		deploy.Health = -1
		return
	}

	deploy.Health = status
}

func findDeployByPort(port int, deploys []*Deploy) *Deploy {
	for _, deploy := range deploys {
		if deploy.Port == port {
			return deploy
		}
	}
	return nil
}

func findDeployById(id string, deploys []*Deploy) *Deploy {
	for _, deploy := range deploys {
		if deploy.Id == id {
			return deploy
		}
	}
	return nil
}

func (s *ServerImpl) findUnusedPort() (int, error) {
	for i := s.startPort; i <= s.endPort; i++ {

		if portFree(i) {
			return i, nil
		}

	}

	return -1, errors.New("Could not find free port")
}

func portFree(port int) bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		// TODO: Is this now safe to assume the port is free?
		// NOTE(dan): I tried implementing listening on the port
		// instead, but it always succeeded even if there was
		// actually something already there...
		return true
	} else {
		conn.Close()
		return false
	}
}

func (s *ServerImpl) writeConfig() error {
	data, err := json.MarshalIndent(s.config, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path.Join(s.root, serverConfigFileName),
		data, os.FileMode(0644))
}

func (s *ServerImpl) SetMainByPort(port int) error {
	return s.reloadHaproxy(port)
}

func (s *ServerImpl) Run(deployIdToRun string) error {
	for portStr, deployId := range s.config.Ports {
		if deployIdToRun == deployId {
			return fmt.Errorf("Already configured for port %s", portStr)
		}
	}

	port, err := s.findUnusedPort()
	if err != nil {
		return err
	}
	log.Println("Found port ", port)

	deployPath := s.deployDir(deployIdToRun)

	app, err := ApplicationFromConfig(path.Join(deployPath, "deploy.json"))
	if err != nil {
		return err
	}

	s.config.Ports[strconv.Itoa(port)] = deployIdToRun
	err = s.writeConfig()
	if err != nil {
		return fmt.Errorf("write config: %s", err)
	}
	println(deployPath)
	println(app.RunCmd(port))
	cmd := exec.Command("sh", "-c", app.RunCmd(port))

	// process working dir
	cmd.Dir = deployPath

	detachProc(cmd)

	err = cmd.Start()
	if err != nil {
		return err
	}

	return s.waitForAppToStart(port, app)
}

func detachProc(cmd *exec.Cmd) {
	// give it its own process group, so it doesn't die
	// when the manager process exits for whatever reason
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Setpgid = true
}

var MAX_STARTUP_TIME = time.Duration(10) * time.Second
var MAX_HEALTH_CHECK_TIME = time.Duration(2) * time.Second
var STARTUP_HEALTH_CHECK_INTERVAL = time.Duration(100) * time.Millisecond

func (s *ServerImpl) waitForAppToStart(port int, app Application) error {

	end := time.Now().Add(MAX_STARTUP_TIME)
	for {
		log.Print(".")

		status, err := s.testApp(port, app)

		if err == nil {
			if status == 200 {
				log.Println("ok")
				return nil
			} else {
				log.Println("bad:", status)
				return errors.New(fmt.Sprintf("Health check failed %d", status))
			}
		}

		if time.Now().After(end) {
			return errors.New("Failed to connect to app after timeout")
		}

		time.Sleep(STARTUP_HEALTH_CHECK_INTERVAL)
	}
}

func (s *ServerImpl) testApp(port int, app Application) (int, error) {
	resp, err := s.client.Get(
		fmt.Sprintf("http://localhost:%d%s", port, app.HealthEndpoint()))

	if err == nil {
		return resp.StatusCode, nil
	}

	return -1, err
}

func (s *ServerImpl) reloadHaproxy(port int) error {
	if port < s.startPort {
		return fmt.Errorf("Invalid prod port %d", port)
	}
	cfg := HaproxyConfig(s.endPort, s.endPort-1, port)

	cfgFile := path.Join(s.root, haproxyConfig)
	pidFile := path.Join(s.root, haproxyPid)

	if err := ioutil.WriteFile(cfgFile, []byte(cfg), os.FileMode(0644)); err != nil {
		return err
	}

	runningPid, err := readPid(pidFile)
	if err != nil {
		return err
	}

	cmd := haproxyCmd(cfgFile, pidFile, runningPid)

	return cmd.Start()
}

func haproxyCmd(cfgFile string, pidFile string, runningPid int) *exec.Cmd {
	log.Println("PID ", runningPid, " ", pidFile)
	var cmd *exec.Cmd
	if runningPid > 0 {
		cmd = exec.Command(
			"/usr/local/sbin/haproxy",
			"-f", cfgFile,
			"-p", pidFile,
			"-sf", strconv.Itoa(runningPid))
	} else {
		cmd = exec.Command(
			"/usr/local/sbin/haproxy",
			"-f", cfgFile,
			"-p", pidFile)
	}

	detachProc(cmd)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd
}

func readPid(pidFile string) (int, error) {
	if data, err := ioutil.ReadFile(pidFile); err == nil {
		pid, err := strconv.Atoi(strings.TrimSpace(string(data)))

		if err != nil {
			return -1, fmt.Errorf("Invalid pid data, %s", err)
		}

		return pid, nil
	} else {
		return -1, nil // OK - no current pid
	}

}
<|endoftext|>


<commit_before>package revel

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	MainRouter         *Router
	MainTemplateLoader *TemplateLoader
	MainWatcher        *Watcher
	Server             *http.Server
)

// This method handles all requests.  It dispatches to handleInternal after
// handling / adapting websocket connections.
func handle(w http.ResponseWriter, r *http.Request) {
	upgrade := r.Header.Get("Upgrade")
	if upgrade == "websocket" || upgrade == "Websocket" {
		websocket.Handler(func(ws *websocket.Conn) {
			r.Method = "WS"
			handleInternal(w, r, ws)
		}).ServeHTTP(w, r)
	} else {
		handleInternal(w, r, nil)
	}
}

func handleInternal(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
	var (
		req  = NewRequest(r)
		resp = NewResponse(w)
		c    = NewController(req, resp)
	)
	req.Websocket = ws

	Filters[0](c, Filters[1:])
	if c.Result != nil {
		c.Result.Apply(req, resp)
	}
}

// Run the server.
// This is called from the generated main file.
// If port is non-zero, use that.  Else, read the port from app.conf.
func Run(port int) {
	address := HttpAddr
	if port == 0 {
		port = HttpPort
	}

	var network = "tcp"
	var localAddress string

	// If the port is zero, treat the address as a fully qualified local address.
	// This address must be prefixed with the network type followed by a colon,
	// e.g. unix:/tmp/app.socket or tcp6:::1 (equivalent to tcp6:0:0:0:0:0:0:0:1)
	if port == 0 {
		parts := strings.SplitN(address, ":", 2)
		network = parts[0]
		localAddress = parts[1]
	} else {
		localAddress = address + ":" + strconv.Itoa(port)
	}

	MainTemplateLoader = NewTemplateLoader(TemplatePaths)

	// The "watch" config variable can turn on and off all watching.
	// (As a convenient way to control it all together.)
	if Config.BoolDefault("watch", true) {
		MainWatcher = NewWatcher()
		Filters = append([]Filter{WatchFilter}, Filters...)
	}

	// If desired (or by default), create a watcher for templates and routes.
	// The watcher calls Refresh() on things on the first request.
	if MainWatcher != nil && Config.BoolDefault("watch.templates", true) {
		MainWatcher.Listen(MainTemplateLoader, MainTemplateLoader.paths...)
	} else {
		MainTemplateLoader.Refresh()
	}

	Server = &http.Server{
		Addr:    localAddress,
		Handler: http.HandlerFunc(handle),
	}

	runStartupHooks()

	go func() {
		time.Sleep(100 * time.Millisecond)
		fmt.Printf("Listening on %s...\n", localAddress)
	}()

	if HttpSsl {
		if network != "tcp" {
			// This limitation is just to reduce complexity, since it is standard
			// to terminate SSL upstream when using unix domain sockets.
			ERROR.Fatalln("SSL is only supported for TCP sockets. Specify a port to listen on.")
		}
		ERROR.Fatalln("Failed to listen:",
			Server.ListenAndServeTLS(HttpSslCert, HttpSslKey))
	} else {
		listener, err := net.Listen(network, localAddress)
		if err != nil {
			ERROR.Fatalln("Failed to listen:", err)
		}
		ERROR.Fatalln("Failed to serve:", Server.Serve(listener))
	}
}

func runStartupHooks() {
	for _, hook := range startupHooks {
		hook()
	}
}

var startupHooks []func()

// Register a function to be run at app startup.
//
// The order you register the functions will be the order they are run.
// You can think of it as a FIFO queue.
// This process will happen after the config file is read
// and before the server is listening for connections.
//
// Ideally, your application should have only one call to init() in the file init.go.
// The reason being that the call order of multiple init() functions in
// the same package is undefined.
// Inside of init() call revel.OnAppStart() for each function you wish to register.
//
// Example:
//
//      // from: yourapp/app/controllers/somefile.go
//      func InitDB() {
//          // do DB connection stuff here
//      }
//
//      func FillCache() {
//          // fill a cache from DB
//          // this depends on InitDB having been run
//      }
//
//      // from: yourapp/app/init.go
//      func init() {
//          // set up filters...
//
//          // register startup functions
//          revel.OnAppStart(InitDB)
//          revel.OnAppStart(FillCache)
//      }
//
// This can be useful when you need to establish connections to databases or third-party services,
// setup app components, compile assets, or any thing you need to do between starting Revel and accepting connections.
//
func OnAppStart(f func()) {
	startupHooks = append(startupHooks, f)
}
<commit_msg>shutdown hooks<commit_after>package revel

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	MainRouter         *Router
	MainTemplateLoader *TemplateLoader
	MainWatcher        *Watcher
	Server             *http.Server
)

// This method handles all requests.  It dispatches to handleInternal after
// handling / adapting websocket connections.
func handle(w http.ResponseWriter, r *http.Request) {
	upgrade := r.Header.Get("Upgrade")
	if upgrade == "websocket" || upgrade == "Websocket" {
		websocket.Handler(func(ws *websocket.Conn) {
			r.Method = "WS"
			handleInternal(w, r, ws)
		}).ServeHTTP(w, r)
	} else {
		handleInternal(w, r, nil)
	}
}

func handleInternal(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
	var (
		req  = NewRequest(r)
		resp = NewResponse(w)
		c    = NewController(req, resp)
	)
	req.Websocket = ws

	Filters[0](c, Filters[1:])
	if c.Result != nil {
		c.Result.Apply(req, resp)
	}
}

// Run the server.
// This is called from the generated main file.
// If port is non-zero, use that.  Else, read the port from app.conf.
func Run(port int) {
	address := HttpAddr
	if port == 0 {
		port = HttpPort
	}

	var network = "tcp"
	var localAddress string

	// If the port is zero, treat the address as a fully qualified local address.
	// This address must be prefixed with the network type followed by a colon,
	// e.g. unix:/tmp/app.socket or tcp6:::1 (equivalent to tcp6:0:0:0:0:0:0:0:1)
	if port == 0 {
		parts := strings.SplitN(address, ":", 2)
		network = parts[0]
		localAddress = parts[1]
	} else {
		localAddress = address + ":" + strconv.Itoa(port)
	}

	MainTemplateLoader = NewTemplateLoader(TemplatePaths)

	// The "watch" config variable can turn on and off all watching.
	// (As a convenient way to control it all together.)
	if Config.BoolDefault("watch", true) {
		MainWatcher = NewWatcher()
		Filters = append([]Filter{WatchFilter}, Filters...)
	}

	// If desired (or by default), create a watcher for templates and routes.
	// The watcher calls Refresh() on things on the first request.
	if MainWatcher != nil && Config.BoolDefault("watch.templates", true) {
		MainWatcher.Listen(MainTemplateLoader, MainTemplateLoader.paths...)
	} else {
		MainTemplateLoader.Refresh()
	}

	Server = &http.Server{
		Addr:    localAddress,
		Handler: http.HandlerFunc(handle),
	}

	runStartupHooks()

	go func() {
		time.Sleep(100 * time.Millisecond)
		fmt.Printf("Listening on %s...\n", localAddress)
	}()

	if HttpSsl {
		if network != "tcp" {
			// This limitation is just to reduce complexity, since it is standard
			// to terminate SSL upstream when using unix domain sockets.
			ERROR.Fatalln("SSL is only supported for TCP sockets. Specify a port to listen on.")
		}
		ERROR.Fatalln("Failed to listen:",
			Server.ListenAndServeTLS(HttpSslCert, HttpSslKey))
	} else {
		listener, err := net.Listen(network, localAddress)
		if err != nil {
			ERROR.Fatalln("Failed to listen:", err)
		}
		ERROR.Fatalln("Failed to serve:", Server.Serve(listener))
	}
}

func runStartupHooks() {
	for _, hook := range startupHooks {
		hook()
	}
}

var startupHooks []func()

// Register a function to be run at app startup.
//
// The order you register the functions will be the order they are run.
// You can think of it as a FIFO queue.
// This process will happen after the config file is read
// and before the server is listening for connections.
//
// Ideally, your application should have only one call to init() in the file init.go.
// The reason being that the call order of multiple init() functions in
// the same package is undefined.
// Inside of init() call revel.OnAppStart() for each function you wish to register.
//
// Example:
//
//      // from: yourapp/app/controllers/somefile.go
//      func InitDB() {
//          // do DB connection stuff here
//      }
//
//      func FillCache() {
//          // fill a cache from DB
//          // this depends on InitDB having been run
//      }
//
//      // from: yourapp/app/init.go
//      func init() {
//          // set up filters...
//
//          // register startup functions
//          revel.OnAppStart(InitDB)
//          revel.OnAppStart(FillCache)
//      }
//
// This can be useful when you need to establish connections to databases or third-party services,
// setup app components, compile assets, or any thing you need to do between starting Revel and accepting connections.
//
func OnAppStart(f func()) {
	startupHooks = append(startupHooks, f)
}

var shutdownHooks []func()<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/chrj/smtpd"
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

type Server struct {
	*smtpd.Server
	hooks *Hooks
}

func NewServer(srv *smtpd.Server, hooks *Hooks) *Server {
	s := &Server{srv, hooks}
	s.Server.Handler = s.ServeSMTP
	s.Server.RecipientChecker = s.CheckRecipient
	return s
}

func (s *Server) CheckRecipient(_ smtpd.Peer, addr string) error {
	if _, ok := s.hooks.Find(addr); ok {
		return nil
	}
	fmt.Println("denied unknown recipient address:", addr)
	return smtpd.Error{550, "address rejected: user unknown in local recipient table"}
}

func (s *Server) ServeSMTP(_ smtpd.Peer, env smtpd.Envelope) error {
	msg, err := NewMessage(bytes.NewBuffer(env.Data))

	if err != nil {
		fmt.Println("could not parse mail:", err)
		return smtpd.Error{565, "mail rejected: invalid format"}
	}

	for _, addr := range env.Recipients {
		hook, ok := s.hooks.Find(addr)

		if !ok {
			fmt.Println("could not find hook for address:", addr)
			return smtpd.Error{430, "internal server error"}
		}
		buf := bytes.NewBuffer(nil)

		if err := json.NewEncoder(buf).Encode(NewPayload(env.Sender, addr, msg)); err != nil {
			fmt.Println("could not json encode message:", err)
			return smtpd.Error{430, "internal server error"}
		}
		resp, err := http.Post(hook.Hook, "application/json", buf)

		if err != nil {
			fmt.Println("could not dispatch message:", err)
			return smtpd.Error{420, "internal server error"}
		}

		if resp.StatusCode < 200 || 299 < resp.StatusCode {
			fmt.Println("could not dispatch message: server responded with:", resp.Status)
			return smtpd.Error{420, "internal server error"}
		}
		fmt.Println("relayed mail for", addr, "to", hook.Hook)
	}
	return nil
}
<commit_msg>treat 4xx responses as permanent errors<commit_after>package main

import (
	"bitbucket.org/chrj/smtpd"
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

type Server struct {
	*smtpd.Server
	hooks *Hooks
}

func NewServer(srv *smtpd.Server, hooks *Hooks) *Server {
	s := &Server{srv, hooks}
	s.Server.Handler = s.ServeSMTP
	s.Server.RecipientChecker = s.CheckRecipient
	return s
}

func (s *Server) CheckRecipient(_ smtpd.Peer, addr string) error {
	if _, ok := s.hooks.Find(addr); ok {
		return nil
	}
	fmt.Println("denied unknown recipient address:", addr)
	return smtpd.Error{550, "address rejected: user unknown in local recipient table"}
}

func (s *Server) ServeSMTP(_ smtpd.Peer, env smtpd.Envelope) error {
	msg, err := NewMessage(bytes.NewBuffer(env.Data))

	if err != nil {
		fmt.Println("could not parse mail:", err)
		return smtpd.Error{565, "mail rejected: invalid format"}
	}

	for _, addr := range env.Recipients {
		hook, ok := s.hooks.Find(addr)

		if !ok {
			fmt.Println("could not find hook for address:", addr)
			return smtpd.Error{430, "internal server error"}
		}
		buf := bytes.NewBuffer(nil)

		if err := json.NewEncoder(buf).Encode(NewPayload(env.Sender, addr, msg)); err != nil {
			fmt.Println("could not json encode message:", err)
			return smtpd.Error{430, "internal server error"}
		}
		resp, err := http.Post(hook.Hook, "application/json", buf)

		if err != nil {
			fmt.Println("could not dispatch message:", err)
			return smtpd.Error{440, "unable to reach destination system"}
		}

		if 400 <= resp.StatusCode && resp.StatusCode <= 499 {
			fmt.Println("could not dispatch message: server responded with:", resp.Status)
			return smtpd.Error{500, "destination system does not accept message"}
		}

		if resp.StatusCode < 200 || 299 < resp.StatusCode {
			fmt.Println("could not dispatch message: server responded with:", resp.Status)
			return smtpd.Error{430, "internal server error"}
		}
		fmt.Println("relayed mail for", addr, "to", hook.Hook)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "log"
    "os"
    "bufio"
    "strings"
)

func main() {

    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/css/", staticHandler)
    http.HandleFunc("/img/", staticHandler)
    http.HandleFunc("/js/", staticHandler)

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("something went wrong...")
    }
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    file, err := os.Open("public/index.html")
    if err != nil {
        w.WriteHeader(404)
        w.Write([]byte("404 - " + http.StatusText(http.StatusNotFound)))
    } else {
        bw := bufio.NewReader(file);

        bw.WriteTo(w)
    }
}

func staticHandler(w http.ResponseWriter, r *http.Request) {
    path := "public/" + r.URL.Path
    file, err := os.Open(path)
    if err != nil {
        w.WriteHeader(404)
        w.Write([]byte("404 - " + http.StatusText(http.StatusNotFound)))
    } else {
        var contentType string
        bw := bufio.NewReader(file)

        if strings.HasSuffix(path, ".css") {
            contentType = "text/css"
        } else if strings.HasSuffix(path, ".png") {
            contentType = "image/png"
        } else if strings.HasSuffix(path, ".js") {
            contentType = "application/js"
        } else {
            contentType = "text/plain"
        }

        w.Header().Set("Content-Type", contentType)
        bw.WriteTo(w)
    }
}
<commit_msg>Refactor server<commit_after>package main

import (
    "net/http"
    "log"
    "os"
    "bufio"
    "path/filepath"
)

func main() {

    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/css/", staticHandler)
    http.HandleFunc("/img/", staticHandler)
    http.HandleFunc("/js/", staticHandler)

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("something went wrong...")
    }
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    file, err := os.Open("public/index.html")
    if err != nil {
        http.Error(w, "404 - Not Found", http.StatusNotFound)
        return
    }
    defer file.Close()

    bw := bufio.NewReader(file);
    bw.WriteTo(w)
}

func staticHandler(w http.ResponseWriter, r *http.Request) {
    path := "public/" + r.URL.Path

    file, err := os.Open(path)
    if err != nil {
        http.Error(w, "404 - Not Found", http.StatusNotFound)
        return
    }
    defer file.Close();

    bw := bufio.NewReader(file)
    w.Header().Set("Content-Type", getContentType(path))
    bw.WriteTo(w)
}

func getContentType(path string) string {
	var contentType string

	switch filepath.Ext(path) {
	case ".css":
		contentType = "text/css"
	case ".png":
		contentType = "image/png"
	case ".js":
		contentType = "application/js"
	default:
		contentType = "text/plain"
	}

	return contentType
}
<|endoftext|>


<commit_before>// spdy/server.go

package spdy

import (
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// ListenAndServe creates a new Server that serves on the given address.  If
// the handler is nil, then http.DefaultServeMux is used.
func ListenAndServe(addr string, handler http.Handler) error {
	srv := &Server{addr, handler}
	return srv.ListenAndServe()
}

// ListenAndServeTLS acts like ListenAndServe except it uses TLS.
func ListenAndServeTLS(addr string, certFile, keyFile string, handler http.Handler) (err error) {
	config := &tls.Config{
		Rand:         rand.Reader,
		Time:         time.Now,
		NextProtos:   []string{"http/1.1"},
		Certificates: make([]tls.Certificate, 1),
	}
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return
	}
	tlsListener := tls.NewListener(conn, config)
	return (&Server{addr, handler}).Serve(tlsListener)
}

// A Server handles incoming SPDY connections with HTTP handlers.
type Server struct {
	Addr    string
	Handler http.Handler
}

// ListenAndServe services SPDY requests on the given address.
// If the handler is nil, then http.DefaultServeMux is used.
func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(l)
}

// ListenAndServe services SPDY requests using the given listener.
// If the handler is nil, then http.DefaultServeMux is used.
func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = http.DefaultServeMux
	}
	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}
		s, err := newSession(c, handler)
		if err != nil {
			return err
		}
		go s.serve()
	}
	return nil
}

// A session manages a single TCP connection to a client.
type session struct {
	c       net.Conn
	handler http.Handler
	in, out chan Frame
	streams map[uint32]*serverStream // all access is done synchronously

	headerReader *HeaderReader
	headerWriter *HeaderWriter
}

func newSession(c net.Conn, h http.Handler) (s *session, err error) {
	s = &session{
		c:            c,
		handler:      h,
		headerReader: NewHeaderReader(),
		headerWriter: NewHeaderWriter(-1),
		in:           make(chan Frame),
		out:          make(chan Frame),
		streams:      make(map[uint32]*serverStream),
	}
	return
}

func (sess *session) serve() {
	defer sess.c.Close()
	go sess.sendFrames()
	go sess.receiveFrames()

	for {
		select {
		case f := <-sess.in:
			switch frame := f.(type) {
			case ControlFrame:
				sess.handleControl(frame)
			case DataFrame:
				sess.handleData(frame)
			}
		}
	}
}

func (sess *session) handleControl(frame ControlFrame) {
	switch frame.Type {
	case TypeSynStream:
		if stream, err := newServerStream(sess, frame); err == nil {
			if _, exists := sess.streams[stream.id]; !exists {
				sess.streams[stream.id] = stream
				go func() {
					sess.handler.ServeHTTP(stream, stream.Request())
					stream.finish()
				}()
			}
		}
	case TypeRstStream:
		d := bytes.NewBuffer(frame.Data)
		var streamId, statusCode uint32
		readBinary(d, &streamId, &statusCode)
	case TypePing:
		d := bytes.NewBuffer(frame.Data)
		var pingId uint32
		readBinary(d, &pingId)
		sess.out <- ControlFrame{
			Type: TypePing,
			Data: []byte{
				byte(pingId & 0xff000000 >> 24),
				byte(pingId & 0x00ff0000 >> 16),
				byte(pingId & 0x0000ff00 >> 8),
				byte(pingId & 0x000000ff >> 0),
			},
		}
	}
}

func (sess *session) handleData(frame DataFrame) {
	st, found := sess.streams[frame.StreamID]
	if !found {
		// TODO: Error?
		return
	}
	if st.dataPipe != nil {
		st.dataPipe.write(frame.Data)
		if frame.Flags&FlagFin != 0 {
			st.dataPipe.wclose(nil)
		}
	}
}

func (sess *session) sendFrames() {
	for frame := range sess.out {
		// TODO: Check for errors
		frame.WriteTo(sess.c)
	}
}

func (sess *session) receiveFrames() {
	defer close(sess.in)
	for {
		frame, err := ReadFrame(sess.c)
		if err != nil {
			return
		}
		sess.in <- frame
	}
}

// A serverStream is a logical data stream inside a session.  A serverStream
// services a single request.
type serverStream struct {
	id      uint32
	session *session
	closed  bool

	requestHeaders  http.Header
	responseHeaders http.Header
	wroteHeader     bool

	dataPipe *asyncPipe
}

func newServerStream(sess *session, frame ControlFrame) (st *serverStream, err error) {
	if frame.Type != TypeSynStream {
		err = errors.New("Server stream must be created from a SynStream frame")
		return
	}
	st = &serverStream{
		session:         sess,
		responseHeaders: make(http.Header),
	}
	if frame.Flags&FlagFin == 0 {
		// Request body will follow
		st.dataPipe = apipe()
	}
	// Read frame data
	data := bytes.NewBuffer(frame.Data)
	err = binary.Read(data, binary.BigEndian, &st.id)
	if err != nil {
		return
	}
	_, err = io.ReadFull(data, make([]byte, 6)) // skip associated stream ID and priority
	if err != nil {
		return
	}
	st.requestHeaders, err = sess.headerReader.Decode(data.Bytes())
	return
}

// Request returns the request data associated with the serverStream.
func (st *serverStream) Request() (req *http.Request) {
	// TODO: Add more info
	req = &http.Request{
		Method:     st.requestHeaders.Get("method"),
		Proto:      st.requestHeaders.Get("version"),
		Header:     st.requestHeaders,
		Body:       st,
		RemoteAddr: st.session.c.RemoteAddr().String(),
	}
	req.URL, _ = url.ParseRequestURI(st.requestHeaders.Get("url"))
	return
}

func (st *serverStream) Read(p []byte) (n int, err error) {
	return st.dataPipe.read(p)
}

// Header returns the current response headers.
func (st *serverStream) Header() http.Header { return st.responseHeaders }

func (st *serverStream) Write(p []byte) (n int, err error) {
	if st.closed {
		err = errors.New("Write on closed serverStream")
		return
	}
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	for len(p) > 0 {
		frame := DataFrame{
			StreamID: st.id,
		}
		if len(p) < MaxDataLength {
			frame.Data = make([]byte, len(p))
		} else {
			frame.Data = make([]byte, MaxDataLength)
		}
		copy(frame.Data, p)
		p = p[len(frame.Data):]
		st.session.out <- frame
		n += len(frame.Data)
	}
	return
}

// A synReplyFrame defers header compression until the server writes the frame.
// This is necessary to guarantee correctly ordered compression.
type synReplyFrame struct {
	stream *serverStream
	header http.Header
	flags  FrameFlags
}

func (frame synReplyFrame) GetFlags() FrameFlags {
	return frame.flags
}

func (frame synReplyFrame) GetData() []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, frame.stream.id&0x7fffffff)
	buf.Write([]byte{0, 0})
	frame.stream.session.headerWriter.WriteHeader(buf, frame.stream.responseHeaders)
	return buf.Bytes()
}

func (frame synReplyFrame) WriteTo(w io.Writer) (n int64, err error) {
	cf := ControlFrame{Type: TypeSynReply, Data: frame.GetData()}
	return cf.WriteTo(w)
}

func (st *serverStream) WriteHeader(code int) {
	if st.wroteHeader {
		return
	}
	st.responseHeaders.Set("status", strconv.Itoa(code)+" "+http.StatusText(code))
	st.responseHeaders.Set("version", "HTTP/1.1")
	if st.responseHeaders.Get("Content-Type") == "" {
		st.responseHeaders.Set("Content-Type", "text/html; charset=utf-8")
	}
	if st.responseHeaders.Get("Date") == "" {
		st.responseHeaders.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}
	// Write the frame
	// TODO: Copy headers
	st.session.out <- synReplyFrame{stream: st, header: st.responseHeaders}
	st.wroteHeader = true
}

// Close sends a closing frame, thus preventing the server from sending more
// data over the stream.  The client may still send data.
func (st *serverStream) Close() (err error) {
	if st.closed {
		return
	}
	st.session.out <- DataFrame{
		StreamID: st.id,
		Flags:    FlagFin,
		Data:     []byte{},
	}
	st.closed = true
	return nil
}

func (st *serverStream) finish() (err error) {
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	return st.Close()
}
<commit_msg>Offer spdy/2 with NPN<commit_after>// spdy/server.go

package spdy

import (
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// ListenAndServe creates a new Server that serves on the given address.  If
// the handler is nil, then http.DefaultServeMux is used.
func ListenAndServe(addr string, handler http.Handler) error {
	srv := &Server{addr, handler}
	return srv.ListenAndServe()
}

// ListenAndServeTLS acts like ListenAndServe except it uses TLS.
func ListenAndServeTLS(addr string, certFile, keyFile string, handler http.Handler) (err error) {
	config := &tls.Config{
		Rand:         rand.Reader,
		Time:         time.Now,
		NextProtos:   []string{"spdy/2", "http/1.1"},
		Certificates: make([]tls.Certificate, 1),
	}
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return
	}
	tlsListener := tls.NewListener(conn, config)
	return (&Server{addr, handler}).Serve(tlsListener)
}

// A Server handles incoming SPDY connections with HTTP handlers.
type Server struct {
	Addr    string
	Handler http.Handler
}

// ListenAndServe services SPDY requests on the given address.
// If the handler is nil, then http.DefaultServeMux is used.
func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(l)
}

// ListenAndServe services SPDY requests using the given listener.
// If the handler is nil, then http.DefaultServeMux is used.
func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = http.DefaultServeMux
	}
	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}
		s, err := newSession(c, handler)
		if err != nil {
			return err
		}
		go s.serve()
	}
	return nil
}

// A session manages a single TCP connection to a client.
type session struct {
	c       net.Conn
	handler http.Handler
	in, out chan Frame
	streams map[uint32]*serverStream // all access is done synchronously

	headerReader *HeaderReader
	headerWriter *HeaderWriter
}

func newSession(c net.Conn, h http.Handler) (s *session, err error) {
	s = &session{
		c:            c,
		handler:      h,
		headerReader: NewHeaderReader(),
		headerWriter: NewHeaderWriter(-1),
		in:           make(chan Frame),
		out:          make(chan Frame),
		streams:      make(map[uint32]*serverStream),
	}
	return
}

func (sess *session) serve() {
	defer sess.c.Close()
	go sess.sendFrames()
	go sess.receiveFrames()

	for {
		select {
		case f := <-sess.in:
			switch frame := f.(type) {
			case ControlFrame:
				sess.handleControl(frame)
			case DataFrame:
				sess.handleData(frame)
			}
		}
	}
}

func (sess *session) handleControl(frame ControlFrame) {
	switch frame.Type {
	case TypeSynStream:
		if stream, err := newServerStream(sess, frame); err == nil {
			if _, exists := sess.streams[stream.id]; !exists {
				sess.streams[stream.id] = stream
				go func() {
					sess.handler.ServeHTTP(stream, stream.Request())
					stream.finish()
				}()
			}
		}
	case TypeRstStream:
		d := bytes.NewBuffer(frame.Data)
		var streamId, statusCode uint32
		readBinary(d, &streamId, &statusCode)
	case TypePing:
		d := bytes.NewBuffer(frame.Data)
		var pingId uint32
		readBinary(d, &pingId)
		sess.out <- ControlFrame{
			Type: TypePing,
			Data: []byte{
				byte(pingId & 0xff000000 >> 24),
				byte(pingId & 0x00ff0000 >> 16),
				byte(pingId & 0x0000ff00 >> 8),
				byte(pingId & 0x000000ff >> 0),
			},
		}
	}
}

func (sess *session) handleData(frame DataFrame) {
	st, found := sess.streams[frame.StreamID]
	if !found {
		// TODO: Error?
		return
	}
	if st.dataPipe != nil {
		st.dataPipe.write(frame.Data)
		if frame.Flags&FlagFin != 0 {
			st.dataPipe.wclose(nil)
		}
	}
}

func (sess *session) sendFrames() {
	for frame := range sess.out {
		// TODO: Check for errors
		frame.WriteTo(sess.c)
	}
}

func (sess *session) receiveFrames() {
	defer close(sess.in)
	for {
		frame, err := ReadFrame(sess.c)
		if err != nil {
			return
		}
		sess.in <- frame
	}
}

// A serverStream is a logical data stream inside a session.  A serverStream
// services a single request.
type serverStream struct {
	id      uint32
	session *session
	closed  bool

	requestHeaders  http.Header
	responseHeaders http.Header
	wroteHeader     bool

	dataPipe *asyncPipe
}

func newServerStream(sess *session, frame ControlFrame) (st *serverStream, err error) {
	if frame.Type != TypeSynStream {
		err = errors.New("Server stream must be created from a SynStream frame")
		return
	}
	st = &serverStream{
		session:         sess,
		responseHeaders: make(http.Header),
	}
	if frame.Flags&FlagFin == 0 {
		// Request body will follow
		st.dataPipe = apipe()
	}
	// Read frame data
	data := bytes.NewBuffer(frame.Data)
	err = binary.Read(data, binary.BigEndian, &st.id)
	if err != nil {
		return
	}
	_, err = io.ReadFull(data, make([]byte, 6)) // skip associated stream ID and priority
	if err != nil {
		return
	}
	st.requestHeaders, err = sess.headerReader.Decode(data.Bytes())
	return
}

// Request returns the request data associated with the serverStream.
func (st *serverStream) Request() (req *http.Request) {
	// TODO: Add more info
	req = &http.Request{
		Method:     st.requestHeaders.Get("method"),
		Proto:      st.requestHeaders.Get("version"),
		Header:     st.requestHeaders,
		Body:       st,
		RemoteAddr: st.session.c.RemoteAddr().String(),
	}
	req.URL, _ = url.ParseRequestURI(st.requestHeaders.Get("url"))
	return
}

func (st *serverStream) Read(p []byte) (n int, err error) {
	return st.dataPipe.read(p)
}

// Header returns the current response headers.
func (st *serverStream) Header() http.Header { return st.responseHeaders }

func (st *serverStream) Write(p []byte) (n int, err error) {
	if st.closed {
		err = errors.New("Write on closed serverStream")
		return
	}
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	for len(p) > 0 {
		frame := DataFrame{
			StreamID: st.id,
		}
		if len(p) < MaxDataLength {
			frame.Data = make([]byte, len(p))
		} else {
			frame.Data = make([]byte, MaxDataLength)
		}
		copy(frame.Data, p)
		p = p[len(frame.Data):]
		st.session.out <- frame
		n += len(frame.Data)
	}
	return
}

// A synReplyFrame defers header compression until the server writes the frame.
// This is necessary to guarantee correctly ordered compression.
type synReplyFrame struct {
	stream *serverStream
	header http.Header
	flags  FrameFlags
}

func (frame synReplyFrame) GetFlags() FrameFlags {
	return frame.flags
}

func (frame synReplyFrame) GetData() []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, frame.stream.id&0x7fffffff)
	buf.Write([]byte{0, 0})
	frame.stream.session.headerWriter.WriteHeader(buf, frame.stream.responseHeaders)
	return buf.Bytes()
}

func (frame synReplyFrame) WriteTo(w io.Writer) (n int64, err error) {
	cf := ControlFrame{Type: TypeSynReply, Data: frame.GetData()}
	return cf.WriteTo(w)
}

func (st *serverStream) WriteHeader(code int) {
	if st.wroteHeader {
		return
	}
	st.responseHeaders.Set("status", strconv.Itoa(code)+" "+http.StatusText(code))
	st.responseHeaders.Set("version", "HTTP/1.1")
	if st.responseHeaders.Get("Content-Type") == "" {
		st.responseHeaders.Set("Content-Type", "text/html; charset=utf-8")
	}
	if st.responseHeaders.Get("Date") == "" {
		st.responseHeaders.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}
	// Write the frame
	// TODO: Copy headers
	st.session.out <- synReplyFrame{stream: st, header: st.responseHeaders}
	st.wroteHeader = true
}

// Close sends a closing frame, thus preventing the server from sending more
// data over the stream.  The client may still send data.
func (st *serverStream) Close() (err error) {
	if st.closed {
		return
	}
	st.session.out <- DataFrame{
		StreamID: st.id,
		Flags:    FlagFin,
		Data:     []byte{},
	}
	st.closed = true
	return nil
}

func (st *serverStream) finish() (err error) {
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	return st.Close()
}
<|endoftext|>


<commit_before>package distnano

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"sync"
)

func handler(w http.ResponseWriter, r *http.Request) {
	log.Println("Handling request for url path: ", r.URL.Path)

	// Our actual response to the request.
	var response JSONResponse

	// Mutex to protect concurrent modification of response and WaitGroup to
	// wait for all responses.
	var mtx = &sync.Mutex{}
	var wg = &sync.WaitGroup{}

	// Note what kind of request this is.
	schemaRequest := strings.HasPrefix(r.URL.Path, "/schema")
	wg.Add(5)
	// Send off the request to each server that we know exists.
	for i := 1; i <= 5; i++ {
		go func(counter int) {
			defer wg.Done()

			rawResponse, err := http.Get(
				fmt.Sprintf("http://localhost:900%v%v", counter, r.URL.Path),
			)

			if err != nil {
				log.Fatalf(
					"Cannot continue, error getting %v from node %v\n",
					r.URL.Path,
					i,
				)
			}

			// Read the response and unmarshal into a NanocubeResponse object.
			defer rawResponse.Body.Close()
			content, _ := ioutil.ReadAll(rawResponse.Body)

			var partitionResponse JSONResponse
			if schemaRequest {
				partitionResponse = new(SchemaResponse)
			} else {
				partitionResponse = new(NanocubeResponse)
			}

			err = partitionResponse.Unmarshal(content)
			if err != nil {
				log.Fatal(err)
			}

			// Update our global response.
			mtx.Lock()
			defer mtx.Unlock()
			if response == nil {
				// First one to respond, we just set the global response to be
				// our response.
				response = partitionResponse
				return
			}

			// TODO(asubiotto): We can use a more intelligent merging strategy
			// when we have a lot of nodes (in a tree-like fashion).

			// Otherwise we merge with what we have already.
			response.Merge(partitionResponse)
		}(i)
	}

	wg.Wait()
	b, err := response.Marshal()
	if err != nil {
		log.Fatal(err)
	}

	w.Write(b)
}

func Run() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(":29512", nil))
}
<commit_msg>Added header for requests. The visualizer works\! Let's pop some champagne\!<commit_after>package distnano

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"sync"
)

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	log.Println("Handling request for url path: ", r.URL.Path)

	// Our actual response to the request.
	var response JSONResponse

	// Mutex to protect concurrent modification of response and WaitGroup to
	// wait for all responses.
	var mtx = &sync.Mutex{}
	var wg = &sync.WaitGroup{}

	// Note what kind of request this is.
	schemaRequest := strings.HasPrefix(r.URL.Path, "/schema")
	wg.Add(5)
	// Send off the request to each server that we know exists.
	for i := 1; i <= 5; i++ {
		go func(counter int) {
			defer wg.Done()

			rawResponse, err := http.Get(
				fmt.Sprintf("http://localhost:900%v%v", counter, r.URL.Path),
			)

			if err != nil {
				log.Fatalf(
					"Cannot continue, error getting %v from node %v\n",
					r.URL.Path,
					i,
				)
			}

			// Read the response and unmarshal into a NanocubeResponse object.
			defer rawResponse.Body.Close()
			content, _ := ioutil.ReadAll(rawResponse.Body)

			var partitionResponse JSONResponse
			if schemaRequest {
				partitionResponse = new(SchemaResponse)
			} else {
				partitionResponse = new(NanocubeResponse)
			}

			err = partitionResponse.Unmarshal(content)
			if err != nil {
				log.Fatal(err)
			}

			// Update our global response.
			mtx.Lock()
			defer mtx.Unlock()
			if response == nil {
				// First one to respond, we just set the global response to be
				// our response.
				response = partitionResponse
				return
			}

			// TODO(asubiotto): We can use a more intelligent merging strategy
			// when we have a lot of nodes (in a tree-like fashion).

			// Otherwise we merge with what we have already.
			response.Merge(partitionResponse)
		}(i)
	}

	wg.Wait()
	b, err := response.Marshal()
	if err != nil {
		log.Fatal(err)
	}

	w.Write(b)
}

func Run() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(":29512", nil))
}
<|endoftext|>