Beispiel #1
0
func httpProxy(writer http.ResponseWriter, request *http.Request) {

	proxyRequest := new(http.Request)
	*proxyRequest = *request

	log.Printf("request = %s %s", request.Method, request.URL.Host)

	if strings.ToUpper(proxyRequest.Method) == "CONNECT" {
		hostPort := request.URL.Host
		pandora, err := dialer.Dial("tcp", hostPort) // tuner.pandora.com:443
		if err != nil {
			log.Printf("pianobarproxy: error: %v", err)
			writer.WriteHeader(http.StatusInternalServerError)
			return
		}

		client, writer, err := writer.(http.Hijacker).Hijack()
		writer.WriteString("HTTP/1.0 200 Connection Established\r\n\r\n")
		writer.Flush()
		go pipe(client, pandora)
		go pipe(pandora, client)
		return
	}
	proxyRequest.Proto = "HTTP/1.1"
	proxyRequest.ProtoMajor = 1
	proxyRequest.ProtoMinor = 1
	proxyRequest.Close = false

	// Remove the connection header to the backend.  We want a
	// persistent connection, regardless of what the client sent
	// to us.
	if proxyRequest.Header.Get("Connection") != "" {
		proxyRequest.Header = make(http.Header)
		copyHeader(proxyRequest.Header, request.Header)
		proxyRequest.Header.Del("Connection")
	}

	response, err := transport.RoundTrip(proxyRequest)
	if err != nil {
		log.Printf("pianobarproxy: error: %v", err)
		writer.WriteHeader(http.StatusInternalServerError)
		return
	}

	copyHeader(writer.Header(), response.Header)

	writer.WriteHeader(response.StatusCode)

	if response.Body != nil {
		io.Copy(io.Writer(writer), response.Body)
	}
}
Beispiel #2
0
// serveQuery parses an incoming query and, if valid, executes the query.
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
	atomic.AddInt64(&h.stats.QueryRequests, 1)
	defer func(start time.Time) {
		atomic.AddInt64(&h.stats.QueryRequestDuration, time.Since(start).Nanoseconds())
	}(time.Now())

	// Retrieve the underlying ResponseWriter or initialize our own.
	rw, ok := w.(ResponseWriter)
	if !ok {
		rw = NewResponseWriter(w, r)
	}

	// Retrieve the node id the query should be executed on.
	nodeID, _ := strconv.ParseUint(r.FormValue("node_id"), 10, 64)

	var qr io.Reader
	// Attempt to read the form value from the "q" form value.
	if qp := strings.TrimSpace(r.FormValue("q")); qp != "" {
		qr = strings.NewReader(qp)
	} else if r.MultipartForm != nil && r.MultipartForm.File != nil {
		// If we have a multipart/form-data, try to retrieve a file from 'q'.
		if fhs := r.MultipartForm.File["q"]; len(fhs) > 0 {
			f, err := fhs[0].Open()
			if err != nil {
				h.httpError(rw, err.Error(), http.StatusBadRequest)
				return
			}
			defer f.Close()
			qr = f
		}
	}

	if qr == nil {
		h.httpError(rw, `missing required parameter "q"`, http.StatusBadRequest)
		return
	}

	epoch := strings.TrimSpace(r.FormValue("epoch"))

	p := influxql.NewParser(qr)
	db := r.FormValue("db")

	// Sanitize the request query params so it doesn't show up in the response logger.
	// Do this before anything else so a parsing error doesn't leak passwords.
	sanitize(r)

	// Parse the parameters
	rawParams := r.FormValue("params")
	if rawParams != "" {
		var params map[string]interface{}
		decoder := json.NewDecoder(strings.NewReader(rawParams))
		decoder.UseNumber()
		if err := decoder.Decode(&params); err != nil {
			h.httpError(rw, "error parsing query parameters: "+err.Error(), http.StatusBadRequest)
			return
		}

		// Convert json.Number into int64 and float64 values
		for k, v := range params {
			if v, ok := v.(json.Number); ok {
				var err error
				if strings.Contains(string(v), ".") {
					params[k], err = v.Float64()
				} else {
					params[k], err = v.Int64()
				}

				if err != nil {
					h.httpError(rw, "error parsing json value: "+err.Error(), http.StatusBadRequest)
					return
				}
			}
		}
		p.SetParams(params)
	}

	// Parse query from query string.
	query, err := p.ParseQuery()
	if err != nil {
		h.httpError(rw, "error parsing query: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Check authorization.
	if h.Config.AuthEnabled {
		if err := h.QueryAuthorizer.AuthorizeQuery(user, query, db); err != nil {
			if err, ok := err.(meta.ErrAuthorize); ok {
				h.Logger.Printf("Unauthorized request | user: %q | query: %q | database %q\n", err.User, err.Query.String(), err.Database)
			}
			h.httpError(rw, "error authorizing query: "+err.Error(), http.StatusForbidden)
			return
		}
	}

	// Parse chunk size. Use default if not provided or unparsable.
	chunked := r.FormValue("chunked") == "true"
	chunkSize := DefaultChunkSize
	if chunked {
		if n, err := strconv.ParseInt(r.FormValue("chunk_size"), 10, 64); err == nil && int(n) > 0 {
			chunkSize = int(n)
		}
	}

	// Parse whether this is an async command.
	async := r.FormValue("async") == "true"

	// Make sure if the client disconnects we signal the query to abort
	var closing chan struct{}
	if !async {
		closing = make(chan struct{})
		if notifier, ok := w.(http.CloseNotifier); ok {
			// CloseNotify() is not guaranteed to send a notification when the query
			// is closed. Use this channel to signal that the query is finished to
			// prevent lingering goroutines that may be stuck.
			done := make(chan struct{})
			defer close(done)

			notify := notifier.CloseNotify()
			go func() {
				// Wait for either the request to finish
				// or for the client to disconnect
				select {
				case <-done:
				case <-notify:
					close(closing)
				}
			}()
		} else {
			defer close(closing)
		}
	}

	// Execute query.
	rw.Header().Add("Connection", "close")
	results := h.QueryExecutor.ExecuteQuery(query, influxql.ExecutionOptions{
		Database:  db,
		ChunkSize: chunkSize,
		ReadOnly:  r.Method == "GET",
		NodeID:    nodeID,
	}, closing)

	// If we are running in async mode, open a goroutine to drain the results
	// and return with a StatusNoContent.
	if async {
		go h.async(query, results)
		h.writeHeader(w, http.StatusNoContent)
		return
	}

	// if we're not chunking, this will be the in memory buffer for all results before sending to client
	resp := Response{Results: make([]*influxql.Result, 0)}

	// Status header is OK once this point is reached.
	// Attempt to flush the header immediately so the client gets the header information
	// and knows the query was accepted.
	h.writeHeader(rw, http.StatusOK)
	if w, ok := w.(http.Flusher); ok {
		w.Flush()
	}

	// pull all results from the channel
	rows := 0
	for r := range results {
		// Ignore nil results.
		if r == nil {
			continue
		}

		// if requested, convert result timestamps to epoch
		if epoch != "" {
			convertToEpoch(r, epoch)
		}

		// Write out result immediately if chunked.
		if chunked {
			n, _ := rw.WriteResponse(Response{
				Results: []*influxql.Result{r},
			})
			atomic.AddInt64(&h.stats.QueryRequestBytesTransmitted, int64(n))
			w.(http.Flusher).Flush()
			continue
		}

		// Limit the number of rows that can be returned in a non-chunked response.
		// This is to prevent the server from going OOM when returning a large response.
		// If you want to return more than the default chunk size, then use chunking
		// to process multiple blobs.
		rows += len(r.Series)
		if h.Config.MaxRowLimit > 0 && rows > h.Config.MaxRowLimit {
			break
		}

		// It's not chunked so buffer results in memory.
		// Results for statements need to be combined together.
		// We need to check if this new result is for the same statement as
		// the last result, or for the next statement
		l := len(resp.Results)
		if l == 0 {
			resp.Results = append(resp.Results, r)
		} else if resp.Results[l-1].StatementID == r.StatementID {
			if r.Err != nil {
				resp.Results[l-1] = r
				continue
			}

			cr := resp.Results[l-1]
			rowsMerged := 0
			if len(cr.Series) > 0 {
				lastSeries := cr.Series[len(cr.Series)-1]

				for _, row := range r.Series {
					if !lastSeries.SameSeries(row) {
						// Next row is for a different series than last.
						break
					}
					// Values are for the same series, so append them.
					lastSeries.Values = append(lastSeries.Values, row.Values...)
					rowsMerged++
				}
			}

			// Append remaining rows as new rows.
			r.Series = r.Series[rowsMerged:]
			cr.Series = append(cr.Series, r.Series...)
			cr.Messages = append(cr.Messages, r.Messages...)
		} else {
			resp.Results = append(resp.Results, r)
		}
	}

	// If it's not chunked we buffered everything in memory, so write it out
	if !chunked {
		n, _ := rw.WriteResponse(resp)
		atomic.AddInt64(&h.stats.QueryRequestBytesTransmitted, int64(n))
	}
}
Beispiel #3
0
func handler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		r := recover()
		if r != nil {
			w.Header().Set("Content-Type", "text/plain")
			Fprintf(w, "%v", r)
		}
	}()

	req.ParseForm()
	if f4, ok4 := req.Form["list"]; ok4 {
		what := f4[0]
		if what == "" {
			// List all files
			names, err := filepath.Glob("*.bas")
			if err != nil {
				panic(err)
			}
			sort.Strings(names)
			w.Header().Set("Content-Type", "text/html")
			Fprintf(w, "<html><body>")
			for _, name := range names {
				Fprintf(w, `<a href="/?list=%s">%s</a><br>`+"\n", name, name)
			}
		} else {
			// List one file
			fd, err := os.Open(what)
			if err != nil {
				panic(err)
			}
			w.Header().Set("Content-Type", "text/plain")
			_, err = io.Copy(w, fd)
			if err != nil {
				panic(err)
			}
			err = fd.Close()
			if err != nil {
				panic(err)
			}
		}
	} else if f1, ok1 := req.Form["run"]; ok1 {
		var putchar func(ch byte)
		forward_putchar := func(ch byte) {
			putchar(ch)
		}

		terp := NewTerp(f1[0], forward_putchar)
		terp.SetExpiration("30s")
		d := draw.Register(terp)
		putchar = d.Putchar

		if f3, ok3 := req.Form["progname"]; ok3 {
			name := f3[0]
			if name == "" {
				name = "untitled"
			}
			name = strings.Trim(name, " \t\r\n")
			flags := os.O_CREATE | os.O_WRONLY | os.O_APPEND
			if strings.HasSuffix(name, "!") {
				flags |= os.O_EXCL
			}
			fd, err := os.OpenFile(CurlyEncode(name)+".bas", flags, 0666)
			if err != nil {
				panic(err)
			}
			w := bufio.NewWriter(fd)
			Fprintf(w, "###### ###### ###### ###### ###### ######\n")
			Fprintf(w, "%s\n", strings.Replace(f1[0], "\r", "", -1))
			w.Flush()
			fd.Close()
		}

		terp.Run()
		if d.HasImage() {
			w.Header().Set("Content-Type", "image/png")
			d.WritePng(w)
		} else {
			w.Header().Set("Content-Type", "text/plain")
			Fprintf(w, "Use 'PRINT' or 'CALL triangle' statements to produce output.")
		}
	} else {
		dict := make(map[string]interface{})
		if f2, ok2 := req.Form["load"]; ok2 {
			code, err := ioutil.ReadFile(strings.Trim(f2[0], " \t\n\r"))
			s := "\n" + string(code)
			if err != nil {
				panic(err)
			}
			m := THRU_CRUNCH.FindStringSubmatch(s)
			if m != nil {
				s = m[1]
			}
			dict["Code"] = s
		} else {
			dict["Code"] = template.HTML(DEMO)
		}

		{
			names, err := filepath.Glob("*{33}.bas")
			if err != nil {
				panic(err)
			}
			for i, e := range names {
				names[i] = e[:len(e)-8] // Chop "{33}.bas"
			}
			sort.Strings(names)
			dict["Links"] = names
		}

		Tmpl.ExecuteTemplate(w, "Main", dict)
	}
}