コード例 #1
0
ファイル: vault.go プロジェクト: fedgrant/tonika
func (v *Vault0) serveLocal(fpath, query string) (*http.Response, os.Error) {
	fpath = path.Clean(fpath)
	if len(fpath) > 0 && fpath[0] == '/' {
		fpath = fpath[1:]
	}
	if fpath == "" {
		fpath = "index.html"
	}
	full := path.Join(v.hdir, fpath)
	if !isFile(full) {
		if fpath == "index.html" {
			return newRespNoIndexHTML(), nil
		} else {
			return newRespNotFound(), os.ErrorString("not found")
		}
	}

	// Serve file if we can allocate a file descriptor in time
	if v.fdlim.LockOrTimeout(10e9) == nil {
		body, bodylen, err := http.FileToBody(full)
		if err != nil {
			if body != nil {
				body.Close()
			}
			v.fdlim.Unlock()
			return newRespServiceUnavailable(), os.ErrorString("service unavailable")
		}
		body = http.NewRunOnClose(body, func() { v.fdlim.Unlock() })
		return buildRespFromBody(body, bodylen), nil
	} else {
		return newRespServiceUnavailable(), os.ErrorString("service unavailable")
	}
	panic("unreach")
}
コード例 #2
0
ファイル: monitor.go プロジェクト: fedgrant/tonika
func (mon *Monitor) report(url *http.URL, every int64) {
	i := 0
	for {
		// Sleep between updates
		if i > 0 {
			time.Sleep(every)
		}
		i++

		// Prepare HTTP request
		jj, err := mon.dumper.MarshalJSON()
		if err != nil {
			continue
		}
		//printJSON(jj)

		repbuf, err := EncodeReport(jj, mon.key)
		if err != nil {
			continue
		}
		bodylen, body := http.BytesToBody(repbuf)

		req := &http.Request{
			Method:        "POST",
			URL:           url,
			Body:          body,
			ContentLength: int64(bodylen),
			Close:         true,
			UserAgent:     sys.Name + "-Client-Monitor",
		}

		// Connect
		conn, err := net.Dial("tcp", "", url.Host)
		if err != nil {
			if conn != nil {
				conn.Close()
			}
			continue
		}
		cc := http.NewClientConn(conn, nil)

		// Send request
		wch := make(chan int, 1)
		req.Body = http.NewRunOnClose(req.Body, func() { wch <- 1 })
		err = cc.Write(req)
		if err != nil {
			cc.Close()
			conn.Close()
		} else {
			<-wch // wait until the request has been sent
			cc.Close()
			conn.Close()
		}
	}
}
コード例 #3
0
ファイル: vault.go プロジェクト: fedgrant/tonika
func (v *Vault0) serve(req *http.Request, my bool) (*http.Response, os.Error) {
	// We don't expect a body in requests (at this stage, there are no POSTs)
	if req.Body != nil {
		req.Body.Close()
		req.Body = nil
	}

	// Parse the URL
	tid, fpath, query, err := parseURL(req)
	if err != nil {
		return newRespBadRequest(), os.ErrorString("bad request")
	}

	// If request is at its final destination
	if tid == v.id {
		h, err := parseReqHop(req)
		if err == nil {
			v.w.AddHopsFwd(h) // Stat hop count at destination
		}
		resp, err := v.serveLocal(fpath, query)
		if err == nil {
			setRespHop(resp, 0)
		}
		return resp, err
	}

	// Stop if too many hops or hops not included
	h, err := parseReqHop(req)
	if err != nil || h > maxHops {
		v.w.IncTooManyHops()
		return newRespServiceUnavailable(), os.ErrorString("too many hops")
	}

	// Parse the HTTP header
	sid, err := parseOrigin(req)
	if err != nil {
		sid = v.id
	}
	hid := v.c.QueryQuantize(sid, tid)
	if hid == nil {
		return newRespServiceUnavailable(), os.ErrorString("no route to destination")
	}
	d := v.isHealthy()
	if d == nil {
		return newRespServiceUnavailable(), os.ErrorString("service unavailable")
	}
	cc := d.Dial(*hid, "vault0")
	if cc == nil {
		return newRespServiceUnavailable(), os.ErrorString("service unavailable")
	}
	pcc := prof.NewConn(cc)
	acc := http.NewAsyncClientConn(pcc)

	// Pre-fetch
	_, err = parseReqHop(req)
	if err != nil {
		setReqHop(req, 0)
	}
	setReqVersion(req)

	// Fetch and post-fetch
	resp, err := acc.Fetch(req)
	if err != nil {
		acc.Close()
		pcc.Close()
		return newRespServiceUnavailable(), os.ErrorString("service unavailable")
	}

	// Update hop
	setRespVersion(resp)
	h, err = parseRespHop(resp)
	if err == nil {
		setRespHop(resp, h+1)
	} else {
		setRespHop(resp, 0)
	}

	// Do we understand the response?
	if !statusCodeSupported(resp.StatusCode) {
		acc.Close()
		pcc.Close()
		return newRespUnsupported(), nil
	}

	// Set hooks for when body is fully read
	if resp.Body == nil {
		// Update traffic stats
		if my {
			v.w.IncFwdMyInTraffic(*hid, pcc.InTraffic())
			v.w.IncFwdMyOutTraffic(*hid, pcc.OutTraffic())
		} else {
			v.w.IncFwdBehalfInTraffic(*hid, pcc.InTraffic())
			v.w.IncFwdBehalfOutTraffic(*hid, pcc.OutTraffic())
		}
		v.w.RecFwdLatencyPerByte(*hid, float64(pcc.Duration())/float64(pcc.InTraffic()))

		acc.Close()
		pcc.Close()
	} else {
		resp.Body = http.NewRunOnClose(resp.Body, func() {
			// Update traffic stats
			if my {
				v.w.IncFwdMyInTraffic(*hid, pcc.InTraffic())
				v.w.IncFwdMyOutTraffic(*hid, pcc.OutTraffic())
			} else {
				v.w.IncFwdBehalfInTraffic(*hid, pcc.InTraffic())
				v.w.IncFwdBehalfOutTraffic(*hid, pcc.OutTraffic())
			}
			v.w.RecFwdLatencyPerByte(*hid, float64(pcc.Duration())/float64(pcc.InTraffic()))

			acc.Close()
			pcc.Close()
		})
	}

	return resp, nil
}
コード例 #4
0
ファイル: server.go プロジェクト: fedgrant/tonika
func (s *Server) serve(q *http.Query) {
	q.Continue()
	req := q.GetRequest()
	if req.Body != nil {
		req.Body.Close()
	}
	s.log(req, q.RemoteAddr())

	// Path cleanup
	path := path.Clean(req.URL.Path)
	if path == "" {
		path = "/"
	}

	// Host analysis
	hostport := strings.Split(req.Host, ":", 2)
	if len(hostport) == 0 {
		q.Write(newRespBadRequest())
		return
	}
	hostparts := misc.ReverseHost(strings.Split(hostport[0], ".", -1))

	// http://5ttt.org, http://www.5ttt.org
	if len(hostparts) < 3 || hostparts[2] == "www" {
		if path == "/" {
			path = "/index.html"
		}

		if isIndex(path) {
			s.lk.Lock()
			s.stats.Views++
			s.lk.Unlock()
		}

		var resp *http.Response
		if isIndex(path) {
			resp = s.replyIndex()
		} else {
			resp = s.replyStatic(path)
		}

		if isDownload(path) && resp.Body != nil {
			resp.Body = http.NewRunOnClose(resp.Body, func() {
				// TODO: This also counts incomplete downloads, but for now
				// it's fine.
				s.lk.Lock()
				s.stats.Downloads++
				s.lk.Unlock()
			})
		}
		q.Write(resp)
		return
	}

	// Remove 5ttt.org from host
	hostparts = hostparts[2:]

	// http://*.a.5ttt.org/*
	if hostparts[0] == "a" {
		q.Write(s.replyStatic("/tadmin.html")) // Trying to access a Tonika Admin
		return
	}

	// http://*.[id].5ttt.org
	if _, err := sys.ParseId(hostparts[0]); err == nil {
		q.Write(s.replyStatic("/turl.html")) // Trying to access a Tonika URL
		return
	}

	// Otherwise
	q.Write(newRespNotFound())
}